This commit is contained in:
bakhirev 2025-06-27 09:29:37 +03:00
parent c38a04e02f
commit 7455e8555b
48 changed files with 698 additions and 124 deletions

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#7F9BE0"><path d="M480-480q-66 0-113-47t-47-113q0-66 47-113t113-47q66 0 113 47t47 113q0 66-47 113t-113 47ZM160-160v-112q0-34 17.5-62.5T224-378q62-31 126-46.5T480-440q66 0 130 15.5T736-378q29 15 46.5 43.5T800-272v112H160Zm80-80h480v-32q0-11-5.5-20T700-306q-54-27-109-40.5T480-360q-56 0-111 13.5T260-306q-9 5-14.5 14t-5.5 20v32Zm240-320q33 0 56.5-23.5T560-640q0-33-23.5-56.5T480-720q-33 0-56.5 23.5T400-640q0 33 23.5 56.5T480-560Zm0-80Zm0 400Z"/></svg>

After

Width:  |  Height:  |  Size: 550 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#146825"><path d="M720-400v-120H600v-80h120v-120h80v120h120v80H800v120h-80Zm-360-80q-66 0-113-47t-47-113q0-66 47-113t113-47q66 0 113 47t47 113q0 66-47 113t-113 47ZM40-160v-112q0-34 17.5-62.5T104-378q62-31 126-46.5T360-440q66 0 130 15.5T616-378q29 15 46.5 43.5T680-272v112H40Zm80-80h480v-32q0-11-5.5-20T580-306q-54-27-109-40.5T360-360q-56 0-111 13.5T140-306q-9 5-14.5 14t-5.5 20v32Zm240-320q33 0 56.5-23.5T440-640q0-33-23.5-56.5T360-720q-33 0-56.5 23.5T280-640q0 33 23.5 56.5T360-560Zm0-80Zm0 400Z"/></svg>

After

Width:  |  Height:  |  Size: 605 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#2C3959"><path d="M40-160v-112q0-34 17.5-62.5T104-378q62-31 126-46.5T360-440q66 0 130 15.5T616-378q29 15 46.5 43.5T680-272v112H40Zm720 0v-120q0-44-24.5-84.5T666-434q51 6 96 20.5t84 35.5q36 20 55 44.5t19 53.5v120H760ZM360-480q-66 0-113-47t-47-113q0-66 47-113t113-47q66 0 113 47t47 113q0 66-47 113t-113 47Zm400-160q0 66-47 113t-113 47q-11 0-28-2.5t-28-5.5q27-32 41.5-71t14.5-81q0-42-14.5-81T544-792q14-5 28-6.5t28-1.5q66 0 113 47t47 113ZM120-240h480v-32q0-11-5.5-20T580-306q-54-27-109-40.5T360-360q-56 0-111 13.5T140-306q-9 5-14.5 14t-5.5 20v32Zm240-320q33 0 56.5-23.5T440-640q0-33-23.5-56.5T360-720q-33 0-56.5 23.5T280-640q0 33 23.5 56.5T360-560Zm0 320Zm0-400Z"/></svg>

After

Width:  |  Height:  |  Size: 768 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#ED675F"><path d="M640-520v-80h240v80H640Zm-280 40q-66 0-113-47t-47-113q0-66 47-113t113-47q66 0 113 47t47 113q0 66-47 113t-113 47ZM40-160v-112q0-34 17.5-62.5T104-378q62-31 126-46.5T360-440q66 0 130 15.5T616-378q29 15 46.5 43.5T680-272v112H40Zm80-80h480v-32q0-11-5.5-20T580-306q-54-27-109-40.5T360-360q-56 0-111 13.5T140-306q-9 5-14.5 14t-5.5 20v32Zm240-320q33 0 56.5-23.5T440-640q0-33-23.5-56.5T360-720q-33 0-56.5 23.5T280-640q0 33 23.5 56.5T360-560Zm0-80Zm0 400Z"/></svg>

After

Width:  |  Height:  |  Size: 572 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#146825"><path d="M200-120q-33 0-56.5-23.5T120-200v-500q0-14 4.5-26.5T138-750l56-68q9-11 20.5-16.5T240-840h480q14 0 25.5 5.5T766-818l56 68q9 11 13.5 23.5T840-700v500q0 33-23.5 56.5T760-120H200Zm16-600h528l-34-40H250l-34 40Zm-16 520h560v-440H200v440Zm382-78 142-142-142-142-58 58 84 84-84 84 58 58Zm-202 0 58-58-84-84 84-84-58-58-142 142 142 142Zm-180 78v-440 440Z"/></svg>

After

Width:  |  Height:  |  Size: 472 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#7F9BE0"><path d="M620-163 450-333l56-56 114 114 226-226 56 56-282 282Zm220-397h-80v-200h-80v120H280v-120h-80v560h240v80H200q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h167q11-35 43-57.5t70-22.5q40 0 71.5 22.5T594-840h166q33 0 56.5 23.5T840-760v200ZM480-760q17 0 28.5-11.5T520-800q0-17-11.5-28.5T480-840q-17 0-28.5 11.5T440-800q0 17 11.5 28.5T480-760Z"/></svg>

After

Width:  |  Height:  |  Size: 468 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#2C3959"><path d="m397-115-99-184-184-99 71-70 145 25 102-102-317-135 84-86 385 68 124-124q23-23 57-23t57 23q23 23 23 56.5T822-709L697-584l68 384-85 85-136-317-102 102 26 144-71 71Z"/></svg>

After

