update
1
build/assets/chart/person.svg
Normal 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 |
1
build/assets/chart/person_add.svg
Normal 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 |
1
build/assets/chart/person_add_remove.svg
Normal 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 |
1
build/assets/chart/person_remove.svg
Normal 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 |
1
build/assets/chart/release.svg
Normal 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 |
1
build/assets/chart/tasks.svg
Normal 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 |
1
build/assets/chart/travel.svg
Normal 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 |
1
build/assets/chart/vacation_end.svg
Normal 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 |
1
build/assets/chart/vacation_start.svg
Normal 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 |
1
public/assets/chart/travel.svg
Normal 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 |
1
public/assets/chart/vacation_end.svg
Normal 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 |
1
public/assets/chart/vacation_start.svg
Normal 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 |
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
BIN
public/themes/grafana/header.png
Normal file
After Width: | Height: | Size: 7.5 KiB |
|
@ -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>
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()}
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
export interface Filters {
|
||||
release?: boolean;
|
||||
firstLastDays?: boolean;
|
||||
absence?: boolean;
|
||||
types?: string[];
|
||||
authors?: string[];
|
||||
}
|
||||
|
|
77
src/ts/helpers/DataGrip/components/absence.ts
Normal 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();
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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'" */
|
||||
|
|
|
@ -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(':');
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
98
src/ts/pages/Team/components/Author/components/Absence.tsx
Normal 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;
|
|
@ -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;
|
|
@ -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}
|
||||
|
|
|
@ -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>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
|
|
@ -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)}
|
||||
/>
|
||||
|
|
28
src/ts/pages/Team/components/Month/index.module.scss
Normal 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)
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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: Раб. дней
|
||||
|
|
|
@ -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: 工作天
|
||||
|
|