This commit is contained in:
Бахирев 2024-06-20 00:50:09 +03:00
parent c0be2310cf
commit 7c334250b0
113 changed files with 2324 additions and 193 deletions

View file

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128.83 128.83"><defs><style>.cls-1{fill:none;stroke:#000;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.93px;}</style></defs><line class="cls-1" x1="63.03" y1="22.12" x2="65.8" y2="22.12"/><g><line class="cls-1" x1="56.91" y1="32.09" x2="59.68" y2="32.09"/><line class="cls-1" x1="69.15" y1="32.09" x2="71.92" y2="32.09"/></g><g><line class="cls-1" x1="50.79" y1="42.06" x2="53.56" y2="42.06"/><line class="cls-1" x1="63.03" y1="42.06" x2="65.8" y2="42.06"/></g><line class="cls-1" x1="75.27" y1="42.06" x2="78.04" y2="42.06"/><g><g><line class="cls-1" x1="44.67" y1="52.02" x2="47.44" y2="52.02"/><line class="cls-1" x1="56.91" y1="52.02" x2="59.68" y2="52.02"/></g><line class="cls-1" x1="69.15" y1="52.02" x2="71.92" y2="52.02"/><g><line class="cls-1" x1="69.15" y1="52.02" x2="71.92" y2="52.02"/><line class="cls-1" x1="81.39" y1="52.02" x2="84.16" y2="52.02"/></g><g><line class="cls-1" x1="38.55" y1="61.99" x2="41.32" y2="61.99"/><line class="cls-1" x1="50.79" y1="61.99" x2="53.56" y2="61.99"/></g><line class="cls-1" x1="63.03" y1="61.99" x2="65.8" y2="61.99"/><g><line class="cls-1" x1="63.03" y1="61.99" x2="65.8" y2="61.99"/><line class="cls-1" x1="75.27" y1="61.99" x2="78.04" y2="61.99"/></g><line class="cls-1" x1="87.51" y1="61.99" x2="90.28" y2="61.99"/></g><g><g><line class="cls-1" x1="32.43" y1="71.96" x2="35.2" y2="71.96"/><line class="cls-1" x1="44.67" y1="71.96" x2="47.44" y2="71.96"/></g><line class="cls-1" x1="56.91" y1="71.96" x2="59.68" y2="71.96"/><g><line class="cls-1" x1="56.91" y1="71.96" x2="59.68" y2="71.96"/><line class="cls-1" x1="69.15" y1="71.96" x2="71.92" y2="71.96"/></g><g><line class="cls-1" x1="81.39" y1="71.96" x2="84.16" y2="71.96"/><line class="cls-1" x1="93.63" y1="71.96" x2="96.4" y2="71.96"/></g></g><g><g><line class="cls-1" x1="26.31" y1="81.82" x2="29.09" y2="81.82"/><line class="cls-1" x1="38.55" y1="81.82" x2="41.32" y2="81.82"/></g><line class="cls-1" x1="50.79" y1="81.82" x2="53.56" y2="81.82"/><g><line class="cls-1" x1="50.79" y1="81.82" x2="53.56" y2="81.82"/><line class="cls-1" x1="63.03" y1="81.82" x2="65.8" y2="81.82"/></g><g><line class="cls-1" x1="75.27" y1="81.82" x2="78.04" y2="81.82"/><line class="cls-1" x1="87.51" y1="81.82" x2="90.28" y2="81.82"/></g><g><line class="cls-1" x1="87.51" y1="81.82" x2="90.28" y2="81.82"/><line class="cls-1" x1="99.74" y1="81.82" x2="102.52" y2="81.82"/></g></g><g><g><line class="cls-1" x1="20.19" y1="91.67" x2="22.97" y2="91.67"/><line class="cls-1" x1="32.43" y1="91.67" x2="35.2" y2="91.67"/></g><line class="cls-1" x1="44.67" y1="91.67" x2="47.44" y2="91.67"/><g><line class="cls-1" x1="44.67" y1="91.67" x2="47.44" y2="91.67"/><line class="cls-1" x1="56.91" y1="91.67" x2="59.68" y2="91.67"/></g><g><line class="cls-1" x1="69.15" y1="91.67" x2="71.92" y2="91.67"/><line class="cls-1" x1="81.39" y1="91.67" x2="84.16" y2="91.67"/></g><g><line class="cls-1" x1="81.39" y1="91.67" x2="84.16" y2="91.67"/><line class="cls-1" x1="93.63" y1="91.67" x2="96.4" y2="91.67"/></g><g><line class="cls-1" x1="81.39" y1="91.67" x2="84.16" y2="91.67"/><line class="cls-1" x1="93.63" y1="91.67" x2="96.4" y2="91.67"/></g><g><line class="cls-1" x1="93.63" y1="91.67" x2="96.4" y2="91.67"/><line class="cls-1" x1="105.86" y1="91.67" x2="108.64" y2="91.67"/></g></g></svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

View file

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128.83 128.83"><defs><style>.cls-1{fill:none;stroke:#000;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.93px;}</style></defs><g><path class="cls-1" d="M71.77,79.68v4.64c0,1.54,.83,2.93,2.17,3.94s3.19,1.63,5.22,1.63c11.11,0,20.12,6.79,20.12,15.16v1.05H25.43v-1.05c0-4.18,2.25-7.97,5.89-10.72,3.64-2.74,8.67-4.44,14.22-4.44,4.08,0,7.39-2.49,7.39-5.57v-4.64"/><line class="cls-1" x1="62.35" y1="106.09" x2="45.54" y2="89.88"/><line class="cls-1" x1="62.35" y1="106.09" x2="79.16" y2="89.88"/><path class="cls-1" d="M41.47,55.08s-.01,.02,0,.03c-.01,.2-.01,.41-.01,.62,0,10.47,4.66,19.54,11.46,23.95h0c2.83,1.85,6.03,2.89,9.42,2.89s6.59-1.04,9.42-2.88h0c3.35-2.18,6.19-5.49,8.19-9.55"/><path class="cls-1" d="M83.23,55.54h0v.2c0,1.81-.14,3.59-.41,5.3"/><path class="cls-1" d="M46.87,37.72c3.81,2.65,9.34,4.32,15.48,4.32s11.66-1.67,15.48-4.32"/><path class="cls-1" d="M47.07,37.65l-5.52,17.89c-2.1-10.03-2.47-23.03,9.52-27.59,3.08-1.17,5.69-4.15,6.13-7.42,1.48,.93,2.21,2.62,2.24,4.36,2.65-1.17,4.51-3.96,4.59-6.85,2.11,1.9,3.47,4.6,3.85,7.41,2.18,.37,4.46-1.11,5.09-3.22,1.23,1.62,1.85,3.68,1.7,5.71,9.11,4.38,11.22,14.09,8.55,27.79"/><path class="cls-1" d="M77.71,37.65l5.52,17.89"/><path class="cls-1" d="M68.63,72.31c-1.66,1.47-3.87,2.36-6.27,2.36s-4.39-.81-6.03-2.17c-2.1-1.73-3.43-4.35-3.43-7.29"/><line class="cls-1" x1="65.09" y1="65.21" x2="52.89" y2="65.21"/><path class="cls-1" d="M40.49,47.7c-3.08,.64-5.45,4.3-5.45,8.7,0,4.86,2.88,8.8,6.42,8.8,.44,0,.87-.06,1.29-.18"/><path class="cls-1" d="M84.26,47.72c3.06,.67,5.4,4.31,5.4,8.69,0,2.18-.59,4.18-1.55,5.72"/><path class="cls-1" d="M68.63,72.31c5.78-3.46,11.46-3.3,18.42-1.75,4.26,.94,8.67,1.68,12.96,1.36s8.64-2.48,9.46-6.13c.31-1.38-.45-2.82-1.88-3.61-2.12,2.46-6.26,2.94-9.88,2.29-2.54-.46-6.32-1.52-9.6-2.34-1.4-.35-2.72-.66-3.8-.86-.5-.09-1-.17-1.49-.23-11.67-1.53-19.78,4.81-26.49,11.46"/></g><line class="cls-1" x1="53.38" y1="51.9" x2="55.02" y2="51.9"/><line class="cls-1" x1="69.44" y1="51.9" x2="71.08" y2="51.9"/></svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View file

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128.83 128.83"><defs><style>.cls-1{fill:none;stroke:#000;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.93px;}</style></defs><path class="cls-1" d="M100.86,100.16H34.28c-1.79,0-3.4-.78-4.64-2.06-1.44-1.5-2.34-3.68-2.34-6.11V41.04c0-4.52,3.13-8.17,6.98-8.17h18.16c3.85,0,6.98,3.65,6.98,8.17v3.27h35.4c3.33,0,6.03,3.16,6.03,7.06v11.74"/><path class="cls-1" d="M29.64,98.1l7.87-28.89c.99-3.64,3.88-6.1,7.13-6.1h62.9c2.51,0,4.25,2.93,3.37,5.67l-10.06,31.38"/></svg>

After

Width:  |  Height:  |  Size: 576 B

View file

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128.83 128.83"><defs><style>.cls-1{fill:none;stroke:#000;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.93px;}</style></defs><polygon class="cls-1" points="64.42 103.1 25.73 64.42 64.42 25.73 72.27 33.59 41.44 64.42 64.42 87.38 87.39 64.42 79.83 56.86 64.42 72.27 56.56 64.42 79.83 41.14 103.1 64.42 64.42 103.1"/></svg>

After

Width:  |  Height:  |  Size: 435 B