Width:  |  Height:  |  Size: 290 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="#ED675F"><path d="M47,109.6951a111.539,111.539,0,0,1,54.7653,14.315A145.3113,145.3113,0,0,0,40.96,173.848,17.1,17.1,0,0,0,68.978,193.46a111.435,111.435,0,0,1,35.6181-32.3663,145.66,145.66,0,0,0-4.4184,68.984,17.0981,17.0981,0,0,0,33.6768-5.9375,111.4816,111.4816,0,0,1,7.7964-64.7165,279.0741,279.0741,0,0,1,64.3639,143.7084A178.41,178.41,0,0,0,100.74,387.0738c12.3052,5.6661,21.644,12.3505,28.6426,17.4461,10.437,7.5981,12.6079,9.1845,22.0151,9.1845,9.4444,0,11.6153-1.5864,22.0523-9.1845,14.8345-10.79,39.6328-28.8155,82.4292-28.8155s67.5947,18.0073,82.42,28.7783c10.502,7.6353,12.6729,9.2217,22.145,9.2217,9.4536,0,11.6338-1.5864,22.1079-9.1938,7.06-5.13,16.4789-11.8727,28.9222-17.562a178.5214,178.5214,0,0,0-63.56-65.47,208.7472,208.7472,0,0,1,40.1-82.55,85.4981,85.4981,0,0,1,4.1307,44.188,17.0954,17.0954,0,0,0,13.87,19.8072,17.2845,17.2845,0,0,0,2.9874.269,17.1113,17.1113,0,0,0,16.82-14.1387,120.2668,120.2668,0,0,0-1.1829-47.8595,85.9878,85.9878,0,0,1,19.9788,19.9347,17.1,17.1,0,1,0,28.0176-19.6123,119.8851,119.8851,0,0,0-43.3913-37.9652A85.797,85.797,0,0,1,465,195.5755a17.0982,17.0982,0,0,0,0-34.1963,119.6328,119.6328,0,0,0-64.04,18.513A119.6763,119.6763,0,0,0,359.1084,127.99a17.1,17.1,0,1,0-19.6123,28.0175,86.1317,86.1317,0,0,1,19.9393,19.9881,120.6788,120.6788,0,0,0-47.8548-1.1922,17.0981,17.0981,0,1,0,5.9375,33.6768,85.6208,85.6208,0,0,1,47.0141,5.0585,242.7527,242.7527,0,0,0-48.1367,92.9056A178.2713,178.2713,0,0,0,256,295.9007c-5.5258,0-10.9948.2737-16.4081.77a312.9262,312.9262,0,0,0-74.25-161.9592,111.3187,111.3187,0,0,1,66.76-8.8181,17.0981,17.0981,0,0,0,5.9375-33.6768A145.656,145.656,0,0,0,169.06,96.635a111.4162,111.4162,0,0,1,32.371-35.6274A17.1,17.1,0,0,0,181.8184,32.99a145.2835,145.2835,0,0,0-52.78,67.66A145.3029,145.3029,0,0,0,47,75.4989a17.0981,17.0981,0,0,0,0,34.1962Z"/><path d="M465,409.9007c-31.7007,0-48.6875,12.3482-62.3345,22.2656-12.0884,8.7857-21.644,15.7344-42.2212,15.7344s-30.1328-6.9394-42.2212-15.7344c-13.6562-9.9174-30.643-22.2656-62.3437-22.2656-31.6821,0-48.66,12.3482-62.2974,22.2656-12.0791,8.795-21.6255,15.7344-42.1841,15.7344-20.54,0-30.0678-6.9394-42.1376-15.7251C95.6226,422.2489,78.6636,409.9007,47,409.9007a17.0982,17.0982,0,0,0,0,34.1963c20.5308,0,30.0586,6.94,42.1284,15.7251,13.6377,9.9268,30.606,22.2749,62.2695,22.2749,31.6822,0,48.66-12.3481,62.2974-22.2656,12.0791-8.7949,21.6255-15.7344,42.1841-15.7344,20.5864,0,30.1421,6.9487,42.23,15.7344,13.6562,9.9175,30.643,22.2656,62.3344,22.2656,31.7007,0,48.6875-12.3481,62.3345-22.2656C434.8672,451.0457,444.4229,444.097,465,444.097a17.0982,17.0982,0,0,0,0-34.1963Z"/></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="#146825"><path d="M47,109.6951a111.539,111.539,0,0,1,54.7653,14.315A145.3113,145.3113,0,0,0,40.96,173.848,17.1,17.1,0,0,0,68.978,193.46a111.435,111.435,0,0,1,35.6181-32.3663,145.66,145.66,0,0,0-4.4184,68.984,17.0981,17.0981,0,0,0,33.6768-5.9375,111.4816,111.4816,0,0,1,7.7964-64.7165,279.0741,279.0741,0,0,1,64.3639,143.7084A178.41,178.41,0,0,0,100.74,387.0738c12.3052,5.6661,21.644,12.3505,28.6426,17.4461,10.437,7.5981,12.6079,9.1845,22.0151,9.1845,9.4444,0,11.6153-1.5864,22.0523-9.1845,14.8345-10.79,39.6328-28.8155,82.4292-28.8155s67.5947,18.0073,82.42,28.7783c10.502,7.6353,12.6729,9.2217,22.145,9.2217,9.4536,0,11.6338-1.5864,22.1079-9.1938,7.06-5.13,16.4789-11.8727,28.9222-17.562a178.5214,178.5214,0,0,0-63.56-65.47,208.7472,208.7472,0,0,1,40.1-82.55,85.4981,85.4981,0,0,1,4.1307,44.188,17.0954,17.0954,0,0,0,13.87,19.8072,17.2845,17.2845,0,0,0,2.9874.269,17.1113,17.1113,0,0,0,16.82-14.1387,120.2668,120.2668,0,0,0-1.1829-47.8595,85.9878,85.9878,0,0,1,19.9788,19.9347,17.1,17.1,0,1,0,28.0176-19.6123,119.8851,119.8851,0,0,0-43.3913-37.9652A85.797,85.797,0,0,1,465,195.5755a17.0982,17.0982,0,0,0,0-34.1963,119.6328,119.6328,0,0,0-64.04,18.513A119.6763,119.6763,0,0,0,359.1084,127.99a17.1,17.1,0,1,0-19.6123,28.0175,86.1317,86.1317,0,0,1,19.9393,19.9881,120.6788,120.6788,0,0,0-47.8548-1.1922,17.0981,17.0981,0,1,0,5.9375,33.6768,85.6208,85.6208,0,0,1,47.0141,5.0585,242.7527,242.7527,0,0,0-48.1367,92.9056A178.2713,178.2713,0,0,0,256,295.9007c-5.5258,0-10.9948.2737-16.4081.77a312.9262,312.9262,0,0,0-74.25-161.9592,111.3187,111.3187,0,0,1,66.76-8.8181,17.0981,17.0981,0,0,0,5.9375-33.6768A145.656,145.656,0,0,0,169.06,96.635a111.4162,111.4162,0,0,1,32.371-35.6274A17.1,17.1,0,0,0,181.8184,32.99a145.2835,145.2835,0,0,0-52.78,67.66A145.3029,145.3029,0,0,0,47,75.4989a17.0981,17.0981,0,0,0,0,34.1962Z"/><path d="M465,409.9007c-31.7007,0-48.6875,12.3482-62.3345,22.2656-12.0884,8.7857-21.644,15.7344-42.2212,15.7344s-30.1328-6.9394-42.2212-15.7344c-13.6562-9.9174-30.643-22.2656-62.3437-22.2656-31.6821,0-48.66,12.3482-62.2974,22.2656-12.0791,8.795-21.6255,15.7344-42.1841,15.7344-20.54,0-30.0678-6.9394-42.1376-15.7251C95.6226,422.2489,78.6636,409.9007,47,409.9007a17.0982,17.0982,0,0,0,0,34.1963c20.5308,0,30.0586,6.94,42.1284,15.7251,13.6377,9.9268,30.606,22.2749,62.2695,22.2749,31.6822,0,48.66-12.3481,62.2974-22.2656,12.0791-8.7949,21.6255-15.7344,42.1841-15.7344,20.5864,0,30.1421,6.9487,42.23,15.7344,13.6562,9.9175,30.643,22.2656,62.3344,22.2656,31.7007,0,48.6875-12.3481,62.3345-22.2656C434.8672,451.0457,444.4229,444.097,465,444.097a17.0982,17.0982,0,0,0,0-34.1963Z"/></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#2C3959"><path d="m397-115-99-184-184-99 71-70 145 25 102-102-317-135 84-86 385 68 124-124q23-23 57-23t57 23q23 23 23 56.5T822-709L697-584l68 384-85 85-136-317-102 102 26 144-71 71Z"/></svg>

After

