Compare commits
	
		
			1509 Commits
		
	
	
		
			0.1.2-1
			...
			bbf659e52d
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| bbf659e52d | |||
| 30a26ef3ed | |||
| ca066aa7a7 | |||
| 7d9314f05c | |||
| 3842d8b104 | |||
| a827279163 | |||
| b0063cdead | |||
| 9298a0dc92 | |||
| b9e2e65331 | |||
| 27e7bbb9d1 | |||
| 2139b197ba | |||
| 4e1a944a2d | |||
| e3c6d5a5c0 | |||
| 8c3f0092d2 | |||
| 6fad04c862 | |||
| 838dcbfd7e | |||
| f5df252857 | |||
| 285fc91c66 | |||
| d95b6cf589 | |||
| 51ba1c852c | |||
| 80ca7aa08b | |||
| 25c38ea381 | |||
| 06cfd603ca | |||
| 500886e410 | |||
| 51d9b35dc4 | |||
| 16dc789db5 | |||
| e4f9b1a605 | |||
| 3a8533a7ba | |||
| 5ac6fe30b5 | |||
| 14501d3828 | |||
| c78bdfa5e2 | |||
| b2ed2afd8a | |||
| 00d198895e | |||
| b5c079da9a | |||
| 93422b9779 | |||
| 6dcfd9a4fe | |||
| 6d1919974a | |||
| f27c716296 | |||
| 21395241de | |||
| 8d2cb13195 | |||
| e61e8b063a | |||
| 073c78d98a | |||
| 85e4faf898 | |||
| 7f802d57f8 | |||
| 868dc3f7e2 | |||
| 9e8c236281 | |||
| 827fb317bc | |||
| edd5da89a7 | |||
| 27187b428d | |||
| e28f543d89 | |||
| 1ec8e2186b | |||
| 657fb04f1b | |||
| dc1e6b7a67 | |||
| 77a432817e | |||
| 31a4ff9d90 | |||
| cb315d94fd | |||
| 32f72df105 | |||
| 724e84441e | |||
| ecd418c5db | |||
| 5dcb4cb508 | |||
| 9c03e359a4 | |||
| 1505080afd | |||
| 8cb6093f0b | |||
| 7a92adcd3a | |||
| 27396e17f2 | |||
| fefd5c8237 | |||
| f9993c60f5 | |||
| 8e314f8676 | |||
| 98a3b07237 | |||
| efad6fdf2e | |||
| bce6d484a9 | |||
| 2dea19df89 | |||
| 6c986467d3 | |||
| 3cedbebe40 | |||
| dc986e4fe5 | |||
| cae6be6f86 | |||
| 1d6ed99073 | |||
| 1605b0f7b2 | |||
| 244be471f0 | |||
| a12c4f87d2 | |||
| 312457494c | |||
| 3288ffb3cc | |||
| 81a8ce002c | |||
| e1bf435080 | |||
| 80ab4e037e | |||
| 3fab344779 | |||
| d841727439 | |||
| 87df34bb56 | |||
| 9c3b742a98 | |||
| 68bf3717f9 | |||
| f88c6e0dba | |||
| 12050cdda9 | |||
| 01e77a97f3 | |||
| 10824b5d9b | |||
| d9870e03bc | |||
| 785b9e0b60 | |||
| fce2bc645e | |||
| 991716a7f5 | |||
| aa720f2460 | |||
| ac4ef8fb6d | |||
| eae0afda23 | |||
| 5291f8a4d1 | |||
| e2d7de1e9e | |||
| d7c9c27ec7 | |||
| 153b1b3c2b | |||
| ec63c7c1c5 | |||
| 05c2535698 | |||
| e261d5e345 | |||
| c00497d776 | |||
| 766eeab49f | |||
| 3c9b404234 | |||
| 9c56b3883e | |||
| 3d506db975 | |||
| d7e84a79a8 | |||
| 102471eaaa | |||
| 90b0fec236 | |||
| 4883e179e7 | |||
| 13c6e96292 | |||
| f547c0cc81 | |||
| fbe38eede9 | |||
| 22551c379f | |||
| a102af5a78 | |||
| e9dffcea83 | |||
| b9563d75dd | |||
| 3a569422ad | |||
| 0ee43f80a6 | |||
| f4542adf3b | |||
| 9f0623d194 | |||
| 5bab95a942 | |||
| 831f36946d | |||
| a4fbabaf9a | |||
| 04897c7d2e | |||
| b7e6fdaeac | |||
| 481f6b686e | |||
| e7a69ebdca | |||
| 194c3c4886 | |||
| f5a46aa203 | |||
| 443371e2fd | |||
| d7ab9247cd | |||
| a2cd54fba4 | |||
| 9048f3df77 | |||
| fecf3b59a3 | |||
| 18a4623e71 | |||
| 968a7ccc0e | |||
| 6249502a88 | |||
| 8a78034079 | |||
| 7633b7b056 | |||
| b8e6b24bf3 | |||
| 97b7ca931f | |||
| 48dd9acde5 | |||
| 5147a20b3c | |||
| bb2319a78d | |||
| 7c10d95c1c | |||
| f734d1e3f6 | |||
| e567bb35c3 | |||
| 3ec18a6964 | |||
| 847fa288f1 | |||
| 824ecfab2e | |||
| 5f5d8277b9 | |||
| 0a6cf619b0 | |||
| 050a146ae0 | |||
| 1bc53146b9 | |||
| e82350df4a | |||
| 3d3ce2918b | |||
| 79e6a4212d | |||
| 37cdbba0a3 | |||
| c37fb98bed | |||
| 975f145444 | |||
| 391186d01f | |||
| ae056cd88c | |||
| 7f989b206b | |||
| 65ce02e777 | |||
| 878d9714cb | |||
| f99b7f4bb8 | |||
| e23098410c | |||
| 04494d2a2a | |||
| e2d6fbb513 | |||
| 477c650f3f | |||
| fc15c68cba | |||
| 32b5f5420b | |||
| ee87f82799 | |||
| 7c08f522aa | |||
| e211554579 | |||
| 7ba890dfd7 | |||
| 68b4309164 | |||
| d803f3d490 | |||
| 5468766d87 | |||
| e967d8d20c | |||
| ad4db882f0 | |||
| 84aa846b87 | |||
| 3532968b33 | |||
| 6bb49db4ee | |||
| 38fb111f7a | |||
| a50447f457 | |||
| b1a2044631 | |||
| c883920caa | |||
| 21453ef272 | |||
| 10182433f8 | |||
| b338f33a63 | |||
| cb82200481 | |||
| 9abf74d6d2 | |||
| 50e81a6cb5 | |||
| 8fae1fb6b3 | |||
| 372fa110ec | |||
| 35bec9fe58 | |||
| 93d67bdba9 | |||
| a5e72a18e3 | |||
| 91d2f46b93 | |||
| c60bae4533 | |||
| 43ac878d44 | |||
| ceabd06a43 | |||
| 9bfc0c5338 | |||
| fb8206ff13 | |||
| dceb0ef461 | |||
| 88bc1982ca | |||
| e741a9d7e7 | |||
| 65f1d22205 | |||
| d867c08aba | |||
| 6193eff38e | |||
| f1929e7cf9 | |||
| 373484c242 | |||
| f77460bb0c | |||
| 574e0dcb05 | |||
| 7b19a0aa08 | |||
| 08642d7618 | |||
| c3e9c27cd3 | |||
| 29a2854671 | |||
| 8e6786e722 | |||
| 6ad40564e3 | |||
| 776973bfe9 | |||
| 6025e43baa | |||
| d9a47f882c | |||
| 4235758a6d | |||
| 59fe2dfabb | |||
| 6364536dcd | |||
| a8a771114d | |||
| 4e0a2c8301 | |||
| b6fed92a17 | |||
| 97b57aeb0c | |||
| e25ed1fff9 | |||
| a2ff5b8a14 | |||
| 0284f18beb | |||
| 803d64c78c | |||
| dacb2f8ace | |||
| b7a53960e5 | |||
| 66f1e6b4fe | |||
| 33166bfafc | |||
| b2648645e8 | |||
| 53e3ddb751 | |||
| edc2dcab92 | |||
| d49f545d94 | |||
| 3b98c99b72 | |||
| 1da775a09b | |||
| f0475bd9a0 | |||
| 15d8afefbb | |||
| f3bcc01685 | |||
| 95238606d5 | |||
| bbf8170cb9 | |||
| 8c628f23dc | |||
| 811f5d5754 | |||
| 69ec7fc1fe | |||
| 064197d222 | |||
| e9cf2bc849 | |||
| 103ad57ddc | |||
| 2856c5c1b7 | |||
| a953349c14 | |||
| 175d86745f | |||
| 081a141218 | |||
| e904ab0b84 | |||
| a2f9dbbe01 | |||
| 8b922309b9 | |||
| 4c81e3c432 | |||
| 6c1a707166 | |||
| 7d8253618b | |||
| dbaf85799a | |||
| daeea24e0e | |||
| 0fca0352c5 | |||
| 8eaad8219a | |||
| 6bc92f4a08 | |||
| 8be40e2d80 | |||
| 01b415d4cb | |||
| 5e82638f35 | |||
| 46d076af9d | |||
| 38a665024e | |||
| d32eb8266b | |||
| bc4ac0f316 | |||
| 6952b8727f | |||
| 81d4da6550 | |||
| 6154ca7ddf | |||
| 8fb1e0ca0f | |||
| 763a01af09 | |||
| 663cb29ccd | |||
| 56c3365656 | |||
| e7b2c64798 | |||
| 7cb6b63eb9 | |||
| d6d88f5f60 | |||
| 2c208c4381 | |||
| 39bc6c4945 | |||
| b94e3b745f | |||
| 6f337aeee1 | |||
| 5d48060834 | |||
| c842c203e2 | |||
| 5bcfc8db75 | |||
| 27b4dde755 | |||
| 91ab199769 | |||
| e75be49be4 | |||
| 505fb8cb08 | |||
| e5c9265588 | |||
| 02003ec80e | |||
| 133470b6f2 | |||
| 4a6230c439 | |||
| fdc7d80bbf | |||
| 352551e168 | |||
| 7aec050419 | |||
| 4289034436 | |||
| 8f8858f100 | |||
| d98fb0d5b2 | |||
| 5014bf5bc5 | |||
| 0708cabc75 | |||
| 4fcb26cf93 | |||
| 269def20d1 | |||
| b8de9e0e42 | |||
| 7b2b598588 | |||
| e0b61486b0 | |||
| 3f86f7412f | |||
| 6454d960de | |||
| 37154c188b | |||
| 8da7578a0a | |||
| 2a64094006 | |||
| e9ce9644ff | |||
| 52439aa5bc | |||
| ccf865687b | |||
| cac34db1fd | |||
| faf3893180 | |||
| c33dfcfddd | |||
| 019e14ab1f | |||
| b5790196c6 | |||
| 94a64ca690 | |||
| a6ce04c903 | |||
| 165c154233 | |||
| 318547db46 | |||
| e60c09e19c | |||
| 4834d1484c | |||
| 4b6342727e | |||
| cb5fa52cd9 | |||
| 947d01cf7f | |||
| 3563394fb3 | |||
| 269d7a7def | |||
| e95f2333b0 | |||
| 950217e0a3 | |||
| 5e65fb3301 | |||
| 2a294cde04 | |||
| e95420d79c | |||
| cffbd17dc7 | |||
| 00de8c3d75 | |||
| 1f4711d07a | |||
| 30e3396897 | |||
| 5291e049a1 | |||
| 08fbb504c9 | |||
| e9ca1d3e5d | |||
| eb80406fdb | |||
| 9fe53b0b9c | |||
| 9ae5e62e5d | |||
| 1613ae7de6 | |||
| ed1caa7be7 | |||
| d88f3a5a27 | |||
| c98eb49ae3 | |||
| 6d9d8a4724 | |||
| 7d8c68a455 | |||
| 8f33640bec | |||
| f89023e24a | |||
| f5d14f2e18 | |||
| 9811ede3b2 | |||
| b2e51fea48 | |||
| 2a915620c9 | |||
| 526688935f | |||
| c7dcf7c66d | |||
| 06411dc147 | |||
| 195d182cc9 | |||
| 17217dae76 | |||
| f105cc0a41 | |||
| 2f62c7ae89 | |||
| f6c1fea17c | |||
| 178dc93319 | |||
| 8ffe8eff06 | |||
| bd4952ee57 | |||
| 4b171fd04f | |||
| 2c198cfde8 | |||
| 7c6d39b5fa | |||
| 9c13b2f9e9 | |||
| 1ec9556aa6 | |||
| adec38b50b | |||
| a35af6f020 | |||
| e74ff5e885 | |||
| c87561f63b | |||
| c681570134 | |||
| 53b945c72f | |||
| f6985daec7 | |||
| 5662c3b6da | |||
| 9def0b27c9 | |||
| 52a02c82d2 | |||
| c241961d0a | |||
| 8f50555a06 | |||
| b35375c929 | |||
| bf1e715261 | |||
| 0240e1dca2 | |||
| 7cec2a00c5 | |||
| 239f79fecb | |||
| 0265a59b82 | |||
| 57dce34fc5 | |||
| d8110580e9 | |||
| 03b7ada5ef | |||
| 1df505ea00 | |||
| 3e8dac3203 | |||
| 95707a71a9 | |||
| fc2c2907c4 | |||
| ebdd1c2c0c | |||
| 13254b24dd | |||
| e17eb64031 | |||
| a0727a0291 | |||
| f7f7926829 | |||
| 9b7dca341b | |||
| da3300562a | |||
| e2ddb5a14c | |||
| fb9645aed6 | |||
| a4ebc7e126 | |||
| fd5db7d68a | |||
| e7eddb4f08 | |||
| 94155845f0 | |||
| 3abf608b15 | |||
| d31fe2363b | |||
| 11a56f87e8 | |||
| 19793cdcd4 | |||
| 9363773fa1 | |||
| c7990882cf | |||
| d4ab76ea1b | |||
| 2c992a0e63 | |||
| 88f96acc3c | |||
| 245db06173 | |||
| 49c2cd5c4b | |||
| 64db553185 | |||
| a06a19ce9c | |||
| 592ddc1541 | |||
| cb5f2b73d0 | |||
| 2304b12c1c | |||
| 38d3e1912c | |||
| fbc14fd7b4 | |||
| 0283df22c8 | |||
| 845737ee8e | |||
| 6993511c67 | |||
| 9111ad147c | |||
| 333214aa8f | |||
| c0cde02fec | |||
| 070a20a2e5 | |||
| c5e8409079 | |||
| 67eff0eda9 | |||
| 3de7b632e0 | |||
| 842248e4c4 | |||
| c5d7ec25b5 | |||
| a9a965d698 | |||
| dc866dd540 | |||
| 89252619b1 | |||
| 2699b06d7c | |||
| fd0d45f721 | |||
| 5ecf838dd2 | |||
| 45a7a90cb8 | |||
| cac851f2b1 | |||
| 238082b657 | |||
| aecbabe522 | |||
| a9cdac4f74 | |||
| a59dbbe50e | |||
| 9bec95ede8 | |||
| 70307a9e82 | |||
| ef077b4e6a | |||
| dcabed4e93 | |||
| 1af047f66e | |||
| ee91748b3c | |||
| e5241d619b | |||
| d79608edbb | |||
| 4cbd26580e | |||
| fe62ad5539 | |||
| eb13f038a1 | |||
| 9505c2b030 | |||
| 008835c24f | |||
| 7083b3d8d2 | |||
| 754931b2f6 | |||
| 2dc8ffba32 | |||
| d0fe6a2e85 | |||
| 85705b6e68 | |||
| 3ea7a015a9 | |||
| 44329413ed | |||
| 46db68ab22 | |||
| dc9d7f22a2 | |||
| f917018fd9 | |||
| 7b420c430d | |||
| 00359d25c1 | |||
| d8a3063735 | |||
| 6491af19e3 | |||
| 61328d20ed | |||
| 0a6d92a1f3 | |||
| 3a576d1073 | |||
| b30b98b521 | |||
| 43d82a2af0 | |||
| 6a4495b813 | |||
| e8a0ad6647 | |||
| 92b89cc4d8 | |||
| 268b1b1d98 | |||
| 75bc89ca30 | |||
| 0625937068 | |||
| 32a9074963 | |||
| b869b5fd2a | |||
| 3a3e2f7157 | |||
| bea57aa03a | |||
| 30991d5364 | |||
| 5cc8b0811c | |||
| 2c73b9862d | |||
| 732b2f061e | |||
| 3680533eef | |||
| 1307d72c9d | |||
| 405dfa0c34 | |||
| 5c2d154ad1 | |||
| f2bf8d9bac | |||
| f9cfd6bd06 | |||
| 287f63fa52 | |||
| 5fe47634e8 | |||
| a6590910cf | |||
| ad454c386c | |||
| 0b2c296de0 | |||
| 0e85940cba | |||
| 8d479c32f8 | |||
| 549785cf7d | |||
| aafc4c8d62 | |||
| 47dedbdc73 | |||
| 6fe134afc8 | |||
| 63a50f92e7 | |||
| ca6da15ef7 | |||
| 8dfa19fa0f | |||
| 0feee0ae2f | |||
| 2a6a39916a | |||
| f0a2b2859f | |||
| 32ddb66fc8 | |||
| df63c2388d | |||
| 757655ea63 | |||
| 329c1cc037 | |||
| da6dd55d13 | |||
| 0e5490f1c8 | |||
| b82d638de1 | |||
| 224034dcc6 | |||
| 026d3d41c1 | |||
| fd1a06b359 | |||
| 452d010183 | |||
| eb1c17e3ac | |||
| a101873eb0 | |||
| 3d2acb692a | |||
| 0900c2691e | |||
| 1337676e08 | |||
| 2e075eafab | |||
| 14d64b6070 | |||
| 81b8fbf4e3 | |||
| 24d074752f | |||
| 08047a9307 | |||
| 1b0cd5b90b | |||
| 65e8998894 | |||
| 449948050b | |||
| cf97281592 | |||
| 75684efa1a | |||
| 2c4f27a943 | |||
| 53b7dec7cd | |||
| e0cbfb000b | |||
| 3a66f4c862 | |||
| 85ceaa464f | |||
| 976755338b | |||
| 1c980059cf | |||
| 2d8c4c1698 | |||
| 19a333d7bd | |||
| 96c55db63d | |||
| fecb07ee37 | |||
| e10c6480a5 | |||
| f3cc07c009 | |||
| 068076dd47 | |||
| 02158605be | |||
| 674e6a90ec | |||
| f679330466 | |||
| 93fc7c2e83 | |||
| f299617c60 | |||
| 28cbc5b98c | |||
| c28f1ee0bc | |||
| cff112d705 | |||
| 9fc4ad63c4 | |||
| 97054a71c1 | |||
| 2391668a25 | |||
| 717d33547c | |||
| 997be32679 | |||
| 134f00c40e | |||
| 47c898bdfd | |||
| e752ee12d1 | |||
| cc4515ff66 | |||
| f190292171 | |||
| b246f2b349 | |||
| 76b69d851a | |||
| 224f586368 | |||
| 9add6c8ff1 | |||
| 7a63d4eed1 | |||
| e54a4807f7 | |||
| cee04c1d6f | |||
| cbec78589d | |||
| a85db7cb3f | |||
| 2bd3779839 | |||
| 303e33cafb | |||
| b4e689dddf | |||
| 98a0b036c5 | |||
| fb3f30fb10 | |||
| 6213952007 | |||
| 07ac041d69 | |||
| 5c02028841 | |||
| c561b53670 | |||
| dcd0d5a362 | |||
| 18acac83bc | |||
| d7d44470bb | |||
| 0f0aae7ba4 | |||
| 4c0886a5d9 | |||
| 04a3038369 | |||
| bdcf5d3fc0 | |||
| c7a858eed7 | |||
| de5aa9237d | |||
| d015f97395 | |||
| 57618156b4 | |||
| 865254d646 | |||
| 1dbab03fe7 | |||
| a943aaf5fc | |||
| 6e6e8b2617 | |||
| 4c2c24af2c | |||
| 3d3a10aafb | |||
| 000fc97beb | |||
| 5645eeaafa | |||
| 961477d522 | |||
| a5f71015a6 | |||
| e42ea943b7 | |||
| 9c5fc6b61c | |||
| 302caf015f | |||
| e11296071a | |||
| 112eb29f93 | |||
| c6c97516b3 | |||
| 03676b2894 | |||
| 9ca57fac2e | |||
| 18f151c1fb | |||
| e90e56d8b2 | |||
| d241ca5698 | |||
| b512cf8667 | |||
| a24d2923c6 | |||
| 467808abef | |||
| 861f1f2216 | |||
| 509b22bea0 | |||
| 7447b2f4c1 | |||
| fef14b6e4f | |||
| 01d2a7e6aa | |||
| ac586fec5a | |||
| 5476808683 | |||
| 331d737796 | |||
| ef81b8adf9 | |||
| 8a7d635cef | |||
| 4c259c1eef | |||
| 5b4ede5e2f | |||
| d0ab3dda78 | |||
| d9cf51b4bb | |||
| aa17f24220 | |||
| cf60edf7d4 | |||
| ffbc243194 | |||
| b6b07cf30c | |||
| 495a6b22bd | |||
| 0acaffbdfa | |||
| 6043bc4517 | |||
| e6ed066e3f | |||
| ee4e8655b8 | |||
| 37970d2be6 | |||
| 1376788016 | |||
| 4cad86cf85 | |||
| 6304116edb | |||
| 834ff8fa63 | |||
| 1f428a535e | |||
| 0c40966970 | |||
| 9da071fe9b | |||
| 892a04f289 | |||
| 27cc9727f1 | |||
| f0738d451b | |||
| 9e6a8daf2c | |||
| bfacfec765 | |||
| 0bae5bf32b | |||
| 22b09d16d0 | |||
| 9c867e106e | |||
| 304f28a3c1 | |||
| d65d3793de | |||
| 3638d87bd2 | |||
| b97a92860d | |||
| 7c86a5eeb3 | |||
| d23dbaaf69 | |||
| e6ffc371e1 | |||
| 3177c6eaa3 | |||
| acd2f0519d | |||
| 18ec100c33 | |||
| fa55fce76e | |||
| f47d5e347d | |||
| 7488a8b597 | |||
| 2e3ac154be | |||
| 2472640755 | |||
| 7b685d6cad | |||
| 17f6f4e616 | |||
| 48cfc15cfb | |||
| bb9b779cee | |||
| af63ce67ae | |||
| 5cc4871ec4 | |||
| c8cfe669b8 | |||
| 8b74d6d759 | |||
| a9227768de | |||
| d966e1d4de | |||
| ceb2146c1b | |||
| 8d006d8c74 | |||
| 777304f259 | |||
| 12433f7c23 | |||
| 44b53da345 | |||
| ab45fc144e | |||
| e99e9e0708 | |||
| 467404bfc8 | |||
| ce50fa2a62 | |||
| 10a011d842 | |||
| 5352410d0c | |||
| c5d155396a | |||
| 93187099d3 | |||
| aa24b1dce5 | |||
| eb3ede9593 | |||
| d7fecfbd0b | |||
| b065b4ff21 | |||
| 87370d24be | |||
| 8f8b9988ad | |||
| f8ccf4f5d8 | |||
| 25d8b86efd | |||
| 0cd3e937d8 | |||
| 89bb9c215e | |||
| 2d18686ce7 | |||
| 1d999d4910 | |||
| 7dfaa7579a | |||
| 08cb079e97 | |||
| 450aa83592 | |||
| 0614c76e92 | |||
| 97e338f9d4 | |||
| 636f018daa | |||
| c8d639024a | |||
| 6be2ee626a | |||
| f7fc1967a5 | |||
| aedb8a765b | |||
| cf58bd15c3 | |||
| 34f4f68524 | |||
| 09b8144080 | |||
| f1e6fb4ce7 | |||
| 2ca63fd1f6 | |||
| a5d25e7d92 | |||
| 4167819e7a | |||
| 5bd3a463f0 | |||
| 79c447b4c6 | |||
| 540304f947 | |||
| 75d8f7331b | |||
| b2509e9e53 | |||
| 7862f44653 | |||
| 962dd0c1bb | |||
| 5d5f7c7f5c | |||
| 6aaf838451 | |||
| ad3bd312e9 | |||
| 5fa9939696 | |||
| 4956bb0e9c | |||
| c074c12be7 | |||
| ddbc293e9c | |||
| a3921b45c7 | |||
| 38e1c8c5a1 | |||
| c2d29ff233 | |||
| 2316baa898 | |||
| f185d559c0 | |||
| 73d95bc004 | |||
| fcd55f89d7 | |||
| f9fe793573 | |||
| bc36411993 | |||
| 48506236bf | |||
| ded9b589fe | |||
| 67c3732fad | |||
| 2932f4591e | |||
| df53c07450 | |||
| 40899e9d80 | |||
| f794af0950 | |||
| 1665a1a093 | |||
| 4a36fb6d95 | |||
| acf78a8822 | |||
| f5c1ec9939 | |||
| 4b3d38b05b | |||
| 23e0b53107 | |||
| c907486c4d | |||
| 6b5945add8 | |||
| 55693de934 | |||
| d467475b6d | |||
| 44bc14820f | |||
| ec447e2e36 | |||
| 0af2647965 | |||
| 08442154f4 | |||
| 9f7d2234fb | |||
| ddd82a71a7 | |||
| 014ba3bf67 | |||
| c87321f804 | |||
| 8b451b3c67 | |||
| 0cfc87fbe6 | |||
| ae9673070c | |||
| 008027db0e | |||
| aec5e3473e | |||
| 95c8fde72f | |||
| 0f32968fae | |||
| ae79e9fea1 | |||
| 1a52aaf8d1 | |||
| d6c315ab8e | |||
| 983ce56048 | |||
| de2fe0e9f1 | |||
| c3c95bf291 | |||
| d2050b5948 | |||
| 6b92405bae | |||
| 49e87ccb15 | |||
| 50fffef13b | |||
| 82b1811971 | |||
| aeadef60bb | |||
| a1ab65a0e9 | |||
| 17e0805fe6 | |||
| ddd9c396b6 | |||
| ef49e507c1 | |||
| fbe74a5d80 | |||
| 076893981f | |||
| fac059f02c | |||
| 0313f8cc49 | |||
| 7ad6b73574 | |||
| 3cd0468b19 | |||
| f46ccb610e | |||
| 8a32569a3b | |||
| 535b23ae91 | |||
| 4715978f81 | |||
| a516aa7775 | |||
| 77e9c205f9 | |||
| e852305400 | |||
| c6a15264b3 | |||
| 2d0beaaaad | |||
| 5c5ef95d2b | |||
| e838e6f321 | |||
| ca983c72d4 | |||
| 91dd5256e9 | |||
| 3d4dc2d72b | |||
| ba3471068a | |||
| 5a7b2cf886 | |||
| cc926e84fb | |||
| fff16e6650 | |||
| d6f6d10cb6 | |||
| 1249904582 | |||
| 8e0437728b | |||
| fb0c0718e4 | |||
| aa8196db3a | |||
| b2223b5110 | |||
| 3837c5673a | |||
| 910a0860a0 | |||
| 009431fb98 | |||
| 579ece6256 | |||
| 822e97d3c3 | |||
| e0ae2ec42b | |||
| 859f6e2567 | |||
| 120d3c9dc8 | |||
| 342a95ddbe | |||
| 0b6134dd80 | |||
| e76854c23b | |||
| 24b98983cf | |||
| 3945f3cf38 | |||
| 5d7eb690e4 | |||
| bef180f4ba | |||
| e76e5abcf8 | |||
| 418f9c2662 | |||
| 716b72880a | |||
| 5de0fd792f | |||
| 13b557aba8 | |||
| 34dfc9add6 | |||
| 3a4575f251 | |||
| 178c2579d5 | |||
| 50be992b72 | |||
| d00f46eee1 | |||
| 44d6cba403 | |||
| 37bc5ff17b | |||
| e459bb04cc | |||
| 01eba88adf | |||
| 016fba5279 | |||
| da5d62ae03 | |||
| eb46c5eea6 | |||
| 100094e803 | |||
| e723cbf3b3 | |||
| 00d16ef59f | |||
| 5204ba5e24 | |||
| 629aabd3a3 | |||
| 1b9b9ed372 | |||
| b4e7f9046c | |||
| f09224d5c0 | |||
| 5f6ee33e2b | |||
| 635e2ba0e0 | |||
| 6109996ade | |||
| e4b80c9ab3 | |||
| 7521ad8bbb | |||
| b994065e18 | |||
| 5b1b84caff | |||
| d28a0e1dbb | |||
| 94d52df322 | |||
| 0ae3d36f0c | |||
| a7fb2b8a1a | |||
| 16d0dbab5b | |||
| 99c3050411 | |||
| 937265e828 | |||
| 60cd52a959 | |||
| b009501a53 | |||
| ee49e78dcd | |||
| 60aa919b14 | |||
| 8252a35771 | |||
| fc668c6880 | |||
| bb7f2a611a | |||
| c575c73764 | |||
| bd3ea721c3 | |||
| 82423ec467 | |||
| 64311e9652 | |||
| 77662b9c19 | |||
| b1031e3115 | |||
| 64c96f25d4 | |||
| 5ad42d6ca7 | |||
| 0386d4e88a | |||
| cda4512822 | |||
| eb6af4b4f0 | |||
| 4e51b128e6 | |||
| 0277263f98 | |||
| 99fb420d58 | |||
| a45c5da0a7 | |||
| ff1bc8a44a | |||
| 284bdc6e33 | |||
| 107360cd93 | |||
| abf9aa475b | |||
| 6156e04eb3 | |||
| b541c93797 | |||
| 6fd6413d6f | |||
| dfa38d3421 | |||
| 6e04b71c1a | |||
| 5afa541b30 | |||
| 9e5a093a3a | |||
| 2cf8e0291a | |||
| 53aa3bc3ae | |||
| 1ada5d9c2c | |||
| eb0910be57 | |||
| e9d5527482 | |||
| e6df764562 | |||
| 915bbbbde0 | |||
| f67e089ff3 | |||
| b841cc8b95 | |||
| 5f4b4baadb | |||
| 94b5a54655 | |||
| 95eb8b6ae4 | |||
| 8acbfa8967 | |||
| 1a115a8423 | |||
| 5a2172bb9b | |||
| 6b590671bc | |||
| cee1ab1347 | |||
| 9d0c6b9ef4 | |||
| 0e682bf630 | |||
| 8b95b300e2 | |||
| e1bd364278 | |||
| 7edc3427e1 | |||
| d3a3de2eac | |||
| bc2a8caf3e | |||
| d00446dc7b | |||
| 40ada1c31e | |||
| f24b2b9b4c | |||
| 2a644d7070 | |||
| ee0c1496e6 | |||
| 1da15783d5 | |||
| 6a925cb27f | |||
| 88ad64f113 | |||
| 1bc840430f | |||
| 76be8d5a87 | |||
| 8c4a54eb07 | |||
| dab5bee3c0 | |||
| bc8548fa1e | |||
| 476f919121 | |||
| 1c330d0301 | |||
| 47f0cd0b58 | |||
| f97be4e729 | |||
| 48b8dfe973 | |||
| 64b6c4d5f7 | |||
| fe16c66cf2 | |||
| 8d8695ba13 | |||
| e296256332 | |||
| 65111e87c1 | |||
| 95b1490f84 | |||
| 27a1f57ed3 | |||
| c6db6c5535 | |||
| bd22d3be36 | |||
| ba9d4587cb | |||
| 74c042a86b | |||
| 95fcd1dcc4 | |||
| 2de861d4c1 | |||
| e6d80c8ccb | |||
| 1aa2b3b065 | |||
| 88566719ec | |||
| 870e772da2 | |||
| e8e3ddceff | |||
| a5d1b76891 | |||
| e93f4e99f9 | |||
| 50aa891709 | |||
| 4b47e70b13 | |||
| c4acf774ec | |||
| 7ff1d50079 | |||
| 258b3cea66 | |||
| a3daa2d24f | |||
| 9f754ef0e9 | |||
| 773fbfc579 | |||
| 85fa9d942e | |||
| 83e782c7c5 | |||
| 9ee768551f | |||
| e45f8fa9ef | |||
| 891ea2da12 | |||
| 5e417f0714 | |||
| c53b579fca | |||
| ca9c390bb2 | |||
| 7d654f4a20 | |||
| b810bb01db | |||
| c2bd696bfe | |||
| 9fec315910 | |||
| 9a8a978e49 | |||
| e24b84e709 | |||
| 305b18ef57 | |||
| 22e9f53c42 | |||
| 6870e31a81 | |||
| e07d1e42e2 | |||
| c89caf7855 | |||
| 3b7c25b106 | |||
| 6079e1fa90 | |||
| 434466b306 | |||
| e1ac35f848 | |||
| 5994b22464 | |||
| bfc93158f5 | |||
| 29f99f0b20 | |||
| e4872131c8 | |||
| 16e1434f2a | |||
| 842badfa69 | |||
| 8ebc88aebb | |||
| c111ec9d91 | |||
| e85cdaf324 | |||
| 599d340a72 | |||
| 89b7fb8072 | |||
| dcaca2ecbd | |||
| b8725c96cd | |||
| 36930259d2 | |||
| 4397566f1e | |||
| 7c324869a4 | |||
| 7e80608066 | |||
| af7e44cf7c | |||
| 05099d066b | |||
| 937486a66b | |||
| e8de1f6d9c | |||
| cd9a5469fd | |||
| 1124f25ea3 | |||
| a79a87de4c | |||
| d2193bf428 | |||
| d9eab9f254 | |||
| 7d08ea8466 | |||
| ba1eb2fa73 | |||
| 4dbca6096f | |||
| 03be2d0492 | |||
| b0aca9de13 | |||
| 019a0297a9 | |||
| 0f93febd86 | |||
| d6c96b781f | |||
| 918bb94644 | |||
| a8774fa524 | |||
| e4b908ecde | |||
| 247ba40309 | |||
| fcf01ba677 | |||
| fb5a64c251 | |||
| 3d51ba0dc2 | |||
| 07636f51c4 | |||
| eff2050959 | |||
| c96a21cf99 | |||
| d4d847059a | |||
| 880d722912 | |||
| 1ef1053d3f | |||
| fa522a85d6 | |||
| f09e58c69c | |||
| 63e02492e8 | |||
| fd406eb3e6 | |||
| 88ade26ef7 | |||
| 3aea259e41 | |||
| 0f64767437 | |||
| d2430badbe | |||
| a880ed2b18 | |||
| e0f0fa9a2a | |||
| 5d2025aa43 | |||
| 8b7f5a765b | |||
| 9cd94004fc | |||
| 8042bca7cc | |||
| 8d89d158d1 | |||
| ccacdf274b | |||
| f1ceef05fc | |||
| c5697242ee | |||
| d0a48ab94b | |||
| 2b037d41ac | |||
| b7d38dd849 | |||
| 02d24139e9 | |||
| ad638e8bc8 | |||
| 76cb20debe | |||
| 3e9383e6d9 | |||
| aec8bf56a2 | |||
| 18335e3325 | |||
| bca9605d4a | |||
| 0321f0e979 | |||
| f97c2a36f6 | |||
| 5d945f5bc5 | |||
| 1c4975589f | |||
| fffe5c2c4b | |||
| 3a57e1c766 | |||
| 8b70882fec | |||
| 7fb7ba0d2b | |||
| 78514c6572 | |||
| 19393006ef | |||
| cb704c4551 | |||
| 04a09c3ce5 | |||
| ca8f978667 | |||
| 0cc91ac037 | |||
| 1b6f86669c | |||
| 264868bb6a | |||
| 02087a541e | |||
| dee0e37a85 | |||
| ea7a4a560b | |||
| f63e17775c | |||
| c4240d36f7 | |||
| ed13a0d14b | |||
| 8fa0be7633 | |||
| a99c022608 | |||
| 12bcbd28f3 | |||
| 4ece21cdf2 | |||
| a7642c2da4 | |||
| 32024cf2c5 | |||
| ef373caba7 | |||
| 396bd22199 | |||
| 4bff50a088 | |||
| dbc0ab76af | |||
| 289a8c14d3 | |||
| 44ed633cbf | |||
| 0a55d73146 | |||
| 40dda1150c | |||
| 09b61ec684 | |||
| e53467da22 | |||
| 09d27c0b05 | |||
| 3b18be5874 | |||
| ff15308c03 | |||
| fa3dc870d3 | |||
| 5e6ada140c | |||
| e8f7c1c832 | |||
| 266a11f64f | |||
| e442b92a5f | |||
| b337873ca2 | |||
| 896fff04aa | |||
| 6d0bca6d67 | |||
| d53c1ae2bd | |||
| afd73d53be | |||
| 652e55e80e | |||
| 9aa8e7edff | |||
| d67dfdf2e7 | |||
| 51f2d96ad6 | |||
| 30867b4ba1 | |||
| ec8d946a41 | |||
| 1eea935207 | |||
| 616990b930 | |||
| e5c31c9dd4 | |||
| 4f3f7d1edb | |||
| 86f13003b5 | |||
| 6d2431b683 | |||
| 57e17f2864 | |||
| bcc7d7770e | |||
| 89ea40d7f3 | |||
| 56b5008278 | |||
| 555778fca4 | |||
| ec1a6226a9 | |||
| 3c541ada89 | |||
| 5f677e71e9 | |||
| 5b3e66c4f6 | |||
| 9b0252fb75 | |||
| 2e3750c87c | |||
| 377d691053 | |||
| e90fe73aa2 | |||
| 3baa681c2b | |||
| a588bc4631 | |||
| b195c707b0 | |||
| 25ac84e5fd | |||
| 5e9df32bfa | |||
| 722feac8bd | |||
| 4be87a64b9 | |||
| 505ca6a58e | |||
| 5f1c8f3627 | |||
| 3834079481 | |||
| 9faa93e292 | |||
| 27609dc5e0 | |||
| 2b57d49e4e | |||
| dc0c738471 | |||
| ca41f4d4f2 | |||
| e1427f3ecb | |||
| e2fb9a66ad | |||
| 7278648642 | |||
| a4c955ce85 | |||
| f086027910 | |||
| 543b3bd937 | |||
| c0534a3b06 | |||
| 0c9785af36 | |||
| 482149139a | |||
| c89038f337 | |||
| 696d3ffabf | |||
| 14e5d0e740 | |||
| e4ae1dd475 | |||
| 46cd262fab | |||
| 3f5418083f | |||
| 1586c2f9e6 | |||
| e64b318a42 | |||
| 2033572c83 | |||
| ce678c1b76 | |||
| 83495b101c | |||
| 6c2a5f904d | |||
| c1251d3332 | |||
| 4ef1b7abe8 | |||
| d822e4ab3f | |||
| b01fe050d2 | |||
| 1a4cf211eb | |||
| 4541304fa8 | |||
| 894160f3f7 | |||
| 6a91bd53e2 | |||
| 0f013304ef | |||
| 6f4f4ccb16 | |||
| 7138ca1f5f | |||
| 45e7f6a0d1 | |||
| a7098df9cf | |||
| 054c7faaac | |||
| c3a4c659c0 | |||
| 087c85e586 | |||
| 43df188df1 | |||
| 1d40c6d068 | |||
| 6614242df6 | |||
| b25dc623cf | |||
| 9d3bb4e4e3 | |||
| 5dc11c285d | |||
| 575b4ce970 | |||
| e23821a7cb | |||
| e415258787 | |||
| eddfeb10a5 | |||
| 0361f8ad69 | |||
| 1451991d03 | |||
| 92fee08dc4 | |||
| 7b7e484091 | |||
| e7291a31f3 | |||
| 2dc31912cc | |||
| 428a8a10ff | |||
| 8b2f1965e2 | |||
| b18a99e4df | |||
| 1fac0c8640 | |||
| f0be73c2cd | |||
| 42bd632539 | |||
| f8014c6213 | |||
| 65f49489ad | |||
| e9e24d5f2d | |||
| 369b09972c | |||
| e4e2bdac72 | |||
| ec0db39184 | |||
| a02be78df5 | |||
| 3490abe9a7 | |||
| b5013426e6 | |||
| 7dd0881d29 | |||
| acf0562851 | |||
| 80c3a90d6f | |||
| c6985087a8 | |||
| ef50e2d5ce | |||
| ed1dc6e8d5 | |||
| c2d7a319a0 | |||
| 0e04298b7c | |||
| 3a5a73b02c | |||
| 901c7c1241 | |||
| 4e82369b16 | |||
| 945963db32 | |||
| 5741cbe756 | |||
| 6401aeb3a8 | |||
| 6a0c129d39 | |||
| bbec9ffcdf | |||
| 541f1fa2e3 | |||
| 7897820632 | |||
| 0dd9de2abc | |||
| 7131ba99b6 | |||
| c69026aa5b | |||
| 16ac96c64e | |||
| 1bb79b1c98 | |||
| eeee272f03 | |||
| 2563e9d1d6 | |||
| 24bec66390 | |||
| 3d30734dc2 | |||
| cbc1d53cc2 | |||
| 9a1f7a13f8 | |||
| d48b9b7f91 | |||
| 358865dc6a | |||
| e750c37473 | |||
| 71a589c10c | |||
| 3281239ff5 | |||
| 3e2cdddd5a | |||
| 1d4c3e51c7 | |||
| 1694c71528 | |||
| 169ffc1b0b | |||
| 89eb3259d4 | |||
| ffd88ffc66 | |||
| 6c2d13bd17 | |||
| 8d92a75ef0 | |||
| acb86ae266 | |||
| 473cf978b4 | |||
| 4debab2636 | |||
| e91e197873 | |||
| b30b6734a1 | |||
| dadb80608a | |||
| 16c9969fb9 | |||
| dc1644ed25 | |||
| 2cc9b3e1ed | |||
| 415f340a68 | |||
| 2d4869128d | |||
| ae8bc01d9b | |||
| e2552d9442 | |||
| 5d1b5d80b6 | |||
| 366804aa29 | |||
| 9240e0c903 | |||
| 7baaf2cff3 | |||
| 9fbc1ba031 | |||
| 6704c07db0 | |||
| a87165148a | |||
| ec4bcd093b | |||
| 5552055b98 | |||
| 03aa67034d | |||
| fc21427685 | |||
| 819b02a204 | |||
| de0bd5fd57 | |||
| 8aa1d94a1a | |||
| f8a59133a2 | |||
| c382f770dc | |||
| cde4ec13ef | |||
| ecad1ea839 | |||
| 6b91b22713 | |||
| e34c91b2cc | |||
| 822759a688 | |||
| b606037890 | |||
| 74d9b94119 | |||
| b1e9f955b0 | |||
| e0356fa360 | |||
| fbc67eeb98 | |||
| 09fd73b130 | |||
| 259a76f46b | |||
| c6504c2eaf | |||
| 7d104a1514 | |||
| b3bd61c89e | |||
| e49dca0275 | |||
| 03125b3a2d | |||
| a523379b3a | |||
| aa6348a29a | |||
| b9f0f1a69a | |||
| a284806d3c | |||
| 7e10c1db65 | |||
| 11790638d6 | |||
| 0583cbe266 | |||
| 2e6874c822 | |||
| 2ce41990bf | |||
| c8aeba38ba | |||
| 5e02502a5c | |||
| 382cc3d844 | |||
| dd74d9ee89 | |||
| 383f82807f | |||
| d4579a9a41 | |||
| 66a07c6a51 | |||
| 66ffd8e936 | |||
| 44f07ca231 | |||
| ff14f024af | |||
| dccf7c6c8d | |||
| 10d7955f99 | |||
| 64ade901de | |||
| eb0dd3f781 | |||
| 66e6cd80d3 | |||
| 959b32de1c | |||
| 9c6dc5b424 | |||
| aaab95d414 | |||
| 7d4e93912c | |||
| d65b463547 | |||
| 6e5a4bcb39 | |||
| 1a799dc30a | |||
| e3d2676858 | |||
| 42851686ca | |||
| e3943d868a | |||
| 7654b795c7 | |||
| 489244f1a9 | |||
| cbcce336d6 | |||
| 52a96b2a4f | |||
| ce6002a631 | |||
| 84a9cf069a | |||
| 83f19a7572 | |||
| a1a4c8b56d | |||
| d8901126d0 | |||
| 854db4ece8 | |||
| 07f2e65fc7 | |||
| ccf09f97d5 | |||
| 8f9a4ebc04 | |||
| f1833f13d5 | |||
| 6a81e369fa | |||
| 597e9e1ea9 | |||
| 9bb027ec4c | |||
| fbbbaa5d49 | |||
| aaec5a3fc9 | |||
| 7cd24cd51d | |||
| c81b34c1d0 | |||
| 7b1acc494d | |||
| 6ff90694e2 | |||
| 157c7c66b5 | |||
| 93249258c6 | |||
| 01c01a46fa | |||
| 0e2a10fe94 | |||
| 0b9f3de47c | |||
| bc239eead1 | |||
| 7a09869b0c | |||
| bdc0de6ada | |||
| 6870a7f9b1 | |||
| ace1a1b063 | |||
| d87b879cc3 | |||
| 65b36f8e69 | |||
| 87387a54f2 | |||
| b497cebe76 | |||
| 0fa107a75b | |||
| b34e3aeed0 | |||
| 35b18d72fd | |||
| 4b80f30afb | |||
| 86c54e04a8 | |||
| ef9fc596f5 | |||
| ad34e455ce | |||
| 01fdd0bee2 | |||
| 32ffa345cd | |||
| 6fc3c16073 | |||
| 7d58657c80 | |||
| fcd657c10e | |||
| 4ab77c5557 | |||
| 2bbaa500f4 | |||
| 722a20e141 | |||
| 041c24a837 | |||
| 39a3baa00b | |||
| f7acbb1eaa | |||
| e6fbf7aa5b | |||
| 87926e69db | |||
| 36a084eab6 | |||
| a9e319e0c0 | |||
| ea23b97231 | |||
| 7df76f9642 | |||
| f6db117a5e | |||
| 23c3cd605d | |||
| 77690702c0 | |||
| e0093480d9 | |||
| c7679b7a67 | |||
| e6ac34bde8 | |||
| 8c4b595c30 | |||
| be629e5c6b | |||
| 63569684a3 | |||
| 5937a0d7ce | |||
| 4512272c1c | |||
| ce1f3842e0 | |||
| ee01c3a059 | |||
| 81c1537bad | |||
| 98ecfab032 | |||
| b948b8c1a4 | |||
| f856c6ae37 | |||
| 2dd2580530 | |||
| 330755c63e | |||
| 9cf0174b41 | |||
| 16f572480a | |||
| b8a9e4f272 | |||
| c089bb3929 | |||
| 3caa1fc277 | |||
| 43b406592e | |||
| 1dd6674faa | |||
| 4674b52717 | |||
| cd5831251a | |||
| 45ec97066f | |||
| b08c0f145a | |||
| f0c100aee4 | |||
| 0e31ba212f | |||
| 692c906cd2 | |||
| 4f3837ac45 | |||
| ccb5125a48 | |||
| 1c356a41f5 | |||
| 6012d0577e | |||
| 5ec1dfa8b0 | |||
| 3754f09b2f | |||
| b9e0be4483 | |||
| e1d5d54cfb | |||
| e97c5c3b7e | |||
| 242b5afbe9 | |||
| 12e5835360 | |||
| 4af3cd158d | |||
| 2a37dfafa4 | |||
| 2cbb431acc | |||
| d92c6c0de9 | |||
| 19887c8f96 | |||
| 519ba79e1d | |||
| 4ccd18ca9f | |||
| b9aa00e8de | |||
| ded31980bf | |||
| 264e0d7ed9 | 
| @@ -1,3 +1,4 @@ | ||||
| public/env.sample.js | ||||
| public/workbox-*.js | ||||
| public/workbox-*.js.map | ||||
| .pnpm-store | ||||
| .yarn | ||||
| .pnp.* | ||||
							
								
								
									
										41
									
								
								.drone.yml
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								.drone.yml
									
									
									
									
									
								
							| @@ -1,41 +0,0 @@ | ||||
| --- | ||||
| kind: pipeline | ||||
| type: docker | ||||
| name: build:dev | ||||
|  | ||||
| steps: | ||||
|   - name: build dev | ||||
|     image: plugins/docker | ||||
|     depends_on: [clone] | ||||
|     settings: | ||||
|       username: | ||||
|         from_secret: DOCKER_REGISTRY_USER | ||||
|       password: | ||||
|         from_secret: DOCKER_REGISTRY_PASSWORD | ||||
|       repo: registry.odit.services/lfk/frontend | ||||
|       tags: | ||||
|         - dev | ||||
|       registry: registry.odit.services | ||||
|   - name: run full license export | ||||
|     depends_on: ["clone"] | ||||
|     image: node:alpine | ||||
|     commands: | ||||
|       - yarn | ||||
|       - yarn licenses:export | ||||
|   - name: push new licenses file to repo | ||||
|     depends_on: ["run full license export"] | ||||
|     image: appleboy/drone-git-push | ||||
|     settings: | ||||
|       branch: dev | ||||
|       commit: true | ||||
|       commit_message: new license file version [CI SKIP] | ||||
|       author_email: bot@odit.services | ||||
|       remote: git@git.odit.services:lfk/frontend.git | ||||
|       ssh_key: | ||||
|         from_secret: GITLAB_SSHKEY | ||||
|  | ||||
| trigger: | ||||
|   branch: | ||||
|     - dev | ||||
|   event: | ||||
|     - push | ||||
							
								
								
									
										33
									
								
								.gitea/workflows/release.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								.gitea/workflows/release.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| name: Build release images | ||||
| on: | ||||
|   push: | ||||
|     tags: | ||||
|       - "*.*.*" | ||||
|  | ||||
| jobs: | ||||
|   build-container: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - name: Checkout | ||||
|         uses: actions/checkout@v4 | ||||
|       - name: Set up Node.js | ||||
|         uses: actions/setup-node@v4 | ||||
|         with: | ||||
|           node-version: 19 | ||||
|       - run: npm i -g pnpm@10.8 && pnpm i | ||||
|       - run: pnpm licenses:export | ||||
|       - name: Login to registry | ||||
|         uses: docker/login-action@v3 | ||||
|         with: | ||||
|           registry: registry.odit.services | ||||
|           username: ${{ vars.REGISTRY_USERNAME }} | ||||
|           password: ${{ secrets.REGISTRY_PASSWORD }} | ||||
|       - name: Set up Docker Buildx | ||||
|         uses: docker/setup-buildx-action@v3 | ||||
|       - name: Build and push | ||||
|         uses: docker/build-push-action@v6 | ||||
|         with: | ||||
|           push: true | ||||
|           tags: | | ||||
|             ${{ vars.REGISTRY }}/lfk/frontend:${{ github.ref_name }} | ||||
|           platforms: linux/amd64,linux/arm64 | ||||
							
								
								
									
										14
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,10 +1,8 @@ | ||||
| .vscode | ||||
| .idea | ||||
| node_modules | ||||
| dist | ||||
| dist-ssr | ||||
| public/env.js | ||||
| /build | ||||
| yarn.lock | ||||
| package-lock.json | ||||
| *.map | ||||
| public/env.js | ||||
| public/index.html | ||||
| /dist | ||||
| .pnpm-store | ||||
| .yarn | ||||
| .pnp.* | ||||
							
								
								
									
										12
									
								
								.vscode/extensions.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								.vscode/extensions.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| { | ||||
|   "recommendations": [ | ||||
|     "2gua.rainbow-brackets", | ||||
|     "christian-kohler.npm-intellisense", | ||||
|     "remimarsal.prettier-now", | ||||
|     "svelte.svelte-vscode", | ||||
|     "lokalise.i18n-ally", | ||||
|     "fivethree.vscode-svelte-snippets", | ||||
|     "voorjaar.windicss-intellisense" | ||||
|   ], | ||||
|   "unwantedRecommendations": ["antfu.i18n-ally"] | ||||
| } | ||||
							
								
								
									
										7
									
								
								.vscode/i18n-ally-custom-framework.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								.vscode/i18n-ally-custom-framework.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| languageIds: | ||||
|   - javascript | ||||
|   - svelte | ||||
|   - html | ||||
| monopoly: false | ||||
| refactorTemplates: | ||||
|   - "{$_('$1')}" | ||||
							
								
								
									
										5
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| { | ||||
|   "i18n-ally.localesPaths": "src/locales", | ||||
|   "i18n-ally.keystyle": "nested", | ||||
|   "windicss.enableCodeFolding": false | ||||
| } | ||||
							
								
								
									
										2039
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										2039
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										25
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								Dockerfile
									
									
									
									
									
								
							| @@ -1,19 +1,16 @@ | ||||
| FROM node:15.4.0-alpine3.12 | ||||
| FROM registry.odit.services/hub/library/node:23.10.0-alpine3.21 AS build | ||||
| ARG NPM_REGISTRY_URL=https://registry.npmjs.org | ||||
| WORKDIR /app | ||||
| RUN npm i -g pnpm | ||||
| COPY package.json ./ | ||||
| RUN pnpm i | ||||
| COPY package.json *.config.js workbox-config.js ./ | ||||
|  | ||||
| COPY package.json pnpm-lock.yaml vite.config.js index.html ./ | ||||
| RUN npm config set registry $NPM_REGISTRY_URL && npm i -g pnpm@10.8 | ||||
| RUN mkdir /pnpm && pnpm config set store-dir /pnpm && pnpm i | ||||
|  | ||||
| COPY src ./src | ||||
| COPY public ./public | ||||
| RUN pnpm run build:sw | ||||
| RUN pnpm run build | ||||
| RUN pnpm build | ||||
|  | ||||
| # final image | ||||
| FROM alpine | ||||
| COPY --from=0 /app/build /app | ||||
| RUN rm -rf /app/build/_dist_/components | ||||
| RUN rm -rf /app/build/_dist_/locales | ||||
| RUN rm -rf /app/build-manifest.json | ||||
| FROM fholzer/nginx-brotli:v1.19.1 | ||||
| COPY --from=1 /app /usr/share/nginx/html | ||||
| FROM registry.odit.services/library/nginx-brotli:3.15 AS final | ||||
| COPY --from=build /app/dist /usr/share/nginx/html | ||||
| COPY ./nginx.conf /etc/nginx/nginx.conf | ||||
							
								
								
									
										362
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										362
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,362 @@ | ||||
| Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Creative | ||||
| Commons Corporation ("Creative Commons") is not a law firm and does not provide | ||||
| legal services or legal advice. Distribution of Creative Commons public licenses | ||||
| does not create a lawyer-client or other relationship. Creative Commons makes | ||||
| its licenses and related information available on an "as-is" basis. Creative | ||||
| Commons gives no warranties regarding its licenses, any material licensed | ||||
| under their terms and conditions, or any related information. Creative Commons | ||||
| disclaims all liability for damages resulting from their use to the fullest | ||||
| extent possible. | ||||
|  | ||||
| Using Creative Commons Public Licenses | ||||
|  | ||||
| Creative Commons public licenses provide a standard set of terms and conditions | ||||
| that creators and other rights holders may use to share original works of | ||||
| authorship and other material subject to copyright and certain other rights | ||||
| specified in the public license below. The following considerations are for | ||||
| informational purposes only, are not exhaustive, and do not form part of our | ||||
| licenses. | ||||
|  | ||||
| Considerations for licensors: Our public licenses are intended for use by | ||||
| those authorized to give the public permission to use material in ways otherwise | ||||
| restricted by copyright and certain other rights. Our licenses are irrevocable. | ||||
| Licensors should read and understand the terms and conditions of the license | ||||
| they choose before applying it. Licensors should also secure all rights necessary | ||||
| before applying our licenses so that the public can reuse the material as | ||||
| expected. Licensors should clearly mark any material not subject to the license. | ||||
| This includes other CC-licensed material, or material used under an exception | ||||
| or limitation to copyright. More considerations for licensors : wiki.creativecommons.org/Considerations_for_licensors | ||||
|  | ||||
| Considerations for the public: By using one of our public licenses, a licensor | ||||
| grants the public permission to use the licensed material under specified | ||||
| terms and conditions. If the licensor's permission is not necessary for any | ||||
| reason–for example, because of any applicable exception or limitation to copyright–then | ||||
| that use is not regulated by the license. Our licenses grant only permissions | ||||
| under copyright and certain other rights that a licensor has authority to | ||||
| grant. Use of the licensed material may still be restricted for other reasons, | ||||
| including because others have copyright or other rights in the material. A | ||||
| licensor may make special requests, such as asking that all changes be marked | ||||
| or described. Although not required by our licenses, you are encouraged to | ||||
| respect those requests where reasonable. More considerations for the public | ||||
| : wiki.creativecommons.org/Considerations_for_licensees | ||||
|  | ||||
| Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public | ||||
| License | ||||
|  | ||||
| By exercising the Licensed Rights (defined below), You accept and agree to | ||||
| be bound by the terms and conditions of this Creative Commons Attribution-NonCommercial-ShareAlike | ||||
| 4.0 International Public License ("Public License"). To the extent this Public | ||||
| License may be interpreted as a contract, You are granted the Licensed Rights | ||||
| in consideration of Your acceptance of these terms and conditions, and the | ||||
| Licensor grants You such rights in consideration of benefits the Licensor | ||||
| receives from making the Licensed Material available under these terms and | ||||
| conditions. | ||||
|  | ||||
| Section 1 – Definitions. | ||||
|  | ||||
| a. Adapted Material means material subject to Copyright and Similar Rights | ||||
| that is derived from or based upon the Licensed Material and in which the | ||||
| Licensed Material is translated, altered, arranged, transformed, or otherwise | ||||
| modified in a manner requiring permission under the Copyright and Similar | ||||
| Rights held by the Licensor. For purposes of this Public License, where the | ||||
| Licensed Material is a musical work, performance, or sound recording, Adapted | ||||
| Material is always produced where the Licensed Material is synched in timed | ||||
| relation with a moving image. | ||||
|  | ||||
| b. Adapter's License means the license You apply to Your Copyright and Similar | ||||
| Rights in Your contributions to Adapted Material in accordance with the terms | ||||
| and conditions of this Public License. | ||||
|  | ||||
| c. BY-NC-SA Compatible License means a license listed at creativecommons.org/compatiblelicenses, | ||||
| approved by Creative Commons as essentially the equivalent of this Public | ||||
| License. | ||||
|  | ||||
| d. Copyright and Similar Rights means copyright and/or similar rights closely | ||||
| related to copyright including, without limitation, performance, broadcast, | ||||
| sound recording, and Sui Generis Database Rights, without regard to how the | ||||
| rights are labeled or categorized. For purposes of this Public License, the | ||||
| rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. | ||||
|  | ||||
| e. Effective Technological Measures means those measures that, in the absence | ||||
| of proper authority, may not be circumvented under laws fulfilling obligations | ||||
| under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, | ||||
| and/or similar international agreements. | ||||
|  | ||||
| f. Exceptions and Limitations means fair use, fair dealing, and/or any other | ||||
| exception or limitation to Copyright and Similar Rights that applies to Your | ||||
| use of the Licensed Material. | ||||
|  | ||||
| g. License Elements means the license attributes listed in the name of a Creative | ||||
| Commons Public License. The License Elements of this Public License are Attribution, | ||||
| NonCommercial, and ShareAlike. | ||||
|  | ||||
| h. Licensed Material means the artistic or literary work, database, or other | ||||
| material to which the Licensor applied this Public License. | ||||
|  | ||||
| i. Licensed Rights means the rights granted to You subject to the terms and | ||||
| conditions of this Public License, which are limited to all Copyright and | ||||
| Similar Rights that apply to Your use of the Licensed Material and that the | ||||
| Licensor has authority to license. | ||||
|  | ||||
| j. Licensor means the individual(s) or entity(ies) granting rights under this | ||||
| Public License. | ||||
|  | ||||
| k. NonCommercial means not primarily intended for or directed towards commercial | ||||
| advantage or monetary compensation. For purposes of this Public License, the | ||||
| exchange of the Licensed Material for other material subject to Copyright | ||||
| and Similar Rights by digital file-sharing or similar means is NonCommercial | ||||
| provided there is no payment of monetary compensation in connection with the | ||||
| exchange. | ||||
|  | ||||
| l. Share means to provide material to the public by any means or process that | ||||
| requires permission under the Licensed Rights, such as reproduction, public | ||||
| display, public performance, distribution, dissemination, communication, or | ||||
| importation, and to make material available to the public including in ways | ||||
| that members of the public may access the material from a place and at a time | ||||
| individually chosen by them. | ||||
|  | ||||
| m. Sui Generis Database Rights means rights other than copyright resulting | ||||
| from Directive 96/9/EC of the European Parliament and of the Council of 11 | ||||
| March 1996 on the legal protection of databases, as amended and/or succeeded, | ||||
| as well as other essentially equivalent rights anywhere in the world. | ||||
|  | ||||
| n. You means the individual or entity exercising the Licensed Rights under | ||||
| this Public License. Your has a corresponding meaning. | ||||
|  | ||||
| Section 2 – Scope. | ||||
|  | ||||
|    a. License grant. | ||||
|  | ||||
| 1. Subject to the terms and conditions of this Public License, the Licensor | ||||
| hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, | ||||
| irrevocable license to exercise the Licensed Rights in the Licensed Material | ||||
| to: | ||||
|  | ||||
| A. reproduce and Share the Licensed Material, in whole or in part, for NonCommercial | ||||
| purposes only; and | ||||
|  | ||||
| B. produce, reproduce, and Share Adapted Material for NonCommercial purposes | ||||
| only. | ||||
|  | ||||
| 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions | ||||
| and Limitations apply to Your use, this Public License does not apply, and | ||||
| You do not need to comply with its terms and conditions. | ||||
|  | ||||
|       3. Term. The term of this Public License is specified in Section 6(a). | ||||
|  | ||||
| 4. Media and formats; technical modifications allowed. The Licensor authorizes | ||||
| You to exercise the Licensed Rights in all media and formats whether now known | ||||
| or hereafter created, and to make technical modifications necessary to do | ||||
| so. The Licensor waives and/or agrees not to assert any right or authority | ||||
| to forbid You from making technical modifications necessary to exercise the | ||||
| Licensed Rights, including technical modifications necessary to circumvent | ||||
| Effective Technological Measures. For purposes of this Public License, simply | ||||
| making modifications authorized by this Section 2(a)(4) never produces Adapted | ||||
| Material. | ||||
|  | ||||
|       5. Downstream recipients. | ||||
|  | ||||
| A. Offer from the Licensor – Licensed Material. Every recipient of the Licensed | ||||
| Material automatically receives an offer from the Licensor to exercise the | ||||
| Licensed Rights under the terms and conditions of this Public License. | ||||
|  | ||||
| B. Additional offer from the Licensor – Adapted Material. Every recipient | ||||
| of Adapted Material from You automatically receives an offer from the Licensor | ||||
| to exercise the Licensed Rights in the Adapted Material under the conditions | ||||
| of the Adapter's License You apply. | ||||
|  | ||||
| C. No downstream restrictions. You may not offer or impose any additional | ||||
| or different terms or conditions on, or apply any Effective Technological | ||||
| Measures to, the Licensed Material if doing so restricts exercise of the Licensed | ||||
| Rights by any recipient of the Licensed Material. | ||||
|  | ||||
| 6. No endorsement. Nothing in this Public License constitutes or may be construed | ||||
| as permission to assert or imply that You are, or that Your use of the Licensed | ||||
| Material is, connected with, or sponsored, endorsed, or granted official status | ||||
| by, the Licensor or others designated to receive attribution as provided in | ||||
| Section 3(a)(1)(A)(i). | ||||
|  | ||||
|    b. Other rights. | ||||
|  | ||||
| 1. Moral rights, such as the right of integrity, are not licensed under this | ||||
| Public License, nor are publicity, privacy, and/or other similar personality | ||||
| rights; however, to the extent possible, the Licensor waives and/or agrees | ||||
| not to assert any such rights held by the Licensor to the limited extent necessary | ||||
| to allow You to exercise the Licensed Rights, but not otherwise. | ||||
|  | ||||
| 2. Patent and trademark rights are not licensed under this Public License. | ||||
|  | ||||
| 3. To the extent possible, the Licensor waives any right to collect royalties | ||||
| from You for the exercise of the Licensed Rights, whether directly or through | ||||
| a collecting society under any voluntary or waivable statutory or compulsory | ||||
| licensing scheme. In all other cases the Licensor expressly reserves any right | ||||
| to collect such royalties, including when the Licensed Material is used other | ||||
| than for NonCommercial purposes. | ||||
|  | ||||
| Section 3 – License Conditions. | ||||
|  | ||||
| Your exercise of the Licensed Rights is expressly made subject to the following | ||||
| conditions. | ||||
|  | ||||
|    a. Attribution. | ||||
|  | ||||
| 1. If You Share the Licensed Material (including in modified form), You must: | ||||
|  | ||||
| A. retain the following if it is supplied by the Licensor with the Licensed | ||||
| Material: | ||||
|  | ||||
| i. identification of the creator(s) of the Licensed Material and any others | ||||
| designated to receive attribution, in any reasonable manner requested by the | ||||
| Licensor (including by pseudonym if designated); | ||||
|  | ||||
|             ii. a copyright notice; | ||||
|  | ||||
|             iii. a notice that refers to this Public License; | ||||
|  | ||||
|             iv. a notice that refers to the disclaimer of warranties; | ||||
|  | ||||
|              | ||||
|  | ||||
| v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; | ||||
|  | ||||
| B. indicate if You modified the Licensed Material and retain an indication | ||||
| of any previous modifications; and | ||||
|  | ||||
| C. indicate the Licensed Material is licensed under this Public License, and | ||||
| include the text of, or the URI or hyperlink to, this Public License. | ||||
|  | ||||
| 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner | ||||
| based on the medium, means, and context in which You Share the Licensed Material. | ||||
| For example, it may be reasonable to satisfy the conditions by providing a | ||||
| URI or hyperlink to a resource that includes the required information. | ||||
|  | ||||
| 3. If requested by the Licensor, You must remove any of the information required | ||||
| by Section 3(a)(1)(A) to the extent reasonably practicable. | ||||
|  | ||||
| b. ShareAlike.In addition to the conditions in Section 3(a), if You Share | ||||
| Adapted Material You produce, the following conditions also apply. | ||||
|  | ||||
| 1. The Adapter's License You apply must be a Creative Commons license with | ||||
| the same License Elements, this version or later, or a BY-NC-SA Compatible | ||||
| License. | ||||
|  | ||||
| 2. You must include the text of, or the URI or hyperlink to, the Adapter's | ||||
| License You apply. You may satisfy this condition in any reasonable manner | ||||
| based on the medium, means, and context in which You Share Adapted Material. | ||||
|  | ||||
| 3. You may not offer or impose any additional or different terms or conditions | ||||
| on, or apply any Effective Technological Measures to, Adapted Material that | ||||
| restrict exercise of the rights granted under the Adapter's License You apply. | ||||
|  | ||||
| Section 4 – Sui Generis Database Rights. | ||||
|  | ||||
| Where the Licensed Rights include Sui Generis Database Rights that apply to | ||||
| Your use of the Licensed Material: | ||||
|  | ||||
| a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, | ||||
| reuse, reproduce, and Share all or a substantial portion of the contents of | ||||
| the database for NonCommercial purposes only; | ||||
|  | ||||
| b. if You include all or a substantial portion of the database contents in | ||||
| a database in which You have Sui Generis Database Rights, then the database | ||||
| in which You have Sui Generis Database Rights (but not its individual contents) | ||||
| is Adapted Material, including for purposes of Section 3(b); and | ||||
|  | ||||
| c. You must comply with the conditions in Section 3(a) if You Share all or | ||||
| a substantial portion of the contents of the database. | ||||
|  | ||||
| For the avoidance of doubt, this Section 4 supplements and does not replace | ||||
| Your obligations under this Public License where the Licensed Rights include | ||||
| other Copyright and Similar Rights. | ||||
|  | ||||
| Section 5 – Disclaimer of Warranties and Limitation of Liability. | ||||
|  | ||||
| a. Unless otherwise separately undertaken by the Licensor, to the extent possible, | ||||
| the Licensor offers the Licensed Material as-is and as-available, and makes | ||||
| no representations or warranties of any kind concerning the Licensed Material, | ||||
| whether express, implied, statutory, or other. This includes, without limitation, | ||||
| warranties of title, merchantability, fitness for a particular purpose, non-infringement, | ||||
| absence of latent or other defects, accuracy, or the presence or absence of | ||||
| errors, whether or not known or discoverable. Where disclaimers of warranties | ||||
| are not allowed in full or in part, this disclaimer may not apply to You. | ||||
|  | ||||
| b. To the extent possible, in no event will the Licensor be liable to You | ||||
| on any legal theory (including, without limitation, negligence) or otherwise | ||||
| for any direct, special, indirect, incidental, consequential, punitive, exemplary, | ||||
| or other losses, costs, expenses, or damages arising out of this Public License | ||||
| or use of the Licensed Material, even if the Licensor has been advised of | ||||
| the possibility of such losses, costs, expenses, or damages. Where a limitation | ||||
| of liability is not allowed in full or in part, this limitation may not apply | ||||
| to You. | ||||
|  | ||||
| c. The disclaimer of warranties and limitation of liability provided above | ||||
| shall be interpreted in a manner that, to the extent possible, most closely | ||||
| approximates an absolute disclaimer and waiver of all liability. | ||||
|  | ||||
| Section 6 – Term and Termination. | ||||
|  | ||||
| a. This Public License applies for the term of the Copyright and Similar Rights | ||||
| licensed here. However, if You fail to comply with this Public License, then | ||||
| Your rights under this Public License terminate automatically. | ||||
|  | ||||
| b. Where Your right to use the Licensed Material has terminated under Section | ||||
| 6(a), it reinstates: | ||||
|  | ||||
| 1. automatically as of the date the violation is cured, provided it is cured | ||||
| within 30 days of Your discovery of the violation; or | ||||
|  | ||||
|       2. upon express reinstatement by the Licensor. | ||||
|  | ||||
| For the avoidance of doubt, this Section 6(b) does not affect any right the | ||||
| Licensor may have to seek remedies for Your violations of this Public License. | ||||
|  | ||||
| c. For the avoidance of doubt, the Licensor may also offer the Licensed Material | ||||
| under separate terms or conditions or stop distributing the Licensed Material | ||||
| at any time; however, doing so will not terminate this Public License. | ||||
|  | ||||
|    d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. | ||||
|  | ||||
| Section 7 – Other Terms and Conditions. | ||||
|  | ||||
| a. The Licensor shall not be bound by any additional or different terms or | ||||
| conditions communicated by You unless expressly agreed. | ||||
|  | ||||
| b. Any arrangements, understandings, or agreements regarding the Licensed | ||||
| Material not stated herein are separate from and independent of the terms | ||||
| and conditions of this Public License. | ||||
|  | ||||
| Section 8 – Interpretation. | ||||
|  | ||||
| a. For the avoidance of doubt, this Public License does not, and shall not | ||||
| be interpreted to, reduce, limit, restrict, or impose conditions on any use | ||||
| of the Licensed Material that could lawfully be made without permission under | ||||
| this Public License. | ||||
|  | ||||
| b. To the extent possible, if any provision of this Public License is deemed | ||||
| unenforceable, it shall be automatically reformed to the minimum extent necessary | ||||
| to make it enforceable. If the provision cannot be reformed, it shall be severed | ||||
| from this Public License without affecting the enforceability of the remaining | ||||
| terms and conditions. | ||||
|  | ||||
| c. No term or condition of this Public License will be waived and no failure | ||||
| to comply consented to unless expressly agreed to by the Licensor. | ||||
|  | ||||
| d. Nothing in this Public License constitutes or may be interpreted as a limitation | ||||
| upon, or waiver of, any privileges and immunities that apply to the Licensor | ||||
| or You, including from the legal processes of any jurisdiction or authority. | ||||
|  | ||||
| Creative Commons is not a party to its public licenses. Notwithstanding, Creative | ||||
| Commons may elect to apply one of its public licenses to material it publishes | ||||
| and in those instances will be considered the "Licensor." The text of the | ||||
| Creative Commons public licenses is dedicated to the public domain under the | ||||
| CC0 Public Domain Dedication. Except for the limited purpose of indicating | ||||
| that material is shared under a Creative Commons public license or as otherwise | ||||
| permitted by the Creative Commons policies published at creativecommons.org/policies, | ||||
| Creative Commons does not authorize the use of the trademark "Creative Commons" | ||||
| or any other trademark or logo of Creative Commons without its prior written | ||||
| consent including, without limitation, in connection with any unauthorized | ||||
| modifications to any of its public licenses or any other arrangements, understandings, | ||||
| or agreements concerning use of licensed material. For the avoidance of doubt, | ||||
| this paragraph does not form part of the public licenses. | ||||
|  | ||||
| Creative Commons may be contacted at creativecommons.org. | ||||
							
								
								
									
										27
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| # @odit/lfk-frontend | ||||
|  | ||||
| ## ✒️ Overview | ||||
|  | ||||
| This is an API client for [https://git.odit.services/lfk/backend](@lfk/backend) | ||||
|  | ||||
| This application is intended for use by admin users/ members only. | ||||
|  | ||||
| ## 🚀 Getting Started | ||||
|  | ||||
| ``` | ||||
| pnpm i | ||||
| ``` | ||||
|  | ||||
| ## Development | ||||
|  | ||||
| ``` | ||||
| pnpm dev | ||||
| / | ||||
| pnpm dev --open | ||||
| ``` | ||||
|  | ||||
| ## Build | ||||
|  | ||||
| ``` | ||||
| pnpm build | ||||
| ``` | ||||
| @@ -1,8 +1,8 @@ | ||||
| version: "3" | ||||
| services: | ||||
|     httpd: | ||||
|         build: . | ||||
|         volumes: | ||||
|             - ./public/env.sample.js:/usr/share/nginx/html/env.js | ||||
|         ports: | ||||
|             - 4050:80 | ||||
|   httpd: | ||||
|     build: . | ||||
|     volumes: | ||||
|       - ./public/env.sample.js:/usr/share/nginx/html/env.js | ||||
|     ports: | ||||
|       - 4050:80 | ||||
|   | ||||
| @@ -1,6 +0,0 @@ | ||||
| const config = { | ||||
| 	baseurl: 'https://dev.lauf-fuer-kaya.de', | ||||
| 	fallback_username: 'admin', | ||||
| 	fallback_password: '72fpTzsev4xUu78QPs2FCbwZ3', | ||||
| 	prefersHashRouting: true | ||||
| }; | ||||
							
								
								
									
										22
									
								
								index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								index.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| <!doctype html> | ||||
| <html lang="en"> | ||||
|   <head> | ||||
|     <meta charset="utf-8" /> | ||||
|     <link rel="icon" href="/favicon.png" /> | ||||
|     <link rel="manifest" href="/manifest.webmanifest" /> | ||||
|     <link rel="apple-touch-icon" href="/lfk-logo.png" /> | ||||
|     <meta name="theme-color" content="#FFFFFF" /> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" /> | ||||
|     <meta name="description" content="Lauf Für Kaya! - Admin" /> | ||||
|     <title>Lauf für Kaya! - Admin</title> | ||||
|   </head> | ||||
|  | ||||
|   <body> | ||||
|     <span style="display: none; visibility: hidden" id="buildinfo" | ||||
|       >RELEASE_INFO-1.13.4-RELEASE_INFO</span | ||||
|     > | ||||
|     <noscript>You need to enable JavaScript to run this app.</noscript> | ||||
|     <script src="/env.js"></script> | ||||
|     <script type="module" src="/src/main.js"></script> | ||||
|   </body> | ||||
| </html> | ||||
							
								
								
									
										16
									
								
								nginx.conf
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								nginx.conf
									
									
									
									
									
								
							| @@ -6,10 +6,24 @@ http { | ||||
|     server { | ||||
|         error_page 404 /index.html; | ||||
|         root /usr/share/nginx/html; | ||||
|         location /assets { | ||||
|             expires 1y; | ||||
|             log_not_found off; | ||||
|             access_log off; | ||||
|         } | ||||
|         location = /index.html { | ||||
|             add_header Cache-Control 'no-store'; | ||||
|         } | ||||
|         location = / { | ||||
|             add_header Cache-Control 'no-store'; | ||||
|         } | ||||
|         location = /env.js { | ||||
|             add_header Cache-Control 'no-store'; | ||||
|         } | ||||
|         location / { | ||||
|             try_files $uri $uri/ /index.html; | ||||
|         } | ||||
|         location ~* \.(?:ico|css|js|gif|jpe?g|png)$ { | ||||
|         location ~* \.(?:ico|css|gif|jpe?g|png)$ { | ||||
|             expires 1y; | ||||
|             add_header Pragma public; | ||||
|             add_header Cache-Control "public"; | ||||
|   | ||||
							
								
								
									
										28
									
								
								order.js
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								order.js
									
									
									
									
									
								
							| @@ -1,16 +1,18 @@ | ||||
| const fs = require('fs'); | ||||
| import fs from "fs"; | ||||
| // get all language files | ||||
| const files = fs.readdirSync('./src/locales/'); | ||||
| const files = fs.readdirSync("./src/locales/"); | ||||
| files.forEach((f) => { | ||||
| 	// read file as object | ||||
| 	const unordered = JSON.parse(fs.readFileSync(`src/locales/${f}`)); | ||||
| 	// order object by keys alpabetically A-Z | ||||
| 	const ordered = Object.keys(unordered).sort().reduce((obj, key) => { | ||||
| 		obj[key] = unordered[key]; | ||||
| 		return obj; | ||||
| 	}, {}); | ||||
| 	// format output as json for commit diff compatibility | ||||
| 	const out = JSON.stringify(ordered, 0, 4); | ||||
| 	// write output file | ||||
| 	fs.writeFileSync(`src/locales/${f}`, out); | ||||
|   // read file as object | ||||
|   const unordered = JSON.parse(fs.readFileSync(`src/locales/${f}`)); | ||||
|   // order object by keys alpabetically A-Z | ||||
|   const ordered = Object.keys(unordered) | ||||
|     .sort() | ||||
|     .reduce((obj, key) => { | ||||
|       obj[key] = unordered[key]; | ||||
|       return obj; | ||||
|     }, {}); | ||||
|   // format output as json for commit diff compatibility | ||||
|   const out = JSON.stringify(ordered, 0, 4); | ||||
|   // write output file | ||||
|   fs.writeFileSync(`src/locales/${f}`, out); | ||||
| }); | ||||
|   | ||||
							
								
								
									
										119
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										119
									
								
								package.json
									
									
									
									
									
								
							| @@ -1,56 +1,67 @@ | ||||
| { | ||||
| 	"name": "@odit/lfk-frontend", | ||||
| 	"version": "0.1.2-1", | ||||
| 	"scripts": { | ||||
| 		"i18n-order": "node order.js", | ||||
| 		"dev": "snowpack dev", | ||||
| 		"build": "snowpack build", | ||||
| 		"build:sw": "workbox generateSW workbox-config.js", | ||||
| 		"release": "release-it", | ||||
| 		"changelog": "npx auto-changelog --commit-limit false --template https://raw.githubusercontent.com/release-it/release-it/master/templates/changelog-compact.hbs", | ||||
| 		"licenses:export": "license-exporter --json -o public" | ||||
| 	}, | ||||
| 	"dependencies": { | ||||
| 		"@odit/lfk-client-js": "0.0.10", | ||||
| 		"filepond": "4.25.1", | ||||
| 		"gridjs": "3.2.1", | ||||
| 		"localforage": "1.9.0", | ||||
| 		"svelte-filepond": "0.0.1", | ||||
| 		"svelte-focus-trap": "1.0.1", | ||||
| 		"svelte-i18n": "3.3.0", | ||||
| 		"tailwindcss": "2.0.2", | ||||
| 		"tinro": "0.5.6", | ||||
| 		"toastify-js": "1.9.3", | ||||
| 		"validator": "13.5.2" | ||||
| 	}, | ||||
| 	"devDependencies": { | ||||
| 		"@odit/license-exporter": "0.0.9", | ||||
| 		"@snowpack/plugin-svelte": "3.4.1", | ||||
| 		"auto-changelog": "^2.2.1", | ||||
| 		"autoprefixer": "10.2.1", | ||||
| 		"postcss": "8.2.4", | ||||
| 		"postcss-load-config": "3.0.0", | ||||
| 		"release-it": "^14.2.2", | ||||
| 		"snowpack": "3.0.0-rc.2", | ||||
| 		"svelte": "3.31.2", | ||||
| 		"svelte-preprocess": "4.6.1", | ||||
| 		"workbox-cli": "6.0.2" | ||||
| 	}, | ||||
| 	"release-it": { | ||||
| 		"git": { | ||||
| 			"commit": true, | ||||
| 			"requireCleanWorkingDir": false, | ||||
| 			"commitMessage": "🚀RELEASE v${version}", | ||||
| 			"push": false, | ||||
| 			"tag": true, | ||||
| 			"tagName": null, | ||||
| 			"tagAnnotation": "v${version}" | ||||
| 		}, | ||||
| 		"npm": { | ||||
| 			"publish": false | ||||
| 		}, | ||||
| 		"hooks": { | ||||
| 			"after:bump": "npx auto-changelog --commit-limit false -p -u --hide-credit  && git add CHANGELOG.md" | ||||
| 		} | ||||
| 	} | ||||
|   "name": "@odit/lfk-frontend", | ||||
|   "version": "1.13.4", | ||||
|   "type": "module", | ||||
|   "scripts": { | ||||
|     "i18n-order": "node order.js", | ||||
|     "dev": "vite", | ||||
|     "format": "prettier --write --plugin-search-dir=. .", | ||||
|     "build": "vite build", | ||||
|     "release": "release-it --only-version", | ||||
|     "licenses:export": "license-exporter --json -o public" | ||||
|   }, | ||||
|   "license": "CC-BY-NC-SA-4.0", | ||||
|   "devDependencies": { | ||||
|     "@odit/license-exporter": "0.2.0", | ||||
|     "@sveltejs/vite-plugin-svelte": "2.1.1", | ||||
|     "@types/papaparse": "^5.3.15", | ||||
|     "@types/underscore": "^1.13.0", | ||||
|     "auto-changelog": "2.5.0", | ||||
|     "prettier": "3.5.3", | ||||
|     "prettier-plugin-svelte": "3.3.3", | ||||
|     "release-it": "17.10.0", | ||||
|     "svelte-select": "3.17.0", | ||||
|     "vite": "6.3.2", | ||||
|     "vite-plugin-mkcert": "^1.17.8" | ||||
|   }, | ||||
|   "release-it": { | ||||
|     "git": { | ||||
|       "commit": true, | ||||
|       "requireCleanWorkingDir": false, | ||||
|       "commitMessage": "chore(release): ${version}", | ||||
|       "push": true, | ||||
|       "tag": true, | ||||
|       "tagName": "${version}", | ||||
|       "tagAnnotation": "${version}" | ||||
|     }, | ||||
|     "npm": { | ||||
|       "publish": false | ||||
|     }, | ||||
|     "hooks": { | ||||
|       "after:bump": "npx auto-changelog --commit-limit false -p -u --hide-credit && git add CHANGELOG.md && node versionbuilder.js  && git add index.html && node order.js  && git add src/locales" | ||||
|     } | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@bwip-js/browser": "^4.6.0", | ||||
|     "@fontsource/athiti": "^5.2.5", | ||||
|     "@odit/lfk-client-js": "1.2.7", | ||||
|     "@paralleldrive/cuid2": "2.2.2", | ||||
|     "@tailwindcss/vite": "^4.1.4", | ||||
|     "@tanstack/svelte-table": "8.9.1", | ||||
|     "check-password-strength": "2.0.10", | ||||
|     "html5-qrcode": "^2.3.8", | ||||
|     "localforage": "1.10.0", | ||||
|     "papaparse": "^5.5.2", | ||||
|     "svelte": "3.58.0", | ||||
|     "svelte-french-toast": "1.2.0", | ||||
|     "svelte-i18n": "4.0.1", | ||||
|     "tailwindcss": "^4.1.4", | ||||
|     "tinro": "0.6.12", | ||||
|     "underscore": "^1.13.7", | ||||
|     "validator": "13.15.0", | ||||
|     "xlsx": "0.18.5" | ||||
|   }, | ||||
|   "volta": { | ||||
|     "node": "20.0.0" | ||||
|   } | ||||
| } | ||||
|   | ||||
							
								
								
									
										4154
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										4154
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										3
									
								
								pnpm-workspace.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								pnpm-workspace.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| onlyBuiltDependencies: | ||||
|   - es5-ext | ||||
|   - esbuild | ||||
							
								
								
									
										
											BIN
										
									
								
								public/beep.mp3
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								public/beep.mp3
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -1,5 +1,12 @@ | ||||
| const config = { | ||||
| 	baseurl: 'http://localhost:4010', | ||||
| 	// optional | ||||
| 	prefersHashRouting: true | ||||
|   baseurl: "http://localhost:4010", | ||||
|   baseurl_selfservice: "http://localhost:5174", | ||||
|   baseurl_documentserver: "http://localhost:4010/documents", | ||||
|   documentserver_key: | ||||
|     "NqZSYTy5AFQ7MppbLW5moqpTk7u7YrNUHKYhKYuThnnya2WpCOIU694hIZT1FzYe", | ||||
|   // optional | ||||
|   default_username: "demo", | ||||
|   default_password: "demo", | ||||
|   prefersHashRouting: true, | ||||
| }; | ||||
| window.config = config; | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								public/error.mp3
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								public/error.mp3
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -1,21 +0,0 @@ | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
|  | ||||
| <head> | ||||
|   <meta charset="utf-8" /> | ||||
|   <link rel="icon" href="/favicon.png" /> | ||||
|   <link rel="manifest" href="/manifest.webmanifest"> | ||||
|   <link rel="apple-touch-icon" href="/lfk-logo.png"> | ||||
|   <meta name="theme-color" content="#FFFFFF"> | ||||
|   <meta name="viewport" content="width=device-width, initial-scale=1" /> | ||||
|   <meta name="description" content="Lauf Für Kaya! - Admin" /> | ||||
|   <title>Lauf für Kaya! - Admin</title> | ||||
| </head> | ||||
|  | ||||
| <body> | ||||
|   <noscript>You need to enable JavaScript to run this app.</noscript> | ||||
|   <script src="/env.js"></script> | ||||
|   <script defer type="module" src="/_dist_/index.js"></script> | ||||
| </body> | ||||
|  | ||||
| </html> | ||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -1,4 +0,0 @@ | ||||
| <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 98.1 118"> | ||||
|   <path fill="#ff3e00" d="M91.8 15.6C80.9-.1 59.2-4.7 43.6 5.2L16.1 22.8A31.25 31.25 0 001.9 43.9c-1.3 7.3-.2 14.8 3.3 21.3-2.4 3.6-4 7.6-4.7 11.8-1.6 8.9.5 18.1 5.7 25.4 11 15.7 32.6 20.3 48.2 10.4l27.5-17.5c7.5-4.7 12.7-12.4 14.2-21.1 1.3-7.3.2-14.8-3.3-21.3 2.4-3.6 4-7.6 4.7-11.8 1.7-9-.4-18.2-5.7-25.5"/> | ||||
|   <path fill="#fff" d="M40.9 103.9a21.8 21.8 0 01-23.4-8.7c-3.2-4.4-4.4-9.9-3.5-15.3l.6-2.6.5-1.6 1.4 1c3.3 2.4 6.9 4.2 10.8 5.4l1 .3-.1 1c-.1 1.4.3 2.9 1.1 4.1a6.62 6.62 0 008.8 2L65.5 72c1.4-.9 2.3-2.2 2.6-3.8.3-1.6-.1-3.3-1-4.6a6.56 6.56 0 00-8.8-1.9l-10.5 6.7a18.6 18.6 0 01-5.6 2.4 21.8 21.8 0 01-23.4-8.7 20.2 20.2 0 01-3.4-15.3c.9-5.2 4.1-9.9 8.6-12.7l27.5-17.5c1.7-1.1 3.6-1.9 5.6-2.5a21.8 21.8 0 0123.4 8.7c3.2 4.4 4.4 9.9 3.5 15.3-.2.9-.4 1.7-.7 2.6l-.5 1.6-1.4-1c-3.3-2.4-6.9-4.2-10.8-5.4l-1-.3.1-1c.1-1.4-.3-2.9-1.1-4.1a6.56 6.56 0 00-8.8-1.9L32.4 46.1c-1.4.9-2.3 2.2-2.6 3.8s.1 3.3 1 4.6a6.56 6.56 0 008.8 1.9l10.5-6.7c1.7-1.1 3.6-1.9 5.6-2.5a21.8 21.8 0 0123.4 8.7c3.2 4.4 4.4 9.9 3.5 15.3-.9 5.2-4.1 9.9-8.6 12.7l-27.5 17.5c-1.7 1.1-3.6 1.9-5.6 2.5"/> | ||||
| </svg> | ||||
| Before Width: | Height: | Size: 1.1 KiB | 
| @@ -1,27 +1,39 @@ | ||||
| { | ||||
| 	"name": "Lauf für Kaya! - Admin", | ||||
| 	"short_name": "LfK!Admin", | ||||
| 	"start_url": ".", | ||||
| 	"display": "standalone", | ||||
| 	"background_color": "#fff", | ||||
| 	"theme_color": "#fff", | ||||
| 	"description": "Lauf für Kaya! - Admin", | ||||
| 	"icons": [ | ||||
| 		{ | ||||
| 			"src": "/favicon.png", | ||||
| 			"sizes": "48x48", | ||||
| 			"type": "image/png" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"src": "/favicon.png", | ||||
| 			"sizes": "144x144", | ||||
| 			"type": "image/png" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"src": "/lfk-logo.png", | ||||
| 			"sizes": "1540x144", | ||||
| 			"type": "image/png" | ||||
| 		}, | ||||
| 		{ "src": "/maskable_icon_x1.png", "sizes": "750x750", "type": "image/png", "purpose": "any maskable" } | ||||
| 	] | ||||
|   "name": "Lauf für Kaya! - Admin", | ||||
|   "short_name": "LfK!Admin", | ||||
|   "start_url": "/?utm_source=pwa", | ||||
|   "orientation": "portrait-primary", | ||||
|   "display": "standalone", | ||||
|   "background_color": "#fff", | ||||
|   "theme_color": "#fff", | ||||
|   "description": "Lauf für Kaya! - Admin", | ||||
|   "shortcuts": [ | ||||
|     { | ||||
|       "name": "Users", | ||||
|       "url": "/users/?utm_source=pwa" | ||||
|     } | ||||
|   ], | ||||
|   "icons": [ | ||||
|     { | ||||
|       "src": "/favicon.png", | ||||
|       "sizes": "48x48", | ||||
|       "type": "image/png" | ||||
|     }, | ||||
|     { | ||||
|       "src": "/favicon.png", | ||||
|       "sizes": "144x144", | ||||
|       "type": "image/png" | ||||
|     }, | ||||
|     { | ||||
|       "src": "/lfk-logo.png", | ||||
|       "sizes": "1540x144", | ||||
|       "type": "image/png" | ||||
|     }, | ||||
|     { | ||||
|       "src": "/maskable_icon_x1.png", | ||||
|       "sizes": "750x750", | ||||
|       "type": "image/png", | ||||
|       "purpose": "any maskable" | ||||
|     } | ||||
|   ] | ||||
| } | ||||
|   | ||||
| @@ -1,2 +0,0 @@ | ||||
| if(!self.define){const e=e=>{"require"!==e&&(e+=".js");let r=Promise.resolve();return i[e]||(r=new Promise((async r=>{if("document"in self){const i=document.createElement("script");i.src=e,document.head.appendChild(i),i.onload=r}else importScripts(e),r()}))),r.then((()=>{if(!i[e])throw new Error(`Module ${e} didn’t register its module`);return i[e]}))},r=(r,i)=>{Promise.all(r.map(e)).then((e=>i(1===e.length?e[0]:e)))},i={require:Promise.resolve(r)};self.define=(r,s,o)=>{i[r]||(i[r]=Promise.resolve().then((()=>{let i={};const c={uri:location.origin+r.slice(1)};return Promise.all(s.map((r=>{switch(r){case"exports":return i;case"module":return c;default:return e(r)}}))).then((e=>{const r=o(...e);return i.default||(i.default=r),i}))})))}}define("./sw.js",["./workbox-c8ead010"],(function(e){"use strict";self.addEventListener("message",(e=>{e.data&&"SKIP_WAITING"===e.data.type&&self.skipWaiting()})),e.precacheAndRoute([{url:"favicon.ico",revision:"ba44f340afba5bb1a07f14decc15dd04"},{url:"favicon.png",revision:"07a9941cec62319578fa2a1734db9959"},{url:"favicon.svg",revision:"689d6c6fda51e359c0e5725d9e905064"},{url:"index.html",revision:"931c34f3675364dcc09411aa0f223776"},{url:"logo.svg",revision:"4c9e31a1f4268d7e36e22cda7656e561"},{url:"manifest.webmanifest",revision:"75c93eb352c4877216e77b1d7f73445f"},{url:"robots.txt",revision:"61c27d2cd39a713f7829422c3d9edcc7"}],{})})); | ||||
| //# sourceMappingURL=sw.js.map | ||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -1,30 +0,0 @@ | ||||
| /** @type {import("snowpack").SnowpackUserConfig } */ | ||||
| module.exports = { | ||||
| 	mount: { | ||||
| 		public: '/', | ||||
| 		src: '/_dist_' | ||||
| 	}, | ||||
| 	plugins: [ '@snowpack/plugin-svelte' ], | ||||
| 	install: [ | ||||
| 		/* ... */ | ||||
| 	], | ||||
| 	installOptions: { | ||||
| 		/* ... */ | ||||
| 		sourceMap: false | ||||
| 	}, | ||||
| 	devOptions: { | ||||
| 		/* ... */ | ||||
| 	}, | ||||
| 	buildOptions: { | ||||
| 		/* ... */ | ||||
| 	}, | ||||
| 	proxy: { | ||||
| 		/* ... */ | ||||
| 	}, | ||||
| 	alias: { | ||||
| 		/* ... */ | ||||
| 	}, | ||||
| 	experiments: { | ||||
| 		optimize: { bundle: true, minify: true } | ||||
| 	} | ||||
| }; | ||||
							
								
								
									
										188
									
								
								src/App.svelte
									
									
									
									
									
								
							
							
						
						
									
										188
									
								
								src/App.svelte
									
									
									
									
									
								
							| @@ -1,6 +1,4 @@ | ||||
| <script> | ||||
|   import "./TailwindStyles.svelte"; | ||||
|   import "toastify-js/src/toastify.css"; | ||||
|   import { Route, router } from "tinro"; | ||||
|   router.subscribe((routeInfo) => { | ||||
|     window.scrollTo(0, 0); | ||||
| @@ -24,40 +22,70 @@ | ||||
|     name: "lfk_admin", | ||||
|     version: 1.0, | ||||
|     storeName: "lfk_admin", | ||||
|     description: "LfK! admin dashbaord", | ||||
|     description: "LfK! admin dashboard", | ||||
|   }); | ||||
|   window.onunhandledrejection = (event) => { | ||||
|     if (event.reason.toString() == "Error: Unauthorized") { | ||||
|       localForage.clear(); | ||||
|       location.replace("/"); | ||||
|     } | ||||
|   }; | ||||
|   // | ||||
|   import Login from "./components/Login.svelte"; | ||||
|   import Dashboard from "./components/Dashboard.svelte"; | ||||
|   import Login from "./components/auth/Login.svelte"; | ||||
|   import Dashboard from "./components/dashboard/Dashboard.svelte"; | ||||
|   import store from "./store.js"; | ||||
|   import NotFound from "./components/NotFound.svelte"; | ||||
|   import ForgotPassword from "./components/ForgotPassword.svelte"; | ||||
|   import MainDashContent from "./components/MainDashContent.svelte"; | ||||
|   import Users from "./components/Users.svelte"; | ||||
|   import About from "./components/About.svelte"; | ||||
|   import Settings from "./components/Settings.svelte"; | ||||
|   import Transition from "./components/Transition.svelte"; | ||||
|   import Orgs from "./components/Orgs.svelte"; | ||||
|   import Runners from "./components/Runners.svelte"; | ||||
|   import Footer from "./components/Footer.svelte"; | ||||
|   import Tracks from "./components/Tracks.svelte"; | ||||
|   import TracksOverview from "./components/TracksOverview.svelte"; | ||||
|   import OrgDetail from "./components/OrgDetail.svelte"; | ||||
|   import Teams from "./components/Teams.svelte"; | ||||
|   import { OpenAPI, AuthService } from "@odit/lfk-client-js"; | ||||
|   import UserDetail from "./components/UserDetail.svelte"; | ||||
|   import ForgotPassword from "./components/auth/ForgotPassword.svelte"; | ||||
|   import MainDashContent from "./components/dashboard/MainDashContent.svelte"; | ||||
|   import Users from "./components/users/Users.svelte"; | ||||
|   import About from "./components/general/About.svelte"; | ||||
|   import Settings from "./components/settings/Settings.svelte"; | ||||
|   import Transition from "./components/base/Transition.svelte"; | ||||
|   import Orgs from "./components/orgs/Orgs.svelte"; | ||||
|   import CardAssignment from "./components/tools/CardAssignment.svelte"; | ||||
|   import Runners from "./components/runners/Runners.svelte"; | ||||
|   import Footer from "./components/general/Footer.svelte"; | ||||
|   import TracksOverview from "./components/tracks/TracksOverview.svelte"; | ||||
|   import OrgDetail from "./components/orgs/OrgDetail.svelte"; | ||||
|   import Teams from "./components/teams/Teams.svelte"; | ||||
|   import { OpenAPI } from "@odit/lfk-client-js"; | ||||
|   import UserDetail from "./components/users/UserDetail.svelte"; | ||||
|   OpenAPI.BASE = config.baseurl; | ||||
|   import { register as registerSW } from "./swmodule"; | ||||
|   import TeamDetail from "./components/teams/TeamDetail.svelte"; | ||||
|   import UserPermissions from "./components/users/UserPermissions.svelte"; | ||||
|   import GroupPermissions from "./components/groups/GroupPermissions.svelte"; | ||||
|   import RunnerDetail from "./components/runners/RunnerDetail.svelte"; | ||||
|   import ResetPassword from "./components/auth/ResetPassword.svelte"; | ||||
|   import Contacts from "./components/contacts/Contacts.svelte"; | ||||
|   import ContactDetail from "./components/contacts/ContactDetail.svelte"; | ||||
|   import Donors from "./components/donors/Donors.svelte"; | ||||
|   import Groups from "./components/groups/Groups.svelte"; | ||||
|   import DonorDetail from "./components/donors/DonorDetail.svelte"; | ||||
|   import Donations from "./components/donations/Donations.svelte"; | ||||
|   import DonationDetail from "./components/donations/DonationDetail.svelte"; | ||||
|   import GroupDetail from "./components/groups/GroupDetail.svelte"; | ||||
|   import ScanStations from "./components/scanstations/ScanStations.svelte"; | ||||
|   import ScanStationDetail from "./components/scanstations/ScanStationDetail.svelte"; | ||||
|   import Scans from "./components/scans/Scans.svelte"; | ||||
|   import ScanDetail from "./components/scans/ScanDetail.svelte"; | ||||
|   import Cards from "./components/cards/Cards.svelte"; | ||||
|   import StatsClients from "./components/statsclients/StatsClients.svelte"; | ||||
|   import StatsClientDetail from "./components/statsclients/StatsClientDetail.svelte"; | ||||
|   import CardReplacement from "./components/tools/CardReplacement.svelte"; | ||||
|   import ScanClient from "./components/tools/ScanClient.svelte"; | ||||
|   import DonationCreate from "./components/tools/DonationCreate.svelte"; | ||||
|   store.init(); | ||||
|   // registerSW(); | ||||
| </script> | ||||
|  | ||||
| <Route> | ||||
|   {#if $router.path === '/forgot_password'} | ||||
|   {#if $router.path === "/forgot_password"} | ||||
|     <Route path="/forgot_password"> | ||||
|       <ForgotPassword /> | ||||
|     </Route> | ||||
|   {:else if $router.path === '/about'} | ||||
|   {:else if $router.path.includes("/reset")} | ||||
|     <Route path="/reset/:resetkey" let:params> | ||||
|       <ResetPassword {params} /> | ||||
|     </Route> | ||||
|   {:else if $router.path === "/about"} | ||||
|     <Route path="/about"> | ||||
|       <About /> | ||||
|     </Route> | ||||
| @@ -71,8 +99,26 @@ | ||||
|           <Route path="/"> | ||||
|             <Users /> | ||||
|           </Route> | ||||
|           <Route path="/:userid" let:params> | ||||
|             <UserDetail {params} /> | ||||
|           <Route path="/:userid/*" let:params> | ||||
|             <Route path="/"> | ||||
|               <UserDetail {params} /> | ||||
|             </Route> | ||||
|             <Route path="/permissions/"> | ||||
|               <UserPermissions {params} /> | ||||
|             </Route> | ||||
|           </Route> | ||||
|         </Route> | ||||
|         <Route path="/groups/*"> | ||||
|           <Route path="/"> | ||||
|             <Groups /> | ||||
|           </Route> | ||||
|           <Route path="/:groupid/*" let:params> | ||||
|             <Route path="/"> | ||||
|               <GroupDetail {params} /> | ||||
|             </Route> | ||||
|             <Route path="/permissions/"> | ||||
|               <GroupPermissions {params} /> | ||||
|             </Route> | ||||
|           </Route> | ||||
|         </Route> | ||||
|         <Route path="/tracks/*"> | ||||
| @@ -81,11 +127,43 @@ | ||||
|           </Route> | ||||
|           <Route path="/:trackid" let:params /> | ||||
|         </Route> | ||||
|         <Route path="/runners"> | ||||
|           <Runners /> | ||||
|         <Route path="/runners/*"> | ||||
|           <Route path="/" let:meta> | ||||
|             <Runners created_via={meta.query.created_via} /> | ||||
|           </Route> | ||||
|           <Route path="/:runnerid" let:params> | ||||
|             <RunnerDetail {params} /> | ||||
|           </Route> | ||||
|         </Route> | ||||
|         <Route path="/teams"> | ||||
|           <Teams /> | ||||
|         <Route path="/tools/*"> | ||||
|           <Route path="/cardassignment/"> | ||||
|               <CardAssignment /> | ||||
|           </Route> | ||||
|           <Route path="/cardreplacement/"> | ||||
|               <CardReplacement /> | ||||
|           </Route> | ||||
|           <Route path="/scanclient/"> | ||||
|               <ScanClient /> | ||||
|           </Route> | ||||
|           <Route path="/donationcreate/"> | ||||
|               <DonationCreate /> | ||||
|           </Route> | ||||
|         </Route> | ||||
|         <Route path="/teams/*"> | ||||
|           <Route path="/"> | ||||
|             <Teams /> | ||||
|           </Route> | ||||
|           <Route path="/:teamid" let:params> | ||||
|             <TeamDetail {params} /> | ||||
|           </Route> | ||||
|         </Route> | ||||
|         <Route path="/contacts/*"> | ||||
|           <Route path="/"> | ||||
|             <Contacts /> | ||||
|           </Route> | ||||
|           <Route path="/:contact" let:params> | ||||
|             <ContactDetail {params} /> | ||||
|           </Route> | ||||
|         </Route> | ||||
|         <Route path="/orgs/*"> | ||||
|           <Route path="/"> | ||||
| @@ -95,6 +173,54 @@ | ||||
|             <OrgDetail {params} /> | ||||
|           </Route> | ||||
|         </Route> | ||||
|         <Route path="/donors/*"> | ||||
|           <Route path="/"> | ||||
|             <Donors /> | ||||
|           </Route> | ||||
|           <Route path="/:donorid" let:params> | ||||
|             <DonorDetail {params} /> | ||||
|           </Route> | ||||
|         </Route> | ||||
|         <Route path="/donations/*"> | ||||
|           <Route path="/"> | ||||
|             <Donations /> | ||||
|           </Route> | ||||
|           <Route path="/:donationid" let:params> | ||||
|             <DonationDetail {params} /> | ||||
|           </Route> | ||||
|         </Route> | ||||
|         <Route path="/cards/*"> | ||||
|           <Route path="/"> | ||||
|             <Cards /> | ||||
|           </Route> | ||||
|           <!-- <Route path="/:scanid" let:params> | ||||
|             <ScanDetail {params} /> | ||||
|           </Route> --> | ||||
|         </Route> | ||||
|         <Route path="/scans/*"> | ||||
|           <Route path="/"> | ||||
|             <Scans /> | ||||
|           </Route> | ||||
|           <Route path="/:scanid" let:params> | ||||
|             <ScanDetail {params} /> | ||||
|           </Route> | ||||
|         </Route> | ||||
|         <Route path="/scanstations/*"> | ||||
|           <Route path="/"> | ||||
|             <ScanStations /> | ||||
|           </Route> | ||||
|           <Route path="/:stationid" let:params> | ||||
|             <ScanStationDetail {params} /> | ||||
|           </Route> | ||||
|         </Route> | ||||
|         <Route path="/statsclients/*"> | ||||
|           <Route path="/"> | ||||
|             <StatsClients /> | ||||
|           </Route> | ||||
|           <Route path="/:clientid" let:params> | ||||
|             <StatsClientDetail {params} /> | ||||
|           </Route> | ||||
|         </Route> | ||||
|         <Route path="/about"> | ||||
|           <About /> | ||||
|         </Route> | ||||
|   | ||||
| @@ -1,6 +0,0 @@ | ||||
| <style global> | ||||
|   /*! @import */ | ||||
|   @tailwind base; | ||||
|   @tailwind components; | ||||
|   @tailwind utilities; | ||||
| </style> | ||||
| @@ -1,197 +0,0 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import { clickOutside } from "./outsideclick"; | ||||
|   import { focusTrap } from "svelte-focus-trap"; | ||||
|   export let modal_open; | ||||
|   (function () { | ||||
|     document.onkeydown = function (e) { | ||||
|       e = e || window.event; | ||||
|       if (e.key === "Escape") { | ||||
|         modal_open = false; | ||||
|       } | ||||
|     }; | ||||
|   })(); | ||||
|   const license_promise = fetch("/licenses.json"); | ||||
|   let licenses = []; | ||||
|   $: currentlicense = ""; | ||||
|   $: licensetext = ""; | ||||
|   license_promise | ||||
|     .then((response) => response.json()) | ||||
|     .then((json) => { | ||||
|       licenses = json; | ||||
|     }); | ||||
| </script> | ||||
|  | ||||
| {#if modal_open} | ||||
|   <div | ||||
|     class="fixed z-10 inset-0 overflow-y-auto" | ||||
|     use:focusTrap | ||||
|     use:clickOutside | ||||
|     on:click_outside={() => { | ||||
|       modal_open = false; | ||||
|     }}> | ||||
|     <div | ||||
|       class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"> | ||||
|       <div class="fixed inset-0 transition-opacity" aria-hidden="true"> | ||||
|         <div | ||||
|           class="absolute inset-0 bg-gray-500 opacity-75" | ||||
|           data-id="modal_backdrop" /> | ||||
|       </div> | ||||
|       <span | ||||
|         class="hidden sm:inline-block sm:align-middle sm:h-screen" | ||||
|         aria-hidden="true">​</span> | ||||
|       <div | ||||
|         class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full" | ||||
|         role="dialog" | ||||
|         aria-modal="true" | ||||
|         aria-labelledby="modal-headline"> | ||||
|         <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4"> | ||||
|           <div class="sm:flex sm:items-start"> | ||||
|             <div | ||||
|               class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10"> | ||||
|               <svg | ||||
|                 class="h-6 w-6 text-blue-600" | ||||
|                 fill="none" | ||||
|                 width="24" | ||||
|                 height="24" | ||||
|                 xmlns="http://www.w3.org/2000/svg" | ||||
|                 viewBox="0 0 640 512"><path | ||||
|                   fill="currentColor" | ||||
|                   d="M635.7 167.2L556.1 31.7c-8.8-15-28.3-20.1-43.5-11.5l-69 39.1L503.3 161c2.2 3.8.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9L416 75l-55.2 31.3 27.9 47.4c2.2 3.8.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9L333.2 122 278 153.3 337.8 255c2.2 3.7.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9l-59.7-101.7-55.2 31.3 27.9 47.4c2.2 3.8.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9l-27.9-47.5-55.2 31.3 59.7 101.7c2.2 3.7.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9L84.9 262.9l-69 39.1C.7 310.7-4.6 329.8 4.2 344.8l79.6 135.6c8.8 15 28.3 20.1 43.5 11.5L624.1 210c15.2-8.6 20.4-27.8 11.6-42.8z" /></svg> | ||||
|             </div> | ||||
|             <div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left"> | ||||
|               <h3 class="text-lg leading-6 font-medium"> | ||||
|                 {$_('read-license')} | ||||
|               </h3> | ||||
|               <div class="mt-2 mb-6"> | ||||
|                 <p class="text-sm text-gray-500">{currentlicense}</p> | ||||
|               </div> | ||||
|               <div class="mt-2 mb-6"> | ||||
|                 <p class="text-sm text-gray-500">{licensetext}</p> | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse"> | ||||
|           <button | ||||
|             on:click={() => { | ||||
|               modal_open = false; | ||||
|             }} | ||||
|             type="button" | ||||
|             class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm"> | ||||
|             Close | ||||
|           </button> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| {/if} | ||||
| <!-- /// --> | ||||
| <div class="pt-12 px-4 sm:px-6 lg:px-8 lg:pt-20 bg-gray-900 pb-12"> | ||||
|   <div class="text-center mb-8"> | ||||
|     <h1 | ||||
|       class="mt-9 font-display text-4xl leading-none font-semibold text-white sm:text-5xl lg:text-6xl"> | ||||
|       {$_('about')} | ||||
|       🧾 | ||||
|     </h1> | ||||
|     <p | ||||
|       class="mt-2 max-w-xl mx-auto text-xl lg:max-w-3xl lg:text-2xl text-gray-300"> | ||||
|       Lauf für Kaya! | ||||
|       <strong class="text-white font-medium"> | ||||
|         {$_('by')} | ||||
|         <a href="https://odit.services" class="underline">ODIT.Services</a> | ||||
|       </strong> | ||||
|       <br /> | ||||
|       <span class="text-lg">{$_('lfk-is-os')}</span> | ||||
|     </p> | ||||
|   </div> | ||||
| </div> | ||||
|  | ||||
| <div class="pt-0 pb-16 overflow-hidden lg:pt-12 lg:py-24"> | ||||
|   <div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8"> | ||||
|     <h2 class="text-4xl font-display font-semibold md:text-5xl"> | ||||
|       {$_('credits')} | ||||
|     </h2> | ||||
|     <div | ||||
|       class="max-w-3xl mx-auto text-xl leading-8 font-medium mt-8"> | ||||
|       <p class="text-center">{$_('oss_credit_description')}</p> | ||||
|     </div> | ||||
|     <div class="w-screen leading-8 pl-5 mt-5"> | ||||
|       {#await license_promise} | ||||
|         <p class="text-center w-full">Licenses are being loaded...</p> | ||||
|       {:then} | ||||
|         <table> | ||||
|           <thead> | ||||
|             <tr> | ||||
|               <th>{$_('dependency_name')}</th> | ||||
|               <th>{$_('license')}</th> | ||||
|               <th>{$_('repo_link')}</th> | ||||
|               <th>{$_('installed-version')}</th> | ||||
|               <th>{$_('author')}</th> | ||||
|             </tr> | ||||
|           </thead> | ||||
|           <tbody> | ||||
|             {#each licenses as l} | ||||
|               <tr> | ||||
|                 <td>{l.name}</td> | ||||
|                 <td> | ||||
|                   {l.license || '?'}<br /><span | ||||
|                     class="underline cursor-pointer" | ||||
|                     on:click={() => { | ||||
|                       modal_open = true; | ||||
|                       currentlicense = l.name + '@' + l.version; | ||||
|                       licensetext = l.licensetext || $_('no-license-text-could-be-found'); | ||||
|                     }}>{$_('read-license')}</span> | ||||
|                 </td> | ||||
|                 <td> | ||||
|                   {(l.repo?.url || l.repo) | ||||
|                     .replace('git+', '') | ||||
|                     .replace('git://', '')} | ||||
|                 </td> | ||||
|                 <td>{l.version || '?'}</td> | ||||
|                 <td>{l.author?.name || l.author || '?'}</td> | ||||
|               </tr> | ||||
|             {/each} | ||||
|           </tbody> | ||||
|         </table> | ||||
|       {:catch error} | ||||
|         <div | ||||
|           class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-500"> | ||||
|           <span class="inline-block align-middle mr-8"> | ||||
|             <b class="capitalize">{$_('general_promise_error')}</b> | ||||
|             {error} | ||||
|           </span> | ||||
|         </div> | ||||
|       {/await} | ||||
|     </div> | ||||
|     <h2 class="text-4xl font-display font-semibold md:text-5xl"> | ||||
|       Fragen | ||||
|     </h2> | ||||
|     <div class="mt-6 border-t-2 border-gray-100 pt-10"> | ||||
|       <dl class="md:grid md:grid-cols-2 md:gap-8"> | ||||
|         <div> | ||||
|           <div> | ||||
|             <dt class="text-lg leading-6 font-medium">Q</dt> | ||||
|             <dd class="mt-2"> | ||||
|               <p class="text-base text-gray-500">A</p> | ||||
|             </dd> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div class="mt-12 sm:mt-0"> | ||||
|           <div id="team-pricing"> | ||||
|             <dt class="text-lg leading-6 font-medium">Q</dt> | ||||
|             <dd class="mt-2"> | ||||
|               <p class="text-base text-gray-500">A</p> | ||||
|             </dd> | ||||
|           </div> | ||||
|           <div class="mt-12"> | ||||
|             <dt class="text-lg leading-6 font-medium">Q</dt> | ||||
|             <dd class="mt-2"> | ||||
|               <p class="text-base text-gray-500">A</p> | ||||
|             </dd> | ||||
|           </div> | ||||
|         </div> | ||||
|       </dl> | ||||
|     </div> | ||||
|   </div> | ||||
| </div> | ||||
| @@ -1,264 +0,0 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import { clickOutside } from "./outsideclick"; | ||||
|   import { focusTrap } from "svelte-focus-trap"; | ||||
|   import { tracks as tracksstore } from "../store.js"; | ||||
|   import { TrackService, UserService } from "@odit/lfk-client-js"; | ||||
|   import isEmail from "validator/es/lib/isEmail"; | ||||
|   import Toastify from "toastify-js"; | ||||
|   import "toastify-js/src/toastify.css"; | ||||
|   import About from "./About.svelte"; | ||||
|   export let modal_open; | ||||
|   let firstname_input; | ||||
|   let lastname_input; | ||||
|   let middlename_input; | ||||
|   let password_input; | ||||
|   let email_input; | ||||
|   function focus(el) { | ||||
|     el.focus(); | ||||
|   } | ||||
|   $: middlename_input_value = ""; | ||||
|   $: password_input_value = ""; | ||||
|   $: email_input_value = ""; | ||||
|   $: lastname_input_value = ""; | ||||
|   $: firstname_input_value = ""; | ||||
|   $: track_min_duration = 0; | ||||
|   $: tracklength = 0; | ||||
|   $: processed_last_submit = true; | ||||
|   $: isPasswordValid = password_input_value.trim().length === 0; | ||||
|   $: isEmailValid = isEmail(email_input_value); | ||||
|   $: isLastnameValid = lastname_input_value.trim().length === 0; | ||||
|   $: isFirstnameValid = firstname_input_value.trim().length === 0; | ||||
|   $: createbtnenabled = !isFirstnameValid && !isLastnameValid; | ||||
|   (function () { | ||||
|     document.onkeydown = function (e) { | ||||
|       e = e || window.event; | ||||
|       if (e.key === "Escape") { | ||||
|         modal_open = false; | ||||
|       } | ||||
|       if (e.keyCode === 13) { | ||||
|         if (createbtnenabled === true) { | ||||
|           createbtnenabled = false; | ||||
|           submit(); | ||||
|         } | ||||
|       } | ||||
|     }; | ||||
|   })(); | ||||
|   function submit() { | ||||
|     if (processed_last_submit === true) { | ||||
|       processed_last_submit = false; | ||||
|       const toast = Toastify({ | ||||
|         text: "User is being added...", | ||||
|         duration: -1, | ||||
|       }).showToast(); | ||||
|       UserService.userControllerPost({ | ||||
|         firstname: firstname_input_value, | ||||
|         lastname: lastname_input_value, | ||||
|         middlename: middlename_input_value, | ||||
|         email:email_input_value,password:password_input_value | ||||
|       }) | ||||
|         .then((result) => { | ||||
|           firstname_input_value = ""; | ||||
|           lastname_input_value = ""; | ||||
|           middlename_input_value = ""; | ||||
|           modal_open = false; | ||||
|           // | ||||
|           Toastify({ | ||||
|             text: "User added", | ||||
|             duration: 500, | ||||
|             backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", | ||||
|           }).showToast(); | ||||
|           let storeval = []; | ||||
|           tracksstore.subscribe((val) => { | ||||
|             storeval = val; | ||||
|           }); | ||||
|           storeval.push(result); | ||||
|           tracksstore.set(storeval); | ||||
|         }) | ||||
|         .catch((err) => { | ||||
|           // | ||||
|         }) | ||||
|         .finally(() => { | ||||
|           processed_last_submit = true; | ||||
|           // | ||||
|           toast.hideToast(); | ||||
|         }); | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| {#if modal_open} | ||||
|   <div | ||||
|     class="fixed z-10 inset-0 overflow-y-auto" | ||||
|     use:focusTrap | ||||
|     use:clickOutside | ||||
|     on:click_outside={() => { | ||||
|       modal_open = false; | ||||
|     }}> | ||||
|     <div | ||||
|       class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"> | ||||
|       <div class="fixed inset-0 transition-opacity" aria-hidden="true"> | ||||
|         <div | ||||
|           class="absolute inset-0 bg-gray-500 opacity-75" | ||||
|           data-id="modal_backdrop" /> | ||||
|       </div> | ||||
|       <span | ||||
|         class="hidden sm:inline-block sm:align-middle sm:h-screen" | ||||
|         aria-hidden="true">​</span> | ||||
|       <div | ||||
|         class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full" | ||||
|         role="dialog" | ||||
|         aria-modal="true" | ||||
|         aria-labelledby="modal-headline"> | ||||
|         <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4"> | ||||
|           <div class="sm:flex sm:items-start"> | ||||
|             <div | ||||
|               class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10"> | ||||
|               <svg | ||||
|                 class="h-6 w-6 text-blue-600" | ||||
|                 fill="currentColor" | ||||
|                 width="24" | ||||
|                 height="24" | ||||
|                 xmlns="http://www.w3.org/2000/svg" | ||||
|                 viewBox="0 0 640 512"><path | ||||
|                   fill="currentColor" | ||||
|                   d="M610.5 341.3c2.6-14.1 2.6-28.5 0-42.6l25.8-14.9c3-1.7 4.3-5.2 3.3-8.5-6.7-21.6-18.2-41.2-33.2-57.4-2.3-2.5-6-3.1-9-1.4l-25.8 14.9c-10.9-9.3-23.4-16.5-36.9-21.3v-29.8c0-3.4-2.4-6.4-5.7-7.1-22.3-5-45-4.8-66.2 0-3.3.7-5.7 3.7-5.7 7.1v29.8c-13.5 4.8-26 12-36.9 21.3l-25.8-14.9c-2.9-1.7-6.7-1.1-9 1.4-15 16.2-26.5 35.8-33.2 57.4-1 3.3.4 6.8 3.3 8.5l25.8 14.9c-2.6 14.1-2.6 28.5 0 42.6l-25.8 14.9c-3 1.7-4.3 5.2-3.3 8.5 6.7 21.6 18.2 41.1 33.2 57.4 2.3 2.5 6 3.1 9 1.4l25.8-14.9c10.9 9.3 23.4 16.5 36.9 21.3v29.8c0 3.4 2.4 6.4 5.7 7.1 22.3 5 45 4.8 66.2 0 3.3-.7 5.7-3.7 5.7-7.1v-29.8c13.5-4.8 26-12 36.9-21.3l25.8 14.9c2.9 1.7 6.7 1.1 9-1.4 15-16.2 26.5-35.8 33.2-57.4 1-3.3-.4-6.8-3.3-8.5l-25.8-14.9zM496 368.5c-26.8 0-48.5-21.8-48.5-48.5s21.8-48.5 48.5-48.5 48.5 21.8 48.5 48.5-21.7 48.5-48.5 48.5zM96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm224 32c1.9 0 3.7-.5 5.6-.6 8.3-21.7 20.5-42.1 36.3-59.2 7.4-8 17.9-12.6 28.9-12.6 6.9 0 13.7 1.8 19.6 5.3l7.9 4.6c.8-.5 1.6-.9 2.4-1.4 7-14.6 11.2-30.8 11.2-48 0-61.9-50.1-112-112-112S208 82.1 208 144c0 61.9 50.1 112 112 112zm105.2 194.5c-2.3-1.2-4.6-2.6-6.8-3.9-8.2 4.8-15.3 9.8-27.5 9.8-10.9 0-21.4-4.6-28.9-12.6-18.3-19.8-32.3-43.9-40.2-69.6-10.7-34.5 24.9-49.7 25.8-50.3-.1-2.6-.1-5.2 0-7.8l-7.9-4.6c-3.8-2.2-7-5-9.8-8.1-3.3.2-6.5.6-9.8.6-24.6 0-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h255.4c-3.7-6-6.2-12.8-6.2-20.3v-9.2zM173.1 274.6C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z" /></svg> | ||||
|             </div> | ||||
|             <div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left"> | ||||
|               <h3 class="text-lg leading-6 font-medium text-gray-900"> | ||||
|                 Create a new User | ||||
|               </h3> | ||||
|               <div class="mt-2 mb-6"> | ||||
|                 <p class="text-sm text-gray-500"> | ||||
|                   Please provide the required information to add a new user. | ||||
|                 </p> | ||||
|               </div> | ||||
|               <div class="grid grid-cols-6 gap-6"> | ||||
|                 <div class="col-span-6"> | ||||
|                   <label | ||||
|                     for="firstname" | ||||
|                     class="block text-sm font-medium text-gray-700">First Name</label> | ||||
|                   <input | ||||
|                     use:focus | ||||
|                     autocomplete="off" | ||||
|                     placeholder="First Name" | ||||
|                     class:border-red-500={isFirstnameValid} | ||||
|                     class:focus:border-red-500={isFirstnameValid} | ||||
|                     class:focus:ring-red-500={isFirstnameValid} | ||||
|                     bind:value={firstname_input_value} | ||||
|                     bind:this={firstname_input} | ||||
|                     type="text" | ||||
|                     name="firstname" | ||||
|                     class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> | ||||
|                   {#if isFirstnameValid} | ||||
|                     <span | ||||
|                       class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> | ||||
|                       First Name is required | ||||
|                     </span> | ||||
|                   {/if} | ||||
|                 </div> | ||||
|                 <div class="col-span-6"> | ||||
|                   <label | ||||
|                     for="trackname" | ||||
|                     class="block text-sm font-medium text-gray-700">Middle Name</label> | ||||
|                   <input | ||||
|                     autocomplete="off" | ||||
|                     placeholder="Middle Name" | ||||
|                     bind:value={middlename_input_value} | ||||
|                     bind:this={middlename_input} | ||||
|                     type="text" | ||||
|                     name="trackname" | ||||
|                     class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> | ||||
|                 </div> | ||||
|                 <div class="col-span-6"> | ||||
|                   <label | ||||
|                     for="lastname" | ||||
|                     class="block text-sm font-medium text-gray-700">Last Name</label> | ||||
|                   <input | ||||
|                     autocomplete="off" | ||||
|                     placeholder="Last Name" | ||||
|                     class:border-red-500={isLastnameValid} | ||||
|                     class:focus:border-red-500={isLastnameValid} | ||||
|                     class:focus:ring-red-500={isLastnameValid} | ||||
|                     bind:value={lastname_input_value} | ||||
|                     bind:this={lastname_input} | ||||
|                     type="text" | ||||
|                     name="lastname" | ||||
|                     class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> | ||||
|                   {#if isLastnameValid} | ||||
|                     <span | ||||
|                       class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> | ||||
|                       Last Name is required | ||||
|                     </span> | ||||
|                   {/if} | ||||
|                 </div> | ||||
|                 <div class="col-span-6"> | ||||
|                   <label | ||||
|                     for="password" | ||||
|                     class="block text-sm font-medium text-gray-700">Password</label> | ||||
|                   <input | ||||
|                     autocomplete="off" | ||||
|                     placeholder="Password" | ||||
|                     class:border-red-500={isPasswordValid} | ||||
|                     class:focus:border-red-500={isPasswordValid} | ||||
|                     class:focus:ring-red-500={isPasswordValid} | ||||
|                     bind:value={password_input_value} | ||||
|                     bind:this={password_input} | ||||
|                     type="password" | ||||
|                     name="password" | ||||
|                     class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> | ||||
|                   {#if isPasswordValid} | ||||
|                     <span | ||||
|                       class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> | ||||
|                       Password is required | ||||
|                     </span> | ||||
|                   {/if} | ||||
|                 </div> | ||||
|                 <div class="col-span-6"> | ||||
|                   <label | ||||
|                     for="email" | ||||
|                     class="block text-sm font-medium text-gray-700">E-Mail</label> | ||||
|                   <input | ||||
|                     autocomplete="off" | ||||
|                     placeholder="E-Mail" | ||||
|                     class:border-red-500={!isEmailValid} | ||||
|                     class:focus:border-red-500={!isEmailValid} | ||||
|                     class:focus:ring-red-500={!isEmailValid} | ||||
|                     bind:value={email_input_value} | ||||
|                     bind:this={email_input} | ||||
|                     type="email" | ||||
|                     name="email" | ||||
|                     class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> | ||||
|                   {#if !isEmailValid} | ||||
|                     <span | ||||
|                       class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> | ||||
|                       valid email is required | ||||
|                     </span> | ||||
|                   {/if} | ||||
|                 </div> | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse"> | ||||
|           <button | ||||
|             disabled={!createbtnenabled} | ||||
|             class:opacity-50={!createbtnenabled} | ||||
|             on:click={submit} | ||||
|             type="button" | ||||
|             class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm"> | ||||
|             Create | ||||
|           </button> | ||||
|           <button | ||||
|             on:click={() => { | ||||
|               modal_open = false; | ||||
|             }} | ||||
|             type="button" | ||||
|             class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"> | ||||
|             Cancel | ||||
|           </button> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| {/if} | ||||
| @@ -1,80 +0,0 @@ | ||||
| <h3 class="text-lg">Standard Avatars</h3> | ||||
| <div class="relative rounded-full w-4 h-4"> | ||||
|   <img | ||||
|     alt="" | ||||
|     src="https://gustui.s3.amazonaws.com/avatar.png" | ||||
|     class="absolute left-0 top-0 w-full h-full rounded-full object-cover" /> | ||||
| </div> | ||||
| <div class="relative rounded-full w-8 h-8"> | ||||
|   <img | ||||
|     alt="" | ||||
|     src="https://gustui.s3.amazonaws.com/avatar.png" | ||||
|     class="absolute left-0 top-0 w-full h-full rounded-full object-cover" /> | ||||
| </div> | ||||
| <div class="relative rounded-full w-12 h-12"> | ||||
|   <img | ||||
|     alt="" | ||||
|     src="https://gustui.s3.amazonaws.com/avatar.png" | ||||
|     class="absolute left-0 top-0 w-full h-full rounded-full object-cover" /> | ||||
| </div> | ||||
| <div class="relative rounded-full w-16 h-16"> | ||||
|   <img | ||||
|     alt="" | ||||
|     src="https://gustui.s3.amazonaws.com/avatar.png" | ||||
|     class="absolute left-0 top-0 w-full h-full rounded-full object-cover" /> | ||||
| </div> | ||||
| <div class="relative rounded-full w-20 h-20"> | ||||
|   <img | ||||
|     alt="" | ||||
|     src="https://gustui.s3.amazonaws.com/avatar.png" | ||||
|     class="absolute left-0 top-0 w-full h-full rounded-full object-cover" /> | ||||
| </div> | ||||
| <div class="relative rounded-full w-24 h-24"> | ||||
|   <img | ||||
|     alt="" | ||||
|     src="https://gustui.s3.amazonaws.com/avatar.png" | ||||
|     class="absolute left-0 top-0 w-full h-full rounded-full object-cover" /> | ||||
| </div> | ||||
| <h3 class="text-lg">Status Avatars</h3> | ||||
| <div class="relative rounded-full w-4 h-4"> | ||||
|   <img | ||||
|     alt="" | ||||
|     src="https://gustui.s3.amazonaws.com/avatar.png" | ||||
|     class="absolute left-0 top-0 w-full h-full rounded-full object-cover" /> | ||||
|   <div class="absolute rounded-full right-0 bottom-0 w-1 h-1 bg-gray-200" /> | ||||
| </div> | ||||
| <div class="relative rounded-full w-8 h-8"> | ||||
|   <img | ||||
|     alt="" | ||||
|     src="https://gustui.s3.amazonaws.com/avatar.png" | ||||
|     class="absolute left-0 top-0 w-full h-full rounded-full object-cover" /> | ||||
|   <div class="absolute rounded-full right-0 bottom-0 w-2 h-2 bg-green-400" /> | ||||
| </div> | ||||
| <div class="relative rounded-full w-12 h-12"> | ||||
|   <img | ||||
|     alt="" | ||||
|     src="https://gustui.s3.amazonaws.com/avatar.png" | ||||
|     class="absolute left-0 top-0 w-full h-full rounded-full object-cover" /> | ||||
|   <div class="absolute rounded-full right-0 bottom-0 w-4 h-4 bg-red-600" /> | ||||
| </div> | ||||
| <div class="relative rounded-full w-16 h-16"> | ||||
|   <img | ||||
|     alt="" | ||||
|     src="https://gustui.s3.amazonaws.com/avatar.png" | ||||
|     class="absolute left-0 top-0 w-full h-full rounded-full object-cover" /> | ||||
|   <div class="absolute rounded-full right-0 bottom-0 w-5 h-5 bg-gray-200" /> | ||||
| </div> | ||||
| <div class="relative rounded-full w-20 h-20"> | ||||
|   <img | ||||
|     alt="" | ||||
|     src="https://gustui.s3.amazonaws.com/avatar.png" | ||||
|     class="absolute left-0 top-0 w-full h-full rounded-full object-cover" /> | ||||
|   <div class="absolute rounded-full right-0 bottom-0 w-6 h-6 bg-green-400" /> | ||||
| </div> | ||||
| <div class="relative rounded-full w-24 h-24"> | ||||
|   <img | ||||
|     alt="" | ||||
|     src="https://gustui.s3.amazonaws.com/avatar.png" | ||||
|     class="absolute left-0 top-0 w-full h-full rounded-full object-cover" /> | ||||
|   <div class="absolute rounded-full right-0 bottom-0 w-6 h-6 bg-red-600" /> | ||||
| </div> | ||||
| @@ -1,53 +0,0 @@ | ||||
| <h3 class="text-lg">badges</h3> | ||||
| <span | ||||
|   class="text-sm font-medium bg-green-100 py-1 px-2 rounded text-green-500 align-middle">Paid</span> | ||||
| <span | ||||
|   class="text-sm font-medium bg-red-100 py-1 px-2 rounded text-red-500 align-middle">Overdue</span> | ||||
| <span | ||||
|   class="rounded-sm py-1 px-2 text-xs font-medium text-white bg-blue-600">Primary</span> | ||||
| <span | ||||
|   class="rounded-sm py-1 px-2 text-xs font-medium text-white bg-gray-600">Secondary</span> | ||||
| <span | ||||
|   class="rounded-sm py-1 px-2 text-xs font-medium text-white bg-green-600">Success</span> | ||||
| <span | ||||
|   class="rounded-sm py-1 px-2 text-xs font-medium text-white bg-red-600">Danger</span> | ||||
| <span | ||||
|   class="rounded-sm py-1 px-2 text-xs font-medium text-black bg-yellow-400">Warning</span> | ||||
| <span | ||||
|   class="rounded-sm py-1 px-2 text-xs font-medium text-white bg-indigo-300">Info</span> | ||||
| <span | ||||
|   class="rounded-sm py-1 px-2 text-xs font-medium text-black bg-gray-200">Light</span> | ||||
| <span | ||||
|   class="rounded-sm py-1 px-2 text-xs font-medium text-white bg-gray-900">Dark</span> | ||||
| <h3 class="text-lg">closable badges</h3> | ||||
| <span class="rounded-sm py-1 px-2 text-xs font-medium text-white bg-blue-600"> | ||||
|   Primary | ||||
|   <span class="ml-2 text-base cursor-pointer">×</span> | ||||
| </span> | ||||
| <span class="rounded-sm py-1 px-2 text-xs font-medium text-white bg-gray-600"> | ||||
|   Secondary | ||||
|   <span class="ml-2 text-base cursor-pointer">×</span> | ||||
| </span> | ||||
| <span class="rounded-sm py-1 px-2 text-xs font-medium text-white bg-green-600"> | ||||
|   Success | ||||
|   <span class="ml-2 text-base cursor-pointer">×</span> | ||||
| </span> | ||||
| <span class="rounded-sm py-1 px-2 text-xs font-medium text-white bg-red-600"> | ||||
|   Danger | ||||
|   <span class="ml-2 text-base cursor-pointer">×</span> | ||||
| </span> | ||||
| <span class="rounded-sm py-1 px-2 text-xs font-medium text-black bg-yellow-400"> | ||||
|   Warning | ||||
|   <span class="ml-2 text-base cursor-pointer">×</span> | ||||
| </span> | ||||
| <span class="rounded-sm py-1 px-2 text-xs font-medium text-white bg-indigo-300"> | ||||
|   Info | ||||
|   <span class="ml-2 text-base cursor-pointer">×</span> | ||||
| </span> | ||||
| <span class="rounded-sm py-1 px-2 text-xs font-medium text-black bg-gray-200"> | ||||
|   Light<span class="ml-2 text-base cursor-pointer">×</span> | ||||
| </span> | ||||
| <span class="rounded-sm py-1 px-2 text-xs font-medium text-white bg-gray-900"> | ||||
|   Dark | ||||
|   <span class="ml-2 text-base cursor-pointer">×</span> | ||||
| </span> | ||||
| @@ -1,62 +0,0 @@ | ||||
| <div class="flex flex-row mb-4"> | ||||
|   <div class="w-full"> | ||||
|     <nav class="w-full flex"> | ||||
|       <ol class="list-none flex flex-row items-center justify-start"> | ||||
|         <li class="mr-2 flex items-center"> | ||||
|           <svg | ||||
|             stroke="currentColor" | ||||
|             fill="none" | ||||
|             stroke-width="2" | ||||
|             viewBox="0 0 24 24" | ||||
|             stroke-linecap="round" | ||||
|             stroke-linejoin="round" | ||||
|             class="h-3 w-3 stroke-current" | ||||
|             height="1em" | ||||
|             width="1em" | ||||
|             xmlns="http://www.w3.org/2000/svg"><path | ||||
|               d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" /> | ||||
|             <polyline points="9 22 9 12 15 12 15 22" /></svg> | ||||
|         </li> | ||||
|         <li class="flex items-center"> | ||||
|           <a class="mr-2" href="/">Home</a><svg | ||||
|             stroke="currentColor" | ||||
|             fill="none" | ||||
|             stroke-width="2" | ||||
|             viewBox="0 0 24 24" | ||||
|             stroke-linecap="round" | ||||
|             stroke-linejoin="round" | ||||
|             class="h-3 w-3 mr-2 stroke-current" | ||||
|             height="1em" | ||||
|             width="1em" | ||||
|             xmlns="http://www.w3.org/2000/svg"><line | ||||
|               x1="5" | ||||
|               y1="12" | ||||
|               x2="19" | ||||
|               y2="12" /> | ||||
|             <polyline points="12 5 19 12 12 19" /></svg> | ||||
|         </li> | ||||
|         <li class="flex items-center"> | ||||
|           <a class="mr-2" href="/">Second level</a><svg | ||||
|             stroke="currentColor" | ||||
|             fill="none" | ||||
|             stroke-width="2" | ||||
|             viewBox="0 0 24 24" | ||||
|             stroke-linecap="round" | ||||
|             stroke-linejoin="round" | ||||
|             class="h-3 w-3 mr-2 stroke-current" | ||||
|             height="1em" | ||||
|             width="1em" | ||||
|             xmlns="http://www.w3.org/2000/svg"><line | ||||
|               x1="5" | ||||
|               y1="12" | ||||
|               x2="19" | ||||
|               y2="12" /> | ||||
|             <polyline points="12 5 19 12 12 19" /></svg> | ||||
|         </li> | ||||
|         <li class="flex items-center"> | ||||
|           <a class="mr-2" href="/">Third level</a> | ||||
|         </li> | ||||
|       </ol> | ||||
|     </nav> | ||||
|   </div> | ||||
| </div> | ||||
| @@ -1,75 +0,0 @@ | ||||
| <script> | ||||
|   import Avatars from "./Avatars.svelte"; | ||||
|   import Badges from "./Badges.svelte"; | ||||
|   import BreadcrumbNav from "./BreadcrumbNav.svelte"; | ||||
|   import FileUpload from "./FileUpload.svelte"; | ||||
|   import Pagination from "./Pagination.svelte"; | ||||
|   import Table from "./Table.svelte"; | ||||
|   import Tabs from "./Tabs.svelte"; | ||||
|   import Tags from "./Tags.svelte"; | ||||
| </script> | ||||
|  | ||||
| <div class="border-4 border-dashed rounded h-96 mb-4" /> | ||||
| <div class="mb-8"> | ||||
|   <FileUpload /> | ||||
| </div> | ||||
| <div class="mb-8"> | ||||
|   <Tabs /> | ||||
| </div> | ||||
| <div class="mb-8"> | ||||
|   <Tags /> | ||||
| </div> | ||||
| <div class="mb-8"> | ||||
|   <Badges /> | ||||
| </div> | ||||
| <div class="mb-8"> | ||||
|   <Avatars /> | ||||
| </div> | ||||
| <Pagination /> | ||||
| <div class="mb-8"> | ||||
|   <Table /> | ||||
| </div> | ||||
| <div | ||||
|   class="widget w-full p-4 mb-4 rounded-lg bg-white border border-grey-100 dark:bg-grey-895 dark:border-grey-890"> | ||||
|   <div class="flex flex-row items-center justify-between mb-6"> | ||||
|     <div class="flex flex-col"> | ||||
|       <div class="text-sm font-light text-grey-500">Regular</div> | ||||
|       <div class="text-sm font-bold"><span>Text inputs</span></div> | ||||
|     </div> | ||||
|   </div> | ||||
|   <div class="flex flex-col lg:flex-row lg:flex-wrap w-full lg:space-x-4"> | ||||
|     <div class="w-full lg:w-1/4"> | ||||
|       <div class="form-element "> | ||||
|         <div class="form-label">Label</div><input | ||||
|           name="name" | ||||
|           type="text" | ||||
|           class="form-input" | ||||
|           placeholder="Enter something..." /> | ||||
|         <div class="form-hint">This is a hint</div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="w-full lg:w-1/4"> | ||||
|       <div class="form-element "> | ||||
|         <div class="form-label">First name</div><input | ||||
|           name="name" | ||||
|           type="text" | ||||
|           class="form-input form-input-invalid" | ||||
|           placeholder="john@example.com" /> | ||||
|         <div class="form-error">First name is required</div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="w-full lg:w-1/4"> | ||||
|       <div class="form-element "> | ||||
|         <div class="form-label">First name</div><input | ||||
|           name="name" | ||||
|           type="text" | ||||
|           class="form-input form-input-valid" | ||||
|           placeholder="john@example.com" /> | ||||
|         <div class="form-success">First name is valid</div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| </div> | ||||
| <div class="mb-8"> | ||||
|   <BreadcrumbNav /> | ||||
| </div> | ||||
| @@ -1,281 +0,0 @@ | ||||
| <div class="w-full p-4 rounded-lg bg-white border border-grey-100 dark:bg-grey-895 dark:border-grey-890"> | ||||
|   <div class="flex flex-row items-center justify-between mb-6"> | ||||
|     <div class="flex flex-col"> | ||||
|       <div class="text-sm font-light text-grey-500">Conversions</div> | ||||
|       <div class="text-sm font-bold"><span>This year</span></div> | ||||
|     </div> | ||||
|     <div class="relative"><button | ||||
|         class="btn btn-default btn-circle btn-icon bg-transparent hover:bg-transparent active:bg-transparent relative"><svg | ||||
|           stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" | ||||
|           stroke-linejoin="round" class="stroke-current stroke-1" size="18" height="18" width="18" | ||||
|           xmlns="http://www.w3.org/2000/svg"> | ||||
|           <circle cx="12" cy="12" r="1"></circle> | ||||
|           <circle cx="12" cy="5" r="1"></circle> | ||||
|           <circle cx="12" cy="19" r="1"></circle> | ||||
|         </svg></button> | ||||
|       <div class="dropdown absolute top-0 right-0 mt-8 "> | ||||
|         <div class="dropdown-content w-48 bottom-start"> | ||||
|           <div class="flex flex-col w-full"> | ||||
|             <ul class="list-none"> | ||||
|               <li><a | ||||
|                   class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100 dark:bg-grey-895 dark:hover:bg-grey-890" | ||||
|                   href="/">Today</a></li> | ||||
|               <li><a | ||||
|                   class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100 dark:bg-grey-895 dark:hover:bg-grey-890" | ||||
|                   href="/">This week</a></li> | ||||
|               <li><a | ||||
|                   class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100 dark:bg-grey-895 dark:hover:bg-grey-890" | ||||
|                   href="/">This month</a></li> | ||||
|               <li><a | ||||
|                   class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100 dark:bg-grey-895 dark:hover:bg-grey-890" | ||||
|                   href="/">This year</a></li> | ||||
|             </ul> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
|   <div class="flex flex-row w-full"> | ||||
|     <div style="width:100%;height:240px"> | ||||
|       <div class="recharts-responsive-container" style="width:100%;height:100%"> | ||||
|         <div class="recharts-wrapper" | ||||
|           style="position: relative; cursor: default; width: 704px; height: 240px;"><svg | ||||
|             class="recharts-surface" width="704" height="240" viewBox="0 0 704 240" version="1.1"> | ||||
|             <defs> | ||||
|               <clipPath id="recharts3-clip"> | ||||
|                 <rect x="40" y="10" height="190" width="654"></rect> | ||||
|               </clipPath> | ||||
|             </defs> | ||||
|             <g class="recharts-layer recharts-cartesian-axis recharts-xAxis xAxis"> | ||||
|               <g class="recharts-cartesian-axis-ticks"> | ||||
|                 <g class="recharts-layer recharts-cartesian-axis-tick"><text width="654" height="30" x="67.25" | ||||
|                     y="208" stroke="none" fill="#666" class="recharts-text recharts-cartesian-axis-tick-value" | ||||
|                     text-anchor="middle"> | ||||
|                     <tspan x="67.25" dy="0.71em">Jan</tspan> | ||||
|                   </text></g> | ||||
|                 <g class="recharts-layer recharts-cartesian-axis-tick"><text width="654" height="30" | ||||
|                     x="121.75" y="208" stroke="none" fill="#666" | ||||
|                     class="recharts-text recharts-cartesian-axis-tick-value" text-anchor="middle"> | ||||
|                     <tspan x="121.75" dy="0.71em">Feb</tspan> | ||||
|                   </text></g> | ||||
|                 <g class="recharts-layer recharts-cartesian-axis-tick"><text width="654" height="30" | ||||
|                     x="176.25" y="208" stroke="none" fill="#666" | ||||
|                     class="recharts-text recharts-cartesian-axis-tick-value" text-anchor="middle"> | ||||
|                     <tspan x="176.25" dy="0.71em">Mar</tspan> | ||||
|                   </text></g> | ||||
|                 <g class="recharts-layer recharts-cartesian-axis-tick"><text width="654" height="30" | ||||
|                     x="230.75" y="208" stroke="none" fill="#666" | ||||
|                     class="recharts-text recharts-cartesian-axis-tick-value" text-anchor="middle"> | ||||
|                     <tspan x="230.75" dy="0.71em">Apr</tspan> | ||||
|                   </text></g> | ||||
|                 <g class="recharts-layer recharts-cartesian-axis-tick"><text width="654" height="30" | ||||
|                     x="285.25" y="208" stroke="none" fill="#666" | ||||
|                     class="recharts-text recharts-cartesian-axis-tick-value" text-anchor="middle"> | ||||
|                     <tspan x="285.25" dy="0.71em">May</tspan> | ||||
|                   </text></g> | ||||
|                 <g class="recharts-layer recharts-cartesian-axis-tick"><text width="654" height="30" | ||||
|                     x="339.75" y="208" stroke="none" fill="#666" | ||||
|                     class="recharts-text recharts-cartesian-axis-tick-value" text-anchor="middle"> | ||||
|                     <tspan x="339.75" dy="0.71em">Jun</tspan> | ||||
|                   </text></g> | ||||
|                 <g class="recharts-layer recharts-cartesian-axis-tick"><text width="654" height="30" | ||||
|                     x="394.25" y="208" stroke="none" fill="#666" | ||||
|                     class="recharts-text recharts-cartesian-axis-tick-value" text-anchor="middle"> | ||||
|                     <tspan x="394.25" dy="0.71em">Jul</tspan> | ||||
|                   </text></g> | ||||
|                 <g class="recharts-layer recharts-cartesian-axis-tick"><text width="654" height="30" | ||||
|                     x="448.75" y="208" stroke="none" fill="#666" | ||||
|                     class="recharts-text recharts-cartesian-axis-tick-value" text-anchor="middle"> | ||||
|                     <tspan x="448.75" dy="0.71em">Aug</tspan> | ||||
|                   </text></g> | ||||
|                 <g class="recharts-layer recharts-cartesian-axis-tick"><text width="654" height="30" | ||||
|                     x="503.25" y="208" stroke="none" fill="#666" | ||||
|                     class="recharts-text recharts-cartesian-axis-tick-value" text-anchor="middle"> | ||||
|                     <tspan x="503.25" dy="0.71em">Sep</tspan> | ||||
|                   </text></g> | ||||
|                 <g class="recharts-layer recharts-cartesian-axis-tick"><text width="654" height="30" | ||||
|                     x="557.75" y="208" stroke="none" fill="#666" | ||||
|                     class="recharts-text recharts-cartesian-axis-tick-value" text-anchor="middle"> | ||||
|                     <tspan x="557.75" dy="0.71em">Oct</tspan> | ||||
|                   </text></g> | ||||
|                 <g class="recharts-layer recharts-cartesian-axis-tick"><text width="654" height="30" | ||||
|                     x="612.25" y="208" stroke="none" fill="#666" | ||||
|                     class="recharts-text recharts-cartesian-axis-tick-value" text-anchor="middle"> | ||||
|                     <tspan x="612.25" dy="0.71em">Nov</tspan> | ||||
|                   </text></g> | ||||
|                 <g class="recharts-layer recharts-cartesian-axis-tick"><text width="654" height="30" | ||||
|                     x="666.75" y="208" stroke="none" fill="#666" | ||||
|                     class="recharts-text recharts-cartesian-axis-tick-value" text-anchor="middle"> | ||||
|                     <tspan x="666.75" dy="0.71em">Dec</tspan> | ||||
|                   </text></g> | ||||
|               </g> | ||||
|             </g> | ||||
|             <g class="recharts-layer recharts-cartesian-axis recharts-yAxis yAxis"> | ||||
|               <g class="recharts-cartesian-axis-ticks"> | ||||
|                 <g class="recharts-layer recharts-cartesian-axis-tick"><text width="30" height="190" x="32" | ||||
|                     y="200" stroke="none" fill="#666" class="recharts-text recharts-cartesian-axis-tick-value" | ||||
|                     text-anchor="end"> | ||||
|                     <tspan x="32" dy="0.355em">0</tspan> | ||||
|                   </text></g> | ||||
|                 <g class="recharts-layer recharts-cartesian-axis-tick"><text width="30" height="190" x="32" | ||||
|                     y="152.5" stroke="none" fill="#666" | ||||
|                     class="recharts-text recharts-cartesian-axis-tick-value" text-anchor="end"> | ||||
|                     <tspan x="32" dy="0.355em">65</tspan> | ||||
|                   </text></g> | ||||
|                 <g class="recharts-layer recharts-cartesian-axis-tick"><text width="30" height="190" x="32" | ||||
|                     y="105" stroke="none" fill="#666" class="recharts-text recharts-cartesian-axis-tick-value" | ||||
|                     text-anchor="end"> | ||||
|                     <tspan x="32" dy="0.355em">130</tspan> | ||||
|                   </text></g> | ||||
|                 <g class="recharts-layer recharts-cartesian-axis-tick"><text width="30" height="190" x="32" | ||||
|                     y="57.5" stroke="none" fill="#666" | ||||
|                     class="recharts-text recharts-cartesian-axis-tick-value" text-anchor="end"> | ||||
|                     <tspan x="32" dy="0.355em">195</tspan> | ||||
|                   </text></g> | ||||
|                 <g class="recharts-layer recharts-cartesian-axis-tick"><text width="30" height="190" x="32" | ||||
|                     y="10" stroke="none" fill="#666" class="recharts-text recharts-cartesian-axis-tick-value" | ||||
|                     text-anchor="end"> | ||||
|                     <tspan x="32" dy="0.355em">260</tspan> | ||||
|                   </text></g> | ||||
|               </g> | ||||
|             </g> | ||||
|             <g class="recharts-layer recharts-bar"> | ||||
|               <g class="recharts-layer recharts-bar-rectangles"> | ||||
|                 <g class="recharts-layer"> | ||||
|                   <g class="recharts-layer recharts-bar-rectangle"> | ||||
|                     <path fill="#90caf9" width="10" height="119.11538461538461" x="55" y="80.88461538461539" | ||||
|                       radius="0" class="recharts-rectangle" | ||||
|                       d="M 55,80.88461538461539 h 10 v 119.11538461538461 h -10 Z"></path> | ||||
|                   </g> | ||||
|                   <g class="recharts-layer recharts-bar-rectangle"> | ||||
|                     <path fill="#90caf9" width="10" height="95" x="109.5" y="105" radius="0" | ||||
|                       class="recharts-rectangle" d="M 109.5,105 h 10 v 95 h -10 Z"></path> | ||||
|                   </g> | ||||
|                   <g class="recharts-layer recharts-bar-rectangle"> | ||||
|                     <path fill="#90caf9" width="10" height="122.03846153846155" x="164" y="77.96153846153845" | ||||
|                       radius="0" class="recharts-rectangle" | ||||
|                       d="M 164,77.96153846153845 h 10 v 122.03846153846155 h -10 Z"></path> | ||||
|                   </g> | ||||
|                   <g class="recharts-layer recharts-bar-rectangle"> | ||||
|                     <path fill="#90caf9" width="10" height="81.11538461538461" x="218.5" | ||||
|                       y="118.88461538461539" radius="0" class="recharts-rectangle" | ||||
|                       d="M 218.5,118.88461538461539 h 10 v 81.11538461538461 h -10 Z"></path> | ||||
|                   </g> | ||||
|                   <g class="recharts-layer recharts-bar-rectangle"> | ||||
|                     <path fill="#90caf9" width="10" height="114" x="273" y="86" radius="0" | ||||
|                       class="recharts-rectangle" d="M 273,86 h 10 v 114 h -10 Z"></path> | ||||
|                   </g> | ||||
|                   <g class="recharts-layer recharts-bar-rectangle"> | ||||
|                     <path fill="#90caf9" width="10" height="117.65384615384616" x="327.5" | ||||
|                       y="82.34615384615384" radius="0" class="recharts-rectangle" | ||||
|                       d="M 327.5,82.34615384615384 h 10 v 117.65384615384616 h -10 Z"></path> | ||||
|                   </g> | ||||
|                   <g class="recharts-layer recharts-bar-rectangle"> | ||||
|                     <path fill="#90caf9" width="10" height="103.76923076923076" x="382" y="96.23076923076924" | ||||
|                       radius="0" class="recharts-rectangle" | ||||
|                       d="M 382,96.23076923076924 h 10 v 103.76923076923076 h -10 Z"></path> | ||||
|                   </g> | ||||
|                   <g class="recharts-layer recharts-bar-rectangle"> | ||||
|                     <path fill="#90caf9" width="10" height="92.80769230769232" x="436.5" | ||||
|                       y="107.19230769230768" radius="0" class="recharts-rectangle" | ||||
|                       d="M 436.5,107.19230769230768 h 10 v 92.80769230769232 h -10 Z"></path> | ||||
|                   </g> | ||||
|                   <g class="recharts-layer recharts-bar-rectangle"> | ||||
|                     <path fill="#90caf9" width="10" height="92.80769230769232" x="491" y="107.19230769230768" | ||||
|                       radius="0" class="recharts-rectangle" | ||||
|                       d="M 491,107.19230769230768 h 10 v 92.80769230769232 h -10 Z"></path> | ||||
|                   </g> | ||||
|                   <g class="recharts-layer recharts-bar-rectangle"> | ||||
|                     <path fill="#90caf9" width="10" height="127.8846153846154" x="545.5" y="72.1153846153846" | ||||
|                       radius="0" class="recharts-rectangle" | ||||
|                       d="M 545.5,72.1153846153846 h 10 v 127.8846153846154 h -10 Z"></path> | ||||
|                   </g> | ||||
|                   <g class="recharts-layer recharts-bar-rectangle"> | ||||
|                     <path fill="#90caf9" width="10" height="105.23076923076924" x="600" y="94.76923076923076" | ||||
|                       radius="0" class="recharts-rectangle" | ||||
|                       d="M 600,94.76923076923076 h 10 v 105.23076923076924 h -10 Z"></path> | ||||
|                   </g> | ||||
|                   <g class="recharts-layer recharts-bar-rectangle"> | ||||
|                     <path fill="#90caf9" width="10" height="115.46153846153845" x="654.5" | ||||
|                       y="84.53846153846155" radius="0" class="recharts-rectangle" | ||||
|                       d="M 654.5,84.53846153846155 h 10 v 115.46153846153845 h -10 Z"></path> | ||||
|                   </g> | ||||
|                 </g> | ||||
|               </g> | ||||
|             </g> | ||||
|             <g class="recharts-layer recharts-bar"> | ||||
|               <g class="recharts-layer recharts-bar-rectangles"> | ||||
|                 <g class="recharts-layer"> | ||||
|                   <g class="recharts-layer recharts-bar-rectangle"> | ||||
|                     <path fill="#1e88e5" width="10" height="112.53846153846155" x="69" y="87.46153846153845" | ||||
|                       radius="0" class="recharts-rectangle" | ||||
|                       d="M 69,87.46153846153845 h 10 v 112.53846153846155 h -10 Z"></path> | ||||
|                   </g> | ||||
|                   <g class="recharts-layer recharts-bar-rectangle"> | ||||
|                     <path fill="#1e88e5" width="10" height="151.26923076923077" x="123.5" | ||||
|                       y="48.730769230769226" radius="0" class="recharts-rectangle" | ||||
|                       d="M 123.5,48.730769230769226 h 10 v 151.26923076923077 h -10 Z"></path> | ||||
|                   </g> | ||||
|                   <g class="recharts-layer recharts-bar-rectangle"> | ||||
|                     <path fill="#1e88e5" width="10" height="181.23076923076923" x="178" y="18.769230769230774" | ||||
|                       radius="0" class="recharts-rectangle" | ||||
|                       d="M 178,18.769230769230774 h 10 v 181.23076923076923 h -10 Z"></path> | ||||
|                   </g> | ||||
|                   <g class="recharts-layer recharts-bar-rectangle"> | ||||
|                     <path fill="#1e88e5" width="10" height="165.8846153846154" x="232.5" y="34.11538461538461" | ||||
|                       radius="0" class="recharts-rectangle" | ||||
|                       d="M 232.5,34.11538461538461 h 10 v 165.8846153846154 h -10 Z"></path> | ||||
|                   </g> | ||||
|                   <g class="recharts-layer recharts-bar-rectangle"> | ||||
|                     <path fill="#1e88e5" width="10" height="156.38461538461536" x="287" y="43.61538461538464" | ||||
|                       radius="0" class="recharts-rectangle" | ||||
|                       d="M 287,43.61538461538464 h 10 v 156.38461538461536 h -10 Z"></path> | ||||
|                   </g> | ||||
|                   <g class="recharts-layer recharts-bar-rectangle"> | ||||
|                     <path fill="#1e88e5" width="10" height="118.38461538461539" x="341.5" | ||||
|                       y="81.61538461538461" radius="0" class="recharts-rectangle" | ||||
|                       d="M 341.5,81.61538461538461 h 10 v 118.38461538461539 h -10 Z"></path> | ||||
|                   </g> | ||||
|                   <g class="recharts-layer recharts-bar-rectangle"> | ||||
|                     <path fill="#1e88e5" width="10" height="138.84615384615384" x="396" y="61.15384615384616" | ||||
|                       radius="0" class="recharts-rectangle" | ||||
|                       d="M 396,61.15384615384616 h 10 v 138.84615384615384 h -10 Z"></path> | ||||
|                   </g> | ||||
|                   <g class="recharts-layer recharts-bar-rectangle"> | ||||
|                     <path fill="#1e88e5" width="10" height="175.3846153846154" x="450.5" | ||||
|                       y="24.615384615384613" radius="0" class="recharts-rectangle" | ||||
|                       d="M 450.5,24.615384615384613 h 10 v 175.3846153846154 h -10 Z"></path> | ||||
|                   </g> | ||||
|                   <g class="recharts-layer recharts-bar-rectangle"> | ||||
|                     <path fill="#1e88e5" width="10" height="155.65384615384613" x="505" y="44.34615384615387" | ||||
|                       radius="0" class="recharts-rectangle" | ||||
|                       d="M 505,44.34615384615387 h 10 v 155.65384615384613 h -10 Z"></path> | ||||
|                   </g> | ||||
|                   <g class="recharts-layer recharts-bar-rectangle"> | ||||
|                     <path fill="#1e88e5" width="10" height="179.76923076923077" x="559.5" | ||||
|                       y="20.230769230769226" radius="0" class="recharts-rectangle" | ||||
|                       d="M 559.5,20.230769230769226 h 10 v 179.76923076923077 h -10 Z"></path> | ||||
|                   </g> | ||||
|                   <g class="recharts-layer recharts-bar-rectangle"> | ||||
|                     <path fill="#1e88e5" width="10" height="173.19230769230768" x="614" y="26.80769230769232" | ||||
|                       radius="0" class="recharts-rectangle" | ||||
|                       d="M 614,26.80769230769232 h 10 v 173.19230769230768 h -10 Z"></path> | ||||
|                   </g> | ||||
|                   <g class="recharts-layer recharts-bar-rectangle"> | ||||
|                     <path fill="#1e88e5" width="10" height="146.15384615384616" x="668.5" | ||||
|                       y="53.84615384615384" radius="0" class="recharts-rectangle" | ||||
|                       d="M 668.5,53.84615384615384 h 10 v 146.15384615384616 h -10 Z"></path> | ||||
|                   </g> | ||||
|                 </g> | ||||
|               </g> | ||||
|             </g> | ||||
|           </svg> | ||||
|           <div class="recharts-tooltip-wrapper" | ||||
|             style="pointer-events: none; visibility: hidden; position: absolute; top: 0px; transform: translate(538.875px, 126px);"> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div style="position:absolute;width:0;height:0;visibility:hidden;display:none"></div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| </div> | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,359 +0,0 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import { active } from "tinro"; | ||||
|   import localForage from "localforage"; | ||||
|   import { router } from "tinro"; | ||||
|  | ||||
|   import store from "../store"; | ||||
|   import NoComponentLoaded from "./NoComponentLoaded.svelte"; | ||||
|   import { AuthService, OpenAPI } from "@odit/lfk-client-js"; | ||||
|  | ||||
|   let activePage = "dashboard"; | ||||
|   let dropdown1 = false; | ||||
|   let navOpen = false; | ||||
|   function ismobile() { | ||||
|     let check = false; | ||||
|     (function (a) { | ||||
|       if ( | ||||
|         /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test( | ||||
|           a | ||||
|         ) || | ||||
|         /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test( | ||||
|           a.substr(0, 4) | ||||
|         ) | ||||
|       ) | ||||
|         check = true; | ||||
|     })(navigator.userAgent || navigator.vendor || window.opera); | ||||
|     return check; | ||||
|   } | ||||
|   $: mobile = ismobile(); | ||||
|   function logout() { | ||||
|     localForage.clear(); | ||||
|     location.replace("/"); | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| <section class="min-h-screen bg-gray-50 dark:bg-black dark:text-gray-100"> | ||||
|   <nav | ||||
|     class:hidden={!navOpen && mobile} | ||||
|     class="select-none fixed top-0 left-0 z-20 h-full pb-10 overflow-x-hidden overflow-y-auto transition origin-left transform border-r w-60 md:translate-x-0"> | ||||
|     <a href="/" class="flex items-center px-4 py-5"> | ||||
|       <img | ||||
|         src="/lfk-logo.png" | ||||
|         alt="Logo" | ||||
|         class="h-10" /> | ||||
|       <h3 class="text-lg">Lauf für Kaya! Admin</h3> | ||||
|     </a> | ||||
|     <nav class="text-sm font-medium text-gray-600" aria-label="Main Navigation"> | ||||
|       <a | ||||
|         class:dark:bg-gray-900={$router.path === '/'} | ||||
|         class:bg-gray-100={$router.path === '/'} | ||||
|         class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" | ||||
|         href="/"> | ||||
|         <svg | ||||
|           class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600" | ||||
|           xmlns="http://www.w3.org/2000/svg" | ||||
|           viewBox="0 0 20 20" | ||||
|           fill="currentColor"> | ||||
|           <path | ||||
|             d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z" /> | ||||
|         </svg> | ||||
|         <span>{$_('dashboard-title')}</span> | ||||
|       </a> | ||||
|       <a | ||||
|         class:dark:bg-gray-900={$router.path.includes('/orgs/')} | ||||
|         class:bg-gray-100={$router.path.includes('/orgs/')} | ||||
|         class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" | ||||
|         href="/orgs/"> | ||||
|         <svg | ||||
|           class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600" | ||||
|           fill="currentColor" | ||||
|           xmlns="http://www.w3.org/2000/svg" | ||||
|           viewBox="0 0 24 24" | ||||
|           width="24" | ||||
|           height="24"><path fill="none" d="M0 0h24v24H0z" /> | ||||
|           <path | ||||
|             d="M17 19h2v-8h-6v8h2v-6h2v6zM3 19V4a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v5h2v10h1v2H2v-2h1zm4-8v2h2v-2H7zm0 4v2h2v-2H7zm0-8v2h2V7H7z" /></svg> | ||||
|         <span>{$_('orgs')}</span> | ||||
|       </a> | ||||
|       <a | ||||
|         class:dark:bg-gray-900={$router.path === '/users/'} | ||||
|         class:bg-gray-100={$router.path === '/users/'} | ||||
|         class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" | ||||
|         href="/users/"> | ||||
|         <svg | ||||
|           class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600" | ||||
|           fill="currentColor" | ||||
|           width="24" | ||||
|           height="24" | ||||
|           xmlns="http://www.w3.org/2000/svg" | ||||
|           viewBox="0 0 640 512"><path | ||||
|             fill="currentColor" | ||||
|             d="M610.5 341.3c2.6-14.1 2.6-28.5 0-42.6l25.8-14.9c3-1.7 4.3-5.2 3.3-8.5-6.7-21.6-18.2-41.2-33.2-57.4-2.3-2.5-6-3.1-9-1.4l-25.8 14.9c-10.9-9.3-23.4-16.5-36.9-21.3v-29.8c0-3.4-2.4-6.4-5.7-7.1-22.3-5-45-4.8-66.2 0-3.3.7-5.7 3.7-5.7 7.1v29.8c-13.5 4.8-26 12-36.9 21.3l-25.8-14.9c-2.9-1.7-6.7-1.1-9 1.4-15 16.2-26.5 35.8-33.2 57.4-1 3.3.4 6.8 3.3 8.5l25.8 14.9c-2.6 14.1-2.6 28.5 0 42.6l-25.8 14.9c-3 1.7-4.3 5.2-3.3 8.5 6.7 21.6 18.2 41.1 33.2 57.4 2.3 2.5 6 3.1 9 1.4l25.8-14.9c10.9 9.3 23.4 16.5 36.9 21.3v29.8c0 3.4 2.4 6.4 5.7 7.1 22.3 5 45 4.8 66.2 0 3.3-.7 5.7-3.7 5.7-7.1v-29.8c13.5-4.8 26-12 36.9-21.3l25.8 14.9c2.9 1.7 6.7 1.1 9-1.4 15-16.2 26.5-35.8 33.2-57.4 1-3.3-.4-6.8-3.3-8.5l-25.8-14.9zM496 368.5c-26.8 0-48.5-21.8-48.5-48.5s21.8-48.5 48.5-48.5 48.5 21.8 48.5 48.5-21.7 48.5-48.5 48.5zM96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm224 32c1.9 0 3.7-.5 5.6-.6 8.3-21.7 20.5-42.1 36.3-59.2 7.4-8 17.9-12.6 28.9-12.6 6.9 0 13.7 1.8 19.6 5.3l7.9 4.6c.8-.5 1.6-.9 2.4-1.4 7-14.6 11.2-30.8 11.2-48 0-61.9-50.1-112-112-112S208 82.1 208 144c0 61.9 50.1 112 112 112zm105.2 194.5c-2.3-1.2-4.6-2.6-6.8-3.9-8.2 4.8-15.3 9.8-27.5 9.8-10.9 0-21.4-4.6-28.9-12.6-18.3-19.8-32.3-43.9-40.2-69.6-10.7-34.5 24.9-49.7 25.8-50.3-.1-2.6-.1-5.2 0-7.8l-7.9-4.6c-3.8-2.2-7-5-9.8-8.1-3.3.2-6.5.6-9.8.6-24.6 0-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h255.4c-3.7-6-6.2-12.8-6.2-20.3v-9.2zM173.1 274.6C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z" /></svg> | ||||
|         <span>{$_('users')}</span> | ||||
|       </a> | ||||
|       <a | ||||
|         class:dark:bg-gray-900={$router.path === '/runners/'} | ||||
|         class:bg-gray-100={$router.path === '/runners/'} | ||||
|         class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" | ||||
|         href="/runners/"> | ||||
|         <svg | ||||
|           xmlns="http://www.w3.org/2000/svg" | ||||
|           viewBox="0 0 24 24" | ||||
|           class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600" | ||||
|           fill="currentColor" | ||||
|           width="24" | ||||
|           height="24"><path fill="none" d="M0 0h24v24H0z" /> | ||||
|           <path | ||||
|             d="M9.83 8.79L8 9.456V13H6V8.05h.015l5.268-1.918c.244-.093.51-.14.782-.131a2.616 2.616 0 0 1 2.427 1.82c.186.583.356.977.51 1.182A4.992 4.992 0 0 0 19 11v2a6.986 6.986 0 0 1-5.402-2.547l-.581 3.297L15 15.67V23h-2v-5.986l-2.05-1.987-.947 4.298-6.894-1.215.348-1.97 4.924.868L9.83 8.79zM13.5 5.5a2 2 0 1 1 0-4 2 2 0 0 1 0 4z" /></svg> | ||||
|         <span>{$_('runners')}</span> | ||||
|       </a> | ||||
|       <a | ||||
|         class:dark:bg-gray-900={$router.path === '/teams/'} | ||||
|         class:bg-gray-100={$router.path === '/teams/'} | ||||
|         class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" | ||||
|         href="/teams/"> | ||||
|         <svg | ||||
|           class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600" | ||||
|           fill="currentColor" | ||||
|           width="24" | ||||
|           height="24" | ||||
|           xmlns="http://www.w3.org/2000/svg" | ||||
|           viewBox="0 0 640 512"><path | ||||
|             fill="currentColor" | ||||
|             d="M96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm448 0c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm32 32h-64c-17.6 0-33.5 7.1-45.1 18.6 40.3 22.1 68.9 62 75.1 109.4h66c17.7 0 32-14.3 32-32v-32c0-35.3-28.7-64-64-64zm-256 0c61.9 0 112-50.1 112-112S381.9 32 320 32 208 82.1 208 144s50.1 112 112 112zm76.8 32h-8.3c-20.8 10-43.9 16-68.5 16s-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h288c26.5 0 48-21.5 48-48v-28.8c0-63.6-51.6-115.2-115.2-115.2zm-223.7-13.4C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z" /></svg> | ||||
|         <span>{$_('teams')}</span> | ||||
|       </a> | ||||
|       <a | ||||
|         class:dark:bg-gray-900={$router.path === '/tracks/'} | ||||
|         class:bg-gray-100={$router.path === '/tracks/'} | ||||
|         class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" | ||||
|         href="/tracks/"> | ||||
|         <svg | ||||
|           class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600" | ||||
|           fill="currentColor" | ||||
|           width="24" | ||||
|           height="24" | ||||
|           xmlns="http://www.w3.org/2000/svg" | ||||
|           viewBox="0 0 640 512"><path | ||||
|             fill="currentColor" | ||||
|             d="M635.7 167.2L556.1 31.7c-8.8-15-28.3-20.1-43.5-11.5l-69 39.1L503.3 161c2.2 3.8.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9L416 75l-55.2 31.3 27.9 47.4c2.2 3.8.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9L333.2 122 278 153.3 337.8 255c2.2 3.7.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9l-59.7-101.7-55.2 31.3 27.9 47.4c2.2 3.8.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9l-27.9-47.5-55.2 31.3 59.7 101.7c2.2 3.7.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9L84.9 262.9l-69 39.1C.7 310.7-4.6 329.8 4.2 344.8l79.6 135.6c8.8 15 28.3 20.1 43.5 11.5L624.1 210c15.2-8.6 20.4-27.8 11.6-42.8z" /></svg> | ||||
|         <span>{$_('tracks')}</span> | ||||
|       </a> | ||||
|       <a | ||||
|         class:dark:bg-gray-900={activePage === 'blub'} | ||||
|         class:bg-gray-100={activePage === 'blub'} | ||||
|         class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" | ||||
|         href="#"> | ||||
|         <svg | ||||
|           class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600" | ||||
|           xmlns="http://www.w3.org/2000/svg" | ||||
|           viewBox="0 0 20 20" | ||||
|           fill="currentColor"> | ||||
|           <path d="M9 2a1 1 0 000 2h2a1 1 0 100-2H9z" /> | ||||
|           <path | ||||
|             fill-rule="evenodd" | ||||
|             d="M4 5a2 2 0 012-2 3 3 0 003 3h2a3 3 0 003-3 2 2 0 012 2v11a2 2 0 01-2 2H6a2 2 0 01-2-2V5zm9.707 5.707a1 1 0 00-1.414-1.414L9 12.586l-1.293-1.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" | ||||
|             clip-rule="evenodd" /> | ||||
|         </svg> | ||||
|         <span>Checklists</span> | ||||
|       </a> | ||||
|       <div> | ||||
|         <div | ||||
|           tabindex="0" | ||||
|           class:dark:bg-gray-900={activePage === 'blub'} | ||||
|           class:bg-gray-100={activePage === 'blub'} | ||||
|           class="flex items-center justify-between px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" | ||||
|           role="button" | ||||
|           on:click={() => { | ||||
|             dropdown1 = !dropdown1; | ||||
|           }}> | ||||
|           <div class="flex items-center"> | ||||
|             <svg | ||||
|               class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600" | ||||
|               xmlns="http://www.w3.org/2000/svg" | ||||
|               viewBox="0 0 20 20" | ||||
|               fill="currentColor"> | ||||
|               <path | ||||
|                 fill-rule="evenodd" | ||||
|                 d="M12.316 3.051a1 1 0 01.633 1.265l-4 12a1 1 0 11-1.898-.632l4-12a1 1 0 011.265-.633zM5.707 6.293a1 1 0 010 1.414L3.414 10l2.293 2.293a1 1 0 11-1.414 1.414l-3-3a1 1 0 010-1.414l3-3a1 1 0 011.414 0zm8.586 0a1 1 0 011.414 0l3 3a1 1 0 010 1.414l-3 3a1 1 0 11-1.414-1.414L16.586 10l-2.293-2.293a1 1 0 010-1.414z" | ||||
|                 clip-rule="evenodd" /> | ||||
|             </svg> | ||||
|             <span>Integrations</span> | ||||
|           </div> | ||||
|           {#if dropdown1} | ||||
|             <svg | ||||
|               class="flex-shrink-0 w-4 h-4 ml-2 transition transform" | ||||
|               xmlns="http://www.w3.org/2000/svg" | ||||
|               style="transform:rotate(90deg)" | ||||
|               viewBox="0 0 20 20" | ||||
|               fill="currentColor"> | ||||
|               <path | ||||
|                 fill-rule="evenodd" | ||||
|                 d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" | ||||
|                 clip-rule="evenodd" /> | ||||
|             </svg> | ||||
|           {:else} | ||||
|             <svg | ||||
|               class="flex-shrink-0 w-4 h-4 ml-2 transition transform" | ||||
|               xmlns="http://www.w3.org/2000/svg" | ||||
|               viewBox="0 0 20 20" | ||||
|               fill="currentColor"> | ||||
|               <path | ||||
|                 fill-rule="evenodd" | ||||
|                 d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" | ||||
|                 clip-rule="evenodd" /> | ||||
|             </svg> | ||||
|           {/if} | ||||
|         </div> | ||||
|         {#if dropdown1} | ||||
|           <div class="mb-4"> | ||||
|             <a | ||||
|               class="flex items-center py-2 pl-12 pr-4 transition cursor-pointer hover:bg-gray-100 hover:text-gray-900" | ||||
|               href="#">Shopify</a> | ||||
|             <a | ||||
|               class="flex items-center py-2 pl-12 pr-4 transition cursor-pointer hover:bg-gray-100 hover:text-gray-900" | ||||
|               href="#">Slack</a> | ||||
|             <a | ||||
|               class="flex items-center py-2 pl-12 pr-4 transition cursor-pointer hover:bg-gray-100 hover:text-gray-900" | ||||
|               href="#">Zapier</a> | ||||
|           </div> | ||||
|         {/if} | ||||
|       </div> | ||||
|       <a | ||||
|         class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" | ||||
|         href="#"> | ||||
|         <svg | ||||
|           class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600" | ||||
|           xmlns="http://www.w3.org/2000/svg" | ||||
|           viewBox="0 0 20 20" | ||||
|           fill="currentColor"> | ||||
|           <path | ||||
|             fill-rule="evenodd" | ||||
|             d="M5 5a3 3 0 015-2.236A3 3 0 0114.83 6H16a2 2 0 110 4h-5V9a1 1 0 10-2 0v1H4a2 2 0 110-4h1.17C5.06 5.687 5 5.35 5 5zm4 1V5a1 1 0 10-1 1h1zm3 0a1 1 0 10-1-1v1h1z" | ||||
|             clip-rule="evenodd" /> | ||||
|           <path d="M9 11H3v5a2 2 0 002 2h4v-7zM11 18h4a2 2 0 002-2v-5h-6v7z" /> | ||||
|         </svg> | ||||
|         <span>{$_('changelog')}</span> | ||||
|       </a> | ||||
|       <a | ||||
|         class:dark:bg-gray-900={$router.path === '/settings/'} | ||||
|         class:bg-gray-100={$router.path === '/settings/'} | ||||
|         class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" | ||||
|         href="/settings/"> | ||||
|         <svg | ||||
|           class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600" | ||||
|           xmlns="http://www.w3.org/2000/svg" | ||||
|           viewBox="0 0 20 20" | ||||
|           fill="currentColor"> | ||||
|           <path | ||||
|             fill-rule="evenodd" | ||||
|             d="M11.49 3.17c-.38-1.56-2.6-1.56-2.98 0a1.532 1.532 0 01-2.286.948c-1.372-.836-2.942.734-2.106 2.106.54.886.061 2.042-.947 2.287-1.561.379-1.561 2.6 0 2.978a1.532 1.532 0 01.947 2.287c-.836 1.372.734 2.942 2.106 2.106a1.532 1.532 0 012.287.947c.379 1.561 2.6 1.561 2.978 0a1.533 1.533 0 012.287-.947c1.372.836 2.942-.734 2.106-2.106a1.533 1.533 0 01.947-2.287c1.561-.379 1.561-2.6 0-2.978a1.532 1.532 0 01-.947-2.287c.836-1.372-.734-2.942-2.106-2.106a1.532 1.532 0 01-2.287-.947zM10 13a3 3 0 100-6 3 3 0 000 6z" | ||||
|             clip-rule="evenodd" /> | ||||
|         </svg> | ||||
|         <span>{$_('settings')}</span> | ||||
|       </a> | ||||
|       <a | ||||
|         class:dark:bg-gray-900={$router.path === '/about/'} | ||||
|         class:bg-gray-100={$router.path === '/about/'} | ||||
|         class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" | ||||
|         href="/about/"> | ||||
|         <svg | ||||
|           class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600" | ||||
|           xmlns="http://www.w3.org/2000/svg" | ||||
|           fill="none" | ||||
|           stroke="currentColor" | ||||
|           stroke-width="2" | ||||
|           stroke-linecap="round" | ||||
|           stroke-linejoin="round" | ||||
|           viewBox="0 0 24 24"><circle cx="12" cy="12" r="10" /> | ||||
|           <path d="M12 16v-4M12 8h.01" /></svg> | ||||
|         <span>{$_('about')}</span> | ||||
|       </a> | ||||
|       <span | ||||
|         tabindex="0" | ||||
|         class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" | ||||
|         on:click={() => { | ||||
|           AuthService.authControllerLogout(); | ||||
|           logout(); | ||||
|         }}> | ||||
|         <svg | ||||
|           class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600" | ||||
|           fill="currentColor" | ||||
|           width="24" | ||||
|           height="24" | ||||
|           xmlns="http://www.w3.org/2000/svg" | ||||
|           viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z" /> | ||||
|           <path | ||||
|             d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2a9.985 9.985 0 0 1 8 4h-2.71a8 8 0 1 0 .001 12h2.71A9.985 9.985 0 0 1 12 22zm7-6v-3h-8v-2h8V8l5 4-5 4z" /></svg> | ||||
|         <span>{$_('logout')}</span> | ||||
|       </span> | ||||
|     </nav> | ||||
|   </nav> | ||||
|   <div class="ml-0 transition md:ml-60"> | ||||
|     <header | ||||
|       class="flex items-center justify-between w-full px-4 border-b h-14"> | ||||
|       <button | ||||
|         on:click={() => { | ||||
|           navOpen = !navOpen; | ||||
|         }} | ||||
|         class="block btn btn-light md:hidden"> | ||||
|         <span class="sr-only">Menu</span> | ||||
|         <svg | ||||
|           class="w-4 h-4" | ||||
|           xmlns="http://www.w3.org/2000/svg" | ||||
|           viewBox="0 0 20 20" | ||||
|           fill="currentColor"> | ||||
|           <path | ||||
|             fill-rule="evenodd" | ||||
|             d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 15a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z" | ||||
|             clip-rule="evenodd" /> | ||||
|         </svg> | ||||
|       </button> | ||||
|       <!-- <div class="hidden -ml-3 form-icon md:block w-96"> | ||||
|         <svg | ||||
|           xmlns="http://www.w3.org/2000/svg" | ||||
|           fill="none" | ||||
|           viewBox="0 0 24 24" | ||||
|           stroke="currentColor"> | ||||
|           <path | ||||
|             stroke-linecap="round" | ||||
|             stroke-linejoin="round" | ||||
|             stroke-width="2" | ||||
|             d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" /> | ||||
|         </svg> | ||||
|         <input | ||||
|           class="border-0 form-input" | ||||
|           placeholder="Search for articles..." /> | ||||
|       </div> --> | ||||
|       <div class="flex items-end"> | ||||
|         <a href="#" class="flex text-gray-500"> | ||||
|           <svg | ||||
|             class="flex-shrink-0 w-5 h-5" | ||||
|             xmlns="http://www.w3.org/2000/svg" | ||||
|             viewBox="0 0 20 20" | ||||
|             fill="currentColor"> | ||||
|             <path | ||||
|               d="M10 2a6 6 0 00-6 6v3.586l-.707.707A1 1 0 004 14h12a1 1 0 00.707-1.707L16 11.586V8a6 6 0 00-6-6zM10 18a3 3 0 01-3-3h6a3 3 0 01-3 3z" /> | ||||
|           </svg> | ||||
|         </a> | ||||
|         <a href="/profile/" class="ml-4"> | ||||
|           <img | ||||
|             class="h-8 w-8 rounded-full" | ||||
|             src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" | ||||
|             alt="Profile Picture" /> | ||||
|         </a> | ||||
|       </div> | ||||
|     </header> | ||||
|     <slot> | ||||
|       <NoComponentLoaded /> | ||||
|     </slot> | ||||
|   </div> | ||||
|   <!-- Sidebar Backdrop --> | ||||
|   <div | ||||
|     on:click={() => { | ||||
|       navOpen = false; | ||||
|     }} | ||||
|     class:hidden={!navOpen} | ||||
|     class="fixed inset-0 z-10 w-screen h-screen bg-black bg-opacity-25 md:hidden" /> | ||||
| </section> | ||||
| @@ -1,31 +0,0 @@ | ||||
| <script> | ||||
|   import { _, json } from "svelte-i18n"; | ||||
|   import { getlang } from "./datatable_i18n"; | ||||
|   import { Grid } from "gridjs"; | ||||
|   import "gridjs/dist/theme/mermaid.css"; | ||||
|   // | ||||
|   let table; | ||||
|   const datatable = new Grid({ | ||||
|     columns: ["Name", "Email", "Phone Number"], | ||||
|     language: getlang($json("datatable")), | ||||
|     sort: true, | ||||
|     search: { enabled: true }, | ||||
|     data: [ | ||||
|       ["John", "john@example.com", "(353) 01 222 3333"], | ||||
|       ["Mark", "mark@gmail.com", "(01) 22 888 4444"], | ||||
|       ["Eoin", "eoin@gmail.com", "0097 22 654 00033"], | ||||
|       ["Sarah", "sarahcdd@gmail.com", "+322 876 1233"], | ||||
|       ["Afshin", "afshin@mail.com", "(353) 22 87 8356"], | ||||
|     ], | ||||
|     pagination: { | ||||
|       enabled: true, | ||||
|       limit: 2, | ||||
|       summary: false, | ||||
|     }, | ||||
|   }); | ||||
|   setTimeout(() => { | ||||
|     datatable.render(table); | ||||
|   }, 0); | ||||
| </script> | ||||
|  | ||||
| <div bind:this={table} /> | ||||
| @@ -1,25 +0,0 @@ | ||||
| <script> | ||||
|   import "filepond/dist/filepond.css"; | ||||
|   import FilePond from "svelte-filepond"; | ||||
|   let pond; | ||||
|   // pond.getFiles() will return the active files | ||||
|   // the name to use for the internal file input | ||||
|   let name = "filepond"; | ||||
|   function handleInit() { | ||||
|     // console.log("FilePond has initialised"); | ||||
|   } | ||||
|   function handleAddFile(err, fileItem) { | ||||
|     // console.log("A file has been added", fileItem); | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| <div class="app"> | ||||
|   <FilePond | ||||
|     bind:this={pond} | ||||
|     {name} | ||||
|     server="/api" | ||||
|     allowMultiple={false} | ||||
|     credits={false} | ||||
|     oninit={handleInit} | ||||
|     onaddfile={handleAddFile} /> | ||||
| </div> | ||||
| @@ -1,15 +0,0 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
| </script> | ||||
|  | ||||
| <footer class="container"> | ||||
|   <hr class="mt-2 mb-4 border-b-1 border-gray-300" /> | ||||
|   <p class="text-sm text-gray-500 mt-4"> | ||||
|     Lauf für Kaya! Läufersystem - Copyright © 2020 + proudly powered by | ||||
|     <a | ||||
|       class="underline" | ||||
|       href="https://odit.services" | ||||
|       rel="noopener,noreferrer" | ||||
|       target="_blank">ODIT.Services</a> | ||||
|   </p> | ||||
| </footer> | ||||
| @@ -1,274 +0,0 @@ | ||||
| <!-- | ||||
|   This example requires Tailwind CSS v2.0+  | ||||
|    | ||||
|   This example requires some changes to your config: | ||||
|    | ||||
|   ``` | ||||
|   // tailwind.config.js | ||||
|   module.exports = { | ||||
|     // ... | ||||
|     plugins: [ | ||||
|       // ... | ||||
|       require('@tailwindcss/forms'), | ||||
|     ] | ||||
|   } | ||||
|   ``` | ||||
| --> | ||||
| <div> | ||||
|   <div class="md:grid md:grid-cols-3 md:gap-6"> | ||||
|     <div class="md:col-span-1"> | ||||
|       <div class="px-4 sm:px-0"> | ||||
|         <h3 class="text-lg font-medium leading-6 text-gray-900">Profile</h3> | ||||
|         <p class="mt-1 text-sm text-gray-600"> | ||||
|           This information will be displayed publicly so be careful what you share. | ||||
|         </p> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="mt-5 md:mt-0 md:col-span-2"> | ||||
|       <form action="#" method="POST"> | ||||
|         <div class="shadow sm:rounded-md sm:overflow-hidden"> | ||||
|           <div class="px-4 py-5 bg-white space-y-6 sm:p-6"> | ||||
|             <div class="grid grid-cols-3 gap-6"> | ||||
|               <div class="col-span-3 sm:col-span-2"> | ||||
|                 <label for="company_website" class="block text-sm font-medium text-gray-700"> | ||||
|                   Website | ||||
|                 </label> | ||||
|                 <div class="mt-1 flex rounded-md shadow-sm"> | ||||
|                   <span class="inline-flex items-center px-3 rounded-l-md border border-r-0 border-gray-300 bg-gray-50 text-gray-500 text-sm"> | ||||
|                     http:// | ||||
|                   </span> | ||||
|                   <input type="text" name="company_website" id="company_website" class="focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-r-md sm:text-sm border-gray-300" placeholder="www.example.com"> | ||||
|                 </div> | ||||
|               </div> | ||||
|             </div> | ||||
|  | ||||
|             <div> | ||||
|               <label for="about" class="block text-sm font-medium text-gray-700"> | ||||
|                 About | ||||
|               </label> | ||||
|               <div class="mt-1"> | ||||
|                 <textarea id="about" name="about" rows="3" class="shadow-sm focus:ring-indigo-500 focus:border-indigo-500 mt-1 block w-full sm:text-sm border-gray-300 rounded-md" placeholder="you@example.com"></textarea> | ||||
|               </div> | ||||
|               <p class="mt-2 text-sm text-gray-500"> | ||||
|                 Brief description for your profile. URLs are hyperlinked. | ||||
|               </p> | ||||
|             </div> | ||||
|  | ||||
|             <div> | ||||
|               <!-- svelte-ignore a11y-label-has-associated-control --> | ||||
|               <label class="block text-sm font-medium text-gray-700"> | ||||
|                 Photo | ||||
|               </label> | ||||
|               <div class="mt-2 flex items-center"> | ||||
|                 <span class="inline-block h-12 w-12 rounded-full overflow-hidden bg-gray-100"> | ||||
|                   <svg class="h-full w-full text-gray-300" fill="currentColor" viewBox="0 0 24 24"> | ||||
|                     <path d="M24 20.993V24H0v-2.996A14.977 14.977 0 0112.004 15c4.904 0 9.26 2.354 11.996 5.993zM16.002 8.999a4 4 0 11-8 0 4 4 0 018 0z" /> | ||||
|                   </svg> | ||||
|                 </span> | ||||
|                 <button type="button" class="ml-5 bg-white py-2 px-3 border border-gray-300 rounded-md shadow-sm text-sm leading-4 font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"> | ||||
|                   Change | ||||
|                 </button> | ||||
|               </div> | ||||
|             </div> | ||||
|  | ||||
|             <div> | ||||
|               <!-- svelte-ignore a11y-label-has-associated-control --> | ||||
|               <label class="block text-sm font-medium text-gray-700"> | ||||
|                 Cover photo | ||||
|               </label> | ||||
|               <div class="mt-2 flex justify-center px-6 pt-5 pb-6 border-2 border-gray-300 border-dashed rounded-md"> | ||||
|                 <div class="space-y-1 text-center"> | ||||
|                   <svg class="mx-auto h-12 w-12 text-gray-400" stroke="currentColor" fill="none" viewBox="0 0 48 48" aria-hidden="true"> | ||||
|                     <path d="M28 8H12a4 4 0 00-4 4v20m32-12v8m0 0v8a4 4 0 01-4 4H12a4 4 0 01-4-4v-4m32-4l-3.172-3.172a4 4 0 00-5.656 0L28 28M8 32l9.172-9.172a4 4 0 015.656 0L28 28m0 0l4 4m4-24h8m-4-4v8m-12 4h.02" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /> | ||||
|                   </svg> | ||||
|                   <div class="flex text-sm text-gray-600"> | ||||
|                     <label for="file-upload" class="relative cursor-pointer bg-white rounded-md font-medium text-indigo-600 hover:text-indigo-500 focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-indigo-500"> | ||||
|                       <span>Upload a file</span> | ||||
|                       <input id="file-upload" name="file-upload" type="file" class="sr-only"> | ||||
|                     </label> | ||||
|                     <p class="pl-1">or drag and drop</p> | ||||
|                   </div> | ||||
|                   <p class="text-xs text-gray-500"> | ||||
|                     PNG, JPG, GIF up to 10MB | ||||
|                   </p> | ||||
|                 </div> | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|           <div class="px-4 py-3 bg-gray-50 text-right sm:px-6"> | ||||
|             <button type="submit" class="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"> | ||||
|               Save | ||||
|             </button> | ||||
|           </div> | ||||
|         </div> | ||||
|       </form> | ||||
|     </div> | ||||
|   </div> | ||||
| </div> | ||||
|  | ||||
| <div class="hidden sm:block" aria-hidden="true"> | ||||
|   <div class="py-5"> | ||||
|     <div class="border-t border-gray-200"></div> | ||||
|   </div> | ||||
| </div> | ||||
|  | ||||
| <div class="mt-10 sm:mt-0"> | ||||
|   <div class="md:grid md:grid-cols-3 md:gap-6"> | ||||
|     <div class="md:col-span-1"> | ||||
|       <div class="px-4 sm:px-0"> | ||||
|         <h3 class="text-lg font-medium leading-6 text-gray-900">Personal Information</h3> | ||||
|         <p class="mt-1 text-sm text-gray-600"> | ||||
|           Use a permanent address where you can receive mail. | ||||
|         </p> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="mt-5 md:mt-0 md:col-span-2"> | ||||
|       <form action="#" method="POST"> | ||||
|         <div class="shadow overflow-hidden sm:rounded-md"> | ||||
|           <div class="px-4 py-5 bg-white sm:p-6"> | ||||
|             <div class="grid grid-cols-6 gap-6"> | ||||
|               <div class="col-span-6 sm:col-span-3"> | ||||
|                 <label for="first_name" class="block text-sm font-medium text-gray-700">First name</label> | ||||
|                 <input type="text" name="first_name" id="first_name" autocomplete="given-name" class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md"> | ||||
|               </div> | ||||
|  | ||||
|               <div class="col-span-6 sm:col-span-3"> | ||||
|                 <label for="last_name" class="block text-sm font-medium text-gray-700">Last name</label> | ||||
|                 <input type="text" name="last_name" id="last_name" autocomplete="family-name" class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md"> | ||||
|               </div> | ||||
|  | ||||
|               <div class="col-span-6 sm:col-span-4"> | ||||
|                 <label for="email_address" class="block text-sm font-medium text-gray-700">Email address</label> | ||||
|                 <input type="text" name="email_address" id="email_address" autocomplete="email" class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md"> | ||||
|               </div> | ||||
|  | ||||
|               <div class="col-span-6 sm:col-span-3"> | ||||
|                 <label for="country" class="block text-sm font-medium text-gray-700">Country / Region</label> | ||||
|                 <select id="country" name="country" autocomplete="country" class="mt-1 block w-full py-2 px-3 border border-gray-300 bg-white rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"> | ||||
|                   <option>United States</option> | ||||
|                   <option>Canada</option> | ||||
|                   <option>Mexico</option> | ||||
|                 </select> | ||||
|               </div> | ||||
|  | ||||
|               <div class="col-span-6"> | ||||
|                 <label for="street_address" class="block text-sm font-medium text-gray-700">Street address</label> | ||||
|                 <input type="text" name="street_address" id="street_address" autocomplete="street-address" class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md"> | ||||
|               </div> | ||||
|  | ||||
|               <div class="col-span-6 sm:col-span-6 lg:col-span-2"> | ||||
|                 <label for="city" class="block text-sm font-medium text-gray-700">City</label> | ||||
|                 <input type="text" name="city" id="city" class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md"> | ||||
|               </div> | ||||
|  | ||||
|               <div class="col-span-6 sm:col-span-3 lg:col-span-2"> | ||||
|                 <label for="state" class="block text-sm font-medium text-gray-700">State / Province</label> | ||||
|                 <input type="text" name="state" id="state" class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md"> | ||||
|               </div> | ||||
|  | ||||
|               <div class="col-span-6 sm:col-span-3 lg:col-span-2"> | ||||
|                 <label for="postal_code" class="block text-sm font-medium text-gray-700">ZIP / Postal</label> | ||||
|                 <input type="text" name="postal_code" id="postal_code" autocomplete="postal-code" class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md"> | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|           <div class="px-4 py-3 bg-gray-50 text-right sm:px-6"> | ||||
|             <button type="submit" class="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"> | ||||
|               Save | ||||
|             </button> | ||||
|           </div> | ||||
|         </div> | ||||
|       </form> | ||||
|     </div> | ||||
|   </div> | ||||
| </div> | ||||
|  | ||||
| <div class="hidden sm:block" aria-hidden="true"> | ||||
|   <div class="py-5"> | ||||
|     <div class="border-t border-gray-200"></div> | ||||
|   </div> | ||||
| </div> | ||||
|  | ||||
| <div class="mt-10 sm:mt-0"> | ||||
|   <div class="md:grid md:grid-cols-3 md:gap-6"> | ||||
|     <div class="md:col-span-1"> | ||||
|       <div class="px-4 sm:px-0"> | ||||
|         <h3 class="text-lg font-medium leading-6 text-gray-900">Notifications</h3> | ||||
|         <p class="mt-1 text-sm text-gray-600"> | ||||
|           Decide which communications you'd like to receive and how. | ||||
|         </p> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="mt-5 md:mt-0 md:col-span-2"> | ||||
|       <form action="#" method="POST"> | ||||
|         <div class="shadow overflow-hidden sm:rounded-md"> | ||||
|           <div class="px-4 py-5 bg-white space-y-6 sm:p-6"> | ||||
|             <fieldset> | ||||
|               <legend class="text-base font-medium text-gray-900">By Email</legend> | ||||
|               <div class="mt-4 space-y-4"> | ||||
|                 <div class="flex items-start"> | ||||
|                   <div class="flex items-center h-5"> | ||||
|                     <input id="comments" name="comments" type="checkbox" class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"> | ||||
|                   </div> | ||||
|                   <div class="ml-3 text-sm"> | ||||
|                     <label for="comments" class="font-medium text-gray-700">Comments</label> | ||||
|                     <p class="text-gray-500">Get notified when someones posts a comment on a posting.</p> | ||||
|                   </div> | ||||
|                 </div> | ||||
|                 <div class="flex items-start"> | ||||
|                   <div class="flex items-center h-5"> | ||||
|                     <input id="candidates" name="candidates" type="checkbox" class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"> | ||||
|                   </div> | ||||
|                   <div class="ml-3 text-sm"> | ||||
|                     <label for="candidates" class="font-medium text-gray-700">Candidates</label> | ||||
|                     <p class="text-gray-500">Get notified when a candidate applies for a job.</p> | ||||
|                   </div> | ||||
|                 </div> | ||||
|                 <div class="flex items-start"> | ||||
|                   <div class="flex items-center h-5"> | ||||
|                     <input id="offers" name="offers" type="checkbox" class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"> | ||||
|                   </div> | ||||
|                   <div class="ml-3 text-sm"> | ||||
|                     <label for="offers" class="font-medium text-gray-700">Offers</label> | ||||
|                     <p class="text-gray-500">Get notified when a candidate accepts or rejects an offer.</p> | ||||
|                   </div> | ||||
|                 </div> | ||||
|               </div> | ||||
|             </fieldset> | ||||
|             <fieldset> | ||||
|               <div> | ||||
|                 <legend class="text-base font-medium text-gray-900">Push Notifications</legend> | ||||
|                 <p class="text-sm text-gray-500">These are delivered via SMS to your mobile phone.</p> | ||||
|               </div> | ||||
|               <div class="mt-4 space-y-4"> | ||||
|                 <div class="flex items-center"> | ||||
|                   <input id="push_everything" name="push_notifications" type="radio" class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300"> | ||||
|                   <label for="push_everything" class="ml-3 block text-sm font-medium text-gray-700"> | ||||
|                     Everything | ||||
|                   </label> | ||||
|                 </div> | ||||
|                 <div class="flex items-center"> | ||||
|                   <input id="push_email" name="push_notifications" type="radio" class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300"> | ||||
|                   <label for="push_email" class="ml-3 block text-sm font-medium text-gray-700"> | ||||
|                     Same as email | ||||
|                   </label> | ||||
|                 </div> | ||||
|                 <div class="flex items-center"> | ||||
|                   <input id="push_nothing" name="push_notifications" type="radio" class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300"> | ||||
|                   <label for="push_nothing" class="ml-3 block text-sm font-medium text-gray-700"> | ||||
|                     No push notifications | ||||
|                   </label> | ||||
|                 </div> | ||||
|               </div> | ||||
|             </fieldset> | ||||
|           </div> | ||||
|           <div class="px-4 py-3 bg-gray-50 text-right sm:px-6"> | ||||
|             <button type="submit" class="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"> | ||||
|               Save | ||||
|             </button> | ||||
|           </div> | ||||
|         </div> | ||||
|       </form> | ||||
|     </div> | ||||
|   </div> | ||||
| </div> | ||||
| @@ -1,97 +0,0 @@ | ||||
| <script> | ||||
|   import store from "../store.js"; | ||||
|   store.init(); | ||||
|   const login = () => { | ||||
|     store.login(); | ||||
|   }; | ||||
| </script> | ||||
|  | ||||
| <div | ||||
|   class="min-h-screen flex items-center justify-center bg-gray-50 py-12 px-4 sm:px-6 lg:px-8"> | ||||
|   <div class="max-w-md w-full space-y-8"> | ||||
|     <div> | ||||
|       <img | ||||
|         class="mx-auto h-12 w-auto" | ||||
|         src="/lfk-logo.png" | ||||
|         alt="" /> | ||||
|       <h2 class="mt-6 text-center text-3xl font-extrabold text-gray-900"> | ||||
|         Sign in to your account | ||||
|       </h2> | ||||
|       <p class="mt-2 text-center text-sm text-gray-600"> | ||||
|         Or | ||||
|         <a href="#" class="font-medium text-indigo-600 hover:text-indigo-500"> | ||||
|           start your 14-day free trial | ||||
|         </a> | ||||
|       </p> | ||||
|     </div> | ||||
|     <div> | ||||
|       <input type="hidden" name="remember" value="true" /> | ||||
|       <div class="rounded-md shadow-sm -space-y-px"> | ||||
|         <div> | ||||
|           <label for="email-address" class="sr-only">Email address</label> | ||||
|           <input | ||||
|             id="email-address" | ||||
|             name="email" | ||||
|             type="email" | ||||
|             autocomplete="email" | ||||
|             required | ||||
|             class="appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-t-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm" | ||||
|             placeholder="Email address" /> | ||||
|         </div> | ||||
|         <div> | ||||
|           <label for="password" class="sr-only">Password</label> | ||||
|           <input | ||||
|             id="password" | ||||
|             name="password" | ||||
|             type="password" | ||||
|             autocomplete="current-password" | ||||
|             required | ||||
|             class="appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-b-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm" | ||||
|             placeholder="Password" /> | ||||
|         </div> | ||||
|       </div> | ||||
|  | ||||
|       <div class="flex items-center justify-between"> | ||||
|         <div class="flex items-center"> | ||||
|           <input | ||||
|             id="remember_me" | ||||
|             name="remember_me" | ||||
|             type="checkbox" | ||||
|             class="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded" /> | ||||
|           <label for="remember_me" class="ml-2 block text-sm text-gray-900"> | ||||
|             Remember me | ||||
|           </label> | ||||
|         </div> | ||||
|  | ||||
|         <div class="text-sm"> | ||||
|           <a href="#" class="font-medium text-indigo-600 hover:text-indigo-500"> | ||||
|             Forgot your password? | ||||
|           </a> | ||||
|         </div> | ||||
|       </div> | ||||
|  | ||||
|       <div> | ||||
|         <button | ||||
|         on:click="{login}" | ||||
|           type="submit" | ||||
|           class="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"> | ||||
|           <span class="absolute left-0 inset-y-0 flex items-center pl-3"> | ||||
|             <!-- Heroicon name: lock-closed --> | ||||
|             <svg | ||||
|               class="h-5 w-5 text-indigo-500 group-hover:text-indigo-400" | ||||
|               xmlns="http://www.w3.org/2000/svg" | ||||
|               viewBox="0 0 20 20" | ||||
|               fill="currentColor" | ||||
|               aria-hidden="true"> | ||||
|               <path | ||||
|                 fill-rule="evenodd" | ||||
|                 d="M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z" | ||||
|                 clip-rule="evenodd" /> | ||||
|             </svg> | ||||
|           </span> | ||||
|           Sign in | ||||
|         </button> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| </div> | ||||
| @@ -1,25 +0,0 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import StatCards from "./StatCards.svelte"; | ||||
|   import store from "../store"; | ||||
|   import ComponentDump from "./ComponentDump.svelte"; | ||||
|   let navOpen = false; | ||||
| </script> | ||||
|  | ||||
| <div | ||||
|   class="p-5 overflow-x-hidden" | ||||
|   on:click={() => { | ||||
|     navOpen = false; | ||||
|   }}> | ||||
|   <!-- <div class="border-4 border-dashed rounded h-96" /> --> | ||||
|   <h1 class="text-3xl leading-tight"> | ||||
|     <span class="font-extrabold">{$_('dashboard-title')}</span> <span> | ||||
|       - | ||||
|       {$_('dashboard-greeting')}, | ||||
|       <span | ||||
|         class="text-blue-500">{store.state.jwtinfo.userdetails.firstname}</span> | ||||
|       👋</span> | ||||
|   </h1> | ||||
|   <StatCards /> | ||||
|   <ComponentDump /> | ||||
| </div> | ||||
| @@ -1,83 +0,0 @@ | ||||
| <script> | ||||
|   export let params; | ||||
| </script> | ||||
|  | ||||
| <section class="container p-5"> | ||||
|   <span class="mb-1 text-3xl font-extrabold leading-tight"> | ||||
|     Orgs | ||||
|   </span> | ||||
|   <p class="mb-8 text-lg text-gray-500"> | ||||
|     configure the tracks & minimum lap times | ||||
|   </p> | ||||
|   <div class="flex flex-row mb-4"> | ||||
|     <div class="w-full"> | ||||
|       <nav class="w-full flex"> | ||||
|         <ol class="list-none flex flex-row items-center justify-start"> | ||||
|           <li class="mr-2 flex items-center"> | ||||
|             <svg | ||||
|               stroke="currentColor" | ||||
|               fill="none" | ||||
|               stroke-width="2" | ||||
|               viewBox="0 0 24 24" | ||||
|               stroke-linecap="round" | ||||
|               stroke-linejoin="round" | ||||
|               class="h-3 w-3 stroke-current" | ||||
|               height="1em" | ||||
|               width="1em" | ||||
|               xmlns="http://www.w3.org/2000/svg"><path | ||||
|                 d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" /> | ||||
|               <polyline points="9 22 9 12 15 12 15 22" /></svg> | ||||
|           </li> | ||||
|           <li class="flex items-center"> | ||||
|             <a class="mr-2" href="/">Home</a><svg | ||||
|               stroke="currentColor" | ||||
|               fill="none" | ||||
|               stroke-width="2" | ||||
|               viewBox="0 0 24 24" | ||||
|               stroke-linecap="round" | ||||
|               stroke-linejoin="round" | ||||
|               class="h-3 w-3 mr-2 stroke-current" | ||||
|               height="1em" | ||||
|               width="1em" | ||||
|               xmlns="http://www.w3.org/2000/svg"><line | ||||
|                 x1="5" | ||||
|                 y1="12" | ||||
|                 x2="19" | ||||
|                 y2="12" /> | ||||
|               <polyline points="12 5 19 12 12 19" /></svg> | ||||
|           </li> | ||||
|           <li class="mr-2 flex items-center"> | ||||
|             <svg | ||||
|               xmlns="http://www.w3.org/2000/svg" | ||||
|               viewBox="0 0 24 24" | ||||
|               width="24" | ||||
|               height="24"><path fill="none" d="M0 0h24v24H0z" /> | ||||
|               <path | ||||
|                 d="M21 20h2v2H1v-2h2V3a1 1 0 0 1 1-1h16a1 1 0 0 1 1 1v17zm-2 0V4H5v16h14zM8 11h3v2H8v-2zm0-4h3v2H8V7zm0 8h3v2H8v-2zm5 0h3v2h-3v-2zm0-4h3v2h-3v-2zm0-4h3v2h-3V7z" /></svg> | ||||
|           </li> | ||||
|           <li class="flex items-center"> | ||||
|             <a class="mr-2" href="./">Orgs</a><svg | ||||
|               stroke="currentColor" | ||||
|               fill="none" | ||||
|               stroke-width="2" | ||||
|               viewBox="0 0 24 24" | ||||
|               stroke-linecap="round" | ||||
|               stroke-linejoin="round" | ||||
|               class="h-3 w-3 mr-2 stroke-current" | ||||
|               height="1em" | ||||
|               width="1em" | ||||
|               xmlns="http://www.w3.org/2000/svg"><line | ||||
|                 x1="5" | ||||
|                 y1="12" | ||||
|                 x2="19" | ||||
|                 y2="12" /> | ||||
|               <polyline points="12 5 19 12 12 19" /></svg> | ||||
|           </li> | ||||
|           <li class="flex items-center"> | ||||
|             <span class="mr-2">Org-Details #{params.orgid}</span> | ||||
|           </li> | ||||
|         </ol> | ||||
|       </nav> | ||||
|     </div> | ||||
|   </div> | ||||
| </section> | ||||
| @@ -1,13 +0,0 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
| </script> | ||||
|  | ||||
| <section class="container p-5"> | ||||
|   <span class="mb-1 text-3xl font-extrabold leading-tight"> | ||||
|     Orgs | ||||
|   </span> | ||||
|   <p class="mb-8 text-lg text-gray-500"> | ||||
|     add, delete, edit organizations | ||||
|   </p> | ||||
|   <nav><a class="underline" href="./1">Org 1</a></nav> | ||||
| </section> | ||||
| @@ -1,103 +0,0 @@ | ||||
| <!-- This example requires Tailwind CSS v2.0+ --> | ||||
| <div | ||||
|   class="bg-white px-4 py-3 flex items-center justify-between border-t border-gray-200 sm:px-6"> | ||||
|   <div class="flex-1 flex justify-between sm:hidden"> | ||||
|     <a | ||||
|       href="#" | ||||
|       class="relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:text-gray-500"> | ||||
|       Previous | ||||
|     </a> | ||||
|     <a | ||||
|       href="#" | ||||
|       class="ml-3 relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:text-gray-500"> | ||||
|       Next | ||||
|     </a> | ||||
|   </div> | ||||
|   <div class="hidden sm:flex-1 sm:flex sm:items-center sm:justify-between"> | ||||
|     <div> | ||||
|       <p class="text-sm text-gray-700"> | ||||
|         Showing | ||||
|         <span class="font-medium">1</span> | ||||
|         to | ||||
|         <span class="font-medium">10</span> | ||||
|         of | ||||
|         <span class="font-medium">97</span> | ||||
|         results | ||||
|       </p> | ||||
|     </div> | ||||
|     <div> | ||||
|       <nav | ||||
|         class="relative z-0 inline-flex shadow-sm -space-x-px" | ||||
|         aria-label="Pagination"> | ||||
|         <a | ||||
|           href="#" | ||||
|           class="relative inline-flex items-center px-2 py-2 rounded-l-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50"> | ||||
|           <span class="sr-only">Previous</span> | ||||
|           <!-- Heroicon name: chevron-left --> | ||||
|           <svg | ||||
|             class="h-5 w-5" | ||||
|             xmlns="http://www.w3.org/2000/svg" | ||||
|             viewBox="0 0 20 20" | ||||
|             fill="currentColor" | ||||
|             aria-hidden="true"> | ||||
|             <path | ||||
|               fill-rule="evenodd" | ||||
|               d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z" | ||||
|               clip-rule="evenodd" /> | ||||
|           </svg> | ||||
|         </a> | ||||
|         <a | ||||
|           href="#" | ||||
|           class="relative inline-flex items-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50"> | ||||
|           1 | ||||
|         </a> | ||||
|         <a | ||||
|           href="#" | ||||
|           class="relative inline-flex items-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50"> | ||||
|           2 | ||||
|         </a> | ||||
|         <a | ||||
|           href="#" | ||||
|           class="hidden md:inline-flex relative items-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50"> | ||||
|           3 | ||||
|         </a> | ||||
|         <span | ||||
|           class="relative inline-flex items-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-700"> | ||||
|           ... | ||||
|         </span> | ||||
|         <a | ||||
|           href="#" | ||||
|           class="hidden md:inline-flex relative items-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50"> | ||||
|           8 | ||||
|         </a> | ||||
|         <a | ||||
|           href="#" | ||||
|           class="relative inline-flex items-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50"> | ||||
|           9 | ||||
|         </a> | ||||
|         <a | ||||
|           href="#" | ||||
|           class="relative inline-flex items-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50"> | ||||
|           10 | ||||
|         </a> | ||||
|         <a | ||||
|           href="#" | ||||
|           class="relative inline-flex items-center px-2 py-2 rounded-r-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50"> | ||||
|           <span class="sr-only">Next</span> | ||||
|           <!-- Heroicon name: chevron-right --> | ||||
|           <svg | ||||
|             class="h-5 w-5" | ||||
|             xmlns="http://www.w3.org/2000/svg" | ||||
|             viewBox="0 0 20 20" | ||||
|             fill="currentColor" | ||||
|             aria-hidden="true"> | ||||
|             <path | ||||
|               fill-rule="evenodd" | ||||
|               d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" | ||||
|               clip-rule="evenodd" /> | ||||
|           </svg> | ||||
|         </a> | ||||
|       </nav> | ||||
|     </div> | ||||
|   </div> | ||||
| </div> | ||||
| @@ -1,242 +0,0 @@ | ||||
| <div class="min-h-screen w-full p-4"> | ||||
|   <div class="section-title w-full mb-6 pt-3"> | ||||
|     <div class="flex flex-row items-center justify-between mb-4"> | ||||
|       <div class="flex flex-col"> | ||||
|         <div class="text-xs uppercase font-light text-grey-500">Pages</div> | ||||
|         <div class="text-xl font-bold">User profile</div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
|   <div | ||||
|     class="w-full p-4 mb-4 rounded-lg bg-white border border-grey-100 dark:bg-grey-895 dark:border-grey-890"> | ||||
|     <div class="flex flex-row items-center justify-start p-4"> | ||||
|       <div class="flex-shrink-0 w-24"> | ||||
|         <img | ||||
|           src="/images/faces/m1.png" | ||||
|           alt="media" | ||||
|           class="shadow rounded-full h-20 w-20 shadow-outline mb-2" /> | ||||
|       </div> | ||||
|       <div class="py-2 px-2"> | ||||
|         <p class="text-base font-bold whitespace-no-wrap">Lucas Smith</p> | ||||
|         <p class="text-sm text-grey-500 whitespace-no-wrap"> | ||||
|           Vital Database Dude | ||||
|         </p> | ||||
|         <div | ||||
|           class="flex flex-row items-center justify-start w-full py-1 space-x-2"> | ||||
|           <svg | ||||
|             stroke="currentColor" | ||||
|             fill="none" | ||||
|             stroke-width="2" | ||||
|             viewBox="0 0 24 24" | ||||
|             stroke-linecap="round" | ||||
|             stroke-linejoin="round" | ||||
|             class="stroke-current text-xl text-twitter" | ||||
|             height="1em" | ||||
|             width="1em" | ||||
|             xmlns="http://www.w3.org/2000/svg"><path | ||||
|               d="M23 3a10.9 10.9 0 0 1-3.14 1.53 4.48 4.48 0 0 0-7.86 3v1A10.66 10.66 0 0 1 3 4s-4 9 5 13a11.64 11.64 0 0 1-7 2c9 5 20 0 20-11.5a4.5 4.5 0 0 0-.08-.83A7.72 7.72 0 0 0 23 3z" /></svg><svg | ||||
|             stroke="currentColor" | ||||
|             fill="none" | ||||
|             stroke-width="2" | ||||
|             viewBox="0 0 24 24" | ||||
|             stroke-linecap="round" | ||||
|             stroke-linejoin="round" | ||||
|             class="stroke-current text-xl text-facebook" | ||||
|             height="1em" | ||||
|             width="1em" | ||||
|             xmlns="http://www.w3.org/2000/svg"><path | ||||
|               d="M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z" /></svg><svg | ||||
|             stroke="currentColor" | ||||
|             fill="none" | ||||
|             stroke-width="2" | ||||
|             viewBox="0 0 24 24" | ||||
|             stroke-linecap="round" | ||||
|             stroke-linejoin="round" | ||||
|             class="stroke-current text-xl text-instagram" | ||||
|             height="1em" | ||||
|             width="1em" | ||||
|             xmlns="http://www.w3.org/2000/svg"><rect | ||||
|               x="2" | ||||
|               y="2" | ||||
|               width="20" | ||||
|               height="20" | ||||
|               rx="5" | ||||
|               ry="5" /> | ||||
|             <path d="M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z" /> | ||||
|             <line x1="17.5" y1="6.5" x2="17.5" y2="6.5" /></svg> | ||||
|         </div> | ||||
|       </div> | ||||
|       <div class="ml-auto flex-shrink-0 space-x-2 hidden lg:flex"> | ||||
|         <button | ||||
|           class="btn btn-default btn-rounded bg-blue-500 hover:bg-blue-600 text-white">Subscribe</button><button | ||||
|           class="btn btn-default btn-rounded bg-blue-500 hover:bg-blue-600 text-white">Follow</button> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="flex flex-wrap"> | ||||
|       <div class="w-full p-4"> | ||||
|         <div class="flex flex-wrap flex-col w-full tabs"> | ||||
|           <div class="flex lg:flex-wrap flex-row lg:space-x-2"> | ||||
|             <div class="flex-none"> | ||||
|               <button class="tab tab-underline tab-active" type="button">Account | ||||
|                 settings</button> | ||||
|             </div> | ||||
|             <div class="flex-none"> | ||||
|               <button class="tab tab-underline" type="button">Email preferences</button> | ||||
|             </div> | ||||
|             <div class="flex-none"> | ||||
|               <button class="tab tab-underline" type="button">Security settings</button> | ||||
|             </div> | ||||
|           </div> | ||||
|           <div class="tab-content block"> | ||||
|             <div class="py-4 w-full lg:w-1/2"> | ||||
|               <div class="flex flex-col"> | ||||
|                 <form class="form flex flex-wrap w-full"> | ||||
|                   <div class="w-full"> | ||||
|                     <div class="form-element"> | ||||
|                       <div class="form-label">First name</div><input | ||||
|                         name="first-name" | ||||
|                         type="text" | ||||
|                         class="form-input " | ||||
|                         placeholder="Enter you first name" /> | ||||
|                     </div> | ||||
|                     <div class="form-element"> | ||||
|                       <div class="form-label">Last name</div><input | ||||
|                         name="last-name" | ||||
|                         type="text" | ||||
|                         class="form-input " | ||||
|                         placeholder="Enter you last name" /> | ||||
|                     </div> | ||||
|                     <div class="form-element"> | ||||
|                       <div class="form-label">Email address</div><input | ||||
|                         name="email" | ||||
|                         type="email" | ||||
|                         class="form-input " | ||||
|                         placeholder="Enter you email address" /> | ||||
|                     </div> | ||||
|                     <div class="form-element"> | ||||
|                       <div class="form-label">Company</div><input | ||||
|                         name="company" | ||||
|                         type="text" | ||||
|                         class="form-input " | ||||
|                         placeholder="Enter you company" /> | ||||
|                     </div> | ||||
|                     <div class="form-element"> | ||||
|                       <div class="form-label">Position</div><input | ||||
|                         name="position" | ||||
|                         type="text" | ||||
|                         class="form-input " | ||||
|                         placeholder="Enter you position" /> | ||||
|                     </div> | ||||
|                     <div class="form-element"> | ||||
|                       <div class="form-label">Language</div><select | ||||
|                         name="language" | ||||
|                         class="form-select "><option>Select language</option> | ||||
|                         <option value="english">English</option> | ||||
|                         <option value="spanish">Spanish</option> | ||||
|                         <option value="portuguese">Portuguese</option></select> | ||||
|                     </div> | ||||
|                   </div><input | ||||
|                     type="submit" | ||||
|                     class="btn btn-default bg-blue-500 hover:bg-blue-600 text-white btn-rounded" /> | ||||
|                 </form> | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|           <div class="tab-content hidden"> | ||||
|             <div class="py-4 w-full lg:w-1/2"> | ||||
|               <div class="flex flex-col"> | ||||
|                 <form class="form flex flex-wrap w-full"> | ||||
|                   <div class="w-full"> | ||||
|                     <div class="form-element"> | ||||
|                       <div class="form-label">Current email</div><input | ||||
|                         name="email" | ||||
|                         type="email" | ||||
|                         class="form-input " | ||||
|                         placeholder="Enter you current email address" /> | ||||
|                     </div> | ||||
|                     <div class="form-element"> | ||||
|                       <div class="form-label">New email</div><input | ||||
|                         name="email" | ||||
|                         type="email" | ||||
|                         class="form-input " | ||||
|                         placeholder="Enter you new email address" /> | ||||
|                     </div> | ||||
|                     <div class="form-element"> | ||||
|                       <div class="form-label">Daily updates</div> | ||||
|                       <div class="flex items-center justify-start space-x-2"> | ||||
|                         <label | ||||
|                           class="flex items-center justify-start space-x-2"><input | ||||
|                             type="radio" | ||||
|                             name="daily-updates" | ||||
|                             class="form-radio h-4 w-4 " | ||||
|                             value="yes" /><span | ||||
|                             class="">Yes</span></label><label | ||||
|                           class="flex items-center justify-start space-x-2"><input | ||||
|                             type="radio" | ||||
|                             name="daily-updates" | ||||
|                             class="form-radio h-4 w-4 " | ||||
|                             value="no" /><span class="">No</span></label> | ||||
|                       </div> | ||||
|                     </div> | ||||
|                     <div class="form-element"> | ||||
|                       <div class="form-label">Weekly updates</div> | ||||
|                       <div class="flex items-center justify-start space-x-2"> | ||||
|                         <label | ||||
|                           class="flex items-center justify-start space-x-2"><input | ||||
|                             type="radio" | ||||
|                             name="weekle-updates" | ||||
|                             class="form-radio h-4 w-4 " | ||||
|                             value="yes" /><span | ||||
|                             class="">Yes</span></label><label | ||||
|                           class="flex items-center justify-start space-x-2"><input | ||||
|                             type="radio" | ||||
|                             name="weekle-updates" | ||||
|                             class="form-radio h-4 w-4 " | ||||
|                             value="no" /><span class="">No</span></label> | ||||
|                       </div> | ||||
|                     </div> | ||||
|                   </div><input | ||||
|                     type="submit" | ||||
|                     class="btn btn-default bg-blue-500 hover:bg-blue-600 text-white btn-rounded" /> | ||||
|                 </form> | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|           <div class="tab-content hidden"> | ||||
|             <div class="py-4 w-full lg:w-1/2"> | ||||
|               <div class="flex flex-col"> | ||||
|                 <form class="form flex flex-wrap w-full"> | ||||
|                   <div class="w-full"> | ||||
|                     <div class="form-element"> | ||||
|                       <div class="form-label">Current password</div><input | ||||
|                         name="current-password" | ||||
|                         type="password" | ||||
|                         class="form-input " | ||||
|                         placeholder="Enter your current password" /> | ||||
|                     </div> | ||||
|                     <div class="form-element"> | ||||
|                       <div class="form-label">New password</div><input | ||||
|                         name="new-password" | ||||
|                         type="password" | ||||
|                         class="form-input " | ||||
|                         placeholder="Enter your new password" /> | ||||
|                     </div> | ||||
|                     <div class="form-element"> | ||||
|                       <div class="form-label">Confirm new password</div><input | ||||
|                         name="confirm-new-password" | ||||
|                         type="password" | ||||
|                         class="form-input " | ||||
|                         placeholder="Enter your new password confirmation" /> | ||||
|                     </div> | ||||
|                   </div><input | ||||
|                     type="submit" | ||||
|                     class="btn btn-default bg-blue-500 hover:bg-blue-600 text-white btn-rounded" /> | ||||
|                 </form> | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| </div> | ||||
| @@ -1,10 +0,0 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
| </script> | ||||
|  | ||||
| <section class="container p-5"> | ||||
|   <span class="mb-1 text-3xl font-extrabold leading-tight"> | ||||
|     {$_('runners')} | ||||
|   </span> | ||||
|   <p class="mb-8 text-lg text-gray-500">läuft bei ihnen</p> | ||||
| </section> | ||||
| @@ -1,35 +0,0 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
| import FormLayout from "./FormLayout.svelte"; | ||||
| </script> | ||||
|  | ||||
| <div class="pt-12 px-4 sm:px-6 lg:px-8 lg:pt-20 bg-gray-900 pb-12"> | ||||
|   <div class="text-center mb-8"> | ||||
|     <h1 | ||||
|       class="mt-9 font-display text-4xl leading-none font-semibold text-white sm:text-5xl lg:text-6xl"> | ||||
|       🔨<br />{$_('settings')} | ||||
|     </h1> | ||||
|     <p | ||||
|       class="mt-2 max-w-xl mx-auto text-xl lg:max-w-3xl lg:text-2xl text-gray-300"> | ||||
|       <span class="text-lg">configure your profile however you want</span> | ||||
|     </p> | ||||
|   </div> | ||||
| </div> | ||||
|  | ||||
| <div class="pt-0 pb-16 bg-gray-50 overflow-hidden lg:pt-12 lg:py-24"> | ||||
|   <div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8"> | ||||
|     <!-- <h2 class="text-4xl font-display font-semibold text-gray-900 md:text-5xl"> | ||||
|       General | ||||
|     </h2> --> | ||||
|     <div | ||||
|       class="max-w-3xl mx-auto text-xl leading-8 font-medium text-gray-900 mb-16"> | ||||
|       <p class="text-center"> | ||||
|         Lorem ipsum dolor sit amet consectetur, adipisicing elit. Temporibus et | ||||
|         amet voluptate nulla accusantium vero blanditiis nobis facere veritatis. | ||||
|         Impedit deserunt saepe aliquid unde consequuntur officia consequatur | ||||
|         fugit iusto dolorem? | ||||
|       </p> | ||||
|     </div> | ||||
|     <FormLayout /> | ||||
|   </div> | ||||
| </div> | ||||
| @@ -1,82 +0,0 @@ | ||||
| <script> | ||||
|   let open = false; | ||||
| </script> | ||||
|  | ||||
| <div class="md:flex flex-col md:flex-row h-screen w-full"> | ||||
|   <div | ||||
|     class="flex flex-col w-full md:w-64 text-gray-700 bg-white dark-mode:text-gray-200 dark-mode:bg-gray-800 flex-shrink-0"> | ||||
|     <div | ||||
|       class="flex-shrink-0 px-8 py-4 flex flex-row items-center justify-between"> | ||||
|       <a | ||||
|         href="/#/test" | ||||
|         class="text-lg font-semibold tracking-widest text-gray-900 uppercase rounded-lg dark-mode:text-white focus:outline-none focus:shadow-outline">Sidebar</a> | ||||
|       <button | ||||
|         class="rounded-lg md:hidden focus:outline-none focus:shadow-outline"> | ||||
|         <svg fill="currentColor" viewBox="0 0 20 20" class="w-6 h-6"> | ||||
|           {#if open} | ||||
|             <path | ||||
|               fill-rule="evenodd" | ||||
|               d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" | ||||
|               clip-rule="evenodd" /> | ||||
|           {/if} | ||||
|           {#if !open} | ||||
|             <path | ||||
|               fill-rule="evenodd" | ||||
|               d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM9 15a1 1 0 011-1h6a1 1 0 110 2h-6a1 1 0 01-1-1z" | ||||
|               clip-rule="evenodd" /> | ||||
|           {/if} | ||||
|         </svg> | ||||
|       </button> | ||||
|     </div> | ||||
|     <nav | ||||
|       :class:block={open} | ||||
|       :class:hidden={!open} | ||||
|       class="flex-grow md:block px-4 pb-4 md:pb-0 md:overflow-y-auto"> | ||||
|       <a | ||||
|         class="block px-4 py-2 mt-2 text-sm font-semibold text-gray-900 bg-gray-200 rounded-lg dark-mode:bg-gray-700 dark-mode:hover:bg-gray-600 dark-mode:focus:bg-gray-600 dark-mode:focus:text-white dark-mode:hover:text-white dark-mode:text-gray-200 hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:bg-gray-200 focus:outline-none focus:shadow-outline" | ||||
|         href="#">Blog</a> | ||||
|       <a | ||||
|         class="block px-4 py-2 mt-2 text-sm font-semibold text-gray-900 bg-transparent rounded-lg dark-mode:bg-transparent dark-mode:hover:bg-gray-600 dark-mode:focus:bg-gray-600 dark-mode:focus:text-white dark-mode:hover:text-white dark-mode:text-gray-200 hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:bg-gray-200 focus:outline-none focus:shadow-outline" | ||||
|         href="#">Portfolio</a> | ||||
|       <a | ||||
|         class="block px-4 py-2 mt-2 text-sm font-semibold text-gray-900 bg-transparent rounded-lg dark-mode:bg-transparent dark-mode:hover:bg-gray-600 dark-mode:focus:bg-gray-600 dark-mode:focus:text-white dark-mode:hover:text-white dark-mode:text-gray-200 hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:bg-gray-200 focus:outline-none focus:shadow-outline" | ||||
|         href="#">About</a> | ||||
|       <a | ||||
|         class="block px-4 py-2 mt-2 text-sm font-semibold text-gray-900 bg-transparent rounded-lg dark-mode:bg-transparent dark-mode:hover:bg-gray-600 dark-mode:focus:bg-gray-600 dark-mode:focus:text-white dark-mode:hover:text-white dark-mode:text-gray-200 hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:bg-gray-200 focus:outline-none focus:shadow-outline" | ||||
|         href="#">Contact</a> | ||||
|       <div class="relative"> | ||||
|         <button | ||||
|           on:click={() => { | ||||
|             open = !open; | ||||
|           }} | ||||
|           class="flex flex-row items-center w-full px-4 py-2 mt-2 text-sm font-semibold text-left bg-transparent rounded-lg dark-mode:bg-transparent dark-mode:focus:text-white dark-mode:hover:text-white dark-mode:focus:bg-gray-600 dark-mode:hover:bg-gray-600 md:block hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:bg-gray-200 focus:outline-none focus:shadow-outline"> | ||||
|           <span>Dropdown</span> | ||||
|           <svg | ||||
|             fill="currentColor" | ||||
|             viewBox="0 0 20 20" | ||||
|             class="inline w-4 h-4 mt-1 ml-1 transition-transform duration-200 transform md:-mt-1"><path | ||||
|               fill-rule="evenodd" | ||||
|               d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" | ||||
|               clip-rule="evenodd" /></svg> | ||||
|         </button> | ||||
|         <div | ||||
|           class:block={open} | ||||
|           class:hidden={!open} | ||||
|           class="absolute right-0 w-full mt-2 origin-top-right rounded-md shadow-lg"> | ||||
|           <div | ||||
|             class="px-2 py-2 bg-white rounded-md shadow dark-mode:bg-gray-800"> | ||||
|             <a | ||||
|               class="block px-4 py-2 mt-2 text-sm font-semibold bg-transparent rounded-lg dark-mode:bg-transparent dark-mode:hover:bg-gray-600 dark-mode:focus:bg-gray-600 dark-mode:focus:text-white dark-mode:hover:text-white dark-mode:text-gray-200 md:mt-0 hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:bg-gray-200 focus:outline-none focus:shadow-outline" | ||||
|               href="#">Link #1</a> | ||||
|             <a | ||||
|               class="block px-4 py-2 mt-2 text-sm font-semibold bg-transparent rounded-lg dark-mode:bg-transparent dark-mode:hover:bg-gray-600 dark-mode:focus:bg-gray-600 dark-mode:focus:text-white dark-mode:hover:text-white dark-mode:text-gray-200 md:mt-0 hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:bg-gray-200 focus:outline-none focus:shadow-outline" | ||||
|               href="#">Link #2</a> | ||||
|             <a | ||||
|               class="block px-4 py-2 mt-2 text-sm font-semibold bg-transparent rounded-lg dark-mode:bg-transparent dark-mode:hover:bg-gray-600 dark-mode:focus:bg-gray-600 dark-mode:focus:text-white dark-mode:hover:text-white dark-mode:text-gray-200 md:mt-0 hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:bg-gray-200 focus:outline-none focus:shadow-outline" | ||||
|               href="#">Link #3</a> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </nav> | ||||
|   </div> | ||||
| </div> | ||||
| @@ -1,165 +0,0 @@ | ||||
| <script> | ||||
|   import { StatsService } from "@odit/lfk-client-js"; | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   const stats_promise = StatsService.statsControllerGet(); | ||||
| </script> | ||||
|  | ||||
| <!--  --> | ||||
| <h1>{$_('general-stats')}</h1> | ||||
| {#await stats_promise} | ||||
|   <div | ||||
|     class="bg-teal-lightest border-t-4 border-teal rounded-b text-teal-darkest px-4 py-3 shadow-md my-2" | ||||
|     role="alert"> | ||||
|     <p class="font-bold">{$_('stats-are-being-loaded')}</p> | ||||
|     <p class="text-sm">{$_('this-might-take-a-moment')}</p> | ||||
|   </div> | ||||
| {:then stats} | ||||
|   <div | ||||
|     class="flex flex-col lg:flex-row w-full lg:space-x-2 space-y-2 lg:space-y-0 mb-2 lg:mb-4"> | ||||
|     <a href="/runners/" class="w-full lg:w-1/4"> | ||||
|       <div | ||||
|         class="widget w-full p-4 rounded-lg bg-white border border-grey-100 dark:bg-grey-895 dark:border-grey-890 dark:bg-gray-900 dark:text-white"> | ||||
|         <div class="flex flex-row items-center justify-between"> | ||||
|           <div class="flex flex-col"> | ||||
|             <div class="text-xs uppercase font-light text-grey-500"> | ||||
|               {$_('runners')} | ||||
|             </div> | ||||
|             <div class="text-xl font-bold">{stats.total_runners}</div> | ||||
|           </div> | ||||
|           <svg | ||||
|             height="24" | ||||
|             width="24" | ||||
|             fill="currentColor" | ||||
|             xmlns="http://www.w3.org/2000/svg" | ||||
|             viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none" /> | ||||
|             <path | ||||
|               d="M13.49 5.48c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm-3.6 13.9l1-4.4 2.1 2v6h2v-7.5l-2.1-2 .6-3c1.3 1.5 3.3 2.5 5.5 2.5v-2c-1.9 0-3.5-1-4.3-2.4l-1-1.6c-.4-.6-1-1-1.7-1-.3 0-.5.1-.8.1l-5.2 2.2v4.7h2v-3.4l1.8-.7-1.6 8.1-4.9-1-.4 2 7 1.4z" /></svg> | ||||
|         </div> | ||||
|       </div> | ||||
|     </a> | ||||
|     <div class="w-full lg:w-1/4"> | ||||
|       <div | ||||
|         class="widget w-full p-4 rounded-lg bg-white border border-grey-100 dark:bg-grey-895 dark:border-grey-890 dark:bg-gray-900 dark:text-white"> | ||||
|         <div class="flex flex-row items-center justify-between"> | ||||
|           <div class="flex flex-col"> | ||||
|             <div class="text-xs uppercase font-light text-grey-500"> | ||||
|               {$_('total-scans')} | ||||
|             </div> | ||||
|             <div class="text-xl font-bold">{stats.total_scans}</div> | ||||
|           </div><svg | ||||
|             stroke="currentColor" | ||||
|             fill="currentColor" | ||||
|             stroke-width="2" | ||||
|             viewBox="0 0 24 24" | ||||
|             stroke-linecap="round" | ||||
|             stroke-linejoin="round" | ||||
|             size="24" | ||||
|             class="stroke-current text-grey-500" | ||||
|             height="24" | ||||
|             width="24" | ||||
|             xmlns="http://www.w3.org/2000/svg"><polyline | ||||
|               points="22 12 18 12 15 21 9 3 6 12 2 12" /></svg> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="w-full lg:w-1/4"> | ||||
|       <div | ||||
|         class="widget w-full p-4 rounded-lg bg-white border border-grey-100 dark:bg-grey-895 dark:border-grey-890 dark:bg-gray-900 dark:text-white"> | ||||
|         <div class="flex flex-row items-center justify-between"> | ||||
|           <div class="flex flex-col"> | ||||
|             <div class="text-xs uppercase font-light text-grey-500"> | ||||
|               {$_('total-donations')} | ||||
|             </div> | ||||
|             <div class="text-xl font-bold">{stats.total_donation} €</div> | ||||
|           </div><svg | ||||
|             xmlns="http://www.w3.org/2000/svg" | ||||
|             height="24" | ||||
|             fill="currentColor" | ||||
|             width="24"><path d="M0 0h24v24H0z" fill="none" /> | ||||
|             <path | ||||
|               d="M15 18.5A6.48 6.48 0 019.24 15H15v-2H8.58c-.05-.33-.08-.66-.08-1s.03-.67.08-1H15V9H9.24A6.491 6.491 0 0115 5.5c1.61 0 3.09.59 4.23 1.57L21 5.3A8.955 8.955 0 0015 3c-3.92 0-7.24 2.51-8.48 6H3v2h3.06a8.262 8.262 0 000 2H3v2h3.52c1.24 3.49 4.56 6 8.48 6 2.31 0 4.41-.87 6-2.3l-1.78-1.77c-1.13.98-2.6 1.57-4.22 1.57z" /></svg> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="w-full lg:w-1/4"> | ||||
|       <div | ||||
|         class="widget w-full p-4 rounded-lg bg-white border border-grey-100 dark:bg-grey-895 dark:border-grey-890 dark:bg-gray-900 dark:text-white"> | ||||
|         <div class="flex flex-row items-center justify-between"> | ||||
|           <div class="flex flex-col"> | ||||
|             <div class="text-xs uppercase font-light text-grey-500"> | ||||
|               {$_('total-distance')} | ||||
|             </div> | ||||
|             <div class="text-xl font-bold"> | ||||
|               {stats.total_distance / 1000} | ||||
|               km | ||||
|             </div> | ||||
|           </div> | ||||
|           <svg | ||||
|             fill="currentColor" | ||||
|             xmlns="http://www.w3.org/2000/svg" | ||||
|             height="24" | ||||
|             width="24"><path d="M0 0h24v24H0z" fill="none" /> | ||||
|             <path | ||||
|               d="M21 6H3c-1.1 0-2 .9-2 2v8c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm0 10H3V8h2v4h2V8h2v4h2V8h2v4h2V8h2v4h2V8h2v8z" /></svg> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <a href="/teams/" class="w-full lg:w-1/4"> | ||||
|       <div | ||||
|         class="widget w-full p-4 rounded-lg bg-white border border-grey-100 dark:bg-grey-895 dark:border-grey-890 dark:bg-gray-900 dark:text-white"> | ||||
|         <div class="flex flex-row items-center justify-between"> | ||||
|           <div class="flex flex-col"> | ||||
|             <div class="text-xs uppercase font-light text-grey-500"> | ||||
|               {$_('count_teams')} | ||||
|             </div> | ||||
|             <div class="text-xl font-bold">{stats.total_teams}</div> | ||||
|           </div> | ||||
|           <svg | ||||
|             stroke="currentColor" | ||||
|             fill="none" | ||||
|             stroke-width="2" | ||||
|             viewBox="0 0 24 24" | ||||
|             stroke-linecap="round" | ||||
|             stroke-linejoin="round" | ||||
|             size="24" | ||||
|             class="stroke-current text-grey-500" | ||||
|             height="24" | ||||
|             width="24" | ||||
|             xmlns="http://www.w3.org/2000/svg"><path | ||||
|               d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" /> | ||||
|             <circle cx="9" cy="7" r="4" /> | ||||
|             <path d="M23 21v-2a4 4 0 0 0-3-3.87" /> | ||||
|             <path d="M16 3.13a4 4 0 0 1 0 7.75" /></svg> | ||||
|         </div> | ||||
|       </div> | ||||
|     </a> | ||||
|     <a href="/orgs/" class="w-full lg:w-1/4"> | ||||
|       <div | ||||
|         class="widget w-full p-4 rounded-lg bg-white border border-grey-100 dark:bg-grey-895 dark:border-grey-890 dark:bg-gray-900 dark:text-white"> | ||||
|         <div class="flex flex-row items-center justify-between"> | ||||
|           <div class="flex flex-col"> | ||||
|             <div class="text-xs uppercase font-light text-grey-500"> | ||||
|               {$_('count_organizations')} | ||||
|             </div> | ||||
|             <div class="text-xl font-bold">{stats.total_orgs}</div> | ||||
|           </div> | ||||
|           <svg | ||||
|             height="24" | ||||
|             fill="currentColor" | ||||
|             width="24" | ||||
|             xmlns="http://www.w3.org/2000/svg" | ||||
|             viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z" /> | ||||
|             <path | ||||
|               d="M17 11V3H7v4H3v14h8v-4h2v4h8V11h-4zM7 19H5v-2h2v2zm0-4H5v-2h2v2zm0-4H5V9h2v2zm4 4H9v-2h2v2zm0-4H9V9h2v2zm0-4H9V5h2v2zm4 8h-2v-2h2v2zm0-4h-2V9h2v2zm0-4h-2V5h2v2zm4 12h-2v-2h2v2zm0-4h-2v-2h2v2z" /></svg> | ||||
|         </div> | ||||
|       </div> | ||||
|     </a> | ||||
|   </div> | ||||
| {:catch error} | ||||
|   <div class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-500"> | ||||
|     <span class="inline-block align-middle mr-8"> | ||||
|       <b class="capitalize">{$_('general_promise_error')}</b> | ||||
|       {error} | ||||
|     </span> | ||||
|   </div> | ||||
| {/await} | ||||
| @@ -1,84 +0,0 @@ | ||||
| <!-- This example requires Tailwind CSS v2.0+ --> | ||||
| <div class="flex flex-col"> | ||||
|   <div class="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8"> | ||||
|     <div class="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8"> | ||||
|       <div | ||||
|         class="shadow overflow-hidden border-b border-gray-200 sm:rounded-lg"> | ||||
|         <table class="min-w-full divide-y divide-gray-200"> | ||||
|           <thead class="bg-gray-50"> | ||||
|             <tr> | ||||
|               <th | ||||
|                 scope="col" | ||||
|                 class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> | ||||
|                 Name | ||||
|               </th> | ||||
|               <th | ||||
|                 scope="col" | ||||
|                 class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> | ||||
|                 Title | ||||
|               </th> | ||||
|               <th | ||||
|                 scope="col" | ||||
|                 class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> | ||||
|                 Status | ||||
|               </th> | ||||
|               <th | ||||
|                 scope="col" | ||||
|                 class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> | ||||
|                 Role | ||||
|               </th> | ||||
|               <th scope="col" class="relative px-6 py-3"> | ||||
|                 <span class="sr-only">Edit</span> | ||||
|               </th> | ||||
|             </tr> | ||||
|           </thead> | ||||
|           <tbody class="divide-y divide-gray-200"> | ||||
|             <tr> | ||||
|               <td class="px-6 py-4 whitespace-nowrap"> | ||||
|                 <div class="flex items-center"> | ||||
|                   <div class="flex-shrink-0 h-10 w-10"> | ||||
|                     <img | ||||
|                       class="h-10 w-10 rounded-full" | ||||
|                       src="https://images.unsplash.com/photo-1494790108377-be9c29b29330?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=4&w=256&h=256&q=60" | ||||
|                       alt="" /> | ||||
|                   </div> | ||||
|                   <div class="ml-4"> | ||||
|                     <div class="text-sm font-medium text-gray-900"> | ||||
|                       Jane Cooper | ||||
|                     </div> | ||||
|                     <div class="text-sm text-gray-500"> | ||||
|                       jane.cooper@example.com | ||||
|                     </div> | ||||
|                   </div> | ||||
|                 </div> | ||||
|               </td> | ||||
|               <td class="px-6 py-4 whitespace-nowrap"> | ||||
|                 <div class="text-sm text-gray-900"> | ||||
|                   Regional Paradigm Technician | ||||
|                 </div> | ||||
|                 <div class="text-sm text-gray-500">Optimization</div> | ||||
|               </td> | ||||
|               <td class="px-6 py-4 whitespace-nowrap"> | ||||
|                 <span | ||||
|                   class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800"> | ||||
|                   Active | ||||
|                 </span> | ||||
|               </td> | ||||
|               <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> | ||||
|                 Admin | ||||
|               </td> | ||||
|               <td | ||||
|                 class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> | ||||
|                 <a | ||||
|                   href="#" | ||||
|                   class="text-indigo-600 hover:text-indigo-900">Edit</a> | ||||
|               </td> | ||||
|             </tr> | ||||
|  | ||||
|             <!-- More rows... --> | ||||
|           </tbody> | ||||
|         </table> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| </div> | ||||
| @@ -1,18 +0,0 @@ | ||||
| <h3 class="text-lg">Tabs</h3> | ||||
| <div | ||||
|   class="w-full flex sm:border-b sm:border-gray-300 relative flex-col sm:flex-row"> | ||||
|   <div | ||||
|     class="flex-1 sm:text-center font-medium pb-3 cursor-pointer hover:text-blue-400 false"> | ||||
|     1 | ||||
|   </div> | ||||
|   <div | ||||
|     class="flex-1 sm:text-center font-medium pb-3 cursor-pointer hover:text-blue-400 false"> | ||||
|     2 | ||||
|   </div> | ||||
|   <div | ||||
|     class="flex-1 sm:text-center font-medium pb-3 cursor-pointer hover:text-blue-400 false"> | ||||
|     3 | ||||
|   </div> | ||||
|   <div | ||||
|     class="hidden sm:block absolute bottom-0 left-0 h-1 bg-blue-400 transition-transform duration-300 ease-out w-1/4 transform translate-x-double" /> | ||||
| </div> | ||||
| @@ -1,102 +0,0 @@ | ||||
| <div> | ||||
|   <div | ||||
|     class="text-xs inline-flex items-center font-bold leading-sm uppercase px-3 py-1 bg-blue-200 text-blue-700 rounded-full"> | ||||
|     <svg | ||||
|       xmlns="http://www.w3.org/2000/svg" | ||||
|       width="16" | ||||
|       height="16" | ||||
|       viewBox="0 0 24 24" | ||||
|       fill="none" | ||||
|       stroke="currentColor" | ||||
|       stroke-width="2" | ||||
|       stroke-linecap="round" | ||||
|       stroke-linejoin="round" | ||||
|       class="feather feather-bell-off mr-2"> | ||||
|       <path d="M13.73 21a2 2 0 0 1-3.46 0" /> | ||||
|       <path d="M18.63 13A17.89 17.89 0 0 1 18 8" /> | ||||
|       <path d="M6.26 6.26A5.86 5.86 0 0 0 6 8c0 7-3 9-3 9h14" /> | ||||
|       <path d="M18 8a6 6 0 0 0-9.33-5" /> | ||||
|       <line x1="1" y1="1" x2="23" y2="23" /> | ||||
|     </svg> | ||||
|     Tag | ||||
|   </div> | ||||
|  | ||||
|   <div | ||||
|     class="ml-4 text-xs inline-flex items-center font-bold leading-sm uppercase px-3 py-1 bg-green-200 text-green-700 rounded-full"> | ||||
|     <svg | ||||
|       xmlns="http://www.w3.org/2000/svg" | ||||
|       width="16" | ||||
|       height="16" | ||||
|       viewBox="0 0 24 24" | ||||
|       fill="none" | ||||
|       stroke="currentColor" | ||||
|       stroke-width="2" | ||||
|       stroke-linecap="round" | ||||
|       stroke-linejoin="round" | ||||
|       class="feather feather-arrow-right mr-2"> | ||||
|       <line x1="5" y1="12" x2="19" y2="12" /> | ||||
|       <polyline points="12 5 19 12 12 19" /> | ||||
|     </svg> | ||||
|     Tag | ||||
|   </div> | ||||
|  | ||||
|   <div | ||||
|     class="ml-4 text-xs inline-flex items-center font-bold leading-sm uppercase px-3 py-1 bg-orange-200 text-orange-700 rounded-full"> | ||||
|     <svg | ||||
|       xmlns="http://www.w3.org/2000/svg" | ||||
|       width="16" | ||||
|       height="16" | ||||
|       viewBox="0 0 24 24" | ||||
|       fill="none" | ||||
|       stroke="currentColor" | ||||
|       stroke-width="2" | ||||
|       stroke-linecap="round" | ||||
|       stroke-linejoin="round" | ||||
|       class="feather feather-activity mr-2"> | ||||
|       <polyline points="22 12 18 12 15 21 9 3 6 12 2 12" /> | ||||
|     </svg> | ||||
|     Tag | ||||
|   </div> | ||||
|  | ||||
|   <div | ||||
|     class="ml-4 text-xs inline-flex items-center font-bold leading-sm uppercase px-3 py-1 bg-red-200 text-red-700 rounded-full"> | ||||
|     <svg | ||||
|       xmlns="http://www.w3.org/2000/svg" | ||||
|       width="16" | ||||
|       height="16" | ||||
|       viewBox="0 0 24 24" | ||||
|       fill="none" | ||||
|       stroke="currentColor" | ||||
|       stroke-width="2" | ||||
|       stroke-linecap="round" | ||||
|       stroke-linejoin="round" | ||||
|       class="feather feather-archive mr-2"> | ||||
|       <polyline points="21 8 21 21 3 21 3 8" /> | ||||
|       <rect x="1" y="3" width="22" height="5" /> | ||||
|       <line x1="10" y1="12" x2="14" y2="12" /> | ||||
|     </svg> | ||||
|     Tag | ||||
|   </div> | ||||
|  | ||||
|   <div | ||||
|     class="ml-4 text-xs inline-flex items-center font-bold leading-sm uppercase px-3 py-1 rounded-full bg-white text-gray-700 border"> | ||||
|     <svg | ||||
|       xmlns="http://www.w3.org/2000/svg" | ||||
|       width="16" | ||||
|       height="16" | ||||
|       viewBox="0 0 24 24" | ||||
|       fill="none" | ||||
|       stroke="currentColor" | ||||
|       stroke-width="2" | ||||
|       stroke-linecap="round" | ||||
|       stroke-linejoin="round" | ||||
|       class="feather feather-hard-drive mr-2"> | ||||
|       <line x1="22" y1="12" x2="2" y2="12" /> | ||||
|       <path | ||||
|         d="M5.45 5.11L2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z" /> | ||||
|       <line x1="6" y1="16" x2="6.01" y2="16" /> | ||||
|       <line x1="10" y1="16" x2="10.01" y2="16" /> | ||||
|     </svg> | ||||
|     Tag | ||||
|   </div> | ||||
| </div> | ||||
| @@ -1,10 +0,0 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
| </script> | ||||
|  | ||||
| <section class="container p-5"> | ||||
|   <span class="mb-1 text-3xl font-extrabold leading-tight"> | ||||
|     {$_('teams')} | ||||
|   </span> | ||||
|   <p class="mb-8 text-lg text-gray-500">everything is more fun together 🏃♂️🏃♀️🏃♂️</p> | ||||
| </section> | ||||
| @@ -1,5 +0,0 @@ | ||||
| <script> | ||||
|   import { _, locale } from "svelte-i18n"; | ||||
| </script> | ||||
|  | ||||
| <div>$locale $_('hallo')</div> | ||||
| @@ -1,189 +0,0 @@ | ||||
| <script> | ||||
|   import { _, json } from "svelte-i18n"; | ||||
|   import Toastify from "toastify-js"; | ||||
|   import TracksEmptyState from "./TracksEmptyState.svelte"; | ||||
|   import { TrackService } from "@odit/lfk-client-js"; | ||||
|   const tracks_promise = TrackService.trackControllerGetAll(); | ||||
|   import { getlang } from "./datatable_i18n"; | ||||
|   import { Grid, html } from "gridjs"; | ||||
|   import "gridjs/dist/theme/mermaid.css"; | ||||
|   import { tracks as tracksstore } from "../store.js"; | ||||
|   $: trackscache = []; | ||||
|   $: blocked = []; | ||||
|   let table; | ||||
|   let datatable; | ||||
|   let datatable_inited = false; | ||||
|   tracksstore.subscribe((val) => { | ||||
|     trackscache = val; | ||||
|     setTimeout(() => { | ||||
|       if (val.length > 0) { | ||||
|         renderdatatable(); | ||||
|       } | ||||
|     }, 100); | ||||
|   }); | ||||
|   tracks_promise.then((data) => { | ||||
|     tracksstore.set(data); | ||||
|   }); | ||||
|   window.track__edit_cancel = () => renderdatatable(); | ||||
|   window.track__edit_save = () => { | ||||
|     const trackid = parseInt(window.event.target.getAttribute("data-trackid")); | ||||
|     if (blocked.includes(trackid)) { | ||||
|       // | ||||
|     } else { | ||||
|       blocked.push(trackid); | ||||
|       const elem = document.querySelector( | ||||
|         `[data-id="triggered_table_actions_${trackid}"]` | ||||
|       ).parentNode.parentNode.parentNode; | ||||
|       Toastify({ | ||||
|         text: "Track is being updated...", | ||||
|         duration: 500, | ||||
|       }).showToast(); | ||||
|       TrackService.trackControllerPut(trackid, { | ||||
|         id: trackid, | ||||
|         name: elem.childNodes[0].childNodes[0].value, | ||||
|         distance: parseInt(elem.childNodes[1].childNodes[0].value), | ||||
|         minimumLapTime: parseInt(elem.childNodes[2].childNodes[0].value), | ||||
|       }) | ||||
|         .then((r) => { | ||||
|           Toastify({ | ||||
|             text: "Track was updated!", | ||||
|             backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", | ||||
|             duration: 1000, | ||||
|           }).showToast(); | ||||
|           blocked = blocked.filter((e) => e !== trackid); | ||||
|           document | ||||
|             .querySelector(`[data-id="default_table_actions_${trackid}"]`) | ||||
|             .classList.remove("hidden"); | ||||
|           document | ||||
|             .querySelector(`[data-id="triggered_table_actions_${trackid}"]`) | ||||
|             .classList.add("hidden"); | ||||
|           // | ||||
|           elem.childNodes[0].innerHTML = `<td data-column-id="trackName" class="gridjs-td">${elem.childNodes[0].childNodes[0].value}</td>`; | ||||
|           elem.childNodes[1].innerHTML = `<td data-column-id="trackName" class="gridjs-td">${elem.childNodes[1].childNodes[0].value}</td>`; | ||||
|           elem.childNodes[2].innerHTML = `<td data-column-id="trackName" class="gridjs-td">${elem.childNodes[2].childNodes[0].value}</td>`; | ||||
|         }) | ||||
|         .catch((err) => { | ||||
|           console.error(err); | ||||
|         }); | ||||
|     } | ||||
|   }; | ||||
|   window.track__delete_handler = () => { | ||||
|     const trackid = parseInt(window.event.target.getAttribute("data-trackid")); | ||||
|     document | ||||
|       .querySelector(`[data-id="default_table_actions_${trackid}"]`) | ||||
|       .classList.add("hidden"); | ||||
|     document | ||||
|       .querySelector(`[data-id="deleteconfirmation_table_actions_${trackid}"]`) | ||||
|       .classList.remove("hidden"); | ||||
|   }; | ||||
|   window.track__delete_cancel = () => { | ||||
|     const trackid = parseInt(window.event.target.getAttribute("data-trackid")); | ||||
|     document | ||||
|       .querySelector(`[data-id="default_table_actions_${trackid}"]`) | ||||
|       .classList.remove("hidden"); | ||||
|     document | ||||
|       .querySelector(`[data-id="deleteconfirmation_table_actions_${trackid}"]`) | ||||
|       .classList.add("hidden"); | ||||
|   }; | ||||
|   window.track__delete_confirm = () => { | ||||
|     const trackid = parseInt(window.event.target.getAttribute("data-trackid")); | ||||
|     TrackService.trackControllerRemove(trackid) | ||||
|       .then(() => { | ||||
|         const newStoreVal = trackscache.filter((obj) => obj.id !== trackid); | ||||
|         tracksstore.set(newStoreVal); | ||||
|         renderdatatable(); | ||||
|       }) | ||||
|       .catch((err) => { | ||||
|         console.log(err); | ||||
|       }); | ||||
|   }; | ||||
|   window.track__edit_handler = () => { | ||||
|     const trackid = parseInt(window.event.target.getAttribute("data-trackid")); | ||||
|     document | ||||
|       .querySelector(`[data-id="default_table_actions_${trackid}"]`) | ||||
|       .classList.add("hidden"); | ||||
|     document | ||||
|       .querySelector(`[data-id="triggered_table_actions_${trackid}"]`) | ||||
|       .classList.remove("hidden"); | ||||
|     const elem = document.querySelector( | ||||
|       `[data-id="triggered_table_actions_${trackid}"]` | ||||
|     ).parentNode.parentNode.parentNode; | ||||
|     const trackname = elem.childNodes[0].textContent; | ||||
|     const tracklength = parseInt(elem.childNodes[1].textContent); | ||||
|     const trackmintime = parseInt(elem.childNodes[2].textContent); | ||||
|     elem.childNodes[0].innerHTML = `<input type="text" value="${trackname}" name="trackname" class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2">`; | ||||
|     elem.childNodes[1].innerHTML = `<input type="text" value="${tracklength}" name="tracklength" class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2">`; | ||||
|     elem.childNodes[2].innerHTML = `<input type="text" value="${trackmintime}" name="trackmintime" class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2">`; | ||||
|   }; | ||||
|   // | ||||
|   function renderdatatable() { | ||||
|     let tabledata = []; | ||||
|     trackscache.forEach((track) => { | ||||
|       tabledata.push([ | ||||
|         track.name, | ||||
|         track.distance, | ||||
|         track.minimumLapTime || 0, | ||||
|         html(` | ||||
|         <div class="hidden" data-id="triggered_table_actions_${track.id}"> | ||||
|           <button class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-green-400 text-base font-medium text-white sm:w-auto sm:text-sm" data-trackid="${track.id}" onclick="track__edit_save()">Save</button> | ||||
|           <button class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-500 text-base font-medium text-white sm:w-auto sm:text-sm" data-trackid="${track.id}" onclick="track__edit_cancel()">Cancel</button> | ||||
|         </div> | ||||
|         <div data-id="default_table_actions_${track.id}"> | ||||
|           <button class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-gray-400 text-base font-medium text-white sm:w-auto sm:text-sm" data-trackid="${track.id}" onclick="track__edit_handler()">Edit</button> | ||||
|           <button class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-500 text-base font-medium text-white sm:w-auto sm:text-sm" data-trackid="${track.id}" onclick="track__delete_handler()">Delete</button> | ||||
|         </div> | ||||
|         <div class="hidden" data-id="deleteconfirmation_table_actions_${track.id}"> | ||||
|           <button class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-gray-400 text-base font-medium text-white sm:w-auto sm:text-sm" data-trackid="${track.id}" onclick="track__delete_cancel()">Cancel</button> | ||||
|           <button class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-500 text-base font-medium text-white sm:w-auto sm:text-sm" data-trackid="${track.id}" onclick="track__delete_confirm()">Confirm</button> | ||||
|         </div> | ||||
|       `), | ||||
|       ]); | ||||
|     }); | ||||
|  | ||||
|     if (datatable_inited === false) { | ||||
|       datatable = new Grid({ | ||||
|         columns: [ | ||||
|           $_("track-name"), | ||||
|           $_("track-length-in-m"), | ||||
|           $_("minimum-lap-time-in-s"), | ||||
|           $_("action"), | ||||
|         ], | ||||
|         language: getlang($json("datatable")), | ||||
|         sort: true, | ||||
|         search: { enabled: true }, | ||||
|         data: tabledata, | ||||
|         pagination: { | ||||
|           enabled: true, | ||||
|           limit: 25, | ||||
|           summary: false, | ||||
|         }, | ||||
|       }).render(table); | ||||
|       datatable_inited = true; | ||||
|     } else { | ||||
|       datatable.updateConfig({ data: tabledata }).forceRender(); | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| {#if trackscache.length > 0} | ||||
|   <div bind:this={table} /> | ||||
| {/if} | ||||
| {#await tracks_promise} | ||||
|   <div | ||||
|     class="bg-teal-lightest border-t-4 border-teal rounded-b text-teal-darkest px-4 py-3 shadow-md my-2" | ||||
|     role="alert"> | ||||
|     <p class="font-bold">{$_('track-data-is-being-loaded')}</p> | ||||
|     <p class="text-sm">{$_('this-might-take-a-moment')}</p> | ||||
|   </div> | ||||
| {:then} | ||||
|   {#if trackscache.length === 0} | ||||
|     <TracksEmptyState /> | ||||
|   {/if} | ||||
| {:catch error} | ||||
|   <div class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-500"> | ||||
|     <span class="inline-block align-middle mr-8"> | ||||
|       <b class="capitalize">{$_('general_promise_error')}</b> | ||||
|       {error} | ||||
|     </span> | ||||
|   </div> | ||||
| {/await} | ||||
| @@ -1,25 +0,0 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import AddTrackModal from "./AddTrackModal.svelte"; | ||||
|   let modal_open = false; | ||||
|   import Tracks from "./Tracks.svelte"; | ||||
| </script> | ||||
|  | ||||
| <section class="container p-5"> | ||||
|   <span class="mb-1 text-3xl font-extrabold leading-tight"> | ||||
|     Tracks | ||||
|     <button | ||||
|       on:click={() => { | ||||
|         modal_open = true; | ||||
|       }} | ||||
|       type="button" | ||||
|       class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm"> | ||||
|       Create Track | ||||
|     </button> | ||||
|   </span> | ||||
|   <p class="mb-8 text-lg text-gray-500"> | ||||
|     configure the tracks & minimum lap times | ||||
|   </p> | ||||
|   <Tracks /> | ||||
| </section> | ||||
| <AddTrackModal bind:modal_open /> | ||||
| @@ -1,128 +0,0 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import { UserService } from "@odit/lfk-client-js"; | ||||
|   import "gridjs/dist/theme/mermaid.css"; | ||||
|   import { tracks as tracksstore } from "../store.js"; | ||||
|   import PromiseError from "./PromiseError.svelte"; | ||||
|   export let params; | ||||
|   const user_promise = UserService.userControllerGetOne(params.userid); | ||||
|   user_promise.then((data) => { | ||||
|     console.log(data); | ||||
|     tracksstore.set(data); | ||||
|   }); | ||||
| </script> | ||||
|  | ||||
| {#await user_promise} | ||||
|   <!--  --> | ||||
| {:then user} | ||||
|   <section class="container p-5 select-none"> | ||||
|     <div class="flex flex-row mb-4"> | ||||
|       <div class="w-full"> | ||||
|         <nav class="w-full flex"> | ||||
|           <ol class="list-none flex flex-row items-center justify-start"> | ||||
|             <li class="flex items-center"> | ||||
|               <svg | ||||
|                 class="flex-shrink-0 w-5 h-5 mr-2" | ||||
|                 fill="currentColor" | ||||
|                 width="24" | ||||
|                 height="24" | ||||
|                 xmlns="http://www.w3.org/2000/svg" | ||||
|                 viewBox="0 0 640 512"><path | ||||
|                   fill="currentColor" | ||||
|                   d="M610.5 341.3c2.6-14.1 2.6-28.5 0-42.6l25.8-14.9c3-1.7 4.3-5.2 3.3-8.5-6.7-21.6-18.2-41.2-33.2-57.4-2.3-2.5-6-3.1-9-1.4l-25.8 14.9c-10.9-9.3-23.4-16.5-36.9-21.3v-29.8c0-3.4-2.4-6.4-5.7-7.1-22.3-5-45-4.8-66.2 0-3.3.7-5.7 3.7-5.7 7.1v29.8c-13.5 4.8-26 12-36.9 21.3l-25.8-14.9c-2.9-1.7-6.7-1.1-9 1.4-15 16.2-26.5 35.8-33.2 57.4-1 3.3.4 6.8 3.3 8.5l25.8 14.9c-2.6 14.1-2.6 28.5 0 42.6l-25.8 14.9c-3 1.7-4.3 5.2-3.3 8.5 6.7 21.6 18.2 41.1 33.2 57.4 2.3 2.5 6 3.1 9 1.4l25.8-14.9c10.9 9.3 23.4 16.5 36.9 21.3v29.8c0 3.4 2.4 6.4 5.7 7.1 22.3 5 45 4.8 66.2 0 3.3-.7 5.7-3.7 5.7-7.1v-29.8c13.5-4.8 26-12 36.9-21.3l25.8 14.9c2.9 1.7 6.7 1.1 9-1.4 15-16.2 26.5-35.8 33.2-57.4 1-3.3-.4-6.8-3.3-8.5l-25.8-14.9zM496 368.5c-26.8 0-48.5-21.8-48.5-48.5s21.8-48.5 48.5-48.5 48.5 21.8 48.5 48.5-21.7 48.5-48.5 48.5zM96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm224 32c1.9 0 3.7-.5 5.6-.6 8.3-21.7 20.5-42.1 36.3-59.2 7.4-8 17.9-12.6 28.9-12.6 6.9 0 13.7 1.8 19.6 5.3l7.9 4.6c.8-.5 1.6-.9 2.4-1.4 7-14.6 11.2-30.8 11.2-48 0-61.9-50.1-112-112-112S208 82.1 208 144c0 61.9 50.1 112 112 112zm105.2 194.5c-2.3-1.2-4.6-2.6-6.8-3.9-8.2 4.8-15.3 9.8-27.5 9.8-10.9 0-21.4-4.6-28.9-12.6-18.3-19.8-32.3-43.9-40.2-69.6-10.7-34.5 24.9-49.7 25.8-50.3-.1-2.6-.1-5.2 0-7.8l-7.9-4.6c-3.8-2.2-7-5-9.8-8.1-3.3.2-6.5.6-9.8.6-24.6 0-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h255.4c-3.7-6-6.2-12.8-6.2-20.3v-9.2zM173.1 274.6C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z" /></svg> | ||||
|             </li> | ||||
|             <li class="flex items-center"> | ||||
|               <a class="mr-2" href="./">{$_('users')}</a><svg | ||||
|                 stroke="currentColor" | ||||
|                 fill="none" | ||||
|                 stroke-width="2" | ||||
|                 viewBox="0 0 24 24" | ||||
|                 stroke-linecap="round" | ||||
|                 stroke-linejoin="round" | ||||
|                 class="h-3 w-3 mr-2 stroke-current" | ||||
|                 height="1em" | ||||
|                 width="1em" | ||||
|                 xmlns="http://www.w3.org/2000/svg"><line | ||||
|                   x1="5" | ||||
|                   y1="12" | ||||
|                   x2="19" | ||||
|                   y2="12" /> | ||||
|                 <polyline points="12 5 19 12 12 19" /></svg> | ||||
|             </li> | ||||
|             <li class="flex items-center"> | ||||
|               <span class="mr-2">{user.firstname} | ||||
|                 {user.middlename || ''} | ||||
|                 {user.lastname}</span> | ||||
|             </li> | ||||
|           </ol> | ||||
|         </nav> | ||||
|       </div> | ||||
|     </div> | ||||
|     <span | ||||
|       class="mb-4 text-3xl font-extrabold leading-tight">{user.firstname} | ||||
|       {user.middlename || ''} | ||||
|       {user.lastname} | ||||
|       <button | ||||
|         type="button" | ||||
|         class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm">Delete | ||||
|         User</button></span> | ||||
|     <!--  --> | ||||
|     <div class="mt-2 flex items-center"> | ||||
|       <img | ||||
|         alt={$_('profile-picture')} | ||||
|         class="inline-block h-20 w-20 rounded-full overflow-hidden bg-gray-100" | ||||
|         src={user.profilePic} /> | ||||
|       <!-- <span | ||||
|         class="inline-block h-12 w-12 rounded-full overflow-hidden bg-gray-100"><svg | ||||
|           class="h-full w-full text-gray-300" | ||||
|           fill="currentColor" | ||||
|           viewBox="0 0 24 24"><path | ||||
|             d="M24 20.993V24H0v-2.996A14.977 14.977 0 0112.004 15c4.904 0 9.26 2.354 11.996 5.993zM16.002 8.999a4 4 0 11-8 0 4 4 0 018 0z" /></svg></span> --> | ||||
|       <button | ||||
|         type="button" | ||||
|         class="ml-5 bg-white py-2 px-3 border border-gray-300 rounded-md shadow-sm text-sm leading-4 font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">Change</button> | ||||
|     </div> | ||||
|     <!--  --> | ||||
|     <div class="mt-3 text-sm w-full"> | ||||
|       <input | ||||
|         id="enabled" | ||||
|         name="enabled" | ||||
|         type="checkbox" | ||||
|         class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded" /> | ||||
|       <label | ||||
|         for="enabled" | ||||
|         class="ml-1 font-medium text-gray-700">Active?</label> | ||||
|       <p class="text-gray-500">set the user active/ inactive</p> | ||||
|     </div> | ||||
|     <div class="text-sm w-full"> | ||||
|       <label for="firstname" class="font-medium text-gray-700">First name</label> | ||||
|       <input | ||||
|         autocomplete="off" | ||||
|         placeholder="First name" | ||||
|         type="text" | ||||
|         name="firstname" | ||||
|         class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 dark:bg-gray-900 dark:text-gray-100 rounded-md p-2" /> | ||||
|     </div> | ||||
|     <div class="text-sm w-full"> | ||||
|       <label for="middlename" class="font-medium text-gray-700">Middle name</label> | ||||
|       <input | ||||
|         autocomplete="off" | ||||
|         placeholder="Middle name" | ||||
|         type="text" | ||||
|         name="middlename" | ||||
|         class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 dark:bg-gray-900 dark:text-gray-100 rounded-md p-2" /> | ||||
|     </div> | ||||
|     <div class="text-sm w-full"> | ||||
|       <label for="lastname" class="font-medium text-gray-700">Last name</label> | ||||
|       <input | ||||
|         autocomplete="off" | ||||
|         placeholder="Last name" | ||||
|         type="text" | ||||
|         name="lastname" | ||||
|         class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 dark:bg-gray-900 dark:text-gray-100 rounded-md p-2" /> | ||||
|     </div> | ||||
|   </section> | ||||
| {:catch error} | ||||
|   <!-- promise was rejected --> | ||||
|   <PromiseError {error} /> | ||||
| {/await} | ||||
| @@ -1,217 +0,0 @@ | ||||
| <script> | ||||
|   import { _, json } from "svelte-i18n"; | ||||
|   import Toastify from "toastify-js"; | ||||
|   import TracksEmptyState from "./TracksEmptyState.svelte"; | ||||
|   import { TrackService, UserService } from "@odit/lfk-client-js"; | ||||
|   const users_promise = UserService.userControllerGetAll(); | ||||
|   import { getlang } from "./datatable_i18n"; | ||||
|   import { Grid, html } from "gridjs"; | ||||
|   import "gridjs/dist/theme/mermaid.css"; | ||||
|   import { tracks as tracksstore } from "../store.js"; | ||||
|   import UsersEmptyState from "./UsersEmptyState.svelte"; | ||||
|   $: userscache = []; | ||||
|   $: blocked = []; | ||||
|   let table; | ||||
|   let datatable; | ||||
|   let datatable_inited = false; | ||||
|   tracksstore.subscribe((val) => { | ||||
|     userscache = val; | ||||
|   }); | ||||
|   users_promise.then((data) => { | ||||
|     console.log(data); | ||||
|     tracksstore.set(data); | ||||
|   }); | ||||
| </script> | ||||
|  | ||||
| {#await users_promise} | ||||
|   <div | ||||
|     class="bg-teal-lightest border-t-4 border-teal rounded-b text-teal-darkest px-4 py-3 shadow-md my-2" | ||||
|     role="alert"> | ||||
|     <p class="font-bold">users are being loaded...</p> | ||||
|     <p class="text-sm">{$_('this-might-take-a-moment')}</p> | ||||
|   </div> | ||||
| {:then users} | ||||
|   {#if userscache.length === 0} | ||||
|     <UsersEmptyState /> | ||||
|   {:else} | ||||
|     <div | ||||
|       class="shadow border-b border-gray-200 sm:rounded-lg overflow-x-scroll"> | ||||
|       <table class="divide-y divide-gray-200 w-full"> | ||||
|         <thead class="bg-gray-50"> | ||||
|           <tr> | ||||
|             <th | ||||
|               scope="col" | ||||
|               class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> | ||||
|               Name | ||||
|             </th> | ||||
|             <th | ||||
|               scope="col" | ||||
|               class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> | ||||
|               Title | ||||
|             </th> | ||||
|             <th | ||||
|               scope="col" | ||||
|               class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> | ||||
|               Status | ||||
|             </th> | ||||
|             <th | ||||
|               scope="col" | ||||
|               class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> | ||||
|               Groups | ||||
|             </th> | ||||
|             <th scope="col" class="relative px-6 py-3"> | ||||
|               <span class="sr-only">Action</span> | ||||
|             </th> | ||||
|           </tr> | ||||
|         </thead> | ||||
|         <tbody class="divide-y divide-gray-200"> | ||||
|           {#each users as u} | ||||
|             <tr> | ||||
|               <td class="px-6 py-4 whitespace-nowrap"> | ||||
|                 <div class="flex items-center"> | ||||
|                   {#if u.profilePic} | ||||
|                     <div class="flex-shrink-0 h-10 w-10"> | ||||
|                       <img | ||||
|                         class="h-10 w-10 rounded-full" | ||||
|                         src={u.profilePic} | ||||
|                         alt="" /> | ||||
|                     </div> | ||||
|                   {/if} | ||||
|                   <div class="ml-4"> | ||||
|                     <div | ||||
|                       class="text-sm font-medium text-gray-900 dark:text-gray-100"> | ||||
|                       {u.firstname} | ||||
|                       {u.middlename || ''} | ||||
|                       {u.lastname} | ||||
|                     </div> | ||||
|                     <div class="text-sm text-gray-500"> | ||||
|                       {u.email || u.username} | ||||
|                     </div> | ||||
|                   </div> | ||||
|                 </div> | ||||
|               </td> | ||||
|               <td class="px-6 py-4 whitespace-nowrap"> | ||||
|                 <div class="text-sm text-gray-900 dark:text-gray-100"> | ||||
|                   Regional Paradigm Technician | ||||
|                 </div> | ||||
|                 <div class="text-sm text-gray-500">Optimization</div> | ||||
|               </td> | ||||
|               <td class="px-6 py-4 whitespace-nowrap"> | ||||
|                 {#if u.enabled} | ||||
|                   <span | ||||
|                     class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">Active</span> | ||||
|                 {:else} | ||||
|                   <span | ||||
|                     class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800">Inactive</span> | ||||
|                 {/if} | ||||
|               </td> | ||||
|               <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> | ||||
|                 {#each u.groups as g} | ||||
|                   <a | ||||
|                     href="../groups/{g.id}" | ||||
|                     class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800">{g.name}</a> | ||||
|                 {/each} | ||||
|               </td> | ||||
|               <td | ||||
|                 class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> | ||||
|                 <a | ||||
|                   href="./{u.id}" | ||||
|                   class="text-indigo-600 hover:text-indigo-900">Edit</a> | ||||
|                 <span | ||||
|                   tabindex="0" | ||||
|                   href="#" | ||||
|                   class="ml-4 text-red-600 hover:text-red-900 cursor-pointer">Delete</span> | ||||
|               </td> | ||||
|             </tr> | ||||
|           {/each} | ||||
|         </tbody> | ||||
|       </table> | ||||
|       <div | ||||
|         class="grid px-4 py-3 text-xs font-semibold tracking-wide text-gray-500 uppercase border-t dark:border-gray-700 bg-gray-50 sm:grid-cols-9 dark:text-gray-400 dark:bg-gray-900"> | ||||
|         <span class="flex items-center col-span-3"> Showing 21-30 of 100 </span> | ||||
|         <span class="col-span-2" /> | ||||
|         <!-- Pagination --> | ||||
|         <span class="flex col-span-4 mt-2 sm:mt-auto sm:justify-end"> | ||||
|           <nav aria-label="Table navigation"> | ||||
|             <ul class="inline-flex items-center"> | ||||
|               <li> | ||||
|                 <button | ||||
|                   class="px-3 py-1 rounded-md rounded-l-lg focus:outline-none focus:shadow-outline-purple" | ||||
|                   aria-label="Previous"> | ||||
|                   <svg | ||||
|                     aria-hidden="true" | ||||
|                     class="w-4 h-4 fill-current" | ||||
|                     viewBox="0 0 20 20"> | ||||
|                     <path | ||||
|                       d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z" | ||||
|                       clip-rule="evenodd" | ||||
|                       fill-rule="evenodd" /> | ||||
|                   </svg> | ||||
|                 </button> | ||||
|               </li> | ||||
|               <li> | ||||
|                 <button | ||||
|                   class="px-3 py-1 rounded-md focus:outline-none focus:shadow-outline-purple"> | ||||
|                   1 | ||||
|                 </button> | ||||
|               </li> | ||||
|               <li> | ||||
|                 <button | ||||
|                   class="px-3 py-1 rounded-md focus:outline-none focus:shadow-outline-purple"> | ||||
|                   2 | ||||
|                 </button> | ||||
|               </li> | ||||
|               <li> | ||||
|                 <button | ||||
|                   class="px-3 py-1 text-white transition-colors duration-150 bg-purple-600 border border-r-0 border-purple-600 rounded-md focus:outline-none focus:shadow-outline-purple"> | ||||
|                   3 | ||||
|                 </button> | ||||
|               </li> | ||||
|               <li> | ||||
|                 <button | ||||
|                   class="px-3 py-1 rounded-md focus:outline-none focus:shadow-outline-purple"> | ||||
|                   4 | ||||
|                 </button> | ||||
|               </li> | ||||
|               <li><span class="px-3 py-1">...</span></li> | ||||
|               <li> | ||||
|                 <button | ||||
|                   class="px-3 py-1 rounded-md focus:outline-none focus:shadow-outline-purple"> | ||||
|                   8 | ||||
|                 </button> | ||||
|               </li> | ||||
|               <li> | ||||
|                 <button | ||||
|                   class="px-3 py-1 rounded-md focus:outline-none focus:shadow-outline-purple"> | ||||
|                   9 | ||||
|                 </button> | ||||
|               </li> | ||||
|               <li> | ||||
|                 <button | ||||
|                   class="px-3 py-1 rounded-md rounded-r-lg focus:outline-none focus:shadow-outline-purple" | ||||
|                   aria-label="Next"> | ||||
|                   <svg | ||||
|                     class="w-4 h-4 fill-current" | ||||
|                     aria-hidden="true" | ||||
|                     viewBox="0 0 20 20"> | ||||
|                     <path | ||||
|                       d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" | ||||
|                       clip-rule="evenodd" | ||||
|                       fill-rule="evenodd" /> | ||||
|                   </svg> | ||||
|                 </button> | ||||
|               </li> | ||||
|             </ul> | ||||
|           </nav> | ||||
|         </span> | ||||
|       </div> | ||||
|     </div> | ||||
|   {/if} | ||||
| {:catch error} | ||||
|   <div class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-500"> | ||||
|     <span class="inline-block align-middle mr-8"> | ||||
|       <b class="capitalize">{$_('general_promise_error')}</b> | ||||
|       {error} | ||||
|     </span> | ||||
|   </div> | ||||
| {/await} | ||||
| @@ -1,33 +1,22 @@ | ||||
| <script> | ||||
|   import { ApiError, AuthService } from "@odit/lfk-client-js"; | ||||
|   import { AuthService } from "@odit/lfk-client-js"; | ||||
|   import toast from "svelte-french-toast"; | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import Toastify from "toastify-js"; | ||||
|   import "toastify-js/src/toastify.css"; | ||||
|   import isEmail from "validator/es/lib/isEmail"; | ||||
| 
 | ||||
|   let reset_mail_sent = false; | ||||
|   let usersEmail = ""; | ||||
|   function reset() { | ||||
|     if (isEmail(usersEmail)) { | ||||
|       AuthService.authControllerGetResetToken({ email: usersEmail }) | ||||
|       toast.loading($_("mail-validation-in-progress")); | ||||
|       AuthService.authControllerGetResetToken("de", { email: usersEmail }) | ||||
|         .then((resp) => { | ||||
|           console.log(resp); | ||||
|           console.log(resp.resetToken); | ||||
|           Toastify({ | ||||
|             text: $_("mail-validation-in-progress"), | ||||
|             duration: 3500, | ||||
|           }).showToast(); | ||||
|           toast.dismiss(); | ||||
|           reset_mail_sent = true; | ||||
|         }) | ||||
|         .catch((err) => { | ||||
|           console.log(err.body.name); | ||||
|           console.log(err.body.message); | ||||
|         }); | ||||
|         .catch((err) => {}); | ||||
|     } else { | ||||
|       Toastify({ | ||||
|         text: $_("invalid-mail-reset"), | ||||
|         duration: 3500, | ||||
|       }).showToast(); | ||||
|       toast($_("invalid-mail-reset")); | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
| @@ -37,19 +26,18 @@ | ||||
|     <div class="max-w-md w-full py-12 px-6"> | ||||
|       <img style="height:10rem;" class="mx-auto" src="/lfk-logo.png" alt="" /> | ||||
|       <p class="mt-6 text-lg text-center font-bold text-gray-900"> | ||||
|         {$_('application_name')} | ||||
|         {$_("application_name")} | ||||
|       </p> | ||||
|       <p class="mt-2 mb-2 text-sm text-center text-gray-900"> | ||||
|         Passwort-Reset Mail wurde an | ||||
|         {usersEmail} | ||||
|         geschickt | ||||
|         {$_("password-reset-mail-sent", { values: { usersEmail: usersEmail } })} | ||||
|       </p> | ||||
|       <div class="mt-6"> | ||||
|         <div class="mt-6"> | ||||
|           <a | ||||
|             href="/" | ||||
|             class="block w-full text-center py-2 px-3 border border-gray-300 rounded-md text-gray-900 font-medium hover:border-gray-400 focus:outline-none focus:border-gray-400 sm:text-sm"> | ||||
|             {$_('goback')} | ||||
|             class="block w-full text-center py-2 px-3 border border-gray-300 rounded-md text-gray-900 font-medium hover:border-gray-400 focus:outline-none focus:border-gray-400 sm:text-sm" | ||||
|           > | ||||
|             {$_("goback")} | ||||
|           </a> | ||||
|         </div> | ||||
|       </div> | ||||
| @@ -60,25 +48,26 @@ | ||||
|     <div class="max-w-md w-full py-12 px-6"> | ||||
|       <img style="height:10rem;" class="mx-auto" src="/lfk-logo.png" alt="" /> | ||||
|       <p class="mt-6 text-lg text-center font-bold text-gray-900"> | ||||
|         {$_('application_name')} | ||||
|         {$_("application_name")} | ||||
|       </p> | ||||
|       <p class="mt-6 text-sm text-center text-gray-900"> | ||||
|         {$_('forgot_password?')} | ||||
|         {$_("forgot_password")} | ||||
|       </p> | ||||
|       <p class="mt-2 mb-2 text-sm text-center text-gray-900"> | ||||
|         {$_('dont-panic-were-resetting-it')} | ||||
|         {$_("dont-panic-were-resetting-it")} | ||||
|       </p> | ||||
|       <div> | ||||
|         <div class="rounded-md shadow-sm"> | ||||
|           <div> | ||||
|             <input | ||||
|               aria-label={$_('e-mail-adress')} | ||||
|               aria-label={$_("e-mail-adress")} | ||||
|               name="email" | ||||
|               type="email" | ||||
|               required="" | ||||
|               class="border-gray-300 placeholder-gray-500 appearance-none rounded-none relative block w-full px-3 py-2 border text-gray-900 rounded-t-md focus:outline-none focus:shadow-outline-blue focus:border-blue-300 focus:z-10 sm:text-sm" | ||||
|               placeholder={$_('e-mail-adress')} | ||||
|               bind:value={usersEmail} /> | ||||
|               placeholder={$_("e-mail-adress")} | ||||
|               bind:value={usersEmail} | ||||
|             /> | ||||
|           </div> | ||||
|         </div> | ||||
| 
 | ||||
| @@ -86,19 +75,22 @@ | ||||
|           <button | ||||
|             on:click={reset} | ||||
|             type="submit" | ||||
|             class="relative block w-full py-2 px-3 border border-transparent rounded-md text-white font-semibold bg-gray-800 hover:bg-gray-700 focus:bg-gray-900 focus:outline-none focus:shadow-outline sm:text-sm"> | ||||
|             class="relative block w-full py-2 px-3 border border-transparent rounded-md text-white font-semibold bg-gray-800 hover:bg-gray-700 focus:bg-gray-900 focus:outline-none focus:shadow-outline sm:text-sm" | ||||
|           > | ||||
|             <span class="absolute left-0 inset-y pl-3"> | ||||
|               <svg | ||||
|                 class="h-5 w-5 text-gray-500" | ||||
|                 fill="currentColor" | ||||
|                 viewBox="0 0 20 20"> | ||||
|                 viewBox="0 0 20 20" | ||||
|               > | ||||
|                 <path | ||||
|                   fill-rule="evenodd" | ||||
|                   d="M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z" | ||||
|                   clip-rule="evenodd" /> | ||||
|                   clip-rule="evenodd" | ||||
|                 /> | ||||
|               </svg> | ||||
|             </span> | ||||
|             {$_('reset-my-password')} | ||||
|             {$_("reset-my-password")} | ||||
|           </button> | ||||
|         </div> | ||||
|         <div class="mt-6"> | ||||
| @@ -107,24 +99,30 @@ | ||||
|               <div class="w-full border-t border-gray-300" /> | ||||
|             </div> | ||||
|             <div class="relative flex justify-center text-sm"> | ||||
|               <span | ||||
|                 class="px-2 bg-gray-100 text-gray-500">{$_('dont-have-your-email-connected')}</span> | ||||
|               <span class="px-2 bg-gray-100 text-gray-500" | ||||
|                 >{$_("dont-have-your-email-connected")}</span | ||||
|               > | ||||
|             </div> | ||||
|           </div> | ||||
|           <span | ||||
|             class="mt-2 text-sm px-2 bg-gray-100 text-gray-500 justify-center relative flex">{$_('cannot-reset-your-password-directly')}</span> | ||||
|             class="mt-2 text-sm px-2 bg-gray-100 text-gray-500 justify-center relative flex" | ||||
|             >{$_("cannot-reset-your-password-directly")}</span | ||||
|           > | ||||
| 
 | ||||
|           <div class="mt-6"> | ||||
|             <a | ||||
|               href="mailto:lfk@odit.services" | ||||
|               class="block w-full text-center py-2 px-3 border border-gray-300 rounded-md text-gray-900 font-medium hover:border-gray-400 focus:outline-none focus:border-gray-400 sm:text-sm"> | ||||
|               {$_('send-a-mail-to-lfk-odit-services')} | ||||
|               class="block w-full text-center py-2 px-3 border border-gray-300 rounded-md text-gray-900 font-medium hover:border-gray-400 focus:outline-none focus:border-gray-400 sm:text-sm" | ||||
|             > | ||||
|               {$_("send-a-mail-to-lfk-odit-services")} | ||||
|             </a> | ||||
|           </div> | ||||
|           <div class="mt-6"> | ||||
|             <a | ||||
|               href="/" | ||||
|               class="block w-full text-center py-2 px-3 border border-gray-300 rounded-md text-gray-900 font-medium hover:border-gray-400 focus:outline-none focus:border-gray-400 sm:text-sm">{$_('goback')}</a> | ||||
|               class="block w-full text-center py-2 px-3 border border-gray-300 rounded-md text-gray-900 font-medium hover:border-gray-400 focus:outline-none focus:border-gray-400 sm:text-sm" | ||||
|               >{$_("goback")}</a | ||||
|             > | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
| @@ -1,13 +1,15 @@ | ||||
| <script> | ||||
|   import store from "../store.js"; | ||||
|   import store from "../../store.js"; | ||||
|   import localForage from "localforage"; | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   store.init(); | ||||
|   import { OpenAPI, AuthService } from "@odit/lfk-client-js"; | ||||
|   import Toastify from "toastify-js"; | ||||
|   import Footer from "../general/Footer.svelte"; | ||||
|   import isEmail from "validator/es/lib/isEmail"; | ||||
|   import toast from "svelte-french-toast"; | ||||
|   // ------ | ||||
|   let username = "demo"; | ||||
|   let password = "demo"; | ||||
|   let username = config.default_username || ""; | ||||
|   let password = config.default_password || ""; | ||||
|   let is_blocked_by_autologin = false; | ||||
|   let last_loginclick_processed = true; | ||||
| 
 | ||||
| @@ -17,12 +19,7 @@ | ||||
|         is_blocked_by_autologin = true; | ||||
|         OpenAPI.TOKEN = value.access_token; | ||||
|         const jwtinfo = JSON.parse(atob(OpenAPI.TOKEN.split(".")[1])); | ||||
|         store.login(value.access_token, jwtinfo); | ||||
|         Toastify({ | ||||
|           text: $_("welcome_wavinghand"), | ||||
|           duration: 500, | ||||
|           backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", | ||||
|         }).showToast(); | ||||
|         store.login(value, jwtinfo); | ||||
|       } | ||||
|     } | ||||
|   }); | ||||
| @@ -31,45 +28,38 @@ | ||||
|     // prevent login button spamming | ||||
|     if (last_loginclick_processed && is_blocked_by_autologin === false) { | ||||
|       last_loginclick_processed = false; | ||||
|       Toastify({ | ||||
|         text: $_("login_is_checked"), | ||||
|         duration: 500, | ||||
|       }).showToast(); | ||||
|       AuthService.authControllerLogin({ | ||||
|         username, | ||||
|         password, | ||||
|       }) | ||||
|       toast.loading($_("login_is_checked")); | ||||
|       let postdata = {}; | ||||
|       if (isEmail(username)) { | ||||
|         postdata = { | ||||
|           email: username, | ||||
|           password, | ||||
|         }; | ||||
|       } else { | ||||
|         postdata = { | ||||
|           username, | ||||
|           password, | ||||
|         }; | ||||
|       } | ||||
|       AuthService.authControllerLogin(postdata) | ||||
|         .then(async (result) => { | ||||
|           await localForage.setItem("logindata", result); | ||||
|           OpenAPI.TOKEN = result.access_token; | ||||
|           const jwtinfo = JSON.parse(atob(OpenAPI.TOKEN.split(".")[1])); | ||||
|           store.login(result.access_token, jwtinfo); | ||||
|           location.replace("/"); | ||||
|           Toastify({ | ||||
|             text: $_("welcome_wavinghand"), | ||||
|             duration: 500, | ||||
|             backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", | ||||
|           }).showToast(); | ||||
|           toast.dismiss(); | ||||
|         }) | ||||
|         .catch((err) => { | ||||
|           Toastify({ | ||||
|             text: $_("error_on_login"), | ||||
|             duration: 500, | ||||
|             backgroundColor: | ||||
|               "linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)", | ||||
|           }).showToast(); | ||||
|           toast.dismiss(); | ||||
|           toast.error($_("error_on_login")); | ||||
|         }) | ||||
|         .finally(() => { | ||||
|           last_loginclick_processed = true; | ||||
|         }); | ||||
|       // last login was not processed yet | ||||
|     } else { | ||||
|       Toastify({ | ||||
|         text: "chill...", | ||||
|         duration: 1500, | ||||
|         backgroundColor: | ||||
|           "linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)", | ||||
|       }).showToast(); | ||||
|       toast($_("please-wait-a-moment-your-login-is-still-being-processed")); | ||||
|     } | ||||
|   }; | ||||
|   function handleKeydown(e) { | ||||
| @@ -80,34 +70,37 @@ | ||||
| </script> | ||||
| 
 | ||||
| <div | ||||
|   class="min-h-screen flex items-center justify-center bg-gray-100 text-gray-900"> | ||||
|   class="min-h-screen flex items-center justify-center bg-gray-100 text-gray-900" | ||||
| > | ||||
|   <div class="max-w-md w-full py-12 px-6" role="main"> | ||||
|     <img style="height:10rem;" class="mx-auto" src="/lfk-logo.png" alt="" /> | ||||
|     <p class="mt-6 text-lg text-center font-bold">{$_('application_name')}</p> | ||||
|     <p class="mt-6 text-sm text-center">{$_('log_in_to_your_account')}</p> | ||||
|     <p class="mt-6 text-xl text-center font-bold">{$_("application_name")}</p> | ||||
|     <p class="mt-2 mb-6 text-sm text-center">{$_("log_in_to_your_account")}</p> | ||||
|     <div> | ||||
|       <div class="rounded-md shadow-sm"> | ||||
|         <div> | ||||
|           <!-- svelte-ignore a11y-autofocus --> | ||||
|           <input | ||||
|             autofocus | ||||
|             aria-label={$_('email_address_or_username')} | ||||
|             aria-label={$_("email_address_or_username")} | ||||
|             type="text" | ||||
|             required="" | ||||
|             class="border-gray-300 placeholder-gray-500 appearance-none rounded-none relative block w-full px-3 py-2 border rounded-t-md focus:outline-none focus:shadow-outline-blue focus:border-blue-300 focus:z-10 sm:text-sm" | ||||
|             on:keydown={handleKeydown} | ||||
|             placeholder={$_('email_address_or_username')} | ||||
|             bind:value={username} /> | ||||
|             placeholder={$_("email_address_or_username")} | ||||
|             bind:value={username} | ||||
|           /> | ||||
|         </div> | ||||
|         <div class="-mt-px relative"> | ||||
|           <input | ||||
|             aria-label={$_('password')} | ||||
|             aria-label={$_("password")} | ||||
|             type="password" | ||||
|             required="" | ||||
|             bind:value={password} | ||||
|             class="border-gray-300 placeholder-gray-500 appearance-none rounded-none relative block w-full px-3 py-2 border rounded-b-md focus:outline-none focus:shadow-outline-blue focus:border-blue-300 focus:z-10 sm:text-sm" | ||||
|             on:keydown={handleKeydown} | ||||
|             placeholder={$_('password')} /> | ||||
|             placeholder={$_("password")} | ||||
|           /> | ||||
|         </div> | ||||
|       </div> | ||||
| 
 | ||||
| @@ -115,28 +108,33 @@ | ||||
|         <button | ||||
|           on:click={login} | ||||
|           type="submit" | ||||
|           class="relative block w-full py-2 px-3 border border-transparent rounded-md text-white font-semibold bg-gray-800 hover:bg-gray-700 focus:bg-gray-900 focus:outline-none focus:shadow-outline sm:text-sm"> | ||||
|           class="relative block w-full py-2 px-3 border border-transparent rounded-md text-white font-semibold bg-gray-800 hover:bg-gray-700 focus:bg-gray-900 focus:outline-none focus:shadow-outline sm:text-sm" | ||||
|         > | ||||
|           <span class="absolute left-0 inset-y pl-3"> | ||||
|             <svg | ||||
|               class="h-5 w-5 text-gray-500" | ||||
|               fill="currentColor" | ||||
|               viewBox="0 0 20 20"> | ||||
|               viewBox="0 0 20 20" | ||||
|             > | ||||
|               <path | ||||
|                 fill-rule="evenodd" | ||||
|                 d="M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z" | ||||
|                 clip-rule="evenodd" /> | ||||
|                 clip-rule="evenodd" | ||||
|               /> | ||||
|             </svg> | ||||
|           </span> | ||||
|           {$_('log_in')} | ||||
|           {$_("log_in")} | ||||
|         </button> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="mt-2"> | ||||
|     <!-- <div class="mt-2"> | ||||
|       <a | ||||
|         href="/forgot_password" | ||||
|         class="block w-full text-center py-2 px-3 border border-gray-300 rounded-md font-medium hover:border-gray-400 focus:outline-none focus:border-gray-400 sm:text-sm"> | ||||
|         {$_('forgot_password?')} | ||||
|         class="block w-full text-center py-2 px-3 border border-gray-300 rounded-md font-medium hover:border-gray-400 focus:outline-none focus:border-gray-400 sm:text-sm" | ||||
|       > | ||||
|         {$_("forgot_password")} | ||||
|       </a> | ||||
|     </div> | ||||
|     </div> --> | ||||
|   </div> | ||||
| </div> | ||||
| <Footer /> | ||||
							
								
								
									
										52
									
								
								src/components/auth/PasswordStrength.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/components/auth/PasswordStrength.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| <script context="module"> | ||||
|   import { passwordStrength } from "check-password-strength"; | ||||
|   export function password_strong_enough(password_change) { | ||||
|     let strength = passwordStrength(password_change); | ||||
|     return ( | ||||
|       strength?.contains.includes("lowercase") && | ||||
|       strength?.contains.includes("uppercase") && | ||||
|       strength?.contains.includes("number") && | ||||
|       strength?.length > 9 | ||||
|     ); | ||||
|   } | ||||
|   export function password_strong_enough_and_equal( | ||||
|     password_change, | ||||
|     password_confirm | ||||
|   ) { | ||||
|     return ( | ||||
|       password_strong_enough(password_change) && | ||||
|       password_change === password_confirm | ||||
|     ); | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| <script> | ||||
|   import { getLocaleFromNavigator, _ } from "svelte-i18n"; | ||||
|   import { passwordStrength as Strength } from "check-password-strength"; | ||||
|   export let password_change; | ||||
|   export let password_confirm; | ||||
|  | ||||
|   $: strength = Strength(password_change); | ||||
|   $: passwords_match = | ||||
|     !password_confirm || password_confirm === password_change; | ||||
| </script> | ||||
|  | ||||
| <div class="ml-4"> | ||||
|   <ul class="list-disc font-medium tracking-wide text-red-500 text-xs"> | ||||
|     {#if !strength.contains.includes("lowercase")} | ||||
|       <li>{$_("must-contain-a-lowercase-letter")}</li> | ||||
|     {/if} | ||||
|     {#if !strength.contains.includes("uppercase")} | ||||
|       <li>{$_("must-contain-a-uppercase-letter")}</li> | ||||
|     {/if} | ||||
|     {#if !strength.contains.includes("number")} | ||||
|       <li>{$_("must-contain-a-number")}</li> | ||||
|     {/if} | ||||
|     {#if !(strength.length > 9)} | ||||
|       <li>{$_("must-be-at-least-10-characters-long")}</li> | ||||
|     {/if} | ||||
|     {#if !(passwords_match == true)} | ||||
|       <li>{$_("passwords-dont-match")}</li> | ||||
|     {/if} | ||||
|   </ul> | ||||
| </div> | ||||
							
								
								
									
										135
									
								
								src/components/auth/ResetPassword.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								src/components/auth/ResetPassword.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,135 @@ | ||||
| <script> | ||||
|   import { AuthService } from "@odit/lfk-client-js"; | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import toast from "svelte-french-toast"; | ||||
|   import PasswordStrength, { | ||||
|     password_strong_enough, | ||||
|   } from "../auth/PasswordStrength.svelte"; | ||||
|   let state = "reset_in_progress"; | ||||
|   let password = ""; | ||||
|   export let params; | ||||
|   function set_new_password() { | ||||
|     if (password.trim() !== "") { | ||||
|       toast.loading($_("password-reset-in-progress")); | ||||
|       AuthService.authControllerResetPassword(atob(params.resetkey), { | ||||
|         password, | ||||
|       }) | ||||
|         .then((resp) => { | ||||
|           toast.dismiss(); | ||||
|           toast($_("password-reset-successful")); | ||||
|           state = "reset_success"; | ||||
|         }) | ||||
|         .catch((err) => { | ||||
|           state = "reset_error"; | ||||
|         }); | ||||
|     } else { | ||||
|       toast.dismiss(); | ||||
|       toast.error($_("please-provide-a-password")); | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| {#if state === "reset_success"} | ||||
|   <div class="min-h-screen flex items-center justify-center bg-gray-100"> | ||||
|     <div class="max-w-md w-full py-12 px-6"> | ||||
|       <img style="height:10rem;" class="mx-auto" src="/lfk-logo.png" alt="" /> | ||||
|       <p class="mt-6 text-lg text-center font-bold text-gray-900"> | ||||
|         {$_("application_name")} | ||||
|       </p> | ||||
|       <p class="mt-2 mb-2 text-sm text-center text-gray-900 font-bold"> | ||||
|         {$_("successful-password-reset")} | ||||
|       </p> | ||||
|       <p class="mt-2 mb-2 text-sm text-center text-gray-900"> | ||||
|         {$_("you-can-now-use-your-new-password-to-log-in-to-your-account")} | ||||
|       </p> | ||||
|       <div class="mt-6"> | ||||
|         <div class="mt-6"> | ||||
|           <a | ||||
|             href="/login/" | ||||
|             class="text-center relative block w-full py-2 px-3 border border-transparent rounded-md text-white font-semibold bg-gray-800 hover:bg-gray-700 focus:bg-gray-900 focus:outline-none focus:shadow-outline sm:text-sm" | ||||
|           > | ||||
|             {$_("go-to-login")} | ||||
|           </a> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| {:else if state === "reset_error"} | ||||
|   <div class="min-h-screen flex items-center justify-center bg-gray-100"> | ||||
|     <div class="max-w-md w-full py-12 px-6"> | ||||
|       <img style="height:10rem;" class="mx-auto" src="/lfk-logo.png" alt="" /> | ||||
|       <p class="mt-6 text-lg text-center font-bold text-gray-900"> | ||||
|         {$_("application_name")} | ||||
|       </p> | ||||
|       <p class="mt-2 mb-2 text-sm text-center text-gray-900 font-bold"> | ||||
|         {$_("password-reset-failed")} | ||||
|       </p> | ||||
|       <p class="mt-2 mb-2 text-sm text-center text-gray-900"> | ||||
|         {$_("please-request-a-new-reset-mail")} | ||||
|       </p> | ||||
|       <div class="mt-6"> | ||||
|         <div class="mt-6"> | ||||
|           <a | ||||
|             href="/forgot_password/" | ||||
|             class="text-center relative block w-full py-2 px-3 border border-transparent rounded-md text-white font-semibold bg-gray-800 hover:bg-gray-700 focus:bg-gray-900 focus:outline-none focus:shadow-outline sm:text-sm" | ||||
|           > | ||||
|             {$_("request-a-new-reset-mail")} | ||||
|           </a> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| {:else if state === "reset_in_progress"} | ||||
|   <div class="min-h-screen flex items-center justify-center bg-gray-100"> | ||||
|     <div class="max-w-md w-full py-12 px-6"> | ||||
|       <img style="height:10rem;" class="mx-auto" src="/lfk-logo.png" alt="" /> | ||||
|       <p class="mt-6 text-lg text-center font-bold text-gray-900"> | ||||
|         {$_("application_name")} | ||||
|       </p> | ||||
|       <p class="mt-2 mb-4 text-md text-center text-gray-900"> | ||||
|         {$_("reset-password")} | ||||
|       </p> | ||||
|       <div> | ||||
|         <div class="rounded-md shadow-sm"> | ||||
|           <div> | ||||
|             <input | ||||
|               aria-label={$_("new-password")} | ||||
|               name="password" | ||||
|               type="password" | ||||
|               required="" | ||||
|               class="border-gray-300 placeholder-gray-500 appearance-none rounded-md relative block w-full px-3 py-2 border text-gray-900 focus:outline-none focus:shadow-outline-blue focus:border-blue-300 focus:z-10 sm:text-sm" | ||||
|               placeholder={$_("new-password")} | ||||
|               bind:value={password} | ||||
|             /> | ||||
|           </div> | ||||
|           <PasswordStrength bind:password_change={password} /> | ||||
|         </div> | ||||
|  | ||||
|         <div class="mt-5"> | ||||
|           <button | ||||
|             on:click={set_new_password} | ||||
|             disabled={!password_strong_enough(password)} | ||||
|             class:opacity-50={!password_strong_enough(password)} | ||||
|             type="submit" | ||||
|             class="relative block w-full py-2 px-3 border border-transparent rounded-md text-white font-semibold bg-gray-800 hover:bg-gray-700 focus:bg-gray-900 focus:outline-none focus:shadow-outline sm:text-sm" | ||||
|           > | ||||
|             <span class="absolute left-0 inset-y pl-3"> | ||||
|               <svg | ||||
|                 class="h-5 w-5 text-gray-500" | ||||
|                 fill="currentColor" | ||||
|                 viewBox="0 0 20 20" | ||||
|               > | ||||
|                 <path | ||||
|                   fill-rule="evenodd" | ||||
|                   d="M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z" | ||||
|                   clip-rule="evenodd" | ||||
|                 /> | ||||
|               </svg> | ||||
|             </span> | ||||
|             {$_("reset-my-password")} | ||||
|           </button> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| {/if} | ||||
| @@ -4,19 +4,22 @@ | ||||
| 
 | ||||
| <body class="antialiased font-sans"> | ||||
|   <div class="flex min-h-screen"> | ||||
|     <div class="w-full bg-white flex items-center justify-center "> | ||||
|     <div class="w-full bg-white flex items-center justify-center"> | ||||
|       <div class="max-w-sm m-8"> | ||||
|         <div class="text-black text-5xl md:text-15xl font-black"> | ||||
|           Internal Error | ||||
|           {$_("internal-error")} | ||||
|         </div> | ||||
|         <div class="w-16 h-1 bg-purple-light my-3 md:my-6" /> | ||||
|         <p | ||||
|           class="text-grey-darker text-2xl md:text-3xl font-light mb-8 leading-normal"> | ||||
|           Something went wrong in the UI logic | ||||
|           class="text-grey-darker text-2xl md:text-3xl font-light mb-8 leading-normal" | ||||
|         > | ||||
|           {$_("generic-ui-logic-error")} | ||||
|         </p> | ||||
|         <a | ||||
|           href="/" | ||||
|           class="bg-transparent text-grey-darkest font-bold uppercase tracking-wide py-3 px-6 border-2 border-grey-light hover:border-grey rounded-lg">{$_('goback')}</a> | ||||
|           class="bg-transparent text-grey-darkest font-bold uppercase tracking-wide py-3 px-6 border-2 border-grey-light hover:border-grey rounded-lg" | ||||
|           >{$_("goback")}</a | ||||
|         > | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| @@ -5,7 +5,7 @@ | ||||
| 
 | ||||
| <div class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-500"> | ||||
|   <span class="inline-block align-middle mr-8"> | ||||
|     <b class="capitalize">{$_('general_promise_error')}</b> | ||||
|     <b>{$_("general_promise_error")}</b> | ||||
|     {error} | ||||
|   </span> | ||||
| </div> | ||||
							
								
								
									
										25
									
								
								src/components/base/datatable_i18n.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/components/base/datatable_i18n.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| export function getlang(langkeys) { | ||||
|   return { | ||||
|     search: { | ||||
|       placeholder: langkeys.search, | ||||
|     }, | ||||
|     sort: { | ||||
|       sortAsc: langkeys.sort_column_ascending, | ||||
|       sortDesc: langkeys.sort_column_descending, | ||||
|     }, | ||||
|     pagination: { | ||||
|       previous: langkeys.previous, | ||||
|       next: langkeys.next, | ||||
|       navigate: (page, pages) => | ||||
|         `${langkeys.page} ${page} ${langkeys.of} ${pages}`, | ||||
|       page: (page) => `${langkeys.page} ${page}`, | ||||
|       showing: langkeys.showing, | ||||
|       of: langkeys.of, | ||||
|       to: langkeys.to, | ||||
|       results: langkeys.records, | ||||
|     }, | ||||
|     loading: langkeys.loading, | ||||
|     noRecordsFound: langkeys.no_matching_records_found, | ||||
|     error: langkeys.an_error_happened_while_fetching_the_data, | ||||
|   }; | ||||
| } | ||||
							
								
								
									
										6
									
								
								src/components/base/importfixes.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/components/base/importfixes.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| <!-- | ||||
|     Temporary tailwind import fixes for classes that wouldn't be directly used otherwise.  | ||||
|     Or as others may call it: Real big bullshit time. | ||||
|     Issue: https://git.odit.services/lfk/frontend/issues/136 | ||||
|  --> | ||||
| <div class="opacity-50" /> | ||||
							
								
								
									
										10
									
								
								src/components/base/outsideclick.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/components/base/outsideclick.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| /** Dispatch event on click outside of node */ | ||||
| export function clickOutside(node) { | ||||
|   const handleClick = (event) => { | ||||
|     if (event.target.getAttribute("data-id") === "modal_backdrop") { | ||||
|       node.dispatchEvent(new CustomEvent("click_outside", node)); | ||||
|     } | ||||
|   }; | ||||
|   document.removeEventListener("click", handleClick, true); | ||||
|   document.addEventListener("click", handleClick, true); | ||||
| } | ||||
							
								
								
									
										323
									
								
								src/components/base/simple.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										323
									
								
								src/components/base/simple.css
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,323 @@ | ||||
| .simplecontent * { | ||||
|   margin: 0; | ||||
|   padding: 0; | ||||
| } | ||||
| .simplecontent address, | ||||
| .simplecontent area, | ||||
| .simplecontent article, | ||||
| .simplecontent aside, | ||||
| .simplecontent audio, | ||||
| .simplecontent blockquote, | ||||
| .simplecontent datalist, | ||||
| .simplecontent details, | ||||
| .simplecontent dl, | ||||
| .simplecontent fieldset, | ||||
| .simplecontent figure, | ||||
| .simplecontent form, | ||||
| .simplecontent iframe, | ||||
| .simplecontent img, | ||||
| .simplecontent input, | ||||
| .simplecontent meter, | ||||
| .simplecontent nav, | ||||
| .simplecontent ol, | ||||
| .simplecontent optgroup, | ||||
| .simplecontent option, | ||||
| .simplecontent output, | ||||
| .simplecontent p, | ||||
| .simplecontent pre, | ||||
| .simplecontent progress, | ||||
| .simplecontent ruby, | ||||
| .simplecontent section, | ||||
| .simplecontent table, | ||||
| .simplecontent textarea, | ||||
| .simplecontent ul, | ||||
| .simplecontent video { | ||||
|   margin-bottom: 1rem; | ||||
| } | ||||
| .simplecontent button, | ||||
| .simplecontent html, | ||||
| .simplecontent input, | ||||
| .simplecontent select { | ||||
|   font-family: var(--nc-font-sans); | ||||
| } | ||||
| .simplecontent body { | ||||
|   margin: 0 auto; | ||||
|   max-width: 750px; | ||||
|   padding: 2rem; | ||||
|   border-radius: 6px; | ||||
|   overflow-x: hidden; | ||||
|   word-break: break-word; | ||||
|   overflow-wrap: break-word; | ||||
|   background: var(--nc-bg-1); | ||||
|   color: var(--nc-tx-2); | ||||
|   font-size: 1.03rem; | ||||
|   line-height: 1.5; | ||||
| } | ||||
| .simplecontent h1, | ||||
| .simplecontent h2, | ||||
| .simplecontent h3, | ||||
| .simplecontent h4, | ||||
| .simplecontent h5, | ||||
| .simplecontent h6 { | ||||
|   line-height: 1; | ||||
|   color: var(--nc-tx-1); | ||||
|   padding-top: 0.875rem; | ||||
| } | ||||
| .simplecontent h1, | ||||
| .simplecontent h2, | ||||
| .simplecontent h3 { | ||||
|   color: var(--nc-tx-1); | ||||
|   padding-bottom: 2px; | ||||
|   margin-bottom: 8px; | ||||
|   border-bottom: 1px solid var(--nc-bg-2); | ||||
| } | ||||
| .simplecontent h4, | ||||
| .simplecontent h5, | ||||
| .simplecontent h6 { | ||||
|   margin-bottom: 0.3rem; | ||||
| } | ||||
| .simplecontent h1 { | ||||
|   font-size: 2.25rem; | ||||
| } | ||||
| .simplecontent h2 { | ||||
|   font-size: 1.85rem; | ||||
| } | ||||
| .simplecontent h3 { | ||||
|   font-size: 1.55rem; | ||||
| } | ||||
| .simplecontent h4 { | ||||
|   font-size: 1.25rem; | ||||
| } | ||||
| .simplecontent h5 { | ||||
|   font-size: 1rem; | ||||
| } | ||||
| .simplecontent h6 { | ||||
|   font-size: 0.875rem; | ||||
| } | ||||
| .simplecontent a { | ||||
|   color: #3d5af1; | ||||
| } | ||||
| .simplecontent a:hover { | ||||
|   color: var(--nc-lk-2); | ||||
| } | ||||
| .simplecontent abbr:hover { | ||||
|   cursor: help; | ||||
| } | ||||
| .simplecontent blockquote { | ||||
|   padding: 1.5rem; | ||||
|   background: #ddd; | ||||
|   border-left: 5px solid var(--nc-bg-3); | ||||
| } | ||||
| .simplecontent abbr { | ||||
|   cursor: help; | ||||
| } | ||||
| .simplecontent blockquote :last-child { | ||||
|   padding-bottom: 0; | ||||
|   margin-bottom: 0; | ||||
| } | ||||
| .simplecontent header { | ||||
|   background: #ddd; | ||||
|   border-bottom: 1px solid var(--nc-bg-3); | ||||
|   padding: 2rem 1.5rem; | ||||
|   margin: -2rem calc(0px - (50vw - 50%)) 2rem; | ||||
|   padding-left: calc(50vw - 50%); | ||||
|   padding-right: calc(50vw - 50%); | ||||
| } | ||||
| .simplecontent header h1, | ||||
| .simplecontent header h2, | ||||
| .simplecontent header h3 { | ||||
|   padding-bottom: 0; | ||||
|   border-bottom: 0; | ||||
| } | ||||
| .simplecontent header > :first-child { | ||||
|   margin-top: 0; | ||||
|   padding-top: 0; | ||||
| } | ||||
| .simplecontent header > :last-child { | ||||
|   margin-bottom: 0; | ||||
| } | ||||
| .simplecontent a button, | ||||
| .simplecontent button, | ||||
| .simplecontent input[type="button"], | ||||
| .simplecontent input[type="reset"], | ||||
| .simplecontent input[type="submit"] { | ||||
|   font-size: 1rem; | ||||
|   display: inline-block; | ||||
|   padding: 6px 12px; | ||||
|   text-align: center; | ||||
|   text-decoration: none; | ||||
|   white-space: nowrap; | ||||
|   background: #3d5af1; | ||||
|   color: var(--nc-lk-tx); | ||||
|   border: 0; | ||||
|   border-radius: 4px; | ||||
|   box-sizing: border-box; | ||||
|   cursor: pointer; | ||||
|   color: var(--nc-lk-tx); | ||||
| } | ||||
| .simplecontent a button[disabled], | ||||
| .simplecontent button[disabled], | ||||
| .simplecontent input[type="button"][disabled], | ||||
| .simplecontent input[type="reset"][disabled], | ||||
| .simplecontent input[type="submit"][disabled] { | ||||
|   cursor: default; | ||||
|   opacity: 0.5; | ||||
|   cursor: not-allowed; | ||||
| } | ||||
| .simplecontent .button:focus, | ||||
| .simplecontent .button:hover, | ||||
| .simplecontent button:focus, | ||||
| .simplecontent button:hover, | ||||
| .simplecontent input[type="button"]:focus, | ||||
| .simplecontent input[type="button"]:hover, | ||||
| .simplecontent input[type="reset"]:focus, | ||||
| .simplecontent input[type="reset"]:hover, | ||||
| .simplecontent input[type="submit"]:focus, | ||||
| .simplecontent input[type="submit"]:hover { | ||||
|   background: var(--nc-lk-2); | ||||
| } | ||||
| .simplecontent code, | ||||
| .simplecontent kbd, | ||||
| .simplecontent pre, | ||||
| .simplecontent samp { | ||||
|   font-family: var(--nc-font-mono); | ||||
| } | ||||
| .simplecontent code, | ||||
| .simplecontent kbd, | ||||
| .simplecontent pre, | ||||
| .simplecontent samp { | ||||
|   background: #ddd; | ||||
|   border: 1px solid var(--nc-bg-3); | ||||
|   border-radius: 4px; | ||||
|   padding: 3px 6px; | ||||
|   font-size: 0.9rem; | ||||
| } | ||||
| .simplecontent kbd { | ||||
|   border-bottom: 3px solid var(--nc-bg-3); | ||||
| } | ||||
| .simplecontent pre { | ||||
|   padding: 1rem 1.4rem; | ||||
|   max-width: 100%; | ||||
|   overflow: auto; | ||||
| } | ||||
| .simplecontent pre code { | ||||
|   background: inherit; | ||||
|   font-size: inherit; | ||||
|   color: inherit; | ||||
|   border: 0; | ||||
|   padding: 0; | ||||
|   margin: 0; | ||||
| } | ||||
| .simplecontent code pre { | ||||
|   display: inline; | ||||
|   background: inherit; | ||||
|   font-size: inherit; | ||||
|   color: inherit; | ||||
|   border: 0; | ||||
|   padding: 0; | ||||
|   margin: 0; | ||||
| } | ||||
| .simplecontent details { | ||||
|   padding: 0.6rem 1rem; | ||||
|   background: #ddd; | ||||
|   border: 1px solid var(--nc-bg-3); | ||||
|   border-radius: 4px; | ||||
| } | ||||
| .simplecontent summary { | ||||
|   cursor: pointer; | ||||
|   font-weight: 700; | ||||
| } | ||||
| .simplecontent details[open] { | ||||
|   padding-bottom: 0.75rem; | ||||
| } | ||||
| .simplecontent details[open] summary { | ||||
|   margin-bottom: 6px; | ||||
| } | ||||
| .simplecontent details[open] > :last-child { | ||||
|   margin-bottom: 0; | ||||
| } | ||||
| .simplecontent dt { | ||||
|   font-weight: 700; | ||||
| } | ||||
| .simplecontent dd::before { | ||||
|   content: "→ "; | ||||
| } | ||||
| .simplecontent hr { | ||||
|   border: 0; | ||||
|   border-bottom: 1px solid var(--nc-bg-3); | ||||
|   margin: 1rem auto; | ||||
| } | ||||
| .simplecontent fieldset { | ||||
|   margin-top: 1rem; | ||||
|   padding: 2rem; | ||||
|   border: 1px solid var(--nc-bg-3); | ||||
|   border-radius: 4px; | ||||
| } | ||||
| .simplecontent legend { | ||||
|   padding: auto 0.5rem; | ||||
| } | ||||
| .simplecontent table { | ||||
|   border-collapse: collapse; | ||||
|   width: 100%; | ||||
| } | ||||
| .simplecontent td, | ||||
| .simplecontent th { | ||||
|   border: 1px solid var(--nc-bg-3); | ||||
|   text-align: left; | ||||
|   padding: 0.5rem; | ||||
| } | ||||
| .simplecontent th { | ||||
|   background: #ddd; | ||||
| } | ||||
| .simplecontent tr:nth-child(even) { | ||||
|   background: #ddd; | ||||
| } | ||||
| .simplecontent table caption { | ||||
|   font-weight: 700; | ||||
|   margin-bottom: 0.5rem; | ||||
| } | ||||
| .simplecontent textarea { | ||||
|   max-width: 100%; | ||||
| } | ||||
| .simplecontent ol, | ||||
| .simplecontent ul { | ||||
|   padding-left: 2rem; | ||||
| } | ||||
| .simplecontent li { | ||||
|   margin-top: 0.4rem; | ||||
| } | ||||
| .simplecontent ol ol, | ||||
| .simplecontent ol ul, | ||||
| .simplecontent ul ol, | ||||
| .simplecontent ul ul { | ||||
|   margin-bottom: 0; | ||||
| } | ||||
| .simplecontent mark { | ||||
|   padding: 3px 6px; | ||||
|   background: var(--nc-ac-1); | ||||
|   color: var(--nc-ac-tx); | ||||
| } | ||||
| .simplecontent input, | ||||
| .simplecontent select, | ||||
| .simplecontent textarea { | ||||
|   padding: 6px 12px; | ||||
|   margin-bottom: 0.5rem; | ||||
|   background: #ddd; | ||||
|   color: var(--nc-tx-2); | ||||
|   border: 1px solid var(--nc-bg-3); | ||||
|   border-radius: 4px; | ||||
|   box-shadow: none; | ||||
|   box-sizing: border-box; | ||||
| } | ||||
| .simplecontent img { | ||||
|   max-width: 100%; | ||||
| } | ||||
| .simplecontent blockquote { | ||||
|   background: #ececec; | ||||
| } | ||||
| .simplecontent ol { | ||||
|   list-style-type: decimal; | ||||
| } | ||||
| .simplecontent ul { | ||||
|   list-style-type: circle; | ||||
| } | ||||
							
								
								
									
										218
									
								
								src/components/cards/AddCardBulkModal.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										218
									
								
								src/components/cards/AddCardBulkModal.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,218 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import { clickOutside } from "../base/outsideclick"; | ||||
|  | ||||
|   import { RunnerCardService } from "@odit/lfk-client-js"; | ||||
|   import { createEventDispatcher } from "svelte"; | ||||
|   import toast from "svelte-french-toast"; | ||||
|   import DocumentServer from "../pdf_generation/DocumentServer"; | ||||
|   export let bulk_modal_open; | ||||
|   const dispatch = createEventDispatcher(); | ||||
|   const documentServer = new DocumentServer(config.baseurl_documentserver,config.documentserver_key); | ||||
|  | ||||
|  | ||||
|   $: card_count = 0; | ||||
|   $: is_card_count_valid = card_count > 0; | ||||
|   $: processed_last_submit = true; | ||||
|   $: createbtnenabled = is_card_count_valid; | ||||
|   (() => { | ||||
|     document.onkeydown = (e) => { | ||||
|       e = e || window.event; | ||||
|       if (e.key === "Escape") { | ||||
|         bulk_modal_open = false; | ||||
|       } | ||||
|       if (e.keyCode === 13) { | ||||
|         if (createbtnenabled === true) { | ||||
|           createbtnenabled = false; | ||||
|           submit_with_print(); | ||||
|         } | ||||
|       } | ||||
|     }; | ||||
|   })(); | ||||
|   function submit_without_print() { | ||||
|     if (processed_last_submit === true) { | ||||
|       processed_last_submit = false; | ||||
|       toast.loading($_("creating-blanco-cards")); | ||||
|       RunnerCardService.runnerCardControllerPostBlancoBulk(card_count, true) | ||||
|         .then((result) => { | ||||
|           bulk_modal_open = false; | ||||
|           // | ||||
|           toast.dismiss(); | ||||
|           toast.success($_("created-blanco-cards")); | ||||
|           dispatch("created", { cards: result }); | ||||
|         }) | ||||
|         .catch((err) => { | ||||
|           // | ||||
|         }) | ||||
|         .finally(() => { | ||||
|           processed_last_submit = true; | ||||
|         }); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   function submit_with_print() { | ||||
|     if (processed_last_submit === true) { | ||||
|       processed_last_submit = false; | ||||
|       toast.dismiss(); | ||||
|       toast.loading($_("creating-blanco-cards")); | ||||
|       RunnerCardService.runnerCardControllerPostBlancoBulk(card_count, true) | ||||
|         .then((result) => { | ||||
|           bulk_modal_open = false; | ||||
|           // | ||||
|           toast.dismiss(); | ||||
|           toast.success($_("created-blanco-cards")); | ||||
|           toast.loading($_("generating-pdf")); | ||||
|           dispatch("created", { cards: result }); | ||||
|           documentServer.generateCards(result, "de") | ||||
|             .then((blob) => { | ||||
|               const url = window.URL.createObjectURL(blob); | ||||
|               let a = document.createElement("a"); | ||||
|               a.href = url; | ||||
|               a.download = "Bulkcards.pdf"; | ||||
|               document.body.appendChild(a); | ||||
|               a.click(); | ||||
|               a.remove(); | ||||
|               toast.dismiss(); | ||||
|               toast.success($_("pdf-successfully-generated")); | ||||
|             }) | ||||
|             .catch((err) => { | ||||
|               console.error(err); | ||||
|             }); | ||||
|         }) | ||||
|         .catch((err) => { | ||||
|           // | ||||
|         }) | ||||
|         .finally(() => { | ||||
|           processed_last_submit = true; | ||||
|         }); | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| {#if bulk_modal_open} | ||||
|   <div | ||||
|     class="fixed z-10 inset-0 overflow-y-hidden" | ||||
|     use:clickOutside | ||||
|     on:click_outside={() => { | ||||
|       bulk_modal_open = false; | ||||
|     }} | ||||
|   > | ||||
|     <div | ||||
|       class="flex items-end justify-center h-screen text-center sm:block p-0 lg:p-4" | ||||
|     > | ||||
|       <div class="fixed inset-0 transition-opacity" aria-hidden="true"> | ||||
|         <div | ||||
|           class="absolute inset-0 bg-gray-500 opacity-75" | ||||
|           data-id="modal_backdrop" | ||||
|         /> | ||||
|       </div> | ||||
|       <span | ||||
|         class="hidden sm:inline-block sm:align-middle sm:h-screen" | ||||
|         aria-hidden="true">​</span | ||||
|       > | ||||
|       <div | ||||
|         class="inline-block align-bottom bg-white rounded-lg text-left shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-2xl sm:w-full relative z-10" | ||||
|         role="dialog" | ||||
|         aria-modal="true" | ||||
|         aria-labelledby="modal-headline" | ||||
|       > | ||||
|         <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4 rounded-t-xl"> | ||||
|           <div class=""> | ||||
|             <div | ||||
|               class="flex-shrink-0 flex items-center justify-center size-12 rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10" | ||||
|             > | ||||
|               <svg | ||||
|                 class="size-6 text-blue-600" | ||||
|                 fill="currentColor" | ||||
|                 xmlns="http://www.w3.org/2000/svg" | ||||
|                 viewBox="0 0 24 24" | ||||
|                 width="24" | ||||
|                 height="24" | ||||
|                 ><path fill="none" d="M0 0h24v24H0z" /> | ||||
|                 <path | ||||
|                   fill="currentColor" | ||||
|                   d="M22 10v10a1 1 0 01-1 1H3a1 1 0 01-1-1V10h20zm0-2H2V4a1 1 0 011-1h18a1 1 0 011 1v4zm-7 8v2h4v-2h-4z" | ||||
|                 /></svg | ||||
|               > | ||||
|             </div> | ||||
|             <div class="mt-3 sm:mt-0"> | ||||
|               <h3 class="text-lg leading-6 font-medium text-gray-900"> | ||||
|                 {$_("create-bulk-blanco-cards")} | ||||
|               </h3> | ||||
|               <div class="mb-6"> | ||||
|                 <p class="text-sm text-gray-500"> | ||||
|                   {$_( | ||||
|                     "just-enter-how-many-you-want-and-the-system-will-create-them" | ||||
|                   )} | ||||
|                 </p> | ||||
|               </div> | ||||
|               <div class="grid grid-cols-6 gap-2 lg:gap-6 text-left"> | ||||
|                 <div class="col-span-6"> | ||||
|                   <label | ||||
|                     for="amount" | ||||
|                     class="block text-sm font-medium text-gray-700" | ||||
|                     >{$_("amount")}</label | ||||
|                   > | ||||
|                   <div class="mt-1 flex rounded-md shadow-sm"> | ||||
|                     <input | ||||
|                       autocomplete="off" | ||||
|                       class:border-red-500={!is_card_count_valid} | ||||
|                       class:focus:border-red-500={!is_card_count_valid} | ||||
|                       class:focus:ring-red-500={!is_card_count_valid} | ||||
|                       bind:value={card_count} | ||||
|                       type="number" | ||||
|                       step="1" | ||||
|                       name="amount" | ||||
|                       class="focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-neutral-800 p-2" | ||||
|                       placeholder="400" | ||||
|                     /> | ||||
|                     <span | ||||
|                       class="inline-flex items-center px-3 rounded-r-md border border-gray-300 bg-gray-50 text-gray-500 text-sm" | ||||
|                       >{$_("cards")}</span | ||||
|                     > | ||||
|                   </div> | ||||
|                   {#if !is_card_count_valid} | ||||
|                     <span | ||||
|                       class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1" | ||||
|                     > | ||||
|                       {$_("you-must-create-at-least-one-card-or-cancel")} | ||||
|                     </span> | ||||
|                   {/if} | ||||
|                 </div> | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div class="bg-gray-50 px-4 lg:py-3 sm:px-6 grid gap-2 lg:rounded-b-xl pt-3 pb-10"> | ||||
|           <button | ||||
|             disabled={!createbtnenabled} | ||||
|             class:opacity-50={!createbtnenabled} | ||||
|             on:click={submit_with_print} | ||||
|             type="button" | ||||
|             class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" | ||||
|           > | ||||
|             {$_("create-and-generate-pdf")} | ||||
|           </button> | ||||
|           <button | ||||
|             disabled={!createbtnenabled} | ||||
|             class:opacity-50={!createbtnenabled} | ||||
|             on:click={submit_without_print} | ||||
|             type="button" | ||||
|             class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-gray-400 text-base font-medium text-white hover:bg-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" | ||||
|           > | ||||
|             {$_("create-without-pdf")} | ||||
|           </button> | ||||
|           <button | ||||
|             on:click={() => { | ||||
|               bulk_modal_open = false; | ||||
|             }} | ||||
|             type="button" | ||||
|             class="mr-auto w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" | ||||
|           > | ||||
|             {$_("cancel")} | ||||
|           </button> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| {/if} | ||||
							
								
								
									
										191
									
								
								src/components/cards/AddCardModal.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										191
									
								
								src/components/cards/AddCardModal.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,191 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import { clickOutside } from "../base/outsideclick"; | ||||
|  | ||||
|   import { RunnerCardService, RunnerService } from "@odit/lfk-client-js"; | ||||
|   import Select from "svelte-select"; | ||||
|   import { createEventDispatcher } from "svelte"; | ||||
|   import toast from "svelte-french-toast"; | ||||
|   export let modal_open; | ||||
|  | ||||
|   const dispatch = createEventDispatcher(); | ||||
|   const getRunnerLabel = (option) => { | ||||
|     if (option.middlename) { | ||||
|       return option.firstname + " " + option.middlename + " " + option.lastname; | ||||
|     } | ||||
|     return option.firstname + " " + option.lastname; | ||||
|   }; | ||||
|  | ||||
|   const filterRunners = (label, filterText, option) => { | ||||
|     if (filterText.startsWith("#")) { | ||||
|       return option.value.id == parseInt(filterText.replace("#", "")); | ||||
|     } | ||||
|     return ( | ||||
|       label.toLowerCase().includes(filterText.toLowerCase()) || | ||||
|       option.value.toString().startsWith(filterText.toLowerCase()) | ||||
|     ); | ||||
|   }; | ||||
|   function focus(el) { | ||||
|     el.focus(); | ||||
|   } | ||||
|   $: runner = 0; | ||||
|   $: enabled = true; | ||||
|   $: processed_last_submit = true; | ||||
|  | ||||
|   let loading = true; | ||||
|   let runners = []; | ||||
|   RunnerService.runnerControllerGetAll().then((val) => { | ||||
|     runners = val.map((r) => { | ||||
|       return { label: getRunnerLabel(r), value: r }; | ||||
|     }); | ||||
|     loading = false; | ||||
|   }); | ||||
|   $: createbtnenabled = true; | ||||
|   (() => { | ||||
|     document.onkeydown = (e) => { | ||||
|       e = e || window.event; | ||||
|       if (e.key === "Escape") { | ||||
|         modal_open = false; | ||||
|       } | ||||
|       if (e.keyCode === 13) { | ||||
|         if (createbtnenabled === true) { | ||||
|           createbtnenabled = false; | ||||
|           submit(); | ||||
|         } | ||||
|       } | ||||
|     }; | ||||
|   })(); | ||||
|   function submit() { | ||||
|     if (processed_last_submit === true) { | ||||
|       processed_last_submit = false; | ||||
|       toast.loading($_("adding-card")); | ||||
|       let postdata = { | ||||
|         runner, | ||||
|         enabled, | ||||
|       }; | ||||
|       RunnerCardService.runnerCardControllerPost(postdata) | ||||
|         .then((result) => { | ||||
|           runner = 0; | ||||
|           modal_open = false; | ||||
|           // | ||||
|           toast.dismiss(); | ||||
|           toast.success($_("card-added")); | ||||
|           dispatch("created", { cards: [result] }); | ||||
|         }) | ||||
|         .catch((err) => { | ||||
|           // | ||||
|         }) | ||||
|         .finally(() => { | ||||
|           processed_last_submit = true; | ||||
|         }); | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| {#if modal_open} | ||||
|   <div | ||||
|     class="fixed z-10 inset-0 overflow-y-hidden" | ||||
|     use:clickOutside | ||||
|     on:click_outside={() => { | ||||
|       modal_open = false; | ||||
|     }} | ||||
|   > | ||||
|     <div | ||||
|       class="flex items-end justify-center h-screen text-center sm:block p-0 lg:p-4" | ||||
|     > | ||||
|       <div class="fixed inset-0 transition-opacity" aria-hidden="true"> | ||||
|         <div | ||||
|           class="absolute inset-0 bg-gray-500 opacity-75" | ||||
|           data-id="modal_backdrop" | ||||
|         /> | ||||
|       </div> | ||||
|       <span | ||||
|         class="hidden sm:inline-block sm:align-middle sm:h-screen" | ||||
|         aria-hidden="true">​</span | ||||
|       > | ||||
|       <div | ||||
|         class="inline-block align-bottom text-left shadow-xl transform transition-all sm:align-middle w-full lg:w-auto min-w-auto lg:min-w-[35vw] relative z-10" | ||||
|         role="dialog" | ||||
|         aria-modal="true" | ||||
|         aria-labelledby="modal-headline" | ||||
|       > | ||||
|         <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4 rounded-t-xl"> | ||||
|           <div class=""> | ||||
|             <div | ||||
|               class="flex-shrink-0 flex items-center justify-center size-12 rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10" | ||||
|             > | ||||
|               <svg | ||||
|                 class="size-6 text-blue-600" | ||||
|                 fill="currentColor" | ||||
|                 xmlns="http://www.w3.org/2000/svg" | ||||
|                 viewBox="0 0 24 24" | ||||
|                 width="24" | ||||
|                 height="24" | ||||
|                 ><path fill="none" d="M0 0h24v24H0z" /> | ||||
|                 <path | ||||
|                   fill="currentColor" | ||||
|                   d="M22 10v10a1 1 0 01-1 1H3a1 1 0 01-1-1V10h20zm0-2H2V4a1 1 0 011-1h18a1 1 0 011 1v4zm-7 8v2h4v-2h-4z" | ||||
|                 /></svg | ||||
|               > | ||||
|             </div> | ||||
|             <div class="mt-3 sm:mt-0"> | ||||
|               <h3 class="text-lg leading-6 font-medium text-gray-900"> | ||||
|                 {$_("create-a-new-card")} | ||||
|               </h3> | ||||
|               <div class="mb-6"> | ||||
|                 <p class="text-sm text-gray-500"> | ||||
|                   {$_("you-can-provide-a-runner-but-you-dont-have-to")} | ||||
|                   {$_( | ||||
|                     "if-you-want-to-create-multiple-blanco-cards-try-the-add-bulk-button" | ||||
|                   )} | ||||
|                 </p> | ||||
|               </div> | ||||
|               <div class="grid grid-cols-6 gap-2 lg:gap-6 text-left"> | ||||
|                 <div class="col-span-6"> | ||||
|                   <label | ||||
|                     for="donor" | ||||
|                     class="block text-sm font-medium text-gray-700" | ||||
|                     >{$_("runner")}</label | ||||
|                   > | ||||
|                   <Select | ||||
|                     containerClasses="rounded-l-md mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-gray-300 border bg-gray-50 text-neutral-800 rounded-md p-2" | ||||
|                     itemFilter={(label, filterText, option) => | ||||
|                       filterRunners(label, filterText, option)} | ||||
|                     items={runners} | ||||
|                     bind:loading | ||||
|                     showChevron={!loading} | ||||
|                     placeholder={$_("search-for-runner-by-name-or-id")} | ||||
|                     noOptionsMessage={$_("no-runners-found")} | ||||
|                     on:select={(selectedValue) => | ||||
|                       (runner = selectedValue.detail.value.id)} | ||||
|                     on:clear={() => (runner = null)} | ||||
|                   /> | ||||
|                 </div> | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div class="bg-gray-50 px-4 lg:py-3 sm:px-6 grid gap-2 lg:rounded-b-xl pt-3 pb-10"> | ||||
|           <button | ||||
|             disabled={!createbtnenabled} | ||||
|             class:opacity-50={!createbtnenabled} | ||||
|             on:click={submit} | ||||
|             type="button" | ||||
|             class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" | ||||
|           > | ||||
|             {$_("create")} | ||||
|           </button> | ||||
|           <button | ||||
|             on:click={() => { | ||||
|               modal_open = false; | ||||
|             }} | ||||
|             type="button" | ||||
|             class="w-full justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 hidden lg:block" | ||||
|           > | ||||
|             {$_("cancel")} | ||||
|           </button> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| {/if} | ||||
							
								
								
									
										200
									
								
								src/components/cards/CardDetailModal.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										200
									
								
								src/components/cards/CardDetailModal.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,200 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import { clickOutside } from "../base/outsideclick"; | ||||
|  | ||||
|   import { RunnerCardService, RunnerService } from "@odit/lfk-client-js"; | ||||
|   import Select from "svelte-select"; | ||||
|   import { createEventDispatcher } from "svelte"; | ||||
|   import toast from "svelte-french-toast"; | ||||
|   export let edit_modal_open; | ||||
|   export let runner = {}; | ||||
|   export let editable = {}; | ||||
|   export let original_data = {}; | ||||
|   const getRunnerLabel = (option) => | ||||
|     option.firstname + " " + (option.middlename || "") + " " + option.lastname; | ||||
|   const filterRunners = (label, filterText, option) => { | ||||
|     if (filterText.startsWith("#")) { | ||||
|       return option.value.id == parseInt(filterText.replace("#", "")); | ||||
|     } | ||||
|     return ( | ||||
|       label.toLowerCase().includes(filterText.toLowerCase()) || | ||||
|       option.value.toString().startsWith(filterText.toLowerCase()) | ||||
|     ); | ||||
|   }; | ||||
|  | ||||
|   function focus(el) { | ||||
|     el.focus(); | ||||
|   } | ||||
|   $: runners = []; | ||||
|   $: enabled = true; | ||||
|   $: processed_last_submit = true; | ||||
|   const dispatch = createEventDispatcher(); | ||||
|   RunnerService.runnerControllerGetAll().then((val) => { | ||||
|     runners = val.map((r) => { | ||||
|       return { label: getRunnerLabel(r), value: r }; | ||||
|     }); | ||||
|   }); | ||||
|   $: createbtnenabled = !( | ||||
|     JSON.stringify(editable) === JSON.stringify(original_data) | ||||
|   ); | ||||
|   (() => { | ||||
|     document.onkeydown = (e) => { | ||||
|       e = e || window.event; | ||||
|       if (e.key === "Escape") { | ||||
|         edit_modal_open = false; | ||||
|       } | ||||
|       if (e.keyCode === 13) { | ||||
|         if (createbtnenabled === true) { | ||||
|           createbtnenabled = false; | ||||
|           submit(); | ||||
|         } | ||||
|       } | ||||
|     }; | ||||
|   })(); | ||||
|   function submit() { | ||||
|     if (processed_last_submit === true) { | ||||
|       processed_last_submit = false; | ||||
|       toast.loading($_("updating-card")); | ||||
|       RunnerCardService.runnerCardControllerPut(original_data.id, editable) | ||||
|         .then((result) => { | ||||
|           runner = {}; | ||||
|           editable = {}; | ||||
|           original_data = {}; | ||||
|           edit_modal_open = false; | ||||
|           // | ||||
|           toast.dismiss(); | ||||
|           toast.success($_("card-updated")); | ||||
|           dispatch("dataUpdated", { card: result }); | ||||
|         }) | ||||
|         .catch((err) => { | ||||
|           // | ||||
|         }) | ||||
|         .finally(() => { | ||||
|           processed_last_submit = true; | ||||
|         }); | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| {#if edit_modal_open} | ||||
|   <div | ||||
|     class="fixed z-10 inset-0 overflow-y-hidden" | ||||
|     use:clickOutside | ||||
|     on:click_outside={() => { | ||||
|       edit_modal_open = false; | ||||
|     }} | ||||
|   > | ||||
|     <div | ||||
|       class="flex items-end justify-center h-screen text-center sm:block p-0 lg:p-4" | ||||
|     > | ||||
|       <div class="fixed inset-0 transition-opacity" aria-hidden="true"> | ||||
|         <div | ||||
|           class="absolute inset-0 bg-gray-500 opacity-75" | ||||
|           data-id="modal_backdrop" | ||||
|         /> | ||||
|       </div> | ||||
|       <span | ||||
|         class="hidden sm:inline-block sm:align-middle sm:h-screen" | ||||
|         aria-hidden="true">​</span | ||||
|       > | ||||
|       <div | ||||
|         class="inline-block align-bottom text-left shadow-xl transform transition-all sm:align-middle w-full lg:w-auto min-w-auto lg:min-w-[35vw] relative z-10" | ||||
|         role="dialog" | ||||
|         aria-modal="true" | ||||
|         aria-labelledby="modal-headline" | ||||
|       > | ||||
|         <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4 rounded-t-xl"> | ||||
|           <div class=""> | ||||
|             <div | ||||
|               class="flex-shrink-0 flex items-center justify-center size-12 rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10" | ||||
|             > | ||||
|               <svg | ||||
|                 class="size-6 text-blue-600" | ||||
|                 fill="currentColor" | ||||
|                 xmlns="http://www.w3.org/2000/svg" | ||||
|                 viewBox="0 0 24 24" | ||||
|                 width="24" | ||||
|                 height="24" | ||||
|                 ><path fill="none" d="M0 0h24v24H0z" /> | ||||
|                 <path | ||||
|                   fill="currentColor" | ||||
|                   d="M22 10v10a1 1 0 01-1 1H3a1 1 0 01-1-1V10h20zm0-2H2V4a1 1 0 011-1h18a1 1 0 011 1v4zm-7 8v2h4v-2h-4z" | ||||
|                 /></svg | ||||
|               > | ||||
|             </div> | ||||
|             <div class="mt-3 sm:text-left max-h-[75vh] overflow-y-auto"> | ||||
|               <h3 class="text-lg leading-6 font-medium text-gray-900"> | ||||
|                 {$_("edit-a-card")} | ||||
|               </h3> | ||||
|               <div class="mb-6"> | ||||
|                 <p class="text-sm text-gray-500"> | ||||
|                   {$_("you-can-provide-a-runner-but-you-dont-have-to")} | ||||
|                 </p> | ||||
|               </div> | ||||
|               <div class="grid grid-cols-6 gap-2 lg:gap-6 text-left"> | ||||
|                 <div class="col-span-6"> | ||||
|                   <label | ||||
|                     for="runner" | ||||
|                     class="block text-sm font-medium text-gray-700" | ||||
|                     >{$_("runner")}</label | ||||
|                   > | ||||
|                   <Select | ||||
|                     containerClasses="rounded-l-md mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-gray-300 border bg-gray-50 text-neutral-800 rounded-md p-2" | ||||
|                     itemFilter={(label, filterText, option) => | ||||
|                       filterRunners(label, filterText, option)} | ||||
|                     items={runners} | ||||
|                     showChevron={true} | ||||
|                     placeholder={$_("search-for-runner-by-name-or-id")} | ||||
|                     noOptionsMessage={$_("no-runners-found")} | ||||
|                     bind:selectedValue={runner} | ||||
|                     on:select={(selectedValue) => | ||||
|                       (editable.runner = selectedValue.detail.value.id)} | ||||
|                     on:clear={() => (editable.runner = null)} | ||||
|                   /> | ||||
|                 </div> | ||||
|                 <div class="col-span-6"> | ||||
|                   <p class="text-gray-500"> | ||||
|                     <input | ||||
|                       id="enabled" | ||||
|                       on:change={() => { | ||||
|                         editable.enabled = !editable.enabled; | ||||
|                       }} | ||||
|                       name="enabled" | ||||
|                       type="checkbox" | ||||
|                       checked={editable.enabled} | ||||
|                       class="focus:ring-indigo-500 size-4 text-indigo-600 border-gray-300 rounded" | ||||
|                     /> | ||||
|                     {$_("this-card-is")} | ||||
|                     {#if editable.enabled} | ||||
|                       {$_("enabled")} | ||||
|                     {:else}{$_("disabled")}{/if} | ||||
|                   </p> | ||||
|                 </div> | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div class="bg-gray-50 px-4 lg:py-3 sm:px-6 grid gap-2 lg:rounded-b-xl pt-3 pb-10"> | ||||
|           <button | ||||
|             disabled={!createbtnenabled} | ||||
|             class:opacity-50={!createbtnenabled} | ||||
|             on:click={submit} | ||||
|             type="button" | ||||
|             class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" | ||||
|           > | ||||
|             {$_("save-changes")} | ||||
|           </button> | ||||
|           <button | ||||
|             on:click={() => { | ||||
|               edit_modal_open = false; | ||||
|             }} | ||||
|             type="button" | ||||
|             class="w-full justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 hidden lg:block" | ||||
|           > | ||||
|             {$_("cancel")} | ||||
|           </button> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| {/if} | ||||
							
								
								
									
										16
									
								
								src/components/cards/CardRunner.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/components/cards/CardRunner.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   export let runner; | ||||
| </script> | ||||
|  | ||||
| {#if !runner} | ||||
|   {$_("non-blanko")} | ||||
| {:else} | ||||
|   <a class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full border border-current bg-blue-100 text-blue-800" href={`/runners/${runner.id}`}> | ||||
|     {#if runner.middlename} | ||||
|       {runner.firstname} {runner.middlename} {runner.lastname} | ||||
|     {:else} | ||||
|       {runner.firstname} {runner.lastname} | ||||
|     {/if} | ||||
|   </a> | ||||
| {/if} | ||||
							
								
								
									
										16
									
								
								src/components/cards/CardStatus.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/components/cards/CardStatus.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   export let enabled = false; | ||||
| </script> | ||||
|  | ||||
| {#if enabled} | ||||
|   <span | ||||
|     class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full border border-current bg-green-100 text-green-800" | ||||
|     >{$_("enabled")}</span | ||||
|   > | ||||
| {:else} | ||||
|   <span | ||||
|     class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full border border-current bg-red-100 text-red-800" | ||||
|     >{$_("disabled")}</span | ||||
|   > | ||||
| {/if} | ||||
							
								
								
									
										53
									
								
								src/components/cards/Cards.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								src/components/cards/Cards.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import store from "../../store"; | ||||
|   import AddCardBulkModal from "./AddCardBulkModal.svelte"; | ||||
|   import AddCardModal from "./AddCardModal.svelte"; | ||||
|   import CardsOverview from "./CardsOverview.svelte"; | ||||
|   $: current_cards = []; | ||||
|   export let modal_open = false; | ||||
|   export let bulk_modal_open = false; | ||||
|   let addCards; | ||||
| </script> | ||||
|  | ||||
| <section class="container p-5"> | ||||
|   <h4 class="mb-1 text-3xl font-extrabold leading-tight"> | ||||
|     {$_("cards")} | ||||
|   </h4> | ||||
|   {#if store.state.jwtinfo.userdetails.permissions.includes("CARD:CREATE")} | ||||
|     <button | ||||
|       on:click={() => { | ||||
|         modal_open = true; | ||||
|       }} | ||||
|       type="button" | ||||
|       class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:w-auto sm:text-sm  mb-1 lg:mb-0" | ||||
|     > | ||||
|       {$_("add-card")} | ||||
|     </button> | ||||
|     <button | ||||
|       on:click={() => { | ||||
|         bulk_modal_open = true; | ||||
|       }} | ||||
|       type="button" | ||||
|       class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:w-auto sm:text-sm  mb-1 lg:mb-0" | ||||
|     > | ||||
|       {$_("create-bulk-cards")} | ||||
|     </button> | ||||
|   {/if} | ||||
|   <CardsOverview bind:current_cards bind:addCards /> | ||||
| </section> | ||||
|  | ||||
| {#if store.state.jwtinfo.userdetails.permissions.includes("CARD:CREATE")} | ||||
|   <AddCardModal | ||||
|     bind:modal_open | ||||
|     on:created={(event) => { | ||||
|       addCards(event.detail.cards); | ||||
|     }} | ||||
|   /> | ||||
|   <AddCardBulkModal | ||||
|     bind:bulk_modal_open | ||||
|     on:created={(event) => { | ||||
|       addCards(event.detail.cards); | ||||
|     }} | ||||
|   /> | ||||
| {/if} | ||||
							
								
								
									
										12
									
								
								src/components/cards/CardsEmptyState.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/components/cards/CardsEmptyState.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import cards_empty from "./cards.svg"; | ||||
| </script> | ||||
|  | ||||
| <div class="text-center items-center justify-center"> | ||||
|   <p class="mb-16 text-lg text-gray-500"> | ||||
|     <img class="m-auto mt-2" style="height:15rem" src={cards_empty} alt="" /> | ||||
|     <span class="font-bold">{$_("there-are-no-cards-yet")}</span><br /> | ||||
|     <span>{$_("add-your-first-card")}</span> | ||||
|   </p> | ||||
| </div> | ||||
							
								
								
									
										314
									
								
								src/components/cards/CardsOverview.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										314
									
								
								src/components/cards/CardsOverview.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,314 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import { RunnerCardService } from "@odit/lfk-client-js"; | ||||
|   import store from "../../store"; | ||||
|   import toast from "svelte-french-toast"; | ||||
|   import CardsEmptyState from "./CardsEmptyState.svelte"; | ||||
|   import CardDetailModal from "./CardDetailModal.svelte"; | ||||
|   import GenerateRunnerCards from "../pdf_generation/GenerateRunnerCards.svelte"; | ||||
|   import InputElement from "../shared/InputElement.svelte"; | ||||
|   import { | ||||
|     createSvelteTable, | ||||
|     flexRender, | ||||
|     getCoreRowModel, | ||||
|     getFilteredRowModel, | ||||
|     getPaginationRowModel, | ||||
|     getSortedRowModel, | ||||
|     renderComponent, | ||||
|   } from "@tanstack/svelte-table"; | ||||
|   import { writable } from "svelte/store"; | ||||
|   import TableBottom from "../shared/TableBottom.svelte"; | ||||
|   import TableActions from "../shared/TableActions.svelte"; | ||||
|   import TableHeader from "../shared/TableHeader.svelte"; | ||||
|   import CardStatus from "./CardStatus.svelte"; | ||||
|   import CardRunner from "./CardRunner.svelte"; | ||||
|   import { onMount } from "svelte"; | ||||
|   import { runnerFilter, statusFilter } from "../shared/tablefilters"; | ||||
|   import DeleteCardModal from "./DeleteCardModal.svelte"; | ||||
|  | ||||
|   export let edit_modal_open = false; | ||||
|   export let runner = {}; | ||||
|   export let editable = {}; | ||||
|   export let original_data = {}; | ||||
|   export let current_cards = []; | ||||
|   export const addCards = (cards) => { | ||||
|     current_cards = current_cards.concat(...cards); | ||||
|     options.update((options) => ({ | ||||
|       ...options, | ||||
|       data: current_cards, | ||||
|     })); | ||||
|   }; | ||||
|  | ||||
|   $: dataLoaded = false; | ||||
|   $: selected = | ||||
|     $table?.getSelectedRowModel().rows.map((row) => row.index) || []; | ||||
|   $: selectedCards = | ||||
|     $table?.getSelectedRowModel().rows.map((row) => row.original) || []; | ||||
|   $: active_delete = undefined; | ||||
|   $: cards_show = generate_cards.length > 0; | ||||
|   $: generate_cards = []; | ||||
|  | ||||
|   const columns = [ | ||||
|     { | ||||
|       accessorKey: "code", | ||||
|       header: () => $_("code"), | ||||
|       filterFn: `includesString`, | ||||
|     }, | ||||
|     { | ||||
|       accessorKey: "runner", | ||||
|       header: () => $_("runner"), | ||||
|       cell: (info) => { | ||||
|         return renderComponent(CardRunner, { runner: info.getValue() }); | ||||
|       }, | ||||
|       filterFn: `runner`, | ||||
|     }, | ||||
|     { | ||||
|       accessorKey: "enabled", | ||||
|       cell: (info) => { | ||||
|         return renderComponent(CardStatus, { enabled: info.getValue() }); | ||||
|       }, | ||||
|       header: () => $_("status"), | ||||
|       filterFn: `status`, | ||||
|     }, | ||||
|     { | ||||
|       accessorKey: "actions", | ||||
|       header: () => $_("action"), | ||||
|       cell: (info) => { | ||||
|         return renderComponent(TableActions, { | ||||
|           detailsAction: () => { | ||||
|             open_edit_modal( | ||||
|               current_cards[ | ||||
|                 current_cards.findIndex((r) => r.id == info.row.original.id) | ||||
|               ] | ||||
|             ); | ||||
|           }, | ||||
|           deleteAction: () => { | ||||
|             active_delete = | ||||
|               current_cards[ | ||||
|                 current_cards.findIndex((r) => r.id == info.row.original.id) | ||||
|               ]; | ||||
|           }, | ||||
|           deleteEnabled: | ||||
|             store.state.jwtinfo.userdetails.permissions.includes("CARD:DELETE"), | ||||
|         }); | ||||
|       }, | ||||
|       enableColumnFilter: false, | ||||
|       enableSorting: false, | ||||
|     }, | ||||
|   ]; | ||||
|  | ||||
|   const options = writable({ | ||||
|     data: [], | ||||
|     columns: columns, | ||||
|     initialState: { | ||||
|       pagination: { | ||||
|         pageSize: 50, | ||||
|       }, | ||||
|     }, | ||||
|     filterFns: { | ||||
|       runner: runnerFilter, | ||||
|       status: statusFilter, | ||||
|     }, | ||||
|     enableRowSelection: true, | ||||
|     getCoreRowModel: getCoreRowModel(), | ||||
|     getFilteredRowModel: getFilteredRowModel(), | ||||
|     getPaginationRowModel: getPaginationRowModel(), | ||||
|     getSortedRowModel: getSortedRowModel(), | ||||
|   }); | ||||
|  | ||||
|   const table = createSvelteTable(options); | ||||
|  | ||||
|   function open_edit_modal(card) { | ||||
|     const getRunnerLabel = (option) => | ||||
|       option.firstname + | ||||
|       " " + | ||||
|       (option.middlename || "") + | ||||
|       " " + | ||||
|       option.lastname; | ||||
|     if (card.runner?.id) { | ||||
|       runner = Object.assign( | ||||
|         { runner }, | ||||
|         { label: getRunnerLabel(card.runner), value: card.runner } | ||||
|       ); | ||||
|       card.runner = card.runner.id; | ||||
|     } else { | ||||
|       card.runner = null; | ||||
|       runner = null; | ||||
|     } | ||||
|     editable = Object.assign(editable, card); | ||||
|     original_data = Object.assign(original_data, card); | ||||
|     edit_modal_open = true; | ||||
|   } | ||||
|  | ||||
|   async function deleteCard(delete_card_id) { | ||||
|     await RunnerCardService.runnerCardControllerRemove(delete_card_id, true); | ||||
|     current_cards = current_cards.filter((r) => r.id !== delete_card_id); | ||||
|     options.update((options) => ({ | ||||
|       ...options, | ||||
|       data: current_cards, | ||||
|     })); | ||||
|     toast.success($_("card-deleted")); | ||||
|   } | ||||
|  | ||||
|   onMount(async () => { | ||||
|     let page = 0; | ||||
|     let pagesize = 500; | ||||
|     while (page >= 0) { | ||||
|       const cards = await RunnerCardService.runnerCardControllerGetAll( | ||||
|         page, | ||||
|         pagesize | ||||
|       ); | ||||
|       if (cards.length == 0) { | ||||
|         page = -2; | ||||
|       } | ||||
|  | ||||
|       current_cards = current_cards.concat(...cards); | ||||
|       options.update((options) => ({ | ||||
|         ...options, | ||||
|         data: current_cards, | ||||
|       })); | ||||
|  | ||||
|       dataLoaded = true; | ||||
|       page++; | ||||
|     } | ||||
|   }); | ||||
| </script> | ||||
|  | ||||
| {#if store.state.jwtinfo.userdetails.permissions.includes("CARD:UPDATE")} | ||||
|   <CardDetailModal | ||||
|     bind:edit_modal_open | ||||
|     bind:runner | ||||
|     bind:editable | ||||
|     bind:original_data | ||||
|     on:dataUpdated={(editevent) => { | ||||
|       console.log(editevent.detail.card) | ||||
|       current_cards = current_cards.filter((c) => c.id !== editevent.detail.card.id).concat([editevent.detail.card]).sort((a, b) => a.code - b.code); | ||||
|       options.update((options) => ({ | ||||
|         ...options, | ||||
|         data: current_cards, | ||||
|       })); | ||||
|     }} | ||||
|   /> | ||||
| {/if} | ||||
|  | ||||
| {#if store.state.jwtinfo.userdetails.permissions.includes("CARD:GET")} | ||||
|   <DeleteCardModal | ||||
|     delete_card={active_delete} | ||||
|     modal_open={active_delete != undefined} | ||||
|     on:delete={(event) => { | ||||
|       deleteCard(event.detail.id); | ||||
|     }} | ||||
|   /> | ||||
|   {#if !dataLoaded} | ||||
|     <div | ||||
|       class="bg-teal-lightest border-t-4 border-teal rounded-b text-teal-darkest px-4 py-3 shadow-md my-2" | ||||
|       role="alert" | ||||
|     > | ||||
|       <p class="font-bold">{$_("loading-cards")}</p> | ||||
|       <p class="text-sm">{$_("this-might-take-a-moment")}</p> | ||||
|     </div> | ||||
|   {:else if current_cards.length === 0} | ||||
|     <CardsEmptyState /> | ||||
|   {:else} | ||||
|     <div class="h-12 mt-1"> | ||||
|       {#if selected.length > 0} | ||||
|         <button | ||||
|           type="button" | ||||
|           class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:w-auto sm:text-sm inline-flex" | ||||
|           id="options-menu" | ||||
|           on:click={async () => { | ||||
|             const prom = []; | ||||
|             for (const card of selectedCards) { | ||||
|               prom.push( | ||||
|                 await RunnerCardService.runnerCardControllerRemove( | ||||
|                   card.id, | ||||
|                   true | ||||
|                 ) | ||||
|               ); | ||||
|             } | ||||
|             await Promise.all(prom); | ||||
|             for (const card of selectedCards) { | ||||
|               current_cards = current_cards.filter((r) => r.id !== card.id); | ||||
|             } | ||||
|             options.update((options) => ({ | ||||
|               ...options, | ||||
|               data: current_cards, | ||||
|             })); | ||||
|             $table.resetRowSelection(); | ||||
|           }} | ||||
|         > | ||||
|           {$_("delete-cards")} | ||||
|           <svg | ||||
|             xmlns="http://www.w3.org/2000/svg" | ||||
|             fill="none" | ||||
|             viewBox="0 0 24 24" | ||||
|             stroke-width="1.5" | ||||
|             stroke="currentColor" | ||||
|             class="w-5 h-5" | ||||
|           > | ||||
|             <path | ||||
|               stroke-linecap="round" | ||||
|               stroke-linejoin="round" | ||||
|               d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" | ||||
|             /> | ||||
|           </svg> | ||||
|         </button> | ||||
|       {/if} | ||||
|       <GenerateRunnerCards | ||||
|         cards_show={selected.length > 0} | ||||
|         bind:generate_cards={selectedCards} | ||||
|       /> | ||||
|     </div> | ||||
|     <div class="overflow-x-auto"> | ||||
|       <table class="w-full"> | ||||
|         <thead class="border-b border-gray-400"> | ||||
|           {#each $table.getHeaderGroups() as headerGroup} | ||||
|             <tr class="select-none"> | ||||
|               <th class="inset-y-0 left-0 px-4 py-2 text-left w-px"> | ||||
|                 <InputElement | ||||
|                   type="checkbox" | ||||
|                   checked={$table.getIsAllRowsSelected()} | ||||
|                   indeterminate={$table.getIsSomeRowsSelected()} | ||||
|                   on:change={() => $table.toggleAllRowsSelected()} | ||||
|                 /> | ||||
|               </th> | ||||
|               {#each headerGroup.headers as header} | ||||
|                 <TableHeader {header} /> | ||||
|               {/each} | ||||
|             </tr> | ||||
|           {/each} | ||||
|         </thead> | ||||
|         <tbody> | ||||
|           {#each $table.getRowModel().rows as row} | ||||
|             <tr class="odd:bg-white even:bg-gray-100"> | ||||
|               <td class="inset-y-0 left-0 px-4 py-2 text-center w-px"> | ||||
|                 <InputElement | ||||
|                   type="checkbox" | ||||
|                   checked={row.getIsSelected()} | ||||
|                   on:change={() => row.toggleSelected()} | ||||
|                 /> | ||||
|               </td> | ||||
|               {#each row.getVisibleCells() as cell} | ||||
|                 <td> | ||||
|                   <svelte:component | ||||
|                     this={flexRender( | ||||
|                       cell.column.columnDef.cell, | ||||
|                       cell.getContext() | ||||
|                     )} | ||||
|                   /> | ||||
|                 </td> | ||||
|               {/each} | ||||
|             </tr> | ||||
|           {/each} | ||||
|         </tbody> | ||||
|       </table> | ||||
|     </div> | ||||
|     <TableBottom {table} {selected} /> | ||||
|   {/if} | ||||
| {/if} | ||||
|  | ||||
| <style> | ||||
|   table tbody tr td:nth-child(2) { | ||||
|     font-family: monospace; | ||||
|   } | ||||
| </style> | ||||
							
								
								
									
										123
									
								
								src/components/cards/DeleteCardModal.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								src/components/cards/DeleteCardModal.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,123 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import { clickOutside } from "../base/outsideclick"; | ||||
|   import { createEventDispatcher, onMount } from "svelte"; | ||||
|   export let modal_open; | ||||
|   export let delete_card = { | ||||
|     id: 0, | ||||
|     code: "", | ||||
|     runner: { | ||||
|       firstname: "", | ||||
|       lastname: "", | ||||
|     }, | ||||
|   }; | ||||
|   const dispatch = createEventDispatcher(); | ||||
|   onMount(() => { | ||||
|     document.onkeydown = (e) => { | ||||
|       e = e || window.event; | ||||
|       if (e.key === "Escape") { | ||||
|         modal_open = false; | ||||
|       } | ||||
|       if (e.keyCode === 13) { | ||||
|         if (createbtnenabled === true) { | ||||
|           createbtnenabled = false; | ||||
|           submit(); | ||||
|         } | ||||
|       } | ||||
|     }; | ||||
|   }); | ||||
|   async function submit() { | ||||
|     dispatch("delete", { id: delete_card.id }); | ||||
|     modal_open = false; | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| {#if modal_open} | ||||
|   <div | ||||
|     class="fixed z-10 inset-0 overflow-y-hidden" | ||||
|     use:clickOutside | ||||
|     on:click_outside={() => { | ||||
|       modal_open = false; | ||||
|     }} | ||||
|   > | ||||
|     <div | ||||
|       class="flex items-end justify-center h-screen text-center sm:block p-0 lg:p-4" | ||||
|     > | ||||
|       <div class="fixed inset-0 transition-opacity" aria-hidden="true"> | ||||
|         <div | ||||
|           class="absolute inset-0 bg-gray-500 opacity-75" | ||||
|           data-id="modal_backdrop" | ||||
|         /> | ||||
|       </div> | ||||
|       <span | ||||
|         class="hidden sm:inline-block sm:align-middle sm:h-screen" | ||||
|         aria-hidden="true">​</span | ||||
|       > | ||||
|       <div | ||||
|         class="inline-block align-bottom text-left shadow-xl transform transition-all sm:align-middle w-full lg:w-auto min-w-auto lg:min-w-[35vw] relative z-10" | ||||
|         role="dialog" | ||||
|         aria-modal="true" | ||||
|         aria-labelledby="modal-headline" | ||||
|       > | ||||
|         <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4 rounded-t-xl"> | ||||
|           <div class=""> | ||||
|             <div | ||||
|               class="flex-shrink-0 flex items-center justify-center size-12 rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10" | ||||
|             > | ||||
|               <svg | ||||
|                 class="size-6 text-blue-600" | ||||
|                 fill="currentColor" | ||||
|                 xmlns="http://www.w3.org/2000/svg" | ||||
|                 viewBox="0 0 24 24" | ||||
|                 width="24" | ||||
|                 height="24" | ||||
|                 ><path fill="none" d="M0 0h24v24H0z" /> | ||||
|                 <path | ||||
|                   fill="currentColor" | ||||
|                   d="M22 10v10a1 1 0 01-1 1H3a1 1 0 01-1-1V10h20zm0-2H2V4a1 1 0 011-1h18a1 1 0 011 1v4zm-7 8v2h4v-2h-4z" | ||||
|                 /></svg | ||||
|               > | ||||
|             </div> | ||||
|             <div class="mt-3 sm:text-left max-h-[75vh] overflow-y-auto"> | ||||
|               <h3 class="text-lg leading-6 font-medium text-gray-900"> | ||||
|                 {$_("please-confirm-the-deletion-of-card")} | ||||
|               </h3> | ||||
|               <div class="w-full"> | ||||
|                 {$_("card")} #{delete_card.code}<br /> | ||||
|                 <span class="inline-block"> | ||||
|                   {$_("runner")}: | ||||
|                   {#if delete_card.runner} | ||||
|                     <span class="inline-block" | ||||
|                       >{delete_card.runner.firstname} | ||||
|                       {delete_card.runner.lastname}</span | ||||
|                     > | ||||
|                   {:else} | ||||
|                     {$_("non-blanko")} | ||||
|                   {/if}</span | ||||
|                 > | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div class="bg-gray-50 px-4 lg:py-3 sm:px-6 grid gap-2 lg:rounded-b-xl pt-3 pb-10"> | ||||
|           <button | ||||
|             on:click={submit} | ||||
|             type="button" | ||||
|             class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500" | ||||
|           > | ||||
|             {$_("delete")} | ||||
|           </button> | ||||
|           <button | ||||
|             on:click={() => { | ||||
|               modal_open = false; | ||||
|             }} | ||||
|             type="button" | ||||
|             class="w-full justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 hidden lg:block" | ||||
|           > | ||||
|             {$_("cancel")} | ||||
|           </button> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| {/if} | ||||
							
								
								
									
										1
									
								
								src/components/cards/cards.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/components/cards/cards.svg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| <svg data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 653.9 247.6"><path d="M272 211l-53 12s-11-2-1-17l4-4 27-14v-2l-6-41-2-16 17-17 4-3 44 41v1l-19 33z" fill="#ffb7b7"/><path d="M253 198l-54 13a6 6 0 01-5-7 16 16 0 012-5 48 48 0 016-9l28-14-4-24-1-12-2-8-2-16 21-19 22 21 22 20-3 5-3 7-17 30-3 6z" fill="#ffb7b7"/><path d="M346 190s-20-1-28-15a24 24 0 01-3-14l-8-17-11-23-30-4-2 1-21 19-7 6-10 9-49 44a37 37 0 01-7 9 50 50 0 01-9 7c-10 5-24 9-44 7L10 248 0 176l89-29 131-86 89 23 41 58z" fill="#ffb7b7"/><path d="M648 0H275a5 5 0 00-5 5v221a5 5 0 005 6h373a5 5 0 006-6V5a5 5 0 00-6-5z" fill="#fff"/><path d="M648 0H275a5 5 0 00-5 5v221a5 5 0 005 6h373a5 5 0 006-6V5a5 5 0 00-6-5zm4 226a4 4 0 01-4 4H275a4 4 0 01-3-4V5a4 4 0 013-3h373a4 4 0 014 3z" fill="#3f3d56"/><path d="M312 30a9 9 0 119-9 9 9 0 01-9 9zm0-17a8 8 0 107 8 8 8 0 00-7-8z" fill="#6c63ff"/><path d="M297 21a8 8 0 016-8 8 8 0 100 16 8 8 0 01-6-8zM349 130a7 7 0 01-7-7v-20a7 7 0 0114 0v20a7 7 0 01-7 7zM368 130a7 7 0 01-7-7v-20a7 7 0 0114 0v20a7 7 0 01-7 7zM386 130a7 7 0 01-7-7v-20a7 7 0 0114 0v20a7 7 0 01-7 7zM415 130a7 7 0 01-7-7v-20a7 7 0 0114 0v20a7 7 0 01-7 7zM434 130a7 7 0 01-7-7v-20a7 7 0 0113 0v20a7 7 0 01-6 7zM452 130a7 7 0 01-7-7v-20a7 7 0 0114 0v20a7 7 0 01-7 7zM481 130a7 7 0 01-7-7v-20a7 7 0 0114 0v20a7 7 0 01-7 7zM499 130a7 7 0 01-7-7v-20a7 7 0 0114 0v20a7 7 0 01-7 7zM518 130a7 7 0 01-7-7v-20a7 7 0 0114 0v20a7 7 0 01-7 7zM546 130a7 7 0 01-7-7v-20a7 7 0 0114 0v20a7 7 0 01-7 7zM565 130a7 7 0 01-7-7v-20a7 7 0 0114 0v20a7 7 0 01-7 7zM583 130a7 7 0 01-7-7v-20a7 7 0 0114 0v20a7 7 0 01-7 7z" fill="#6c63ff"/><path d="M396 208h-99a5 5 0 110-10h99a5 5 0 010 10zM364 188h-35a5 5 0 110-10h35a5 5 0 110 10z" fill="#e6e6e6"/><path fill="#3f3d56" d="M271 46h381v2H271z"/><path opacity=".1" d="M228 203l-1-2 33-15 8-27-12-10 1-1 13 10-8 30-34 15zM196 199l-9 4-17 2a50 50 0 01-9 7l-16-1-26-1 88-74 18 4-29 59z"/><path d="M318 175l-8 1-47 4-29 1-38 18-9 4-70 8 95-81 11 2 20 5 22 5 18 1 24 1 20 1a13 13 0 0112 13c0 7-5 14-21 17z" fill="#ffb7b7"/><path d="M325 170s-7-2-9-9c-2-4-1-9 3-15l1 1c-3 6-4 10-3 14 2 5 9 7 9 7zM197 197l34-16v2l-33 16zM218 135l48-19v2l-41 16 35 6v2l-42-7z" opacity=".1"/></svg> | ||||
| After Width: | Height: | Size: 2.1 KiB | 
							
								
								
									
										480
									
								
								src/components/contacts/AddContactModal.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										480
									
								
								src/components/contacts/AddContactModal.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,480 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import { clickOutside } from "../base/outsideclick"; | ||||
|  | ||||
|   import { | ||||
|     GroupContactService, | ||||
|     RunnerTeamService, | ||||
|     RunnerOrganizationService, | ||||
|   } from "@odit/lfk-client-js"; | ||||
|   import isEmail from "validator/es/lib/isEmail"; | ||||
|   import isMobilePhone from "validator/es/lib/isMobilePhone"; | ||||
|   import toast from "svelte-french-toast"; | ||||
|   export let modal_open; | ||||
|   export let current_contacts; | ||||
|   $: selected_team = []; | ||||
|   let firstname_input; | ||||
|   let lastname_input; | ||||
|   let middlename_input; | ||||
|   let phone_input; | ||||
|   let email_input; | ||||
|   let address_input1; | ||||
|   let address_input2; | ||||
|   let address_zipcode; | ||||
|   let address_city; | ||||
|   let teams = []; | ||||
|   let orgs = []; | ||||
|   RunnerTeamService.runnerTeamControllerGetAll().then((val) => { | ||||
|     teams = val; | ||||
|   }); | ||||
|   RunnerOrganizationService.runnerOrganizationControllerGetAll().then((val) => { | ||||
|     orgs = val; | ||||
|   }); | ||||
|   function focus(el) { | ||||
|     el.focus(); | ||||
|   } | ||||
|   $: middlename_input_value = ""; | ||||
|   $: phone_input_value = ""; | ||||
|   $: email_input_value = ""; | ||||
|   $: lastname_input_value = ""; | ||||
|   $: firstname_input_value = ""; | ||||
|   $: address_input1_value = ""; | ||||
|   $: address_input2_value = ""; | ||||
|   $: address_zipcode_value = ""; | ||||
|   $: address_city_value = ""; | ||||
|   $: processed_last_submit = true; | ||||
|   $: address_checked = false; | ||||
|   $: isPhoneValidOrEmpty = | ||||
|     (phone_input_value.includes("+") && | ||||
|       isMobilePhone( | ||||
|         phone_input_value | ||||
|           .replaceAll("(", "") | ||||
|           .replaceAll(")", "") | ||||
|           .replaceAll("-", "") | ||||
|           .replaceAll(" ", "") | ||||
|       )) || | ||||
|     phone_input_value === ""; | ||||
|   $: isEmailValidOrEmpty = | ||||
|     isEmail(email_input_value) || email_input_value === ""; | ||||
|   $: isLastnameValid = lastname_input_value.trim().length !== 0; | ||||
|   $: isFirstnameValid = firstname_input_value.trim().length !== 0; | ||||
|   $: isAddress1Valid = address_input1_value.trim().length !== 0; | ||||
|   $: iszipcodevalid = address_zipcode_value.trim().length !== 0; | ||||
|   $: iscityvalid = address_city_value.trim().length !== 0; | ||||
|   $: createbtnenabled = | ||||
|     isFirstnameValid && | ||||
|     isLastnameValid && | ||||
|     isEmailValidOrEmpty && | ||||
|     isPhoneValidOrEmpty && | ||||
|     ((isAddress1Valid && iszipcodevalid && iscityvalid) || | ||||
|       address_checked === false); | ||||
|   (() => { | ||||
|     document.onkeydown = (e) => { | ||||
|       e = e || window.event; | ||||
|       if (e.key === "Escape") { | ||||
|         modal_open = false; | ||||
|       } | ||||
|       if (e.keyCode === 13) { | ||||
|         if (createbtnenabled === true) { | ||||
|           createbtnenabled = false; | ||||
|           submit(); | ||||
|         } | ||||
|       } | ||||
|     }; | ||||
|   })(); | ||||
|   function submit() { | ||||
|     if (processed_last_submit === true) { | ||||
|       processed_last_submit = false; | ||||
|       toast.loading($_("contact-is-being-added")); | ||||
|       let address = {}; | ||||
|       if (address_checked === true) { | ||||
|         address = { | ||||
|           address1: address_input1_value, | ||||
|           address2: address_input2_value || "", | ||||
|           postalcode: address_zipcode_value, | ||||
|           city: address_city_value, | ||||
|           country: "DE", | ||||
|         }; | ||||
|       } | ||||
|       let postdata = { | ||||
|         groups: selected_team, | ||||
|         firstname: firstname_input_value, | ||||
|         lastname: lastname_input_value, | ||||
|         address, | ||||
|       }; | ||||
|       if (middlename_input_value) { | ||||
|         postdata.middlename = middlename_input_value; | ||||
|       } | ||||
|       if (phone_input_value) { | ||||
|         postdata.phone = phone_input_value; | ||||
|       } | ||||
|       if (email_input_value) { | ||||
|         postdata.email = email_input_value; | ||||
|       } | ||||
|       GroupContactService.groupContactControllerPost(postdata) | ||||
|         .then((result) => { | ||||
|           firstname_input_value = ""; | ||||
|           lastname_input_value = ""; | ||||
|           middlename_input_value = ""; | ||||
|           email_input_value = ""; | ||||
|           modal_open = false; | ||||
|           // | ||||
|           toast.dismiss(); | ||||
|           toast.success($_("contact-added")); | ||||
|           current_contacts.push(result); | ||||
|           current_contacts = current_contacts; | ||||
|         }) | ||||
|         .catch((err) => { | ||||
|           // | ||||
|         }) | ||||
|         .finally(() => { | ||||
|           processed_last_submit = true; | ||||
|         }); | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| {#if modal_open} | ||||
|   <div | ||||
|     class="fixed z-10 inset-0 overflow-y-hidden" | ||||
|     use:clickOutside | ||||
|     on:click_outside={() => { | ||||
|       modal_open = false; | ||||
|     }} | ||||
|   > | ||||
|     <div | ||||
|       class="flex items-end justify-center h-screen text-center sm:block p-0 lg:p-4" | ||||
|     > | ||||
|       <div class="fixed inset-0 transition-opacity" aria-hidden="true"> | ||||
|         <div | ||||
|           class="absolute inset-0 bg-gray-500 opacity-75" | ||||
|           data-id="modal_backdrop" | ||||
|         /> | ||||
|       </div> | ||||
|       <span | ||||
|         class="hidden sm:inline-block sm:align-middle sm:h-screen" | ||||
|         aria-hidden="true">​</span | ||||
|       > | ||||
|       <div | ||||
|         class="inline-block align-bottom text-left shadow-xl transform transition-all sm:align-middle w-full lg:w-auto min-w-auto lg:min-w-[35vw] relative z-10" | ||||
|         role="dialog" | ||||
|         aria-modal="true" | ||||
|         aria-labelledby="modal-headline" | ||||
|       > | ||||
|         <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4 rounded-t-xl"> | ||||
|           <div class=""> | ||||
|             <div | ||||
|               class="flex-shrink-0 flex items-center justify-center size-12 rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10" | ||||
|             > | ||||
|               <svg | ||||
|                 class="size-6 text-blue-600" | ||||
|                 fill="currentColor" | ||||
|                 xmlns="http://www.w3.org/2000/svg" | ||||
|                 viewBox="0 0 24 24" | ||||
|                 width="24" | ||||
|                 height="24" | ||||
|                 ><path fill="none" d="M0 0h24v24H0z" /> | ||||
|                 <path | ||||
|                   d="M2 22a8 8 0 1 1 16 0H2zm8-9c-3.315 0-6-2.685-6-6s2.685-6 6-6 6 2.685 6 6-2.685 6-6 6zm10 4h4v2h-4v-2zm-3-5h7v2h-7v-2zm2-5h5v2h-5V7z" | ||||
|                 /></svg | ||||
|               > | ||||
|             </div> | ||||
|             <div class="mt-3 sm:text-left max-h-[75vh] overflow-y-auto"> | ||||
|               <h3 class="text-lg leading-6 font-medium text-gray-900"> | ||||
|                 {$_("create-a-new-contact")} | ||||
|               </h3> | ||||
|               <div class="mb-6"> | ||||
|                 <p class="text-sm text-gray-500"> | ||||
|                   {$_( | ||||
|                     "please-provide-the-required-information-to-add-a-new-contact" | ||||
|                   )} | ||||
|                 </p> | ||||
|               </div> | ||||
|               <div class="grid grid-cols-6 gap-2 lg:gap-6 text-left"> | ||||
|                 <div class="col-span-6"> | ||||
|                   <label | ||||
|                     for="firstname" | ||||
|                     class="block text-sm font-medium text-gray-700" | ||||
|                     >{$_("first-name")}</label | ||||
|                   > | ||||
|                   <input | ||||
|                     use:focus | ||||
|                     autocomplete="off" | ||||
|                     placeholder={$_("first-name")} | ||||
|                     class:border-red-500={!isFirstnameValid} | ||||
|                     class:focus:border-red-500={!isFirstnameValid} | ||||
|                     class:focus:ring-red-500={!isFirstnameValid} | ||||
|                     bind:value={firstname_input_value} | ||||
|                     bind:this={firstname_input} | ||||
|                     type="text" | ||||
|                     name="firstname" | ||||
|                     class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-neutral-800 rounded-md p-2" | ||||
|                   /> | ||||
|                   {#if !isFirstnameValid} | ||||
|                     <span | ||||
|                       class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1" | ||||
|                     > | ||||
|                       {$_("first-name-is-required")} | ||||
|                     </span> | ||||
|                   {/if} | ||||
|                 </div> | ||||
|                 <div class="col-span-6"> | ||||
|                   <label | ||||
|                     for="trackname" | ||||
|                     class="block text-sm font-medium text-gray-700" | ||||
|                     >{$_("middle-name")}</label | ||||
|                   > | ||||
|                   <input | ||||
|                     autocomplete="off" | ||||
|                     placeholder={$_("middle-name")} | ||||
|                     bind:value={middlename_input_value} | ||||
|                     bind:this={middlename_input} | ||||
|                     type="text" | ||||
|                     name="trackname" | ||||
|                     class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-neutral-800 rounded-md p-2" | ||||
|                   /> | ||||
|                 </div> | ||||
|                 <div class="col-span-6"> | ||||
|                   <label | ||||
|                     for="lastname" | ||||
|                     class="block text-sm font-medium text-gray-700" | ||||
|                     >{$_("last-name")}</label | ||||
|                   > | ||||
|                   <input | ||||
|                     autocomplete="off" | ||||
|                     placeholder={$_("last-name")} | ||||
|                     class:border-red-500={!isLastnameValid} | ||||
|                     class:focus:border-red-500={!isLastnameValid} | ||||
|                     class:focus:ring-red-500={!isLastnameValid} | ||||
|                     bind:value={lastname_input_value} | ||||
|                     bind:this={lastname_input} | ||||
|                     type="text" | ||||
|                     name="lastname" | ||||
|                     class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-neutral-800 rounded-md p-2" | ||||
|                   /> | ||||
|                   {#if !isLastnameValid} | ||||
|                     <span | ||||
|                       class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1" | ||||
|                     > | ||||
|                       {$_("last-name-is-required")} | ||||
|                     </span> | ||||
|                   {/if} | ||||
|                 </div> | ||||
|                 <div class="col-span-6"> | ||||
|                   <label | ||||
|                     for="team" | ||||
|                     class="block text-sm font-medium text-gray-700" | ||||
|                     >{$_("teams")}</label | ||||
|                   > | ||||
|                   <select | ||||
|                     name="team" | ||||
|                     multiple | ||||
|                     bind:value={selected_team} | ||||
|                     class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-neutral-800 rounded-md p-2" | ||||
|                   > | ||||
|                     {#each teams as team} | ||||
|                       <option value={team.id}> | ||||
|                         {team.parentGroup.name} | ||||
|                         > | ||||
|                         {team.name} | ||||
|                       </option> | ||||
|                     {/each} | ||||
|                     {#each orgs as org} | ||||
|                       <option value={org.id}>{org.name}</option> | ||||
|                     {/each} | ||||
|                   </select> | ||||
|                 </div> | ||||
|                 <div class="col-span-6"> | ||||
|                   <label | ||||
|                     for="phone" | ||||
|                     class="block text-sm font-medium text-gray-700" | ||||
|                     >{$_("phone")}</label | ||||
|                   > | ||||
|                   <input | ||||
|                     autocomplete="off" | ||||
|                     placeholder={$_("phone")} | ||||
|                     class:border-red-500={!isPhoneValidOrEmpty} | ||||
|                     class:focus:border-red-500={!isPhoneValidOrEmpty} | ||||
|                     class:focus:ring-red-500={!isPhoneValidOrEmpty} | ||||
|                     bind:value={phone_input_value} | ||||
|                     bind:this={phone_input} | ||||
|                     type="tel" | ||||
|                     name="phone" | ||||
|                     class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-neutral-800 rounded-md p-2" | ||||
|                   /> | ||||
|                   {#if !isPhoneValidOrEmpty} | ||||
|                     <span | ||||
|                       class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1" | ||||
|                     > | ||||
|                       {@html $_( | ||||
|                         "the-provided-phone-number-is-invalid-less-than-br-greater-than-please-enter-a-valid-international-number" | ||||
|                       )} | ||||
|                     </span> | ||||
|                   {/if} | ||||
|                 </div> | ||||
|                 <div class="col-span-6"> | ||||
|                   <label | ||||
|                     for="email" | ||||
|                     class="block text-sm font-medium text-gray-700" | ||||
|                     >{$_("e-mail-adress")}</label | ||||
|                   > | ||||
|                   <input | ||||
|                     autocomplete="off" | ||||
|                     placeholder={$_("e-mail-adress")} | ||||
|                     class:border-red-500={!isEmailValidOrEmpty} | ||||
|                     class:focus:border-red-500={!isEmailValidOrEmpty} | ||||
|                     class:focus:ring-red-500={!isEmailValidOrEmpty} | ||||
|                     bind:value={email_input_value} | ||||
|                     bind:this={email_input} | ||||
|                     type="email" | ||||
|                     name="email" | ||||
|                     class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-neutral-800 rounded-md p-2" | ||||
|                   /> | ||||
|                   {#if !isEmailValidOrEmpty} | ||||
|                     <span | ||||
|                       class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1" | ||||
|                     > | ||||
|                       {$_("valid-email-is-required")} | ||||
|                     </span> | ||||
|                   {/if} | ||||
|                 </div> | ||||
|                 <div class="flex items-start"> | ||||
|                   <div class="flex items-center h-5"> | ||||
|                     <input | ||||
|                       bind:checked={address_checked} | ||||
|                       id="comments" | ||||
|                       name="comments" | ||||
|                       type="checkbox" | ||||
|                       class="focus:ring-indigo-500 size-4 text-indigo-600 border-gray-300 rounded" | ||||
|                     /> | ||||
|                   </div> | ||||
|                   <div class="ml-3 text-sm"> | ||||
|                     <label for="comments" class="font-semibold text-gray-700" | ||||
|                       >{$_("address")}</label | ||||
|                     > | ||||
|                   </div> | ||||
|                 </div> | ||||
|                 {#if address_checked === true} | ||||
|                   <div class="col-span-6"> | ||||
|                     <label | ||||
|                       for="address1" | ||||
|                       class="block text-sm font-medium text-gray-700" | ||||
|                       >{$_("address")}</label | ||||
|                     > | ||||
|                     <input | ||||
|                       autocomplete="off" | ||||
|                       placeholder={$_("address")} | ||||
|                       class:border-red-500={!isAddress1Valid} | ||||
|                       class:focus:border-red-500={!isAddress1Valid} | ||||
|                       class:focus:ring-red-500={!isAddress1Valid} | ||||
|                       bind:value={address_input1_value} | ||||
|                       bind:this={address_input1} | ||||
|                       type="text" | ||||
|                       name="address1" | ||||
|                       class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-neutral-800 rounded-md p-2" | ||||
|                     /> | ||||
|                     {#if !isAddress1Valid} | ||||
|                       <span | ||||
|                         class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1" | ||||
|                       > | ||||
|                         {$_("address-is-required")} | ||||
|                       </span> | ||||
|                     {/if} | ||||
|                   </div> | ||||
|                   <div class="col-span-6"> | ||||
|                     <label | ||||
|                       for="address2" | ||||
|                       class="block text-sm font-medium text-gray-700" | ||||
|                       >{$_("apartment-suite-etc")}</label | ||||
|                     > | ||||
|                     <input | ||||
|                       autocomplete="off" | ||||
|                       placeholder={$_("apartment-suite-etc")} | ||||
|                       bind:value={address_input2_value} | ||||
|                       bind:this={address_input2} | ||||
|                       type="text" | ||||
|                       name="address2" | ||||
|                       class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-neutral-800 rounded-md p-2" | ||||
|                     /> | ||||
|                   </div> | ||||
|                   <div class="col-span-6"> | ||||
|                     <label | ||||
|                       for="zipcode" | ||||
|                       class="block text-sm font-medium text-gray-700" | ||||
|                       >{$_("zip-postal-code")}</label | ||||
|                     > | ||||
|                     <input | ||||
|                       autocomplete="off" | ||||
|                       placeholder={$_("zip-postal-code")} | ||||
|                       class:border-red-500={!iszipcodevalid} | ||||
|                       class:focus:border-red-500={!iszipcodevalid} | ||||
|                       class:focus:ring-red-500={!iszipcodevalid} | ||||
|                       bind:value={address_zipcode_value} | ||||
|                       bind:this={address_zipcode} | ||||
|                       type="text" | ||||
|                       name="zipcode" | ||||
|                       class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-neutral-800 rounded-md p-2" | ||||
|                     /> | ||||
|                     {#if !iszipcodevalid} | ||||
|                       <span | ||||
|                         class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1" | ||||
|                       > | ||||
|                         {$_("valid-zipcode-postal-code-is-required")} | ||||
|                       </span> | ||||
|                     {/if} | ||||
|                   </div> | ||||
|                   <div class="col-span-6"> | ||||
|                     <label | ||||
|                       for="city" | ||||
|                       class="block text-sm font-medium text-gray-700" | ||||
|                       >{$_("city")}</label | ||||
|                     > | ||||
|                     <input | ||||
|                       autocomplete="off" | ||||
|                       placeholder={$_("city")} | ||||
|                       class:border-red-500={!iscityvalid} | ||||
|                       class:focus:border-red-500={!iscityvalid} | ||||
|                       class:focus:ring-red-500={!iscityvalid} | ||||
|                       bind:value={address_city_value} | ||||
|                       bind:this={address_city} | ||||
|                       type="text" | ||||
|                       name="city" | ||||
|                       class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-neutral-800 rounded-md p-2" | ||||
|                     /> | ||||
|                     {#if !iscityvalid} | ||||
|                       <span | ||||
|                         class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1" | ||||
|                       > | ||||
|                         {$_("valid-city-is-required")} | ||||
|                       </span> | ||||
|                     {/if} | ||||
|                   </div> | ||||
|                 {/if} | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div class="bg-gray-50 px-4 lg:py-3 sm:px-6 grid gap-2 lg:rounded-b-xl pt-3 pb-10"> | ||||
|           <button | ||||
|             disabled={!createbtnenabled} | ||||
|             class:opacity-50={!createbtnenabled} | ||||
|             on:click={submit} | ||||
|             type="button" | ||||
|             class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" | ||||
|           > | ||||
|             {$_("create")} | ||||
|           </button> | ||||
|           <button | ||||
|             on:click={() => { | ||||
|               modal_open = false; | ||||
|             }} | ||||
|             type="button" | ||||
|             class="w-full justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 hidden lg:block" | ||||
|           > | ||||
|             {$_("cancel")} | ||||
|           </button> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| {/if} | ||||
							
								
								
									
										399
									
								
								src/components/contacts/ContactDetail.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										399
									
								
								src/components/contacts/ContactDetail.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,399 @@ | ||||
| <script> | ||||
| 	import { _ } from "svelte-i18n"; | ||||
| 	import store from "../../store"; | ||||
| 	import { | ||||
| 		GroupContactService, | ||||
| 		RunnerTeamService, | ||||
| 		RunnerOrganizationService, | ||||
| 	} from "@odit/lfk-client-js"; | ||||
| 	import PromiseError from "../base/PromiseError.svelte"; | ||||
| 	import isEmail from "validator/es/lib/isEmail"; | ||||
| 	import toast from "svelte-french-toast"; | ||||
| 	let data_loaded = false; | ||||
| 	let orgs = []; | ||||
| 	let teams = []; | ||||
| 	export let params; | ||||
| 	$: delete_triggered = false; | ||||
| 	$: original_data = {}; | ||||
| 	$: editable = {}; | ||||
| 	$: changes_performed = !( | ||||
| 		JSON.stringify(original_data) === JSON.stringify(editable) | ||||
| 	); | ||||
| 	$: isEmailValid = | ||||
| 		(editable.email || "") === "" || | ||||
| 		(editable.email && isEmail(editable.email || "")); | ||||
| 	$: isFirstnameValid = editable.firstname !== ""; | ||||
| 	$: isLastnameValid = editable.lastname !== ""; | ||||
| 	$: save_enabled = | ||||
| 		changes_performed && | ||||
| 		isFirstnameValid && | ||||
| 		isLastnameValid && | ||||
| 		isEmailValid && | ||||
| 		isPhoneValidOrEmpty && | ||||
| 		((isAddress1Valid && iszipcodevalid && iscityvalid) || | ||||
| 			editable.address_checked === false); | ||||
| 	const promise = GroupContactService.groupContactControllerGetOne( | ||||
| 		params.contact | ||||
| 	).then((data) => { | ||||
| 		data_loaded = true; | ||||
| 		original_data = Object.assign(original_data, data); | ||||
| 		editable = Object.assign(editable, original_data); | ||||
| 		editable.groups = editable.groups.map((g) => g.id); | ||||
| 		original_data.groups = original_data.groups.map((g) => g.id); | ||||
| 		editable.address_checked = editable.address.address1 !== null; | ||||
| 		original_data.address_checked = editable.address.address1 !== null; | ||||
| 		if (editable.address_checked === false) { | ||||
| 			editable.address = { | ||||
| 				address1: "", | ||||
| 				address2: "", | ||||
| 				city: "", | ||||
| 				postalcode: "", | ||||
| 				country: "", | ||||
| 			}; | ||||
| 		} | ||||
| 	}); | ||||
| 	RunnerOrganizationService.runnerOrganizationControllerGetAll().then((val) => { | ||||
| 		orgs = val; | ||||
| 	}); | ||||
| 	RunnerTeamService.runnerTeamControllerGetAll().then((val) => { | ||||
| 		teams = val; | ||||
| 	}); | ||||
| 	$: isPhoneValidOrEmpty = | ||||
| 		editable.phone?.includes("+") || | ||||
| 		editable.phone === "" || | ||||
| 		editable.phone === null; | ||||
| 	$: isAddress1Valid = editable.address?.address1?.trim().length !== 0; | ||||
| 	$: iszipcodevalid = editable.address?.postalcode?.trim().length !== 0; | ||||
| 	$: iscityvalid = editable.address?.city?.trim().length !== 0; | ||||
| 	function submit() { | ||||
| 		if (data_loaded === true && save_enabled) { | ||||
| 			toast.loading($_("contact-is-being-updated")); | ||||
| 			editable.address.country = "DE"; | ||||
| 			if (editable.address_checked === false) { | ||||
| 				editable.address = null; | ||||
| 			} | ||||
| 			if (editable.email) editable.email = editable.email; | ||||
| 			if (editable.phone) editable.phone = editable.phone; | ||||
| 			if (editable.middlename) editable.middlename = editable.middlename; | ||||
| 			GroupContactService.groupContactControllerPut(original_data.id, editable) | ||||
| 				.then((resp) => { | ||||
| 					Object.assign(original_data, editable); | ||||
| 					original_data = original_data; | ||||
| 					toast.dismiss(); | ||||
| 					toast.success($_("updated-contact")); | ||||
| 				}) | ||||
| 				.catch((err) => {}); | ||||
| 		} else { | ||||
| 		} | ||||
| 	} | ||||
| 	function deleteContact() { | ||||
| 		GroupContactService.groupContactControllerRemove(original_data.id, true) | ||||
| 			.then((resp) => { | ||||
| 				location.replace("./"); | ||||
| 			}) | ||||
| 			.catch((err) => {}); | ||||
| 	} | ||||
| </script> | ||||
|  | ||||
| {#await promise} | ||||
| 	{$_("loading-contact-details")} | ||||
| {:then} | ||||
| 	<section class="container p-5 select-none"> | ||||
| 		<div class="flex flex-row mb-4"> | ||||
| 			<div class="w-full"> | ||||
| 				<nav class="w-full flex"> | ||||
| 					<ol class="list-none flex flex-row items-center justify-start"> | ||||
| 						<li class="flex items-center"> | ||||
| 							<a class="mr-2" href="./" | ||||
| 								><svg | ||||
| 									xmlns="http://www.w3.org/2000/svg" | ||||
| 									width="24" | ||||
| 									height="24" | ||||
| 									viewBox="0 0 24 24" | ||||
| 									fill="none" | ||||
| 									stroke="currentColor" | ||||
| 									stroke-width="2" | ||||
| 									stroke-linecap="round" | ||||
| 									stroke-linejoin="round" | ||||
| 									class="inline-block" | ||||
| 									><path d="m12 19-7-7 7-7" /><path d="M19 12H5" /></svg | ||||
| 								> | ||||
| 								{$_("contacts")}</a | ||||
| 							> | ||||
| 						</li> | ||||
| 					</ol> | ||||
| 				</nav> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 		<div class="mb-4 text-3xl font-extrabold leading-tight"> | ||||
| 			{original_data.firstname} | ||||
| 			{original_data.middlename || ""} | ||||
| 			{original_data.lastname} | ||||
| 			<div data-id="contact_actions_${editable.id}"> | ||||
| 				{#if store.state.jwtinfo.userdetails.permissions.includes("CONTACT:DELETE")} | ||||
| 					{#if delete_triggered} | ||||
| 						<button | ||||
| 							on:click={deleteContact} | ||||
| 							class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:w-auto sm:text-sm" | ||||
| 							>{$_("confirm-deletion")}</button | ||||
| 						> | ||||
| 						<button | ||||
| 							on:click={() => { | ||||
| 								delete_triggered = !delete_triggered; | ||||
| 							}} | ||||
| 							class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-400 text-base font-medium text-white sm:w-auto sm:text-sm" | ||||
| 							>{$_("cancel")}</button | ||||
| 						> | ||||
| 					{/if} | ||||
| 					{#if !delete_triggered} | ||||
| 						<button | ||||
| 							on:click={() => { | ||||
| 								delete_triggered = true; | ||||
| 							}} | ||||
| 							type="button" | ||||
| 							class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:w-auto sm:text-sm" | ||||
| 							>{$_("delete-contact")}</button | ||||
| 						> | ||||
| 					{/if} | ||||
| 				{/if} | ||||
| 				{#if !delete_triggered} | ||||
| 					<button | ||||
| 						disabled={!save_enabled} | ||||
| 						class:opacity-50={!save_enabled} | ||||
| 						type="button" | ||||
| 						on:click={submit} | ||||
| 						class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:w-auto sm:text-sm mb-1 lg:mb-0" | ||||
| 						>{$_("save-changes")}</button | ||||
| 					> | ||||
| 				{/if} | ||||
| 			</div> | ||||
| 		</div> | ||||
| 		<!--  --> | ||||
| 		<div class="text-sm w-full mt-2"> | ||||
| 			<label for="firstname" class="font-semibold text-gray-700" | ||||
| 				>{$_("first-name")}</label | ||||
| 			> | ||||
| 			<input | ||||
| 				autocomplete="off" | ||||
| 				placeholder={$_("first-name")} | ||||
| 				type="text" | ||||
| 				class:border-red-500={!isFirstnameValid} | ||||
| 				class:focus:border-red-500={!isFirstnameValid} | ||||
| 				class:focus:ring-red-500={!isFirstnameValid} | ||||
| 				bind:value={editable.firstname} | ||||
| 				name="firstname" | ||||
| 				class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-neutral-800 rounded-md p-2" | ||||
| 			/> | ||||
| 			{#if !isFirstnameValid} | ||||
| 				<span | ||||
| 					class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1" | ||||
| 				> | ||||
| 					{$_("first-name-is-required")} | ||||
| 				</span> | ||||
| 			{/if} | ||||
| 		</div> | ||||
| 		<div class="text-sm w-full mt-2"> | ||||
| 			<label for="middlename" class="font-semibold text-gray-700" | ||||
| 				>{$_("middle-name")}</label | ||||
| 			> | ||||
| 			<input | ||||
| 				autocomplete="off" | ||||
| 				placeholder={$_("middle-name")} | ||||
| 				type="text" | ||||
| 				bind:value={editable.middlename} | ||||
| 				name="middlename" | ||||
| 				class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-neutral-800 rounded-md p-2" | ||||
| 			/> | ||||
| 		</div> | ||||
| 		<div class="text-sm w-full mt-2"> | ||||
| 			<label for="lastname" class="font-semibold text-gray-700" | ||||
| 				>{$_("last-name")}</label | ||||
| 			> | ||||
| 			<input | ||||
| 				autocomplete="off" | ||||
| 				placeholder={$_("last-name")} | ||||
| 				type="text" | ||||
| 				bind:value={editable.lastname} | ||||
| 				class:border-red-500={!isLastnameValid} | ||||
| 				class:focus:border-red-500={!isLastnameValid} | ||||
| 				class:focus:ring-red-500={!isLastnameValid} | ||||
| 				name="lastname" | ||||
| 				class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-neutral-800 rounded-md p-2" | ||||
| 			/> | ||||
| 			{#if !isLastnameValid} | ||||
| 				<span | ||||
| 					class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1" | ||||
| 				> | ||||
| 					{$_("last-name-is-required")} | ||||
| 				</span> | ||||
| 			{/if} | ||||
| 		</div> | ||||
| 		<div class="text-sm w-full mt-2"> | ||||
| 			<label for="email" class="font-semibold text-gray-700" | ||||
| 				>{$_("e-mail-adress")}</label | ||||
| 			> | ||||
| 			<input | ||||
| 				autocomplete="off" | ||||
| 				placeholder={$_("e-mail-adress")} | ||||
| 				type="email" | ||||
| 				bind:value={editable.email} | ||||
| 				class:border-red-500={!isEmailValid} | ||||
| 				class:focus:border-red-500={!isEmailValid} | ||||
| 				class:focus:ring-red-500={!isEmailValid} | ||||
| 				name="email" | ||||
| 				class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-neutral-800 rounded-md p-2" | ||||
| 			/> | ||||
| 			{#if !isEmailValid} | ||||
| 				<span | ||||
| 					class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1" | ||||
| 				> | ||||
| 					{$_("valid-email-is-required")} | ||||
| 				</span> | ||||
| 			{/if} | ||||
| 		</div> | ||||
| 		<div class="text-sm w-full mt-2"> | ||||
| 			<label for="phone" class="font-semibold text-gray-700">{$_("phone")}</label> | ||||
| 			<input | ||||
| 				autocomplete="off" | ||||
| 				placeholder={$_("phone")} | ||||
| 				type="tel" | ||||
| 				class:border-red-500={!isPhoneValidOrEmpty} | ||||
| 				class:focus:border-red-500={!isPhoneValidOrEmpty} | ||||
| 				class:focus:ring-red-500={!isPhoneValidOrEmpty} | ||||
| 				bind:value={editable.phone} | ||||
| 				name="phone" | ||||
| 				class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-neutral-800 rounded-md p-2" | ||||
| 			/> | ||||
| 			{#if !isPhoneValidOrEmpty} | ||||
| 				<span | ||||
| 					class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1" | ||||
| 				> | ||||
| 					{$_("valid-international-phone-number-is-required")} | ||||
| 				</span> | ||||
| 			{/if} | ||||
| 		</div> | ||||
| 		<div class="text-sm w-full mt-2"> | ||||
| 			<span class="font-semibold text-gray-700">{$_("groups")}</span> | ||||
| 			<select | ||||
| 				bind:value={editable.groups} | ||||
| 				name="team" | ||||
| 				multiple | ||||
| 				class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-neutral-800 rounded-md p-2" | ||||
| 			> | ||||
| 				{#each teams as team} | ||||
| 					<option value={team.id}> | ||||
| 						{team.parentGroup.name} | ||||
| 						> | ||||
| 						{team.name} | ||||
| 					</option> | ||||
| 				{/each} | ||||
| 				{#each orgs as org} | ||||
| 					<option value={org.id}>{org.name}</option> | ||||
| 				{/each} | ||||
| 			</select> | ||||
| 		</div> | ||||
| 		<!--  --> | ||||
| 		<div class="flex items-start mt-2"> | ||||
| 			<div class="flex items-center h-5"> | ||||
| 				<input | ||||
| 					bind:checked={editable.address_checked} | ||||
| 					id="comments" | ||||
| 					name="comments" | ||||
| 					type="checkbox" | ||||
| 					class="focus:ring-indigo-500 size-4 text-indigo-600 border-gray-300 rounded" | ||||
| 				/> | ||||
| 			</div> | ||||
| 			<div class="ml-3 text-sm"> | ||||
| 				<label for="comments" class="font-semibold text-gray-700" | ||||
| 					>{$_("address")}</label | ||||
| 				> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 		{#if editable.address_checked === true} | ||||
| 			<div class="col-span-6"> | ||||
| 				<label for="address1" class="block text-sm font-medium text-gray-700" | ||||
| 					>{$_("address")}</label | ||||
| 				> | ||||
| 				<input | ||||
| 					autocomplete="off" | ||||
| 					placeholder="Address" | ||||
| 					class:border-red-500={!isAddress1Valid} | ||||
| 					class:focus:border-red-500={!isAddress1Valid} | ||||
| 					class:focus:ring-red-500={!isAddress1Valid} | ||||
| 					bind:value={editable.address.address1} | ||||
| 					type="text" | ||||
| 					name="address1" | ||||
| 					class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-neutral-800 rounded-md p-2" | ||||
| 				/> | ||||
| 				{#if !isAddress1Valid} | ||||
| 					<span | ||||
| 						class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1" | ||||
| 					> | ||||
| 						{$_("address-is-required")} | ||||
| 					</span> | ||||
| 				{/if} | ||||
| 			</div> | ||||
| 			<div class="col-span-6"> | ||||
| 				<label for="address2" class="block text-sm font-medium text-gray-700" | ||||
| 					>{$_("apartment-suite-etc")}</label | ||||
| 				> | ||||
| 				<input | ||||
| 					autocomplete="off" | ||||
| 					placeholder={$_("apartment-suite-etc")} | ||||
| 					bind:value={editable.address.address2} | ||||
| 					type="text" | ||||
| 					name="address2" | ||||
| 					class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-neutral-800 rounded-md p-2" | ||||
| 				/> | ||||
| 			</div> | ||||
| 			<div class="col-span-6"> | ||||
| 				<label for="zipcode" class="block text-sm font-medium text-gray-700" | ||||
| 					>{$_("zip-postal-code")}</label | ||||
| 				> | ||||
| 				<input | ||||
| 					autocomplete="off" | ||||
| 					placeholder={$_("zip-postal-code")} | ||||
| 					class:border-red-500={!iszipcodevalid} | ||||
| 					class:focus:border-red-500={!iszipcodevalid} | ||||
| 					class:focus:ring-red-500={!iszipcodevalid} | ||||
| 					bind:value={editable.address.postalcode} | ||||
| 					type="text" | ||||
| 					name="zipcode" | ||||
| 					class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-neutral-800 rounded-md p-2" | ||||
| 				/> | ||||
| 				{#if !iszipcodevalid} | ||||
| 					<span | ||||
| 						class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1" | ||||
| 					> | ||||
| 						{$_("valid-zipcode-postal-code-is-required")} | ||||
| 					</span> | ||||
| 				{/if} | ||||
| 			</div> | ||||
| 			<div class="col-span-6"> | ||||
| 				<label for="city" class="block text-sm font-medium text-gray-700" | ||||
| 					>{$_("city")}</label | ||||
| 				> | ||||
| 				<input | ||||
| 					autocomplete="off" | ||||
| 					placeholder={$_("city")} | ||||
| 					class:border-red-500={!iscityvalid} | ||||
| 					class:focus:border-red-500={!iscityvalid} | ||||
| 					class:focus:ring-red-500={!iscityvalid} | ||||
| 					bind:value={editable.address.city} | ||||
| 					type="text" | ||||
| 					name="city" | ||||
| 					class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-neutral-800 rounded-md p-2" | ||||
| 				/> | ||||
| 				{#if !iscityvalid} | ||||
| 					<span | ||||
| 						class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1" | ||||
| 					> | ||||
| 						{$_("valid-city-is-required")} | ||||
| 					</span> | ||||
| 				{/if} | ||||
| 			</div> | ||||
| 		{/if} | ||||
| 	</section> | ||||
| {:catch error} | ||||
| 	<PromiseError {error} /> | ||||
| {/await} | ||||
							
								
								
									
										30
									
								
								src/components/contacts/Contacts.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/components/contacts/Contacts.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import store from "../../store"; | ||||
|   import AddContactModal from "./AddContactModal.svelte"; | ||||
|   import ContactsOverview from "./ContactsOverview.svelte"; | ||||
|   export let modal_open = false; | ||||
|   let current_contacts = []; | ||||
| </script> | ||||
|  | ||||
| <section class="container p-5"> | ||||
|   <h4 class="mb-1 text-3xl font-extrabold leading-tight"> | ||||
|     {$_("contacts")} | ||||
|   </h4> | ||||
|   {#if store.state.jwtinfo.userdetails.permissions.includes("CONTACT:CREATE")} | ||||
|     <button | ||||
|       on:click={() => { | ||||
|         modal_open = true; | ||||
|       }} | ||||
|       type="button" | ||||
|       class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:w-auto sm:text-sm" | ||||
|     > | ||||
|       {$_("create-a-new-contact")} | ||||
|     </button> | ||||
|   {/if} | ||||
|   <ContactsOverview bind:current_contacts /> | ||||
| </section> | ||||
|  | ||||
| {#if store.state.jwtinfo.userdetails.permissions.includes("CONTACT:CREATE")} | ||||
|   <AddContactModal bind:current_contacts bind:modal_open /> | ||||
| {/if} | ||||
							
								
								
									
										17
									
								
								src/components/contacts/ContactsEmptyState.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/components/contacts/ContactsEmptyState.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import AddContactModal from "./AddContactModal.svelte"; | ||||
|   import team_empty from "../teams/team_empty.svg"; | ||||
|   let modal_open = false; | ||||
|   let current_contacts = []; | ||||
| </script> | ||||
|  | ||||
| <div class="text-center items-center justify-center"> | ||||
|   <p class="mb-16 text-lg text-gray-500"> | ||||
|     <img class="w-full h-44" src={team_empty} alt="" /> | ||||
|     <span class="font-bold">{$_("there-are-no-contacts-added-yet")}</span><br /> | ||||
|     <span>{$_("add-your-first-contact")}</span> | ||||
|   </p> | ||||
| </div> | ||||
|  | ||||
| <AddContactModal bind:modal_open bind:current_contacts /> | ||||
							
								
								
									
										198
									
								
								src/components/contacts/ContactsOverview.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										198
									
								
								src/components/contacts/ContactsOverview.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,198 @@ | ||||
| <script> | ||||
| 	import { _ } from "svelte-i18n"; | ||||
| 	import { GroupContactService } from "@odit/lfk-client-js"; | ||||
| 	const promise = GroupContactService.groupContactControllerGetAll().then( | ||||
| 		(result) => { | ||||
| 			current_contacts = result; | ||||
| 		} | ||||
| 	); | ||||
| 	import store from "../../store"; | ||||
| 	import ContactsEmptyState from "./ContactsEmptyState.svelte"; | ||||
| 	import toast from "svelte-french-toast"; | ||||
| 	$: searchvalue = ""; | ||||
| 	$: active_deletes = []; | ||||
| 	export let current_contacts = []; | ||||
| </script> | ||||
|  | ||||
| {#if store.state.jwtinfo.userdetails.permissions.includes("TEAM:GET")} | ||||
| 	{#await promise} | ||||
| 		<div | ||||
| 			class="bg-teal-lightest border-t-4 border-teal rounded-b text-teal-darkest px-4 py-3 shadow-md my-2" | ||||
| 			role="alert" | ||||
| 		> | ||||
| 			<p class="font-bold">{$_("contacts-are-being-loaded")}</p> | ||||
| 			<p class="text-sm">{$_("this-might-take-a-moment")}</p> | ||||
| 		</div> | ||||
| 	{:then} | ||||
| 		{#if current_contacts.length === 0} | ||||
| 			<ContactsEmptyState /> | ||||
| 		{:else} | ||||
| 			<input | ||||
| 				type="search" | ||||
| 				bind:value={searchvalue} | ||||
| 				placeholder={$_("datatable.search")} | ||||
| 				aria-label={$_("datatable.search")} | ||||
| 				class="mb-2 w-full sm:w-auto mt-1 sm:mt-0 p-2 rounded-md border" | ||||
| 			/> | ||||
| 			<div | ||||
| 				class="shadow border-b border-gray-200 sm:rounded-lg overflow-x-scroll" | ||||
| 			> | ||||
| 				<table class="divide-y divide-gray-200 w-full"> | ||||
| 					<thead class="bg-gray-50"> | ||||
| 						<tr class="odd:bg-white even:bg-gray-100"> | ||||
| 							<th | ||||
| 								scope="col" | ||||
| 								class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" | ||||
| 							> | ||||
| 								{$_("name")} | ||||
| 							</th> | ||||
| 							<th | ||||
| 								scope="col" | ||||
| 								class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" | ||||
| 							> | ||||
| 								{$_("groups")} | ||||
| 							</th> | ||||
| 							<th | ||||
| 								scope="col" | ||||
| 								class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" | ||||
| 							> | ||||
| 								{$_("address")} | ||||
| 							</th> | ||||
| 							<th scope="col" class="relative px-6 py-3"> | ||||
| 								<span class="sr-only">{$_("action")}</span> | ||||
| 							</th> | ||||
| 						</tr> | ||||
| 					</thead> | ||||
| 					<tbody class="divide-y divide-gray-200"> | ||||
| 						{#each current_contacts as t} | ||||
| 							{#if Object.values(t) | ||||
| 								.toString() | ||||
| 								.toLowerCase() | ||||
| 								.includes(searchvalue)} | ||||
| 								<tr | ||||
| 									class="odd:bg-white even:bg-gray-100" | ||||
| 									data-rowid="team_{t.id}" | ||||
| 								> | ||||
| 									<td class="px-6 py-4 whitespace-nowrap"> | ||||
| 										<div class="flex items-center"> | ||||
| 											<div class="ml-4"> | ||||
| 												<div class="text-sm font-medium text-gray-900"> | ||||
| 													{t.firstname} | ||||
| 													{t.middlename || ""} | ||||
| 													{t.lastname} | ||||
| 												</div> | ||||
| 											</div> | ||||
| 										</div> | ||||
| 									</td> | ||||
| 									<td class="px-6 py-4 whitespace-nowrap"> | ||||
| 										<div class="flex items-center"> | ||||
| 											<div | ||||
| 												class="text-sm font-medium text-gray-900 gap-0.5 flex flex-wrap" | ||||
| 											> | ||||
| 												{#if t.groups.length > 0} | ||||
| 													{#each t.groups as g} | ||||
| 														{#if g.responseType === "RUNNERORGANIZATION"} | ||||
| 															<a | ||||
| 																href="../orgs/{g.id}" | ||||
| 																class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800 border border-current" | ||||
| 																>{g.name}</a | ||||
| 															> | ||||
| 														{:else} | ||||
| 															<a | ||||
| 																href="../teams/{g.id}" | ||||
| 																class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800 border border-current" | ||||
| 																>{g.parentGroup.name} | ||||
| 																> | ||||
| 																{g.name}</a | ||||
| 															> | ||||
| 														{/if} | ||||
| 													{/each} | ||||
| 												{:else} | ||||
| 													{$_("contact-is-not-a-member-in-any-group")} | ||||
| 												{/if} | ||||
| 											</div> | ||||
| 										</div> | ||||
| 									</td> | ||||
| 									<td class="px-6 py-4 whitespace-nowrap"> | ||||
| 										<div class="flex items-center"> | ||||
| 											<div class="ml-4"> | ||||
| 												<div class="text-sm font-medium text-gray-900"> | ||||
| 													{#if t.address.address1 !== null} | ||||
| 														{t.address.address1}<br /> | ||||
| 														{t.address.address2 || ""}<br /> | ||||
| 														{t.address.postalcode} | ||||
| 														{t.address.city} | ||||
| 														{t.address.country} | ||||
| 													{/if} | ||||
| 												</div> | ||||
| 											</div> | ||||
| 										</div> | ||||
| 									</td> | ||||
| 									{#if active_deletes[t.id] === true} | ||||
| 										<td | ||||
| 											class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium" | ||||
| 										> | ||||
| 											<button | ||||
| 												on:click={() => { | ||||
| 													active_deletes[t.id] = false; | ||||
| 												}} | ||||
| 												tabindex="0" | ||||
| 												class="ml-4 text-indigo-600 hover:text-indigo-900 cursor-pointer" | ||||
| 												>{$_("cancel-delete")}</button | ||||
| 											> | ||||
| 											<button | ||||
| 												on:click={() => { | ||||
| 													toast.loading($_("deleting-contact")); | ||||
| 													GroupContactService.groupContactControllerRemove( | ||||
| 														t.id, | ||||
| 														false | ||||
| 													).then((resp) => { | ||||
| 														current_contacts = current_contacts.filter( | ||||
| 															(obj) => obj.id !== t.id | ||||
| 														); | ||||
| 														toast.dismiss(); | ||||
| 														toast.success($_("contact-deleted")); | ||||
| 													}); | ||||
| 												}} | ||||
| 												tabindex="0" | ||||
| 												class="ml-4 text-red-600 hover:text-red-900 cursor-pointer" | ||||
| 												>{$_("confirm-delete")}</button | ||||
| 											> | ||||
| 										</td> | ||||
| 									{:else} | ||||
| 										<td | ||||
| 											class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium" | ||||
| 										> | ||||
| 											<a | ||||
| 												href="./{t.id}" | ||||
| 												class="text-indigo-600 hover:text-indigo-900" | ||||
| 												>{$_("details")}</a | ||||
| 											> | ||||
| 											{#if store.state.jwtinfo.userdetails.permissions.includes("TEAM:DELETE")} | ||||
| 												<button | ||||
| 													on:click={() => { | ||||
| 														active_deletes[t.id] = true; | ||||
| 													}} | ||||
| 													tabindex="0" | ||||
| 													class="ml-4 text-red-600 hover:text-red-900 cursor-pointer" | ||||
| 													>{$_("delete")}</button | ||||
| 												> | ||||
| 											{/if} | ||||
| 										</td> | ||||
| 									{/if} | ||||
| 								</tr> | ||||
| 							{/if} | ||||
| 						{/each} | ||||
| 					</tbody> | ||||
| 				</table> | ||||
| 			</div> | ||||
| 		{/if} | ||||
| 	{:catch error} | ||||
| 		<div class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-500"> | ||||
| 			<span class="inline-block align-middle mr-8"> | ||||
| 				<b>{$_("general_promise_error")}</b> | ||||
| 				{error} | ||||
| 			</span> | ||||
| 		</div> | ||||
| 	{/await} | ||||
| {/if} | ||||
							
								
								
									
										534
									
								
								src/components/dashboard/Dashboard.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										534
									
								
								src/components/dashboard/Dashboard.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,534 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import localForage from "localforage"; | ||||
|   import store from "../../store"; | ||||
|   import { router } from "tinro"; | ||||
|   import NoComponentLoaded from "../base/NoComponentLoaded.svelte"; | ||||
|   import { AuthService } from "@odit/lfk-client-js"; | ||||
|   import { Toaster } from "svelte-french-toast"; | ||||
|   $: navOpen = false; | ||||
|   function logout() { | ||||
|     localForage.clear(); | ||||
|     location.replace("/"); | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| <section class="min-h-screen bg-gray-50"> | ||||
|   <div | ||||
|     class:collapsed_navigation={!navOpen} | ||||
|     style="z-index:11;" | ||||
|     class="select-none fixed top-0 left-0 h-full pb-10 overflow-x-hidden overflow-y-auto transition origin-left transform border-r w-60 bg-gray-50" | ||||
|   > | ||||
|     <a href="/" class="flex items-center px-4 py-5"> | ||||
|       <img src="/lfk-logo.png" alt="Logo" class="h-10" /> | ||||
|       <h3 class="text-lg font-bold">LfK!Admin</h3> | ||||
|     </a> | ||||
|     <nav class="text-sm font-medium text-gray-600" aria-label="Main Navigation"> | ||||
|       <a | ||||
|         class:activenav={$router.path === "/"} | ||||
|         class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold" | ||||
|         href="/" | ||||
|       > | ||||
|         <svg | ||||
|           class="flex-shrink-0 w-5 h-5 mr-2 transition group-hover:text-gray-600" | ||||
|           xmlns="http://www.w3.org/2000/svg" | ||||
|           viewBox="0 0 20 20" | ||||
|           fill="currentColor" | ||||
|         > | ||||
|           <path | ||||
|             d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z" | ||||
|           /> | ||||
|         </svg> | ||||
|         <span>{$_("dashboard-title")}</span> | ||||
|       </a> | ||||
|       <h2 class="px-4 py-2 text-xs font-semibold text-gray-600 uppercase"> | ||||
|         {$_("quick-tools")} | ||||
|       </h2> | ||||
|       {#if store.state.jwtinfo.userdetails.permissions.includes("RUNNER:GET") && store.state.jwtinfo.userdetails.permissions.includes("CARD:GET")} | ||||
|         <a | ||||
|           class:activenav={$router.path.includes("/tools/cardassignment/")} | ||||
|           class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold" | ||||
|           href="/tools/cardassignment/" | ||||
|         > | ||||
|           <svg | ||||
|             xmlns="http://www.w3.org/2000/svg" | ||||
|             viewBox="0 0 24 24" | ||||
|             fill="currentColor" | ||||
|             class="flex-shrink-0 w-5 h-5 mr-2 transition group-hover:text-gray-600" | ||||
|           > | ||||
|             <path | ||||
|               fill-rule="evenodd" | ||||
|               d="M14.615 1.595a.75.75 0 0 1 .359.852L12.982 9.75h7.268a.75.75 0 0 1 .548 1.262l-10.5 11.25a.75.75 0 0 1-1.272-.71l1.992-7.302H3.75a.75.75 0 0 1-.548-1.262l10.5-11.25a.75.75 0 0 1 .913-.143Z" | ||||
|               clip-rule="evenodd" | ||||
|             /> | ||||
|           </svg> | ||||
|  | ||||
|           <span>{$_("card_assignment_menu")}</span> | ||||
|         </a> | ||||
|         <a | ||||
|           class:activenav={$router.path.includes("/tools/cardreplacement/")} | ||||
|           class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold" | ||||
|           href="/tools/cardreplacement/" | ||||
|         > | ||||
|           <svg | ||||
|             xmlns="http://www.w3.org/2000/svg" | ||||
|             viewBox="0 0 24 24" | ||||
|             fill="currentColor" | ||||
|             class="flex-shrink-0 w-5 h-5 mr-2 transition group-hover:text-gray-600" | ||||
|           > | ||||
|             <path | ||||
|               fill-rule="evenodd" | ||||
|               d="M14.615 1.595a.75.75 0 0 1 .359.852L12.982 9.75h7.268a.75.75 0 0 1 .548 1.262l-10.5 11.25a.75.75 0 0 1-1.272-.71l1.992-7.302H3.75a.75.75 0 0 1-.548-1.262l10.5-11.25a.75.75 0 0 1 .913-.143Z" | ||||
|               clip-rule="evenodd" | ||||
|             /> | ||||
|           </svg> | ||||
|  | ||||
|           <span>{$_("card-replacement-menu")}</span> | ||||
|         </a> | ||||
|         {/if} | ||||
|       {#if store.state.jwtinfo.userdetails.permissions.includes("SCAN:CREATE")} | ||||
|         <a | ||||
|           class:activenav={$router.path.includes("/tools/scanclient/")} | ||||
|           class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold" | ||||
|           href="/tools/scanclient/" | ||||
|         > | ||||
|           <svg | ||||
|             xmlns="http://www.w3.org/2000/svg" | ||||
|             viewBox="0 0 24 24" | ||||
|             fill="currentColor" | ||||
|             class="flex-shrink-0 w-5 h-5 mr-2 transition group-hover:text-gray-600" | ||||
|           > | ||||
|             <path | ||||
|               fill-rule="evenodd" | ||||
|               d="M14.615 1.595a.75.75 0 0 1 .359.852L12.982 9.75h7.268a.75.75 0 0 1 .548 1.262l-10.5 11.25a.75.75 0 0 1-1.272-.71l1.992-7.302H3.75a.75.75 0 0 1-.548-1.262l10.5-11.25a.75.75 0 0 1 .913-.143Z" | ||||
|               clip-rule="evenodd" | ||||
|             /> | ||||
|           </svg> | ||||
|  | ||||
|           <span>{$_("scanclient")}</span> | ||||
|         </a> | ||||
|         {/if} | ||||
|         {#if store.state.jwtinfo.userdetails.permissions.includes("DONATION:CREATE")} | ||||
|         <a | ||||
|           class:activenav={$router.path.includes("/tools/donationcreate/")} | ||||
|           class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold" | ||||
|           href="/tools/donationcreate/" | ||||
|         > | ||||
|           <svg | ||||
|             xmlns="http://www.w3.org/2000/svg" | ||||
|             viewBox="0 0 24 24" | ||||
|             fill="currentColor" | ||||
|             class="flex-shrink-0 w-5 h-5 mr-2 transition group-hover:text-gray-600" | ||||
|           > | ||||
|             <path | ||||
|               fill-rule="evenodd" | ||||
|               d="M14.615 1.595a.75.75 0 0 1 .359.852L12.982 9.75h7.268a.75.75 0 0 1 .548 1.262l-10.5 11.25a.75.75 0 0 1-1.272-.71l1.992-7.302H3.75a.75.75 0 0 1-.548-1.262l10.5-11.25a.75.75 0 0 1 .913-.143Z" | ||||
|               clip-rule="evenodd" | ||||
|             /> | ||||
|           </svg> | ||||
|  | ||||
|           <span>{$_("donation-quick-add")}</span> | ||||
|         </a> | ||||
|         {/if} | ||||
|         <h2 class="px-4 py-2 text-xs font-semibold text-gray-600 uppercase"> | ||||
|           {$_("management")} | ||||
|         </h2> | ||||
|         {#if store.state.jwtinfo.userdetails.permissions.includes("RUNNER:GET")} | ||||
|         <a | ||||
|           class:activenav={$router.path.includes("/runners/")} | ||||
|           class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold" | ||||
|           href="/runners/" | ||||
|         > | ||||
|           <svg | ||||
|             xmlns="http://www.w3.org/2000/svg" | ||||
|             viewBox="0 0 24 24" | ||||
|             class="flex-shrink-0 w-5 h-5 mr-2 transition group-hover:text-gray-600" | ||||
|             fill="currentColor" | ||||
|             width="24" | ||||
|             height="24" | ||||
|             ><path fill="none" d="M0 0h24v24H0z" /> | ||||
|             <path | ||||
|               d="M9.83 8.79L8 9.456V13H6V8.05h.015l5.268-1.918c.244-.093.51-.14.782-.131a2.616 2.616 0 0 1 2.427 1.82c.186.583.356.977.51 1.182A4.992 4.992 0 0 0 19 11v2a6.986 6.986 0 0 1-5.402-2.547l-.581 3.297L15 15.67V23h-2v-5.986l-2.05-1.987-.947 4.298-6.894-1.215.348-1.97 4.924.868L9.83 8.79zM13.5 5.5a2 2 0 1 1 0-4 2 2 0 0 1 0 4z" | ||||
|             /></svg | ||||
|           > | ||||
|           <span>{$_("runners")}</span> | ||||
|         </a> | ||||
|       {/if} | ||||
|       {#if store.state.jwtinfo.userdetails.permissions.includes("TEAM:GET")} | ||||
|         <a | ||||
|           class:activenav={$router.path.includes("/teams/")} | ||||
|           class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold" | ||||
|           href="/teams/" | ||||
|         > | ||||
|           <svg | ||||
|             class="flex-shrink-0 w-5 h-5 mr-2 transition group-hover:text-gray-600" | ||||
|             fill="currentColor" | ||||
|             width="24" | ||||
|             height="24" | ||||
|             xmlns="http://www.w3.org/2000/svg" | ||||
|             viewBox="0 0 640 512" | ||||
|             ><path | ||||
|               fill="currentColor" | ||||
|               d="M96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm448 0c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm32 32h-64c-17.6 0-33.5 7.1-45.1 18.6 40.3 22.1 68.9 62 75.1 109.4h66c17.7 0 32-14.3 32-32v-32c0-35.3-28.7-64-64-64zm-256 0c61.9 0 112-50.1 112-112S381.9 32 320 32 208 82.1 208 144s50.1 112 112 112zm76.8 32h-8.3c-20.8 10-43.9 16-68.5 16s-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h288c26.5 0 48-21.5 48-48v-28.8c0-63.6-51.6-115.2-115.2-115.2zm-223.7-13.4C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z" | ||||
|             /></svg | ||||
|           > | ||||
|           <span>{$_("teams")}</span> | ||||
|         </a> | ||||
|       {/if} | ||||
|       {#if store.state.jwtinfo.userdetails.permissions.includes("ORGANIZATION:GET")} | ||||
|         <a | ||||
|           class:activenav={$router.path.includes("/orgs/")} | ||||
|           class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold" | ||||
|           href="/orgs/" | ||||
|         > | ||||
|           <svg | ||||
|             class="flex-shrink-0 w-5 h-5 mr-2 transition group-hover:text-gray-600" | ||||
|             fill="currentColor" | ||||
|             xmlns="http://www.w3.org/2000/svg" | ||||
|             viewBox="0 0 24 24" | ||||
|             width="24" | ||||
|             height="24" | ||||
|             ><path fill="none" d="M0 0h24v24H0z" /> | ||||
|             <path | ||||
|               d="M17 19h2v-8h-6v8h2v-6h2v6zM3 19V4a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v5h2v10h1v2H2v-2h1zm4-8v2h2v-2H7zm0 4v2h2v-2H7zm0-8v2h2V7H7z" | ||||
|             /></svg | ||||
|           > | ||||
|           <span>{$_("orgs")}</span> | ||||
|         </a> | ||||
|       {/if} | ||||
|       {#if store.state.jwtinfo.userdetails.permissions.includes("DONOR:GET")} | ||||
|         <a | ||||
|           class:activenav={$router.path.includes("/donors/")} | ||||
|           class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold" | ||||
|           href="/donors/" | ||||
|         > | ||||
|           <svg | ||||
|             class="flex-shrink-0 w-5 h-5 mr-2 transition group-hover:text-gray-600" | ||||
|             fill="currentColor" | ||||
|             xmlns="http://www.w3.org/2000/svg" | ||||
|             viewBox="0 0 24 24" | ||||
|             width="24" | ||||
|             height="24" | ||||
|             ><path fill="none" d="M0 0h24v24H0z" /> | ||||
|             <path | ||||
|               d="M9.33 11.5h2.17A4.5 4.5 0 0 1 16 16H8.999L9 17h8v-1a5.578 5.578 0 0 0-.886-3H19a5 5 0 0 1 4.516 2.851C21.151 18.972 17.322 21 13 21c-2.761 0-5.1-.59-7-1.625L6 10.071A6.967 6.967 0 0 1 9.33 11.5zM5 19a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1v-9a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v9zM18 5a3 3 0 1 1 0 6 3 3 0 0 1 0-6zm-7-3a3 3 0 1 1 0 6 3 3 0 0 1 0-6z" | ||||
|             /></svg | ||||
|           > | ||||
|           <span>{$_("donors")}</span> | ||||
|         </a> | ||||
|       {/if} | ||||
|       {#if store.state.jwtinfo.userdetails.permissions.includes("DONATION:GET")} | ||||
|         <a | ||||
|           class:activenav={$router.path.includes("/donations/")} | ||||
|           class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold" | ||||
|           href="/donations/" | ||||
|         > | ||||
|           <svg | ||||
|             class="flex-shrink-0 w-5 h-5 mr-2 transition group-hover:text-gray-600" | ||||
|             fill="currentColor" | ||||
|             xmlns="http://www.w3.org/2000/svg" | ||||
|             viewBox="0 0 24 24" | ||||
|             width="24" | ||||
|             height="24" | ||||
|             ><path fill="none" d="M0 0h24v24H0z" /> | ||||
|             <path | ||||
|               d="M14 2a8 8 0 013.3 15.3A8 8 0 116.7 6.7 8 8 0 0114 2zm-3 7H9v1a2.5 2.5 0 00-.16 5h2.25a.5.5 0 010 1H7v2h2v1h2v-1a2.5 2.5 0 00.16-5H8.91a.5.5 0 010-1H13v-2h-2V9zm3-5a5.99 5.99 0 00-4.48 2.01 8 8 0 018.47 8.47A6 6 0 0014 4z" | ||||
|             /></svg | ||||
|           > | ||||
|           <span>{$_("donations")}</span> | ||||
|         </a> | ||||
|       {/if} | ||||
|       {#if store.state.jwtinfo.userdetails.permissions.includes("TRACK:GET")} | ||||
|         <a | ||||
|           class:activenav={$router.path === "/tracks/"} | ||||
|           class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold" | ||||
|           href="/tracks/" | ||||
|         > | ||||
|           <svg | ||||
|             class="flex-shrink-0 w-5 h-5 mr-2 transition group-hover:text-gray-600" | ||||
|             fill="currentColor" | ||||
|             width="24" | ||||
|             height="24" | ||||
|             xmlns="http://www.w3.org/2000/svg" | ||||
|             viewBox="0 0 640 512" | ||||
|             ><path | ||||
|               fill="currentColor" | ||||
|               d="M635.7 167.2L556.1 31.7c-8.8-15-28.3-20.1-43.5-11.5l-69 39.1L503.3 161c2.2 3.8.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9L416 75l-55.2 31.3 27.9 47.4c2.2 3.8.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9L333.2 122 278 153.3 337.8 255c2.2 3.7.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9l-59.7-101.7-55.2 31.3 27.9 47.4c2.2 3.8.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9l-27.9-47.5-55.2 31.3 59.7 101.7c2.2 3.7.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9L84.9 262.9l-69 39.1C.7 310.7-4.6 329.8 4.2 344.8l79.6 135.6c8.8 15 28.3 20.1 43.5 11.5L624.1 210c15.2-8.6 20.4-27.8 11.6-42.8z" | ||||
|             /></svg | ||||
|           > | ||||
|           <span>{$_("tracks")}</span> | ||||
|         </a> | ||||
|       {/if} | ||||
|       {#if store.state.jwtinfo.userdetails.permissions.includes("CARD:GET")} | ||||
|         <a | ||||
|           class:activenav={$router.path === "/cards/"} | ||||
|           class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold" | ||||
|           href="/cards/" | ||||
|         > | ||||
|           <svg | ||||
|             class="flex-shrink-0 w-5 h-5 mr-2 transition group-hover:text-gray-600" | ||||
|             fill="currentColor" | ||||
|             width="24" | ||||
|             height="24" | ||||
|             xmlns="http://www.w3.org/2000/svg" | ||||
|             viewBox="0 0 24 24" | ||||
|           > | ||||
|             <path fill="none" d="M0 0h24v24H0z" /> | ||||
|             <path | ||||
|               fill="currentColor" | ||||
|               d="M22 10v10a1 1 0 01-1 1H3a1 1 0 01-1-1V10h20zm0-2H2V4a1 1 0 011-1h18a1 1 0 011 1v4zm-7 8v2h4v-2h-4z" | ||||
|             /></svg | ||||
|           > | ||||
|           <span>{$_("cards")}</span> | ||||
|         </a> | ||||
|       {/if} | ||||
|       {#if store.state.jwtinfo.userdetails.permissions.includes("SCAN:GET")} | ||||
|         <a | ||||
|           class:activenav={$router.path.includes("/scans/")} | ||||
|           class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold" | ||||
|           href="/scans/" | ||||
|         > | ||||
|           <svg | ||||
|             class="flex-shrink-0 w-5 h-5 mr-2 transition group-hover:text-gray-600" | ||||
|             fill="currentColor" | ||||
|             width="24" | ||||
|             height="24" | ||||
|             xmlns="http://www.w3.org/2000/svg" | ||||
|             viewBox="0 0 24 24" | ||||
|             ><path fill="none" d="M0 0h24v24H0z" /> | ||||
|             <path | ||||
|               fill="currentColor" | ||||
|               d="M2 4h2v16H2V4zm4 0h1v16H6V4zm2 0h2v16H8V4zm3 0h2v16h-2V4zm3 0h2v16h-2V4zm3 0h1v16h-1V4zm2 0h3v16h-3V4z" | ||||
|             /></svg | ||||
|           > | ||||
|           <span>Scans</span> | ||||
|         </a> | ||||
|       {/if} | ||||
|       {#if store.state.jwtinfo.userdetails.permissions.includes("CONTACT:GET")} | ||||
|         <a | ||||
|           class:activenav={$router.path.includes("/contacts/")} | ||||
|           class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold" | ||||
|           href="/contacts/" | ||||
|         > | ||||
|           <svg | ||||
|             fill="currentColor" | ||||
|             class="flex-shrink-0 w-5 h-5 mr-2 transition group-hover:text-gray-600" | ||||
|             xmlns="http://www.w3.org/2000/svg" | ||||
|             viewBox="0 0 24 24" | ||||
|             width="24" | ||||
|             height="24" | ||||
|             ><path fill="none" d="M0 0h24v24H0z" /> | ||||
|             <path | ||||
|               d="M2 22a8 8 0 1 1 16 0H2zm8-9c-3.315 0-6-2.685-6-6s2.685-6 6-6 6 2.685 6 6-2.685 6-6 6zm10 4h4v2h-4v-2zm-3-5h7v2h-7v-2zm2-5h5v2h-5V7z" | ||||
|             /></svg | ||||
|           > | ||||
|           <span>{$_("contacts")}</span> | ||||
|         </a> | ||||
|       {/if} | ||||
|       {#if store.state.jwtinfo.userdetails.permissions.includes("STATION:GET")} | ||||
|         <a | ||||
|           class:activenav={$router.path.includes("/scanstations/")} | ||||
|           class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold" | ||||
|           href="/scanstations/" | ||||
|         > | ||||
|           <svg | ||||
|             class="flex-shrink-0 w-5 h-5 mr-2 transition group-hover:text-gray-600" | ||||
|             fill="currentColor" | ||||
|             width="24" | ||||
|             height="24" | ||||
|             viewBox="0 0 24 24" | ||||
|             xmlns="http://www.w3.org/2000/svg" | ||||
|             ><path fill="none" d="M0 0h24v24H0z" /> | ||||
|             <path | ||||
|               fill="currentColor" | ||||
|               d="M4 5v11h16V5H4zM2 4a1 1 0 011-1h18a1 1 0 011 1v14H2V4zM1 19h22v2H1v-2z" | ||||
|             /></svg | ||||
|           > | ||||
|           <span>{$_("scanstations")}</span> | ||||
|         </a> | ||||
|       {/if} | ||||
|       {#if store.state.jwtinfo.userdetails.permissions.includes("STATSCLIENT:GET")} | ||||
|         <a | ||||
|           class:activenav={$router.path.includes("/statsclients/")} | ||||
|           class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold" | ||||
|           href="/statsclients/" | ||||
|         > | ||||
|           <svg | ||||
|             class="flex-shrink-0 w-5 h-5 mr-2 transition group-hover:text-gray-600" | ||||
|             fill="currentColor" | ||||
|             width="24" | ||||
|             height="24" | ||||
|             viewBox="0 0 24 24" | ||||
|             xmlns="http://www.w3.org/2000/svg" | ||||
|             ><path fill="none" d="M0 0h24v24H0z" /> | ||||
|             <path | ||||
|               fill="currentColor" | ||||
|               d="M4 5v11h16V5H4zM2 4a1 1 0 011-1h18a1 1 0 011 1v14H2V4zM1 19h22v2H1v-2z" | ||||
|             /></svg | ||||
|           > | ||||
|           <span>{$_("statsclients")}</span> | ||||
|         </a> | ||||
|       {/if} | ||||
|       {#if store.state.jwtinfo.userdetails.permissions.includes("USER:GET")} | ||||
|         <a | ||||
|           class:activenav={$router.path.includes("/users/")} | ||||
|           class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold" | ||||
|           href="/users/" | ||||
|         > | ||||
|           <svg | ||||
|             xmlns="http://www.w3.org/2000/svg" | ||||
|             width="24" | ||||
|             height="24" | ||||
|             class="flex-shrink-0 w-5 h-5 mr-2 transition group-hover:text-gray-600" | ||||
|             fill="currentColor" | ||||
|             viewBox="0 0 24 24" | ||||
|             ><path fill="none" d="M0 0h24v24H0z" /> | ||||
|             <path | ||||
|               d="M12 14v8H4a8 8 0 018-8zm0-1a6 6 0 110-12 6 6 0 010 12zm2.6 5.81a3.51 3.51 0 010-1.62l-1-.57 1-1.74 1 .58a3.5 3.5 0 011.4-.82V13.5h2v1.15a3.5 3.5 0 011.4.8l1-.57 1 1.74-1 .57a3.51 3.51 0 010 1.62l1 .57-1 1.74-1-.58a3.5 3.5 0 01-1.4.82v1.14h-2v-1.15a3.5 3.5 0 01-1.4-.8l-1 .57-1-1.74 1-.57zM18 17a1 1 0 100 2 1 1 0 000-2z" | ||||
|             /></svg | ||||
|           > | ||||
|           <span>{$_("users")}</span> | ||||
|         </a> | ||||
|       {/if} | ||||
|       {#if store.state.jwtinfo.userdetails.permissions.includes("USERGROUP:GET")} | ||||
|         <a | ||||
|           class:activenav={$router.path.includes("/groups/")} | ||||
|           class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold" | ||||
|           href="/groups/" | ||||
|         > | ||||
|           <svg | ||||
|             class="flex-shrink-0 w-5 h-5 mr-2 transition group-hover:text-gray-600" | ||||
|             fill="currentColor" | ||||
|             width="24" | ||||
|             height="24" | ||||
|             xmlns="http://www.w3.org/2000/svg" | ||||
|             viewBox="0 0 640 512" | ||||
|             ><path | ||||
|               fill="currentColor" | ||||
|               d="M610.5 341.3c2.6-14.1 2.6-28.5 0-42.6l25.8-14.9c3-1.7 4.3-5.2 3.3-8.5-6.7-21.6-18.2-41.2-33.2-57.4-2.3-2.5-6-3.1-9-1.4l-25.8 14.9c-10.9-9.3-23.4-16.5-36.9-21.3v-29.8c0-3.4-2.4-6.4-5.7-7.1-22.3-5-45-4.8-66.2 0-3.3.7-5.7 3.7-5.7 7.1v29.8c-13.5 4.8-26 12-36.9 21.3l-25.8-14.9c-2.9-1.7-6.7-1.1-9 1.4-15 16.2-26.5 35.8-33.2 57.4-1 3.3.4 6.8 3.3 8.5l25.8 14.9c-2.6 14.1-2.6 28.5 0 42.6l-25.8 14.9c-3 1.7-4.3 5.2-3.3 8.5 6.7 21.6 18.2 41.1 33.2 57.4 2.3 2.5 6 3.1 9 1.4l25.8-14.9c10.9 9.3 23.4 16.5 36.9 21.3v29.8c0 3.4 2.4 6.4 5.7 7.1 22.3 5 45 4.8 66.2 0 3.3-.7 5.7-3.7 5.7-7.1v-29.8c13.5-4.8 26-12 36.9-21.3l25.8 14.9c2.9 1.7 6.7 1.1 9-1.4 15-16.2 26.5-35.8 33.2-57.4 1-3.3-.4-6.8-3.3-8.5l-25.8-14.9zM496 368.5c-26.8 0-48.5-21.8-48.5-48.5s21.8-48.5 48.5-48.5 48.5 21.8 48.5 48.5-21.7 48.5-48.5 48.5zM96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm224 32c1.9 0 3.7-.5 5.6-.6 8.3-21.7 20.5-42.1 36.3-59.2 7.4-8 17.9-12.6 28.9-12.6 6.9 0 13.7 1.8 19.6 5.3l7.9 4.6c.8-.5 1.6-.9 2.4-1.4 7-14.6 11.2-30.8 11.2-48 0-61.9-50.1-112-112-112S208 82.1 208 144c0 61.9 50.1 112 112 112zm105.2 194.5c-2.3-1.2-4.6-2.6-6.8-3.9-8.2 4.8-15.3 9.8-27.5 9.8-10.9 0-21.4-4.6-28.9-12.6-18.3-19.8-32.3-43.9-40.2-69.6-10.7-34.5 24.9-49.7 25.8-50.3-.1-2.6-.1-5.2 0-7.8l-7.9-4.6c-3.8-2.2-7-5-9.8-8.1-3.3.2-6.5.6-9.8.6-24.6 0-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h255.4c-3.7-6-6.2-12.8-6.2-20.3v-9.2zM173.1 274.6C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z" | ||||
|             /></svg | ||||
|           > | ||||
|           <span>{$_("user-groups")}</span> | ||||
|         </a> | ||||
|       {/if} | ||||
| 	  <h2 class="px-4 py-2 text-xs font-semibold text-gray-600 uppercase"> | ||||
| 		  {$_("system")} | ||||
|         </h2> | ||||
|       <a | ||||
|         class:activenav={$router.path === "/settings/"} | ||||
|         class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold" | ||||
|         href="/settings/" | ||||
|       > | ||||
|         <svg | ||||
|           class="flex-shrink-0 w-5 h-5 mr-2 transition group-hover:text-gray-600" | ||||
|           xmlns="http://www.w3.org/2000/svg" | ||||
|           viewBox="0 0 20 20" | ||||
|           fill="currentColor" | ||||
|         > | ||||
|           <path | ||||
|             fill-rule="evenodd" | ||||
|             d="M11.49 3.17c-.38-1.56-2.6-1.56-2.98 0a1.532 1.532 0 01-2.286.948c-1.372-.836-2.942.734-2.106 2.106.54.886.061 2.042-.947 2.287-1.561.379-1.561 2.6 0 2.978a1.532 1.532 0 01.947 2.287c-.836 1.372.734 2.942 2.106 2.106a1.532 1.532 0 012.287.947c.379 1.561 2.6 1.561 2.978 0a1.533 1.533 0 012.287-.947c1.372.836 2.942-.734 2.106-2.106a1.533 1.533 0 01.947-2.287c1.561-.379 1.561-2.6 0-2.978a1.532 1.532 0 01-.947-2.287c.836-1.372-.734-2.942-2.106-2.106a1.532 1.532 0 01-2.287-.947zM10 13a3 3 0 100-6 3 3 0 000 6z" | ||||
|             clip-rule="evenodd" | ||||
|           /> | ||||
|         </svg> | ||||
|         <span>{$_("settings")}</span> | ||||
|       </a> | ||||
|       <a | ||||
|         class:activenav={$router.path === "/about/"} | ||||
|         class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold" | ||||
|         href="/about/" | ||||
|       > | ||||
|         <svg | ||||
|           class="flex-shrink-0 w-5 h-5 mr-2 transition group-hover:text-gray-600" | ||||
|           xmlns="http://www.w3.org/2000/svg" | ||||
|           fill="none" | ||||
|           stroke="currentColor" | ||||
|           stroke-width="2" | ||||
|           stroke-linecap="round" | ||||
|           stroke-linejoin="round" | ||||
|           viewBox="0 0 24 24" | ||||
|           ><circle cx="12" cy="12" r="10" /> | ||||
|           <path d="M12 16v-4M12 8h.01" /></svg | ||||
|         > | ||||
|         <span>{$_("about")}</span> | ||||
|       </a> | ||||
|       <button | ||||
|         tabindex="0" | ||||
|         class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-200 hover:text-gray-900 w-full font-semibold" | ||||
|         on:click={() => { | ||||
|           AuthService.authControllerLogout(); | ||||
|           logout(); | ||||
|         }} | ||||
|       > | ||||
|         <svg | ||||
|           class="flex-shrink-0 w-5 h-5 mr-2 transition group-hover:text-gray-600" | ||||
|           fill="currentColor" | ||||
|           width="24" | ||||
|           height="24" | ||||
|           xmlns="http://www.w3.org/2000/svg" | ||||
|           viewBox="0 0 24 24" | ||||
|           ><path fill="none" d="M0 0h24v24H0z" /> | ||||
|           <path | ||||
|             d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2a9.985 9.985 0 0 1 8 4h-2.71a8 8 0 1 0 .001 12h2.71A9.985 9.985 0 0 1 12 22zm7-6v-3h-8v-2h8V8l5 4-5 4z" | ||||
|           /></svg | ||||
|         > | ||||
|         <span>{$_("logout")}</span> | ||||
|       </button> | ||||
|     </nav> | ||||
|   </div> | ||||
|   <div class="ml-0 transition md:ml-60"> | ||||
|     <header | ||||
|       class="flex items-center w-full px-4 bg-white border-b h-14 md:hidden" | ||||
|     > | ||||
|       <button | ||||
|         on:click={() => { | ||||
|           navOpen = true; | ||||
|         }} | ||||
|         class="block btn btn-light md:hidden" | ||||
|       > | ||||
|         <span class="sr-only">Menu</span><svg | ||||
|           xmlns="http://www.w3.org/2000/svg" | ||||
|           fill="none" | ||||
|           viewBox="0 0 24 24" | ||||
|           stroke-width="1.5" | ||||
|           stroke="currentColor" | ||||
|           class="size-6" | ||||
|         > | ||||
|           <path | ||||
|             stroke-linecap="round" | ||||
|             stroke-linejoin="round" | ||||
|             d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" | ||||
|           /> | ||||
|         </svg> | ||||
|       </button> | ||||
|       <span class="inline-block"> | ||||
|         <img src="/lfk-logo.png" alt="Logo" class="h-8 inline-block" /> | ||||
|         <span class="text-lg font-bold">LfK!Admin</span> | ||||
|       </span> | ||||
|     </header> | ||||
|     <Toaster position="top-right" /> | ||||
|     <slot> | ||||
|       <NoComponentLoaded /> | ||||
|     </slot> | ||||
|   </div> | ||||
|   {#if navOpen === true} | ||||
|     <button | ||||
|       on:click={() => { | ||||
|         navOpen = false; | ||||
|       }} | ||||
|       class:hidden={!navOpen} | ||||
|       class="fixed inset-0 z-10 w-screen h-screen bg-black bg-opacity-25 md:hidden" | ||||
|     /> | ||||
|   {/if} | ||||
| </section> | ||||
|  | ||||
| <style> | ||||
|   .collapsed_navigation { | ||||
|     transform: translateX(-100%); | ||||
|   } | ||||
|   @media (min-width: 768px) { | ||||
|     .collapsed_navigation { | ||||
|       transform: translateX(0px); | ||||
|     } | ||||
|   } | ||||
| </style> | ||||
							
								
								
									
										263
									
								
								src/components/dashboard/MainDashContent.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										263
									
								
								src/components/dashboard/MainDashContent.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,263 @@ | ||||
| <script> | ||||
| 	import { _ } from "svelte-i18n"; | ||||
| 	import { StatsService } from "@odit/lfk-client-js"; | ||||
| 	import store from "../../store"; | ||||
| 	import StatCard from "./StatCard.svelte"; | ||||
| 	const stats_promise = StatsService.statsControllerGet(); | ||||
| </script> | ||||
|  | ||||
| <div class="p-2 md:p-5 overflow-x-hidden"> | ||||
| 	<h4 class="mb-1 text-3xl font-extrabold leading-tight"> | ||||
| 		{$_("dashboard-greeting")} | ||||
| 		<span class="text-blue-500" | ||||
| 			>{store.state.jwtinfo.userdetails.firstname} | ||||
| 			{store.state.jwtinfo.userdetails.lastname}</span | ||||
| 		> | ||||
| 	</h4> | ||||
| 	{#await stats_promise} | ||||
| 		<div | ||||
| 			class="bg-teal-lightest border-t-4 border-teal rounded-b text-teal-darkest px-4 py-3 shadow-md my-2" | ||||
| 			role="alert" | ||||
| 		> | ||||
| 			<p class="font-bold">{$_("stats-are-being-loaded")}</p> | ||||
| 			<p class="text-sm">{$_("this-might-take-a-moment")}</p> | ||||
| 		</div> | ||||
| 	{:then stats} | ||||
| 		<div | ||||
| 			class="grid gap-1 grid-cols-2 lg:grid-cols-3 xl:grid-cols-5 2xl:grid-cols-6 sm:gap-4" | ||||
| 		> | ||||
| 			<StatCard | ||||
| 				title={$_("runners")} | ||||
| 				value={stats.total_runners} | ||||
| 				href="/runners/" | ||||
| 			> | ||||
| 				<svg | ||||
| 					height="24" | ||||
| 					width="24" | ||||
| 					fill="currentColor" | ||||
| 					xmlns="http://www.w3.org/2000/svg" | ||||
| 					viewBox="0 0 24 24" | ||||
| 					><path d="M0 0h24v24H0z" fill="none" /> | ||||
| 					<path | ||||
| 						d="M13.49 5.48c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm-3.6 13.9l1-4.4 2.1 2v6h2v-7.5l-2.1-2 .6-3c1.3 1.5 3.3 2.5 5.5 2.5v-2c-1.9 0-3.5-1-4.3-2.4l-1-1.6c-.4-.6-1-1-1.7-1-.3 0-.5.1-.8.1l-5.2 2.2v4.7h2v-3.4l1.8-.7-1.6 8.1-4.9-1-.4 2 7 1.4z" | ||||
| 					/></svg | ||||
| 				> | ||||
| 			</StatCard> | ||||
| 			<StatCard | ||||
| 				title={$_("total-scans")} | ||||
| 				value={stats.total_scans} | ||||
| 				href="/scans/" | ||||
| 			> | ||||
| 				<svg | ||||
| 					fill="currentColor" | ||||
| 					width="24" | ||||
| 					height="24" | ||||
| 					xmlns="http://www.w3.org/2000/svg" | ||||
| 					viewBox="0 0 24 24" | ||||
| 					><path fill="none" d="M0 0h24v24H0z" /> | ||||
| 					<path | ||||
| 						fill="currentColor" | ||||
| 						d="M2 4h2v16H2V4zm4 0h1v16H6V4zm2 0h2v16H8V4zm3 0h2v16h-2V4zm3 0h2v16h-2V4zm3 0h1v16h-1V4zm2 0h3v16h-3V4z" | ||||
| 					/></svg | ||||
| 				> | ||||
| 			</StatCard> | ||||
| 			<StatCard | ||||
| 				title={$_("total-donors")} | ||||
| 				value={stats.total_donors} | ||||
| 				href="/donors/" | ||||
| 			> | ||||
| 				<svg | ||||
| 					fill="currentColor" | ||||
| 					xmlns="http://www.w3.org/2000/svg" | ||||
| 					viewBox="0 0 24 24" | ||||
| 					width="24" | ||||
| 					height="24" | ||||
| 					><path fill="none" d="M0 0h24v24H0z" /> | ||||
| 					<path | ||||
| 						d="M9.33 11.5h2.17A4.5 4.5 0 0 1 16 16H8.999L9 17h8v-1a5.578 5.578 0 0 0-.886-3H19a5 5 0 0 1 4.516 2.851C21.151 18.972 17.322 21 13 21c-2.761 0-5.1-.59-7-1.625L6 10.071A6.967 6.967 0 0 1 9.33 11.5zM5 19a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1v-9a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v9zM18 5a3 3 0 1 1 0 6 3 3 0 0 1 0-6zm-7-3a3 3 0 1 1 0 6 3 3 0 0 1 0-6z" | ||||
| 					/></svg | ||||
| 				> | ||||
| 			</StatCard> | ||||
| 			<StatCard | ||||
| 				title={$_("total-donation-count")} | ||||
| 				value={stats.total_donations} | ||||
| 				href="/donations/" | ||||
| 			> | ||||
| 				<svg | ||||
| 					fill="currentColor" | ||||
| 					xmlns="http://www.w3.org/2000/svg" | ||||
| 					viewBox="0 0 24 24" | ||||
| 					width="24" | ||||
| 					height="24" | ||||
| 					><path fill="none" d="M0 0h24v24H0z" /> | ||||
| 					<path | ||||
| 						d="M14 2a8 8 0 013.3 15.3A8 8 0 116.7 6.7 8 8 0 0114 2zm-3 7H9v1a2.5 2.5 0 00-.16 5h2.25a.5.5 0 010 1H7v2h2v1h2v-1a2.5 2.5 0 00.16-5H8.91a.5.5 0 010-1H13v-2h-2V9zm3-5a5.99 5.99 0 00-4.48 2.01 8 8 0 018.47 8.47A6 6 0 0014 4z" | ||||
| 					/></svg | ||||
| 				> | ||||
| 			</StatCard> | ||||
| 			<StatCard | ||||
| 				title={$_("average-donation")} | ||||
| 				value={`${parseFloat(stats.average_donation / 100).toLocaleString( | ||||
| 					undefined, | ||||
| 					{ | ||||
| 						minimumFractionDigits: 2, | ||||
| 						maximumFractionDigits: 2, | ||||
| 					} | ||||
| 				)}`} | ||||
| 				href="/donations/" | ||||
| 			> | ||||
| 				<svg | ||||
| 					xmlns="http://www.w3.org/2000/svg" | ||||
| 					height="24" | ||||
| 					fill="currentColor" | ||||
| 					width="24" | ||||
| 					><path d="M0 0h24v24H0z" fill="none" /> | ||||
| 					<path | ||||
| 						d="M15 18.5A6.48 6.48 0 019.24 15H15v-2H8.58c-.05-.33-.08-.66-.08-1s.03-.67.08-1H15V9H9.24A6.491 6.491 0 0115 5.5c1.61 0 3.09.59 4.23 1.57L21 5.3A8.955 8.955 0 0015 3c-3.92 0-7.24 2.51-8.48 6H3v2h3.06a8.262 8.262 0 000 2H3v2h3.52c1.24 3.49 4.56 6 8.48 6 2.31 0 4.41-.87 6-2.3l-1.78-1.77c-1.13.98-2.6 1.57-4.22 1.57z" | ||||
| 					/></svg | ||||
| 				> | ||||
| 			</StatCard> | ||||
| 			<StatCard | ||||
| 				title={$_("total-donations")} | ||||
| 				value={`${parseFloat(stats.total_donation / 100).toLocaleString( | ||||
| 					undefined, | ||||
| 					{ | ||||
| 						minimumFractionDigits: 2, | ||||
| 						maximumFractionDigits: 2, | ||||
| 					} | ||||
| 				)}`} | ||||
| 				href="/donations/" | ||||
| 			> | ||||
| 				<svg | ||||
| 					xmlns="http://www.w3.org/2000/svg" | ||||
| 					height="24" | ||||
| 					fill="currentColor" | ||||
| 					width="24" | ||||
| 					><path d="M0 0h24v24H0z" fill="none" /> | ||||
| 					<path | ||||
| 						d="M15 18.5A6.48 6.48 0 019.24 15H15v-2H8.58c-.05-.33-.08-.66-.08-1s.03-.67.08-1H15V9H9.24A6.491 6.491 0 0115 5.5c1.61 0 3.09.59 4.23 1.57L21 5.3A8.955 8.955 0 0015 3c-3.92 0-7.24 2.51-8.48 6H3v2h3.06a8.262 8.262 0 000 2H3v2h3.52c1.24 3.49 4.56 6 8.48 6 2.31 0 4.41-.87 6-2.3l-1.78-1.77c-1.13.98-2.6 1.57-4.22 1.57z" | ||||
| 					/></svg | ||||
| 				> | ||||
| 			</StatCard> | ||||
| 			<StatCard | ||||
| 				title={$_("total-distance")} | ||||
| 				value={`${stats.total_distance / 1000}km`} | ||||
| 				href="/scans/" | ||||
| 			> | ||||
| 				<svg | ||||
| 					fill="currentColor" | ||||
| 					xmlns="http://www.w3.org/2000/svg" | ||||
| 					height="24" | ||||
| 					width="24" | ||||
| 					><path d="M0 0h24v24H0z" fill="none" /> | ||||
| 					<path | ||||
| 						d="M21 6H3c-1.1 0-2 .9-2 2v8c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm0 10H3V8h2v4h2V8h2v4h2V8h2v4h2V8h2v4h2V8h2v8z" | ||||
| 					/></svg | ||||
| 				> | ||||
| 			</StatCard> | ||||
| 			<StatCard | ||||
| 				title={$_("average-distance")} | ||||
| 				value={`${parseFloat(stats.average_distance / 1000).toLocaleString( | ||||
| 					undefined, | ||||
| 					{ | ||||
| 						minimumFractionDigits: 2, | ||||
| 						maximumFractionDigits: 2, | ||||
| 					} | ||||
| 				)}km`} | ||||
| 				href="/scans/" | ||||
| 			> | ||||
| 				<svg | ||||
| 					fill="currentColor" | ||||
| 					xmlns="http://www.w3.org/2000/svg" | ||||
| 					height="24" | ||||
| 					width="24" | ||||
| 					><path d="M0 0h24v24H0z" fill="none" /> | ||||
| 					<path | ||||
| 						d="M21 6H3c-1.1 0-2 .9-2 2v8c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm0 10H3V8h2v4h2V8h2v4h2V8h2v4h2V8h2v4h2V8h2v8z" | ||||
| 					/></svg | ||||
| 				> | ||||
| 			</StatCard> | ||||
| 			<StatCard | ||||
| 				title={$_("count_teams")} | ||||
| 				value={stats.total_teams} | ||||
| 				href="/teams/" | ||||
| 			> | ||||
| 				<svg | ||||
| 					stroke="currentColor" | ||||
| 					fill="none" | ||||
| 					stroke-width="2" | ||||
| 					viewBox="0 0 24 24" | ||||
| 					stroke-linecap="round" | ||||
| 					stroke-linejoin="round" | ||||
| 					size="24" | ||||
| 					class="stroke-current text-grey-500" | ||||
| 					height="24" | ||||
| 					width="24" | ||||
| 					xmlns="http://www.w3.org/2000/svg" | ||||
| 					><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" /> | ||||
| 					<circle cx="9" cy="7" r="4" /> | ||||
| 					<path d="M23 21v-2a4 4 0 0 0-3-3.87" /> | ||||
| 					<path d="M16 3.13a4 4 0 0 1 0 7.75" /></svg | ||||
| 				> | ||||
| 			</StatCard> | ||||
| 			<StatCard | ||||
| 				title={$_("count_organizations")} | ||||
| 				value={stats.total_orgs} | ||||
| 				href="/orgs/" | ||||
| 			> | ||||
| 				<svg | ||||
| 					height="24" | ||||
| 					fill="currentColor" | ||||
| 					width="24" | ||||
| 					xmlns="http://www.w3.org/2000/svg" | ||||
| 					viewBox="0 0 24 24" | ||||
| 					><path fill="none" d="M0 0h24v24H0z" /> | ||||
| 					<path | ||||
| 						d="M17 11V3H7v4H3v14h8v-4h2v4h8V11h-4zM7 19H5v-2h2v2zm0-4H5v-2h2v2zm0-4H5V9h2v2zm4 4H9v-2h2v2zm0-4H9V9h2v2zm0-4H9V5h2v2zm4 8h-2v-2h2v2zm0-4h-2V9h2v2zm0-4h-2V5h2v2zm4 12h-2v-2h2v2zm0-4h-2v-2h2v2z" | ||||
| 					/></svg | ||||
| 				> | ||||
| 			</StatCard> | ||||
| 			<StatCard | ||||
| 				title={$_("runner_via_selfservice")} | ||||
| 				value={stats.runnersViaSelfservice} | ||||
| 				href="/runners/?created_via=selfservice" | ||||
| 			> | ||||
| 				<svg | ||||
| 					height="24" | ||||
| 					width="24" | ||||
| 					fill="currentColor" | ||||
| 					xmlns="http://www.w3.org/2000/svg" | ||||
| 					viewBox="0 0 24 24" | ||||
| 					><path d="M0 0h24v24H0z" fill="none" /> | ||||
| 					<path | ||||
| 						d="M13.49 5.48c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm-3.6 13.9l1-4.4 2.1 2v6h2v-7.5l-2.1-2 .6-3c1.3 1.5 3.3 2.5 5.5 2.5v-2c-1.9 0-3.5-1-4.3-2.4l-1-1.6c-.4-.6-1-1-1.7-1-.3 0-.5.1-.8.1l-5.2 2.2v4.7h2v-3.4l1.8-.7-1.6 8.1-4.9-1-.4 2 7 1.4z" | ||||
| 					/></svg | ||||
| 				> | ||||
| 			</StatCard> | ||||
| 			<StatCard | ||||
| 				title={$_('runners_via_kiosk')} | ||||
| 				value={stats.runnersViaKiosk} | ||||
| 				href="/runners/?created_via=kiosk" | ||||
| 			> | ||||
| 				<svg | ||||
| 					height="24" | ||||
| 					width="24" | ||||
| 					fill="currentColor" | ||||
| 					xmlns="http://www.w3.org/2000/svg" | ||||
| 					viewBox="0 0 24 24" | ||||
| 					><path d="M0 0h24v24H0z" fill="none" /> | ||||
| 					<path | ||||
| 						d="M13.49 5.48c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm-3.6 13.9l1-4.4 2.1 2v6h2v-7.5l-2.1-2 .6-3c1.3 1.5 3.3 2.5 5.5 2.5v-2c-1.9 0-3.5-1-4.3-2.4l-1-1.6c-.4-.6-1-1-1.7-1-.3 0-.5.1-.8.1l-5.2 2.2v4.7h2v-3.4l1.8-.7-1.6 8.1-4.9-1-.4 2 7 1.4z" | ||||
| 					/></svg | ||||
| 				> | ||||
| 			</StatCard> | ||||
| 		</div> | ||||
| 	{:catch error} | ||||
| 		<div class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-500"> | ||||
| 			<span class="inline-block align-middle mr-8"> | ||||
| 				<b>{$_("general_promise_error")}</b> | ||||
| 				{error} | ||||
| 			</span> | ||||
| 		</div> | ||||
| 	{/await} | ||||
| </div> | ||||
							
								
								
									
										21
									
								
								src/components/dashboard/StatCard.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/components/dashboard/StatCard.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|  | ||||
|   export let href = "#"; | ||||
|   export let title = ""; | ||||
|   export let value = ""; | ||||
| </script> | ||||
|  | ||||
| <a {href}> | ||||
|   <div class="p-3 py-4 sm:p-4 rounded-lg bg-white border border-grey-100"> | ||||
|     <div class="flex flex-row items-center justify-between"> | ||||
|       <div class="flex flex-col"> | ||||
|         <div class="text-md sm:text-xs uppercase font-normal text-grey-500"> | ||||
|           {title} | ||||
|         </div> | ||||
|         <div class="text-2xl sm:text-xl font-bold font-mono">{value}</div> | ||||
|       </div> | ||||
|       <slot /> | ||||
|     </div> | ||||
|   </div> | ||||
| </a> | ||||
| @@ -1,24 +0,0 @@ | ||||
| export function getlang(langkeys) { | ||||
| 	return { | ||||
| 		search: { | ||||
| 			placeholder: langkeys.search | ||||
| 		}, | ||||
| 		sort: { | ||||
| 			sortAsc: langkeys.sort_column_ascending, | ||||
| 			sortDesc: langkeys.sort_column_descending | ||||
| 		}, | ||||
| 		pagination: { | ||||
| 			previous: langkeys.previous, | ||||
| 			next: langkeys.next, | ||||
| 			navigate: (page, pages) => `${langkeys.page} ${page} ${langkeys.of} ${pages}`, | ||||
| 			page: (page) => `${langkeys.page} ${page}`, | ||||
| 			showing: langkeys.showing, | ||||
| 			of: langkeys.of, | ||||
| 			to: langkeys.to, | ||||
| 			results: langkeys.records | ||||
| 		}, | ||||
| 		loading: langkeys.loading, | ||||
| 		noRecordsFound: langkeys.no_matching_records_found, | ||||
| 		error: langkeys.an_error_happened_while_fetching_the_data | ||||
| 	}; | ||||
| } | ||||
							
								
								
									
										380
									
								
								src/components/donations/AddDonationModal.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										380
									
								
								src/components/donations/AddDonationModal.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,380 @@ | ||||
| <script> | ||||
| 	import { _ } from "svelte-i18n"; | ||||
| 	import { clickOutside } from "../base/outsideclick"; | ||||
|  | ||||
| 	import { | ||||
| 		DonationService, | ||||
| 		DonorService, | ||||
| 		RunnerService, | ||||
| 	} from "@odit/lfk-client-js"; | ||||
| 	import Select from "svelte-select"; | ||||
| 	import { createEventDispatcher, onMount } from "svelte"; | ||||
| 	import toast from "svelte-french-toast"; | ||||
| 	export let modal_open; | ||||
| 	const dispatch = createEventDispatcher(); | ||||
| 	const getDonorLabel = (option) => | ||||
| 		option.firstname + " " + (option.middlename || "") + " " + option.lastname; | ||||
| 	const filterDonors = (label, filterText, option) => | ||||
| 		label.toLowerCase().includes(filterText.toLowerCase()) || | ||||
| 		option.value.id.toString().startsWith(filterText.toLowerCase()); | ||||
| 	$: donor = 0; | ||||
| 	$: runner = 0; | ||||
| 	$: donors = []; | ||||
| 	$: runners = []; | ||||
| 	$: type = "distance"; | ||||
| 	$: is_paid = false; | ||||
| 	$: amount_input = 0; | ||||
| 	$: processed_last_submit = true; | ||||
| 	$: is_amount_valid = amount_input > 0; | ||||
| 	$: createbtnenabled = is_amount_valid; | ||||
| 	(() => { | ||||
| 		document.onkeydown = (e) => { | ||||
| 			e = e || window.event; | ||||
| 			if (e.key === "Escape") { | ||||
| 				modal_open = false; | ||||
| 			} | ||||
| 			if (e.keyCode === 13) { | ||||
| 				if (createbtnenabled === true) { | ||||
| 					createbtnenabled = false; | ||||
| 					submit(); | ||||
| 				} | ||||
| 			} | ||||
| 		}; | ||||
| 	})(); | ||||
| 	function submit() { | ||||
| 		if (processed_last_submit === true) { | ||||
| 			let amount_cent = Math.floor(amount_input * 100); | ||||
| 			processed_last_submit = false; | ||||
| 			toast.loading($_("adding-donation")); | ||||
| 			if (type === "fixed") { | ||||
| 				let postdata = { | ||||
| 					donor, | ||||
| 					amount: amount_cent, | ||||
| 					paidAmount: 0, | ||||
| 				}; | ||||
| 				if (is_paid) { | ||||
| 					postdata.paidAmount = amount_cent; | ||||
| 				} | ||||
| 				DonationService.donationControllerPostFixed(postdata) | ||||
| 					.then((result) => { | ||||
| 						donor = donors[0].id || 0; | ||||
| 						runner = runners[0].id || 0; | ||||
| 						amount_input = 0; | ||||
| 						modal_open = false; | ||||
| 						// | ||||
| 						toast.dismiss(); | ||||
| 						toast.success($_("donation_added")); | ||||
| 						dispatch("created", { donations: [result] }); | ||||
| 					}) | ||||
| 					.catch((err) => { | ||||
| 						// | ||||
| 					}) | ||||
| 					.finally(() => { | ||||
| 						processed_last_submit = true; | ||||
| 					}); | ||||
| 			} else if (type === "anonymous") { | ||||
| 				let postdata = { | ||||
| 					amount: amount_cent, | ||||
| 				}; | ||||
| 				DonationService.donationControllerPostAnonymous(postdata) | ||||
| 					.then((result) => { | ||||
| 						amount_input = 0; | ||||
| 						modal_open = false; | ||||
| 						// | ||||
| 						toast.dismiss(); | ||||
| 						toast.success($_("donation_added")); | ||||
| 						dispatch("created", { donations: [result] }); | ||||
| 					}) | ||||
| 					.catch((err) => { | ||||
| 						// | ||||
| 					}) | ||||
| 					.finally(() => { | ||||
| 						processed_last_submit = true; | ||||
| 					}); | ||||
| 			} else if (type === "distance") { | ||||
| 				let postdata = { | ||||
| 					donor, | ||||
| 					runner, | ||||
| 					amountPerDistance: amount_cent, | ||||
| 				}; | ||||
| 				DonationService.donationControllerPostDistance(postdata) | ||||
| 					.then((result) => { | ||||
| 						donor = donors[0].id || 0; | ||||
| 						runner = runners[0].id || 0; | ||||
| 						amount_input = 0; | ||||
| 						modal_open = false; | ||||
| 						// | ||||
| 						toast.dismiss(); | ||||
| 						toast.success($_("donation_added")); | ||||
| 						dispatch("created", { donations: [result] }); | ||||
| 					}) | ||||
| 					.catch((err) => { | ||||
| 						// | ||||
| 					}) | ||||
| 					.finally(() => { | ||||
| 						processed_last_submit = true; | ||||
| 					}); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	onMount(async () => { | ||||
| 		donors = (await DonorService.donorControllerGetAll()).map((r) => { | ||||
| 			return { label: getDonorLabel(r), value: r }; | ||||
| 		}); | ||||
| 		runners = (await RunnerService.runnerControllerGetAll()).map((r) => { | ||||
| 			return { label: getDonorLabel(r), value: r }; | ||||
| 		}); | ||||
| 	}); | ||||
| </script> | ||||
|  | ||||
| {#if modal_open} | ||||
| 	<div | ||||
| 		class="fixed z-10 inset-0 overflow-y-hidden" | ||||
| 		use:clickOutside | ||||
| 		on:click_outside={() => { | ||||
| 			modal_open = false; | ||||
| 		}} | ||||
| 	> | ||||
| 		<div | ||||
| 			class="flex items-end justify-center h-screen text-center sm:block p-0 lg:p-4" | ||||
| 		> | ||||
| 			<div class="fixed inset-0 transition-opacity" aria-hidden="true"> | ||||
| 				<div | ||||
| 					class="absolute inset-0 bg-neutral-500 opacity-75" | ||||
| 					data-id="modal_backdrop" | ||||
| 				/> | ||||
| 			</div> | ||||
| 			<span | ||||
| 				class="hidden sm:inline-block sm:align-middle sm:h-screen" | ||||
| 				aria-hidden="true">​</span | ||||
| 			> | ||||
| 			<div | ||||
| 				class="inline-block align-bottom text-left shadow-xl transform transition-all sm:align-middle w-full lg:w-auto min-w-auto lg:min-w-[35vw] relative z-10" | ||||
| 				role="dialog" | ||||
| 				aria-modal="true" | ||||
| 				aria-labelledby="modal-headline" | ||||
| 			> | ||||
| 				<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4 rounded-t-xl"> | ||||
| 					<div class=""> | ||||
| 						<div | ||||
| 							class="flex-shrink-0 flex items-center justify-center size-12 rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10" | ||||
| 						> | ||||
| 							<svg | ||||
| 								class="size-6 text-blue-600" | ||||
| 								fill="currentColor" | ||||
| 								xmlns="http://www.w3.org/2000/svg" | ||||
| 								viewBox="0 0 24 24" | ||||
| 								width="24" | ||||
| 								height="24" | ||||
| 								><path fill="none" d="M0 0h24v24H0z" /> | ||||
| 								<path | ||||
| 									d="M14 2a8 8 0 013.3 15.3A8 8 0 116.7 6.7 8 8 0 0114 2zm-3 7H9v1a2.5 2.5 0 00-.16 5h2.25a.5.5 0 010 1H7v2h2v1h2v-1a2.5 2.5 0 00.16-5H8.91a.5.5 0 010-1H13v-2h-2V9zm3-5a5.99 5.99 0 00-4.48 2.01 8 8 0 018.47 8.47A6 6 0 0014 4z" | ||||
| 								/></svg | ||||
| 							> | ||||
| 						</div> | ||||
| 						<div class="mt-3"> | ||||
|               <h3 class="text-xl leading-6 font-medium text-neutral-900"> | ||||
|                 {$_("add-donation")} | ||||
|               </h3> | ||||
| 							<nav | ||||
| 								class="relative z-0 flex border border-neutral-200 rounded-xl overflow-hidden mb-2" | ||||
| 							> | ||||
| 								<button | ||||
| 									on:click={() => { | ||||
| 										type = "distance"; | ||||
| 									}} | ||||
| 									type="button" | ||||
| 									id="bar-with-underline-item-1" | ||||
| 									class:donation_active_tab={type === "distance"} | ||||
| 									class:donation_inactive_tab={type !== "distance"} | ||||
| 									aria-selected={type === "distance"} | ||||
| 									role="tab" | ||||
| 								> | ||||
| 									{$_("spende_pro_km")} | ||||
| 								</button> | ||||
| 								<button | ||||
| 									on:click={() => { | ||||
| 										type = "fixed"; | ||||
| 									}} | ||||
| 									type="button" | ||||
| 									id="bar-with-underline-item-2" | ||||
| 									class:donation_active_tab={type === "fixed"} | ||||
| 									class:donation_inactive_tab={type !== "fixed"} | ||||
| 									aria-selected={type === "fixed"} | ||||
| 									role="tab" | ||||
| 								> | ||||
| 									{$_("festbetrag")} | ||||
| 								</button> | ||||
| 								<button | ||||
| 									on:click={() => { | ||||
| 										type = "anonymous"; | ||||
| 									}} | ||||
| 									type="button" | ||||
| 									id="bar-with-underline-item-3" | ||||
| 									class:donation_active_tab={type === "anonymous"} | ||||
| 									class:donation_inactive_tab={type !== "anonymous"} | ||||
| 									aria-selected={type === "anonymous"} | ||||
| 									role="tab" | ||||
| 								> | ||||
| 									{$_("anonyme_spende")} | ||||
| 								</button> | ||||
| 							</nav> | ||||
|  | ||||
| 							<div class="grid grid-cols-6 gap-2 lg:gap-6 text-left"> | ||||
| 								{#if type === "anonymous"} | ||||
| 									<div class="col-span-6"> | ||||
| 										<label | ||||
| 											for="donation_amount_eur" | ||||
| 											class="block text-sm font-medium text-neutral-900" | ||||
| 										> | ||||
| 											{$_("donation-amount")}</label | ||||
| 										> | ||||
| 										<div class="mt-1 flex rounded-md shadow-sm"> | ||||
| 											<input | ||||
| 												autocomplete="off" | ||||
| 												class:border-red-500={!is_amount_valid} | ||||
| 												class:focus:border-red-500={!is_amount_valid} | ||||
| 												class:focus:ring-red-500={!is_amount_valid} | ||||
| 												bind:value={amount_input} | ||||
| 												type="number" | ||||
| 												step="0.01" | ||||
| 												name="donation_amount_eur" | ||||
| 												class="focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-l-md sm:text-sm border-neutral-300 border bg-neutral-50 text-neutral-800 p-2" | ||||
| 												placeholder="2.00" | ||||
| 											/> | ||||
| 											<span | ||||
| 												class="inline-flex items-center px-3 rounded-r-md border border-neutral-300 bg-neutral-50 text-neutral-500 text-sm" | ||||
| 												>€</span | ||||
| 											> | ||||
| 										</div> | ||||
| 										{#if !is_amount_valid} | ||||
| 											<span | ||||
| 												class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1" | ||||
| 											> | ||||
| 												{$_("donation-amount-must-be-greater-that-0-00eur")} | ||||
| 											</span> | ||||
| 										{/if} | ||||
| 									</div> | ||||
| 								{:else} | ||||
| 									<div class="col-span-6"> | ||||
| 										<label | ||||
| 											for="donor" | ||||
| 											class="block text-sm font-medium text-neutral-900" | ||||
| 											>{$_("donor")}</label | ||||
| 										> | ||||
| 										<Select | ||||
| 											containerClasses="rounded-l-md mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-neutral-300 border bg-neutral-50 text-neutral-800 rounded-md p-2" | ||||
| 											itemFilter={(label, filterText, option) => | ||||
| 												filterDonors(label, filterText, option)} | ||||
| 											items={donors} | ||||
| 											showChevron={true} | ||||
| 											placeholder={$_("search-for-donor-name-or-id")} | ||||
| 											noOptionsMessage={$_("no-donors-found")} | ||||
| 											on:select={(selectedValue) => | ||||
| 												(donor = selectedValue.detail.value.id)} | ||||
| 											on:clear={() => (donors = null)} | ||||
| 										/> | ||||
| 									</div> | ||||
| 									{#if type === "distance"} | ||||
| 										<div class="col-span-6"> | ||||
| 											<label | ||||
| 												for="donor" | ||||
| 												class="block text-sm font-medium text-neutral-900" | ||||
| 												>{$_("runner")}</label | ||||
| 											> | ||||
| 											<Select | ||||
| 												containerClasses="rounded-l-md mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-neutral-300 border bg-neutral-50 text-neutral-800 rounded-md p-2" | ||||
| 												itemFilter={(label, filterText, option) => | ||||
| 													filterDonors(label, filterText, option)} | ||||
| 												items={runners} | ||||
| 												showChevron={true} | ||||
| 												placeholder={$_("search-for-runner-by-name-or-id")} | ||||
| 												noOptionsMessage={$_("no-runners-found")} | ||||
| 												on:select={(selectedValue) => | ||||
| 													(runner = selectedValue.detail.value.id)} | ||||
| 												on:clear={() => (runner = null)} | ||||
| 											/> | ||||
| 										</div> | ||||
| 									{/if} | ||||
| 									<div class="col-span-6"> | ||||
| 										<label | ||||
| 											for="donation_amount_eur" | ||||
| 											class="block text-sm font-medium text-neutral-900" | ||||
| 										> | ||||
| 											{#if type === "fixed"} | ||||
| 												{$_("donation-amount")} | ||||
| 											{:else}{$_("amount-per-kilometer")}{/if}</label | ||||
| 										> | ||||
| 										<div class="mt-1 flex rounded-md shadow-sm"> | ||||
| 											<input | ||||
| 												autocomplete="off" | ||||
| 												class:border-red-500={!is_amount_valid} | ||||
| 												class:focus:border-red-500={!is_amount_valid} | ||||
| 												class:focus:ring-red-500={!is_amount_valid} | ||||
| 												bind:value={amount_input} | ||||
| 												type="number" | ||||
| 												step="0.01" | ||||
| 												name="donation_amount_eur" | ||||
| 												class="focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-l-md sm:text-sm border-neutral-300 border bg-neutral-50 text-neutral-800 p-2" | ||||
| 												placeholder="2.00" | ||||
| 											/> | ||||
| 											<span | ||||
| 												class="inline-flex items-center px-3 rounded-r-md border border-neutral-300 bg-neutral-50 text-neutral-500 text-sm" | ||||
| 												>€</span | ||||
| 											> | ||||
| 										</div> | ||||
| 										{#if !is_amount_valid} | ||||
| 											<span | ||||
| 												class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1" | ||||
| 											> | ||||
| 												{$_("donation-amount-must-be-greater-that-0-00eur")} | ||||
| 											</span> | ||||
| 										{/if} | ||||
| 									</div> | ||||
| 								{/if} | ||||
| 								{#if type === "fixed"} | ||||
| 									<div class="flex"> | ||||
| 										<input | ||||
| 											bind:checked={is_paid} | ||||
| 											type="checkbox" | ||||
| 											class="shrink-0 mt-0.5 border-neutral-200 rounded-sm text-blue-600 focus:ring-blue-500 checked:border-blue-500 disabled:opacity-50 disabled:pointer-events-none" | ||||
| 											id="hs-default-checkbox" | ||||
| 										/> | ||||
| 										<label | ||||
| 											for="hs-default-checkbox" | ||||
| 											class="text-base text-neutral-900 ms-2 font-medium" | ||||
| 											>{$_("already-paid")}</label | ||||
| 										> | ||||
| 									</div> | ||||
| 								{/if} | ||||
| 							</div> | ||||
| 						</div> | ||||
| 					</div> | ||||
| 				</div> | ||||
| 				<div | ||||
| 					class="bg-neutral-50 px-4 lg:py-3 sm:px-6 grid gap-2 lg:rounded-b-xl pt-3 pb-10" | ||||
| 				> | ||||
| 					<button | ||||
| 						disabled={!createbtnenabled} | ||||
| 						class:opacity-50={!createbtnenabled} | ||||
| 						on:click={submit} | ||||
| 						type="button" | ||||
| 						class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" | ||||
| 					> | ||||
| 						{$_("create")} | ||||
| 					</button> | ||||
| 					<button | ||||
| 						on:click={() => { | ||||
| 							modal_open = false; | ||||
| 						}} | ||||
| 						type="button" | ||||
| 						class="w-full justify-center rounded-md border border-neutral-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-neutral-900 hover:bg-neutral-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 hidden lg:block" | ||||
| 					> | ||||
| 						{$_("cancel")} | ||||
| 					</button> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 	</div> | ||||
| {/if} | ||||
							
								
								
									
										205
									
								
								src/components/donations/AddDonationPaymentModal.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										205
									
								
								src/components/donations/AddDonationPaymentModal.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,205 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import { clickOutside } from "../base/outsideclick"; | ||||
|   import { DonationService } from "@odit/lfk-client-js"; | ||||
|   import { createEventDispatcher } from "svelte"; | ||||
|   import toast from "svelte-french-toast"; | ||||
|   export let payment_modal_open = false; | ||||
|   export let original_data = {}; | ||||
|   export let paid_amount_input = 0; | ||||
|   const dispatch = createEventDispatcher(); | ||||
|   $: processed_last_submit = true; | ||||
|   $: createbtnenabled = | ||||
|     is_paid_amount_valid && | ||||
|     !(paid_amount_input * 100 == original_data.paidAmount); | ||||
|   $: is_paid_amount_valid = paid_amount_input > 0; | ||||
|   (() => { | ||||
|     document.onkeydown = (e) => { | ||||
|       e = e || window.event; | ||||
|       if (e.key === "Escape") { | ||||
|         payment_modal_open = false; | ||||
|       } | ||||
|       if (e.keyCode === 13) { | ||||
|         if (createbtnenabled === true) { | ||||
|           createbtnenabled = false; | ||||
|           submit(); | ||||
|         } | ||||
|       } | ||||
|     }; | ||||
|   })(); | ||||
|   function submit() { | ||||
|     if (processed_last_submit === true) { | ||||
|       processed_last_submit = false; | ||||
|       toast.loading($_("updating-donation")); | ||||
|       const editable = Object.assign({}, original_data); | ||||
|       editable.donor = editable.donor.id; | ||||
|       editable.paidAmount = Math.round(paid_amount_input * 100); | ||||
|       if (editable.responseType == "DISTANCEDONATION" || editable.runner) { | ||||
|         editable.runner = editable.runner.id; | ||||
|         DonationService.donationControllerPutDistance( | ||||
|           original_data.id, | ||||
|           editable | ||||
|         ) | ||||
|           .then((result) => { | ||||
|             payment_modal_open = false; | ||||
|             // | ||||
|             toast.dismiss(); | ||||
|  | ||||
|             toast.success($_("donation-updated")); | ||||
|             dispatch("created", { donation: result }); | ||||
|           }) | ||||
|           .catch((err) => { | ||||
|             // | ||||
|           }) | ||||
|           .finally(() => { | ||||
|             processed_last_submit = true; | ||||
|           }); | ||||
|       } else { | ||||
|         DonationService.donationControllerPutFixed(original_data.id, editable) | ||||
|           .then((result) => { | ||||
|             payment_modal_open = false; | ||||
|             // | ||||
|             toast.dismiss(); | ||||
|             toast.success($_("donation-updated")); | ||||
|             dispatch("created", { donation: result }); | ||||
|           }) | ||||
|           .catch((err) => { | ||||
|             // | ||||
|           }) | ||||
|           .finally(() => { | ||||
|             processed_last_submit = true; | ||||
|           }); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| {#if payment_modal_open} | ||||
|   <div | ||||
|     class="fixed z-10 inset-0 overflow-y-hidden" | ||||
|     use:clickOutside | ||||
|     on:click_outside={() => { | ||||
|       payment_modal_open = false; | ||||
|     }} | ||||
|   > | ||||
|     <div | ||||
|       class="flex items-end justify-center h-screen text-center sm:block p-0 lg:p-4" | ||||
|     > | ||||
|       <div class="fixed inset-0 transition-opacity" aria-hidden="true"> | ||||
|         <div | ||||
|           class="absolute inset-0 bg-gray-500 opacity-75" | ||||
|           data-id="modal_backdrop" | ||||
|         /> | ||||
|       </div> | ||||
|       <span | ||||
|         class="hidden sm:inline-block sm:align-middle sm:h-screen" | ||||
|         aria-hidden="true">​</span | ||||
|       > | ||||
|       <div | ||||
|         class="inline-block align-bottom text-left shadow-xl transform transition-all sm:align-middle w-full lg:w-auto min-w-auto lg:min-w-[35vw] relative z-10" | ||||
|         role="dialog" | ||||
|         aria-modal="true" | ||||
|         aria-labelledby="modal-headline" | ||||
|       > | ||||
|         <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4 rounded-t-xl"> | ||||
|           <div class=""> | ||||
|             <div | ||||
|               class="flex-shrink-0 flex items-center justify-center size-12 rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10" | ||||
|             > | ||||
|               <svg | ||||
|                 class="size-6 text-blue-600" | ||||
|                 fill="currentColor" | ||||
|                 xmlns="http://www.w3.org/2000/svg" | ||||
|                 viewBox="0 0 24 24" | ||||
|                 width="24" | ||||
|                 height="24" | ||||
|                 ><path fill="none" d="M0 0h24v24H0z" /> | ||||
|                 <path | ||||
|                   fill="currentColor" | ||||
|                   d="M22 10v10a1 1 0 01-1 1H3a1 1 0 01-1-1V10h20zm0-2H2V4a1 1 0 011-1h18a1 1 0 011 1v4zm-7 8v2h4v-2h-4z" | ||||
|                 /></svg | ||||
|               > | ||||
|             </div> | ||||
|             <div class="mt-3 text-left"> | ||||
|               <h3 class="text-lg leading-6 font-medium text-gray-900"> | ||||
|                 {$_("enter-payment")} | ||||
|               </h3> | ||||
|               <div class="mb-6"> | ||||
|                 <p class="text-sm text-gray-500"> | ||||
|                   {$_( | ||||
|                     "you-can-enter-the-donations-paid-amount-manually-or-use-the-max-button-to-use-the-donations-exact-amount" | ||||
|                   )} | ||||
|                 </p> | ||||
|               </div> | ||||
|               <div class="grid grid-cols gap-2 lg:gap-6"> | ||||
|                 <div class="w-full"> | ||||
|                   <label | ||||
|                     for="token" | ||||
|                     class="block text-sm font-medium text-gray-700" | ||||
|                     >{$_("paid-amount")}</label | ||||
|                   > | ||||
|                   <div | ||||
|                     class="inline-flex border-gray-300 border rounded-l-md rounded-r-md bg-gray-50 text-gray-500 w-full" | ||||
|                   > | ||||
|                     <input | ||||
|                       autocomplete="off" | ||||
|                       class:border-red-500={!is_paid_amount_valid} | ||||
|                       class:focus:border-red-500={!is_paid_amount_valid} | ||||
|                       class:focus:ring-red-500={!is_paid_amount_valid} | ||||
|                       bind:value={paid_amount_input} | ||||
|                       type="number" | ||||
|                       step="0.01" | ||||
|                       name="donation_amount_eur" | ||||
|                       class="focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-l-md sm:text-sm p-2" | ||||
|                       placeholder="2.00" | ||||
|                     /> | ||||
|                     <button | ||||
|                       on:click={() => { | ||||
|                         paid_amount_input = paid_amount_input = ( | ||||
|                           original_data.amount / 100 | ||||
|                         ).toFixed(2); | ||||
|                       }} | ||||
|                       class="inline-flex items-center p-r-2 text-indigo-300 hover:text-indigo-700 text-sm" | ||||
|                       >MAX</button | ||||
|                     > | ||||
|                     <span | ||||
|                       class="inline-flex items-center px-3 rounded-r-md border border-gray-300 bg-gray-50 text-gray-500 text-sm" | ||||
|                       >€</span | ||||
|                     > | ||||
|                   </div> | ||||
|                   {#if !is_paid_amount_valid} | ||||
|                     <span | ||||
|                       class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1" | ||||
|                     > | ||||
|                       {$_("payment-amount-must-be-greater-than-0-00eur")} | ||||
|                     </span> | ||||
|                   {/if} | ||||
|                 </div> | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div class="bg-gray-50 px-4 lg:py-3 sm:px-6 grid gap-2 lg:rounded-b-xl pt-3 pb-10"> | ||||
|           <button | ||||
|             disabled={!createbtnenabled} | ||||
|             class:opacity-50={!createbtnenabled} | ||||
|             on:click={submit} | ||||
|             type="button" | ||||
|             class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" | ||||
|           > | ||||
|             {$_("save-changes")} | ||||
|           </button> | ||||
|           <button | ||||
|             on:click={() => { | ||||
|               payment_modal_open = false; | ||||
|             }} | ||||
|             type="button" | ||||
|             class="w-full justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 hidden lg:block" | ||||
|           > | ||||
|             {$_("cancel")} | ||||
|           </button> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| {/if} | ||||
							
								
								
									
										122
									
								
								src/components/donations/DeleteDonationModal.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								src/components/donations/DeleteDonationModal.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,122 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import { clickOutside } from "../base/outsideclick"; | ||||
|   import { createEventDispatcher, onMount } from "svelte"; | ||||
|   export let modal_open; | ||||
|   export let delete_donation = { | ||||
|     id: 0, | ||||
|     runner: { | ||||
|       firstname: "", | ||||
|       lastname: "", | ||||
|     }, | ||||
|     donor: { | ||||
|       firstname: "", | ||||
|       lastname: "", | ||||
|     }, | ||||
|     amount: 0, | ||||
|   }; | ||||
|   const dispatch = createEventDispatcher(); | ||||
|   onMount(() => { | ||||
|     document.onkeydown = (e) => { | ||||
|       e = e || window.event; | ||||
|       if (e.key === "Escape") { | ||||
|         modal_open = false; | ||||
|       } | ||||
|       if (e.keyCode === 13) { | ||||
|         if (createbtnenabled === true) { | ||||
|           createbtnenabled = false; | ||||
|           submit(); | ||||
|         } | ||||
|       } | ||||
|     }; | ||||
|   }); | ||||
|   async function submit() { | ||||
|     dispatch("delete", { id: delete_donation.id }); | ||||
|     modal_open = false; | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| {#if modal_open} | ||||
|   <div | ||||
|     class="fixed z-10 inset-0 overflow-y-hidden" | ||||
|     use:clickOutside | ||||
|     on:click_outside={() => { | ||||
|       modal_open = false; | ||||
|     }} | ||||
|   > | ||||
|     <div | ||||
|       class="flex items-end justify-center h-screen text-center sm:block p-0 lg:p-4" | ||||
|     > | ||||
|       <div class="fixed inset-0 transition-opacity" aria-hidden="true"> | ||||
|         <div | ||||
|           class="absolute inset-0 bg-gray-500 opacity-75" | ||||
|           data-id="modal_backdrop" | ||||
|         /> | ||||
|       </div> | ||||
|       <span | ||||
|         class="hidden sm:inline-block sm:align-middle sm:h-screen" | ||||
|         aria-hidden="true">​</span | ||||
|       > | ||||
|       <div | ||||
|         class="inline-block align-bottom text-left shadow-xl transform transition-all sm:align-middle w-full lg:w-auto min-w-auto lg:min-w-[35vw] relative z-10" | ||||
|         role="dialog" | ||||
|         aria-modal="true" | ||||
|         aria-labelledby="modal-headline" | ||||
|       > | ||||
|         <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4 rounded-t-xl"> | ||||
|           <div class=""> | ||||
|             <div | ||||
|               class="flex-shrink-0 flex items-center justify-center size-12 rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10" | ||||
|             > | ||||
|               <svg | ||||
|                 class="size-6 text-blue-600" | ||||
|                 fill="currentColor" | ||||
|                 xmlns="http://www.w3.org/2000/svg" | ||||
|                 viewBox="0 0 24 24" | ||||
|                 width="24" | ||||
|                 height="24" | ||||
|                 ><path fill="none" d="M0 0h24v24H0z" /> | ||||
|                 <path | ||||
|                   d="M14 2a8 8 0 013.3 15.3A8 8 0 116.7 6.7 8 8 0 0114 2zm-3 7H9v1a2.5 2.5 0 00-.16 5h2.25a.5.5 0 010 1H7v2h2v1h2v-1a2.5 2.5 0 00.16-5H8.91a.5.5 0 010-1H13v-2h-2V9zm3-5a5.99 5.99 0 00-4.48 2.01 8 8 0 018.47 8.47A6 6 0 0014 4z" | ||||
|                 /></svg | ||||
|               > | ||||
|             </div> | ||||
|             <div class="mt-3 sm:text-left max-h-[75vh]"> | ||||
|               <h3 class="text-lg leading-6 font-medium text-gray-900"> | ||||
|                 {$_("please-confirm-the-deletion-of-donation")} | ||||
|               </h3> | ||||
|               <div class="w-full"> | ||||
|                 <span class="inline-block" | ||||
|                   >{#if delete_donation.donor}<b>{$_("donor")}</b>: {delete_donation.donor.firstname} | ||||
|                   {delete_donation.donor.lastname}{:else}{$_("anonymer_sponsor")}{/if} | ||||
|                   <br> | ||||
|                   <b>{$_("amount")}</b>: {`${(delete_donation.amount / 100) | ||||
|           .toFixed(2) | ||||
|           .toLocaleString("de-DE", { valute: "EUR" })}€`}</span | ||||
|                 > | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div class="bg-gray-50 px-4 lg:py-3 sm:px-6 grid gap-2 lg:rounded-b-xl pt-3 pb-10"> | ||||
|           <button | ||||
|             on:click={submit} | ||||
|             type="button" | ||||
|             class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500" | ||||
|           > | ||||
|             {$_("delete")} | ||||
|           </button> | ||||
|           <button | ||||
|             on:click={() => { | ||||
|               modal_open = false; | ||||
|             }} | ||||
|             type="button" | ||||
|             class="w-full justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 hidden lg:block" | ||||
|           > | ||||
|             {$_("cancel")} | ||||
|           </button> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| {/if} | ||||
							
								
								
									
										352
									
								
								src/components/donations/DonationDetail.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										352
									
								
								src/components/donations/DonationDetail.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,352 @@ | ||||
| <script> | ||||
| 	import { _ } from "svelte-i18n"; | ||||
| 	import store from "../../store"; | ||||
| 	import { | ||||
| 		DonationService, | ||||
| 		DonorService, | ||||
| 		RunnerService, | ||||
| 	} from "@odit/lfk-client-js"; | ||||
| 	import toast from "svelte-french-toast"; | ||||
|  | ||||
| 	import PromiseError from "../base/PromiseError.svelte"; | ||||
| 	import Select from "svelte-select"; | ||||
| 	let data_loaded = false; | ||||
| 	export let params; | ||||
| 	$: delete_triggered = false; | ||||
| 	$: original_data = {}; | ||||
| 	$: editable = {}; | ||||
| 	$: donor = {}; | ||||
| 	$: runner = {}; | ||||
| 	$: current_donors = []; | ||||
| 	$: current_runners = []; | ||||
| 	$: amount_input = 0; | ||||
| 	$: is_amount_valid = amount_input > 0; | ||||
| 	$: paid_amount_input = 0; | ||||
| 	$: is_paid_amount_valid = paid_amount_input > 0; | ||||
| 	$: is_everything_set = | ||||
| 		editable.donor != null && | ||||
| 		((original_data.responseType == "DISTANCEDONATION" && | ||||
| 			editable?.runner != null) || | ||||
| 			original_data.responseType !== "DISTANCEDONATION"); | ||||
| 	$: changes_performed = | ||||
| 		!(JSON.stringify(original_data) === JSON.stringify(editable)) || | ||||
| 		(original_data.responseType == "DISTANCEDONATION" && | ||||
| 			!(Math.floor(amount_input * 100) === original_data.amountPerDistance)) || | ||||
| 		(original_data.responseType !== "DISTANCEDONATION" && | ||||
| 			!(Math.floor(amount_input * 100) === original_data.amount)) || | ||||
| 		!(Math.floor(paid_amount_input * 100) === original_data.paidAmount); | ||||
| 	$: save_enabled = changes_performed && is_amount_valid && is_everything_set; | ||||
|  | ||||
| 	const promise = DonationService.donationControllerGetOne( | ||||
| 		params.donationid | ||||
| 	).then((data) => { | ||||
| 		data_loaded = true; | ||||
| 		original_data = Object.assign({}, data); | ||||
| 		editable = Object.assign({}, original_data); | ||||
| 		paid_amount_input = data.paidAmount / 100; | ||||
| 		if (data.responseType == "DISTANCEDONATION") { | ||||
| 			amount_input = data.amountPerDistance / 100; | ||||
| 			RunnerService.runnerControllerGetAll().then((val) => { | ||||
| 				current_runners = val.map((r) => { | ||||
| 					return { label: getDonorLabel(r), value: r }; | ||||
| 				}); | ||||
| 				runner = current_runners.find((g) => g.value.id == editable.runner.id); | ||||
| 			}); | ||||
| 		} else { | ||||
| 			amount_input = data.amount / 100; | ||||
| 		} | ||||
| 		DonorService.donorControllerGetAll().then((val) => { | ||||
| 			current_donors = val.map((r) => { | ||||
| 				return { label: getDonorLabel(r), value: r }; | ||||
| 			}); | ||||
| 			donor = current_donors.find((g) => g.value.id == editable.donor.id); | ||||
| 		}); | ||||
| 	}); | ||||
| 	const getDonorLabel = (option) => | ||||
| 		option.firstname + " " + (option.middlename || "") + " " + option.lastname; | ||||
| 	const filterDonors = (label, filterText, option) => | ||||
| 		label.toLowerCase().includes(filterText.toLowerCase()) || | ||||
| 		option.value.id.toString().startsWith(filterText.toLowerCase()); | ||||
|  | ||||
| 	function submit() { | ||||
| 		if (data_loaded === true && save_enabled) { | ||||
| 			toast($_("updating-donation")); | ||||
| 			let postdata = {}; | ||||
| 			editable.paidAmount = paid_amount_input * 100; | ||||
| 			if (original_data.responseType === "DISTANCEDONATION") { | ||||
| 				editable.amountPerDistance = Math.floor(amount_input * 100); | ||||
| 				postdata = Object.assign(postdata, editable); | ||||
| 				postdata.runner = postdata.runner.id; | ||||
| 				postdata.donor = postdata.donor.id; | ||||
| 				DonationService.donationControllerPutDistance( | ||||
| 					original_data.id, | ||||
| 					postdata | ||||
| 				) | ||||
| 					.then((resp) => { | ||||
| 						Object.assign(original_data, editable); | ||||
| 						original_data = original_data; | ||||
| 						toast.success($_("donation-updated")); | ||||
| 					}) | ||||
| 					.catch((err) => {}); | ||||
| 			} else { | ||||
| 				editable.amount = Math.floor(amount_input * 100); | ||||
| 				postdata = Object.assign(postdata, editable); | ||||
| 				postdata.donor = postdata.donor.id; | ||||
| 				DonationService.donationControllerPutFixed(original_data.id, postdata) | ||||
| 					.then((resp) => { | ||||
| 						Object.assign(original_data, editable); | ||||
| 						original_data = original_data; | ||||
| 						toast.success($_("donation-updated")); | ||||
| 					}) | ||||
| 					.catch((err) => {}); | ||||
| 			} | ||||
| 		} else { | ||||
| 		} | ||||
| 	} | ||||
| 	function deleteDonation() { | ||||
| 		DonationService.donationControllerRemove(original_data.id, false) | ||||
| 			.then((resp) => { | ||||
| 				toast.success($_("donation-deleted")); | ||||
| 				location.replace("./"); | ||||
| 			}) | ||||
| 			.catch((err) => { | ||||
| 				modal_open = true; | ||||
| 				delete_donor = original_data; | ||||
| 			}); | ||||
| 	} | ||||
| </script> | ||||
|  | ||||
| {#await promise} | ||||
| 	{$_("loading-donation-details")} | ||||
| {:then} | ||||
| 	<section class="container p-5 select-none"> | ||||
| 		<div class="flex flex-row mb-4"> | ||||
| 			<div class="mt-2 w-full"> | ||||
| 				<nav class="w-full flex"> | ||||
| 					<ol class="list-none flex flex-row items-center justify-start"> | ||||
| 						<li class="flex items-center"> | ||||
| 							<a class="mr-2" href="./" | ||||
| 								><svg | ||||
| 									xmlns="http://www.w3.org/2000/svg" | ||||
| 									width="24" | ||||
| 									height="24" | ||||
| 									viewBox="0 0 24 24" | ||||
| 									fill="none" | ||||
| 									stroke="currentColor" | ||||
| 									stroke-width="2" | ||||
| 									stroke-linecap="round" | ||||
| 									stroke-linejoin="round" | ||||
| 									class="inline-block" | ||||
| 									><path d="m12 19-7-7 7-7" /><path d="M19 12H5" /></svg | ||||
| 								> | ||||
| 								{$_("donations")}</a | ||||
| 							> | ||||
| 						</li> | ||||
| 					</ol> | ||||
| 				</nav> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 		<div class="mb-4 text-3xl font-extrabold leading-tight"> | ||||
| 			{original_data.donor.firstname} | ||||
| 			{original_data.donor.middlename || ""} | ||||
| 			{original_data.donor.lastname} | ||||
| 			> | ||||
| 			{#if original_data.responseType == "DISTANCEDONATION"} | ||||
| 				{original_data.runner.firstname} | ||||
| 				{original_data.runner.middlename || ""} | ||||
| 				{original_data.runner.lastname} | ||||
| 			{:else} | ||||
| 				{$_("fixed-donation")}: | ||||
| 				{amount_input.toFixed(2).toLocaleString("de-DE", { valute: "EUR" })}€ | ||||
| 			{/if} | ||||
| 			[#{original_data.id}] | ||||
| 			<div data-id="donation_actions_${original_data.id}"> | ||||
| 				{#if store.state.jwtinfo.userdetails.permissions.includes("DONATION:DELETE")} | ||||
| 					{#if delete_triggered} | ||||
| 						<button | ||||
| 							on:click={deleteDonation} | ||||
| 							class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:w-auto sm:text-sm" | ||||
| 							>{$_("confirm-deletion")}</button | ||||
| 						> | ||||
| 						<button | ||||
| 							on:click={() => { | ||||
| 								delete_triggered = !delete_triggered; | ||||
| 							}} | ||||
| 							class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-400 text-base font-medium text-white sm:w-auto sm:text-sm" | ||||
| 							>{$_("cancel")}</button | ||||
| 						> | ||||
| 					{/if} | ||||
| 					{#if !delete_triggered} | ||||
| 						<button | ||||
| 							on:click={() => { | ||||
| 								delete_triggered = true; | ||||
| 							}} | ||||
| 							type="button" | ||||
| 							class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:w-auto sm:text-sm" | ||||
| 							>{$_("delete-donation")}</button | ||||
| 						> | ||||
| 					{/if} | ||||
| 				{/if} | ||||
| 				{#if !delete_triggered} | ||||
| 					<button | ||||
| 						disabled={!save_enabled} | ||||
| 						class:opacity-50={!save_enabled} | ||||
| 						type="button" | ||||
| 						on:click={submit} | ||||
| 						class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:w-auto sm:text-sm mb-1 lg:mb-0" | ||||
| 						>{$_("save-changes")}</button | ||||
| 					> | ||||
| 				{/if} | ||||
| 			</div> | ||||
| 		</div> | ||||
| 		<!--  --> | ||||
| 		<div> | ||||
| 			<span class="font-semibold text-gray-700" | ||||
| 				>{$_("total-donation-amount")}:</span | ||||
| 			> | ||||
| 			<span | ||||
| 				>{(editable.amount / 100) | ||||
| 					.toFixed(2) | ||||
| 					.toLocaleString("de-DE", { valute: "EUR" })}€</span | ||||
| 			> | ||||
| 			| | ||||
| 			<span class="font-semibold text-gray-700">{$_("paid-amount")}:</span> | ||||
| 			<span | ||||
| 				>{(editable.paidAmount / 100) | ||||
| 					.toFixed(2) | ||||
| 					.toLocaleString("de-DE", { valute: "EUR" })}€</span | ||||
| 			> | ||||
| 			| | ||||
| 			<span class="font-semibold text-gray-700">{$_("status")}:</span> | ||||
| 			{#if editable.status == "PAID"} | ||||
| 				<span | ||||
| 					class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full border border-current bg-green-100 text-green-800" | ||||
| 					>{$_("paid")}</span | ||||
| 				> | ||||
| 			{:else} | ||||
| 				<span | ||||
| 					class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full border border-current bg-red-100 text-red-800" | ||||
| 					>{$_("open")}</span | ||||
| 				> | ||||
| 			{/if} | ||||
| 		</div> | ||||
| 		<br /> | ||||
| 		<div class=" mt-2 w-full"> | ||||
| 			<label for="donor" class="block font-semibold text-gray-700" | ||||
| 				>{$_("donor")}</label | ||||
| 			> | ||||
| 			<Select | ||||
| 				containerClasses="rounded-l-md mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-gray-300 border bg-gray-50 text-neutral-800 rounded-md p-2" | ||||
| 				itemFilter={(label, filterText, option) => | ||||
| 					filterDonors(label, filterText, option)} | ||||
| 				items={current_donors} | ||||
| 				showChevron={true} | ||||
| 				placeholder={$_("search-for-donor-name-or-id")} | ||||
| 				noOptionsMessage={$_("no-donors-found")} | ||||
| 				bind:selectedValue={donor} | ||||
| 				on:select={(selectedValue) => { | ||||
| 					editable.donor = selectedValue.detail.value; | ||||
| 					editable.donor.donationAmount = original_data.donor.donationAmount; | ||||
| 					editable.donor.paidDonationAmount = | ||||
| 						original_data.donor.paidDonationAmount; | ||||
| 				}} | ||||
| 				on:clear={() => (editable.donor = null)} | ||||
| 			/> | ||||
| 		</div> | ||||
| 		{#if original_data.responseType == "DISTANCEDONATION"} | ||||
| 			<div class=" mt-2 w-full"> | ||||
| 				<label for="donor" class="block font-semibold text-gray-700" | ||||
| 					>{$_("runner")}</label | ||||
| 				> | ||||
| 				<Select | ||||
| 					containerClasses="rounded-l-md mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-gray-300 border bg-gray-50 text-neutral-800 rounded-md p-2" | ||||
| 					itemFilter={(label, filterText, option) => | ||||
| 						filterDonors(label, filterText, option)} | ||||
| 					items={current_runners} | ||||
| 					showChevron={true} | ||||
| 					placeholder={$_("search-for-runner-by-name-or-id")} | ||||
| 					noOptionsMessage={$_("no-runners-found")} | ||||
| 					bind:selectedValue={runner} | ||||
| 					on:select={(selectedValue) => | ||||
| 						(editable.runner = selectedValue.detail.value)} | ||||
| 					on:clear={() => (editable.runner = null)} | ||||
| 				/> | ||||
| 			</div> | ||||
| 		{/if} | ||||
| 		<div class=" mt-2 w-full"> | ||||
| 			<label for="lastname" class="font-semibold text-gray-700"> | ||||
| 				{#if original_data.responseType == "DISTANCEDONATION"} | ||||
| 					{$_("amount-per-kilometer")} | ||||
| 				{:else}{$_("donation-amount")}{/if} | ||||
| 			</label> | ||||
| 			<div class="mt-1 flex rounded-md shadow-sm"> | ||||
| 				<input | ||||
| 					autocomplete="off" | ||||
| 					class:border-red-500={!is_amount_valid} | ||||
| 					class:focus:border-red-500={!is_amount_valid} | ||||
| 					class:focus:ring-red-500={!is_amount_valid} | ||||
| 					bind:value={amount_input} | ||||
| 					type="number" | ||||
| 					step="0.01" | ||||
| 					name="donation_amount_eur" | ||||
| 					class="focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-l-md sm: border-gray-300 border bg-gray-50 text-neutral-800 p-2" | ||||
| 					placeholder="2.00" | ||||
| 				/> | ||||
| 				<span | ||||
| 					class="inline-flex items-center px-3 rounded-r-md border border-gray-300 bg-gray-50 text-gray-500" | ||||
| 					>€</span | ||||
| 				> | ||||
| 			</div> | ||||
| 			{#if !is_amount_valid} | ||||
| 				<span | ||||
| 					class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1" | ||||
| 				> | ||||
| 					{$_("donation-amount-must-be-greater-that-0-00eur")} | ||||
| 				</span> | ||||
| 			{/if} | ||||
| 		</div> | ||||
| 		<div class="mt-2 w-full"> | ||||
| 			<label for="token" class="block font-semibold text-gray-700" | ||||
| 				>{$_("paid-amount")}</label | ||||
| 			> | ||||
| 			<div | ||||
| 				class="inline-flex border-gray-300 border rounded-l-md rounded-r-md bg-gray-50 text-gray-500 w-full" | ||||
| 			> | ||||
| 				<input | ||||
| 					autocomplete="off" | ||||
| 					class:border-red-500={!is_amount_valid} | ||||
| 					class:focus:border-red-500={!is_amount_valid} | ||||
| 					class:focus:ring-red-500={!is_amount_valid} | ||||
| 					bind:value={paid_amount_input} | ||||
| 					type="number" | ||||
| 					step="0.01" | ||||
| 					name="donation_amount_eur" | ||||
| 					class="focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-l-md sm:text-sm p-2" | ||||
| 					placeholder="2.00" | ||||
| 				/> | ||||
| 				<button | ||||
| 					on:click={() => { | ||||
| 						paid_amount_input = paid_amount_input = ( | ||||
| 							original_data.amount / 100 | ||||
| 						).toFixed(2); | ||||
| 					}} | ||||
| 					class="inline-flex items-center p-r-2 text-indigo-300 hover:text-indigo-700 text-sm" | ||||
| 					>MAX</button | ||||
| 				> | ||||
| 				<span | ||||
| 					class="inline-flex items-center px-3 rounded-r-md border border-gray-300 bg-gray-50 text-gray-500 text-sm" | ||||
| 					>€</span | ||||
| 				> | ||||
| 			</div> | ||||
| 			{#if !is_paid_amount_valid} | ||||
| 				<span | ||||
| 					class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1" | ||||
| 				> | ||||
| 					{$_("payment-amount-must-be-greater-than-0-00eur")} | ||||
| 				</span> | ||||
| 			{/if} | ||||
| 		</div> | ||||
| 	</section> | ||||
| {:catch error} | ||||
| 	<PromiseError {error} /> | ||||
| {/await} | ||||
							
								
								
									
										21
									
								
								src/components/donations/DonationDonor.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/components/donations/DonationDonor.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| <script> | ||||
| 	import { _ } from "svelte-i18n"; | ||||
| 	export let donor; | ||||
| </script> | ||||
|  | ||||
| {#if !donor || donor.firstname == 0} | ||||
| 	<span | ||||
| 		class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800 border border-current" | ||||
| 		>{$_('anonymer_sponsor')}</span | ||||
| 	> | ||||
| {:else} | ||||
| 	<div class="flex items-center"> | ||||
| 		<a | ||||
| 			href="../donors/{donor.id}" | ||||
| 			class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800 border border-current" | ||||
| 			>{donor.firstname} | ||||
| 			{#if donor.middlename}{donor.middlename}{/if} | ||||
| 			{donor.lastname}</a | ||||
| 		> | ||||
| 	</div> | ||||
| {/if} | ||||
							
								
								
									
										18
									
								
								src/components/donations/DonationRunner.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/components/donations/DonationRunner.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   export let runner; | ||||
| </script> | ||||
|  | ||||
| {#if !runner || runner.firstname == 0} | ||||
|   {$_("fixed-donation")} | ||||
| {:else} | ||||
|   <div class="text-sm font-medium text-gray-900"> | ||||
|     <a | ||||
|       href="../runners/{runner.id}" | ||||
|       class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800 border border-current" | ||||
|       >{runner.firstname} | ||||
|       {#if runner.middlename}{runner.middlename}{/if} | ||||
|       {runner.lastname}</a | ||||
|     > | ||||
|   </div> | ||||
| {/if} | ||||
							
								
								
									
										16
									
								
								src/components/donations/DonationStatus.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/components/donations/DonationStatus.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   export let status; | ||||
| </script> | ||||
|  | ||||
| {#if status == "PAID"} | ||||
|   <span | ||||
|     class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full border border-current bg-green-100 text-green-800" | ||||
|     >{$_("paid")}</span | ||||
|   > | ||||
| {:else} | ||||
|   <span | ||||
|     class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full border border-current bg-red-100 text-red-800" | ||||
|     >{$_("open")}</span | ||||
|   > | ||||
| {/if} | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user