View file

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128.83 128.83"><defs><style>.cls-1{fill:none;stroke:#000;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.93px;}</style></defs><circle class="cls-1" cx="64.41" cy="64.41" r="44"/><path class="cls-1" d="M36.93,62.7c-1.51-4.93,.37-10.77,4.27-14.1,2.91-2.48,7.05-3.79,10.78-2.83,1.17,.3,2.29,.79,3.3,1.46,4.13,2.73,5.83,7.87,3.89,12.44-2.16,5.11-9.05,8.03-13.65,4.13-2.09-1.77-3.11-4.68-2.53-7.36,.75-3.44,4.23-6.57,7.89-5.66,2.44,.61,4.54,3.03,4.16,5.61-.35,2.38-2.52,4.31-4.94,4.3-.85,0-1.73-.24-2.35-.82-1.71-1.58-.15-4.54,2.08-4.26"/><path class="cls-1" d="M92.65,48.88c1.51,4.93-.37,10.77-4.27,14.1-2.91,2.48-7.05,3.79-10.78,2.83-1.17-.3-2.29-.79-3.3-1.46-4.13-2.73-5.83-7.87-3.89-12.44,2.16-5.11,9.05-8.03,13.65-4.13,2.09,1.77,3.11,4.68,2.53,7.36-.75,3.44-4.23,6.57-7.89,5.66-2.44-.61-4.54-3.03-4.16-5.61,.35-2.38,2.52-4.31,4.94-4.3,.85,0,1.73,.24,2.35,.82,1.71,1.58,.15,4.54-2.08,4.26"/><polyline class="cls-1" points="42.2 79.85 50.25 85.82 59.67 79.55 69.59 85.62 77.91 79.24 86.63 84.81"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -1 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128.83 128.83"><defs><style>.cls-1{fill:none;stroke:#000;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.93px;}</style></defs><path class="cls-1" d="M87.76,83.68h-31.24c-1.42,0-2.58-1.16-2.58-2.59V25.78c0-1.43,1.16-2.58,2.58-2.58h28.66c1.43,0,2.58,1.15,2.58,2.58v57.78"/><rect class="cls-1" x="58.21" y="28.27" width="25.29" height="11.07"/><circle class="cls-1" cx="70.59" cy="55.52" r="10.36"/><line class="cls-1" x1="75.97" y1="46.65" x2="65.21" y2="64.38"/><rect class="cls-1" x="41.78" y="36.29" width="4.56" height="47.27"/><rect class="cls-1" x="31.52" y="36.29" width="4.56" height="47.27"/><line class="cls-1" x1="44.05" y1="36.18" x2="44.05" y2="26.49"/><line class="cls-1" x1="33.8" y1="36.18" x2="33.8" y2="26.49"/><path class="cls-1" d="M33.8,83.67c4.27,14.03,19.96,20.91,33.52,21.85,9.18,.64,20.36-1.54,26.11-9.38,3.76-5.11,5.05-11.97,2.65-17.88s-7.13-9.98-12.6-12.41"/><path class="cls-1" d="M43.94,83.67c6.29,19.8,55.84,17.23,41.44-8.77"/></svg> <?xml version="1.0" encoding="UTF-8"?><svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128.83 128.83"><defs><style>.cls-1{fill:none;stroke:#000;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.93px;}</style></defs><rect class="cls-1" x="55.37" y="23.33" width="41.25" height="73.77" rx="3.49" ry="3.49"/><rect class="cls-1" x="60.58" y="29.51" width="30.84" height="13.51"/><circle class="cls-1" cx="75.69" cy="62.75" r="12.64"/><line class="cls-1" x1="82.24" y1="51.94" x2="69.13" y2="73.55"/><rect class="cls-1" x="41.53" y="55.66" width="5.56" height="45.84"/><rect class="cls-1" x="29.02" y="55.66" width="5.56" height="45.84"/><line class="cls-1" x1="44.31" y1="111.55" x2="44.31" y2="102.16"/><line class="cls-1" x1="31.8" y1="111.55" x2="31.8" y2="102.16"/></svg>

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 796 B

View file

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128.83 128.83"><defs><style>.cls-1{fill:none;stroke:#000;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.93px;}</style></defs><path class="cls-1" d="M78.83,86.22h-3.92l-10.35,8.16-10.35-8.16h-4.2c-11.6,0-21,7.72-21,17.25v3.45H99.83v-3.45c0-9.52-9.4-17.25-21-17.25Z"/><line class="cls-1" x1="64.55" y1="94.38" x2="64.55" y2="106.91"/><line class="cls-1" x1="54.2" y1="86.22" x2="54.2" y2="78.18"/><line class="cls-1" x1="74.9" y1="86.22" x2="74.9" y2="78.18"/><line class="cls-1" x1="45.07" y1="66.74" x2="45.07" y2="54.89"/><line class="cls-1" x1="64.55" y1="48.49" x2="45.07" y2="54.89"/><line class="cls-1" x1="64.55" y1="72.05" x2="45.07" y2="66.74"/><line class="cls-1" x1="51.75" y1="58.02" x2="51.75" y2="62.65"/><line class="cls-1" x1="58.01" y1="56.66" x2="58.01" y2="64.15"/><line class="cls-1" x1="84.03" y1="66.74" x2="84.03" y2="54.89"/><line class="cls-1" x1="64.55" y1="48.49" x2="84.03" y2="54.89"/><line class="cls-1" x1="64.55" y1="72.05" x2="84.03" y2="66.74"/><line class="cls-1" x1="77.36" y1="58.02" x2="77.36" y2="62.65"/><line class="cls-1" x1="71.09" y1="56.66" x2="71.09" y2="64.15"/><line class="cls-1" x1="64.55" y1="54.16" x2="64.55" y2="66.51"/><path class="cls-1" d="M48.41,53.8c1.56-6.47,5.39-11.64,10.27-13.95,1.79-.85,3.73-1.31,5.74-1.31,4.42,0,8.45,2.22,11.44,5.86,2.08,2.51,3.66,5.7,4.55,9.3"/><path class="cls-1" d="M48.63,67.71c2.32,8.41,8.51,14.42,15.79,14.42s13.43-5.98,15.77-14.34"/><path class="cls-1" d="M58.68,39.85c-.9-2.01-1.27-4.22-.8-6.34,.95-4.24,5-7.16,9.17-8.38s8.59-1.15,12.9-1.75c4.3-.59,8.8-2.04,11.47-5.46-.21,7.17-4.64,14.05-11.07,17.21,4.72-.51,8.59-.81,13.36-2.72-1.49,12.03-11.03,11.64-17.85,11.99"/><line class="cls-1" x1="45.07" y1="106.91" x2="37.96" y2="89.34"/><line class="cls-1" x1="83.24" y1="106.91" x2="90.36" y2="89.34"/></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View file

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128.83 128.83"><defs><style>.cls-1{fill:none;stroke:#000;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.93px;}</style></defs><path class="cls-1" d="M65.9,32.74c5.31,0,9.61-4.3,9.61-9.61s-4.3-9.61-9.61-9.61-9.61,4.3-9.61,9.61,4.3,9.61,9.61,9.61Z"/><path class="cls-1" d="M57.65,73.18h-20.24c.17-7.24,2.05-21.06,12.97-29.25l6.2,12.6c.51,1.03,1.39,1.76,2.39,2.12v.25h25.03c2.34,0,4.24-1.9,4.24-4.24s-1.9-4.24-4.24-4.24h-19.5l-7.98-16.2c-.03-.06-.07-.11-.1-.16-.14-.44-.33-.87-.57-1.29-1.85-3.24-5.97-4.36-9.2-2.51-26.12,14.92-22.68,47.81-22.52,49.2,.11,.97,.43,1.87,.9,2.66,.69,2.19,2.71,3.78,5.13,3.78h21.18v22.76c0,3.67,2.97,6.65,6.65,6.65s6.65-2.98,6.65-6.65v-28.51c0-3.85-3.12-6.98-6.98-6.98Z"/><path class="cls-1" d="M104.68,50.51c.68-1.58-.06-3.41-1.64-4.09-1.58-.67-3.41,.06-4.09,1.64l-6.69,15.64h-16.75c-1.72,0-3.11,1.39-3.11,3.11s1.39,3.11,3.11,3.11h18.8c.19,0,.38-.02,.56-.06,.05,0,.11-.02,.16-.03,.13-.03,.26-.07,.39-.12,.05-.02,.1-.03,.14-.05,.16-.07,.32-.16,.47-.25,.03-.02,.06-.04,.09-.07,.12-.08,.23-.18,.34-.28,.04-.04,.08-.08,.11-.12,.09-.1,.18-.2,.26-.31,.03-.04,.06-.08,.09-.13,.1-.15,.19-.31,.26-.47h0v-.02l7.49-17.51Z"/></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
build/assets/games/4x3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

BIN
build/social/tg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
build/social/vk.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
build/social/youtube.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 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 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128.83 128.83"><defs><style>.cls-1{fill:none;stroke:#000;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.93px;}</style></defs><line class="cls-1" x1="63.03" y1="22.12" x2="65.8" y2="22.12"/><g><line class="cls-1" x1="56.91" y1="32.09" x2="59.68" y2="32.09"/><line class="cls-1" x1="69.15" y1="32.09" x2="71.92" y2="32.09"/></g><g><line class="cls-1" x1="50.79" y1="42.06" x2="53.56" y2="42.06"/><line class="cls-1" x1="63.03" y1="42.06" x2="65.8" y2="42.06"/></g><line class="cls-1" x1="75.27" y1="42.06" x2="78.04" y2="42.06"/><g><g><line class="cls-1" x1="44.67" y1="52.02" x2="47.44" y2="52.02"/><line class="cls-1" x1="56.91" y1="52.02" x2="59.68" y2="52.02"/></g><line class="cls-1" x1="69.15" y1="52.02" x2="71.92" y2="52.02"/><g><line class="cls-1" x1="69.15" y1="52.02" x2="71.92" y2="52.02"/><line class="cls-1" x1="81.39" y1="52.02" x2="84.16" y2="52.02"/></g><g><line class="cls-1" x1="38.55" y1="61.99" x2="41.32" y2="61.99"/><line class="cls-1" x1="50.79" y1="61.99" x2="53.56" y2="61.99"/></g><line class="cls-1" x1="63.03" y1="61.99" x2="65.8" y2="61.99"/><g><line class="cls-1" x1="63.03" y1="61.99" x2="65.8" y2="61.99"/><line class="cls-1" x1="75.27" y1="61.99" x2="78.04" y2="61.99"/></g><line class="cls-1" x1="87.51" y1="61.99" x2="90.28" y2="61.99"/></g><g><g><line class="cls-1" x1="32.43" y1="71.96" x2="35.2" y2="71.96"/><line class="cls-1" x1="44.67" y1="71.96" x2="47.44" y2="71.96"/></g><line class="cls-1" x1="56.91" y1="71.96" x2="59.68" y2="71.96"/><g><line class="cls-1" x1="56.91" y1="71.96" x2="59.68" y2="71.96"/><line class="cls-1" x1="69.15" y1="71.96" x2="71.92" y2="71.96"/></g><g><line class="cls-1" x1="81.39" y1="71.96" x2="84.16" y2="71.96"/><line class="cls-1" x1="93.63" y1="71.96" x2="96.4" y2="71.96"/></g></g><g><g><line class="cls-1" x1="26.31" y1="81.82" x2="29.09" y2="81.82"/><line class="cls-1" x1="38.55" y1="81.82" x2="41.32" y2="81.82"/></g><line class="cls-1" x1="50.79" y1="81.82" x2="53.56" y2="81.82"/><g><line class="cls-1" x1="50.79" y1="81.82" x2="53.56" y2="81.82"/><line class="cls-1" x1="63.03" y1="81.82" x2="65.8" y2="81.82"/></g><g><line class="cls-1" x1="75.27" y1="81.82" x2="78.04" y2="81.82"/><line class="cls-1" x1="87.51" y1="81.82" x2="90.28" y2="81.82"/></g><g><line class="cls-1" x1="87.51" y1="81.82" x2="90.28" y2="81.82"/><line class="cls-1" x1="99.74" y1="81.82" x2="102.52" y2="81.82"/></g></g><g><g><line class="cls-1" x1="20.19" y1="91.67" x2="22.97" y2="91.67"/><line class="cls-1" x1="32.43" y1="91.67" x2="35.2" y2="91.67"/></g><line class="cls-1" x1="44.67" y1="91.67" x2="47.44" y2="91.67"/><g><line class="cls-1" x1="44.67" y1="91.67" x2="47.44" y2="91.67"/><line class="cls-1" x1="56.91" y1="91.67" x2="59.68" y2="91.67"/></g><g><line class="cls-1" x1="69.15" y1="91.67" x2="71.92" y2="91.67"/><line class="cls-1" x1="81.39" y1="91.67" x2="84.16" y2="91.67"/></g><g><line class="cls-1" x1="81.39" y1="91.67" x2="84.16" y2="91.67"/><line class="cls-1" x1="93.63" y1="91.67" x2="96.4" y2="91.67"/></g><g><line class="cls-1" x1="81.39" y1="91.67" x2="84.16" y2="91.67"/><line class="cls-1" x1="93.63" y1="91.67" x2="96.4" y2="91.67"/></g><g><line class="cls-1" x1="93.63" y1="91.67" x2="96.4" y2="91.67"/><line class="cls-1" x1="105.86" y1="91.67" x2="108.64" y2="91.67"/></g></g></svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

View file

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128.83 128.83"><defs><style>.cls-1{fill:none;stroke:#000;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.93px;}</style></defs><g><path class="cls-1" d="M71.77,79.68v4.64c0,1.54,.83,2.93,2.17,3.94s3.19,1.63,5.22,1.63c11.11,0,20.12,6.79,20.12,15.16v1.05H25.43v-1.05c0-4.18,2.25-7.97,5.89-10.72,3.64-2.74,8.67-4.44,14.22-4.44,4.08,0,7.39-2.49,7.39-5.57v-4.64"/><line class="cls-1" x1="62.35" y1="106.09" x2="45.54" y2="89.88"/><line class="cls-1" x1="62.35" y1="106.09" x2="79.16" y2="89.88"/><path class="cls-1" d="M41.47,55.08s-.01,.02,0,.03c-.01,.2-.01,.41-.01,.62,0,10.47,4.66,19.54,11.46,23.95h0c2.83,1.85,6.03,2.89,9.42,2.89s6.59-1.04,9.42-2.88h0c3.35-2.18,6.19-5.49,8.19-9.55"/><path class="cls-1" d="M83.23,55.54h0v.2c0,1.81-.14,3.59-.41,5.3"/><path class="cls-1" d="M46.87,37.72c3.81,2.65,9.34,4.32,15.48,4.32s11.66-1.67,15.48-4.32"/><path class="cls-1" d="M47.07,37.65l-5.52,17.89c-2.1-10.03-2.47-23.03,9.52-27.59,3.08-1.17,5.69-4.15,6.13-7.42,1.48,.93,2.21,2.62,2.24,4.36,2.65-1.17,4.51-3.96,4.59-6.85,2.11,1.9,3.47,4.6,3.85,7.41,2.18,.37,4.46-1.11,5.09-3.22,1.23,1.62,1.85,3.68,1.7,5.71,9.11,4.38,11.22,14.09,8.55,27.79"/><path class="cls-1" d="M77.71,37.65l5.52,17.89"/><path class="cls-1" d="M68.63,72.31c-1.66,1.47-3.87,2.36-6.27,2.36s-4.39-.81-6.03-2.17c-2.1-1.73-3.43-4.35-3.43-7.29"/><line class="cls-1" x1="65.09" y1="65.21" x2="52.89" y2="65.21"/><path class="cls-1" d="M40.49,47.7c-3.08,.64-5.45,4.3-5.45,8.7,0,4.86,2.88,8.8,6.42,8.8,.44,0,.87-.06,1.29-.18"/><path class="cls-1" d="M84.26,47.72c3.06,.67,5.4,4.31,5.4,8.69,0,2.18-.59,4.18-1.55,5.72"/><path class="cls-1" d="M68.63,72.31c5.78-3.46,11.46-3.3,18.42-1.75,4.26,.94,8.67,1.68,12.96,1.36s8.64-2.48,9.46-6.13c.31-1.38-.45-2.82-1.88-3.61-2.12,2.46-6.26,2.94-9.88,2.29-2.54-.46-6.32-1.52-9.6-2.34-1.4-.35-2.72-.66-3.8-.86-.5-.09-1-.17-1.49-.23-11.67-1.53-19.78,4.81-26.49,11.46"/></g><line class="cls-1" x1="53.38" y1="51.9" x2="55.02" y2="51.9"/><line class="cls-1" x1="69.44" y1="51.9" x2="71.08" y2="51.9"/></svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View file

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128.83 128.83"><defs><style>.cls-1{fill:none;stroke:#000;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.93px;}</style></defs><path class="cls-1" d="M100.86,100.16H34.28c-1.79,0-3.4-.78-4.64-2.06-1.44-1.5-2.34-3.68-2.34-6.11V41.04c0-4.52,3.13-8.17,6.98-8.17h18.16c3.85,0,6.98,3.65,6.98,8.17v3.27h35.4c3.33,0,6.03,3.16,6.03,7.06v11.74"/><path class="cls-1" d="M29.64,98.1l7.87-28.89c.99-3.64,3.88-6.1,7.13-6.1h62.9c2.51,0,4.25,2.93,3.37,5.67l-10.06,31.38"/></svg>

After

Width:  |  Height:  |  Size: 576 B

View file

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128.83 128.83"><defs><style>.cls-1{fill:none;stroke:#000;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.93px;}</style></defs><polygon class="cls-1" points="64.42 103.1 25.73 64.42 64.42 25.73 72.27 33.59 41.44 64.42 64.42 87.38 87.39 64.42 79.83 56.86 64.42 72.27 56.56 64.42 79.83 41.14 103.1 64.42 64.42 103.1"/></svg>

After

Width:  |  Height:  |  Size: 435 B

View file

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128.83 128.83"><defs><style>.cls-1{fill:none;stroke:#000;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.93px;}</style></defs><circle class="cls-1" cx="64.41" cy="64.41" r="44"/><path class="cls-1" d="M36.93,62.7c-1.51-4.93,.37-10.77,4.27-14.1,2.91-2.48,7.05-3.79,10.78-2.83,1.17,.3,2.29,.79,3.3,1.46,4.13,2.73,5.83,7.87,3.89,12.44-2.16,5.11-9.05,8.03-13.65,4.13-2.09-1.77-3.11-4.68-2.53-7.36,.75-3.44,4.23-6.57,7.89-5.66,2.44,.61,4.54,3.03,4.16,5.61-.35,2.38-2.52,4.31-4.94,4.3-.85,0-1.73-.24-2.35-.82-1.71-1.58-.15-4.54,2.08-4.26"/><path class="cls-1" d="M92.65,48.88c1.51,4.93-.37,10.77-4.27,14.1-2.91,2.48-7.05,3.79-10.78,2.83-1.17-.3-2.29-.79-3.3-1.46-4.13-2.73-5.83-7.87-3.89-12.44,2.16-5.11,9.05-8.03,13.65-4.13,2.09,1.77,3.11,4.68,2.53,7.36-.75,3.44-4.23,6.57-7.89,5.66-2.44-.61-4.54-3.03-4.16-5.61,.35-2.38,2.52-4.31,4.94-4.3,.85,0,1.73,.24,2.35,.82,1.71,1.58,.15,4.54-2.08,4.26"/><polyline class="cls-1" points="42.2 79.85 50.25 85.82 59.67 79.55 69.59 85.62 77.91 79.24 86.63 84.81"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -1 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128.83 128.83"><defs><style>.cls-1{fill:none;stroke:#000;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.93px;}</style></defs><path class="cls-1" d="M87.76,83.68h-31.24c-1.42,0-2.58-1.16-2.58-2.59V25.78c0-1.43,1.16-2.58,2.58-2.58h28.66c1.43,0,2.58,1.15,2.58,2.58v57.78"/><rect class="cls-1" x="58.21" y="28.27" width="25.29" height="11.07"/><circle class="cls-1" cx="70.59" cy="55.52" r="10.36"/><line class="cls-1" x1="75.97" y1="46.65" x2="65.21" y2="64.38"/><rect class="cls-1" x="41.78" y="36.29" width="4.56" height="47.27"/><rect class="cls-1" x="31.52" y="36.29" width="4.56" height="47.27"/><line class="cls-1" x1="44.05" y1="36.18" x2="44.05" y2="26.49"/><line class="cls-1" x1="33.8" y1="36.18" x2="33.8" y2="26.49"/><path class="cls-1" d="M33.8,83.67c4.27,14.03,19.96,20.91,33.52,21.85,9.18,.64,20.36-1.54,26.11-9.38,3.76-5.11,5.05-11.97,2.65-17.88s-7.13-9.98-12.6-12.41"/><path class="cls-1" d="M43.94,83.67c6.29,19.8,55.84,17.23,41.44-8.77"/></svg> <?xml version="1.0" encoding="UTF-8"?><svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128.83 128.83"><defs><style>.cls-1{fill:none;stroke:#000;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.93px;}</style></defs><rect class="cls-1" x="55.37" y="23.33" width="41.25" height="73.77" rx="3.49" ry="3.49"/><rect class="cls-1" x="60.58" y="29.51" width="30.84" height="13.51"/><circle class="cls-1" cx="75.69" cy="62.75" r="12.64"/><line class="cls-1" x1="82.24" y1="51.94" x2="69.13" y2="73.55"/><rect class="cls-1" x="41.53" y="55.66" width="5.56" height="45.84"/><rect class="cls-1" x="29.02" y="55.66" width="5.56" height="45.84"/><line class="cls-1" x1="44.31" y1="111.55" x2="44.31" y2="102.16"/><line class="cls-1" x1="31.8" y1="111.55" x2="31.8" y2="102.16"/></svg>

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 796 B

View file

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128.83 128.83"><defs><style>.cls-1{fill:none;stroke:#000;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.93px;}</style></defs><path class="cls-1" d="M78.83,86.22h-3.92l-10.35,8.16-10.35-8.16h-4.2c-11.6,0-21,7.72-21,17.25v3.45H99.83v-3.45c0-9.52-9.4-17.25-21-17.25Z"/><line class="cls-1" x1="64.55" y1="94.38" x2="64.55" y2="106.91"/><line class="cls-1" x1="54.2" y1="86.22" x2="54.2" y2="78.18"/><line class="cls-1" x1="74.9" y1="86.22" x2="74.9" y2="78.18"/><line class="cls-1" x1="45.07" y1="66.74" x2="45.07" y2="54.89"/><line class="cls-1" x1="64.55" y1="48.49" x2="45.07" y2="54.89"/><line class="cls-1" x1="64.55" y1="72.05" x2="45.07" y2="66.74"/><line class="cls-1" x1="51.75" y1="58.02" x2="51.75" y2="62.65"/><line class="cls-1" x1="58.01" y1="56.66" x2="58.01" y2="64.15"/><line class="cls-1" x1="84.03" y1="66.74" x2="84.03" y2="54.89"/><line class="cls-1" x1="64.55" y1="48.49" x2="84.03" y2="54.89"/><line class="cls-1" x1="64.55" y1="72.05" x2="84.03" y2="66.74"/><line class="cls-1" x1="77.36" y1="58.02" x2="77.36" y2="62.65"/><line class="cls-1" x1="71.09" y1="56.66" x2="71.09" y2="64.15"/><line class="cls-1" x1="64.55" y1="54.16" x2="64.55" y2="66.51"/><path class="cls-1" d="M48.41,53.8c1.56-6.47,5.39-11.64,10.27-13.95,1.79-.85,3.73-1.31,5.74-1.31,4.42,0,8.45,2.22,11.44,5.86,2.08,2.51,3.66,5.7,4.55,9.3"/><path class="cls-1" d="M48.63,67.71c2.32,8.41,8.51,14.42,15.79,14.42s13.43-5.98,15.77-14.34"/><path class="cls-1" d="M58.68,39.85c-.9-2.01-1.27-4.22-.8-6.34,.95-4.24,5-7.16,9.17-8.38s8.59-1.15,12.9-1.75c4.3-.59,8.8-2.04,11.47-5.46-.21,7.17-4.64,14.05-11.07,17.21,4.72-.51,8.59-.81,13.36-2.72-1.49,12.03-11.03,11.64-17.85,11.99"/><line class="cls-1" x1="45.07" y1="106.91" x2="37.96" y2="89.34"/><line class="cls-1" x1="83.24" y1="106.91" x2="90.36" y2="89.34"/></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View file

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128.83 128.83"><defs><style>.cls-1{fill:none;stroke:#000;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.93px;}</style></defs><path class="cls-1" d="M65.9,32.74c5.31,0,9.61-4.3,9.61-9.61s-4.3-9.61-9.61-9.61-9.61,4.3-9.61,9.61,4.3,9.61,9.61,9.61Z"/><path class="cls-1" d="M57.65,73.18h-20.24c.17-7.24,2.05-21.06,12.97-29.25l6.2,12.6c.51,1.03,1.39,1.76,2.39,2.12v.25h25.03c2.34,0,4.24-1.9,4.24-4.24s-1.9-4.24-4.24-4.24h-19.5l-7.98-16.2c-.03-.06-.07-.11-.1-.16-.14-.44-.33-.87-.57-1.29-1.85-3.24-5.97-4.36-9.2-2.51-26.12,14.92-22.68,47.81-22.52,49.2,.11,.97,.43,1.87,.9,2.66,.69,2.19,2.71,3.78,5.13,3.78h21.18v22.76c0,3.67,2.97,6.65,6.65,6.65s6.65-2.98,6.65-6.65v-28.51c0-3.85-3.12-6.98-6.98-6.98Z"/><path class="cls-1" d="M104.68,50.51c.68-1.58-.06-3.41-1.64-4.09-1.58-.67-3.41,.06-4.09,1.64l-6.69,15.64h-16.75c-1.72,0-3.11,1.39-3.11,3.11s1.39,3.11,3.11,3.11h18.8c.19,0,.38-.02,.56-.06,.05,0,.11-.02,.16-.03,.13-.03,.26-.07,.39-.12,.05-.02,.1-.03,.14-.05,.16-.07,.32-.16,.47-.25,.03-.02,.06-.04,.09-.07,.12-.08,.23-.18,.34-.28,.04-.04,.08-.08,.11-.12,.09-.1,.18-.2,.26-.31,.03-.04,.06-.08,.09-.13,.1-.15,.19-.31,.26-.47h0v-.02l7.49-17.51Z"/></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
public/assets/games/4x3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

BIN
public/social/tg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
public/social/vk.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
public/social/youtube.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -23,7 +23,3 @@ body,
#root { #root {
height: 100%; height: 100%;
} }
img {
max-width: 100%;
}