Width:  |  Height:  |  Size: 290 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="#ED675F"><path d="M47,109.6951a111.539,111.539,0,0,1,54.7653,14.315A145.3113,145.3113,0,0,0,40.96,173.848,17.1,17.1,0,0,0,68.978,193.46a111.435,111.435,0,0,1,35.6181-32.3663,145.66,145.66,0,0,0-4.4184,68.984,17.0981,17.0981,0,0,0,33.6768-5.9375,111.4816,111.4816,0,0,1,7.7964-64.7165,279.0741,279.0741,0,0,1,64.3639,143.7084A178.41,178.41,0,0,0,100.74,387.0738c12.3052,5.6661,21.644,12.3505,28.6426,17.4461,10.437,7.5981,12.6079,9.1845,22.0151,9.1845,9.4444,0,11.6153-1.5864,22.0523-9.1845,14.8345-10.79,39.6328-28.8155,82.4292-28.8155s67.5947,18.0073,82.42,28.7783c10.502,7.6353,12.6729,9.2217,22.145,9.2217,9.4536,0,11.6338-1.5864,22.1079-9.1938,7.06-5.13,16.4789-11.8727,28.9222-17.562a178.5214,178.5214,0,0,0-63.56-65.47,208.7472,208.7472,0,0,1,40.1-82.55,85.4981,85.4981,0,0,1,4.1307,44.188,17.0954,17.0954,0,0,0,13.87,19.8072,17.2845,17.2845,0,0,0,2.9874.269,17.1113,17.1113,0,0,0,16.82-14.1387,120.2668,120.2668,0,0,0-1.1829-47.8595,85.9878,85.9878,0,0,1,19.9788,19.9347,17.1,17.1,0,1,0,28.0176-19.6123,119.8851,119.8851,0,0,0-43.3913-37.9652A85.797,85.797,0,0,1,465,195.5755a17.0982,17.0982,0,0,0,0-34.1963,119.6328,119.6328,0,0,0-64.04,18.513A119.6763,119.6763,0,0,0,359.1084,127.99a17.1,17.1,0,1,0-19.6123,28.0175,86.1317,86.1317,0,0,1,19.9393,19.9881,120.6788,120.6788,0,0,0-47.8548-1.1922,17.0981,17.0981,0,1,0,5.9375,33.6768,85.6208,85.6208,0,0,1,47.0141,5.0585,242.7527,242.7527,0,0,0-48.1367,92.9056A178.2713,178.2713,0,0,0,256,295.9007c-5.5258,0-10.9948.2737-16.4081.77a312.9262,312.9262,0,0,0-74.25-161.9592,111.3187,111.3187,0,0,1,66.76-8.8181,17.0981,17.0981,0,0,0,5.9375-33.6768A145.656,145.656,0,0,0,169.06,96.635a111.4162,111.4162,0,0,1,32.371-35.6274A17.1,17.1,0,0,0,181.8184,32.99a145.2835,145.2835,0,0,0-52.78,67.66A145.3029,145.3029,0,0,0,47,75.4989a17.0981,17.0981,0,0,0,0,34.1962Z"/><path d="M465,409.9007c-31.7007,0-48.6875,12.3482-62.3345,22.2656-12.0884,8.7857-21.644,15.7344-42.2212,15.7344s-30.1328-6.9394-42.2212-15.7344c-13.6562-9.9174-30.643-22.2656-62.3437-22.2656-31.6821,0-48.66,12.3482-62.2974,22.2656-12.0791,8.795-21.6255,15.7344-42.1841,15.7344-20.54,0-30.0678-6.9394-42.1376-15.7251C95.6226,422.2489,78.6636,409.9007,47,409.9007a17.0982,17.0982,0,0,0,0,34.1963c20.5308,0,30.0586,6.94,42.1284,15.7251,13.6377,9.9268,30.606,22.2749,62.2695,22.2749,31.6822,0,48.66-12.3481,62.2974-22.2656,12.0791-8.7949,21.6255-15.7344,42.1841-15.7344,20.5864,0,30.1421,6.9487,42.23,15.7344,13.6562,9.9175,30.643,22.2656,62.3344,22.2656,31.7007,0,48.6875-12.3481,62.3345-22.2656C434.8672,451.0457,444.4229,444.097,465,444.097a17.0982,17.0982,0,0,0,0-34.1963Z"/></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="#146825"><path d="M47,109.6951a111.539,111.539,0,0,1,54.7653,14.315A145.3113,145.3113,0,0,0,40.96,173.848,17.1,17.1,0,0,0,68.978,193.46a111.435,111.435,0,0,1,35.6181-32.3663,145.66,145.66,0,0,0-4.4184,68.984,17.0981,17.0981,0,0,0,33.6768-5.9375,111.4816,111.4816,0,0,1,7.7964-64.7165,279.0741,279.0741,0,0,1,64.3639,143.7084A178.41,178.41,0,0,0,100.74,387.0738c12.3052,5.6661,21.644,12.3505,28.6426,17.4461,10.437,7.5981,12.6079,9.1845,22.0151,9.1845,9.4444,0,11.6153-1.5864,22.0523-9.1845,14.8345-10.79,39.6328-28.8155,82.4292-28.8155s67.5947,18.0073,82.42,28.7783c10.502,7.6353,12.6729,9.2217,22.145,9.2217,9.4536,0,11.6338-1.5864,22.1079-9.1938,7.06-5.13,16.4789-11.8727,28.9222-17.562a178.5214,178.5214,0,0,0-63.56-65.47,208.7472,208.7472,0,0,1,40.1-82.55,85.4981,85.4981,0,0,1,4.1307,44.188,17.0954,17.0954,0,0,0,13.87,19.8072,17.2845,17.2845,0,0,0,2.9874.269,17.1113,17.1113,0,0,0,16.82-14.1387,120.2668,120.2668,0,0,0-1.1829-47.8595,85.9878,85.9878,0,0,1,19.9788,19.9347,17.1,17.1,0,1,0,28.0176-19.6123,119.8851,119.8851,0,0,0-43.3913-37.9652A85.797,85.797,0,0,1,465,195.5755a17.0982,17.0982,0,0,0,0-34.1963,119.6328,119.6328,0,0,0-64.04,18.513A119.6763,119.6763,0,0,0,359.1084,127.99a17.1,17.1,0,1,0-19.6123,28.0175,86.1317,86.1317,0,0,1,19.9393,19.9881,120.6788,120.6788,0,0,0-47.8548-1.1922,17.0981,17.0981,0,1,0,5.9375,33.6768,85.6208,85.6208,0,0,1,47.0141,5.0585,242.7527,242.7527,0,0,0-48.1367,92.9056A178.2713,178.2713,0,0,0,256,295.9007c-5.5258,0-10.9948.2737-16.4081.77a312.9262,312.9262,0,0,0-74.25-161.9592,111.3187,111.3187,0,0,1,66.76-8.8181,17.0981,17.0981,0,0,0,5.9375-33.6768A145.656,145.656,0,0,0,169.06,96.635a111.4162,111.4162,0,0,1,32.371-35.6274A17.1,17.1,0,0,0,181.8184,32.99a145.2835,145.2835,0,0,0-52.78,67.66A145.3029,145.3029,0,0,0,47,75.4989a17.0981,17.0981,0,0,0,0,34.1962Z"/><path d="M465,409.9007c-31.7007,0-48.6875,12.3482-62.3345,22.2656-12.0884,8.7857-21.644,15.7344-42.2212,15.7344s-30.1328-6.9394-42.2212-15.7344c-13.6562-9.9174-30.643-22.2656-62.3437-22.2656-31.6821,0-48.66,12.3482-62.2974,22.2656-12.0791,8.795-21.6255,15.7344-42.1841,15.7344-20.54,0-30.0678-6.9394-42.1376-15.7251C95.6226,422.2489,78.6636,409.9007,47,409.9007a17.0982,17.0982,0,0,0,0,34.1963c20.5308,0,30.0586,6.94,42.1284,15.7251,13.6377,9.9268,30.606,22.2749,62.2695,22.2749,31.6822,0,48.66-12.3481,62.2974-22.2656,12.0791-8.7949,21.6255-15.7344,42.1841-15.7344,20.5864,0,30.1421,6.9487,42.23,15.7344,13.6562,9.9175,30.643,22.2656,62.3344,22.2656,31.7007,0,48.6875-12.3481,62.3345-22.2656C434.8672,451.0457,444.4229,444.097,465,444.097a17.0982,17.0982,0,0,0,0-34.1963Z"/></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View file

@ -25,6 +25,7 @@ body,
.splash_screen,
.progress_bar,
.card_with_icon,
.card_with_icon_scoring,
.table_cell,
.table_header_cell,
.main_wrapper_white,
@ -35,6 +36,7 @@ body,
.ui_kit_select_value,
.modal_window,
.pie_chart,
.line_chart_item,
.tempo_task_commits,
.tempo_task_tag,
.tempo_task_hours,
@ -51,6 +53,10 @@ body,
.header_title,
.header_filters_input,
.header_filters,
.hours_chart_day_time,
.hours_chart_day_name,
.hours_chart_legend_title,
.switch_item_title,
.sidebar_item_title,
.paginator_text,
@ -72,17 +78,31 @@ body,
.description_text,
.description_text > span,
.description_list,
.tempo_header_title,
.tempo_header_day,
.tempo_task_title,
.modal_window_title,
.nothing_found_text,
.recommendations_modal_title,
.recommendations_modal_sub_title,
.nothing_found_title,
.achievement_title,
.achievement_description {
color: var(--theme-font);
}
.page_wrapper,
.ui_kit_tags_item,
.card_with_icon,
.main_wrapper_white,
.hours_chart_hour,
.page_wrapper,
.pie_chart,
.table_row {
.table_row,
.tempo_header,
.tempo_author,
.tempo_column,
.tempo_task,
.tempo_task_header {
border-color: var(--theme-border);
}
@ -141,11 +161,14 @@ body,
}
.pie_chart_icon,
.pie_chart_color,
.line_chart_item {
.pie_chart_color {
filter: grayscale(0.3);
}
.line_chart,
.line_chart_item,
.line_chart_item:last-child,
.hours_chart_hour,
.main_wrapper_white,
.pie_chart {
border-radius: 0;
@ -153,8 +176,22 @@ body,
.ui_kit_button,
.ui_kit_select_value,
.line_chart_item,
.tempo_task_tag,
.tempo_task_commits,
.tempo_task_hours {
border: 1px solid var(--theme-border);
}
.hours_chart_legend_work,
.hours_chart_legend_weekend,
.hours_chart_legend_count,
.ui_kit_button_link,
.line_chart_item:has(.line_chart_sub_item) {
border: none;
}
.tempo_task_icon {
background-color: var(--theme-font);
border-radius: 8px;
}

View file

@ -98,8 +98,12 @@
return parameters;
}
function getElementById(id) {
return document.getElementById(id);
}
function getStyleById(id) {
return document.getElementById(id).style;
return getElementById(id).style;
}
function init() {
@ -112,6 +116,7 @@
if (parameters.sidebarColor) {
sidebar.backgroundColor = `#${parameters.sidebarColor}`;
}
if (parameters.width) {
sidebar.width = `${parameters.width}px`;
getStyleById('frame').width = `calc(100vw - ${parameters.width}px)`;
@ -131,6 +136,10 @@
if (parameters.title) {
document.title = parameters.title;
}
const mode = parameters.mode || 'white';
const url = `http://localhost:3006/?dump=./test.txt&theme=./themes/${mode}.css`;
getElementById('frame').setAttribute('src', url);
}
init();

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

View file

@ -87,6 +87,11 @@
href="./demo.html?sidebarImage=./cs/sidebar.png&sidebarColor=001529&width=200&height=0&title=CodeScoring">
CodeScoring
</a>
<a
target="_blank"
href="./demo.html?headerImage=./grafana/header.png&headerColor=000&height=46&width=0&title=Grafana&mode=dark">
Dark mode
</a>
</nav>
</main>
</body>

View file

@ -6,7 +6,7 @@ import IOption from '../interfaces/Option';
import style from '../styles/index.module.scss';
interface UiKitSelectListProps {
value: any;
value: any; // TODO: remove me
options: IOption[];
search?: string;
keyCode?: string;
@ -16,7 +16,6 @@ interface UiKitSelectListProps {
}
function UiKitSelectList({
value,
options,
search,
keyCode,
@ -26,7 +25,6 @@ function UiKitSelectList({
}: UiKitSelectListProps) {
const [selectedIndex, setSelectedIndex] = useState<number>(-1);
console.log(value);
const searchText = search ? search.toLowerCase() : '';
const searchResult = searchText
? options?.filter((option: any) => option?._textForSearch?.indexOf(searchText) !== -1)

View file

@ -11,6 +11,9 @@ interface DayEvent {
firstDay: Set<string> | undefined;
lastDay: Set<string> | undefined;
release: Set<string> | undefined;
vacationStart: Set<string> | undefined;
vacationEnd: Set<string> | undefined;
travel: Set<string> | undefined;
}
interface DayInfoProps {
@ -33,8 +36,11 @@ function DayInfo({ timestamp, events }: DayInfoProps): React.ReactElement {
taskNumber += Object.keys(tasks).length;
let suffix = '';
if (events?.firstDay?.has(author)) suffix = t('page.team.month.first');
if (events?.lastDay?.has(author)) suffix = t('page.team.month.last');
if (events?.vacationStart?.has(author)) suffix = t('page.team.month.vacation.first');
if (events?.vacationEnd?.has(author)) suffix = t('page.team.month.vacation.last');
if (events?.firstDay?.has(author)) suffix = t('page.team.month.work.first');
if (events?.lastDay?.has(author)) suffix = t('page.team.month.work.last');
if (events?.travel?.has(author)) suffix = t('page.team.month.travel');
return (
<div

View file

@ -1,4 +1,4 @@
import React, { useEffect, useRef, useState } from 'react';
import React from 'react';
import Column from './components/Column';
import style from './styles/index.module.scss';
@ -14,16 +14,6 @@ function Tempo({
author,
order,
}: ITempoProps) {
const [customStyle, setCustomStyle] = useState<any>({});
const ref = useRef() as React.MutableRefObject<HTMLDivElement>;
useEffect(() => {
const element = ref?.current;
if (element?.clientWidth === element?.scrollWidth) {
setCustomStyle({ overflowX: 'hidden' });
}
}, []);
const columns = days.map((dayInfo: any) => (
<Column
key={dayInfo?.timestamp}
@ -35,8 +25,6 @@ function Tempo({
return (
<div
ref={ref}
style={customStyle}
className={`${style.tempo_wrapper} scroll_x`}
onTouchStart={(event) => event.stopPropagation()}
onMouseDown={(event) => event.stopPropagation()}

View file

@ -1,4 +1,5 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { IUiKitWrapperProps } from './Wrapper';
import style from '../styles/tags.module.scss';
@ -10,9 +11,10 @@ interface IUiKitTagProps extends IUiKitWrapperProps {
function UiKitTag({
value,
}: IUiKitTagProps) {
const { t } = useTranslation();
return (
<div className={style.ui_kit_tags_item}>
{value}
{t(`${value}`) || value}
</div>
);
}

View file

@ -18,31 +18,59 @@ interface DayProps {
filters: Filters;
}
function DayIcon({ src }: { src: string }) {
return (
<img
className={style.year_chart_month_body_day_icon}
src={src}
/>
);
function getTextFromSet(list: any) {
return Array.from(list).join(', ');
}
function getText(filters: Filters, events?: DayEvent) {
function getContent(filters: Filters, events?: DayEvent) {
let image = '';
let title = '';
if (filters.absence) {
if (events?.vacationStart) {
image = 'vacation_start';
title = getTextFromSet(events?.vacationStart);
}
if (events?.vacationEnd) {
image = 'vacation_end';
title = getTextFromSet(events?.vacationEnd);
}
if (events?.travel) {
image = 'travel';
title = getTextFromSet(events?.travel);
}
}
if (filters.release && events?.release) {
image = 'release';
title = getTextFromSet(events?.release);
}
if (filters.firstLastDays) {
if (events?.firstDay && !events?.lastDay) {
return (<DayIcon src="./assets/chart/person_add.svg" />);
image = 'person_add';
title = getTextFromSet(events?.firstDay);
}
if (!events?.firstDay && events?.lastDay) {
return (<DayIcon src="./assets/chart/person_remove.svg" />);
image = 'person_remove';
title = getTextFromSet(events?.lastDay);
}
if (events?.firstDay && events?.lastDay) {
return (<DayIcon src="./assets/chart/person_add_remove.svg" />);
image = 'person_add_remove';
title = getTextFromSet(events?.firstDay);
}
}
if (filters.release && events?.release) {
return (<DayIcon src="./assets/chart/release.svg" />);
}
return ' ';
const icon = image ? (
<img
className={style.year_chart_month_body_day_icon}
src={`./assets/chart/${image}.svg`}
/>
) : null;
title = title ? ` | ${title}` : title;
return [title, icon];
}
function getColorList(dayNumber: number, filters: Filters, dayInfo: DataGripDay) {
@ -75,11 +103,11 @@ function Day({
const backgroundColor = getColor(colorList, opacity);
const title = getDate(dayInfo.timestamp);
const text = getText(filters, events);
const [suffix, icon] = getContent(filters, events);
return (
<div
title={title}
title={`${title}${suffix}`}
id={`year_chart_day_${dayInfo?.timestamp}`}
className={style.year_chart_month_body_day}
style={{
@ -89,7 +117,7 @@ function Day({
dayInfoStore.toggle(dayInfo, [event.pageX, event.pageY]);
}}
>
{text || ' '}
{icon || ' '}
</div>
);
}

View file

@ -4,6 +4,9 @@ export interface DayEvent {
firstDay: Set<string> | undefined;
lastDay: Set<string> | undefined;
release: Set<string> | undefined;
vacationStart: Set<string> | undefined;
vacationEnd: Set<string> | undefined;
travel: Set<string> | undefined;
}
export type DayEvents = HashMap<DayEvent>;
@ -13,6 +16,9 @@ function getDayEvent(): DayEvent {
firstDay: undefined,
lastDay: undefined,
release: undefined,
vacationStart: undefined,
vacationEnd: undefined,
travel: undefined,
};
}
@ -34,6 +40,10 @@ function getCallback(property: string, name: string) {
function addByAuthor(events: DayEvents, authors: any[]) {
authors.forEach((user: any) => {
user?.country?.forEach((travel: any) => {
updateEvent(events, travel.timestamp, getCallback('travel', user.author));
});
if (user.isStaff) return;
updateEvent(events, user.firstCommit.timestamp, getCallback('firstDay', user.author));
@ -50,9 +60,21 @@ function addByRelease(events: DayEvents, releases: any[]) {
});
}
export function getEvents(statisticByAuthors: any[], statisticByRelease: any[]) {
function addByAbsence(events: DayEvents, absence: any[]) {
absence.forEach((item: any) => {
if (item.duration > 30) return;
updateEvent(events, item.timestamp.from, getCallback('vacationStart', item.author));
updateEvent(events, item.timestamp.to, getCallback('vacationEnd', item.author));
});
}
export function getEvents(
statisticByAuthors: any[],
dataGrip: any,
) {
const events = new Map();
addByAuthor(events, statisticByAuthors);
addByRelease(events, statisticByRelease);
addByRelease(events, dataGrip.release.statistic);
addByAbsence(events, dataGrip.absence.statistic);
return events;
}

View file

@ -1,6 +1,7 @@
export interface Filters {
release?: boolean;
firstLastDays?: boolean;
absence?: boolean;
types?: string[];
authors?: string[];
}

View file

@ -0,0 +1,77 @@
import { HashMap } from 'ts/interfaces/HashMap';
import ICommit from 'ts/interfaces/Commit';
import { ONE_DAY } from 'ts/helpers/formatter';
export interface DataGripAbsence {
author: string;
from: number;
to: number;
duration: number;
timestamp: {
from: string;
to: string;
}
}
export default class DataGripByAbsence {
lastCommitDate: HashMap<any> = new Map();
statistic: DataGripAbsence[] = [];
statisticByName: HashMap<DataGripAbsence[]> = new Map();
clear() {
this.lastCommitDate.clear();
this.statistic = [];
}
addCommit(commit: ICommit) {
const from = this.lastCommitDate.get(commit.author);
if (from) {
this.#update(from, commit);
} else {
this.#add(commit);
}
}
#update(from: any, commit: ICommit) {
const to = commit.milliseconds;
let duration = ((to - from.milliseconds) / ONE_DAY) - 2;
if (commit.month === 0 && commit.dayInMonth <= 11) duration -= 10;
if (commit.month === 5 && commit.dayInMonth <= 11) duration -= 4;
this.#add(commit);
if (!duration || duration <= 7) return;
this.statistic.push({
author: commit.author,
from: from.milliseconds + ONE_DAY,
to: to - ONE_DAY,
timestamp: {
from: from.timestamp,
to: commit.timestamp,
},
duration,
});
}
#add(commit: ICommit) {
this.lastCommitDate.set(commit.author, {
milliseconds: commit.milliseconds,
timestamp: commit.timestamp,
});
}
updateTotalInfo(dataGripByAuthor: any) {
this.statistic = this.statistic
.filter((absence: DataGripAbsence) => !dataGripByAuthor.statisticByName[absence.author]?.isStaff)
.sort((a: DataGripAbsence, b: DataGripAbsence) => b.to - a.to);
this.statistic.forEach((absence: DataGripAbsence) => {
const statistic = this.statisticByName.get(absence.author) || [];
statistic.push(absence);
this.statisticByName.set(absence.author, statistic);
});
this.lastCommitDate.clear();
}
}

View file

@ -66,7 +66,12 @@ export default class DataGripByAuthor {
if (commit.timezone && statistic.lastCountry !== commit.timezone) {
statistic.lastTimezone = commit.timezone;
statistic.lastCountry = commit.country;
statistic.country.push({ country: commit.country, timezone: commit.timezone, from: commit.milliseconds });
statistic.country.push({
country: commit.country,
timezone: commit.timezone,
from: commit.milliseconds,
timestamp: commit.timestamp,
});
}
}
@ -116,8 +121,8 @@ export default class DataGripByAuthor {
#updateMoneyByMonth(commit: ICommit, key: string) {
const statistic = this.commits.get(commit.author).moneyByMonth[key];
if (statistic.alreadyAdded[commit.milliseconds]) return;
statistic.alreadyAdded[commit.milliseconds] = true;
if (statistic.alreadyAdded.has(commit.milliseconds)) return;
statistic.alreadyAdded.add(commit.milliseconds);
const isWorkDay = statistic.contract.workDaysInWeek[commit.day];
if (isWorkDay) {
@ -133,9 +138,7 @@ export default class DataGripByAuthor {
this.commits.get(commit.author).moneyByMonth[key] = {
workDay: isWorkDay ? 1 : 0,
weekDay: isWorkDay ? 0 : 1,
alreadyAdded: {
[commit.milliseconds]: true,
},
alreadyAdded: new Set([commit.milliseconds]),
contract,
};
}

View file

@ -106,7 +106,6 @@ export default class DataGripByMonth {
.map((dot: any) => {
dot.days = Array.from(dot.days.values());
dot.tasksNumber = Array.from(dot.tasksNumber).length;
console.log(Array.from(dot.usersNumber));
dot.usersNumber = Array
.from(dot.usersNumber) // @ts-ignore
.filter((name) => !dataGripByAuthor.statisticByName[name]?.isStaff)

View file

@ -1,9 +1,9 @@
import ICommit from 'ts/interfaces/Commit';
import IHashMap from 'ts/interfaces/HashMap';
import HashMap from 'ts/interfaces/HashMap';
import { increment } from 'ts/helpers/Math';
export default class DataGripByWeek {
commits: IHashMap<any> = {};
commits: HashMap<any> = new Map;
statistic: any = [];
@ -12,23 +12,23 @@ export default class DataGripByWeek {
}
clear() {
this.commits = {};
this.commits.clear();
this.statistic = [];
}
addCommit(commit: ICommit) {
if (this.commits.hasOwnProperty(commit.week)) {
this.#updateCommitByWeek(commit);
const statistic = this.commits.get(commit.week);
if (statistic) {
this.#updateCommitByWeek(statistic, commit);
} else {
this.#addCommitByWeek(commit);
}
}
#updateCommitByWeek(commit: ICommit) {
const statistic = this.commits[commit.week];
#updateCommitByWeek(statistic: any, commit: ICommit) {
statistic.commits += 1;
statistic.tasks[commit.task] = true;
statistic.timestamp.to = commit.timestamp;
if (commit.task) statistic.tasks.add(commit.task);
const setDefault = (s: any, v: string) => {
if (!s[v]) s[v] = {};
@ -46,21 +46,21 @@ export default class DataGripByWeek {
}
#addCommitByWeek(commit: ICommit) {
this.commits[commit.week] = {
this.commits.set(commit.week, {
commits: 1,
timestamp: { from: commit.timestamp },
tasks: { [commit.task]: true },
tasks: commit.task ? new Set([commit.task]) : new Set(),
types: { [commit.type]: 1 },
changes: { added: commit.added, changes: commit.changes, removed: commit.removed },
authors: { [commit.author]: { [commit.task]: true } },
workDays: { [commit.author]: { [commit.day]: true } },
typeByAuthor: { [commit.author]: { [commit.type]: 1 } },
};
});
}
updateTotalInfo(dataGripByAuthor: any) {
this.statistic = Object.values(this.commits)
this.statistic = Array.from(this.commits.values())
.map((dot: any) => {
const authors = {};
for (let name in dot.authors) authors[name] = Object.keys(dot.authors[name]).filter(v => v).length;
@ -95,7 +95,7 @@ export default class DataGripByWeek {
return {
...dot,
tasks: Object.keys(dot.tasks).filter(n => n).length,
tasks: dot.tasks.size,
authors,
workDays,
lazyDays,

View file

@ -20,6 +20,7 @@ import DataGripByTaskNumbers from './components/taskNumbers';
import DataGripByTaskNumbersDate from './components/taskNumbersDate';
import DataGripByCompany from './components/company';
import DataGripByCountry from './components/country';
import DataGripByAbsence from './components/absence';
class DataGrip {
firstLastCommit: any = new MinMaxCounter();
@ -60,6 +61,8 @@ class DataGrip {
taskNumbersDate: any = new DataGripByTaskNumbersDate();
absence: any = new DataGripByAbsence();
clear() {
this.firstLastCommit.clear();
this.author.clear();
@ -80,6 +83,7 @@ class DataGrip {
this.taskCodes.clear();
this.taskNumbers.clear();
this.taskNumbersDate.clear();
this.absence.clear();
}
addCommit(commit: ICommit | ISystemCommit, totalCommits: number) {
@ -99,6 +103,7 @@ class DataGrip {
this.taskCodes.addCommit(commit);
this.taskNumbers.addCommit(commit);
this.taskNumbersDate.addCommit(commit);
this.absence.addCommit(commit);
}
}
@ -120,6 +125,7 @@ class DataGrip {
this.country.updateTotalInfo(this.author);
this.taskCodes.updateTotalInfo(this.firstLastCommit.maxData, this.author);
this.taskNumbers.updateTotalInfo();
this.absence.updateTotalInfo(this.author);
}
}

View file

@ -11,7 +11,7 @@ export function getGithubPrInfo(text: string) {
.replace(' to ', '", "to": "')
.replace(' into ', '", "to": "');
const data = JSON.parse(`{"${json}"}`);
return [data['Merge pull request #'], data.in, data.from, data.to];
return [data['Merge pull request #'], data.in || '', data.from || '', data.to || ''];
}
/* "Merge branch 'J123456' into 'develop'" */

View file

@ -47,6 +47,10 @@ export function getTypeAndScope(message: string, task: string) {
let type = '';
let scope = '';
if (!message) {
return [type, scope];
}
let formattedMessage = message.replace(task, '').toLowerCase();
const messageParts = formattedMessage.split(':');

View file

@ -19,7 +19,7 @@ export interface ILog {
dayInMonth: number; // 9,
hours: number; // 12,
minutes: number; // 59,
month: number; // 1,
month: number; // 1 (февраль, нумерация начинается от 0)
year: number; // 2021,
timezone: string; // "+03:00",
timestamp: string; // "2021-02-09",

View file

@ -0,0 +1,98 @@
import React from 'react';
import { IPagination } from 'ts/interfaces/Pagination';
import { getDate } from 'ts/helpers/formatter';
import DataView from 'ts/components/DataView';
import Column from 'ts/components/Table/components/Column';
import { ColumnTypesEnum } from 'ts/components/Table/interfaces/Column';
import LineChart from 'ts/components/LineChart';
import getOptions from 'ts/components/LineChart/helpers/getOptions';
import UiKitTags from 'ts/components/UiKit/components/Tags';
import { getMax } from 'ts/pages/Common/helpers/getMax';
interface AbsenceProps {
response?: IPagination<any>;
updateSort?: Function;
rowsForExcel?: any[];
mode?: string;
}
export function Absence({ response, updateSort, rowsForExcel, mode }: AbsenceProps) {
if (!response) return null;
const durationChart = getOptions({ max: getMax(response, 'duration'), suffix: 'page.team.author.days' });
return (
<DataView
rowsForExcel={rowsForExcel}
rows={response.content}
sort={response.sort}
updateSort={updateSort}
mode={mode}
type={mode === 'print' ? 'cards' : undefined}
columnCount={mode === 'print' ? 3 : undefined}
>
<Column
isFixed
template={ColumnTypesEnum.STRING}
formatter={(row: any, index: number) => (index + 1)}
width={40}
/>
<Column
isFixed
template={ColumnTypesEnum.STRING}
properties="author"
title="page.team.pr.author"
minWidth={200}
/>
<Column
title="тип"
formatter={(row: any) => {
return row.duration > 40
? 'page.team.author.absence.transfer'
: 'page.team.author.absence.vacation';
}}
template={(value: string) => <UiKitTags value={value} />}
minWidth={200}
/>
<Column
template={ColumnTypesEnum.STRING}
title="page.team.author.absence.from"
properties="from"
minWidth={130}
formatter={getDate}
/>
<Column
template={ColumnTypesEnum.STRING}
title="page.team.author.absence.to"
properties="to"
minWidth={130}
formatter={getDate}
/>
<Column
template={ColumnTypesEnum.SHORT_NUMBER}
properties="duration"
/>
<Column
isSortable
title="page.team.author.absence.duration"
properties="duration"
minWidth={200}
template={(value: number) => (
<LineChart
options={durationChart}
value={value}
/>
)}
/>
</DataView>
);
}
Absence.defaultProps = {
response: undefined,
};
export default Absence;

View file

@ -0,0 +1,116 @@
import React from 'react';
import { IPagination } from 'ts/interfaces/Pagination';
import IHashMap from 'ts/interfaces/HashMap';
import { getDate } from 'ts/helpers/formatter';
import Table from 'ts/components/Table';
import Column from 'ts/components/Table/components/Column';
import { ColumnTypesEnum } from 'ts/components/Table/interfaces/Column';
import LineChart from 'ts/components/LineChart';
import getOptions from 'ts/components/LineChart/helpers/getOptions';
import UiKitTags from 'ts/components/UiKit/components/Tags';
import { getMax } from 'ts/pages/Common/helpers/getMax';
interface ViewProps {
rows?: any[];
max?: number;
year?: string;
}
export function View({ rows, max, year }: ViewProps) {
const durationChart = getOptions({ max, suffix: 'page.team.author.days' });
return (
<Table rows={rows}>
<Column
title={year}
formatter={(row: any) => {
return row.duration > 40
? 'page.team.author.absence.transfer'
: 'page.team.author.absence.vacation';
}}
template={(value: string) => <UiKitTags value={value}/>}
minWidth={200}
/>
<Column
template={ColumnTypesEnum.STRING}
properties="from"
minWidth={130}
formatter={getDate}
/>
<Column
template={ColumnTypesEnum.STRING}
properties="to"
minWidth={130}
formatter={getDate}
/>
<Column
template={ColumnTypesEnum.SHORT_NUMBER}
properties="duration"
/>
<Column
isSortable
properties="duration"
minWidth={200}
template={(value: number) => (
<LineChart
options={durationChart}
value={value}
/>
)}
/>
</Table>
);
}
function getGroups(rows: any[]) {
return rows.reduce((group: IHashMap<any>, row: any) => {
const year = row.timestamp.from.substring(0, 4);
if (!group[year]) group[year] = [];
group[year].push(row);
return group;
}, {});
}
interface AbsenceDetailsProps {
rows?: any[];
}
export function AbsenceDetails({ rows }: AbsenceDetailsProps) {
if (!rows || !rows?.length) return null;
const max = getMax({ content: rows } as IPagination<any>, 'duration');
const groups = getGroups(rows);
const sections = Object.entries(groups).reverse().map(([year, items]) => {
const limit = 6;
const formattedItems = items.length > limit
? items
.sort((a: any, b: any) => b.duration - a.duration)
.slice(0, limit - 1)
.sort((a: any, b: any) => b.from - a.from)
: items;
return (
<View
key={year}
max={max}
rows={formattedItems}
year={year}
/>
);
});
return (
<div style={{ maxWidth: 750 }}>
{sections}
</div>
);
}
AbsenceDetails.defaultProps = {
response: undefined,
};
export default AbsenceDetails;

View file

@ -16,6 +16,8 @@ import getOptions from 'ts/components/LineChart/helpers/getOptions';
import { getMax, getMaxByLength } from 'ts/pages/Common/helpers/getMax';
import AbsenceDetails from './AbsenceDetails';
interface ViewProps {
response?: IPagination<any>;
updateSort?: Function;
@ -40,16 +42,33 @@ export function View({ response, updateSort, rowsForExcel, mode }: ViewProps) {
const commitsChart = getOptions({ max: getMax(response, 'commits') });
const typeChart = getOptions({ order: dataGripStore.dataGrip.type.list });
const formattedRows = response.content.map((row: any) => {
const content = dataGripStore.dataGrip.absence.statisticByName.get(row.author) || [];
return { ...row, absence: content.length };
});
return (
<DataView
rowsForExcel={rowsForExcel}
rows={response.content}
rows={formattedRows}
sort={response.sort}
updateSort={updateSort}
mode={mode}
type={mode === 'print' ? 'cards' : undefined}
columnCount={mode === 'print' ? 3 : undefined}
>
<Column
isFixed
template={ColumnTypesEnum.DETAILS}
width={40}
properties="absence"
formatter={(row: any) => {
const content = dataGripStore.dataGrip.absence.statisticByName.get(row.author) || [];
return (
<AbsenceDetails rows={content} />
);
}}
/>
<Column
isFixed
template={ColumnTypesEnum.STRING}

View file

@ -17,12 +17,14 @@ import Recommendations from 'ts/components/Recommendations';
import Description from 'ts/components/Description';
import PieCharts from './components/PieCharts';
import View from './components/View';
import Absence from './components/Absence';
const Author = observer(({
mode,
}: ICommonPageProps): React.ReactElement | null => {
const { t } = useTranslation();
const rows = dataGripStore.dataGrip.author.statistic;
const absence = dataGripStore.dataGrip.absence.statistic;
if (!rows?.length) {
return mode !== 'print' ? (<NothingFound />) : null;
@ -38,9 +40,9 @@ const Author = observer(({
/>
)}
<br />
<br />
<PieCharts />
<br/>
<br/>
<PieCharts/>
<Title title="page.team.author.title"/>
<DataLoader
@ -52,7 +54,7 @@ const Author = observer(({
mode={mode}
rowsForExcel={rows}
/>
<Pagination />
<Pagination/>
</DataLoader>
<PageWrapper>
@ -67,6 +69,21 @@ const Author = observer(({
/>
</PageColumn>
</PageWrapper>
<br/>
<br/>
<Title title="page.team.author.absence.title"/>
<DataLoader
to="response"
loader={getFakeLoader(absence, mode)}
watch={`${mode}${dataGripStore.hash}`}
>
<Absence
mode={mode}
rowsForExcel={absence}
/>
<Pagination/>
</DataLoader>
</>
);
});

View file

@ -7,7 +7,7 @@ import SelectWithButtons from 'ts/components/UiKit/components/SelectWithButtons'
import { Filters } from 'ts/components/YearChart/interfaces/Filters';
import dataGripStore from 'ts/store/DataGrip';
import style from '../Country/styles/index.module.scss';
import style from './index.module.scss';
function getFormattedUsers(rows: any[], titleForAll: string) {
const options = rows.map((title: string, id: number) => ({ id: id + 1, title }));
@ -28,8 +28,8 @@ const MonthFilters = observer(({
const authors = dataGripStore.dataGrip.author.list;
const types = dataGripStore.dataGrip.type.list;
const authorsOptions = useMemo(() => getFormattedUsers(authors, t('page.team.month.authors')), [authors]);
const typesOptions = useMemo(() => getFormattedUsers(types, t('page.team.month.types')), [types]);
const authorsOptions = useMemo(() => getFormattedUsers(authors, t('page.team.month.filters.authors')), [authors]);
const typesOptions = useMemo(() => getFormattedUsers(types, t('page.team.month.filters.types')), [types]);
const update = (property: string, value: any) => {
onChange({
...filters,
@ -38,30 +38,34 @@ const MonthFilters = observer(({
};
return (
<div className={style.team_country_filter}>
<div className={style.team_month_filter}>
<SelectWithButtons
title="page.team.tree.filters.author"
className={style.team_country_filter_select}
className={style.team_month_filter_select}
value={filters?.authors?.[0] || authorsOptions[0]}
options={authorsOptions}
onChange={(id: number) => update('authors', [authorsOptions[id]])}
/>
<SelectWithButtons
title="page.team.tree.filters.author"
className={style.team_country_filter_select}
className={style.team_month_filter_select}
value={filters?.types?.[0] || typesOptions[0]}
options={typesOptions}
onChange={(id: number) => update('types', [typesOptions[id]])}
/>
<UiKitCheckbox
title="Релизы"
className={style.team_country_filter_checkbox}
title="page.team.month.filters.release"
className={style.team_month_filter_checkbox}
value={filters.release}
onChange={() => update('release', !filters.release)}
/>
<UiKitCheckbox
title="Первый и последний день"
className={style.team_country_filter_checkbox}
title="page.team.month.filters.absence"
className={style.team_month_filter_checkbox}
value={filters.absence}
onChange={() => update('absence', !filters.absence)}
/>
<UiKitCheckbox
title="page.team.month.filters.firstLastDays"
className={style.team_month_filter_checkbox}
value={filters.firstLastDays}
onChange={() => update('firstLastDays', !filters.firstLastDays)}
/>

View file

@ -0,0 +1,28 @@
@import 'src/styles/variables';
.team_month_filter {
margin: 0 0 var(--space-xxl);
&_checkbox,
&_select {
display: inline-block;
margin-right: var(--space-xxl);
vertical-align: middle;
}
&_select {
min-width: 350px;
}
&_checkbox {
margin-top: var(--space-l);
}
}
@media (max-width: 1540px) {
.team_month_filter_select {
display: block;
min-width: 100%;
margin-bottom: var(--space-xxl)
}
}

View file

@ -18,9 +18,8 @@ const Month = observer(({
}: ICommonPageProps): React.ReactElement => {
const statistic = dataGripStore.dataGrip.month;
const statisticByAuthor = dataGripStore.dataGrip.author.statistic;
const statisticByRelease = dataGripStore.dataGrip.release.statistic;
const recommendations = dataGripStore.dataGrip.recommendations.team?.byTimestamp;
const events = getEvents(statisticByAuthor, statisticByRelease);
const events = getEvents(statisticByAuthor, dataGripStore.dataGrip);
const defaultFilters = { release: false, firstLastDays: true };
const [filters, setFilters] = useState<Filters>(defaultFilters);

View file

@ -53,12 +53,24 @@ export default `
§ page.team.author.type.work: works
§ page.team.author.type.dismissed: dismissed
§ page.team.author.type.staff: staff
§ page.team.author.absence.title: Vacation schedule
§ page.team.author.absence.vacation: Vacation
§ page.team.author.absence.transfer: Department change
§ page.team.author.absence.from: from
§ page.team.author.absence.to: to
§ page.team.author.absence.duration: days
§ page.team.hours.title: Distribution of commits during each day of the week
§ page.team.month.filters.release: Releases
§ page.team.month.filters.absence: Vacations and relocations
§ page.team.month.filters.firstLastDays: First and last days
§ page.team.month.filters.authors: All employees
§ page.team.month.filters.types: All types
§ page.team.month.title: Project work calendar
§ page.team.month.first: (first work day)
§ page.team.month.last: (last work day)
§ page.team.month.authors: All employees
§ page.team.month.types: All types
§ page.team.month.travel: (changed time zone)
§ page.team.month.vacation.first: (goes on vacation)
§ page.team.month.vacation.last: (returned from vacation)
§ page.team.month.work.first: (first work day)
§ page.team.month.work.last: (last work day)
§ page.team.scope.title: Feature statistics
§ page.team.scope.scope: Feature
§ page.team.scope.days: Working Days

View file

@ -53,12 +53,24 @@ export default `
§ page.team.author.type.work: works
§ page.team.author.type.dismissed: dismissed
§ page.team.author.type.staff: staff
§ page.team.author.absence.title: Vacation schedule
§ page.team.author.absence.vacation: Vacation
§ page.team.author.absence.transfer: Department change
§ page.team.author.absence.from: from
§ page.team.author.absence.to: to
§ page.team.author.absence.duration: days
§ page.team.hours.title: Distribution of commits during each day of the week
§ page.team.month.filters.release: Releases
§ page.team.month.filters.absence: Vacations and relocations
§ page.team.month.filters.firstLastDays: First and last days
§ page.team.month.filters.authors: All employees
§ page.team.month.filters.types: All types
§ page.team.month.title: Project work calendar
§ page.team.month.first: (first work day)
§ page.team.month.last: (last work day)
§ page.team.month.authors: All employees
§ page.team.month.types: All types
§ page.team.month.travel: (changed time zone)
§ page.team.month.vacation.first: (goes on vacation)
§ page.team.month.vacation.last: (returned from vacation)
§ page.team.month.work.first: (first work day)
§ page.team.month.work.last: (last work day)
§ page.team.scope.title: Feature statistics
§ page.team.scope.scope: Feature
§ page.team.scope.days: Working Days

View file

@ -53,12 +53,24 @@ export default `
§ page.team.author.type.work: works
§ page.team.author.type.dismissed: dismissed
§ page.team.author.type.staff: staff
§ page.team.author.absence.title: Vacation schedule
§ page.team.author.absence.vacation: Vacation
§ page.team.author.absence.transfer: Department change
§ page.team.author.absence.from: from
§ page.team.author.absence.to: to
§ page.team.author.absence.duration: days
§ page.team.hours.title: Distribución del trabajo cada día de la semana
§ page.team.month.filters.release: Releases
§ page.team.month.filters.absence: Vacations and relocations
§ page.team.month.filters.firstLastDays: First and last days
§ page.team.month.filters.authors: All employees
§ page.team.month.filters.types: All types
§ page.team.month.title: Calendario del proyecto
§ page.team.month.first: (first work day)
§ page.team.month.last: (last work day)
§ page.team.month.authors: All employees
§ page.team.month.types: All types
§ page.team.month.travel: (changed time zone)
§ page.team.month.vacation.first: (goes on vacation)
§ page.team.month.vacation.last: (returned from vacation)
§ page.team.month.work.first: (first work day)
§ page.team.month.work.last: (last work day)
§ page.team.scope.title: Estadísticas de módulos
§ page.team.scope.scope: Elaboración definitiva
§ page.team.scope.days: Siervo. día

View file

@ -53,12 +53,24 @@ export default `
§ page.team.author.type.work: works
§ page.team.author.type.dismissed: dismissed
§ page.team.author.type.staff: staff
§ page.team.author.absence.title: Vacation schedule
§ page.team.author.absence.vacation: Vacation
§ page.team.author.absence.transfer: Department change
§ page.team.author.absence.from: from
§ page.team.author.absence.to: to
§ page.team.author.absence.duration: days
§ page.team.hours.title: Répartition du travail pour chaque jour de la semaine
§ page.team.month.filters.release: Releases
§ page.team.month.filters.absence: Vacations and relocations
§ page.team.month.filters.firstLastDays: First and last days
§ page.team.month.filters.authors: All employees
§ page.team.month.filters.types: All types
§ page.team.month.title: Calendrier du projet
§ page.team.month.first: (first work day)
§ page.team.month.last: (last work day)
§ page.team.month.authors: All employees
§ page.team.month.types: All types
§ page.team.month.travel: (changed time zone)
§ page.team.month.vacation.first: (goes on vacation)
§ page.team.month.vacation.last: (returned from vacation)
§ page.team.month.work.first: (first work day)
§ page.team.month.work.last: (last work day)
§ page.team.scope.title: Statistiques par module
§ page.team.scope.scope: Mise au point
§ page.team.scope.days: Esclave. jours

View file

@ -53,12 +53,24 @@ export default `
§ page.team.author.type.work: works
§ page.team.author.type.dismissed: dismissed
§ page.team.author.type.staff: staff
§ page.team.author.absence.title: Vacation schedule
§ page.team.author.absence.vacation: Vacation
§ page.team.author.absence.transfer: Department change
§ page.team.author.absence.from: from
§ page.team.author.absence.to: to
§ page.team.author.absence.duration: days
§ page.team.hours.title: Distribution of commits during each day of the week
§ page.team.month.filters.release: Releases
§ page.team.month.filters.absence: Vacations and relocations
§ page.team.month.filters.firstLastDays: First and last days
§ page.team.month.filters.authors: All employees
§ page.team.month.filters.types: All types
§ page.team.month.title: Project work calendar
§ page.team.month.first: (first work day)
§ page.team.month.last: (last work day)
§ page.team.month.authors: All employees
§ page.team.month.types: All types
§ page.team.month.travel: (changed time zone)
§ page.team.month.vacation.first: (goes on vacation)
§ page.team.month.vacation.last: (returned from vacation)
§ page.team.month.work.first: (first work day)
§ page.team.month.work.last: (last work day)
§ page.team.scope.title: Feature statistics
§ page.team.scope.scope: Feature
§ page.team.scope.days: Working Days

View file

@ -53,12 +53,24 @@ export default `
§ page.team.author.type.work: works
§ page.team.author.type.dismissed: dismissed
§ page.team.author.type.staff: staff
§ page.team.author.absence.title: Vacation schedule
§ page.team.author.absence.vacation: Vacation
§ page.team.author.absence.transfer: Department change
§ page.team.author.absence.from: from
§ page.team.author.absence.to: to
§ page.team.author.absence.duration: days
§ page.team.hours.title: Distribution of commits during each day of the week
§ page.team.month.filters.release: Releases
§ page.team.month.filters.absence: Vacations and relocations
§ page.team.month.filters.firstLastDays: First and last days
§ page.team.month.filters.authors: All employees
§ page.team.month.filters.types: All types
§ page.team.month.title: Project work calendar
§ page.team.month.first: (first work day)
§ page.team.month.last: (last work day)
§ page.team.month.authors: All employees
§ page.team.month.types: All types
§ page.team.month.travel: (changed time zone)
§ page.team.month.vacation.first: (goes on vacation)
§ page.team.month.vacation.last: (returned from vacation)
§ page.team.month.work.first: (first work day)
§ page.team.month.work.last: (last work day)
§ page.team.scope.title: Feature statistics
§ page.team.scope.scope: Feature
§ page.team.scope.days: Working Days

View file

@ -53,12 +53,24 @@ export default `
§ page.team.author.type.work: работает
§ page.team.author.type.dismissed: уволен
§ page.team.author.type.staff: помощник
§ page.team.author.absence.title: График отпусков
§ page.team.author.absence.vacation: Отпуск
§ page.team.author.absence.transfer: Перевод в другой отдел
§ page.team.author.absence.from: с
§ page.team.author.absence.to: по
§ page.team.author.absence.duration: дней
§ page.team.hours.title: Распределение коммитов в течении каждого дня недели
§ page.team.month.filters.release: Релизы
§ page.team.month.filters.absence: Отпуска и переезды
§ page.team.month.filters.firstLastDays: Первый и последний день
§ page.team.month.filters.authors: Все сотрудники
§ page.team.month.filters.types: Все типы
§ page.team.month.title: Календарь работы по проекту
§ page.team.month.first: (первый рабочий день)
§ page.team.month.last: (последний рабочий день)
§ page.team.month.authors: Все сотрудники
§ page.team.month.types: Все типы
§ page.team.month.travel: (изменил местоположение)
§ page.team.month.vacation.first: (уходит в отпуск)
§ page.team.month.vacation.last: (вернулся из отпуска)
§ page.team.month.work.first: (первый рабочий день)
§ page.team.month.work.last: (последний рабочий день)
§ page.team.scope.title: Статистика по фичам
§ page.team.scope.scope: Фича
§ page.team.scope.days: Раб. дней

View file

@ -48,12 +48,24 @@ export default `
§ page.team.author.type.work: works
§ page.team.author.type.dismissed: dismissed
§ page.team.author.type.staff: staff
§ page.team.hours.title: 每周每一天的工作分配
§ page.team.month.title: 项目工作日历
§ page.team.month.first: (first work day)
§ page.team.month.last: (last work day)
§ page.team.month.authors: All employees
§ page.team.month.types: All types
§ page.team.author.absence.title: Vacation schedule
§ page.team.author.absence.vacation: Vacation
§ page.team.author.absence.transfer: Department change
§ page.team.author.absence.from: from
§ page.team.author.absence.to: to
§ page.team.author.absence.duration: days
§ page.team.hours.title: Distribution of commits during each day of the week
§ page.team.month.filters.release: Releases
§ page.team.month.filters.absence: Vacations and relocations
§ page.team.month.filters.firstLastDays: First and last days
§ page.team.month.filters.authors: All employees
§ page.team.month.filters.types: All types
§ page.team.month.title: Project work calendar
§ page.team.month.travel: (changed time zone)
§ page.team.month.vacation.first: (goes on vacation)
§ page.team.month.vacation.last: (returned from vacation)
§ page.team.month.work.first: (first work day)
§ page.team.month.work.last: (last work day)
§ page.team.scope.title: 按模块划分的统计数字
§ page.team.scope.scope: 修改
§ page.team.scope.days: 工作天