View file

@ -25,4 +25,11 @@
background-repeat: no-repeat; background-repeat: no-repeat;
background-size: 180% auto; background-size: 180% auto;
background-position: center center; background-position: center center;
&_icon {
display: inline-block;
width: 50px;
margin: 0 var(--space-xs) 0 0;
vertical-align: middle;
}
} }

View file

@ -16,15 +16,15 @@ function Banner({ className }: IBannerProps) {
const { const {
ref, ref,
link, link,
title,
banner, banner,
bannerText, text,
textIcon,
color, color,
backgroundColor, backgroundColor,
} = config; } = config;
const props = { const props = {
title, title: text,
to: link || '', to: link || '',
target: '_blank', target: '_blank',
className, className,
@ -43,29 +43,31 @@ function Banner({ className }: IBannerProps) {
); );
} }
if (!banner) { const textFromRef = (ref || '').split('_').splice(1).join(' ').toUpperCase();
const textFromRef = (ref || '').split('_').splice(1).join(' ').toUpperCase(); const background = backgroundColor
const background = backgroundColor ? backgroundColor
? backgroundColor : 'linear-gradient(135deg, rgba(64,117,252,1) 0%, rgba(172,179,246,1) 100%)';
: 'linear-gradient(135deg, rgba(64,117,252,1) 0%, rgba(172,179,246,1) 100%)';
return ( return (
<Link {...props}> <Link {...props}>
<div <div
title={title} title={text}
className={style.banner} className={style.banner}
style={{ style={{
color: color, color,
background, background,
}} }}
> >
{bannerText || textFromRef || ''} {textIcon ? (
</div> <img
</Link> src={textIcon}
); className={style.banner_icon}
} />
) : null}
return null; {text || textFromRef || ''}
</div>
</Link>
);
} }
export default Banner; export default Banner;

View file

@ -4,6 +4,8 @@ import IHashMap from 'ts/interfaces/HashMap';
import Description from 'ts/components/Description'; import Description from 'ts/components/Description';
import ShowSymbol from 'ts/components/ShowSymbol'; import ShowSymbol from 'ts/components/ShowSymbol';
import { shuffle } from 'ts/helpers/random'; import { shuffle } from 'ts/helpers/random';
import GameBanner from 'ts/components/GameBanner';
import styleBanner from 'ts/components/GameBanner/index.module.scss';
import CityMap from './components/CityMap'; import CityMap from './components/CityMap';
import style from './style/wrapper.module.scss'; import style from './style/wrapper.module.scss';
@ -48,14 +50,16 @@ function CityBuilder({
return ( return (
<> <>
<ShowSymbol <GameBanner src="./assets/games/citybuilder.jpg">
text={selected.title} <ShowSymbol
length={20} text={selected.title}
/> length={20}
<Description />
className={style.city_builder_description} <Description
text={`Сейчас в проекте есть ${selected.value || 0} файлов созданных этим пользователем. Это примерно ${percent}% от всех файлов в проекте.`} className={styleBanner.game_banner_text}
/> text={`Сейчас в проекте есть ${selected.value || 0} файлов созданных этим пользователем. Это примерно ${percent}% от всех файлов в проекте.`}
/>
</GameBanner>
<div className={style.city_builder_control}> <div className={style.city_builder_control}>
<button <button
disabled={!selectedIndex} disabled={!selectedIndex}

View file

@ -0,0 +1,36 @@
@import 'src/styles/variables';
.city_builder_banner {
position: relative;
display: block;
width: 100%;
height: 300px;
margin: var(--space-xxl) auto 0;
user-select: none;
background-size: auto 100%;
background-repeat: repeat;
background-position: top left;
&_description {
position: absolute;
left: 0;
right: 0;
display: block;
width: 100%;
padding: 0 var(--space-xxl);
}
&_description {
bottom: 0;
padding-top: var(--space-xxl);
background-color: rgba(0, 0, 0, 0.7);
}
&_text {
margin: var(--space-s) auto;
color: white;
}
}

View file

@ -6,7 +6,7 @@
.city_builder_control { .city_builder_control {
position: relative; position: relative;
margin: var(--space-xxl) auto; margin: 0 auto var(--space-xxl);
user-select: none; user-select: none;
background-color: var(--color-13); background-color: var(--color-13);
@ -36,10 +36,6 @@
background-size: auto 30%; background-size: auto 30%;
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: center center; background-position: center center;
&:hover {
background-color: rgba(0, 0, 0, 0.2);
}
} }
&_prev { &_prev {

View file

@ -3,22 +3,10 @@ import React, { ReactNode } from 'react';
import Button from 'ts/components/UiKit/components/Button'; import Button from 'ts/components/UiKit/components/Button';
import notificationsStore from 'ts/components/Notifications/store'; import notificationsStore from 'ts/components/Notifications/store';
import localization from 'ts/helpers/Localization'; import localization from 'ts/helpers/Localization';
import copyInBuffer from 'ts/helpers/copyInBuffer';
import style from './index.module.scss'; import style from './index.module.scss';
function copyInBuffer(value?: string) {
if (!value) return;
const copyTextarea = document.createElement('textarea');
copyTextarea.style.position = 'fixed';
copyTextarea.style.opacity = '0';
copyTextarea.textContent = value;
document.body.appendChild(copyTextarea);
copyTextarea.select();
document.execCommand('copy');
document.body.removeChild(copyTextarea);
}
interface IConsoleProps { interface IConsoleProps {
textForCopy?: string; textForCopy?: string;
className?: string; className?: string;
@ -36,16 +24,18 @@ function Console({ className, textForCopy, children }: IConsoleProps) {
<div className={`${style.console_body}`}> <div className={`${style.console_body}`}>
{children || textForCopy} {children || textForCopy}
</div> </div>
<Button {textForCopy ? (
mode="second" <Button
className={`${style.console_copy}`} mode="second"
onClick={() => { className={`${style.console_copy}`}
copyInBuffer(textForCopy); onClick={() => {
notificationsStore.show(localization.get('uiKit.console.notification')); copyInBuffer(textForCopy);
}} notificationsStore.show(localization.get('uiKit.console.notification'));
> }}
{localization.get('uiKit.console.button')} >
</Button> {localization.get('uiKit.console.button')}
</Button>
) : null}
</div> </div>
); );
} }

View file

@ -30,7 +30,7 @@ function getTextWithLink(text: string, className?: string) {
return (<>{parts}</>) ; return (<>{parts}</>) ;
} }
function getTextWithStyle(text: string, className?: string) { export function getTextWithStyle(text: string, className?: string) {
const parts = (text || '') const parts = (text || '')
.split('*') .split('*')
.map((value: string, index: number) => (index % 2 .map((value: string, index: number) => (index % 2

View file

@ -52,6 +52,7 @@ export function getOnDrop(setLoading: Function, onChange: Function) {
.map((file: any) => file.kind === 'file' ? file?.getAsFile() : null) .map((file: any) => file.kind === 'file' ? file?.getAsFile() : null)
.filter(file => file); .filter(file => file);
console.log(files);
setLoading(false); setLoading(false);
if (!files.length) return; if (!files.length) return;

View file

@ -0,0 +1,33 @@
@import 'src/styles/variables';
.game_banner {
position: relative;
display: block;
width: 100%;
height: 300px;
margin: var(--space-xxl) auto 0;
user-select: none;
background-size: auto 100%;
background-repeat: repeat;
background-position: top left;
&_description {
position: absolute;
left: 0;
right: 0;
bottom: 0;
display: block;
width: 100%;
padding: var(--space-xxl) var(--space-xxl) 0;
background-color: rgba(0, 0, 0, 0.7);
}
&_text {
margin: var(--space-s) auto;
color: white;
}
}

View file

@ -0,0 +1,30 @@
import React, { ReactNode } from 'react';
import style from './index.module.scss';
interface IGameBannerProps {
src?: string;
children?: ReactNode | string | null;
}
function GameBanner({
src,
children,
}: IGameBannerProps): React.ReactElement | null {
if (!src) return null;
return (
<div
className={style.game_banner}
style={{ backgroundImage: `url(${src})` }}
>
{children ? (
<div className={style.game_banner_description}>
{children}
</div>
) : null}
</div>
);
}
export default GameBanner;

View file

@ -0,0 +1,53 @@
import React, { useEffect, useState } from 'react';
import style from '../styles/index.module.scss';
const SOURCE_CODE = `JIRA-1227 fix(profile): change validation
Counting objects: 100% (22/22), done.
Delta compression using up to 8 threads
JIRA-323 fix: change validation
Writing objects: 100% (12/12), 1.88 KiB | 1.88 MiB/s, done.
Total 12 (delta 9), reused 0 (delta 0), pack-reused 0
`.split('\n');
function getRandomText(index: number, messages?: string[]) {
const source = messages || SOURCE_CODE;
const splitIndex = index % source.length;
const start = source.slice(0, splitIndex);
const end = source.slice(splitIndex);
return [...end, ...start]
.slice(0, 13)
.map((text: string) => (<p key={text}>{text}</p>));
}
interface IMarqueeProps {
commitsInDay: number;
messages?: string[];
}
function Marquee({
commitsInDay,
messages,
}: IMarqueeProps) {
const [index, setIndex] = useState<number>(0);
const text = getRandomText(index, messages);
useEffect(() => {
const delay = 3000 * 6;
const speed = Math.ceil(delay / (commitsInDay || 1));
const timer = setInterval(() => {
setIndex((value: number) => value + 1);
}, speed);
return () => {
clearInterval(timer);
};
}, [commitsInDay]);
return (
<div className={style.game_console_monitor}>
<p id={`game-console-text-${index}`}>{text}</p>
</div>
);
}
export default Marquee;

View file

@ -0,0 +1,57 @@
import React, { useState } from 'react';
import { observer } from 'mobx-react-lite';
import dataGripStore from 'ts/store/DataGrip';
import SelectWithButtons from 'ts/components/UiKit/components/SelectWithButtons';
import Marquee from './components/marquee';
import style from './styles/index.module.scss';
function getSpeedAndMessages(user: string) {
const byTimestamp = dataGripStore.dataGrip.timestamp.statisticByAuthor[user];
const commitsInDay = byTimestamp.commitsByTimestampCounter.max;
const startIndex = byTimestamp.allCommitsByTimestamp.length - 20;
const messages = byTimestamp.allCommitsByTimestamp.slice(startIndex)
.reduce((acc: string[], commit: any) => acc.concat(commit.messages), []);
return {
speed: commitsInDay,
messages: Array.from(new Set(messages)) as string[],
};
}
const GameConsole = observer((): React.ReactElement => {
const employment = dataGripStore.dataGrip.author.employment;
const authors = [
...employment.active,
...employment.dismissed,
].map((title: string) => ({ id: title, title }));
const [user, setUser] = useState<any>(authors[0].id);
const { speed, messages } = getSpeedAndMessages(user);
return (
<div
className={style.game_console}
>
<Marquee
commitsInDay={speed}
messages={messages}
/>
<SelectWithButtons
value={user}
options={authors}
className={style.game_console_select}
onChange={(newUser: any) => {
setUser(newUser?.id || newUser);
}}
/>
</div>
);
});
export default GameConsole;

View file

@ -0,0 +1,39 @@
.game_console {
position: relative;
display: inline-block;
width: 100%;
margin: 0 auto var(--space-xxl);
text-align: left;
background-repeat: no-repeat;
background-size: auto 100%;
background-position: top left;
&_select {
display: block;
width: 500px;
}
&_monitor {
font-size: var(--font-s);
font-weight: 100;
display: block;
width: 500px;
height: 250px;
padding: 8px;
margin: 0 0 var(--space-xxl);
overflow: hidden;
box-sizing: border-box;
line-height: 1.3;
text-align: left;
white-space: nowrap;
border-radius: var(--border-radius-s);
border: 4px solid var(--color-32);
box-shadow: 0 0 10px var(--color-black);
color: #00B200;
background-color: var(--color-black);
}
}

View file

@ -0,0 +1,51 @@
@import 'src/styles/variables';
.locker {
position: absolute;
display: inline-block;
width: 30px;
height: 30px;
padding: 0;
margin: 0;
&_icon {
display: block;
width: 100%;
height: 100%;
}
&_border {
fill: none;
stroke: var(--color-border);
stroke-width: 1px;
}
&_center {
fill: white;
stroke: none;
stroke-width: 0;
}
&_sector {
fill: none;
stroke: var(--color-grey);
stroke-width: 50px;
transition: stroke-dasharray 1s;
}
&_text {
font-size: var(--font-s);
font-weight: 100;
position: absolute;
bottom: 8px;
left: 0;
display: block;
width: 100%;
text-align: center;
text-decoration: none;
color: var(--color-black);
}
}

View file

@ -0,0 +1,84 @@
import React, { useEffect, useState } from 'react';
import style from './index.module.scss';
interface ILockerProps {
delay: number;
callback?: Function;
className?: string;
sectorClassName?: string;
borderClassName?: string;
}
function Locker({
delay,
callback,
className,
sectorClassName,
borderClassName,
}: ILockerProps): React.ReactElement | null {
const [dash, setDash] = useState<string>('');
const [value, setValue] = useState<number>(delay);
useEffect(() => {
const percent = Math.round(100 - (value * 100 / delay));
const strokeLength = Math.PI * 49.5;
const newDash = (strokeLength / 100) * percent;
setDash(`${newDash}, ${strokeLength}`);
if (percent >= 99) {
if (callback) callback();
return;
}
setTimeout(() => {
setValue((next: number) => next - 1);
}, 1000);
}, [value]);
if (!delay || !value) return null;
return (
<div className={`${style.locker} ${className || ''}`}>
<svg
viewBox="0 0 100 100"
preserveAspectRatio="xMidYMid meet"
xmlns="http://www.w3.org/2000/svg"
className={style.locker_icon}
>
<circle
r="25"
cx="50"
cy="50"
style={{ strokeDasharray: dash }}
className={`${style.locker_sector} ${sectorClassName || ''}`}
/>
<circle
r="49.5"
cx="50"
cy="50"
className={`${style.locker_border} ${borderClassName || ''}`}
/>
<circle
r="40"
cx="50"
cy="50"
className={`${style.locker_center} ${borderClassName || ''}`}
/>
</svg>
<p className={style.locker_text}>
{value}
</p>
</div>
);
}
Locker.defaultProps = {
delay: 60,
callback: undefined,
className: '',
sectorClassName: '',
borderClassName: '',
};
export default Locker;

View file

@ -1,26 +1,33 @@
import React, { ReactNode } from 'react'; import React, { ReactNode } from 'react';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import Locker from 'ts/components/Locker';
import style from '../styles/index.module.scss'; import style from '../styles/index.module.scss';
interface IHeaderProps { interface IHeaderProps {
id?: string, id?: string;
className?: string, delay?: number;
onClose?: Function, className?: string;
onClose?: Function;
setCanClose?: Function;
children?: ReactNode; children?: ReactNode;
} }
const Header = observer(({ const Header = observer(({
id, id,
delay,
className, className,
children, children,
onClose, onClose,
setCanClose,
}: IHeaderProps) => ( }: IHeaderProps) => (
<div <div
id={`${id || ''}-title`} id={`${id || ''}-title`}
className={`${style.modal_window_title} ${className || ''}`} className={`${style.modal_window_title} ${className || ''}`}
> >
{children} {children}
{onClose ? ( {onClose ? (
<img <img
id={`${id}-close`} id={`${id}-close`}
@ -32,6 +39,16 @@ const Header = observer(({
}} }}
/> />
) : null} ) : null}
{delay ? (
<Locker
delay={delay}
className={style.modal_window_locker}
callback={() => {
if (setCanClose) setCanClose(true);
}}
/>
) : null}
</div> </div>
)); ));

View file

@ -1,4 +1,4 @@
import React, { ReactNode, useEffect } from 'react'; import React, { ReactNode, useEffect, useState } from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import isMobile from 'ts/helpers/isMobile'; import isMobile from 'ts/helpers/isMobile';
@ -11,6 +11,8 @@ import style from './styles/index.module.scss';
interface IModalProps { interface IModalProps {
id?: string, id?: string,
delay?: number;
mode?: string | string[],
className?: string, className?: string,
onClose?: Function, onClose?: Function,
children?: ReactNode; children?: ReactNode;
@ -18,16 +20,20 @@ interface IModalProps {
function Modal({ function Modal({
id, id,
mode,
delay,
className, className,
onClose, onClose,
children, children,
}: IModalProps) { }: IModalProps) {
const [canClose, setCanClose] = useState<boolean>(!delay);
useEffect(globalScroll.useOnOff, []); useEffect(globalScroll.useOnOff, []);
const childrenWithProps = React.Children.map(children, (child) => (React.isValidElement(child) const childrenWithProps = React.Children.map(children, (child) => (React.isValidElement(child)
? React.cloneElement( ? React.cloneElement(
child, // @ts-ignore child, // @ts-ignore
{ onClose }, { onClose, delay, setCanClose },
) : child)); ) : child));
const customClass = isMobile const customClass = isMobile
@ -41,7 +47,7 @@ function Modal({
onClick={(event: any) => { onClick={(event: any) => {
event.stopPropagation(); event.stopPropagation();
if (event.target?.id !== `${id}-wrapper`) return; if (event.target?.id !== `${id}-wrapper`) return;
if (onClose) onClose(); if (onClose && canClose) onClose();
}} }}
> >
<div <div
@ -51,6 +57,12 @@ function Modal({
event.stopPropagation(); event.stopPropagation();
}} }}
> >
{mode === 'halo' ? (
<img
className={style.modal_window_halo}
src="./assets/sponsor/halo.png"
/>
) : null}
{childrenWithProps} {childrenWithProps}
</div> </div>
</div> </div>
@ -61,6 +73,7 @@ Modal.displayName = 'Modal';
Modal.defaultProps = { Modal.defaultProps = {
id: 'modal-window', id: 'modal-window',
delay: undefined,
className: '', className: '',
onClose: undefined, onClose: undefined,
children: undefined, children: undefined,

View file

@ -28,6 +28,8 @@
} }
.modal_window { .modal_window {
position: relative;
&_wrapper { &_wrapper {
position: fixed; position: fixed;
top: 0; top: 0;
@ -44,6 +46,16 @@
background-color: rgba(90, 90, 90, 0.2); background-color: rgba(90, 90, 90, 0.2);
} }
&_halo {
position: absolute;
top: -50px;
left: -80px;
z-index: -1;
display: block;
width: 560px;
animation: modal_window_halo 40s linear 0s infinite normal none running;
}
&_title, &_title,
&_body, &_body,
&_footer { &_footer {
@ -52,8 +64,6 @@
margin: 0 auto; margin: 0 auto;
box-sizing: border-box; box-sizing: border-box;
border-radius: 8px;
text-align: left; text-align: left;
white-space: normal; white-space: normal;
} }
@ -61,24 +71,30 @@
&_title { &_title {
position: relative; position: relative;
padding: 24px; padding: 24px;
border-radius: 8px 8px 0 0;
} }
&_body { &_body {
max-height: 60vh; max-height: 60vh;
padding: 0 24px; padding: 0 24px;
overflow: auto; overflow: auto;
line-height: 1.5;
} }
&_footer { &_footer {
padding: 24px; padding: 24px;
text-align: right; text-align: right;
border-radius: 0 0 8px 8px;
} }
&_locker,
&_close { &_close {
position: absolute; position: absolute;
top: 12px; top: 12px;
right: 12px; right: 12px;
}
&_close {
display: block; display: block;
width: 32px; width: 32px;
height: 32px; height: 32px;
@ -91,3 +107,20 @@
} }
} }
} }
@keyframes modal_window_halo {
from {
transform: rotateZ(0deg);
opacity: 0;
}
11% {
opacity: 0.4;
}
89% {
opacity: 0.4;
}
to {
transform: rotateZ(360deg);
opacity: 0;
}
}

View file

@ -4,6 +4,7 @@
position: fixed; position: fixed;
top: 12px; top: 12px;
right: 0; right: 0;
z-index: 4;
display: inline-block; display: inline-block;
&_item { &_item {

View file

@ -0,0 +1,51 @@
import React from 'react';
import IAnswer from '../interfaces/Answer';
import style from '../styles/answer.module.scss';
const IS_WRAPPER_MODE = {
error: true,
small: true,
};
interface IAnswerProps {
answer: IAnswer;
mode: string;
onClick: Function;
}
function Answer({
answer,
mode,
onClick,
}: IAnswerProps): React.ReactElement | null {
const className = [style.quize_answer];
if (mode === 'selected') className.push(style.quize_answer_selected);
if (mode === 'correct') className.push(style.quize_answer_correct);
if (mode === 'error') className.push(style.quize_answer_error);
if (mode === 'small') className.push(style.quize_answer_small);
const wrapperClass = [style.quize_answer_wrapper];
if (IS_WRAPPER_MODE[mode]) wrapperClass.push(style.quize_answer_wrapper_small);
return (
<div className={wrapperClass.join(' ')}>
<figure
className={className.join(' ')}
onClick={() => {
onClick();
}}
>
<img
className={style.quize_answer_icon}
src={answer.icon}
/>
<figcaption className={style.quize_answer_text}>
{answer.title}
</figcaption>
</figure>
</div>
);
}
export default Answer;

View file

@ -0,0 +1,23 @@
import React from 'react';
import style from '../styles/progress.module.scss';
interface IProgressProps {
progress: number;
}
function Progress({
progress,
}: IProgressProps): React.ReactElement | null {
return (
<div className={style.quize_progress}>
<div className={style.quize_progress_line}>
<div className={style.quize_progress_text}>
{progress}
</div>
</div>
</div>
);
}
export default Progress;

View file

@ -0,0 +1,103 @@
import React, { useEffect, useState } from 'react';
import UiKitButton from 'ts/components/UiKit/components/Button';
import IQuestion from '../interfaces/Question';
import IAnswer from '../interfaces/Answer';
import Progress from './Progress';
import Answer from './Answer';
import stylePage from '../styles/question.module.scss';
import style from '../styles/index.module.scss';
function getModes(answers: IAnswer[], selected: IAnswer | null) {
return (answers || []).map((answer: IAnswer) => {
if (answer?.score) return 'correct';
if (!answer?.score && answer === selected) return 'error';
return 'small';
});
}
interface IQuestionProps {
question: IQuestion;
progress: number;
onClick: Function;
}
function Question({
question,
progress,
onClick,
}: IQuestionProps): React.ReactElement | null {
const [selected, setSelected] = useState<IAnswer | null>(null);
const [disabled, setDisabled] = useState<boolean>(false);
const [mode, setMode] = useState<string[]>([]);
const formattedAnswers = question.answers || [];
useEffect(() => {
setMode([]);
setSelected(null);
setDisabled(false);
}, [question]);
if (!question) return null;
const answers = formattedAnswers.map((item: IAnswer, index: number) => (
<Answer
key={`${item.id || ''}|${item.title}`}
mode={mode[index]}
answer={item}
onClick={() => {
if (disabled) return;
if (selected !== item) {
const newModes = [];
newModes[index] = 'selected';
setMode(newModes);
setSelected(item);
return;
}
setDisabled(true);
setTimeout(() => {
setMode(getModes(formattedAnswers, selected));
}, 1000);
setTimeout(() => {
onClick(selected);
}, 3000);
}}
/>
));
return (
<div className={stylePage.quize_question}>
<Progress progress={progress}/>
<div className={stylePage.quize_question_body}>
<div className={style.quize_title}>
{question.title}
</div>
<div className={style.quize_question_answer}>
{answers}
</div>
<div className={style.quize_footer}>
<UiKitButton
disabled={disabled}
onClick={() => {
setDisabled(true);
setTimeout(() => {
setMode(getModes(formattedAnswers, selected));
}, 1000);
setTimeout(() => {
onClick(selected);
}, 3000);
}}
>
{question.button || 'Next question'}
</UiKitButton>
</div>
</div>
</div>
);
}
export default Question;

View file

@ -0,0 +1,45 @@
import React from 'react';
import UiKitButton from 'ts/components/UiKit/components/Button';
import IResult from '../interfaces/Result';
import stylePage from '../styles/result.module.scss';
import style from '../styles/index.module.scss';
interface IResultProps {
result: IResult;
onClick: Function;
}
function Result({
result,
onClick,
}: IResultProps): React.ReactElement | null {
return (
<section className={stylePage.quize_result}>
<h4 className={style.quize_title}>
{result.title}
</h4>
<img
className={style.quize_icon}
style={{ backgroundImage: `url(${result.icon})` }}
src="./assets/games/4x3.png"
/>
<p className={style.quize_description}>
{result.description}
</p>
<div className={style.quize_footer}>
<UiKitButton
onClick={() => {
onClick();
}}
>
Replay
</UiKitButton>
</div>
</section>
);
}
export default Result;

View file

@ -0,0 +1,45 @@
import React from 'react';
import UiKitButton from 'ts/components/UiKit/components/Button';
import IQuize from '../interfaces/Quize';
import stylePage from '../styles/start.module.scss';
import style from '../styles/index.module.scss';
interface IStartProps {
quize: IQuize;
onClick: Function;
}
function Start({
quize,
onClick,
}: IStartProps): React.ReactElement | null {
return (
<section className={stylePage.quize_start}>
<h4 className={style.quize_title}>
{quize.title}
</h4>
<img
className={style.quize_icon}
style={{ backgroundImage: `url(${quize.icon})` }}
src="./assets/games/4x3.png"
/>
<p className={style.quize_description}>
{quize.description}
</p>
<div className={style.quize_footer}>
<UiKitButton
onClick={() => {
onClick();
}}
>
{quize.button || 'GO'}
</UiKitButton>
</div>
</section>
);
}
export default Start;

View file

@ -0,0 +1,128 @@
import React, { useState } from 'react';
import Result from './Result';
import Question from './Question';
import Start from './Start';
import IQuize from '../interfaces/Quize';
import IQuestion from '../interfaces/Question';
import IAnswer from '../interfaces/Answer';
import IResult from '../interfaces/Result';
import { getResult, getQuestionByGroups } from '../helpers';
import style from '../styles/index.module.scss';
function getApplyInAnimation(setShowSlide: Function, delay: number) {
return (callback: Function) => {
setShowSlide(true);
setTimeout(() => {
callback();
}, delay / 2);
setTimeout(() => {
setShowSlide(false);
}, delay);
};
}
interface IQuizePageProps {
quize: IQuize;
onEnd: Function;
}
function QuizePage({
quize,
onEnd,
}: IQuizePageProps): React.ReactElement | null {
const [question, setQuestion] = useState<IQuestion>(quize.questions[0]);
const [result, setResult] = useState<IResult>(quize.results[0]);
const [answers, setAnswers] = useState<IAnswer[]>([]);
const [view, setView] = useState<string>('start');
const [showSlide, setShowSlide] = useState<boolean>(false);
const applyInAnimation = getApplyInAnimation(setShowSlide, 1500);
const questions = getQuestionByGroups(quize.questions);
let page: any = null;
if (view === 'start') {
page = (
<Start
quize={quize}
onClick={() => {
applyInAnimation(() => {
setView('question');
});
}}
/>
);
}
if (view === 'question') {
page = (
<Question
question={question}
progress={30}
onClick={(answer: IAnswer) => {
const nextById = questions.byId[answer.nextQuestionId || ''];
const nextByIndex = questions.byIndex[question.index + 1];
const newResult = getResult(answers, quize.results);
setAnswers([...answers, answer]);
if (answer.isEnd) {
applyInAnimation(() => {
setResult(newResult);
setView('result');
});
} if (answer.nextQuestionId && nextById) {
applyInAnimation(() => {
setQuestion(nextById);
});
} else if (nextByIndex) {
applyInAnimation(() => {
setQuestion(nextByIndex);
});
} else {
applyInAnimation(() => {
setResult(newResult);
setView('result');
});
}
}}
/>
);
}
if (view === 'result') {
page = (
<Result
result={result}
onClick={() => {
applyInAnimation(() => {
onEnd();
setQuestion(quize.questions[0]);
setAnswers([]);
setView('start');
});
}}
/>
);
}
const className = showSlide
? `${style.quize_slider} ${style.quize_slider_animation}`
: style.quize_slider;
return (
<div
className={style.quize_container}
style={{ backgroundImage: 'url(./assets/games/quize.png)' }}
>
<div className={className}>
{page}
</div>
</div>
);
}
export default QuizePage;

View file

@ -0,0 +1,71 @@
export default {
title: 'Сотрудники отдела',
icon: './assets/games/wheel.jpg',
description: 'Текст с каким то описанием на три предложения, которые интригуют и манят пройти этот унылый квиз с небольшим количеством графики.',
questions: [
{
title: 'Сколь директорий создал Anatoliy?',
answers: [
{
title: '17',
icon: './assets/games/wheel.jpg',
score: 0,
},
{
title: '23',
icon: './assets/games/wheel.jpg',
score: 1,
},
{
title: '29',
icon: './assets/games/wheel.jpg',
score: 0,
},
],
},
{
title: 'Albert коммитит чаще Marrie?',
answers: [
{
title: 'Да',
icon: './assets/games/wheel.jpg',
score: 0,
},
{
title: 'Нет',
icon: './assets/games/wheel.jpg',
score: 1,
},
],
},
{
title: 'Самое длинное commit message оставил:',
answers: [
{
title: 'Kolya Elow',
icon: './assets/games/wheel.jpg',
score: 0,
},
{
title: 'Subrine Titan',
icon: './assets/games/wheel.jpg',
score: 0,
},
{
title: 'Grebenshikov Muz TV',
icon: './assets/games/wheel.jpg',
score: 1,
},
],
},
],
results: [
{
title: 'Поздравляем, пытка окончена',
icon: './assets/games/wheel.jpg',
description: 'Вы протестировали этот квиз и готовы написать на него отзыв длинной два или три предложения.',
'min': 0,
'max': 60,
},
],
};

View file

@ -0,0 +1,35 @@
import IQuestion from '../interfaces/Question';
import IAnswer from '../interfaces/Answer';
import IResult from '../interfaces/Result';
export function getQuestionByGroups(questions: IQuestion[]) {
const byId = {};
const byIndex = {};
questions.forEach((question: IQuestion, index: number) => {
if (question?.id) byId[question?.id] = question;
byIndex[index] = question;
question.index = index;
});
return { byId, byIndex };
}
export function getResult(answers: IAnswer[], results: IResult[]) {
const total = answers.reduce((sum: number, answer: IAnswer) => (
sum + (answer.score || 0)
), 0);
let result = results[0];
results.forEach((item: IResult) => {
if (item.min && item.max && total >= item.min && total <= item.max) {
result = item;
} else if (item.min && !item.max && total >= item.min) {
result = item;
} else if (!item.min && item.max && total <= item.max) {
result = item;
}
});
return result;
}

View file

@ -0,0 +1,20 @@
import React from 'react';
import IQuize from './interfaces/Quize';
import QuizePage from './components/index';
import example from './helpers/example';
interface IQuizeProps {
}
function Quize({}: IQuizeProps): React.ReactElement | null {
return (
<QuizePage
quize={example as IQuize}
onEnd={() => {
}}
/>
);
}
export default Quize;

View file

@ -0,0 +1,8 @@
export default interface IAnswer {
id?: number;
title: string;
icon?: string;
score?: number;
isEnd?: boolean;
nextQuestionId?: number;
}

View file

@ -0,0 +1,9 @@
import IAnswer from './Answer';
export default interface IQuestion {
id?: number;
index: number;
title: string;
answers?: IAnswer[];
button?: string;
}

View file

@ -0,0 +1,12 @@
import IQuestion from './Question';
import IResult from './Result';
export default interface IQuize {
id?: number;
icon?: string;
title?: string;
description?: string;
button?: string;
questions: IQuestion[];
results: IResult[];
}

View file

@ -0,0 +1,9 @@
export default interface IResult {
id?: number;
title: string;
icon?: string;
description?: string;
button?: string;
min?: number;
max?: number;
}

View file

@ -0,0 +1,112 @@
@import 'src/styles/variables';
.quize_answer {
width: 250px;
padding-bottom: var(--space-m);
cursor: pointer;
border-radius: var(--border-radius-s);
border: none;
box-shadow: 0 0 5px var(--color-grey);
transition: width 0.5s;
background-color: white;
animation-duration: 10s;
animation-iteration-count: infinite;
animation-name: quize_answer;
&_wrapper {
display: inline-block;
padding: 0;
vertical-align: top;
border-radius: var(--border-radius-s);
transition: padding 0.5s;
&_small {
padding: var(--space-s) var(--space-l);
}
}
&_selected {
box-shadow: 0 0 var(--space-l) var(--color-13), 0 0 var(--space-xxl) var(--color-13);
background-color: var(--color-13);
animation-name: quize_answer_selected;
}
&_correct {
box-shadow: 0 0 var(--space-l) var(--color-23), 0 0 var(--space-xxl) var(--color-23);
background-color: var(--color-23);
animation-name: quize_answer_selected;
}
&_error {
width: 218px;
box-shadow: 0 0 var(--space-l) var(--color-12), 0 0 var(--space-xxl) var(--color-12);
background-color: var(--color-12);
animation-name: quize_answer_selected;
}
&_small {
width: 218px;
}
&_icon {
display: block;
width: 100%;
margin: 0 auto var(--space-l);
border-radius: var(--border-radius-s) var(--border-radius-s) 0 0;
background-position: center center;
background-size: auto 100%;
background-repeat: no-repeat;
transition: background-size 0.5s;
&:hover {
background-size: auto 105%;
}
}
&_text {
font-size: var(--font-m);
font-weight: 100;
display: block;
margin: 0 auto;
text-align: center;
color: var(--color-black);
}
}
.quize_answer_wrapper + .quize_answer_wrapper {
margin-left: var(--space-xxl);
}
@keyframes quize_answer {
from {
box-shadow: none;
}
86% {
box-shadow: none;
}
93% {
box-shadow: 0 0 10px var(--color-13), 0 0 20px var(--color-13);
}
to {
box-shadow: none;
}
}
@keyframes quize_answer_selected {
from {
box-shadow: none;
}
50% {
box-shadow: 0 0 10px var(--color-13), 0 0 20px var(--color-13);
}
to {
box-shadow: none;
}
}

View file

@ -0,0 +1,100 @@
@import 'src/styles/variables';
.quize {
&_container {
position: relative;
display: block;
width: 100%;
height: 400px;
margin: 0 auto var(--space-xxl);
text-align: center;
overflow: hidden;
background-position: center center;
background-size: 10%;
background-repeat: repeat;
}
&_slider {
position: absolute;
display: block;
width: 100%;
height: 100%;
animation-duration: 1.5s;
animation-iteration-count: infinite;
animation-fill-mode: both;
&_animation {
animation-name: quize_slider;
}
}
&_footer {
position: absolute;
left: 0;
right: 0;
bottom: 0;
display: block;
width: 100%;
padding: var(--space-xxl) 0;
text-align: center;
}
&_title,
&_description {
font-weight: 100;
margin: 0 auto;
text-align: center;
color: var(--color-black);
}
&_title {
font-size: var(--font-l);
margin: 0 auto var(--space-xxl);
}
&_description {
font-size: var(--font-m);
}
&_icon {
display: block;
width: 100%;
max-width: 300px;
margin: 0 auto var(--space-xxl);
border: var(--space-s) solid var(--color-border);
box-shadow: 0 0 5px var(--color-grey);
background-color: var(--color-border);
background-position: center center;
background-size: auto 100%;
background-repeat: no-repeat;
}
}
@keyframes quize_slider {
from {
right: 0;
left: auto;
opacity: 1;
}
49% {
right: 100%;
opacity: 0;
}
51% {
right: auto;
left: 100%;
opacity: 0;
}
to {
left: 0;
opacity: 1;
}
}

View file

@ -0,0 +1,6 @@
@import 'src/styles/variables';
.quize_progress {
&_description {
}
}

View file

@ -0,0 +1,16 @@
@import 'src/styles/variables';
.quize_question {
&_title {
font-size: var(--font-l);
font-weight: 100;
margin: 0 auto var(--space-xxl);
text-align: center;
color: var(--color-black);
}
&_answer {
display: block;
text-align: center;
}
}

View file

@ -0,0 +1,35 @@
@import 'src/styles/variables';
.quize_result {
&_title,
&_description {
font-weight: 100;
margin: 0 auto;
text-align: center;
color: var(--color-black);
}
&_title {
font-size: var(--font-l);
margin: 0 auto var(--space-xxl);
}
&_description {
font-size: var(--font-m);
}
&_icon {
display: block;
width: 100%;
max-width: 300px;
margin: 0 auto var(--space-xxl);
border: 1px solid var(--color-border);
box-shadow: 0 0 5px var(--color-grey);
background-color: var(--color-border);
background-position: center center;
background-size: auto 100%;
background-repeat: no-repeat;
}
}

View file

@ -0,0 +1,4 @@
@import 'src/styles/variables';
.quize_start {
}

View file

@ -2,6 +2,7 @@ import React, { useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import UiKitButton from 'ts/components/UiKit/components/Button'; import UiKitButton from 'ts/components/UiKit/components/Button';
import GameBanner from 'ts/components/GameBanner';
import { shuffle } from 'ts/helpers/random'; import { shuffle } from 'ts/helpers/random';
import Track from './components/Track'; import Track from './components/Track';
@ -37,19 +38,23 @@ function Races({
}); });
return ( return (
<div className={style.races}> <>
{!showAnimation && ( <GameBanner src="./assets/games/races.jpg">
<UiKitButton {!showAnimation && (
className={style.races_button} <UiKitButton
onClick={() => { className={style.races_button}
setShowAnimation(true); onClick={() => {
}} setShowAnimation(true);
> }}
{t('uiKit.races.go')} >
</UiKitButton> {t('uiKit.races.go')}
)} </UiKitButton>
{lines} )}
</div> </GameBanner>
<div className={style.races}>
{lines}
</div>
</>
); );
} }

View file

@ -24,10 +24,7 @@
} }
&_button { &_button {
position: absolute; margin-bottom: var(--space-xxl);
top: var(--space-l);
left: calc(50% - 100px);
z-index: 1;
} }
} }

View file

@ -57,6 +57,7 @@ const RecommendationDescription = observer(() => {
<Footer className={style.recommendations_modal_footer}> <Footer className={style.recommendations_modal_footer}>
<UiKitButton <UiKitButton
mode={[ isMobile ? 'primary' : 'border', 'full_size']} mode={[ isMobile ? 'primary' : 'border', 'full_size']}
className={style.recommendations_modal_button}
onClick={() => { onClick={() => {
recommendationStore.close(); recommendationStore.close();
}} }}

View file

@ -13,7 +13,7 @@
} }
&_footer { &_footer {
padding: var(--space-s) var(--space-xxl) var(--space-l) var(--space-xxl); padding: var(--space-s) var(--space-xxl) 0 var(--space-xxl);
} }
&_sub_title { &_sub_title {
@ -56,6 +56,11 @@
border-bottom-color: var(--color-temp-border); border-bottom-color: var(--color-temp-border);
} }
&_button {
color: var(--color-temp-title);
border-color: var(--color-temp-border);
}
border: 1px solid var(--color-border); border: 1px solid var(--color-border);
border-left-width: var(--space-s); border-left-width: var(--space-s);
background-color: var(--color-temp-bg); background-color: var(--color-temp-bg);

View file

@ -57,13 +57,13 @@ function ShowSymbol({
paddingTop: mode === 'table-row' ? '8px' : 0, paddingTop: mode === 'table-row' ? '8px' : 0,
}} }}
> >
{line}
<Button <Button
mode={mode} mode={mode}
onClick={() => setShowAll(true)} onClick={() => setShowAll(true)}
> >
» »
</Button> </Button>
{line}
</div> </div>
); );
} }

View file

@ -0,0 +1,28 @@
import React from 'react';
import { Link } from 'react-router-dom';
import getSocialLinks from '../helpers';
import style from '../styles/index.module.scss';
function SocialLinks() {
const buttons = Object.entries(getSocialLinks())
.map(([title, link]: string[]) => (
<Link
key={title}
className={style.sponsor_button}
to={link}
target="_blank"
>
{title}
</Link>
));
return (
<div className={style.sponsor_button_wrapper}>
{buttons}
</div>
);
}
export default SocialLinks;

View file

@ -0,0 +1,52 @@
import React from 'react';
import { observer } from 'mobx-react-lite';
import UiKitButton from 'ts/components/UiKit/components/Button';
import { Modal, Header, Body, Footer } from 'ts/components/ModalWindow';
import sponsorStore from '../store';
import style from '../styles/index.module.scss';
const Money = observer((): React.ReactElement | null => {
return (
<Modal
mode="halo"
onClose={() => {
sponsorStore.close();
}}
>
<Header className={style.sponsor_title}>
Поддержите проект
</Header>
<Body className={style.sponsor_body}>
<p className={style.sponsor_text}>
Мы будем рады, если вы поддержите нас любой суммой! Все средства пойдут на дальнейшее развитие проекта.
</p>
<img
className={style.sponsor_cover}
src="./assets/sponsor/money.jpg"
/>
</Body>
<Footer className={style.sponsor_footer}>
<UiKitButton
mode={['primary', 'full_size']}
onClick={() => {
sponsorStore.close();
}}
>
Разовый платёж (СБП)
</UiKitButton>
<UiKitButton
mode={['border', 'full_size']}
onClick={() => {
sponsorStore.close();
}}
>
Подписка GitHub Sponsor
</UiKitButton>
</Footer>
</Modal>
);
});
export default Money;

View file

@ -0,0 +1,53 @@
import React from 'react';
import { observer } from 'mobx-react-lite';
import { useTranslation } from 'react-i18next';
import { Modal, Header, Body, Footer } from 'ts/components/ModalWindow';
import UiKitButton from 'ts/components/UiKit/components/Button';
import { getTextWithStyle } from 'ts/components/Description';
import notificationsStore from 'ts/components/Notifications/store';
import localization from 'ts/helpers/Localization';
import copyInBuffer from 'ts/helpers/copyInBuffer';
import SocialLinks from './buttons';
import sponsorStore from '../store';
import style from '../styles/index.module.scss';
const Share = observer((): React.ReactElement | null => {
const { t } = useTranslation();
const text = localization.get('page.sponsor.share.description');
return (
<Modal
mode="halo"
delay={10}
onClose={() => {
sponsorStore.close();
}}
>
<Header className={style.sponsor_title}>
{t('page.sponsor.share.title')}
</Header>
<Body className={style.sponsor_body}>
<p className={style.sponsor_text}>
{getTextWithStyle(text)}
</p>
<SocialLinks/>
</Body>
<Footer className={style.sponsor_footer}>
<UiKitButton
mode={['primary', 'full_size']}
onClick={() => {
copyInBuffer('https://github.com/bakhirev/assayo');
notificationsStore.show(localization.get('uiKit.console.notification'));
}}
>
{t('page.sponsor.share.button')}
</UiKitButton>
</Footer>
</Modal>
);
});
export default Share;

View file

@ -0,0 +1,35 @@
import IHashMap from 'ts/interfaces/HashMap';
export default function getSocialLinks(): IHashMap<string> {
const link = 'https://github.com/bakhirev/assayo';
const title = 'Visualization and analysis of git repository';
const subTitle = 'Check your git stats!';
const description = '';
const tags = 'IT,git,statistics,audit,data-visualization,report';
return {
Facebook: `http://www.facebook.com/sharer.php?u=${link}`,
VK: `http://vk.com/share.php?url=${link}&title=${title}&comment=${subTitle}`,
QQ: `http://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey?url=${link}`,
Reddit: `https://reddit.com/submit?url=${link}&title=${title}`,
X: `https://twitter.com/intent/tweet?url=${link}&text=${description}&via=&hashtags=${tags}`,
LinkedIn: `https://www.linkedin.com/sharing/share-offsite/?url=${link}`,
OK: `https://connect.ok.ru/dk?st.cmd=WidgetSharePreview&st.shareUrl=${link}`,
Tumblr: `https://www.tumblr.com/widgets/share/tool?canonicalUrl=${link}&title=${title}&caption=${subTitle}&tags=${tags}`,
Blogger: `https://www.blogger.com/blog-this.g?u=${link}&n=${title}&t=${subTitle}`,
Evernote: `https://www.evernote.com/clip.action?url=${link}&title=${description}`,
Addthis: `http://www.addthis.com/bookmark.php?url=${link}`,
GetPocket: `https://getpocket.com/edit?url=${link}`,
YCombinator: `https://news.ycombinator.com/submitlink?u=${link}&t=${title}`,
Buffer: `https://buffer.com/add?text=${description}&url=${link}`,
Flipboard: `https://share.flipboard.com/bookmarklet/popout?v=2&title=${description}&url=${link}`,
Instapaper: `http://www.instapaper.com/edit?url=${link}&title=${title}&description=${subTitle}`,
Renren: `http://widget.renren.com/dialog/share?resourceUrl=${link}&srcUrl=${link}&title=${title}&description=${subTitle}`,
'The diaspora* Project': `https://share.diasporafoundation.org/?title=${title}&url=${link}`,
Weibo: `http://service.weibo.com/share/share.php?url=${link}&appkey=&title=${title}&pic=&ralateUid=`,
Douban: `http://www.douban.com/recommend/?url=${link}&title=${description}`,
XING: `https://www.xing.com/spi/shares/new?url=${link}`,
// Threema: `threema://compose?text=${description}&id=`,
Line: `https://lineit.line.me/share/ui?url=${link}&text=${description}`,
};
}

View file

@ -0,0 +1,19 @@
import React from 'react';
import { observer } from 'mobx-react-lite';
import Money from './components/money';
import Share from './components/share';
import sponsorStore, { MODAL_TYPE } from './store';
const SponsorScreen = observer((): React.ReactElement | null => {
if (sponsorStore.type === MODAL_TYPE.MONEY) {
return <Money/>;
}
if (sponsorStore.type === MODAL_TYPE.SHARE) {
return <Share/>;
}
return null;
});
export default SponsorScreen;

View file

@ -0,0 +1,48 @@
import { observable, action, makeObservable } from 'mobx';
import isMobile from 'ts/helpers/isMobile';
import themeSettings from 'ts/store/ThemeSettings';
export const MODAL_TYPE = {
HIDE: 0,
MONEY: 1,
SHARE: 2,
};
class SponsorStore {
type: number = MODAL_TYPE.HIDE;
constructor() {
makeObservable(this, {
type: observable,
open: action,
close: action,
});
if (!isMobile && !themeSettings.getConfig()) {
this.setTimer();
}
}
setTimer() {
const ONE_MINUTE = 60 * 1000;
setInterval(() => {
if (this.type) return;
this.type = Math.random() > 0.5
? MODAL_TYPE.SHARE
: MODAL_TYPE.SHARE;
}, 6 * ONE_MINUTE);
}
open() {
this.type = MODAL_TYPE.MONEY;
}
close() {
this.type = MODAL_TYPE.HIDE;
}
}
const sponsorStore = new SponsorStore();
export default sponsorStore;

View file

@ -0,0 +1,69 @@
@import 'src/styles/variables';
@import 'src/ts/components/Description/index.module';
.sponsor {
&_title {
font-weight: bold;
text-align: center;
}
&_body {
padding: 0;
}
&_text {
font-size: var(--space-l);
font-weight: 100;
padding: 0 var(--space-xxl) var(--space-xxl);
text-align: center;
}
&_link {
color: var(--color-button);
&:hover {
text-decoration: underline;
}
}
&_footer {
padding: 0 var(--space-xxl);
}
&_button {
font-size: var(--font-xs);
font-weight: 100;
display: inline-block;
padding: var(--space-s);
margin: var(--space-xxs);
cursor: pointer;
text-decoration: none;
text-align: center;
white-space: nowrap;
border-radius: var(--border-radius-s);
border: 1px solid var(--color-border);
background-color: white;
color: var(--color-button);
&:hover {
text-decoration: underline;
}
&_wrapper {
display: block;
padding: var(--space-xs) 0;
margin: 0 auto;
text-align: center;
user-select: none;
border-radius: 0;
border-top: 1px solid var(--color-border);
border-bottom: 1px solid var(--color-border);
background: linear-gradient(135deg, rgba(26,115,232,1) 0%, rgba(159,167,255,1) 100%);
}
}
}

View file

@ -23,14 +23,16 @@ function getFormattedDate(commits: ICommit[]) {
function getTags(commits: ICommit[]) { function getTags(commits: ICommit[]) {
const uniqueTypes = new Set(commits.map((commit: ICommit) => commit.type)); const uniqueTypes = new Set(commits.map((commit: ICommit) => commit.type));
const tags = Array.from(uniqueTypes).map((title: string) => ( const tags = Array.from(uniqueTypes)
<p .filter((title: string) => title && title !== '—')
key={title} .map((title: string) => (
className={style.tempo_task_tag} <p
> key={title}
{title} className={style.tempo_task_tag}
</p> >
)); {title}
</p>
));
return (<>{tags}</>); return (<>{tags}</>);
} }
@ -48,14 +50,18 @@ function Task({ title, commits }: ITaskProps) {
> >
<div className={style.tempo_task_header}> <div className={style.tempo_task_header}>
<div> <div>
<ExternalLink {title ? (
text={title} <ExternalLink
link={`${userSettings?.settings?.linksPrefix?.task || '/'}${title}`} text={title}
/> link={`${userSettings?.settings?.linksPrefix?.task || '/'}${title}`}
<ExternalLink />
text="PR" ) : '—'}
link={`${userSettings?.settings?.linksPrefix?.pr || '/'}${prId}`} {prId ? (
/> <ExternalLink
text="PR"
link={`${userSettings?.settings?.linksPrefix?.pr || '/'}${prId}`}
/>
) : null}
</div> </div>
<div className={style.tempo_task_tags}> <div className={style.tempo_task_tags}>
{getTags(commits)} {getTags(commits)}

View file

@ -51,8 +51,8 @@ export default class DataGripByTasks {
prDelayDays: pr?.delayDays, prDelayDays: pr?.delayDays,
prAuthor: firstCommit.author === pr?.author ? null : pr?.author, prAuthor: firstCommit.author === pr?.author ? null : pr?.author,
comments: firstCommit.text, comments: firstCommit.text,
types: firstCommit.type && firstCommit.type !== 'не подписан' ? [firstCommit.type] : [], types: firstCommit.type && firstCommit.type !== '' ? [firstCommit.type] : [],
scope: firstCommit.scope && firstCommit.scope !== 'неопределенна' ? [firstCommit.scope] : [], scope: firstCommit.scope && firstCommit.scope !== '' ? [firstCommit.scope] : [],
}; };
if (commits.length === 1) return shortInfo; if (commits.length === 1) return shortInfo;
@ -64,8 +64,8 @@ export default class DataGripByTasks {
commits.forEach((commit: ICommit) => { commits.forEach((commit: ICommit) => {
authors.add(commit.author); authors.add(commit.author);
messages.add(commit.text); messages.add(commit.text);
if (commit.type !== 'не подписан') types.add(commit.type); if (commit.type !== '') types.add(commit.type);
if (commit.scope !== 'неопределенна') scope.add(commit.scope); if (commit.scope !== '') scope.add(commit.scope);
}); });
const comments = Array.from(messages).join(', '); const comments = Array.from(messages).join(', ');

View file

@ -25,7 +25,8 @@ export default class FileGripByPaths {
if (file) { if (file) {
this.#updateDirtyFile(file, fileChange, commit); this.#updateDirtyFile(file, fileChange, commit);
} else { } else {
this.refFileIds[fileChange.id] = this.#getNewDirtyFile(fileChange, commit) as IDirtyFile; file = this.#getNewDirtyFile(fileChange, commit) as IDirtyFile;
this.refFileIds[fileChange.id] = file;
} }
if (fileChange.newId) { if (fileChange.newId) {

View file

@ -35,8 +35,8 @@ export default function getCommitInfo(logString: string): ICommit | ISystemCommi
message, message,
text: '', text: '',
type: 'не подписан', type: '',
scope: 'неопределенна', scope: '',
fileChanges: [], fileChanges: [],
}; };
@ -98,8 +98,8 @@ export default function getCommitInfo(logString: string): ICommit | ISystemCommi
task, task,
taskNumber, taskNumber,
text, text,
type: type || 'не подписан', type: type || '',
scope: scope || 'неопределенна', scope: scope || '',
changes: 0, changes: 0,
added: 0, added: 0,

View file

@ -71,7 +71,7 @@ export function getTypeAndScope(message: string, task: string) {
// ABC-123, #123, gh-123 // ABC-123, #123, gh-123
export function getTask(message: string) { export function getTask(message: string) {
return ((message || '').match(/(([A-Z]+-)|(#)|(gh-)|(GH-))([0-9]+)/gm) || [])[0] || ''; return ((message || '').match(/(([A-Z]+[-_])|(#)|(gh-)|(GH-))([0-9]+)/gm) || [])[0] || '';
} }
// ABC-123 => '123'; // ABC-123 => '123';

View file

@ -10,14 +10,20 @@ class AchievementsByAuthor {
this.authors[name] = []; this.authors[name] = [];
} }
add(authors: Array<[string, number]>, maxAchievementCode: string, minAchievementCode?: string) { add(
authors: Array<[string, number]>,
maxAchievementCode?: string,
minAchievementCode?: string,
) {
const first = authors?.[0]?.[0]; const first = authors?.[0]?.[0];
if (!first) return; if (!first) return;
this.authors?.[first]?.push(maxAchievementCode); if (maxAchievementCode) {
this.authors?.[first]?.push(maxAchievementCode);
if (!minAchievementCode) return; }
const last = authors?.[authors.length - 1]?.[0]; if (minAchievementCode) {
this.authors?.[last]?.push(minAchievementCode); const last = authors?.[authors.length - 1]?.[0];
this.authors?.[last]?.push(minAchievementCode);
}
} }
} }
@ -53,7 +59,7 @@ class AchievementsByCompetition {
byAuthor.add(total.allDaysInProject, 'moreDaysInProject', 'lessDaysInProject'); byAuthor.add(total.allDaysInProject, 'moreDaysInProject', 'lessDaysInProject');
// Дата первого коммита // Дата первого коммита
byAuthor.add(total.firstCommit, 'adam'); byAuthor.add(total.firstCommit, null, 'adam');
// Количество метки «рефакторинг» // Количество метки «рефакторинг»
byAuthor.add(total.moreRefactoring, 'moreRefactoring'); byAuthor.add(total.moreRefactoring, 'moreRefactoring');

View file

@ -0,0 +1,12 @@
export default function copyInBuffer(value?: string) {
if (!value) return;
const copyTextarea = document.createElement('textarea');
copyTextarea.style.position = 'fixed';
copyTextarea.style.opacity = '0';
copyTextarea.textContent = value;
document.body.appendChild(copyTextarea);
copyTextarea.select();
document.execCommand('copy');
document.body.removeChild(copyTextarea);
}

View file

@ -1,18 +1,18 @@
interface IBanner { interface IBanner {
isDefault?: boolean;
ref?: string; ref?: string;
link?: string; link?: string;
isOpenInNewTab?: boolean; isOpenInNewTab?: boolean;
/* Логотип */ /* Логотип */
icon?: string; logo?: string;
title?: string; title?: string;
/* Картинка баннера */ /* Картинка баннера */
banner?: string; banner?: string;
/* Текстовы баннер */ /* Текстовы баннер */
bannerText?: string; text?: string;
textIcon?: string;
color?: string; color?: string;
backgroundColor?: string; backgroundColor?: string;
} }

View file

@ -4,6 +4,7 @@ import { observer } from 'mobx-react-lite';
import dataGripStore, { DataParseStatusEnum } from 'ts/store/DataGrip'; import dataGripStore, { DataParseStatusEnum } from 'ts/store/DataGrip';
import DropZone from 'ts/components/DropZone'; import DropZone from 'ts/components/DropZone';
import Sponsor from 'ts/components/Sponsor';
import SplashScreen from 'ts/components/SplashScreen'; import SplashScreen from 'ts/components/SplashScreen';
import Confirm from 'ts/components/ModalWindow/Confirm'; import Confirm from 'ts/components/ModalWindow/Confirm';
@ -22,6 +23,7 @@ interface IViewWithChartsProps {
function ViewWithCharts({ showSplashScreen }: IViewWithChartsProps) { function ViewWithCharts({ showSplashScreen }: IViewWithChartsProps) {
return ( return (
<> <>
<Sponsor />
<Confirm /> <Confirm />
<Routes> <Routes>
<Route <Route

View file

@ -11,12 +11,11 @@ interface ILogoProps {
function Logo({ center }: ILogoProps) { function Logo({ center }: ILogoProps) {
const { const {
isDefault, logo,
icon,
link, link,
title, title,
isOpenInNewTab, } = themeSettings.getLogo() || {};
} = themeSettings.getLogo(); const isDefault = logo === './assets/logo.svg';
return ( return (
<figure <figure
@ -26,11 +25,11 @@ function Logo({ center }: ILogoProps) {
> >
<Link <Link
to={link || ''} to={link || ''}
target={isOpenInNewTab ? '_blank' : ''} target={isDefault ? '' : '_blank'}
className={style.logo_link} className={style.logo_link}
> >
<img <img
src={icon || ''} src={logo || ''}
title={title || ''} title={title || ''}
className={isDefault className={isDefault
? `${style.logo_icon} ${style.logo_default}` ? `${style.logo_icon} ${style.logo_default}`

View file

@ -12,6 +12,9 @@ import ShowSymbol from 'ts/components/ShowSymbol';
import Column from 'ts/components/Table/components/Column'; import Column from 'ts/components/Table/components/Column';
import LineChart from 'ts/components/LineChart'; import LineChart from 'ts/components/LineChart';
import getOptions from 'ts/components/LineChart/helpers/getOptions'; import getOptions from 'ts/components/LineChart/helpers/getOptions';
import GameConsole from 'ts/components/GameConsole';
import GameBanner from 'ts/components/GameBanner';
import Quize from 'ts/components/Quize';
import { ColumnTypesEnum } from 'ts/components/Table/interfaces/Column'; import { ColumnTypesEnum } from 'ts/components/Table/interfaces/Column';
const TeamBuilding = observer((): React.ReactElement => { const TeamBuilding = observer((): React.ReactElement => {
@ -38,10 +41,16 @@ const TeamBuilding = observer((): React.ReactElement => {
return ( return (
<> <>
<Title title="Скорость коммитов в день"/>
<GameConsole />
<Title title="Квиз"/>
<Quize />
<Title title="Скорость закрытия задач"/> <Title title="Скорость закрытия задач"/>
<Races tracks={tracks} /> <Races tracks={tracks} />
<Title title="Максимальная длинна подписи коммита"/> <Title title="Максимальная длинна подписи коммита"/>
<GameBanner src="./assets/games/wheel.jpg" />
<DataView rows={maxMessageLength}> <DataView rows={maxMessageLength}>
<Column <Column
isFixed isFixed
@ -107,7 +116,7 @@ const TeamBuilding = observer((): React.ReactElement => {
)} )}
/> />
</DataView> </DataView>
{'Квиз'}
{'Небоскребы вверх ввиде графика'} {'Небоскребы вверх ввиде графика'}
</> </>
); );

View file

@ -1,46 +1,57 @@
import { makeObservable, observable, action } from 'mobx'; import { makeObservable, observable, action } from 'mobx';
import IBanner from 'ts/interfaces/Banner';
import IHashMap from 'ts/interfaces/HashMap'; import IHashMap from 'ts/interfaces/HashMap';
import IBanner from 'ts/interfaces/Banner';
const LOGO: IBanner = { const LOGO: IBanner = {
isDefault: true, logo: './assets/logo.svg',
icon: './assets/logo.svg',
isOpenInNewTab: false, isOpenInNewTab: false,
}; };
const EXTERNAL_LOGO: IHashMap<IBanner> = { const EXTERNAL_LOGO: IHashMap<IBanner> = {
vk_frontend_du2: { vk_frontend_du2: {
icon: './social/vk/frontend_du2.png', logo: './social/vk/frontend_du2.png',
banner: './social/vk/frontend_du2.jpg', banner: './social/vk/frontend_du2.jpg',
link: 'https://vk.com/frontend_du2', link: 'https://vk.com/frontend_du2',
title: 'Сообщество о веб-разработке и программировании',
}, },
vk_take_off_staff: { vk_take_off_staff: {
icon: './social/vk/take_off_staff.png', logo: './social/vk/take_off_staff.png',
banner: './social/vk/take_off_staff.jpg', banner: './social/vk/take_off_staff.jpg',
link: 'https://vk.com/takeoff_staff', link: 'https://vk.com/takeoff_staff',
}, },
vk_awesomejs: {
icon: './social/vk/awesomejs.png',
// banner: './social/vk/awesomejs.jpg',
link: 'https://vk.com/awesomejs',
bannerText: 'Сбер Банк',
color: '#000',
backgroundColor: '#C2ECC1',
},
vk_frontend_dev: {
icon: './social/vk/frontend_dev.png',
banner: './social/vk/frontend_dev.jpg',
link: 'https://vk.com/frontend_dev',
},
vk_front_work: {
icon: './social/vk/front_work.png',
banner: './social/vk/front_work.jpg',
link: 'https://frontends.work/?ref=assayo',
},
}; };
function getDefaultBanner(ref: string): IBanner | null {
if (!ref) return null;
const parts = ref.split('_');
const type = parts.shift() || '';
const name = parts.join('_');
if (!name) return null;
return {
isOpenInNewTab: true,
logo: './assets/logo.svg',
link: {
vk: `https://vk.com/${name}`,
yt: `https://www.youtube.com/@${name}`,
tg: `https://t.me/@${name}`,
}[type],
text: name,
textIcon: {
vk: './social/vk.png',
yt: './social/youtube.png',
tg: './social/tg.png',
}[type],
color: '#FFFFFF',
backgroundColor: {
vk: '#5181B8',
yt: '#FE0000',
tg: '#29A6E6',
}[type] || '#EFC526',
};
}
class ThemeSettings { class ThemeSettings {
urlParameters: any = {}; urlParameters: any = {};
@ -55,23 +66,17 @@ class ThemeSettings {
this.urlParameters = urlParameters || {}; this.urlParameters = urlParameters || {};
} }
getLogo(): IBanner { getConfig(): IBanner {
const ref = this.urlParameters?.ref || ''; const ref = this.urlParameters?.ref || '';
let config = EXTERNAL_LOGO[ref]; return EXTERNAL_LOGO[ref] || getDefaultBanner(ref);
if (!config) return LOGO; }
config.ref = ref; getLogo(): IBanner | null {
config.isOpenInNewTab = true; return LOGO || this.getConfig() || LOGO;
return config;
} }
getBanner(): IBanner | null { getBanner(): IBanner | null {
let config = EXTERNAL_LOGO[this.urlParameters?.ref || '']; return this.getConfig();
if (!config) return null;
config.isOpenInNewTab = true;
return config;
} }
} }

View file

@ -87,4 +87,71 @@ export default `
§ achievements.moreStyle.description: tends to change CSS more than others § achievements.moreStyle.description: tends to change CSS more than others
§ achievements.moreOnHoliday.title: No life § achievements.moreOnHoliday.title: No life
§ achievements.moreOnHoliday.description: relatively many commits in non-working hours § achievements.moreOnHoliday.description: relatively many commits in non-working hours
§ achievements.morePRMerge.title: Таможня даёт добро
§ achievements.morePRMerge.description: more often than others, presses the "Merge" button for PR
§ achievements.longWaitPR.title: Завтра точно вольём
§ achievements.longWaitPR.description: создал PR, который больше месяца провисел на ревью
§ achievements.moreLongWaitPR.title: A long time ago in a galaxy far, far away
§ achievements.moreLongWaitPR.description: создал PR, который максимально долго провисел на ревью
§ achievements.oneExtension.title: Один в поле воин
§ achievements.oneExtension.description: только он работает с файлами определенного расширения
§ achievements.fileRush.title: Zerg Rush
§ achievements.fileRush.description: created the most files in the project
§ achievements.moreLintHint.title: Грамар-наци
§ achievements.moreLintHint.description: больше всех создал или изменил в правилах авто-проверки кода
§ achievements.moreReadMe.title: Летописец
§ achievements.moreReadMe.description: больше всех создал или изменил файлов MD
§ achievements.moreDevOps.title: DevOps
§ achievements.moreDevOps.description: больше всех создал или изменил файлов для CI/CD
§ achievements.moreTests.title: Тестировщик
§ achievements.moreTests.description: больше всех создал или изменил файлов для тестирования
§ achievements.allRelease.title: Фулл хаус
§ achievements.allRelease.description: есть релиз, собранный только из его задач
§ achievements.firstCommit.title: First come, first served
§ achievements.firstCommit.description: first commit in this project
§ achievements.lastCommit.title: I've finished
§ achievements.lastCommit.description: последний коммит на проекте
§ achievements.firstLastCommit.title: From beginning to end
§ achievements.firstLastCommit.description: первый и последний коммит на проекте
§ achievements.longFilePath.title: Закрома родины
§ achievements.longFilePath.description: the first created the file with the deepest directory
§ achievements.longFileName.title: Size matters
§ achievements.longFileName.description: created the file with the longest name
§ achievements.workOnWeekends.title: Work not walk
§ achievements.workOnWeekends.description: хоть раз работал на выходных
§ achievements.removeCreateFile.title: Откопал стюардессу
§ achievements.removeCreateFile.description: recover removed file
§ achievements.renameFile.title: Астана Нур-Султан Астана
§ achievements.renameFile.description: переименовывал туда-сюда файл
§ achievements.longTask.title: Easy task
§ achievements.longTask.description: worked on task more than three months
§ achievements.haveNotEmail.title: Mailman
§ achievements.haveNotEmail.description: empty email field in git config
§ achievements.moreAddedFolders.title: Director
§ achievements.moreAddedFolders.description: created the most directories
§ achievements.horoscope1.title: Mercury Retrograde for Capricorn
§ achievements.horoscope2.title: Mercury Retrograde for Aquarius
§ achievements.horoscope3.title: Mercury Retrograde for Pisces
§ achievements.horoscope4.title: Mercury Retrograde for Aries
§ achievements.horoscope5.title: Mercury Retrograde for Taurus
§ achievements.horoscope6.title: Mercury Retrograde for Gemini
§ achievements.horoscope7.title: Mercury Retrograde for Cancer
§ achievements.horoscope8.title: Mercury Retrograde for Leo
§ achievements.horoscope9.title: Mercury Retrograde for Virgo
§ achievements.horoscope10.title: Mercury Retrograde for Libra
§ achievements.horoscope11.title: Mercury Retrograde for Scorpio
§ achievements.horoscope12.title: Mercury Retrograde for Sagittarius
§ achievements.horoscope1.description: by the month of the first commit
§ achievements.horoscope2.description: by the month of the first commit
§ achievements.horoscope3.description: by the month of the first commit
§ achievements.horoscope4.description: by the month of the first commit
§ achievements.horoscope5.description: by the month of the first commit
§ achievements.horoscope6.description: by the month of the first commit
§ achievements.horoscope7.description: by the month of the first commit
§ achievements.horoscope8.description: by the month of the first commit
§ achievements.horoscope9.description: by the month of the first commit
§ achievements.horoscope10.description: by the month of the first commit
§ achievements.horoscope11.description: by the month of the first commit
§ achievements.horoscope12.description: by the month of the first commit
§ achievements.111.description: test
`; `;

View file

@ -214,4 +214,7 @@ will be marked as a jump in "deleted" and "added" lines.
§ page.person.week.days: days § page.person.week.days: days
§ page.person.week.workDay: weekdays § page.person.week.workDay: weekdays
§ page.person.week.weekends: weekends § page.person.week.weekends: weekends
§ page.sponsor.share.title: Please, support this project
§ page.sponsor.share.description: Tell about our [project|https://github.com/bakhirev/assayo] on social networks! You can share [article|https://habr.com/ru/articles/763342/], [post|https://www.reddit.com/r/github/comments/1bvtsl3/how_i_parsed_git_statistics/] or make a video review.
§ page.sponsor.share.button: Copy the link
`; `;

Some files were not shown because too many files have changed in this diff Show more