Compare commits
33 Commits
landing-20
...
developmen
| Author | SHA1 | Date | |
|---|---|---|---|
| c08a665503 | |||
| 023248e514 | |||
| bd461d5b69 | |||
| 376e9e7f28 | |||
| 7082fe196f | |||
| fc931a967d | |||
| 72c360a687 | |||
| aeb8f52682 | |||
| 4369102a75 | |||
| 8c2abab821 | |||
| 3ca59192c8 | |||
| ca2a31270b | |||
| 12f5f97ba8 | |||
| 0368d64bcc | |||
|
|
8a715cd5c3 | ||
|
|
e2c3ec5b8a | ||
|
|
b3f740ca46 | ||
|
|
f8a64f078e | ||
|
|
2c4cde6df8 | ||
|
|
55709a4338 | ||
|
|
eeb6583728 | ||
|
|
3bdd508218 | ||
|
|
371082e47f | ||
|
|
e1bb17ee5a | ||
|
|
13d0a32521 | ||
|
|
3ea5185842 | ||
|
|
b33c7a2b58 | ||
|
|
aafc2421cf | ||
|
|
e278d7bd25 | ||
|
|
c177ca1040 | ||
|
|
0b42131690 | ||
|
|
c5bff17388 | ||
|
|
403ff6adfb |
40
package.json
@@ -16,45 +16,45 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@libsql/client": "^0.15.15",
|
"@libsql/client": "^0.15.15",
|
||||||
"@paralleldrive/cuid2": "^2.2.2",
|
"@paralleldrive/cuid2": "^2.3.1",
|
||||||
"@radix-ui/react-alert-dialog": "^1.1.15",
|
"@radix-ui/react-alert-dialog": "^1.1.15",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||||
"@radix-ui/react-select": "^2.2.6",
|
"@radix-ui/react-select": "^2.2.6",
|
||||||
"@radix-ui/react-slot": "^1.2.3",
|
"@radix-ui/react-slot": "^1.2.4",
|
||||||
"@radix-ui/react-tooltip": "^1.2.8",
|
"@radix-ui/react-tooltip": "^1.2.8",
|
||||||
"@tanstack/react-table": "^8.21.3",
|
"@tanstack/react-table": "^8.21.3",
|
||||||
"@types/three": "^0.178.1",
|
"@types/three": "^0.178.1",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"drizzle-orm": "^0.44.5",
|
"drizzle-orm": "^0.44.7",
|
||||||
"lucide-react": "^0.522.0",
|
"lucide-react": "^0.522.0",
|
||||||
"material-symbols": "^0.31.9",
|
"material-symbols": "^0.31.9",
|
||||||
"next": "15.3.0",
|
"next": "15.3.0",
|
||||||
"next-intl": "^4.3.9",
|
"next-intl": "^4.11.0",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
"react": "^19.1.1",
|
"react": "^19.2.5",
|
||||||
"react-dom": "^19.1.1",
|
"react-dom": "^19.2.5",
|
||||||
"react-icons": "^5.5.0",
|
"react-icons": "^5.6.0",
|
||||||
"react-markdown": "^10.1.0",
|
"react-markdown": "^10.1.0",
|
||||||
"tailwind-merge": "^3.3.1",
|
"tailwind-merge": "^3.5.0",
|
||||||
"three": "^0.178.0",
|
"three": "^0.178.0",
|
||||||
"tw-animate-css": "^1.4.0"
|
"tw-animate-css": "^1.4.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/eslintrc": "^3.3.1",
|
"@eslint/eslintrc": "^3.3.5",
|
||||||
"@tailwindcss/postcss": "^4.1.13",
|
"@tailwindcss/postcss": "^4.2.4",
|
||||||
"@types/bun": "^1.2.22",
|
"@types/bun": "^1.3.13",
|
||||||
"@types/node": "^20.19.17",
|
"@types/node": "^20.19.39",
|
||||||
"@types/react": "^19.1.13",
|
"@types/react": "^19.2.14",
|
||||||
"@types/react-dom": "^19.1.9",
|
"@types/react-dom": "^19.2.3",
|
||||||
"autoprefixer": "^10.4.21",
|
"autoprefixer": "^10.5.0",
|
||||||
"dotenv": "^16.6.1",
|
"dotenv": "^16.6.1",
|
||||||
"drizzle-kit": "^0.31.4",
|
"drizzle-kit": "^0.31.10",
|
||||||
"eslint": "^9.36.0",
|
"eslint": "^9.39.4",
|
||||||
"eslint-config-next": "15.3.0",
|
"eslint-config-next": "15.3.0",
|
||||||
"postcss": "^8.5.6",
|
"postcss": "^8.5.13",
|
||||||
"tailwindcss": "^4.1.13",
|
"tailwindcss": "^4.2.4",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"typescript": "^5.9.2"
|
"typescript": "^5.9.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
6
public/flags/eng.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M0 9.93346V14.4808H6.25333L0 9.93346ZM5.18222 35.25H14.4444V28.515L5.18222 35.25ZM25.5556 28.5162V35.25H34.8167L25.5556 28.5162ZM0 26.0192V30.5665L6.25556 26.0192H0ZM34.8189 5.25H25.5556V11.985L34.8189 5.25ZM40 30.5677V26.0192H33.7433L40 30.5677ZM40 14.4808V9.93346L33.7456 14.4808H40ZM14.4444 5.25H5.18222L14.4444 11.985V5.25Z" fill="#00247D"/>
|
||||||
|
<path d="M27.9332 26.0196L38.7243 33.8669C39.2513 33.3031 39.6301 32.6083 39.8243 31.8489L31.8076 26.0196H27.9332ZM14.4443 26.0196H12.0654L1.27543 33.8658C1.85432 34.4773 2.59654 34.9146 3.42876 35.1177L14.4443 27.1077V26.0196ZM25.5554 14.4812H27.9343L38.7243 6.63503C38.1355 6.01457 37.3891 5.58044 36.5721 5.3831L25.5554 13.3931V14.4812ZM12.0654 14.4812L1.27543 6.63503C0.748709 7.19923 0.369666 7.89392 0.174316 8.6531L8.19098 14.4812H12.0654Z" fill="#C8102E"/>
|
||||||
|
<path d="M40 23.7115H23.3333V35.25H25.5556V28.5162L34.8167 35.25H35.5556C36.1458 35.2501 36.7302 35.1278 37.2744 34.8902C37.8185 34.6526 38.3114 34.305 38.7243 33.8669L27.9332 26.0196H31.8076L39.8243 31.8489C39.9276 31.46 40 31.0569 40 30.6346V30.5677L33.7433 26.0192H40V23.7115ZM0 23.7115V26.0192H6.25556L0 30.5665V30.6346C0 31.8935 0.48765 33.0327 1.27543 33.8658L12.0654 26.0196H14.4443V27.1077L3.42889 35.1162C3.75556 35.1969 4.09333 35.25 4.44444 35.25H5.18222L14.4444 28.515V35.25H16.6667V23.7115H0ZM40 9.86539C40.0007 8.65613 39.5422 7.49574 38.7243 6.63503L27.9343 14.4812H25.5554V13.3931L36.5721 5.3831C36.2393 5.29833 35.8982 5.25339 35.5556 5.25L34.8189 5.25L25.5556 11.985V5.25L23.3333 5.25V16.7885H40V14.4808H33.7456L40 9.93346V9.86539ZM14.4444 5.25V11.985L5.18222 5.25L4.44444 5.25C3.85403 5.24973 3.26952 5.37209 2.72531 5.60988C2.1811 5.84767 1.68805 6.19649 1.27543 6.63503L12.0654 14.4812H8.19098L0.174316 8.6531C0.0640721 9.04738 0.00552601 9.45486 0 9.86539V9.93346L6.25333 14.4808H0V16.7885H16.6667V5.25L14.4444 5.25Z" fill="white"/>
|
||||||
|
<path d="M23.3333 16.7885V5.25H16.6667V16.7885H0V23.7115H16.6667V35.25H23.3333V23.7115H40V16.7885H23.3333Z" fill="#C8102E"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
5
public/flags/est.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M35.5556 5.25H4.44444C3.2657 5.25 2.13524 5.71825 1.30175 6.55175C0.468253 7.38524 0 8.5157 0 9.69444V15.25H40V9.69444C40 8.5157 39.5317 7.38524 38.6983 6.55175C37.8648 5.71825 36.7343 5.25 35.5556 5.25Z" fill="#0072CE"/>
|
||||||
|
<path d="M0 15.25H40V25.25H0V15.25Z" fill="black"/>
|
||||||
|
<path d="M35.5556 35.25H4.44444C3.2657 35.25 2.13524 34.7232 1.30175 33.7855C0.468253 32.8479 0 31.5761 0 30.25V25.25H40V30.25C40 31.5761 39.5317 32.8479 38.6983 33.7855C37.8648 34.7232 36.7343 35.25 35.5556 35.25Z" fill="white"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 617 B |
BIN
public/images/landing/cs2_tournament.jpg
Normal file
|
After Width: | Height: | Size: 6.3 MiB |
BIN
public/images/landing/league_ticket.jpg
Normal file
|
After Width: | Height: | Size: 12 MiB |
BIN
public/images/landing/lol_tournament.png
Normal file
|
After Width: | Height: | Size: 4.0 MiB |
BIN
public/images/landing/student_award.webp
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
public/images/landing/visitor_tournament.jpg
Normal file
|
After Width: | Height: | Size: 747 KiB |
3
public/letters/A.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="390" height="300" viewBox="0 0 390 300" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M262.4 256.4L267.8 300H389.4L339.2 40.4C334.6 17 314 0 290 0H213C195.6 0 179.6 9 170.4 23.8L0 300H125.8L149 256.4H262.4ZM188.8 183L241 86.8L252.8 183H188.8Z" fill="#00A3E0"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 331 B |
3
public/letters/I.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="178" height="300" viewBox="0 0 178 300" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M114.4 300H0L63.6 0H178L114.4 300Z" fill="white"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 167 B |
3
public/letters/L.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="286" height="300" viewBox="0 0 286 300" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M238.4 300H0L63.6 0H178.8L131.2 223.8H285.44L238.4 300Z" fill="#00A3E0"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 190 B |
3
public/letters/N.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="402" height="300" viewBox="0 0 402 300" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M250.88 300H337.68L401.28 0H291.88L259.68 152.4L179.48 23.4C170.28 9 154.28 0 137.08 0H46.88L0 221.12L15.2799 300H92.2799L122.68 156.2L210.28 279C219.68 292.2 234.88 300 250.88 300Z" fill="#00A3E0"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 316 B |
3
public/letters/P.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="411" height="300" viewBox="0 0 411 300" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M135.24 202.4H264.14C349 202.4 382.14 190.6 394.84 130.7L407.1 71.7C408.92 62.16 410.28 54 410.28 46.74C410.28 8.62 380.32 0 307.26 0H63.54L0 300H114.82L135.24 202.42V202.4ZM285.92 107.54L288.2 96.2C288.66 93.94 289.1 91.66 289.1 89.4C289.1 80.32 283.66 76.24 269.58 76.24H162.02L151.12 128.44H258.68C276.38 128.44 282.74 123 285.92 107.56V107.54Z" fill="white"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 520 B |
7
public/letters/T.svg
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<svg width="367" height="300" viewBox="0 0 367 300" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M42.0785 115L147.998 205L292.078 115L170.138 160L167.278 115L144.938 160L42.0785 115Z" fill="#00A3E0"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M141.458 0L120.258 100H220.258L241.458 0H141.458Z" fill="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M104.818 220L93.1584 275C90.2384 288.8 99.0584 300 112.858 300H142.858C156.658 300 170.238 288.8 173.158 275L184.818 220H104.818Z" fill="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.4585 0L0.558456 75C-2.36154 88.8 6.45846 100 20.2585 100H95.2585L116.458 0H16.4585Z" fill="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M266.458 0L245.258 100H320.258C334.058 100 347.638 88.8 350.558 75L366.458 0H266.458Z" fill="white"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 834 B |
@@ -1,20 +1,11 @@
|
|||||||
<svg width="2096" height="300" viewBox="0 0 2096 300" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="2092" height="300" viewBox="0 0 2092 300" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<g clip-path="url(#clip0_141_149)">
|
<path d="M2028.4 300H1941.6C1925.6 300 1910.4 292.2 1901 279L1813.4 156.2L1783 300H1706L1690.72 221.12L1737.6 0H1827.8C1845 0 1861 9 1870.2 23.4L1950.4 152.4L1982.6 0H2092L2028.4 300Z" fill="#00A3E0"/>
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M1694.15 223.752L1708.91 300H1786.92L1817.33 156.127L1894.26 264.032C1894.26 264.032 1895.78 266.281 1898.51 269.653C1899.88 271.339 1901.55 273.305 1903.49 275.412C1905.43 277.52 1907.64 279.768 1910.08 282.016C1912.52 284.264 1915.19 286.512 1918.05 288.619C1920.92 290.727 1923.97 292.695 1927.19 294.381C1928.79 295.224 1930.44 295.997 1932.12 296.682C1933.8 297.367 1935.51 297.964 1937.26 298.455C1939 298.947 1940.78 299.333 1942.57 299.596C1944.37 299.859 1946.2 300 1948.04 300H2032.46L2096 0.000244141H1986.62L1954.4 152.496L1883.25 38.1253C1883.25 38.1253 1882.12 35.7423 1879.88 32.1682C1878.76 30.3811 1877.35 28.2946 1875.68 26.0608C1874 23.8269 1872.05 21.4455 1869.82 19.0628C1867.59 16.68 1865.09 14.2966 1862.31 12.0628C1859.54 9.82892 1856.49 7.74433 1853.17 5.95726C1851.51 5.06374 1849.79 4.24384 1847.99 3.51783C1846.2 2.79186 1844.34 2.16016 1842.41 1.63897C1840.48 1.11773 1838.49 0.707222 1836.43 0.42799C1834.36 0.148683 1832.23 0.000244141 1830.04 0.000244141H1741.54L1694.15 223.752Z" fill="#00A3E0"/>
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M1557 300L1551.6 256.4H1438.2L1415 300H1289.2L1459.6 23.8C1468.8 9 1484.8 0 1502.2 0H1579.2C1603.2 0 1623.8 17 1628.4 40.4L1678.6 300H1557ZM1478 183L1530.2 86.8L1542 183H1478Z" fill="#00A3E0"/>
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M1342.04 223.752L1294.97 300H1420.69L1443.84 256.43H1557.3L1562.75 300H1684.38L1669.62 223.752L1633.55 37.5002C1633.55 37.5002 1633.07 35.1565 1631.79 31.6409C1631.15 29.883 1630.31 27.8323 1629.23 25.635C1628.15 23.4377 1626.82 21.094 1625.22 18.7502C1623.61 16.4065 1621.72 14.0627 1619.51 11.8655C1618.4 10.7668 1617.21 9.70459 1615.94 8.69749C1614.67 7.69044 1613.31 6.73855 1611.86 5.85962C1610.41 4.98073 1608.87 4.1753 1607.23 3.4612C1605.59 2.74706 1603.86 2.12427 1602.02 1.61157C1600.19 1.09888 1598.25 0.696766 1596.21 0.422108C1594.16 0.147336 1592.01 0.000244141 1589.75 0.000244141H1516.68C1514.44 0.000244141 1512.25 0.14726 1510.11 0.422108C1507.97 0.696879 1505.87 1.09888 1503.83 1.61157C1501.79 2.12427 1499.8 2.74706 1497.87 3.4612C1495.94 4.1753 1494.06 4.98073 1492.24 5.85962C1488.61 7.61744 1485.2 9.66821 1482.06 11.8655C1478.91 14.0627 1476.02 16.4065 1473.41 18.7502C1470.8 21.094 1468.47 23.4377 1466.44 25.635C1464.41 27.8323 1462.69 29.883 1461.29 31.6409C1458.49 35.1565 1457 37.5002 1457 37.5002L1342.04 223.752ZM1535.97 86.6878L1547.77 182.905H1483.78L1535.97 86.6878Z" fill="#00A3E0"/>
|
<path d="M1018.64 300H1257.04L1304.08 223.8H1149.84L1197.44 0H1082.24L1018.64 300Z" fill="#00A3E0"/>
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M1089.19 -0.000244141L1025.65 300H1264.97L1312.04 223.752H1156.82L1204.47 -0.000244141H1089.19Z" fill="#00A3E0"/>
|
<path d="M872.218 300H986.618L1050.22 0H935.818L872.218 300Z" fill="white"/>
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M939.821 -0.000244141L876.28 300H990.653L1054.19 -0.000244141H939.821Z" fill="#EEE5E5"/>
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M745.398 202.4H616.498V202.42L596.078 300H481.258L544.798 0H788.518C861.578 0 891.538 8.62 891.538 46.74C891.538 54 890.178 62.16 888.358 71.7L876.098 130.7C863.398 190.6 830.258 202.4 745.398 202.4ZM767.178 107.54L769.458 96.2C769.918 93.94 770.358 91.66 770.358 89.4C770.358 80.32 764.918 76.24 750.838 76.24H643.278L632.378 128.44H739.938C757.638 128.44 763.998 123 767.178 107.56V107.54Z" fill="white"/>
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M550.819 -7.11276e-05L487.278 300H602.104L622.528 202.42H751.425C756.729 202.42 761.832 202.374 766.739 202.273C771.647 202.172 776.359 202.017 780.886 201.797C785.412 201.577 789.752 201.294 793.913 200.937C798.074 200.581 802.056 200.151 805.866 199.64C809.676 199.13 813.314 198.538 816.788 197.855C820.262 197.173 823.573 196.399 826.728 195.527C829.882 194.655 832.88 193.683 835.73 192.603C838.579 191.524 841.281 190.336 843.841 189.031C846.401 187.726 848.821 186.305 851.106 184.758C853.392 183.21 855.544 181.537 857.571 179.728C859.598 177.92 861.499 175.975 863.282 173.888C865.065 171.802 866.729 169.572 868.284 167.189C869.839 164.807 871.284 162.271 872.626 159.574C873.968 156.877 875.207 154.02 876.351 150.992C877.495 147.964 878.543 144.766 879.505 141.388C880.467 138.011 881.342 134.455 882.136 130.711L894.39 71.7088C895.297 66.9433 896.092 62.5181 896.659 58.3767C896.943 56.306 897.17 54.3065 897.325 52.3709C897.481 50.4352 897.565 48.5632 897.565 46.7478C897.565 44.365 897.449 42.0967 897.212 39.9392C896.975 37.7817 896.617 35.7349 896.136 33.7927C895.655 31.8505 895.05 30.0124 894.319 28.2752C893.588 26.5379 892.731 24.9015 891.743 23.3591C890.756 21.8167 889.638 20.3691 888.388 19.0115C887.138 17.6539 885.755 16.3863 884.235 15.2029C882.716 14.0195 881.059 12.9212 879.263 11.9021C877.467 10.883 875.532 9.94304 873.454 9.07787C871.376 8.21269 869.156 7.42073 866.79 6.69899C864.424 5.97726 861.911 5.32578 859.249 4.73806C856.587 4.15035 853.778 3.62787 850.815 3.16382C847.853 2.69977 844.737 2.29414 841.466 1.9431C838.195 1.59198 834.768 1.29463 831.183 1.04666C827.597 0.798721 823.852 0.600241 819.946 0.445091C812.134 0.134792 803.674 -0.000244141 794.54 -0.000244141L550.819 -7.11276e-05ZM649.306 76.2479H756.87C758.629 76.2479 760.252 76.3122 761.747 76.4415C763.242 76.5707 764.61 76.765 765.855 77.0274C767.099 77.2897 768.221 77.6203 769.228 78.0196C770.234 78.4189 771.125 78.8869 771.905 79.4258C772.685 79.9648 773.355 80.5736 773.921 81.2559C774.487 81.9381 774.95 82.6938 775.315 83.5235C775.681 84.3532 775.948 85.2577 776.124 86.2383C776.3 87.2189 776.386 88.2756 776.386 89.4102C776.386 90.5449 776.273 91.6797 776.103 92.8145C775.932 93.9493 775.704 95.0842 775.478 96.2188L773.21 107.565C772.813 109.493 772.365 111.267 771.853 112.893C771.34 114.518 770.762 115.996 770.103 117.336C769.443 118.676 768.703 119.879 767.864 120.951C767.026 122.024 766.09 122.966 765.04 123.789C763.991 124.612 762.827 125.314 761.534 125.904C760.242 126.495 758.82 126.974 757.253 127.35C755.686 127.726 753.973 127.999 752.099 128.178C750.225 128.357 748.19 128.442 745.978 128.442H638.413L649.306 76.2479Z" fill="#EEE5E5"/>
|
<path d="M334.858 300H449.258L512.858 0H398.458L334.858 300Z" fill="white"/>
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M452.28 300L515.819 -0.000244141H401.448L337.907 300H452.28Z" fill="#EEE5E5"/>
|
<path d="M42.0785 115L147.998 205L292.078 115L170.138 160L167.278 115L144.938 160L42.0785 115Z" fill="#00A3E0"/>
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M104.854 220L99.5586 245L93.2051 275C93.0218 275.865 92.8842 276.72 92.791 277.562C92.6977 278.405 92.647 279.235 92.6406 280.051C92.633 280.866 92.6708 281.665 92.746 282.449C92.8227 283.233 92.9403 284.002 93.0975 284.75C93.2548 285.498 93.4494 286.227 93.6835 286.935C93.9174 287.644 94.1903 288.331 94.4979 288.996C94.8055 289.661 95.1492 290.303 95.5272 290.92C95.9052 291.537 96.3183 292.128 96.7635 292.693C97.2087 293.258 97.6861 293.796 98.1952 294.304C98.7044 294.813 99.2445 295.294 99.8143 295.742C100.384 296.19 100.982 296.608 101.609 296.992C102.236 297.376 102.893 297.725 103.574 298.041C104.255 298.356 104.965 298.636 105.697 298.879C106.43 299.121 107.185 299.328 107.965 299.494C108.745 299.66 109.549 299.786 110.373 299.871C111.197 299.956 112.043 300 112.908 300H142.908C143.774 300 144.637 299.956 145.498 299.871C146.358 299.786 147.216 299.66 148.066 299.494C148.917 299.328 149.762 299.121 150.598 298.879C151.433 298.636 152.259 298.356 153.074 298.041C153.889 297.725 154.69 297.376 155.48 296.992C156.27 296.608 157.047 296.19 157.807 295.742C158.566 295.294 159.31 294.813 160.035 294.304C160.76 293.796 161.466 293.258 162.15 292.693C162.835 292.128 163.497 291.537 164.137 290.92C164.776 290.303 165.393 289.661 165.982 288.996C166.571 288.331 167.134 287.644 167.668 286.935C168.202 286.227 168.706 285.498 169.18 284.75C169.654 284.002 170.095 283.233 170.504 282.449C170.912 281.665 171.29 280.866 171.629 280.051C171.967 279.235 172.268 278.405 172.531 277.562C172.795 276.72 173.02 275.865 173.203 275L179.556 245L184.851 220H159.851H129.851H104.854Z" fill="#EEE5E5"/>
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M104.818 220L93.1584 275C90.2384 288.8 99.0584 300 112.858 300H142.858C156.658 300 170.238 288.8 173.158 275L184.818 220H104.818Z" fill="white"/>
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M42.0938 115L148.031 205L292.094 115L170.062 160L167.094 115L145.063 160L42.0938 115Z" fill="#00A3E0"/>
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.4585 0L0.558456 75C-2.36154 88.8 6.45846 100 20.2585 100H95.2585L116.458 0H16.4585ZM141.458 0L120.258 100H220.258L241.458 0H141.458ZM245.258 100L266.458 0H366.458L350.558 75C347.638 88.8 334.058 100 320.258 100H245.258Z" fill="white"/>
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M266.449 -0.000244141L261.154 24.9997L250.564 74.9998L245.27 99.9997H270.27H320.27C321.135 99.9997 321.999 99.9559 322.859 99.8708C323.72 99.7858 324.577 99.6599 325.428 99.494C326.278 99.3281 327.124 99.1217 327.959 98.8788C328.794 98.6361 329.621 98.3563 330.436 98.0409C331.251 97.7257 332.052 97.376 332.842 96.9921C333.632 96.6081 334.408 96.1905 335.168 95.7421C335.928 95.2937 336.672 94.8133 337.396 94.3046C338.121 93.796 338.827 93.2581 339.512 92.6933C340.196 92.1284 340.859 91.5367 341.498 90.9198C342.137 90.3029 342.753 89.6608 343.342 88.996C343.931 88.3312 344.495 87.6441 345.029 86.9355C345.563 86.2269 346.067 85.4981 346.541 84.7499C347.015 84.0017 347.456 83.2329 347.865 82.4491C348.274 81.6654 348.65 80.8658 348.988 80.0506C349.327 79.2354 349.629 78.4048 349.893 77.5624C350.156 76.72 350.381 75.8655 350.564 74.9999L361.154 24.9999L366.449 -0.000128799H341.449H291.449L266.449 -0.000244141Z" fill="#EEE5E5"/>
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M141.449 -0.000244141L120.27 99.9997H220.27L241.449 -0.000244141H141.449Z" fill="#EEE5E5"/>
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.4492 -7.11276e-05L11.1544 24.9999L0.564456 74.9999C0.381149 75.8656 0.243979 76.72 0.150436 77.5624C0.0570817 78.4048 0.00639753 79.2355 -2.76641e-05 80.0507C-0.00758672 80.8659 0.03024 81.6654 0.105453 82.4491C0.182177 83.2329 0.299729 84.0017 0.456957 84.7499C0.614186 85.4981 0.811092 86.2269 1.04486 86.9355C1.27881 87.6441 1.54982 88.3312 1.85733 88.996C2.16498 89.6608 2.50861 90.3029 2.88664 90.9198C3.2647 91.5367 3.67784 92.1284 4.12299 92.6933C4.56818 93.2581 5.04547 93.796 5.55461 94.3046C6.06375 94.8133 6.60389 95.2938 7.17373 95.7421C7.74357 96.1905 8.34143 96.6082 8.96868 96.9921C9.59593 97.3761 10.2521 97.7255 10.9335 98.0409C11.6149 98.3565 12.3243 98.6361 13.0566 98.8788C13.7889 99.1215 14.5441 99.3281 15.3241 99.494C16.1042 99.6599 16.9078 99.7858 17.7323 99.8708C18.5567 99.9559 19.4018 99.9997 20.2675 99.9997H70.2675H95.2675L100.562 74.9998L111.152 24.9997L116.447 -0.000244141H91.4472H41.4472L16.4492 -7.11276e-05Z" fill="#EEE5E5"/>
|
|
||||||
</g>
|
|
||||||
<defs>
|
|
||||||
<clipPath id="clip0_141_149">
|
|
||||||
<rect width="2096" height="300" fill="white" transform="translate(0 -0.000244141)"/>
|
|
||||||
</clipPath>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 1.8 KiB |
7
public/tipilan-icon-white.svg
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<svg width="350" height="300" viewBox="0 0 350 300" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M50 115L175 205L300 115L187.6 160L175.2 115L162.4 160L50 115Z" fill="#00A3E0"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M125 0V100H225V0H125Z" fill="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M135 220V275C135 288.8 146.2 300 160 300H190C203.8 300 215 288.8 215 275V220H135Z" fill="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0V75C0 88.8 11.2 100 25 100H100V0H0Z" fill="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M250 0V100H325C338.8 100 350 88.8 350 75V0H250Z" fill="white"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 648 B |
@@ -1,77 +1,27 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import { useState } from "react";
|
|
||||||
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
||||||
import { scheduleData } from "@/data/timetable";
|
import { getTranslations, setRequestLocale } from "next-intl/server";
|
||||||
import SectionDivider from "@/components/SectionDivider";
|
|
||||||
import { useTranslations } from "next-intl";
|
|
||||||
|
|
||||||
const tabs = Object.keys(scheduleData);
|
export default async function Timetable({
|
||||||
|
params,
|
||||||
export default function Timetable() {
|
}: {
|
||||||
const [activeTab, setActiveTab] = useState(tabs[0]);
|
params: Promise<{ locale: string }>;
|
||||||
const schedule = scheduleData[activeTab];
|
}) {
|
||||||
const t = useTranslations();
|
const { locale } = await params;
|
||||||
|
setRequestLocale(locale);
|
||||||
|
const t = await getTranslations({ locale });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="bg-[#0E0F19] min-h-screen flex flex-col items-center justify-center">
|
||||||
<div className="flex flex-col min-h-[90vh] m-6 mt-16 md:m-16">
|
<h1
|
||||||
<h1
|
className={`${vipnagorgialla.className} font-bold italic text-[clamp(1.75rem,5vw,3rem)] text-[#EEE5E5] uppercase mb-4 text-center`}
|
||||||
className={`text-4xl md:text-5xl lg:text-6xl ${vipnagorgialla.className} font-bold italic uppercase text-[#2A2C3F] dark:text-[#EEE5E5] mt-8 md:mt-16 mb-8`}
|
>
|
||||||
>
|
{t("schedule.title")}
|
||||||
{t("schedule.title")}
|
</h1>
|
||||||
</h1>
|
<p
|
||||||
|
className={`${vipnagorgialla.className} font-bold italic text-[clamp(2rem,8vw,4.5rem)] text-[#EEE5E5] uppercase text-center`}
|
||||||
{/* Tab menu */}
|
>
|
||||||
<div className="flex gap-4 mb-8 flex-wrap">
|
{t("schedule.comingSoon")}
|
||||||
{tabs.map((tab) => (
|
</p>
|
||||||
<button
|
|
||||||
key={tab}
|
|
||||||
onClick={() => setActiveTab(tab)}
|
|
||||||
className={`${vipnagorgialla.className} cursor-pointer uppercase italic px-4 py-2 text-lg font-semibold ${
|
|
||||||
activeTab === tab
|
|
||||||
? "bg-[#00A3E0] text-white"
|
|
||||||
: "bg-[#007CAB] dark:bg-[#007CAB] text-[#EEE5E5] hover:bg-[#00A3E0] dark:hover:bg-[#007CAB]"
|
|
||||||
} transition-colors`}
|
|
||||||
>
|
|
||||||
{t(`schedule.${tab}`)}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Schedule entries */}
|
|
||||||
<div className="space-y-6">
|
|
||||||
{schedule.map((item, idx) => (
|
|
||||||
<div
|
|
||||||
key={idx}
|
|
||||||
className="border-l-3 border-[#007CAB] pl-4 flex flex-col sm:flex-row flex-wrap gap-2 sm:gap-5 items-stretch"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={`${vipnagorgialla.className} md:w-[180px] w-30 text-[#00A3E0] text-3xl md:text-4xl font-bold italic flex-shrink-0 flex items-center sm:justify-center`}
|
|
||||||
>
|
|
||||||
{item.time}
|
|
||||||
</div>
|
|
||||||
<div className="flex-1 flex flex-col justify-center min-w-0 sm:min-h-[120px]">
|
|
||||||
<div
|
|
||||||
className={`${vipnagorgialla.className} text-2xl md:text-3xl italic font-bold text-[#2A2C3F] dark:text-[#EEE5E5] text-balance`}
|
|
||||||
>
|
|
||||||
{t(item.titleKey)}
|
|
||||||
</div>
|
|
||||||
{item.description && (
|
|
||||||
<div className="text-xl md:text-2xl text-[#938BA1] dark:text-[#938BA1] text-balance">
|
|
||||||
{item.description}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className="text-xl md:text-2xl text-[#938BA1] dark:text-[#938BA1] text-balance">
|
|
||||||
{t(item.locationKey)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<SectionDivider />
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
// app/kodukord/page.tsx (App Router)
|
// app/kodukord/page.tsx (App Router)
|
||||||
import ReactMarkdown, { Components } from "react-markdown";
|
import ReactMarkdown, { Components } from "react-markdown";
|
||||||
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
||||||
import SectionDivider from "@/components/SectionDivider";
|
import RuleNav from "@/components/RuleNav";
|
||||||
import { getTranslations, setRequestLocale } from "next-intl/server";
|
import { getTranslations, setRequestLocale } from "next-intl/server";
|
||||||
import { loadRulesBun } from "@/lib/loadRules";
|
import { loadRulesBun } from "@/lib/loadRules";
|
||||||
|
|
||||||
|
const sectionKeys = [
|
||||||
|
{ id: "reminder", labelKey: "rules.reminder" },
|
||||||
|
{ id: "code", labelKey: "rules.code" },
|
||||||
|
];
|
||||||
|
|
||||||
export default async function Page({
|
export default async function Page({
|
||||||
params,
|
params,
|
||||||
}: {
|
}: {
|
||||||
@@ -15,50 +20,77 @@ export default async function Page({
|
|||||||
const t = await getTranslations({ locale });
|
const t = await getTranslations({ locale });
|
||||||
const content = await loadRulesBun("kodukord", locale as "et" | "en");
|
const content = await loadRulesBun("kodukord", locale as "et" | "en");
|
||||||
|
|
||||||
return (
|
const sections = sectionKeys.map((section) => ({
|
||||||
<div>
|
id: section.id,
|
||||||
<div className="flex flex-col min-h-[90vh] m-6 mt-16 md:m-16">
|
label: t(section.labelKey),
|
||||||
{/* Page title (separate from markdown headings) */}
|
}));
|
||||||
<h1
|
|
||||||
className={`text-4xl md:text-5xl lg:text-6xl ${vipnagorgialla.className} font-bold italic text-[#2A2C3F] dark:text-[#EEE5E5] mt-8 md:mt-16 mb-4 uppercase`}
|
|
||||||
>
|
|
||||||
{t("rules.houseRules")}
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<div className="prose prose-lg dark:prose-invert max-w-none">
|
// Track heading count for ID assignment
|
||||||
<ReactMarkdown
|
let headingCount = 0;
|
||||||
components={
|
|
||||||
{
|
return (
|
||||||
h1: (props) => (
|
<div className="bg-[#0E0F19] min-h-screen pt-16 md:pt-20">
|
||||||
<h1 className="text-3xl md:text-4xl font-bold my-4">
|
<div className="max-w-480 mx-auto px-6 md:px-12 py-8 md:py-16">
|
||||||
{props.children}
|
<div className="grid grid-cols-1 lg:grid-cols-[1fr_300px] gap-8 lg:gap-16">
|
||||||
</h1>
|
{/* Main content */}
|
||||||
),
|
<div>
|
||||||
h2: (props) => (
|
{/* Page title */}
|
||||||
<h2 className="text-2xl md:text-3xl font-semibold my-3">
|
<h1
|
||||||
{props.children}
|
className={`${vipnagorgialla.className} font-bold italic text-[clamp(2.5rem,2rem+3vw,4rem)] leading-tight text-[#00A3E0] uppercase mb-8`}
|
||||||
</h2>
|
>
|
||||||
),
|
{t("rules.houseRules")}
|
||||||
ol: (props) => (
|
</h1>
|
||||||
<ol className="list-decimal ml-6 md:text-xl">
|
|
||||||
{props.children}
|
<ReactMarkdown
|
||||||
</ol>
|
components={
|
||||||
),
|
{
|
||||||
ul: (props) => (
|
h1: (props) => {
|
||||||
<ul className="list-disc ml-6 md:text-xl">
|
const id =
|
||||||
{props.children}
|
sections[headingCount]?.id || `section-${headingCount}`;
|
||||||
</ul>
|
headingCount++;
|
||||||
),
|
return (
|
||||||
p: (props) => <p className="md:text-xl">{props.children}</p>,
|
<h1
|
||||||
} as Components
|
id={id}
|
||||||
}
|
className={`${vipnagorgialla.className} font-bold italic text-2xl md:text-3xl text-white uppercase mb-4 mt-12 first:mt-0 scroll-mt-24 md:scroll-mt-28`}
|
||||||
>
|
>
|
||||||
{content}
|
{props.children}
|
||||||
</ReactMarkdown>
|
</h1>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
h2: (props) => (
|
||||||
|
<h2
|
||||||
|
className={`${vipnagorgialla.className} font-bold italic text-xl text-white uppercase mb-2 mt-6`}
|
||||||
|
>
|
||||||
|
{props.children}
|
||||||
|
</h2>
|
||||||
|
),
|
||||||
|
ol: (props) => (
|
||||||
|
<ol className="list-decimal list-inside text-white mb-4">
|
||||||
|
{props.children}
|
||||||
|
</ol>
|
||||||
|
),
|
||||||
|
ul: (props) => (
|
||||||
|
<ul className="list-disc list-inside text-white mb-4">
|
||||||
|
{props.children}
|
||||||
|
</ul>
|
||||||
|
),
|
||||||
|
p: (props) => (
|
||||||
|
<p className="text-white mb-4">{props.children}</p>
|
||||||
|
),
|
||||||
|
li: (props) => (
|
||||||
|
<li className="text-white mb-2">{props.children}</li>
|
||||||
|
),
|
||||||
|
} as Components
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</ReactMarkdown>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Sidebar navigation */}
|
||||||
|
<RuleNav sections={sections} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SectionDivider />
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,13 +24,16 @@ export default async function LocaleLayout({
|
|||||||
<NextIntlClientProvider messages={messages}>
|
<NextIntlClientProvider messages={messages}>
|
||||||
<ThemeProvider
|
<ThemeProvider
|
||||||
attribute="class"
|
attribute="class"
|
||||||
defaultTheme="system"
|
defaultTheme="dark"
|
||||||
enableSystem
|
enableSystem={false}
|
||||||
|
forcedTheme="dark"
|
||||||
disableTransitionOnChange
|
disableTransitionOnChange
|
||||||
>
|
>
|
||||||
<SidebarParent />
|
<div className="min-h-screen flex flex-col">
|
||||||
{children}
|
<SidebarParent />
|
||||||
<Footer />
|
<main className="flex-1 flex flex-col">{children}</main>
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</NextIntlClientProvider>
|
</NextIntlClientProvider>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -17,58 +17,58 @@ export default async function Home({
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{/* Hero */}
|
{/* Hero */}
|
||||||
<div className="mt-18">
|
<div className="mt-16">
|
||||||
<HeroSection />
|
<HeroSection />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Nav cards: Piletid + Turniirid */}
|
{/* Nav cards: Piletid + Turniirid */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 md:h-[260px] border-b-3 border-[#1F5673]">
|
<div className="grid grid-cols-1 md:grid-cols-2 md:h-65 border-b-3 border-[#1F5673]">
|
||||||
<Link
|
<Link
|
||||||
href="/piletid"
|
href="/piletid"
|
||||||
className="px-8 md:px-12 py-8 flex flex-col justify-center gap-4 border-b-3 md:border-b-0 md:border-r-3 group border-[#1F5673] hover:bg-[#007CAB] dark:hover:bg-[#00A3E0] transition"
|
className="px-8 md:px-12 py-8 flex flex-col justify-center gap-4 border-b-3 md:border-b-0 md:border-r-3 group border-[#1F5673] hover:bg-[#007CAB] dark:hover:bg-[#00A3E0] transition"
|
||||||
>
|
>
|
||||||
|
<div className="flex flex-col gap-4">
|
||||||
|
<span
|
||||||
|
className="material-symbols-outlined text-[clamp(2rem,1.5rem+1.5vw,3.5rem)]! text-[#007CAB] dark:text-[#00A3E0] dark:group-hover:text-[#EEE5E5] group-hover:text-[#EEE5E5]"
|
||||||
|
style={{
|
||||||
|
fontVariationSettings:
|
||||||
|
'"FILL" 0, "wght" 700, "GRAD" 0, "opsz" 24',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
local_activity
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
<div className="flex flex-row justify-between gap-4 items-center">
|
<div className="flex flex-row justify-between gap-4 items-center">
|
||||||
<h2
|
<h2
|
||||||
className={`text-[clamp(2rem,1.8rem+1vw,3rem)] ${vipnagorgialla.className} font-bold italic uppercase dark:text-[#EEE5E5] text-[#2A2C3F] group-hover:text-black dark:group-hover:text-[#2A2C3F]`}
|
className={`text-[clamp(2rem,1.8rem+1vw,3rem)] ${vipnagorgialla.className} font-bold italic uppercase dark:text-[#EEE5E5] text-[#2A2C3F] group-hover:text-black dark:group-hover:text-[#2A2C3F]`}
|
||||||
>
|
>
|
||||||
{t("navigation.tickets")}
|
{t("navigation.tickets")}
|
||||||
</h2>
|
</h2>
|
||||||
<span className="material-symbols-outlined !text-[clamp(2rem,1.5rem+1.5vw,3.5rem)] !font-bold text-[#007CAB] dark:text-[#00A3E0] group-hover:translate-x-2 dark:group-hover:text-[#EEE5E5] group-hover:text-[#EEE5E5] transition">
|
<span className="material-symbols-outlined text-[clamp(2rem,1.5rem+1.5vw,3.5rem)]! font-bold! text-[#007CAB] dark:text-[#00A3E0] group-hover:translate-x-2 dark:group-hover:text-[#EEE5E5] group-hover:text-[#EEE5E5] transition">
|
||||||
arrow_right_alt
|
arrow_right_alt
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-4">
|
|
||||||
<span className="material-symbols-outlined !text-[clamp(2rem,1.5rem+1.5vw,3.5rem)] text-[#007CAB] dark:text-[#00A3E0] dark:group-hover:text-[#EEE5E5] group-hover:text-[#EEE5E5]">
|
|
||||||
confirmation_number
|
|
||||||
</span>
|
|
||||||
<p className="text-[clamp(0.875rem,0.75rem+0.5vw,1.25rem)] tracking-[-0.045rem] dark:group-hover:text-[#2A2C3F] group-hover:text-black">
|
|
||||||
{t("home.sections.schedule.description")}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
href="/turniirid"
|
href="/turniirid"
|
||||||
className="px-8 md:px-12 py-8 flex flex-col justify-center gap-4 group border-[#1F5673] hover:bg-[#007CAB] dark:hover:bg-[#00A3E0] transition"
|
className="px-8 md:px-12 py-8 flex flex-col justify-center gap-4 group border-[#1F5673] hover:bg-[#007CAB] dark:hover:bg-[#00A3E0] transition"
|
||||||
>
|
>
|
||||||
|
<div className="flex flex-col gap-4">
|
||||||
|
<span className="material-symbols-outlined text-[clamp(2rem,1.5rem+1.5vw,3.5rem)]! text-[#007CAB] dark:text-[#00A3E0] dark:group-hover:text-[#EEE5E5] group-hover:text-[#EEE5E5]">
|
||||||
|
rewarded_ads
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
<div className="flex flex-row justify-between gap-4 items-center">
|
<div className="flex flex-row justify-between gap-4 items-center">
|
||||||
<h2
|
<h2
|
||||||
className={`text-[clamp(2rem,1.8rem+1vw,3rem)] ${vipnagorgialla.className} font-bold italic uppercase dark:text-[#EEE5E5] text-[#2A2C3F] dark:group-hover:text-[#2A2C3F] group-hover:text-black`}
|
className={`text-[clamp(2rem,1.8rem+1vw,3rem)] ${vipnagorgialla.className} font-bold italic uppercase dark:text-[#EEE5E5] text-[#2A2C3F] dark:group-hover:text-[#2A2C3F] group-hover:text-black`}
|
||||||
>
|
>
|
||||||
{t("navigation.tournaments")}
|
{t("navigation.tournaments")}
|
||||||
</h2>
|
</h2>
|
||||||
<span className="material-symbols-outlined !text-[clamp(2rem,1.5rem+1.5vw,3.5rem)] !font-bold text-[#007CAB] dark:text-[#00A3E0] group-hover:translate-x-2 dark:group-hover:text-[#EEE5E5] group-hover:text-[#EEE5E5] transition">
|
<span className="material-symbols-outlined text-[clamp(2rem,1.5rem+1.5vw,3.5rem)]! font-bold! text-[#007CAB] dark:text-[#00A3E0] group-hover:translate-x-2 dark:group-hover:text-[#EEE5E5] group-hover:text-[#EEE5E5] transition">
|
||||||
arrow_right_alt
|
arrow_right_alt
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-4">
|
|
||||||
<span className="material-symbols-outlined !text-[clamp(2rem,1.5rem+1.5vw,3.5rem)] text-[#007CAB] dark:text-[#00A3E0] dark:group-hover:text-[#EEE5E5] group-hover:text-[#EEE5E5]">
|
|
||||||
trophy
|
|
||||||
</span>
|
|
||||||
<p className="text-[clamp(0.875rem,0.75rem+0.5vw,1.25rem)] tracking-[-0.045rem] dark:group-hover:text-[#2A2C3F] group-hover:text-black">
|
|
||||||
{t("home.sections.tournaments.description")}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -77,7 +77,6 @@ export default async function Home({
|
|||||||
|
|
||||||
{/* Sponsors */}
|
{/* Sponsors */}
|
||||||
<Sponsors />
|
<Sponsors />
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,94 @@
|
|||||||
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import SectionDivider from "@/components/SectionDivider";
|
import Image from "next/image";
|
||||||
import { getTranslations, setRequestLocale } from "next-intl/server";
|
import { getTranslations, setRequestLocale } from "next-intl/server";
|
||||||
|
|
||||||
|
interface TicketCardProps {
|
||||||
|
title: string;
|
||||||
|
subtitle: string;
|
||||||
|
price: string;
|
||||||
|
features: string[];
|
||||||
|
buttonText: string;
|
||||||
|
buttonHref: string;
|
||||||
|
backgroundImage?: string;
|
||||||
|
backgroundOpacity?: number;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function TicketCard({
|
||||||
|
title,
|
||||||
|
subtitle,
|
||||||
|
price,
|
||||||
|
features,
|
||||||
|
buttonText,
|
||||||
|
buttonHref,
|
||||||
|
backgroundImage,
|
||||||
|
backgroundOpacity = 40,
|
||||||
|
className = "",
|
||||||
|
}: TicketCardProps) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`relative bg-[#0E0F19] border-[#1F5673] px-12 py-16 flex flex-col min-h-87.5 h-full ${className}`}
|
||||||
|
>
|
||||||
|
{backgroundImage && (
|
||||||
|
<Image
|
||||||
|
src={backgroundImage}
|
||||||
|
alt=""
|
||||||
|
fill
|
||||||
|
className="object-cover object-center"
|
||||||
|
style={{ opacity: backgroundOpacity / 100 }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<div className="relative z-10 flex flex-col h-full">
|
||||||
|
<h2
|
||||||
|
className={`${vipnagorgialla.className} font-bold italic text-[clamp(2rem,1.5rem+2vw,3rem)] leading-none text-[#00A3E0] uppercase`}
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</h2>
|
||||||
|
<h3
|
||||||
|
className={`${vipnagorgialla.className} font-bold italic text-2xl text-[#EEE5E5] uppercase mb-4`}
|
||||||
|
>
|
||||||
|
{subtitle}
|
||||||
|
</h3>
|
||||||
|
<p
|
||||||
|
className={`${vipnagorgialla.className} font-bold italic text-[clamp(2.5rem,2rem+2vw,4rem)] leading-none mb-4`}
|
||||||
|
>
|
||||||
|
{Array.from(price).map((char, index) => (
|
||||||
|
<span
|
||||||
|
key={`${char}-${index}`}
|
||||||
|
className={
|
||||||
|
char === "€" || char === "+"
|
||||||
|
? "text-[#00A3E0]"
|
||||||
|
: "text-[#EEE5E5]"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{char}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</p>
|
||||||
|
<ul className="flex flex-col gap-2 mb-6 grow">
|
||||||
|
{features.map((feature, index) => (
|
||||||
|
<li
|
||||||
|
key={index}
|
||||||
|
className="flex items-start gap-2 text-[#EEE5E5] text-base"
|
||||||
|
>
|
||||||
|
<span className="w-1 h-full min-h-5 bg-[#00A3E0] shrink-0" />
|
||||||
|
<span>{feature}</span>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
<Link href={buttonHref} target="_blank">
|
||||||
|
<button
|
||||||
|
className={`px-4 py-2 bg-[#00A3E0] hover:bg-[#E5E5EE] text-[#0A121F] cursor-pointer ${vipnagorgialla.className} font-bold italic uppercase transition`}
|
||||||
|
>
|
||||||
|
{buttonText}
|
||||||
|
</button>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default async function Tickets({
|
export default async function Tickets({
|
||||||
params,
|
params,
|
||||||
}: {
|
}: {
|
||||||
@@ -11,108 +97,62 @@ export default async function Tickets({
|
|||||||
const { locale } = await params;
|
const { locale } = await params;
|
||||||
setRequestLocale(locale);
|
setRequestLocale(locale);
|
||||||
const t = await getTranslations({ locale });
|
const t = await getTranslations({ locale });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="bg-[#0E0F19] min-h-0 flex flex-col flex-1">
|
||||||
<div className="flex flex-col min-h-[90vh] m-6 mt-16 md:m-16">
|
{/* 2x2 Grid */}
|
||||||
<h1
|
<div className="grid grid-cols-1 md:grid-cols-2 md:grid-rows-2 auto-rows-fr pt-14 flex-1 min-h-0">
|
||||||
className={`text-4xl wrap-break-word md:text-5xl lg:text-6xl ${vipnagorgialla.className} font-bold italic text-[#2A2C3F] dark:text-[#EEE5E5] mt-8 md:mt-16 mb-4`}
|
{/* KÜLASTAJA PILET */}
|
||||||
>
|
<TicketCard
|
||||||
{t("tickets.title")}
|
title={t("tickets.visitor.name")}
|
||||||
</h1>
|
subtitle={t("tickets.subtitle")}
|
||||||
|
price={t("tickets.visitor.price")}
|
||||||
|
features={t.raw("tickets.visitor.features")}
|
||||||
|
buttonText={t("tickets.buyButton")}
|
||||||
|
buttonHref="https://fienta.com/et/tipilan"
|
||||||
|
backgroundImage="/images/landing/visitor_tournament.jpg"
|
||||||
|
backgroundOpacity={30}
|
||||||
|
className="border-b-[3px] md:border-b-[3px] md:border-r-[3px]"
|
||||||
|
/>
|
||||||
|
|
||||||
<div className="flex justify-center lg:items-center flex-col lg:flex-row gap-8 md:gap-12 flex-grow mb-16 md:mt-8 lg:mt-0">
|
{/* TOETAJA PILET */}
|
||||||
<div className="bg-[#007CAB] -skew-x-2 md:-skew-x-5 text-white italic px-8 md:px-12 py-16 hover:scale-103 transition-all duration-150 w-full md:w-xl lg:w-[400px]">
|
<TicketCard
|
||||||
<h2
|
title={t("tickets.supporter.name")}
|
||||||
className={`text-6xl ${vipnagorgialla.className} font-bold text-[#EEE5E5] pb-2`}
|
subtitle={t("tickets.subtitle")}
|
||||||
>
|
price={t("tickets.supporter.price")}
|
||||||
{t("tickets.visitor.latePrice")}
|
features={t.raw("tickets.supporter.features")}
|
||||||
</h2>
|
buttonText={t("tickets.buyButton")}
|
||||||
<h3
|
buttonHref="https://fienta.com/et/tipilan"
|
||||||
className={`text-3xl ${vipnagorgialla.className} font-bold text-[#EEE5E5] pb-4`}
|
backgroundImage="/images/landing/explore_teaser.png"
|
||||||
>
|
backgroundOpacity={80}
|
||||||
{t("tickets.visitor.title")}
|
className="border-b-[3px]"
|
||||||
</h3>
|
/>
|
||||||
<ul className="pl-4 mb-8 list-[square] marker:text-[#1F5673]">
|
|
||||||
{t
|
|
||||||
.raw("tickets.visitor.features")
|
|
||||||
.map((feature: string, index: number) => (
|
|
||||||
<li key={index} className="text-xl">
|
|
||||||
{feature}
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
<Link href="https://fienta.com/et/tipilan" target="_blank">
|
|
||||||
<button
|
|
||||||
className={`px-4 py-2 bg-[#1F5673] cursor-pointer ${vipnagorgialla.className} font-bold`}
|
|
||||||
>
|
|
||||||
{t("tickets.buyTicket")}
|
|
||||||
</button>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-[#007CAB] -skew-x-2 md:-skew-x-5 text-white italic px-8 md:px-12 py-16 hover:scale-103 transition-all duration-150 w-full md:w-xl lg:w-[400px]">
|
{/* LOL TURNIIRI PILET */}
|
||||||
<h2
|
<TicketCard
|
||||||
className={`text-6xl ${vipnagorgialla.className} font-bold text-[#EEE5E5] pb-2`}
|
title={t("tickets.lol.name")}
|
||||||
>
|
subtitle={t("tickets.subtitle")}
|
||||||
{t("tickets.computerParticipant.latePrice")}
|
price={t("tickets.lol.price")}
|
||||||
</h2>
|
features={t.raw("tickets.lol.features")}
|
||||||
<h3
|
buttonText={t("tickets.buyButton")}
|
||||||
className={`text-3xl ${vipnagorgialla.className} font-bold text-[#EEE5E5] pb-4`}
|
buttonHref="https://fienta.com/et/tipilan"
|
||||||
>
|
backgroundImage="/images/landing/league_ticket.jpg"
|
||||||
{t("tickets.computerParticipant.title")}
|
className="border-b-[3px] md:border-b-0 md:border-r-[3px]"
|
||||||
</h3>
|
/>
|
||||||
<ul className="pl-4 mb-8 list-[square] marker:text-[#1F5673]">
|
|
||||||
{t
|
|
||||||
.raw("tickets.computerParticipant.features")
|
|
||||||
.map((feature: string, index: number) => (
|
|
||||||
<li key={index} className="text-xl">
|
|
||||||
{feature}
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
<Link href="https://fienta.com/et/tipilan" target="_blank">
|
|
||||||
<button
|
|
||||||
className={`px-4 py-2 bg-[#1F5673] cursor-pointer ${vipnagorgialla.className} font-bold`}
|
|
||||||
>
|
|
||||||
{t("tickets.buyTicket")}
|
|
||||||
</button>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
{/* CS2 TURNIIRI PILET */}
|
||||||
|
<TicketCard
|
||||||
<div className="bg-[#1F5673] -skew-x-2 md:-skew-x-5 text-gray-400 italic px-8 md:px-12 py-16 w-full md:w-xl lg:w-[400px]">
|
title={t("tickets.cs2.name")}
|
||||||
<h2
|
subtitle={t("tickets.subtitle")}
|
||||||
className={`text-4xl ${vipnagorgialla.className} font-bold pb-2`}
|
price={t("tickets.cs2.price")}
|
||||||
>
|
features={t.raw("tickets.cs2.features")}
|
||||||
<s>{t("tickets.competitor.price")}</s>
|
buttonText={t("tickets.buyButton")}
|
||||||
</h2>
|
buttonHref="https://fienta.com/et/tipilan"
|
||||||
<h3
|
backgroundImage="/images/landing/compete_teaser.jpg"
|
||||||
className={`text-2xl ${vipnagorgialla.className} font-bold pb-4`}
|
backgroundOpacity={30}
|
||||||
>
|
className="border-b-0"
|
||||||
<s>{t("tickets.competitor.title")}</s>
|
/>
|
||||||
</h3>
|
|
||||||
<ul className="pl-4 mb-8 list-[square] marker:text-[#007CAB]">
|
|
||||||
{t
|
|
||||||
.raw("tickets.competitor.features")
|
|
||||||
.map((feature: string, index: number) => (
|
|
||||||
<li key={index} className="text-sm">
|
|
||||||
{feature}
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
{/*<Link href="https://fienta.com/et/tipilan" target="_blank">*/}
|
|
||||||
<button
|
|
||||||
className={`px-4 py-2 bg-[#007CAB] text-white ${vipnagorgialla.className} font-bold text-xl uppercase opacity-55`}
|
|
||||||
>
|
|
||||||
{t("tickets.soldOut")}!
|
|
||||||
</button>
|
|
||||||
{/*</Link>*/}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SectionDivider />
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,110 +0,0 @@
|
|||||||
import { notFound } from "next/navigation";
|
|
||||||
import ReactMarkdown, { Components } from "react-markdown";
|
|
||||||
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
|
||||||
import SectionDivider from "@/components/SectionDivider";
|
|
||||||
import { getTranslations, setRequestLocale } from "next-intl/server";
|
|
||||||
import { loadRulesBun } from "@/lib/loadRules";
|
|
||||||
|
|
||||||
// Map of valid slugs to their translation keys
|
|
||||||
const rulesMap = {
|
|
||||||
lol: {
|
|
||||||
titleKey: "rules.lolRules",
|
|
||||||
},
|
|
||||||
cs2: {
|
|
||||||
titleKey: "rules.cs2Rules",
|
|
||||||
},
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
type RuleSlug = keyof typeof rulesMap;
|
|
||||||
|
|
||||||
interface PageProps {
|
|
||||||
params: Promise<{ slug: string; locale: string }>;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getRuleContent(slug: string, locale: string) {
|
|
||||||
if (!Object.keys(rulesMap).includes(slug)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ruleConfig = rulesMap[slug as RuleSlug];
|
|
||||||
|
|
||||||
try {
|
|
||||||
const content = await loadRulesBun(
|
|
||||||
slug as "cs2" | "lol",
|
|
||||||
locale as "et" | "en",
|
|
||||||
);
|
|
||||||
return {
|
|
||||||
content,
|
|
||||||
titleKey: ruleConfig.titleKey,
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error(
|
|
||||||
`Error reading rule file for slug ${slug} in locale ${locale}:`,
|
|
||||||
error,
|
|
||||||
);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async function RulePage({ params }: PageProps) {
|
|
||||||
const { slug, locale } = await params;
|
|
||||||
setRequestLocale(locale);
|
|
||||||
const t = await getTranslations({ locale });
|
|
||||||
const ruleData = await getRuleContent(slug, locale);
|
|
||||||
|
|
||||||
if (!ruleData) {
|
|
||||||
notFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
const headingStyle = `text-5xl sm:text-6xl ${vipnagorgialla.className} font-bold uppercase italic text-[#2A2C3F] dark:text-[#EEE5E5]`;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="flex flex-col min-h-[90vh] m-6 mt-16 md:m-16">
|
|
||||||
<h1 className={`${headingStyle} mt-8 md:mt-16 mb-4`}>
|
|
||||||
{t(ruleData.titleKey)}
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<div className="prose prose-lg dark:prose-invert max-w-none">
|
|
||||||
<ReactMarkdown
|
|
||||||
components={
|
|
||||||
{
|
|
||||||
h1: (props) => (
|
|
||||||
<h1 className="text-3xl md:text-4xl font-bold my-4">
|
|
||||||
{props.children}
|
|
||||||
</h1>
|
|
||||||
),
|
|
||||||
h2: (props) => (
|
|
||||||
<h2 className="text-2xl md:text-3xl font-semibold my-3">
|
|
||||||
{props.children}
|
|
||||||
</h2>
|
|
||||||
),
|
|
||||||
ol: (props) => (
|
|
||||||
<ol className="list-none ml-6 md:text-xl">
|
|
||||||
{props.children}
|
|
||||||
</ol>
|
|
||||||
),
|
|
||||||
ul: (props) => (
|
|
||||||
<ul className="list-disc ml-6 md:text-xl">
|
|
||||||
{props.children}
|
|
||||||
</ul>
|
|
||||||
),
|
|
||||||
p: (props) => <p className="md:text-xl">{props.children}</p>,
|
|
||||||
} as Components
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{ruleData.content}
|
|
||||||
</ReactMarkdown>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<SectionDivider />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function generateStaticParams() {
|
|
||||||
return Object.keys(rulesMap).map((slug) => ({
|
|
||||||
slug,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
|
||||||
import SectionDivider from "@/components/SectionDivider";
|
|
||||||
import { getTranslations, setRequestLocale } from "next-intl/server";
|
|
||||||
import NextLink from "next/link";
|
|
||||||
|
|
||||||
export default async function RulesMenu({
|
|
||||||
params,
|
|
||||||
}: {
|
|
||||||
params: Promise<{ locale: string }>;
|
|
||||||
}) {
|
|
||||||
const { locale } = await params;
|
|
||||||
|
|
||||||
setRequestLocale(locale);
|
|
||||||
|
|
||||||
const t = await getTranslations({ locale });
|
|
||||||
|
|
||||||
const headingStyle = `text-4xl md:text-5xl lg:text-6xl ${vipnagorgialla.className} font-bold italic text-[#2A2C3F] dark:text-[#EEE5E5] uppercase`;
|
|
||||||
|
|
||||||
const boxStyle = `-skew-x-2 md:-skew-x-5 text-white md:px-12 hover:scale-103 transition-all duration-150 w-full md:w-xl lg:w-[400px]`;
|
|
||||||
|
|
||||||
const boxTextStyle = `text-2xl md:text-3xl ${vipnagorgialla.className} font-bold uppercase text-[#EEE5E5] pb-2 break-normal whitespace-pre-line`;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="flex flex-col md:m-16">
|
|
||||||
<h1 className={`${headingStyle} ml-3 mt-24 md:ml-0 md:mt-16 mb-4 px-4`}>
|
|
||||||
{t("rules.title")}
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<div className="flex flex-wrap flex-row lg:mt-16 justify-center lg:items-start gap-12 flex-grow mb-8">
|
|
||||||
<NextLink href="/kodukord">
|
|
||||||
<div className={`${boxStyle} bg-[#007CAB] py-20 px-8`}>
|
|
||||||
<h2 className={`${boxTextStyle}`}>{t("rules.houseRules")}</h2>
|
|
||||||
</div>
|
|
||||||
</NextLink>
|
|
||||||
|
|
||||||
<NextLink href="/reeglid/cs2">
|
|
||||||
<div className={`${boxStyle} bg-[#1F5673] py-20 px-8`}>
|
|
||||||
<h2 className={`${boxTextStyle}`}>{t("rules.cs2Rules")}</h2>
|
|
||||||
</div>
|
|
||||||
</NextLink>
|
|
||||||
|
|
||||||
<NextLink href="reeglid/lol">
|
|
||||||
<div className={`${boxStyle} bg-[#007CAB] py-20 px-8`}>
|
|
||||||
<h2 className={`${boxTextStyle}`}>{t("rules.lolRules")}</h2>
|
|
||||||
</div>
|
|
||||||
</NextLink>
|
|
||||||
|
|
||||||
{/* Minitourn. link coming soon*/}
|
|
||||||
{/*<Link href="">*/}
|
|
||||||
{/* ajutine div. kui asendate lingiga, siis saab selle ära võtta */}
|
|
||||||
<div className="cursor-not-allowed">
|
|
||||||
<div
|
|
||||||
className={`${boxStyle} bg-[#1F5673] py-16 px-8 opacity-50 pointer-events-none`}
|
|
||||||
>
|
|
||||||
<h2 className={`${boxTextStyle}`}>{t("rules.miniRules")}</h2>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/*</Link>*/}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<SectionDivider />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -41,12 +41,12 @@ export default async function Home({
|
|||||||
>
|
>
|
||||||
{t("navigation.schedule")}
|
{t("navigation.schedule")}
|
||||||
</h2>
|
</h2>
|
||||||
<span className="material-symbols-outlined !text-[clamp(2rem,1.5rem+1.5vw,3.5rem)] !font-bold text-[#007CAB] dark:text-[#00A3E0] group-hover:translate-x-2 dark:group-hover:text-[#EEE5E5] group-hover:text-[#EEE5E5] transition">
|
<span className="material-symbols-outlined text-[clamp(2rem,1.5rem+1.5vw,3.5rem)]! font-bold! text-[#007CAB] dark:text-[#00A3E0] group-hover:translate-x-2 dark:group-hover:text-[#EEE5E5] group-hover:text-[#EEE5E5] transition">
|
||||||
arrow_right_alt
|
arrow_right_alt
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
<span className="material-symbols-outlined !text-[clamp(2rem,1.5rem+1.5vw,3.5rem)] text-[#007CAB] dark:text-[#00A3E0] dark:group-hover:text-[#EEE5E5] group-hover:text-[#EEE5E5]">
|
<span className="material-symbols-outlined text-[clamp(2rem,1.5rem+1.5vw,3.5rem)]! text-[#007CAB] dark:text-[#00A3E0] dark:group-hover:text-[#EEE5E5] group-hover:text-[#EEE5E5]">
|
||||||
event_note
|
event_note
|
||||||
</span>
|
</span>
|
||||||
<p className="text-[clamp(0.875rem,0.75rem+0.5vw,1.25rem)] tracking-[-0.045rem] dark:group-hover:text-[#2A2C3F] group-hover:text-black">
|
<p className="text-[clamp(0.875rem,0.75rem+0.5vw,1.25rem)] tracking-[-0.045rem] dark:group-hover:text-[#2A2C3F] group-hover:text-black">
|
||||||
@@ -61,7 +61,7 @@ export default async function Home({
|
|||||||
src="https://player.twitch.tv/?channel=tipilan_ee&parent=localhost&parent=tipilan.ee"
|
src="https://player.twitch.tv/?channel=tipilan_ee&parent=localhost&parent=tipilan.ee"
|
||||||
height="100%"
|
height="100%"
|
||||||
width="100%"
|
width="100%"
|
||||||
className="w-full h-full min-h-[400px]"
|
className="w-full h-full min-h-100"
|
||||||
allow="autoplay; encrypted-media"
|
allow="autoplay; encrypted-media"
|
||||||
></iframe>
|
></iframe>
|
||||||
</div>
|
</div>
|
||||||
@@ -78,13 +78,13 @@ export default async function Home({
|
|||||||
>
|
>
|
||||||
{t("navigation.tournaments")}
|
{t("navigation.tournaments")}
|
||||||
</h2>
|
</h2>
|
||||||
<span className="material-symbols-outlined !text-[clamp(2rem,1.5rem+1.5vw,3.5rem)] !font-bold text-[#007CAB] dark:text-[#00A3E0] group-hover:translate-x-2 dark:group-hover:text-[#EEE5E5] group-hover:text-[#EEE5E5] transition">
|
<span className="material-symbols-outlined text-[clamp(2rem,1.5rem+1.5vw,3.5rem)]! font-bold! text-[#007CAB] dark:text-[#00A3E0] group-hover:translate-x-2 dark:group-hover:text-[#EEE5E5] group-hover:text-[#EEE5E5] transition">
|
||||||
arrow_right_alt
|
arrow_right_alt
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
<span className="material-symbols-outlined !text-[clamp(2rem,1.5rem+1.5vw,3.5rem)] text-[#007CAB] dark:text-[#00A3E0] dark:group-hover:text-[#EEE5E5] group-hover:text-[#EEE5E5]">
|
<span className="material-symbols-outlined text-[clamp(2rem,1.5rem+1.5vw,3.5rem)]! text-[#007CAB] dark:text-[#00A3E0] dark:group-hover:text-[#EEE5E5] group-hover:text-[#EEE5E5]">
|
||||||
trophy
|
trophy
|
||||||
</span>
|
</span>
|
||||||
<p className="text-[clamp(0.875rem,0.75rem+0.5vw,1.25rem)] tracking-[-0.045rem] dark:group-hover:text-[#2A2C3F] group-hover:text-black">
|
<p className="text-[clamp(0.875rem,0.75rem+0.5vw,1.25rem)] tracking-[-0.045rem] dark:group-hover:text-[#2A2C3F] group-hover:text-black">
|
||||||
@@ -102,12 +102,12 @@ export default async function Home({
|
|||||||
>
|
>
|
||||||
{t("navigation.expo")}
|
{t("navigation.expo")}
|
||||||
</h2>
|
</h2>
|
||||||
<span className="material-symbols-outlined !text-[clamp(2rem,1.5rem+1.5vw,3.5rem)] !font-bold text-[#007CAB] dark:text-[#00A3E0] group-hover:translate-x-2 dark:group-hover:text-[#EEE5E5] group-hover:text-[#EEE5E5] transition">
|
<span className="material-symbols-outlined text-[clamp(2rem,1.5rem+1.5vw,3.5rem)]! font-bold! text-[#007CAB] dark:text-[#00A3E0] group-hover:translate-x-2 dark:group-hover:text-[#EEE5E5] group-hover:text-[#EEE5E5] transition">
|
||||||
arrow_right_alt
|
arrow_right_alt
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
<span className="material-symbols-outlined !text-[clamp(2rem,1.5rem+1.5vw,3.5rem)] text-[#007CAB] dark:text-[#00A3E0] dark:group-hover:text-[#EEE5E5] group-hover:text-[#EEE5E5]">
|
<span className="material-symbols-outlined text-[clamp(2rem,1.5rem+1.5vw,3.5rem)]! text-[#007CAB] dark:text-[#00A3E0] dark:group-hover:text-[#EEE5E5] group-hover:text-[#EEE5E5]">
|
||||||
weekend
|
weekend
|
||||||
</span>
|
</span>
|
||||||
<p className="text-[clamp(0.875rem,0.75rem+0.5vw,1.25rem)] tracking-[-0.045rem] dark:group-hover:text-[#2A2C3F] group-hover:text-black">
|
<p className="text-[clamp(0.875rem,0.75rem+0.5vw,1.25rem)] tracking-[-0.045rem] dark:group-hover:text-[#2A2C3F] group-hover:text-black">
|
||||||
@@ -125,7 +125,7 @@ export default async function Home({
|
|||||||
<h3 className="text-4xl md:text-5xl dark:text-[#EEE5E5] dark:group-hover:text-[#2A2C3F] text-[#2A2C3F] group-hover:text-black">
|
<h3 className="text-4xl md:text-5xl dark:text-[#EEE5E5] dark:group-hover:text-[#2A2C3F] text-[#2A2C3F] group-hover:text-black">
|
||||||
{t("home.sections.reserveSpot")}
|
{t("home.sections.reserveSpot")}
|
||||||
</h3>
|
</h3>
|
||||||
<span className="material-symbols-outlined !text-[clamp(2rem,1.5rem+1.5vw,3.5rem)] !font-bold text-[#007CAB] dark:text-[#00A3E0] hidden md:block group-hover:translate-x-2 group-hover:text-[#EEE5E5] dark:group-hover:text-[#EEE5E5] transition">
|
<span className="material-symbols-outlined text-[clamp(2rem,1.5rem+1.5vw,3.5rem)]! font-bold! text-[#007CAB] dark:text-[#00A3E0] hidden md:block group-hover:translate-x-2 group-hover:text-[#EEE5E5] dark:group-hover:text-[#EEE5E5] transition">
|
||||||
arrow_right_alt
|
arrow_right_alt
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
229
src/app/[locale]/turniirid/cs2/page.tsx
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
||||||
|
import RuleNav from "@/components/RuleNav";
|
||||||
|
import RulesContent from "@/components/RulesContent";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { getTranslations, setRequestLocale } from "next-intl/server";
|
||||||
|
|
||||||
|
const sectionKeys = [
|
||||||
|
{ id: "intro", labelKey: "cs2page.nav.intro" },
|
||||||
|
{ id: "info", labelKey: "cs2page.nav.info" },
|
||||||
|
{ id: "prizes", labelKey: "cs2page.nav.prizes" },
|
||||||
|
{ id: "format", labelKey: "cs2page.nav.format" },
|
||||||
|
{ id: "vrs", labelKey: "cs2page.nav.vrs" },
|
||||||
|
// { id: "faq", labelKey: "cs2page.nav.faq" },
|
||||||
|
{ id: "rules", labelKey: "cs2page.nav.rules" },
|
||||||
|
];
|
||||||
|
|
||||||
|
export default async function CS2Tournament({
|
||||||
|
params,
|
||||||
|
}: {
|
||||||
|
params: Promise<{ locale: string }>;
|
||||||
|
}) {
|
||||||
|
const { locale } = await params;
|
||||||
|
setRequestLocale(locale);
|
||||||
|
const t = await getTranslations({ locale });
|
||||||
|
|
||||||
|
const sections = sectionKeys.map((section) => ({
|
||||||
|
id: section.id,
|
||||||
|
label: t(section.labelKey),
|
||||||
|
}));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="bg-[#0E0F19] min-h-screen pt-16 md:pt-20">
|
||||||
|
<div className="max-w-480 mx-auto px-6 md:px-12 py-8 md:py-16">
|
||||||
|
<div className="grid grid-cols-1 lg:grid-cols-[1fr_300px] gap-8 lg:gap-16">
|
||||||
|
{/* Main content */}
|
||||||
|
<div>
|
||||||
|
{/* Header */}
|
||||||
|
<h1
|
||||||
|
className={`${vipnagorgialla.className} font-bold italic text-[clamp(2.5rem,2rem+3vw,4rem)] leading-tight text-[#00A3E0] uppercase mb-4`}
|
||||||
|
>
|
||||||
|
{t("cs2page.title")}
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
{/* Buttons */}
|
||||||
|
<div className="flex flex-wrap gap-4 mb-12">
|
||||||
|
<Link
|
||||||
|
href="https://fienta.com/et/tipilan"
|
||||||
|
target="_blank"
|
||||||
|
className={`${vipnagorgialla.className} font-bold italic px-4 py-3 bg-[#00A3E0] hover:bg-[#E5E5EE] text-[#0A121F] uppercase transition`}
|
||||||
|
>
|
||||||
|
{t("cs2page.buyTicket")}
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href="https://git.edunaut.ee/slunk/TipiLAN_reeglistik_ruleset/src/branch/main/CS2%20tournament"
|
||||||
|
target="_blank"
|
||||||
|
className={`${vipnagorgialla.className} font-bold italic text-lg uppercase px-4 py-2 border-2 border-[#00A3E0] bg-[#1F5673] text-[#EEE5E5] hover:bg-[#00A3E0] hover:text-black transition`}
|
||||||
|
>
|
||||||
|
{t("cs2page.viewGithub")}
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* SISSEJUHATUS */}
|
||||||
|
<section id="intro" className="mb-12 scroll-mt-24 md:scroll-mt-28">
|
||||||
|
<h2
|
||||||
|
className={`${vipnagorgialla.className} font-bold italic text-2xl md:text-3xl text-white uppercase mb-4`}
|
||||||
|
>
|
||||||
|
{t("cs2page.intro.title")}
|
||||||
|
</h2>
|
||||||
|
<p className="text-white mb-6">
|
||||||
|
{t("cs2page.intro.description")}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3
|
||||||
|
className={`${vipnagorgialla.className} font-bold italic text-xl text-white uppercase mb-2`}
|
||||||
|
>
|
||||||
|
{t("cs2page.intro.previousWinners")}
|
||||||
|
</h3>
|
||||||
|
<p className="text-white font-bold">2025</p>
|
||||||
|
<ol className="text-white list-decimal list-inside mb-4">
|
||||||
|
<li>RAID (Eesti)</li>
|
||||||
|
<li>hypewrld (Läti)</li>
|
||||||
|
<li>CSDIILIT (Soome/Eesti)</li>
|
||||||
|
</ol>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* ÜLDINE INFO */}
|
||||||
|
<section id="info" className="mb-12 scroll-mt-24 md:scroll-mt-28">
|
||||||
|
<h2
|
||||||
|
className={`${vipnagorgialla.className} font-bold italic text-2xl md:text-3xl text-white uppercase mb-4`}
|
||||||
|
>
|
||||||
|
{t("cs2page.info.title")}
|
||||||
|
</h2>
|
||||||
|
<p className="text-white">{t("cs2page.info.description")}</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* AUHINNAFOND */}
|
||||||
|
<section id="prizes" className="mb-12 scroll-mt-24 md:scroll-mt-28">
|
||||||
|
<h2
|
||||||
|
className={`${vipnagorgialla.className} font-bold italic text-2xl md:text-3xl text-white uppercase mb-4`}
|
||||||
|
>
|
||||||
|
{t("cs2page.prizes.title")}
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<h3
|
||||||
|
className={`${vipnagorgialla.className} font-bold italic text-xl text-white uppercase mb-2`}
|
||||||
|
>
|
||||||
|
{t("cs2page.prizes.mainTitle")}
|
||||||
|
</h3>
|
||||||
|
<ul className="text-white mb-2">
|
||||||
|
<li>
|
||||||
|
1. koht - 3000€, 600€ inimese kohta, 50% ehk 1/2
|
||||||
|
auhinnafondist.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
2. koht - 2000€, 400€ inimese kohta, 33.3...(3)% ehk 1/3
|
||||||
|
auhinnafondist.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
3. koht - 1000€, 200€ inimese kohta, 16.6...(6)% ehk 1/6
|
||||||
|
auhinnafondist.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p className="text-white/80 text-sm mb-6">
|
||||||
|
{t("cs2page.prizes.mainNote")}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3
|
||||||
|
className={`${vipnagorgialla.className} font-bold italic text-xl text-white uppercase mb-2`}
|
||||||
|
>
|
||||||
|
{t("cs2page.prizes.secondTitle")}
|
||||||
|
</h3>
|
||||||
|
<ul className="text-white mb-2">
|
||||||
|
<li>
|
||||||
|
1. koht - 500€, 100€ inimese kohta, 66.6...(6)% ehk 2/3
|
||||||
|
auhinnafondist.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
2. koht - 250€, 50€ inimese kohta, 33.3...(3)% ehk 1/3
|
||||||
|
auhinnafondist.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p className="text-white/80 text-sm">
|
||||||
|
{t("cs2page.prizes.secondNote")}
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* TURNIIRI FORMAAT */}
|
||||||
|
<section id="format" className="mb-12 scroll-mt-24 md:scroll-mt-28">
|
||||||
|
<h2
|
||||||
|
className={`${vipnagorgialla.className} font-bold italic text-2xl md:text-3xl text-white uppercase mb-4`}
|
||||||
|
>
|
||||||
|
{t("cs2page.format.title")}
|
||||||
|
</h2>
|
||||||
|
<p className="text-white mb-4">
|
||||||
|
{t("cs2page.format.description")}
|
||||||
|
</p>
|
||||||
|
<p className="text-white">{t("cs2page.format.day1")}</p>
|
||||||
|
<p className="text-white">{t("cs2page.format.day23")}</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* VRS INFO */}
|
||||||
|
<section id="vrs" className="mb-12 scroll-mt-24 md:scroll-mt-28">
|
||||||
|
<h2
|
||||||
|
className={`${vipnagorgialla.className} font-bold italic text-2xl md:text-3xl text-white uppercase mb-4`}
|
||||||
|
>
|
||||||
|
{t("cs2page.vrs.title")}
|
||||||
|
</h2>
|
||||||
|
<p className="text-white mb-4">{t("cs2page.vrs.description1")}</p>
|
||||||
|
<p className="text-white">{t("cs2page.vrs.description2")}</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* FAQ - commented out until content is ready
|
||||||
|
<section id="faq" className="mb-12">
|
||||||
|
<h2
|
||||||
|
className={`${vipnagorgialla.className} font-bold italic text-2xl md:text-3xl text-white uppercase mb-4`}
|
||||||
|
>
|
||||||
|
{t("cs2page.faq.title")}
|
||||||
|
</h2>
|
||||||
|
<h3
|
||||||
|
className={`${vipnagorgialla.className} font-bold italic text-lg text-white uppercase mb-2`}
|
||||||
|
>
|
||||||
|
{t("cs2page.faq.q1")}
|
||||||
|
</h3>
|
||||||
|
<p className="text-white mb-4">
|
||||||
|
{t("cs2page.faq.a1")}
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
*/}
|
||||||
|
|
||||||
|
{/* REEGLID */}
|
||||||
|
<section id="rules" className="mb-12 scroll-mt-24 md:scroll-mt-28">
|
||||||
|
<h2
|
||||||
|
className={`${vipnagorgialla.className} font-bold italic text-2xl md:text-3xl text-white uppercase mb-4`}
|
||||||
|
>
|
||||||
|
{t("cs2page.rules.title")}
|
||||||
|
</h2>
|
||||||
|
<p className="text-white mb-6">
|
||||||
|
{t("cs2page.rules.description")}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<RulesContent sections={t.raw("cs2page.rules.sections")} />
|
||||||
|
|
||||||
|
<div className="mt-8">
|
||||||
|
<p className="text-white mb-2">{t("cs2page.rules.contact")}</p>
|
||||||
|
<p className="text-[#00A3E0] font-bold">
|
||||||
|
{t("cs2page.rules.contactName")}
|
||||||
|
</p>
|
||||||
|
<p className="text-white">{t("cs2page.rules.contactRole")}</p>
|
||||||
|
<p className="text-white">
|
||||||
|
Discord:{" "}
|
||||||
|
<a
|
||||||
|
href="https://discord.com/users/292372329747710013"
|
||||||
|
target="_blank"
|
||||||
|
className="text-[#00A3E0] hover:text-white transition"
|
||||||
|
>
|
||||||
|
hrkruger
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Sidebar navigation */}
|
||||||
|
<RuleNav sections={sections} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
182
src/app/[locale]/turniirid/lol/page.tsx
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
||||||
|
import RuleNav from "@/components/RuleNav";
|
||||||
|
import RulesContent from "@/components/RulesContent";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { getTranslations, setRequestLocale } from "next-intl/server";
|
||||||
|
|
||||||
|
const sectionKeys = [
|
||||||
|
{ id: "intro", labelKey: "lolpage.nav.intro" },
|
||||||
|
{ id: "info", labelKey: "lolpage.nav.info" },
|
||||||
|
{ id: "prizes", labelKey: "lolpage.nav.prizes" },
|
||||||
|
{ id: "format", labelKey: "lolpage.nav.format" },
|
||||||
|
// { id: "faq", labelKey: "lolpage.nav.faq" },
|
||||||
|
{ id: "rules", labelKey: "lolpage.nav.rules" },
|
||||||
|
];
|
||||||
|
|
||||||
|
export default async function LoLTournament({
|
||||||
|
params,
|
||||||
|
}: {
|
||||||
|
params: Promise<{ locale: string }>;
|
||||||
|
}) {
|
||||||
|
const { locale } = await params;
|
||||||
|
setRequestLocale(locale);
|
||||||
|
const t = await getTranslations({ locale });
|
||||||
|
|
||||||
|
const sections = sectionKeys.map((section) => ({
|
||||||
|
id: section.id,
|
||||||
|
label: t(section.labelKey),
|
||||||
|
}));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="bg-[#0E0F19] min-h-screen pt-16 md:pt-20">
|
||||||
|
<div className="max-w-480 mx-auto px-6 md:px-12 py-8 md:py-16">
|
||||||
|
<div className="grid grid-cols-1 lg:grid-cols-[1fr_300px] gap-8 lg:gap-16">
|
||||||
|
{/* Main content */}
|
||||||
|
<div>
|
||||||
|
{/* Header */}
|
||||||
|
<h1
|
||||||
|
className={`${vipnagorgialla.className} font-bold italic text-[clamp(2.5rem,2rem+3vw,4rem)] leading-tight text-[#00A3E0] uppercase mb-4`}
|
||||||
|
>
|
||||||
|
{t("lolpage.title")}
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
{/* Buttons */}
|
||||||
|
<div className="flex flex-wrap gap-4 mb-12">
|
||||||
|
<Link
|
||||||
|
href="https://fienta.com/et/tipilan"
|
||||||
|
target="_blank"
|
||||||
|
className={`${vipnagorgialla.className} font-bold italic px-4 py-2 bg-[#00A3E0] hover:bg-[#E5E5EE] text-[#0A121F] uppercase transition`}
|
||||||
|
>
|
||||||
|
{t("lolpage.buyTicket")}
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* SISSEJUHATUS */}
|
||||||
|
<section id="intro" className="mb-12 scroll-mt-24 md:scroll-mt-28">
|
||||||
|
<h2
|
||||||
|
className={`${vipnagorgialla.className} font-bold italic text-2xl md:text-3xl text-white uppercase mb-4`}
|
||||||
|
>
|
||||||
|
{t("lolpage.intro.title")}
|
||||||
|
</h2>
|
||||||
|
<p className="text-white mb-6">
|
||||||
|
{t("lolpage.intro.description")}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3
|
||||||
|
className={`${vipnagorgialla.className} font-bold italic text-xl text-white uppercase mb-2`}
|
||||||
|
>
|
||||||
|
{t("lolpage.intro.previousWinners")}
|
||||||
|
</h3>
|
||||||
|
<p className="text-white font-bold">2025</p>
|
||||||
|
<ol className="text-white list-decimal list-inside mb-4">
|
||||||
|
<li>Ükssilm (Eesti)</li>
|
||||||
|
<li>Eesti Rästikud (Eesti)</li>
|
||||||
|
<li>LOMiks (Läti)</li>
|
||||||
|
</ol>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* ÜLDINE INFO */}
|
||||||
|
<section id="info" className="mb-12 scroll-mt-24 md:scroll-mt-28">
|
||||||
|
<h2
|
||||||
|
className={`${vipnagorgialla.className} font-bold italic text-2xl md:text-3xl text-white uppercase mb-4`}
|
||||||
|
>
|
||||||
|
{t("lolpage.info.title")}
|
||||||
|
</h2>
|
||||||
|
<p className="text-white">{t("lolpage.info.description")}</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* AUHINNAFOND */}
|
||||||
|
<section id="prizes" className="mb-12 scroll-mt-24 md:scroll-mt-28">
|
||||||
|
<h2
|
||||||
|
className={`${vipnagorgialla.className} font-bold italic text-2xl md:text-3xl text-white uppercase mb-4`}
|
||||||
|
>
|
||||||
|
{t("lolpage.prizes.title")}
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<h3
|
||||||
|
className={`${vipnagorgialla.className} font-bold italic text-xl text-white uppercase mb-2`}
|
||||||
|
>
|
||||||
|
{t("lolpage.prizes.mainTitle")}
|
||||||
|
</h3>
|
||||||
|
<ul className="text-white mb-2">
|
||||||
|
<li>{t("lolpage.prizes.place1")}</li>
|
||||||
|
<li>{t("lolpage.prizes.place2")}</li>
|
||||||
|
<li>{t("lolpage.prizes.place3")}</li>
|
||||||
|
</ul>
|
||||||
|
<p className="text-white/80 text-sm">
|
||||||
|
{t("lolpage.prizes.note")}
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* TURNIIRI FORMAAT */}
|
||||||
|
<section id="format" className="mb-12 scroll-mt-24 md:scroll-mt-28">
|
||||||
|
<h2
|
||||||
|
className={`${vipnagorgialla.className} font-bold italic text-2xl md:text-3xl text-white uppercase mb-4`}
|
||||||
|
>
|
||||||
|
{t("lolpage.format.title")}
|
||||||
|
</h2>
|
||||||
|
<p className="text-white mb-4">
|
||||||
|
{t("lolpage.format.description")}
|
||||||
|
</p>
|
||||||
|
<p className="text-white">{t("lolpage.format.day1")}</p>
|
||||||
|
<p className="text-white">{t("lolpage.format.day2")}</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* FAQ - commented out until content is ready
|
||||||
|
<section id="faq" className="mb-12">
|
||||||
|
<h2
|
||||||
|
className={`${vipnagorgialla.className} font-bold italic text-2xl md:text-3xl text-white uppercase mb-4`}
|
||||||
|
>
|
||||||
|
{t("lolpage.faq.title")}
|
||||||
|
</h2>
|
||||||
|
<h3
|
||||||
|
className={`${vipnagorgialla.className} font-bold italic text-lg text-white uppercase mb-2`}
|
||||||
|
>
|
||||||
|
{t("lolpage.faq.q1")}
|
||||||
|
</h3>
|
||||||
|
<p className="text-white mb-4">
|
||||||
|
{t("lolpage.faq.a1")}
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
*/}
|
||||||
|
|
||||||
|
{/* REEGLID */}
|
||||||
|
<section id="rules" className="mb-12 scroll-mt-24 md:scroll-mt-28">
|
||||||
|
<h2
|
||||||
|
className={`${vipnagorgialla.className} font-bold italic text-2xl md:text-3xl text-white uppercase mb-4`}
|
||||||
|
>
|
||||||
|
{t("lolpage.rules.title")}
|
||||||
|
</h2>
|
||||||
|
<p className="text-white mb-6">
|
||||||
|
{t("lolpage.rules.description")}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<RulesContent sections={t.raw("lolpage.rules.sections")} />
|
||||||
|
|
||||||
|
<div className="mt-8">
|
||||||
|
<p className="text-white mb-2">{t("lolpage.rules.contact")}</p>
|
||||||
|
<p className="text-[#00A3E0] font-bold">
|
||||||
|
{t("lolpage.rules.contactName")}
|
||||||
|
</p>
|
||||||
|
<p className="text-white">{t("lolpage.rules.contactRole")}</p>
|
||||||
|
<p className="text-white">
|
||||||
|
Discord:{" "}
|
||||||
|
<a
|
||||||
|
href="https://discord.com/users/125585160761638912"
|
||||||
|
target="_blank"
|
||||||
|
className="text-[#00A3E0] hover:text-white transition"
|
||||||
|
>
|
||||||
|
Kukkel
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Sidebar navigation */}
|
||||||
|
<RuleNav sections={sections} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,7 +1,46 @@
|
|||||||
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { getTranslations, setRequestLocale } from "next-intl/server";
|
import { setRequestLocale } from "next-intl/server";
|
||||||
|
|
||||||
|
interface TournamentCardProps {
|
||||||
|
title: string;
|
||||||
|
buttonHref: string;
|
||||||
|
backgroundImage: string;
|
||||||
|
objectPosition?: string;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function TournamentCard({
|
||||||
|
title,
|
||||||
|
buttonHref,
|
||||||
|
backgroundImage,
|
||||||
|
objectPosition = "center",
|
||||||
|
className = "",
|
||||||
|
}: TournamentCardProps) {
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
href={buttonHref}
|
||||||
|
aria-label={title}
|
||||||
|
className={`group relative bg-[#0E0F19] border-[#1F5673] flex flex-col items-center justify-center h-full min-h-87.5 transition-colors duration-200 focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-[#00A3E0]/40 ${className}`}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src={backgroundImage}
|
||||||
|
alt=""
|
||||||
|
fill
|
||||||
|
className="object-cover opacity-50 transition-opacity duration-200 group-hover:opacity-40 group-focus-visible:opacity-40"
|
||||||
|
style={{ objectPosition }}
|
||||||
|
/>
|
||||||
|
<div className="relative z-10 flex flex-col items-center text-center">
|
||||||
|
<h2
|
||||||
|
className={`${vipnagorgialla.className} font-bold italic text-[clamp(2rem,1.5rem+3vw,4rem)] leading-none text-[#EEE5E5] uppercase transition-colors duration-200 group-hover:text-[#00A3E0] group-focus-visible:text-[#00A3E0]`}
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default async function Tourney({
|
export default async function Tourney({
|
||||||
params,
|
params,
|
||||||
@@ -10,254 +49,26 @@ export default async function Tourney({
|
|||||||
}) {
|
}) {
|
||||||
const { locale } = await params;
|
const { locale } = await params;
|
||||||
setRequestLocale(locale);
|
setRequestLocale(locale);
|
||||||
const t = await getTranslations({ locale });
|
|
||||||
const headingStyle = `text-3xl md:text-5xl lg:text-5xl ${vipnagorgialla.className} font-bold uppercase text-[#2A2C3F] dark:text-[#EEE5E5] -skew-x-2 md:-skew-x-5 break-normal`;
|
|
||||||
|
|
||||||
const miniTournaments: {
|
|
||||||
name: string;
|
|
||||||
prize: string;
|
|
||||||
image: string;
|
|
||||||
objectPosition?: string;
|
|
||||||
bgClass?: string;
|
|
||||||
}[] = [
|
|
||||||
{
|
|
||||||
name: "Tekken 8",
|
|
||||||
prize: "200€",
|
|
||||||
image: "/images/miniturniirid/tekken8.jpg",
|
|
||||||
objectPosition: "object-center",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "WRC",
|
|
||||||
prize: "350€",
|
|
||||||
image: "/images/miniturniirid/wrc.jpg",
|
|
||||||
objectPosition: "object-center",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Street Fighter 6",
|
|
||||||
prize: "150€",
|
|
||||||
image: "/images/miniturniirid/street_fighter.jpg",
|
|
||||||
objectPosition: "object-center",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Gran Turismo",
|
|
||||||
prize: "200€",
|
|
||||||
image: "/images/miniturniirid/gran_turismo.jpg",
|
|
||||||
objectPosition: "object-center",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "FC 26",
|
|
||||||
prize: "100€",
|
|
||||||
image: "/images/miniturniirid/fc26.jpg",
|
|
||||||
objectPosition: "object-center",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Dwarf Escape",
|
|
||||||
prize: "50€",
|
|
||||||
image: "/images/miniturniirid/dwarf_escape.png",
|
|
||||||
objectPosition: "object-center",
|
|
||||||
bgClass: "bg-black",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Buckshot Roulette",
|
|
||||||
prize: "Merch",
|
|
||||||
image: "/images/miniturniirid/buckshot_tournament.png",
|
|
||||||
objectPosition: "object-center",
|
|
||||||
bgClass: "bg-black",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "2XKO",
|
|
||||||
prize: "100€",
|
|
||||||
image: "/images/miniturniirid/2xko.png",
|
|
||||||
objectPosition: "object-top",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Super Smash Bros. Ultimate",
|
|
||||||
prize: "100€",
|
|
||||||
image: "/images/miniturniirid/super_smash_bros.jpg",
|
|
||||||
objectPosition: "object-top",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col min-h-[90vh] mt-16">
|
<div className="bg-[#0E0F19] min-h-0 flex flex-col flex-1">
|
||||||
<h1
|
{/* 1x2 Grid */}
|
||||||
className={`text-4xl md:text-5xl lg:text-6xl ${vipnagorgialla.className} font-bold italic uppercase
|
<div className="grid grid-cols-1 md:grid-cols-2 auto-rows-fr pt-14 flex-1 min-h-0">
|
||||||
text-[#2A2C3F] dark:text-[#EEE5E5] mt-8 md:mt-16 mb-8 m-6 md:m-16`}
|
{/* CS2 */}
|
||||||
>
|
<TournamentCard
|
||||||
{t("tournaments.title")}
|
title="COUNTER-STRIKE 2"
|
||||||
</h1>
|
buttonHref="/turniirid/cs2"
|
||||||
|
backgroundImage="/images/landing/cs2_tournament.jpg"
|
||||||
<div className="flex flex-col">
|
className="border-b-[3px] md:border-b-0 md:border-r-3"
|
||||||
|
/>
|
||||||
{/* Mini-turniirid */}
|
|
||||||
<div className="hover:bg-[#007CAB] py-8 md:py-16 border-b-[3px] border-[#1F5673] transition group">
|
|
||||||
<div className="mx-8 md:mx-16 lg:mx-32 xl:mx-48">
|
|
||||||
<div className="-skew-x-2 md:-skew-x-5 mb-8">
|
|
||||||
<h2 className={`${headingStyle}`}>
|
|
||||||
{t("tournaments.mini.title")}
|
|
||||||
</h2>
|
|
||||||
<p
|
|
||||||
className={
|
|
||||||
"text-2xl mb-4 text-neutral-500 group-hover:text-black"
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{t("tournaments.mini.timing")}
|
|
||||||
</p>
|
|
||||||
<p className="text-balance">
|
|
||||||
{t("tournaments.mini.description1")}
|
|
||||||
</p>
|
|
||||||
<p className="text-balance">
|
|
||||||
{t("tournaments.mini.description2")}
|
|
||||||
</p>
|
|
||||||
<br />
|
|
||||||
<div className="flex flex-row flex-wrap gap-4 md:gap-8">
|
|
||||||
<Link href="/kodukord" target="_blank">
|
|
||||||
<button
|
|
||||||
className={`px-4 py-2 bg-[#1F5673] cursor-pointer ${vipnagorgialla.className} font-bold italic text-[#ECE5E5]`}
|
|
||||||
>
|
|
||||||
{t("tournaments.mini.readRules")}
|
|
||||||
</button>
|
|
||||||
</Link>
|
|
||||||
<a href="https://fienta.com/et/tipilan" target="_blank">
|
|
||||||
<button
|
|
||||||
className={`px-4 py-2 bg-[#00A3E0] group-hover:bg-black cursor-pointer ${vipnagorgialla.className} font-bold italic text-[#ECE5E5]`}
|
|
||||||
>
|
|
||||||
{t("tournaments.mini.buyTicket")}
|
|
||||||
</button>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="grid grid-cols-2 md:grid-cols-3 gap-8">
|
|
||||||
{miniTournaments.map((tournament) => (
|
|
||||||
<div key={tournament.name} className="text-center">
|
|
||||||
<Image
|
|
||||||
src={tournament.image}
|
|
||||||
alt={tournament.name}
|
|
||||||
width={400}
|
|
||||||
height={300}
|
|
||||||
className={`outline-10 outline-[#007CAB] bg-black object-cover w-full aspect-video -skew-x-2 md:-skew-x-5 ${
|
|
||||||
tournament.objectPosition || "object-center"
|
|
||||||
}`}
|
|
||||||
/>
|
|
||||||
<div className="-skew-x-2 md:-skew-x-5">
|
|
||||||
<p className="mt-2 font-semibold">{tournament.name} - {tournament.prize}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* CS2 turniir */}
|
|
||||||
<div className="hover:bg-[#007CAB] py-8 md:py-16 transition group">
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 md:gap-16 items-center mx-8 md:mx-16 lg:mx-32 xl:mx-48">
|
|
||||||
<div className="-skew-x-2 md:-skew-x-5">
|
|
||||||
<h2 className={`${headingStyle}`}>
|
|
||||||
{t("tournaments.cs2.title")}
|
|
||||||
</h2>
|
|
||||||
<p
|
|
||||||
className={
|
|
||||||
"text-2xl mb-4 text-neutral-500 group-hover:text-black"
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{t("tournaments.cs2.timing")}
|
|
||||||
</p>
|
|
||||||
<p className="text-balance">
|
|
||||||
{t("tournaments.cs2.description1")}
|
|
||||||
</p>
|
|
||||||
<br />
|
|
||||||
<p className="text-balance">
|
|
||||||
{t("tournaments.cs2.description2")}
|
|
||||||
</p>
|
|
||||||
<p className="text-balance">
|
|
||||||
{t("tournaments.cs2.description3")}
|
|
||||||
</p>
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<div className={"flex flex-row flex-wrap gap-8"}>
|
|
||||||
<Link href="/reeglid/cs2" target="_blank">
|
|
||||||
<button
|
|
||||||
className={`px-4 py-2 bg-[#1F5673] cursor-pointer ${vipnagorgialla.className} font-bold italic text-[#ECE5E5]`}
|
|
||||||
>
|
|
||||||
{t("tournaments.cs2.readRules")}
|
|
||||||
</button>
|
|
||||||
</Link>
|
|
||||||
<a href="https://fienta.com/et/tipilan" target="_blank">
|
|
||||||
<button
|
|
||||||
className={`px-4 py-2 bg-[#00A3E0] group-hover:bg-black cursor-pointer ${vipnagorgialla.className} font-bold italic text-[#ECE5E5]`}
|
|
||||||
>
|
|
||||||
{t("tournaments.cs2.buyTicket")}
|
|
||||||
</button>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="hidden md:block">
|
|
||||||
<div>
|
|
||||||
{/* Outside div needs to remain so that overflow won't occur*/}
|
|
||||||
<Image
|
|
||||||
src="/images/cs2_tournament_logo.png"
|
|
||||||
alt="CS2 tournament"
|
|
||||||
width={600}
|
|
||||||
height={400}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* LoL turniir */}
|
|
||||||
<div className="hover:bg-[#007CAB] py-8 md:py-16 border-t-[3px] border-b-[3px] border-[#1F5673] transition group">
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 md:gap-16 items-center mx-8 md:mx-16 lg:mx-32 xl:mx-48">
|
|
||||||
<div className="hidden md:block">
|
|
||||||
<div>
|
|
||||||
{/* Outside div needs to remain so that overflow won't occur*/}
|
|
||||||
<Image
|
|
||||||
src="/images/lol_tournament_logo.png"
|
|
||||||
alt="LoL tournament"
|
|
||||||
width={600}
|
|
||||||
height={400}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex-auto text-right -skew-x-2 md:-skew-x-5">
|
|
||||||
<h2 className={`${headingStyle}`}>
|
|
||||||
{t("tournaments.lol.title")}
|
|
||||||
</h2>
|
|
||||||
<p
|
|
||||||
className={
|
|
||||||
"text-2xl mb-4 text-neutral-500 group-hover:text-black"
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{t("tournaments.lol.timing")}
|
|
||||||
</p>
|
|
||||||
<p className="text-balance">
|
|
||||||
{t("tournaments.lol.description1")}
|
|
||||||
</p>
|
|
||||||
<br />
|
|
||||||
<p className="text-balance">
|
|
||||||
{t("tournaments.lol.description2")}
|
|
||||||
</p>
|
|
||||||
<br />
|
|
||||||
<div className="flex flex-row flex-wrap gap-4 md:gap-8 justify-end">
|
|
||||||
<Link href="/reeglid/lol" target="_blank">
|
|
||||||
<button
|
|
||||||
className={`px-4 py-2 bg-[#1F5673] cursor-pointer ${vipnagorgialla.className} font-bold italic text-[#ECE5E5]`}
|
|
||||||
>
|
|
||||||
{t("tournaments.lol.readRules")}
|
|
||||||
</button>
|
|
||||||
</Link>
|
|
||||||
<a href="https://fienta.com/et/tipilan" target="_blank">
|
|
||||||
<button
|
|
||||||
className={`px-4 py-2 bg-[#00A3E0] group-hover:bg-black cursor-pointer ${vipnagorgialla.className} font-bold italic text-[#ECE5E5]`}
|
|
||||||
>
|
|
||||||
{t("tournaments.lol.buyTicket")}
|
|
||||||
</button>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
{/* LoL */}
|
||||||
|
<TournamentCard
|
||||||
|
title="LEAGUE OF LEGENDS"
|
||||||
|
buttonHref="/turniirid/lol"
|
||||||
|
backgroundImage="/images/landing/lol_tournament.png"
|
||||||
|
objectPosition="bottom left"
|
||||||
|
className="border-b-0"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,137 +1,186 @@
|
|||||||
@import 'tailwindcss';
|
@import "tailwindcss";
|
||||||
@import "tw-animate-css";
|
@import "tw-animate-css";
|
||||||
|
|
||||||
@custom-variant dark (&:is(.dark *));
|
@custom-variant dark (&:is(.dark *));
|
||||||
|
|
||||||
@theme inline {
|
@theme inline {
|
||||||
--color-background: var(--background);
|
--color-background: var(--background);
|
||||||
--color-foreground: var(--foreground);
|
--color-foreground: var(--foreground);
|
||||||
--font-sans: var(--font-geist-sans);
|
--font-sans: var(--font-geist-sans);
|
||||||
--font-mono: var(--font-geist-mono);
|
--font-mono: var(--font-geist-mono);
|
||||||
--color-sidebar-ring: var(--sidebar-ring);
|
--color-sidebar-ring: var(--sidebar-ring);
|
||||||
--color-sidebar-border: var(--sidebar-border);
|
--color-sidebar-border: var(--sidebar-border);
|
||||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||||
--color-sidebar-accent: var(--sidebar-accent);
|
--color-sidebar-accent: var(--sidebar-accent);
|
||||||
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||||
--color-sidebar-primary: var(--sidebar-primary);
|
--color-sidebar-primary: var(--sidebar-primary);
|
||||||
--color-sidebar-foreground: var(--sidebar-foreground);
|
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||||
--color-sidebar: var(--sidebar);
|
--color-sidebar: var(--sidebar);
|
||||||
--color-chart-5: var(--chart-5);
|
--color-chart-5: var(--chart-5);
|
||||||
--color-chart-4: var(--chart-4);
|
--color-chart-4: var(--chart-4);
|
||||||
--color-chart-3: var(--chart-3);
|
--color-chart-3: var(--chart-3);
|
||||||
--color-chart-2: var(--chart-2);
|
--color-chart-2: var(--chart-2);
|
||||||
--color-chart-1: var(--chart-1);
|
--color-chart-1: var(--chart-1);
|
||||||
--color-ring: var(--ring);
|
--color-ring: var(--ring);
|
||||||
--color-input: var(--input);
|
--color-input: var(--input);
|
||||||
--color-border: var(--border);
|
--color-border: var(--border);
|
||||||
--color-destructive: var(--destructive);
|
--color-destructive: var(--destructive);
|
||||||
--color-accent-foreground: var(--accent-foreground);
|
--color-accent-foreground: var(--accent-foreground);
|
||||||
--color-accent: var(--accent);
|
--color-accent: var(--accent);
|
||||||
--color-muted-foreground: var(--muted-foreground);
|
--color-muted-foreground: var(--muted-foreground);
|
||||||
--color-muted: var(--muted);
|
--color-muted: var(--muted);
|
||||||
--color-secondary-foreground: var(--secondary-foreground);
|
--color-secondary-foreground: var(--secondary-foreground);
|
||||||
--color-secondary: var(--secondary);
|
--color-secondary: var(--secondary);
|
||||||
--color-primary-foreground: var(--primary-foreground);
|
--color-primary-foreground: var(--primary-foreground);
|
||||||
--color-primary: var(--primary);
|
--color-primary: var(--primary);
|
||||||
--color-popover-foreground: var(--popover-foreground);
|
--color-popover-foreground: var(--popover-foreground);
|
||||||
--color-popover: var(--popover);
|
--color-popover: var(--popover);
|
||||||
--color-card-foreground: var(--card-foreground);
|
--color-card-foreground: var(--card-foreground);
|
||||||
--color-card: var(--card);
|
--color-card: var(--card);
|
||||||
--radius-sm: calc(var(--radius) - 4px);
|
--radius-sm: calc(var(--radius) - 4px);
|
||||||
--radius-md: calc(var(--radius) - 2px);
|
--radius-md: calc(var(--radius) - 2px);
|
||||||
--radius-lg: var(--radius);
|
--radius-lg: var(--radius);
|
||||||
--radius-xl: calc(var(--radius) + 4px);
|
--radius-xl: calc(var(--radius) + 4px);
|
||||||
}
|
}
|
||||||
|
|
||||||
@theme {
|
@theme {
|
||||||
--breakpoint-xs: 30rem;
|
--breakpoint-xs: 30rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: "Work Sans", Arial, Helvetica, sans-serif;
|
font-family: "Work Sans", Arial, Helvetica, sans-serif;
|
||||||
max-width: 100vw;
|
max-width: 100vw;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--radius: 0.625rem;
|
--radius: 0.625rem;
|
||||||
--background: oklch(1 0 0);
|
--background: #0a121f;
|
||||||
--foreground: oklch(0.145 0 0);
|
--foreground: oklch(0.145 0 0);
|
||||||
--card: oklch(1 0 0);
|
--card: oklch(1 0 0);
|
||||||
--card-foreground: oklch(0.145 0 0);
|
--card-foreground: oklch(0.145 0 0);
|
||||||
--popover: oklch(1 0 0);
|
--popover: oklch(1 0 0);
|
||||||
--popover-foreground: oklch(0.145 0 0);
|
--popover-foreground: oklch(0.145 0 0);
|
||||||
--primary: oklch(0.205 0 0);
|
--primary: oklch(0.205 0 0);
|
||||||
--primary-foreground: oklch(0.985 0 0);
|
--primary-foreground: oklch(0.985 0 0);
|
||||||
--secondary: oklch(0.97 0 0);
|
--secondary: oklch(0.97 0 0);
|
||||||
--secondary-foreground: oklch(0.205 0 0);
|
--secondary-foreground: oklch(0.205 0 0);
|
||||||
--muted: oklch(0.97 0 0);
|
--muted: oklch(0.97 0 0);
|
||||||
--muted-foreground: oklch(0.556 0 0);
|
--muted-foreground: oklch(0.556 0 0);
|
||||||
--accent: oklch(0.97 0 0);
|
--accent: oklch(0.97 0 0);
|
||||||
--accent-foreground: oklch(0.205 0 0);
|
--accent-foreground: oklch(0.205 0 0);
|
||||||
--destructive: oklch(0.577 0.245 27.325);
|
--destructive: oklch(0.577 0.245 27.325);
|
||||||
--border: oklch(0.922 0 0);
|
--border: oklch(0.922 0 0);
|
||||||
--input: oklch(0.922 0 0);
|
--input: oklch(0.922 0 0);
|
||||||
--ring: oklch(0.708 0 0);
|
--ring: oklch(0.708 0 0);
|
||||||
--chart-1: oklch(0.646 0.222 41.116);
|
--chart-1: oklch(0.646 0.222 41.116);
|
||||||
--chart-2: oklch(0.6 0.118 184.704);
|
--chart-2: oklch(0.6 0.118 184.704);
|
||||||
--chart-3: oklch(0.398 0.07 227.392);
|
--chart-3: oklch(0.398 0.07 227.392);
|
||||||
--chart-4: oklch(0.828 0.189 84.429);
|
--chart-4: oklch(0.828 0.189 84.429);
|
||||||
--chart-5: oklch(0.769 0.188 70.08);
|
--chart-5: oklch(0.769 0.188 70.08);
|
||||||
--sidebar: oklch(0.985 0 0);
|
--sidebar: oklch(0.985 0 0);
|
||||||
--sidebar-foreground: oklch(0.145 0 0);
|
--sidebar-foreground: oklch(0.145 0 0);
|
||||||
--sidebar-primary: oklch(0.205 0 0);
|
--sidebar-primary: oklch(0.205 0 0);
|
||||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||||
--sidebar-accent: oklch(0.97 0 0);
|
--sidebar-accent: oklch(0.97 0 0);
|
||||||
--sidebar-accent-foreground: oklch(0.205 0 0);
|
--sidebar-accent-foreground: oklch(0.205 0 0);
|
||||||
--sidebar-border: oklch(0.922 0 0);
|
--sidebar-border: oklch(0.922 0 0);
|
||||||
--sidebar-ring: oklch(0.708 0 0);
|
--sidebar-ring: oklch(0.708 0 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark {
|
.dark {
|
||||||
--background: oklch(0.145 0 0);
|
--background: #0a121f;
|
||||||
--foreground: oklch(0.985 0 0);
|
--foreground: oklch(0.985 0 0);
|
||||||
--card: oklch(0.205 0 0);
|
--card: oklch(0.205 0 0);
|
||||||
--card-foreground: oklch(0.985 0 0);
|
--card-foreground: oklch(0.985 0 0);
|
||||||
--popover: oklch(0.205 0 0);
|
--popover: oklch(0.205 0 0);
|
||||||
--popover-foreground: oklch(0.985 0 0);
|
--popover-foreground: oklch(0.985 0 0);
|
||||||
--primary: oklch(0.922 0 0);
|
--primary: oklch(0.922 0 0);
|
||||||
--primary-foreground: oklch(0.205 0 0);
|
--primary-foreground: oklch(0.205 0 0);
|
||||||
--secondary: oklch(0.269 0 0);
|
--secondary: oklch(0.269 0 0);
|
||||||
--secondary-foreground: oklch(0.985 0 0);
|
--secondary-foreground: oklch(0.985 0 0);
|
||||||
--muted: oklch(0.269 0 0);
|
--muted: oklch(0.269 0 0);
|
||||||
--muted-foreground: oklch(0.708 0 0);
|
--muted-foreground: oklch(0.708 0 0);
|
||||||
--accent: oklch(0.269 0 0);
|
--accent: oklch(0.269 0 0);
|
||||||
--accent-foreground: oklch(0.985 0 0);
|
--accent-foreground: oklch(0.985 0 0);
|
||||||
--destructive: oklch(0.704 0.191 22.216);
|
--destructive: oklch(0.704 0.191 22.216);
|
||||||
--border: oklch(1 0 0 / 10%);
|
--border: oklch(1 0 0 / 10%);
|
||||||
--input: oklch(1 0 0 / 15%);
|
--input: oklch(1 0 0 / 15%);
|
||||||
--ring: oklch(0.556 0 0);
|
--ring: oklch(0.556 0 0);
|
||||||
--chart-1: oklch(0.488 0.243 264.376);
|
--chart-1: oklch(0.488 0.243 264.376);
|
||||||
--chart-2: oklch(0.696 0.17 162.48);
|
--chart-2: oklch(0.696 0.17 162.48);
|
||||||
--chart-3: oklch(0.769 0.188 70.08);
|
--chart-3: oklch(0.769 0.188 70.08);
|
||||||
--chart-4: oklch(0.627 0.265 303.9);
|
--chart-4: oklch(0.627 0.265 303.9);
|
||||||
--chart-5: oklch(0.645 0.246 16.439);
|
--chart-5: oklch(0.645 0.246 16.439);
|
||||||
--sidebar: oklch(0.205 0 0);
|
--sidebar: oklch(0.205 0 0);
|
||||||
--sidebar-foreground: oklch(0.985 0 0);
|
--sidebar-foreground: oklch(0.985 0 0);
|
||||||
--sidebar-primary: oklch(0.488 0.243 264.376);
|
--sidebar-primary: oklch(0.488 0.243 264.376);
|
||||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||||
--sidebar-accent: oklch(0.269 0 0);
|
--sidebar-accent: oklch(0.269 0 0);
|
||||||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||||
--sidebar-border: oklch(1 0 0 / 10%);
|
--sidebar-border: oklch(1 0 0 / 10%);
|
||||||
--sidebar-ring: oklch(0.556 0 0);
|
--sidebar-ring: oklch(0.556 0 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
* {
|
* {
|
||||||
@apply border-border outline-ring/50;
|
@apply border-border outline-ring/50;
|
||||||
}
|
}
|
||||||
body {
|
body {
|
||||||
@apply bg-background text-foreground;
|
@apply bg-background text-foreground;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.material-symbols-outlined {
|
.material-symbols-outlined {
|
||||||
font-variation-settings: 'FILL' 1, 'wght' 700, 'GRAD' 0, 'opsz' 24;
|
font-variation-settings:
|
||||||
|
"FILL" 1,
|
||||||
|
"wght" 700,
|
||||||
|
"GRAD" 0,
|
||||||
|
"opsz" 24;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tipilan-logo-letter {
|
||||||
|
animation: tipilan-logo-letter-in 720ms cubic-bezier(0.16, 1, 0.3, 1) both;
|
||||||
|
animation-delay: var(--tipilan-logo-letter-delay, 0ms);
|
||||||
|
opacity: 0;
|
||||||
|
transform: translate3d(0, 100%, 0);
|
||||||
|
will-change: opacity, transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes tipilan-logo-letter-in {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translate3d(0, 100%, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
70% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translate3d(0, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes dropdown-item-in {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translate3d(0, -8px, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translate3d(0, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.tipilan-logo-letter {
|
||||||
|
animation: none;
|
||||||
|
opacity: 1;
|
||||||
|
transform: none;
|
||||||
|
will-change: auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ const workSans = Work_Sans({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "TipiLAN 2025",
|
title: "TipiLAN 2026",
|
||||||
description: "TipiLAN 2025 – Eesti suurim tudengite korraldatud LAN!",
|
description: "TipiLAN 2026 – Eesti suurim tudengite korraldatud LAN!",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function RootLayout({
|
export default function RootLayout({
|
||||||
|
|||||||
@@ -5,8 +5,9 @@ export default function NotFound() {
|
|||||||
return (
|
return (
|
||||||
<ThemeProvider
|
<ThemeProvider
|
||||||
attribute="class"
|
attribute="class"
|
||||||
defaultTheme="system"
|
defaultTheme="dark"
|
||||||
enableSystem
|
enableSystem={false}
|
||||||
|
forcedTheme="dark"
|
||||||
disableTransitionOnChange
|
disableTransitionOnChange
|
||||||
>
|
>
|
||||||
<div className="flex flex-col min-h-[90vh] p-12 justify-center items-center">
|
<div className="flex flex-col min-h-[90vh] p-12 justify-center items-center">
|
||||||
|
|||||||
110
src/components/AnimatedTipilanLogo.tsx
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import Image from "next/image";
|
||||||
|
import type { CSSProperties } from "react";
|
||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
|
const LOGO_WIDTH = 2092;
|
||||||
|
const LOGO_HEIGHT = 300;
|
||||||
|
|
||||||
|
const logoLetters = [
|
||||||
|
{ letter: "T", src: "/letters/T.svg", x: 0, width: 367 },
|
||||||
|
{ letter: "I", src: "/letters/I.svg", x: 334.858, width: 178 },
|
||||||
|
{ letter: "P", src: "/letters/P.svg", x: 481.258, width: 411 },
|
||||||
|
{ letter: "I", src: "/letters/I.svg", x: 872.218, width: 178 },
|
||||||
|
{ letter: "L", src: "/letters/L.svg", x: 1018.64, width: 286 },
|
||||||
|
{ letter: "A", src: "/letters/A.svg", x: 1289.2, width: 390 },
|
||||||
|
{ letter: "N", src: "/letters/N.svg", x: 1690.72, width: 402 },
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export default function AnimatedTipilanLogo() {
|
||||||
|
const [isReducedMotion, setIsReducedMotion] = useState(false);
|
||||||
|
const [loadedCount, setLoadedCount] = useState(0);
|
||||||
|
const [shouldAnimate, setShouldAnimate] = useState(false);
|
||||||
|
const loadedLetterIndicesRef = useRef<Set<number>>(new Set());
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
|
||||||
|
setIsReducedMotion(true);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (loadedCount >= logoLetters.length) {
|
||||||
|
const rafId = window.requestAnimationFrame(() => {
|
||||||
|
setShouldAnimate(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => window.cancelAnimationFrame(rafId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}, [loadedCount]);
|
||||||
|
|
||||||
|
const handleLetterLoad = (index: number) => {
|
||||||
|
if (loadedLetterIndicesRef.current.has(index)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadedLetterIndicesRef.current.add(index);
|
||||||
|
setLoadedCount(loadedLetterIndicesRef.current.size);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isReducedMotion) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="relative z-0 w-[max(260px,min(100%,750px))]"
|
||||||
|
style={{ aspectRatio: `${LOGO_WIDTH} / ${LOGO_HEIGHT}` }}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src="/tipilan-dark.svg"
|
||||||
|
alt="TipiLAN Logo"
|
||||||
|
priority
|
||||||
|
fill
|
||||||
|
sizes="(max-width: 750px) 100vw, 750px"
|
||||||
|
className="object-contain"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
aria-label="TipiLAN Logo"
|
||||||
|
className="relative z-0 w-[max(260px,min(100%,750px))] overflow-visible"
|
||||||
|
role="img"
|
||||||
|
style={{ aspectRatio: `${LOGO_WIDTH} / ${LOGO_HEIGHT}` }}
|
||||||
|
>
|
||||||
|
{logoLetters.map((letter, index) => {
|
||||||
|
return (
|
||||||
|
<Image
|
||||||
|
key={`${letter.letter}-${letter.x}`}
|
||||||
|
src={letter.src}
|
||||||
|
width={letter.width}
|
||||||
|
height={LOGO_HEIGHT}
|
||||||
|
alt=""
|
||||||
|
aria-hidden
|
||||||
|
priority
|
||||||
|
className={`absolute top-0 h-full object-fill ${shouldAnimate ? "tipilan-logo-letter" : ""}`}
|
||||||
|
onLoad={() => handleLetterLoad(index)}
|
||||||
|
onError={() => handleLetterLoad(index)}
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
left: `${(letter.x / LOGO_WIDTH) * 100}%`,
|
||||||
|
width: `${(letter.width / LOGO_WIDTH) * 100}%`,
|
||||||
|
zIndex: logoLetters.length - index,
|
||||||
|
"--tipilan-logo-letter-delay": `${index * 90}ms`,
|
||||||
|
...(shouldAnimate
|
||||||
|
? {}
|
||||||
|
: {
|
||||||
|
opacity: 0,
|
||||||
|
transform: "translate3d(0, 100%, 0)",
|
||||||
|
}),
|
||||||
|
} as CSSProperties
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import { SiDiscord, SiInstagram, SiFacebook } from "react-icons/si";
|
import { SiDiscord, SiInstagram, SiFacebook } from "react-icons/si";
|
||||||
import Image from "next/image";
|
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
// Fonts
|
// Fonts
|
||||||
@@ -9,122 +8,80 @@ const Footer = () => {
|
|||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col justify-center sm:justify-between px-6 py-8 md:px-12 md:py-16 gap-4 md:gap-8">
|
<div className="flex flex-col justify-center sm:justify-between px-6 py-6 md:px-12 md:py-8 gap-4 border-t-3 border-[#1F5673]">
|
||||||
<div className="flex md:items-center gap-8 md:gap-0 justify-between flex-col md:flex-row">
|
|
||||||
<div className="flex flex-col items-start md:items-center">
|
|
||||||
<Image
|
|
||||||
src="/tipilan-white.svg"
|
|
||||||
width={250}
|
|
||||||
height={36}
|
|
||||||
alt="TipiLAN Logo"
|
|
||||||
className="h-9 dark:hidden"
|
|
||||||
/>
|
|
||||||
<Image
|
|
||||||
src="/tipilan-dark.svg"
|
|
||||||
width={250}
|
|
||||||
height={36}
|
|
||||||
alt="TipiLAN Logo"
|
|
||||||
className="h-9 not-dark:hidden"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{/* Social media */}
|
|
||||||
<div className="flex flex-row">
|
|
||||||
<a
|
|
||||||
href="https://discord.gg/pPhhatZAfA"
|
|
||||||
target="_blank"
|
|
||||||
className="mx-4 ml-0 md:ml-4"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<SiDiscord
|
|
||||||
title="Discord"
|
|
||||||
size={"2em"}
|
|
||||||
className="text-[#2A2C3F] dark:text-[#EEE5E5] hover:text-[#007CAB] hover:dark:text-[#00A3E0] transition"
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
href="https://instagram.com/tipilan.ee"
|
|
||||||
target="_blank"
|
|
||||||
className="mx-4"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<SiInstagram
|
|
||||||
title="Instagram"
|
|
||||||
size={"2em"}
|
|
||||||
className="text-[#2A2C3F] dark:text-[#EEE5E5] hover:text-[#007CAB] hover:dark:text-[#00A3E0] transition"
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
href="https://facebook.com/tipilan.ee"
|
|
||||||
target="_blank"
|
|
||||||
className="mx-4"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<SiFacebook
|
|
||||||
title="Facebook"
|
|
||||||
size={"2em"}
|
|
||||||
className="text-[#2A2C3F] dark:text-[#EEE5E5] hover:text-[#007CAB] hover:dark:text-[#00A3E0] transition"
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<h2
|
<h2
|
||||||
className={`text-3xl sm:text-4xl ${vipnagorgialla.className} font-bold italic uppercase text-[#2A2C3F] dark:text-[#EEE5E5] mt-8 mb-4`}
|
className={`text-3xl sm:text-4xl ${vipnagorgialla.className} font-bold italic uppercase text-[#2A2C3F] dark:text-[#EEE5E5] mb-4 text-center sm:text-left`}
|
||||||
>
|
>
|
||||||
{t("footer.contact")}
|
{t("footer.contact")}
|
||||||
</h2>
|
</h2>
|
||||||
<div className="flex flex-row justify-between gap-4 items-center">
|
<div className="grid grid-cols-1 sm:grid-cols-3 gap-8 items-start w-full">
|
||||||
<div>
|
<div className="text-center sm:text-left sm:justify-self-start">
|
||||||
<h3 className="text-xl font-bold">{t("footer.studentUnion")}</h3>
|
<h3 className="text-xl font-bold">MTÜ Lapikud</h3>
|
||||||
<div className="flex flex-col gap-2 mt-2">
|
<div className="flex flex-col gap-2 mt-2">
|
||||||
<div className="flex flex-row gap-2">
|
|
||||||
<span className="material-symbols-outlined !font-bold text-[#007CAB] dark:text-[#00A3E0]">
|
|
||||||
mail
|
|
||||||
</span>
|
|
||||||
<a href="mailto:tipilan@ituk.ee" className="underline">
|
|
||||||
tipilan@ituk.ee
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-row gap-2">
|
|
||||||
<span className="material-symbols-outlined !font-bold text-[#007CAB] dark:text-[#00A3E0]">
|
|
||||||
phone
|
|
||||||
</span>
|
|
||||||
<a href="tel:+37256931193" className="underline">
|
|
||||||
+372 5693 1193
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<h3 className="text-xl font-bold pt-4">
|
|
||||||
{t("footer.organization")}
|
|
||||||
</h3>
|
|
||||||
<div>
|
|
||||||
<p>
|
<p>
|
||||||
{t("footer.registrationCode")}:{" "}
|
{t("footer.registrationCode")}:{" "}
|
||||||
<span className="font-semibold text-[#007CAB] dark:text-[#00A3E0]">
|
<span className="font-semibold text-[#007CAB] dark:text-[#00A3E0]">
|
||||||
80391807
|
80167145
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<p className="">ICO-210, Raja tn 4c, Tallinn, Harjumaa, 12616</p>
|
<p className="">Swedbank EE842200221094704780</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="flex flex-col gap-2 items-center text-center sm:justify-self-center">
|
||||||
<div className="block align-middle text-center pt-16">
|
<div className="flex flex-row gap-2">
|
||||||
{t("footer.madeBy")}{" "}
|
<span className="material-symbols-outlined font-bold! text-[#007CAB] dark:text-[#00A3E0]">
|
||||||
<a
|
mail
|
||||||
target="_blank"
|
</span>
|
||||||
href="https://lapikud.ee/"
|
<a href="mailto:tipilaninfogmail.com" className="underline">
|
||||||
className="text-[#E3983E] font-bold"
|
tipilaninfo@gmail.com
|
||||||
>
|
</a>
|
||||||
MTÜ Lapikud
|
</div>
|
||||||
</a>{" "}
|
<div className="flex flex-row gap-2">
|
||||||
{t("footer.withHelpFrom")}{" "}
|
<span className="material-symbols-outlined font-bold! text-[#007CAB] dark:text-[#00A3E0]">
|
||||||
<a
|
phone
|
||||||
target="_blank"
|
</span>
|
||||||
href="https://ituk.ee/"
|
<a href="tel:+37256931193" className="underline">
|
||||||
className="bg-[#7B1642] font-bold not-dark:text-white"
|
+372 5693 1193
|
||||||
>
|
</a>
|
||||||
MTÜ For Tsükkel/ITÜK
|
</div>
|
||||||
</a>
|
</div>
|
||||||
|
{/* Social media */}
|
||||||
|
<div className="flex flex-row gap-4 justify-center sm:justify-self-end sm:justify-end">
|
||||||
|
<a
|
||||||
|
href="https://discord.gg/pPhhatZAfA"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<SiDiscord
|
||||||
|
title="Discord"
|
||||||
|
size={"2em"}
|
||||||
|
className="text-[#2A2C3F] dark:text-[#EEE5E5] hover:text-[#007CAB] hover:dark:text-[#00A3E0] transition"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="https://instagram.com/tipilan.ee"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<SiInstagram
|
||||||
|
title="Instagram"
|
||||||
|
size={"2em"}
|
||||||
|
className="text-[#2A2C3F] dark:text-[#EEE5E5] hover:text-[#007CAB] hover:dark:text-[#00A3E0] transition"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="https://facebook.com/tipilan.ee"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<SiFacebook
|
||||||
|
title="Facebook"
|
||||||
|
size={"2em"}
|
||||||
|
className="text-[#2A2C3F] dark:text-[#EEE5E5] hover:text-[#007CAB] hover:dark:text-[#00A3E0] transition"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,16 +1,12 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
// Icons
|
import { useEffect, useState } from "react";
|
||||||
import {
|
import Image from "next/image";
|
||||||
MdClose,
|
import { Link, usePathname } from "@/i18n/routing";
|
||||||
MdMenu,
|
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
||||||
MdSunny,
|
|
||||||
MdModeNight,
|
|
||||||
MdComputer,
|
|
||||||
} from "react-icons/md";
|
|
||||||
|
|
||||||
// Theme Provider
|
// Icons
|
||||||
import { useTheme } from "next-themes";
|
import { MdClose, MdMenu } from "react-icons/md";
|
||||||
|
|
||||||
import LanguageSwitcher from "./LanguageSwitcher";
|
import LanguageSwitcher from "./LanguageSwitcher";
|
||||||
|
|
||||||
@@ -23,74 +19,195 @@ import {
|
|||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
|
||||||
// Fonts
|
interface NavItem {
|
||||||
// import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
href:
|
||||||
|
| "/"
|
||||||
interface HeaderProps {
|
| "/ajakava"
|
||||||
isOpen: boolean;
|
| "/haldus"
|
||||||
onToggle: () => void;
|
| "/kodukord"
|
||||||
themeLabels: {
|
| "/messiala"
|
||||||
light: string;
|
| "/piletid"
|
||||||
dark: string;
|
| "/striim"
|
||||||
system: string;
|
| "/turniirid";
|
||||||
};
|
label: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Header = ({ isOpen, onToggle, themeLabels }: HeaderProps) => {
|
interface HeaderProps {
|
||||||
const { theme, setTheme } = useTheme();
|
navItems: NavItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const Header = ({ navItems }: HeaderProps) => {
|
||||||
|
const pathname = usePathname();
|
||||||
|
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
||||||
|
|
||||||
|
// Header navigation should include all options except homepage
|
||||||
|
const mainNavItems = navItems.filter((item) => item.href !== "/");
|
||||||
|
const dropdownNavItems = mainNavItems;
|
||||||
|
|
||||||
|
const navIconByHref: Partial<Record<NavItem["href"], string>> = {
|
||||||
|
"/messiala": "weekend",
|
||||||
|
"/ajakava": "event_note",
|
||||||
|
"/piletid": "local_activity",
|
||||||
|
"/turniirid": "rewarded_ads",
|
||||||
|
"/kodukord": "gavel",
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const largeScreenQuery = window.matchMedia("(min-width: 1280px)");
|
||||||
|
|
||||||
|
const handleScaleOrViewportChange = (event: MediaQueryListEvent) => {
|
||||||
|
if (event.matches) {
|
||||||
|
setIsMobileMenuOpen(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (largeScreenQuery.matches) {
|
||||||
|
setIsMobileMenuOpen(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
largeScreenQuery.addEventListener("change", handleScaleOrViewportChange);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
largeScreenQuery.removeEventListener(
|
||||||
|
"change",
|
||||||
|
handleScaleOrViewportChange,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setIsMobileMenuOpen(false);
|
||||||
|
}, [pathname]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className="px-8 py-2 md:px-12 flex items-center bg-[#EEE5E5] dark:bg-[#0E0F19] border-b-3 border-[#1F5673] justify-between text-[#2A2C3F] dark:text-[#EEE5E5]">
|
<header className="px-4 sm:px-8 md:px-12 lg:px-16 py-3 flex items-center bg-[#0E0F19] border-b-3 border-[#1F5673] justify-between">
|
||||||
<button onClick={onToggle}>
|
{/* Logo */}
|
||||||
{isOpen ? (
|
<Link href="/" className="shrink-0">
|
||||||
<MdClose className="h-12 w-12 text-[#2A2C3F] dark:text-[#EEE5E5] cursor-pointer" />
|
<Image
|
||||||
) : (
|
src="/tipilan-icon-white.svg"
|
||||||
<MdMenu className="h-12 w-12 text-[#2A2C3F] dark:text-[#EEE5E5] cursor-pointer" />
|
alt="TipiLAN"
|
||||||
)}
|
width={49}
|
||||||
</button>
|
height={40}
|
||||||
<div className="flex items-center gap-2">
|
className="h-10 w-auto"
|
||||||
|
/>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
{/* Right side - Navigation + controls */}
|
||||||
|
<div className="flex items-center gap-12">
|
||||||
|
{/* Desktop Navigation */}
|
||||||
|
<nav className="hidden xl:flex items-center gap-12">
|
||||||
|
{mainNavItems.map((item) => {
|
||||||
|
const isActive = pathname === item.href;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
key={item.href}
|
||||||
|
href={item.href}
|
||||||
|
aria-current={isActive ? "page" : undefined}
|
||||||
|
aria-disabled={isActive ? true : undefined}
|
||||||
|
tabIndex={isActive ? -1 : undefined}
|
||||||
|
className={`${vipnagorgialla.className} group font-bold italic text-lg uppercase px-4 py-1.5 border-2 border-[#00A3E0] transition ${
|
||||||
|
isActive
|
||||||
|
? "bg-[#00A3E0] text-black cursor-default pointer-events-none"
|
||||||
|
: "bg-[#1F5673] text-[#EEE5E5] hover:bg-[#00A3E0] hover:text-black"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{item.label}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</nav>
|
||||||
<LanguageSwitcher />
|
<LanguageSwitcher />
|
||||||
<DropdownMenu>
|
|
||||||
|
{/* Mobile menu button */}
|
||||||
|
<DropdownMenu
|
||||||
|
open={isMobileMenuOpen}
|
||||||
|
onOpenChange={setIsMobileMenuOpen}
|
||||||
|
>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon"
|
size="icon"
|
||||||
className="size-10 cursor-pointer"
|
className="xl:hidden size-10 cursor-pointer"
|
||||||
>
|
>
|
||||||
<MdSunny className="scale-135 text-[#2A2C3F] dark:hidden" />
|
{isMobileMenuOpen ? (
|
||||||
<MdModeNight className="scale-135 dark:text-[#EEE5E5] not-dark:hidden" />
|
<MdClose className="size-10 text-[#EEE5E5]" />
|
||||||
<span className="sr-only">Toggle theme</span>
|
) : (
|
||||||
|
<MdMenu className="size-10 text-[#EEE5E5]" />
|
||||||
|
)}
|
||||||
|
<span className="sr-only">Menu</span>
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="end" className="w-48 translate-y-4">
|
<DropdownMenuContent
|
||||||
<DropdownMenuItem
|
align="end"
|
||||||
className={`text-lg ${theme === "light" ? "bg-accent/50 font-medium" : ""}`}
|
sideOffset={0}
|
||||||
onClick={() => setTheme("light")}
|
className="xl:hidden w-screen sm:w-72 translate-x-0 sm:translate-x-4 translate-y-3 rounded-none border-3 border-[#1F5673] bg-[#0E0F19] p-0"
|
||||||
disabled={theme === "light"}
|
>
|
||||||
>
|
{dropdownNavItems.map((item, index) => {
|
||||||
<MdSunny className={theme === "light" ? "text-amber-500" : ""} />
|
const isActive = pathname === item.href;
|
||||||
<span>{themeLabels.light}</span>
|
const hasBottomBorder = index !== dropdownNavItems.length - 1;
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem
|
return (
|
||||||
className={`text-lg ${theme === "dark" ? "bg-accent/50 font-medium" : ""}`}
|
<DropdownMenuItem
|
||||||
onClick={() => setTheme("dark")}
|
key={item.href}
|
||||||
disabled={theme === "dark"}
|
style={{ animationDelay: `${index * 55}ms` }}
|
||||||
>
|
className="p-0 opacity-0 animate-[dropdown-item-in_220ms_cubic-bezier(0.16,1,0.3,1)_forwards] focus:bg-transparent data-highlighted:bg-transparent"
|
||||||
<MdModeNight
|
>
|
||||||
className={theme === "dark" ? "text-blue-500" : ""}
|
{isActive ? (
|
||||||
/>
|
<span
|
||||||
<span>{themeLabels.dark}</span>
|
className={`${vipnagorgialla.className} block w-full cursor-default bg-[#00A3E0] px-5 py-2.5 text-xl font-bold italic uppercase text-black ${
|
||||||
</DropdownMenuItem>
|
hasBottomBorder ? "border-b-3 border-[#1F5673]" : ""
|
||||||
<DropdownMenuItem
|
}`}
|
||||||
className={`text-lg ${theme === "system" ? "bg-accent/50 font-medium" : ""}`}
|
>
|
||||||
onClick={() => setTheme("system")}
|
<span className="flex items-center justify-between gap-3">
|
||||||
disabled={theme === "system"}
|
<span>{item.label}</span>
|
||||||
>
|
{navIconByHref[item.href] ? (
|
||||||
<MdComputer
|
<span
|
||||||
className={theme === "system" ? "text-green-500" : ""}
|
className="material-symbols-outlined text-[2rem]! leading-none"
|
||||||
/>
|
style={
|
||||||
<span>{themeLabels.system}</span>
|
item.href === "/piletid"
|
||||||
</DropdownMenuItem>
|
? {
|
||||||
|
fontVariationSettings:
|
||||||
|
'"FILL" 0, "wght" 700, "GRAD" 0, "opsz" 24',
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{navIconByHref[item.href]}
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<Link
|
||||||
|
href={item.href}
|
||||||
|
className={`${vipnagorgialla.className} group block w-full bg-[#0E0F19] px-5 py-2.5 text-xl font-bold italic uppercase text-[#EEE5E5] transition hover:bg-[#00A3E0] hover:text-black ${
|
||||||
|
hasBottomBorder ? "border-b-3 border-[#1F5673]" : ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<span className="flex items-center justify-between gap-3">
|
||||||
|
<span>{item.label}</span>
|
||||||
|
{navIconByHref[item.href] ? (
|
||||||
|
<span
|
||||||
|
className="material-symbols-outlined text-[2rem]! leading-none text-[#00A3E0] group-hover:text-black"
|
||||||
|
style={
|
||||||
|
item.href === "/piletid"
|
||||||
|
? {
|
||||||
|
fontVariationSettings:
|
||||||
|
'"FILL" 0, "wght" 700, "GRAD" 0, "opsz" 24',
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{navIconByHref[item.href]}
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
|
</span>
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { Link } from "@/i18n/routing";
|
import { Link } from "@/i18n/routing";
|
||||||
|
import AnimatedTipilanLogo from "@/components/AnimatedTipilanLogo";
|
||||||
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
@@ -7,7 +8,7 @@ export default function HeroSection() {
|
|||||||
const t = useTranslations("home");
|
const t = useTranslations("home");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="relative h-[569px] overflow-hidden border-b-3 border-[#1F5673]">
|
<section className="relative h-190 md:h-auto md:min-h-142.25 lg:h-142.25 overflow-hidden border-b-3 border-[#1F5673]">
|
||||||
{/* Background image */}
|
{/* Background image */}
|
||||||
<Image
|
<Image
|
||||||
src="/images/landing/main_teaser.jpg"
|
src="/images/landing/main_teaser.jpg"
|
||||||
@@ -20,16 +21,10 @@ export default function HeroSection() {
|
|||||||
<div className="absolute inset-0 bg-[#0E0F19]/75" />
|
<div className="absolute inset-0 bg-[#0E0F19]/75" />
|
||||||
|
|
||||||
{/* Content */}
|
{/* Content */}
|
||||||
<div className="relative h-full grid grid-cols-1 md:grid-cols-[3fr_2fr] items-center gap-8 px-8 md:px-12">
|
<div className="relative h-full grid grid-cols-1 lg:grid-cols-[3fr_2fr] items-center gap-8 px-6 sm:px-8 md:px-12 pt-12 pb-8 md:py-10">
|
||||||
{/* Left: logo + info + CTA */}
|
{/* Left: logo + info + CTA */}
|
||||||
<div className="flex flex-col gap-5">
|
<div className="flex flex-col gap-5 items-center text-center md:items-start md:text-left">
|
||||||
<Image
|
<AnimatedTipilanLogo />
|
||||||
src="/tipilan-dark.svg"
|
|
||||||
width={750}
|
|
||||||
height={106}
|
|
||||||
alt="TipiLAN Logo"
|
|
||||||
className="w-[max(260px,min(100%,750px))] h-auto"
|
|
||||||
/>
|
|
||||||
<div className={`${vipnagorgialla.className} font-bold italic`}>
|
<div className={`${vipnagorgialla.className} font-bold italic`}>
|
||||||
<p className="text-[clamp(1.1rem,0.9rem+1vw,1.75rem)] text-[#00A3E0] uppercase tracking-wide">
|
<p className="text-[clamp(1.1rem,0.9rem+1vw,1.75rem)] text-[#00A3E0] uppercase tracking-wide">
|
||||||
{t("hero.date")}
|
{t("hero.date")}
|
||||||
@@ -40,32 +35,38 @@ export default function HeroSection() {
|
|||||||
</div>
|
</div>
|
||||||
<Link
|
<Link
|
||||||
href="/piletid"
|
href="/piletid"
|
||||||
className={`self-start px-6 py-3 bg-[#007CAB] hover:bg-[#00A3E0] text-[#EEE5E5] ${vipnagorgialla.className} font-bold italic text-[clamp(1rem,0.8rem+0.8vw,1.5rem)] uppercase transition`}
|
className={`self-center md:self-start px-6 py-3 bg-[#00A3E0] hover:bg-[#E5E5EE] text-[#0A121F] ${vipnagorgialla.className} font-bold italic text-[clamp(1rem,0.8rem+0.8vw,1.5rem)] uppercase transition`}
|
||||||
>
|
>
|
||||||
{t("hero.buyTicket")}
|
{t("hero.buyTicket")}
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right: prize pool + award */}
|
{/* Right: prize pool + award */}
|
||||||
<div className="flex flex-col items-start md:items-end gap-3">
|
<div className="flex flex-col items-center md:items-end gap-3 text-center md:text-right pb-6 md:pb-0">
|
||||||
<div className={`${vipnagorgialla.className} font-bold italic text-right`}>
|
<div
|
||||||
<p className="text-[64px] leading-none tracking-normal uppercase text-[#EEE5E5]">
|
className={`${vipnagorgialla.className} font-bold italic text-center md:text-right`}
|
||||||
|
>
|
||||||
|
<p className="text-[clamp(2rem,1.5rem+2.5vw,4rem)] leading-none tracking-normal uppercase text-[#EEE5E5]">
|
||||||
{t("hero.prizePool")}
|
{t("hero.prizePool")}
|
||||||
</p>
|
</p>
|
||||||
<h2 className="text-[clamp(3rem,2rem+4vw,6rem)] leading-none text-[#00A3E0]">
|
<h2 className="text-[clamp(3rem,2rem+4vw,6rem)] leading-none text-[#00A3E0]">
|
||||||
10 000€
|
10 000€
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col items-start md:items-end gap-2 mt-2">
|
<div className="flex flex-col sm:flex-row items-center justify-center md:justify-end gap-3 sm:gap-0 mt-2">
|
||||||
<Image
|
<Image
|
||||||
src="/images/landing/student_award.png"
|
src="/images/landing/student_award.webp"
|
||||||
width={180}
|
width={180}
|
||||||
height={180}
|
height={180}
|
||||||
alt="TalTech student award"
|
alt="TalTech student award"
|
||||||
className="object-contain"
|
className="object-contain"
|
||||||
/>
|
/>
|
||||||
<p className={`text-[32px] leading-none tracking-normal uppercase text-right text-[#EEE5E5] ${vipnagorgialla.className} font-bold italic`}>
|
<p
|
||||||
{t("hero.award")}
|
className={`text-[clamp(1.25rem,1rem+1.5vw,2rem)] leading-none tracking-normal uppercase text-center md:text-right align-middle text-[#EEE5E5] ${vipnagorgialla.className} font-bold italic px-2 sm:px-0`}
|
||||||
|
>
|
||||||
|
{t("hero.awardPrefix")}{" "}
|
||||||
|
<span className="text-[#00A3E0]">{t("hero.awardHighlight")}</span>{" "}
|
||||||
|
{t("hero.awardSuffix")}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
import { useLocale } from "next-intl";
|
import { useLocale } from "next-intl";
|
||||||
import { useRouter, usePathname } from "@/i18n/routing";
|
import { useRouter, usePathname } from "@/i18n/routing";
|
||||||
import { routing } from "@/i18n/routing";
|
import { routing } from "@/i18n/routing";
|
||||||
|
import Image from "next/image";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
|
||||||
|
|
||||||
export default function LanguageSwitcher() {
|
export default function LanguageSwitcher() {
|
||||||
const locale = useLocale();
|
const locale = useLocale();
|
||||||
@@ -17,15 +17,15 @@ export default function LanguageSwitcher() {
|
|||||||
return routing.locales[nextIndex] as "et" | "en";
|
return routing.locales[nextIndex] as "et" | "en";
|
||||||
};
|
};
|
||||||
|
|
||||||
const getNextLanguageName = () => {
|
const getNextLanguageFlag = () => {
|
||||||
const nextLocale = getNextLocale();
|
const nextLocale = getNextLocale();
|
||||||
switch (nextLocale) {
|
switch (nextLocale) {
|
||||||
case "et":
|
case "et":
|
||||||
return "EST";
|
return { src: "/flags/est.svg", alt: "Estonian flag" };
|
||||||
case "en":
|
case "en":
|
||||||
return "ENG";
|
return { src: "/flags/eng.svg", alt: "English flag" };
|
||||||
default:
|
default:
|
||||||
return nextLocale;
|
return { src: "/flags/est.svg", alt: nextLocale };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -38,10 +38,16 @@ export default function LanguageSwitcher() {
|
|||||||
<Button
|
<Button
|
||||||
onClick={handleLanguageSwitch}
|
onClick={handleLanguageSwitch}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="lg"
|
size="icon"
|
||||||
className={`${vipnagorgialla.className} text-3xl font-bold italic uppercase cursor-pointer hover:bg-[#007CAB]/10 dark:hover:bg-[#00A3E0]/10 text-[#007CAB] dark:text-[#00A3E0] hover:text-[#2A2C3F] dark:hover:text-[#EEE5E5] transition-colors`}
|
className="cursor-pointer hover:bg-[#007CAB]/10 dark:hover:bg-[#00A3E0]/10 transition-colors rounded-md"
|
||||||
>
|
>
|
||||||
{getNextLanguageName()}
|
<Image
|
||||||
|
src={getNextLanguageFlag().src}
|
||||||
|
alt={getNextLanguageFlag().alt}
|
||||||
|
width={40}
|
||||||
|
height={40}
|
||||||
|
className="rounded-sm"
|
||||||
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
91
src/components/RuleNav.tsx
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
interface RuleNavProps {
|
||||||
|
sections: { id: string; label: string }[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function RuleNav({ sections }: RuleNavProps) {
|
||||||
|
const [activeSection, setActiveSection] = useState<string>(
|
||||||
|
sections[0]?.id || "",
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (sections.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const getScrollOffset = () => {
|
||||||
|
const firstSection = document.getElementById(sections[0].id);
|
||||||
|
if (!firstSection) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const scrollMarginTop =
|
||||||
|
window.getComputedStyle(firstSection).scrollMarginTop;
|
||||||
|
const parsed = Number.parseFloat(scrollMarginTop);
|
||||||
|
return Number.isNaN(parsed) ? 0 : parsed;
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateActiveSection = () => {
|
||||||
|
const offset = getScrollOffset();
|
||||||
|
const scrollPosition = window.scrollY + offset + 1;
|
||||||
|
let currentId = sections[0].id;
|
||||||
|
|
||||||
|
for (const section of sections) {
|
||||||
|
const element = document.getElementById(section.id);
|
||||||
|
if (!element) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (element.offsetTop <= scrollPosition) {
|
||||||
|
currentId = section.id;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setActiveSection(currentId);
|
||||||
|
};
|
||||||
|
|
||||||
|
updateActiveSection();
|
||||||
|
window.addEventListener("scroll", updateActiveSection, { passive: true });
|
||||||
|
window.addEventListener("resize", updateActiveSection);
|
||||||
|
window.addEventListener("hashchange", updateActiveSection);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("scroll", updateActiveSection);
|
||||||
|
window.removeEventListener("resize", updateActiveSection);
|
||||||
|
window.removeEventListener("hashchange", updateActiveSection);
|
||||||
|
};
|
||||||
|
}, [sections]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<aside className="hidden lg:block">
|
||||||
|
<nav className="sticky top-24">
|
||||||
|
<ul className="flex flex-col">
|
||||||
|
{sections.map((section) => (
|
||||||
|
<li
|
||||||
|
key={section.id}
|
||||||
|
className={`border-l-2 pl-4 py-1 transition ${
|
||||||
|
activeSection === section.id
|
||||||
|
? "border-[#00A3E0]"
|
||||||
|
: "border-white hover:border-[#00A3E0]"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
href={`#${section.id}`}
|
||||||
|
className={`transition ${
|
||||||
|
activeSection === section.id
|
||||||
|
? "text-[#00A3E0] font-bold"
|
||||||
|
: "text-white hover:text-[#00A3E0]"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{section.label}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</aside>
|
||||||
|
);
|
||||||
|
}
|
||||||
98
src/components/RulesContent.tsx
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
||||||
|
|
||||||
|
interface RuleSection {
|
||||||
|
title: string;
|
||||||
|
rules: (
|
||||||
|
| string
|
||||||
|
| { main: string; sub: (string | { main: string; sub: string[] })[] }
|
||||||
|
)[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RulesContentProps {
|
||||||
|
sections: RuleSection[];
|
||||||
|
}
|
||||||
|
|
||||||
|
function RuleItem({
|
||||||
|
rule,
|
||||||
|
index,
|
||||||
|
}: {
|
||||||
|
rule:
|
||||||
|
| string
|
||||||
|
| { main: string; sub: (string | { main: string; sub: string[] })[] };
|
||||||
|
index: number;
|
||||||
|
}) {
|
||||||
|
if (typeof rule === "string") {
|
||||||
|
return (
|
||||||
|
<li className="text-white mb-2">
|
||||||
|
<span className="text-white mr-2">{index}.</span>
|
||||||
|
{rule}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li className="text-white mb-3">
|
||||||
|
<span className="text-white mr-2">{index}.</span>
|
||||||
|
{rule.main}
|
||||||
|
{rule.sub && rule.sub.length > 0 && (
|
||||||
|
<ol className="ml-6 mt-2">
|
||||||
|
{rule.sub.map((subRule, subIndex) => {
|
||||||
|
if (typeof subRule === "string") {
|
||||||
|
return (
|
||||||
|
<li key={subIndex} className="text-white mb-1">
|
||||||
|
<span className="text-white mr-2">
|
||||||
|
{index}.{subIndex + 1}.
|
||||||
|
</span>
|
||||||
|
{subRule}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<li key={subIndex} className="text-white mb-2">
|
||||||
|
<span className="text-white mr-2">
|
||||||
|
{index}.{subIndex + 1}.
|
||||||
|
</span>
|
||||||
|
{subRule.main}
|
||||||
|
{subRule.sub && subRule.sub.length > 0 && (
|
||||||
|
<ol className="ml-6 mt-1">
|
||||||
|
{subRule.sub.map((subSubRule, subSubIndex) => (
|
||||||
|
<li key={subSubIndex} className="text-white/80 mb-1">
|
||||||
|
<span className="text-white/80 mr-2">
|
||||||
|
{index}.{subIndex + 1}.{subSubIndex + 1}.
|
||||||
|
</span>
|
||||||
|
{subSubRule}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ol>
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ol>
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function RulesContent({ sections }: RulesContentProps) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{sections.map((section, sectionIndex) => (
|
||||||
|
<div key={sectionIndex} className="mb-8">
|
||||||
|
<h3
|
||||||
|
className={`${vipnagorgialla.className} font-bold italic text-xl text-white uppercase mb-4`}
|
||||||
|
>
|
||||||
|
{sectionIndex + 1}) {section.title}
|
||||||
|
</h3>
|
||||||
|
<ol className="list-none">
|
||||||
|
{section.rules.map((rule, ruleIndex) => (
|
||||||
|
<RuleItem key={ruleIndex} rule={rule} index={ruleIndex + 1} />
|
||||||
|
))}
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,9 +1,5 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState, useEffect } from "react";
|
|
||||||
import { usePathname } from "@/i18n/routing";
|
|
||||||
import { Link } from "@/i18n/routing";
|
|
||||||
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
|
||||||
import Header from "./Header";
|
import Header from "./Header";
|
||||||
|
|
||||||
interface NavItem {
|
interface NavItem {
|
||||||
@@ -14,71 +10,17 @@ interface NavItem {
|
|||||||
| "/kodukord"
|
| "/kodukord"
|
||||||
| "/messiala"
|
| "/messiala"
|
||||||
| "/piletid"
|
| "/piletid"
|
||||||
| "/reeglid"
|
|
||||||
| "/striim"
|
| "/striim"
|
||||||
| "/turniirid";
|
| "/turniirid";
|
||||||
label: string;
|
label: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SidebarLayoutClientProps {
|
interface SidebarLayoutClientProps {
|
||||||
themeLabels: {
|
|
||||||
light: string;
|
|
||||||
dark: string;
|
|
||||||
system: string;
|
|
||||||
};
|
|
||||||
navItems: NavItem[];
|
navItems: NavItem[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function SidebarLayoutClient({
|
export default function SidebarLayoutClient({
|
||||||
themeLabels,
|
|
||||||
navItems,
|
navItems,
|
||||||
}: SidebarLayoutClientProps) {
|
}: SidebarLayoutClientProps) {
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
return <Header navItems={navItems} />;
|
||||||
const pathname = usePathname();
|
|
||||||
|
|
||||||
const toggleSidebar = () => setIsOpen(!isOpen);
|
|
||||||
|
|
||||||
// Close sidebar when route changes
|
|
||||||
useEffect(() => {
|
|
||||||
if (isOpen) {
|
|
||||||
setIsOpen(false);
|
|
||||||
}
|
|
||||||
}, [pathname]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Header
|
|
||||||
isOpen={isOpen}
|
|
||||||
onToggle={toggleSidebar}
|
|
||||||
themeLabels={themeLabels}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Sidebar */}
|
|
||||||
<>
|
|
||||||
<div
|
|
||||||
className="fixed inset-0 backdrop-blur mt-16 z-20"
|
|
||||||
style={{ display: isOpen ? "block" : "none" }}
|
|
||||||
onClick={() => setIsOpen(false)}
|
|
||||||
></div>
|
|
||||||
<div
|
|
||||||
className={`text-3xl md:text-4xl ${vipnagorgialla.className} font-bold break-all italic uppercase fixed flex items-start xs:pl-25 pl-20 sm:pl-20 md:pl-24 flex-col gap-8 pt-16 top-0 left-0 h-[99vh] mt-16 -skew-x-5 border-r-3 border-[#1F5673] w-screen sm:w-96 md:w-128 bg-[#EEE5E5] dark:bg-[#0E0F19] text-[#2A2C3F] dark:text-[#EEE5E5] transition-transform transform z-20`}
|
|
||||||
style={{
|
|
||||||
transform: isOpen
|
|
||||||
? "translateX(-13%) skewX(calc(5deg * -1)"
|
|
||||||
: "translateX(-150%) skewX(calc(5deg * -1)",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{navItems.map((item) => (
|
|
||||||
<Link
|
|
||||||
key={item.href}
|
|
||||||
href={item.href}
|
|
||||||
className="hover:text-[#00A3E0] md:hover:translate-x-2 transition duration-150"
|
|
||||||
>
|
|
||||||
{item.label}
|
|
||||||
</Link>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,6 @@ import { getTranslations } from "next-intl/server";
|
|||||||
import SidebarLayoutClient from "./SidebarLayoutClient";
|
import SidebarLayoutClient from "./SidebarLayoutClient";
|
||||||
|
|
||||||
export default async function SidebarLayoutServer() {
|
export default async function SidebarLayoutServer() {
|
||||||
const t = await getTranslations("common");
|
|
||||||
|
|
||||||
const themeLabels = {
|
|
||||||
light: t("theme.light"),
|
|
||||||
dark: t("theme.dark"),
|
|
||||||
system: t("theme.system"),
|
|
||||||
};
|
|
||||||
|
|
||||||
const navT = await getTranslations("navigation");
|
const navT = await getTranslations("navigation");
|
||||||
|
|
||||||
const navItems = [
|
const navItems = [
|
||||||
@@ -19,8 +11,7 @@ export default async function SidebarLayoutServer() {
|
|||||||
{ href: "/ajakava" as const, label: navT("schedule") },
|
{ href: "/ajakava" as const, label: navT("schedule") },
|
||||||
{ href: "/turniirid" as const, label: navT("tournaments") },
|
{ href: "/turniirid" as const, label: navT("tournaments") },
|
||||||
{ href: "/kodukord" as const, label: navT("houserules") },
|
{ href: "/kodukord" as const, label: navT("houserules") },
|
||||||
{ href: "/reeglid" as const, label: navT("rules") },
|
|
||||||
];
|
];
|
||||||
|
|
||||||
return <SidebarLayoutClient themeLabels={themeLabels} navItems={navItems} />;
|
return <SidebarLayoutClient navItems={navItems} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,94 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import NextLink from "next/link";
|
import NextLink from "next/link";
|
||||||
|
|
||||||
|
interface Sponsor {
|
||||||
|
href: string;
|
||||||
|
src: string;
|
||||||
|
alt: string;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sponsors: Sponsor[] = [
|
||||||
|
{
|
||||||
|
href: "https://taltech.ee",
|
||||||
|
src: "/sponsors/taltech-color.png",
|
||||||
|
alt: "Taltech (Tallinna Tehnikaülikool)",
|
||||||
|
width: 192,
|
||||||
|
height: 192,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: "https://www.redbull.com/ee-et/",
|
||||||
|
src: "/sponsors/redbull.png",
|
||||||
|
alt: "Redbull",
|
||||||
|
width: 80,
|
||||||
|
height: 80,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: "https://www.simracing.ee/",
|
||||||
|
src: "/sponsors/EVAL.png",
|
||||||
|
alt: "EVAL",
|
||||||
|
width: 200,
|
||||||
|
height: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: "https://www.facebook.com/bfglOfficial",
|
||||||
|
src: "/sponsors/BFGL.png",
|
||||||
|
alt: "BFGL",
|
||||||
|
width: 192,
|
||||||
|
height: 192,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: "https://www.tomorrow.ee/",
|
||||||
|
src: "/sponsors/nt.png",
|
||||||
|
alt: "Network Tomorrow",
|
||||||
|
width: 300,
|
||||||
|
height: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: "https://k-space.ee/",
|
||||||
|
src: "/sponsors/k-space_ee-white.png",
|
||||||
|
alt: "K-Space",
|
||||||
|
width: 200,
|
||||||
|
height: 200,
|
||||||
|
className: "not-dark:invert",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: "https://globalproductions.ee/",
|
||||||
|
src: "/sponsors/Global-productions.png",
|
||||||
|
alt: "Global Productions",
|
||||||
|
width: 200,
|
||||||
|
height: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: "https://www.linkedin.com/company/gamedev-guild/",
|
||||||
|
src: "/sponsors/estonian_gamedev_guild.png",
|
||||||
|
alt: "Estonian Gamedev Guild",
|
||||||
|
width: 200,
|
||||||
|
height: 200,
|
||||||
|
className: "not-dark:invert",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: "https://alzgamer.ee/",
|
||||||
|
src: "/sponsors/alzgamer.png",
|
||||||
|
alt: "AlzGamer",
|
||||||
|
width: 200,
|
||||||
|
height: 200,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
interface SponsorsProps {
|
interface SponsorsProps {
|
||||||
showTitle?: boolean;
|
showTitle?: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tickerSponsors = [...sponsors, ...sponsors, ...sponsors, ...sponsors];
|
||||||
|
|
||||||
export default function Sponsors({
|
export default function Sponsors({
|
||||||
showTitle = true,
|
showTitle = true,
|
||||||
className = "",
|
className = "",
|
||||||
@@ -16,188 +97,52 @@ export default function Sponsors({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`p-12 flex flex-col ${vipnagorgialla.className} font-bold italic border-b-3 border-[#1F5673] ${className}`}
|
className={`flex flex-col w-full xl:h-[414px] mx-auto ${vipnagorgialla.className} font-bold italic border-[#1F5673] ${className}`}
|
||||||
>
|
>
|
||||||
<div className="text-left flex flex-col justify-between xl:justify-start">
|
{showTitle && (
|
||||||
{showTitle && (
|
<h3 className="text-4xl md:text-5xl dark:text-[#EEE5E5] text-[#2A2C3F] uppercase text-center px-12 pt-8 pb-4">
|
||||||
<h3 className="text-4xl md:text-5xl dark:text-[#EEE5E5] text-[#2A2C3F] group-hover:text-black pb-8">
|
{t("home.sections.poweredBy")}
|
||||||
{t("home.sections.poweredBy")}
|
</h3>
|
||||||
</h3>
|
)}
|
||||||
)}
|
|
||||||
<div className="flex flex-col sm:flex-row flex-wrap gap-8 md:gap-18 items-center justify-center xl:justify-start">
|
<div className="relative xl:flex-1 overflow-hidden py-8 sm:py-10 xl:py-4 2xl:py-6">
|
||||||
<NextLink href="https://taltech.ee" target="_blank">
|
<div className="ticker-track flex items-center w-max gap-8 sm:gap-10 md:gap-12 xl:gap-16 2xl:gap-20 px-8 sm:px-10 xl:px-14 2xl:px-20">
|
||||||
<Image
|
{tickerSponsors.map((sponsor, index) => (
|
||||||
src="/sponsors/taltech-color.png"
|
<NextLink
|
||||||
alt="Taltech (Tallinna Tehnikaülikool)"
|
key={`${sponsor.alt}-${index}`}
|
||||||
width={192}
|
href={sponsor.href}
|
||||||
height={192}
|
target="_blank"
|
||||||
className="object-contain"
|
className="flex items-center justify-center shrink-0"
|
||||||
/>
|
aria-hidden={index >= sponsors.length}
|
||||||
</NextLink>
|
tabIndex={index >= sponsors.length ? -1 : undefined}
|
||||||
<NextLink href="https://www.redbull.com/ee-et/" target="_blank">
|
>
|
||||||
<Image
|
<Image
|
||||||
src="/sponsors/redbull.png"
|
src={sponsor.src}
|
||||||
alt="Redbull"
|
alt={sponsor.alt}
|
||||||
width={80}
|
width={sponsor.width}
|
||||||
height={80}
|
height={sponsor.height}
|
||||||
className="object-contain"
|
className={`object-contain max-h-[80px] max-w-[120px] sm:max-h-[110px] sm:max-w-[150px] md:max-h-[130px] md:max-w-[180px] lg:max-h-[140px] lg:max-w-[200px] xl:max-h-[180px] xl:max-w-[240px] 2xl:max-h-[210px] 2xl:max-w-[280px] ${sponsor.className || ""}`}
|
||||||
/>
|
/>
|
||||||
</NextLink>
|
</NextLink>
|
||||||
<NextLink href="https://www.alecoq.ee" target="_blank">
|
))}
|
||||||
<Image
|
|
||||||
src="/sponsors/alecoq.svg"
|
|
||||||
alt="Alecoq"
|
|
||||||
width={200}
|
|
||||||
height={200}
|
|
||||||
className="object-contain"
|
|
||||||
/>
|
|
||||||
</NextLink>
|
|
||||||
<NextLink href="https://www.simracing.ee/" target="_blank">
|
|
||||||
<Image
|
|
||||||
src="/sponsors/EVAL.png"
|
|
||||||
alt="EVAL"
|
|
||||||
width={200}
|
|
||||||
height={200}
|
|
||||||
className="object-contain"
|
|
||||||
/>
|
|
||||||
</NextLink>
|
|
||||||
<NextLink href="https://balsnack.ee" target="_blank">
|
|
||||||
<Image
|
|
||||||
src="/sponsors/balsnack.svg"
|
|
||||||
alt="Balsnack"
|
|
||||||
width={200}
|
|
||||||
height={200}
|
|
||||||
className="object-contain"
|
|
||||||
/>
|
|
||||||
</NextLink>
|
|
||||||
<NextLink
|
|
||||||
href="https://www.rara.ee/sundmused/interaktiivne-videomangude-muuseum-lvlup/"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
src="/sponsors/lvlup_logo_export.svg"
|
|
||||||
alt="LVLup!"
|
|
||||||
width={192}
|
|
||||||
height={192}
|
|
||||||
className="object-contain"
|
|
||||||
/>
|
|
||||||
</NextLink>
|
|
||||||
<NextLink
|
|
||||||
href="https://www.facebook.com/bfglOfficial"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
src="/sponsors/BFGL.png"
|
|
||||||
alt="BFGL"
|
|
||||||
width={192}
|
|
||||||
height={192}
|
|
||||||
className="object-contain"
|
|
||||||
/>
|
|
||||||
</NextLink>
|
|
||||||
<NextLink href="https://www.tallinn.ee/et/haridus" target="_blank">
|
|
||||||
<Image
|
|
||||||
src="/sponsors/Tallinna_Haridusamet_logo_RGB.svg"
|
|
||||||
alt="Tallinna Haridusamet"
|
|
||||||
width={292}
|
|
||||||
height={292}
|
|
||||||
className="object-contain"
|
|
||||||
/>
|
|
||||||
</NextLink>
|
|
||||||
<NextLink href="https://www.militaarseiklus.ee/" target="_blank">
|
|
||||||
<Image
|
|
||||||
src="/sponsors/militaarseiklus.png"
|
|
||||||
alt="Militaarseiklus"
|
|
||||||
width={200}
|
|
||||||
height={200}
|
|
||||||
className="object-contain"
|
|
||||||
/>
|
|
||||||
</NextLink>
|
|
||||||
<NextLink
|
|
||||||
href="https://www.linkedin.com/company/gamedev-guild/"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
src="/sponsors/estonian_gamedev_guild.png"
|
|
||||||
alt="Estonian Gamedev Guild"
|
|
||||||
width={200}
|
|
||||||
height={200}
|
|
||||||
className="object-contain not-dark:invert"
|
|
||||||
/>
|
|
||||||
</NextLink>
|
|
||||||
<NextLink href="https://thotell.ee/" target="_blank">
|
|
||||||
<Image
|
|
||||||
src="/sponsors/thotell.png"
|
|
||||||
alt="Tahentorni Hotell (Tähentorni Hotel)"
|
|
||||||
width={200}
|
|
||||||
height={200}
|
|
||||||
className="object-contain"
|
|
||||||
/>
|
|
||||||
</NextLink>
|
|
||||||
<NextLink href="https://www.dominos.ee/" target="_blank">
|
|
||||||
<Image
|
|
||||||
src="/sponsors/dominos.png"
|
|
||||||
alt="Domino's Pizza"
|
|
||||||
width={250}
|
|
||||||
height={250}
|
|
||||||
className="object-contain"
|
|
||||||
/>
|
|
||||||
</NextLink>
|
|
||||||
<NextLink href="https://www.tomorrow.ee/" target="_blank">
|
|
||||||
<Image
|
|
||||||
src="/sponsors/nt.png"
|
|
||||||
alt="Network Tomorrow"
|
|
||||||
width={300}
|
|
||||||
height={200}
|
|
||||||
className="object-contain"
|
|
||||||
/>
|
|
||||||
</NextLink>
|
|
||||||
<NextLink href="https://driftikeskus.ee/" target="_blank">
|
|
||||||
<Image
|
|
||||||
src="/sponsors/driftikeskus.png"
|
|
||||||
alt="Driftikeskus"
|
|
||||||
width={300}
|
|
||||||
height={200}
|
|
||||||
className="object-contain"
|
|
||||||
/>
|
|
||||||
</NextLink>
|
|
||||||
<NextLink href="https://ingame.ee/" target="_blank">
|
|
||||||
<Image
|
|
||||||
src="/sponsors/ingame.png"
|
|
||||||
alt="Ingame"
|
|
||||||
width={200}
|
|
||||||
height={200}
|
|
||||||
className="object-contain"
|
|
||||||
/>
|
|
||||||
</NextLink>
|
|
||||||
<NextLink href="https://alzgamer.ee/" target="_blank">
|
|
||||||
<Image
|
|
||||||
src="/sponsors/alzgamer.png"
|
|
||||||
alt="AlzGamer"
|
|
||||||
width={200}
|
|
||||||
height={200}
|
|
||||||
className="object-contain"
|
|
||||||
/>
|
|
||||||
</NextLink>
|
|
||||||
<NextLink href="https://k-space.ee/" target="_blank">
|
|
||||||
<Image
|
|
||||||
src="/sponsors/k-space_ee-white.png"
|
|
||||||
alt="K-Space"
|
|
||||||
width={200}
|
|
||||||
height={200}
|
|
||||||
className="object-contain not-dark:invert"
|
|
||||||
/>
|
|
||||||
</NextLink>
|
|
||||||
<NextLink href="https://globalproductions.ee/" target="_blank">
|
|
||||||
<Image
|
|
||||||
src="/sponsors/Global-productions.png"
|
|
||||||
alt="Global Productions"
|
|
||||||
width={200}
|
|
||||||
height={200}
|
|
||||||
className="object-contain"
|
|
||||||
/>
|
|
||||||
</NextLink>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<style jsx>{`
|
||||||
|
.ticker-track {
|
||||||
|
animation: sponsors-ticker 36s linear infinite;
|
||||||
|
will-change: transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes sponsors-ticker {
|
||||||
|
from {
|
||||||
|
transform: translateX(0%);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: translateX(-25%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState, useEffect, useCallback } from "react";
|
import { useState, useEffect, useCallback, useRef } from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { Link } from "@/i18n/routing";
|
import { Link } from "@/i18n/routing";
|
||||||
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
||||||
@@ -16,66 +16,148 @@ type Slide = {
|
|||||||
fullBrightness?: boolean;
|
fullBrightness?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Helper to highlight "LAN" in "TIPILAN" with blue color
|
||||||
|
function highlightLAN(text: string) {
|
||||||
|
const parts = text.split(/(TIPILAN\w*)/gi);
|
||||||
|
return parts.map((part, i) => {
|
||||||
|
const upper = part.toUpperCase();
|
||||||
|
if (upper.startsWith("TIPILAN")) {
|
||||||
|
const suffix = part.slice(7); // Everything after "TIPILAN"
|
||||||
|
return (
|
||||||
|
<span key={i}>
|
||||||
|
TIPI<span className="text-[#00A3E0]">LAN</span>
|
||||||
|
{suffix.toUpperCase()}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return part;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const slides: Slide[] = [
|
const slides: Slide[] = [
|
||||||
{ key: "compete", image: "/images/landing/compete_teaser.jpg", imageAlt: "Võistle", hero: "/images/landing/compete_hero.png", href: "/turniirid" },
|
{
|
||||||
{ key: "play", image: "/images/landing/play_teaser.png", imageAlt: "Mängi", hero: "/images/landing/play_hero.png", href: "/piletid", flip: true, fullBrightness: true },
|
key: "compete",
|
||||||
{ key: "explore", image: "/images/landing/explore_teaser.png", imageAlt: "Avasta", hero: "/images/landing/explore_hero.png", href: "/messiala", fullBrightness: true },
|
image: "/images/landing/compete_teaser.jpg",
|
||||||
|
imageAlt: "Võistle",
|
||||||
|
hero: "/images/landing/compete_hero.png",
|
||||||
|
href: "/turniirid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "play",
|
||||||
|
image: "/images/landing/play_teaser.png",
|
||||||
|
imageAlt: "Mängi",
|
||||||
|
hero: "/images/landing/play_hero.png",
|
||||||
|
href: "/piletid",
|
||||||
|
flip: true,
|
||||||
|
fullBrightness: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "explore",
|
||||||
|
image: "/images/landing/explore_teaser.png",
|
||||||
|
imageAlt: "Avasta",
|
||||||
|
hero: "/images/landing/explore_hero.png",
|
||||||
|
href: "/messiala",
|
||||||
|
fullBrightness: true,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export default function TeaserCarousel() {
|
export default function TeaserCarousel() {
|
||||||
const t = useTranslations("home.teaser");
|
const t = useTranslations("home.teaser");
|
||||||
const [current, setCurrent] = useState(0);
|
const [current, setCurrent] = useState(0);
|
||||||
|
const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
||||||
|
|
||||||
const next = useCallback(() => setCurrent((c) => (c + 1) % slides.length), []);
|
const next = useCallback(
|
||||||
|
() => setCurrent((c) => (c + 1) % slides.length),
|
||||||
|
[],
|
||||||
|
);
|
||||||
const prev = () => setCurrent((c) => (c - 1 + slides.length) % slides.length);
|
const prev = () => setCurrent((c) => (c - 1 + slides.length) % slides.length);
|
||||||
|
|
||||||
useEffect(() => {
|
const restartAutoplay = useCallback(() => {
|
||||||
const id = setInterval(next, 5000);
|
if (intervalRef.current) {
|
||||||
return () => clearInterval(id);
|
clearInterval(intervalRef.current);
|
||||||
|
}
|
||||||
|
intervalRef.current = setInterval(next, 5000);
|
||||||
}, [next]);
|
}, [next]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
restartAutoplay();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (intervalRef.current) {
|
||||||
|
clearInterval(intervalRef.current);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [restartAutoplay]);
|
||||||
|
|
||||||
|
const headingOnRight = Boolean(slides[current]?.flip);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="border-b-3 border-[#1F5673]">
|
<div className="border-b-3 border-[#1F5673]">
|
||||||
{/* Sliding track */}
|
{/* Slides (fade transition + hero lift effect) */}
|
||||||
<div className="relative h-[729px] overflow-hidden">
|
<div className="relative h-135 sm:h-140 md:h-160 lg:h-175 xl:h-155 2xl:h-165 overflow-hidden">
|
||||||
<div
|
{slides.map((slide, i) => {
|
||||||
className="flex h-full transition-transform duration-500 ease-in-out"
|
const title = t(`${slide.key}.title`);
|
||||||
style={{ transform: `translateX(-${current * 100}%)` }}
|
const description = t(`${slide.key}.description`);
|
||||||
>
|
const isActive = i === current;
|
||||||
{slides.map((slide) => {
|
|
||||||
const title = t(`${slide.key}.title`);
|
|
||||||
const description = t(`${slide.key}.description`);
|
|
||||||
return (
|
|
||||||
<div key={slide.key} className="relative flex-none w-full h-full">
|
|
||||||
{/* Background image */}
|
|
||||||
<Image
|
|
||||||
src={slide.image}
|
|
||||||
alt={slide.imageAlt}
|
|
||||||
fill
|
|
||||||
className="object-cover object-center"
|
|
||||||
/>
|
|
||||||
{/* Overlay */}
|
|
||||||
<div className={`absolute inset-0 ${slide.fullBrightness ? "" : "bg-gradient-to-r from-[#0E0F19]/90 via-[#0E0F19]/60 to-[#0E0F19]/20"}`} />
|
|
||||||
|
|
||||||
{/* Content */}
|
return (
|
||||||
<div className={`relative grid grid-cols-1 md:grid-cols-2 h-full ${slide.flip ? "md:[direction:rtl]" : ""}`}>
|
<div
|
||||||
<div className={`flex flex-col justify-between gap-4 px-8 py-8 md:px-12 md:py-10 ${slide.flip ? "md:[direction:ltr]" : ""}`}>
|
key={slide.key}
|
||||||
<div className="flex flex-col gap-3">
|
className={`absolute inset-0 transition-opacity duration-700 ease-out ${
|
||||||
<h2 className={`${vipnagorgialla.className} font-bold italic text-[64px] leading-none tracking-normal uppercase text-[#EEE5E5]`}>
|
isActive ? "opacity-100" : "opacity-0 pointer-events-none"
|
||||||
{t("heading")}
|
}`}
|
||||||
</h2>
|
>
|
||||||
<Link href={slide.href}>
|
{/* Background image */}
|
||||||
<h3 className={`${vipnagorgialla.className} font-bold italic text-[clamp(2.5rem,2rem+2.5vw,5rem)] leading-none text-[#EEE5E5] hover:text-[#00A3E0] transition`}>
|
<Image
|
||||||
{title}
|
src={slide.image}
|
||||||
</h3>
|
alt={slide.imageAlt}
|
||||||
</Link>
|
fill
|
||||||
<p className="text-[clamp(0.875rem,0.75rem+0.5vw,1.1rem)] text-[#EEE5E5] max-w-prose">
|
className="object-cover object-center"
|
||||||
{description}
|
/>
|
||||||
</p>
|
|
||||||
</div>
|
{/* Overlay */}
|
||||||
|
<div
|
||||||
|
className={`absolute inset-0 ${slide.fullBrightness ? "" : "bg-linear-to-r from-[#0E0F19]/90 via-[#0E0F19]/60 to-[#0E0F19]/20"}`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Content */}
|
||||||
|
<div
|
||||||
|
className={`relative grid grid-cols-1 md:grid-cols-2 h-full ${slide.flip ? "md:[direction:rtl]" : ""}`}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={`flex flex-col justify-end px-8 py-8 md:px-12 md:py-10 ${slide.flip ? "md:[direction:ltr]" : ""}`}
|
||||||
|
>
|
||||||
|
{/* Title + description at bottom */}
|
||||||
|
<div
|
||||||
|
className={`flex flex-col gap-3 pb-16 ${slide.flip ? "md:items-end md:text-right md:ml-auto" : ""}`}
|
||||||
|
>
|
||||||
|
<Link href={slide.href}>
|
||||||
|
<h3
|
||||||
|
className={`${vipnagorgialla.className} font-bold italic text-[clamp(2.5rem,2rem+2.5vw,5rem)] leading-none text-[#EEE5E5] hover:text-[#00A3E0] transition`}
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</h3>
|
||||||
|
</Link>
|
||||||
|
<p
|
||||||
|
className={`text-[1.1rem] leading-relaxed md:text-[clamp(0.875rem,0.75rem+0.5vw,1.1rem)] text-[#EEE5E5] max-w-prose ${slide.flip ? "md:text-right" : ""}`}
|
||||||
|
>
|
||||||
|
{description}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{/* Hero image */}
|
</div>
|
||||||
<div className={`hidden md:block relative ${slide.flip ? "md:[direction:ltr]" : ""}`}>
|
|
||||||
|
{/* Hero image */}
|
||||||
|
<div
|
||||||
|
className={`hidden md:block relative overflow-hidden ${slide.flip ? "md:[direction:ltr]" : ""}`}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="absolute inset-0 transition-transform duration-700 ease-out"
|
||||||
|
style={{
|
||||||
|
transform: isActive
|
||||||
|
? "translateY(0)"
|
||||||
|
: "translateY(110%)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Image
|
<Image
|
||||||
src={slide.hero}
|
src={slide.hero}
|
||||||
alt={slide.imageAlt}
|
alt={slide.imageAlt}
|
||||||
@@ -85,39 +167,76 @@ export default function TeaserCarousel() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
</div>
|
||||||
})}
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
|
{/* Floating heading (mobile) */}
|
||||||
|
<div className="absolute top-5 inset-x-0 px-8 z-20 md:hidden pointer-events-none">
|
||||||
|
<h2
|
||||||
|
className={`${vipnagorgialla.className} font-bold italic text-[clamp(2rem,1.5rem+4.6vw,3rem)] leading-[0.95] tracking-normal uppercase text-[#EEE5E5] whitespace-normal wrap-break-word text-left max-w-[12ch]`}
|
||||||
|
>
|
||||||
|
{highlightLAN(t("heading"))}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Floating heading (desktop/tablet) */}
|
||||||
|
<div className="absolute top-8 inset-x-0 px-4 sm:px-6 md:px-12 z-20 hidden md:flex pointer-events-none">
|
||||||
|
<div
|
||||||
|
className="transition-[flex-grow] duration-700 ease-out"
|
||||||
|
style={{ flexGrow: headingOnRight ? 1 : 0 }}
|
||||||
|
/>
|
||||||
|
<h2
|
||||||
|
className={`${vipnagorgialla.className} font-bold italic text-[64px] leading-none tracking-normal uppercase text-[#EEE5E5] whitespace-normal wrap-anywhere text-center shrink`}
|
||||||
|
>
|
||||||
|
{highlightLAN(t("heading"))}
|
||||||
|
</h2>
|
||||||
|
<div
|
||||||
|
className="transition-[flex-grow] duration-700 ease-out"
|
||||||
|
style={{ flexGrow: headingOnRight ? 0 : 1 }}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Arrow buttons */}
|
{/* Arrow buttons */}
|
||||||
<button
|
<button
|
||||||
onClick={prev}
|
onClick={() => {
|
||||||
className="absolute left-4 top-1/2 -translate-y-1/2 w-10 h-10 flex items-center justify-center bg-[#0E0F19]/50 hover:bg-[#007CAB] text-[#EEE5E5] transition"
|
prev();
|
||||||
|
restartAutoplay();
|
||||||
|
}}
|
||||||
|
className="absolute left-4 top-1/2 -translate-y-1/2 w-10 h-10 flex items-center justify-center bg-[#0E0F19]/50 hover:bg-[#007CAB] text-[#EEE5E5] transition z-20"
|
||||||
aria-label="Previous slide"
|
aria-label="Previous slide"
|
||||||
>
|
>
|
||||||
<span className="material-symbols-outlined">chevron_left</span>
|
<span className="material-symbols-outlined">chevron_left</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={next}
|
onClick={() => {
|
||||||
className="absolute right-4 top-1/2 -translate-y-1/2 w-10 h-10 flex items-center justify-center bg-[#0E0F19]/50 hover:bg-[#007CAB] text-[#EEE5E5] transition"
|
next();
|
||||||
|
restartAutoplay();
|
||||||
|
}}
|
||||||
|
className="absolute right-4 top-1/2 -translate-y-1/2 w-10 h-10 flex items-center justify-center bg-[#0E0F19]/50 hover:bg-[#007CAB] text-[#EEE5E5] transition z-20"
|
||||||
aria-label="Next slide"
|
aria-label="Next slide"
|
||||||
>
|
>
|
||||||
<span className="material-symbols-outlined">chevron_right</span>
|
<span className="material-symbols-outlined">chevron_right</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Navigation dots */}
|
{/* Navigation dots */}
|
||||||
<div className="flex justify-center gap-3 py-5">
|
<div className="absolute bottom-5 left-1/2 -translate-x-1/2 flex justify-center gap-3 z-20 px-3 py-2 bg-[#0E0F19]/50 backdrop-blur-md">
|
||||||
{slides.map((_, i) => (
|
{slides.map((_, i) => (
|
||||||
<button
|
<button
|
||||||
key={i}
|
key={i}
|
||||||
onClick={() => setCurrent(i)}
|
onClick={() => {
|
||||||
className={`w-3 h-3 rounded-full transition ${
|
setCurrent(i);
|
||||||
i === current ? "bg-[#007CAB]" : "bg-[#1F5673] hover:bg-[#007CAB]/60"
|
restartAutoplay();
|
||||||
}`}
|
}}
|
||||||
aria-label={`Slide ${i + 1}`}
|
className={`w-3 h-3 rounded-full transition ${
|
||||||
/>
|
i === current
|
||||||
))}
|
? "bg-[#00A3E0]"
|
||||||
|
: "bg-[#1F5673] hover:bg-[#007CAB]/60"
|
||||||
|
}`}
|
||||||
|
aria-label={`Slide ${i + 1}`}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
"use client"
|
"use client";
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react";
|
||||||
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
|
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
||||||
import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"
|
import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react";
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
function DropdownMenu({
|
function DropdownMenu({
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
|
}: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
|
||||||
return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />
|
return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
function DropdownMenuPortal({
|
function DropdownMenuPortal({
|
||||||
@@ -17,7 +17,7 @@ function DropdownMenuPortal({
|
|||||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
|
}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
|
||||||
return (
|
return (
|
||||||
<DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
|
<DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function DropdownMenuTrigger({
|
function DropdownMenuTrigger({
|
||||||
@@ -28,7 +28,7 @@ function DropdownMenuTrigger({
|
|||||||
data-slot="dropdown-menu-trigger"
|
data-slot="dropdown-menu-trigger"
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function DropdownMenuContent({
|
function DropdownMenuContent({
|
||||||
@@ -42,13 +42,13 @@ function DropdownMenuContent({
|
|||||||
data-slot="dropdown-menu-content"
|
data-slot="dropdown-menu-content"
|
||||||
sideOffset={sideOffset}
|
sideOffset={sideOffset}
|
||||||
className={cn(
|
className={cn(
|
||||||
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
|
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-32 origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
</DropdownMenuPrimitive.Portal>
|
</DropdownMenuPrimitive.Portal>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function DropdownMenuGroup({
|
function DropdownMenuGroup({
|
||||||
@@ -56,7 +56,7 @@ function DropdownMenuGroup({
|
|||||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
|
}: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
|
||||||
return (
|
return (
|
||||||
<DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
|
<DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function DropdownMenuItem({
|
function DropdownMenuItem({
|
||||||
@@ -65,8 +65,8 @@ function DropdownMenuItem({
|
|||||||
variant = "default",
|
variant = "default",
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
|
}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
|
||||||
inset?: boolean
|
inset?: boolean;
|
||||||
variant?: "default" | "destructive"
|
variant?: "default" | "destructive";
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<DropdownMenuPrimitive.Item
|
<DropdownMenuPrimitive.Item
|
||||||
@@ -74,12 +74,12 @@ function DropdownMenuItem({
|
|||||||
data-inset={inset}
|
data-inset={inset}
|
||||||
data-variant={variant}
|
data-variant={variant}
|
||||||
className={cn(
|
className={cn(
|
||||||
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:text-destructive! [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 data-inset:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function DropdownMenuCheckboxItem({
|
function DropdownMenuCheckboxItem({
|
||||||
@@ -92,8 +92,8 @@ function DropdownMenuCheckboxItem({
|
|||||||
<DropdownMenuPrimitive.CheckboxItem
|
<DropdownMenuPrimitive.CheckboxItem
|
||||||
data-slot="dropdown-menu-checkbox-item"
|
data-slot="dropdown-menu-checkbox-item"
|
||||||
className={cn(
|
className={cn(
|
||||||
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
checked={checked}
|
checked={checked}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -105,7 +105,7 @@ function DropdownMenuCheckboxItem({
|
|||||||
</span>
|
</span>
|
||||||
{children}
|
{children}
|
||||||
</DropdownMenuPrimitive.CheckboxItem>
|
</DropdownMenuPrimitive.CheckboxItem>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function DropdownMenuRadioGroup({
|
function DropdownMenuRadioGroup({
|
||||||
@@ -116,7 +116,7 @@ function DropdownMenuRadioGroup({
|
|||||||
data-slot="dropdown-menu-radio-group"
|
data-slot="dropdown-menu-radio-group"
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function DropdownMenuRadioItem({
|
function DropdownMenuRadioItem({
|
||||||
@@ -128,8 +128,8 @@ function DropdownMenuRadioItem({
|
|||||||
<DropdownMenuPrimitive.RadioItem
|
<DropdownMenuPrimitive.RadioItem
|
||||||
data-slot="dropdown-menu-radio-item"
|
data-slot="dropdown-menu-radio-item"
|
||||||
className={cn(
|
className={cn(
|
||||||
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
@@ -140,7 +140,7 @@ function DropdownMenuRadioItem({
|
|||||||
</span>
|
</span>
|
||||||
{children}
|
{children}
|
||||||
</DropdownMenuPrimitive.RadioItem>
|
</DropdownMenuPrimitive.RadioItem>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function DropdownMenuLabel({
|
function DropdownMenuLabel({
|
||||||
@@ -148,19 +148,19 @@ function DropdownMenuLabel({
|
|||||||
inset,
|
inset,
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
|
}: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
|
||||||
inset?: boolean
|
inset?: boolean;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<DropdownMenuPrimitive.Label
|
<DropdownMenuPrimitive.Label
|
||||||
data-slot="dropdown-menu-label"
|
data-slot="dropdown-menu-label"
|
||||||
data-inset={inset}
|
data-inset={inset}
|
||||||
className={cn(
|
className={cn(
|
||||||
"px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
|
"px-2 py-1.5 text-sm font-medium data-inset:pl-8",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function DropdownMenuSeparator({
|
function DropdownMenuSeparator({
|
||||||
@@ -173,7 +173,7 @@ function DropdownMenuSeparator({
|
|||||||
className={cn("bg-border -mx-1 my-1 h-px", className)}
|
className={cn("bg-border -mx-1 my-1 h-px", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function DropdownMenuShortcut({
|
function DropdownMenuShortcut({
|
||||||
@@ -185,17 +185,17 @@ function DropdownMenuShortcut({
|
|||||||
data-slot="dropdown-menu-shortcut"
|
data-slot="dropdown-menu-shortcut"
|
||||||
className={cn(
|
className={cn(
|
||||||
"text-muted-foreground ml-auto text-xs tracking-widest",
|
"text-muted-foreground ml-auto text-xs tracking-widest",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function DropdownMenuSub({
|
function DropdownMenuSub({
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
|
}: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
|
||||||
return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />
|
return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
function DropdownMenuSubTrigger({
|
function DropdownMenuSubTrigger({
|
||||||
@@ -204,22 +204,22 @@ function DropdownMenuSubTrigger({
|
|||||||
children,
|
children,
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
|
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
|
||||||
inset?: boolean
|
inset?: boolean;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<DropdownMenuPrimitive.SubTrigger
|
<DropdownMenuPrimitive.SubTrigger
|
||||||
data-slot="dropdown-menu-sub-trigger"
|
data-slot="dropdown-menu-sub-trigger"
|
||||||
data-inset={inset}
|
data-inset={inset}
|
||||||
className={cn(
|
className={cn(
|
||||||
"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8",
|
"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-inset:pl-8",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
<ChevronRightIcon className="ml-auto size-4" />
|
<ChevronRightIcon className="ml-auto size-4" />
|
||||||
</DropdownMenuPrimitive.SubTrigger>
|
</DropdownMenuPrimitive.SubTrigger>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function DropdownMenuSubContent({
|
function DropdownMenuSubContent({
|
||||||
@@ -230,12 +230,12 @@ function DropdownMenuSubContent({
|
|||||||
<DropdownMenuPrimitive.SubContent
|
<DropdownMenuPrimitive.SubContent
|
||||||
data-slot="dropdown-menu-sub-content"
|
data-slot="dropdown-menu-sub-content"
|
||||||
className={cn(
|
className={cn(
|
||||||
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
|
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-32 origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
@@ -254,4 +254,4 @@ export {
|
|||||||
DropdownMenuSub,
|
DropdownMenuSub,
|
||||||
DropdownMenuSubTrigger,
|
DropdownMenuSubTrigger,
|
||||||
DropdownMenuSubContent,
|
DropdownMenuSubContent,
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -1,20 +1,42 @@
|
|||||||
import createMiddleware from 'next-intl/middleware';
|
import createMiddleware from "next-intl/middleware";
|
||||||
import {routing} from './i18n/routing';
|
import { NextRequest } from "next/server";
|
||||||
|
import { routing } from "./i18n/routing";
|
||||||
|
|
||||||
export default createMiddleware(routing);
|
const handleI18nRouting = createMiddleware(routing);
|
||||||
|
|
||||||
|
export default function middleware(request: NextRequest) {
|
||||||
|
const response = handleI18nRouting(request);
|
||||||
|
|
||||||
|
const location = response.headers.get("location");
|
||||||
|
|
||||||
|
if (location) {
|
||||||
|
const url = new URL(location);
|
||||||
|
|
||||||
|
// Keep localhost development redirects intact. If not then DO NOT!
|
||||||
|
const isLocalhost =
|
||||||
|
url.hostname === "localhost" || url.hostname === "127.0.0.1";
|
||||||
|
|
||||||
|
if (!isLocalhost && url.port === "3000") {
|
||||||
|
url.port = "";
|
||||||
|
response.headers.set("location", url.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
// Match only internationalized pathnames
|
// Match only internationalized pathnames
|
||||||
matcher: [
|
matcher: [
|
||||||
// Enable a redirect to a matching locale at the root
|
// Enable a redirect to a matching locale at the root
|
||||||
'/',
|
"/",
|
||||||
|
|
||||||
// Set a cookie to remember the previous locale for
|
// Set a cookie to remember the previous locale for
|
||||||
// all requests that have a locale prefix
|
// all requests that have a locale prefix
|
||||||
'/(et|en)/:path*',
|
"/(et|en)/:path*",
|
||||||
|
|
||||||
// Enable redirects that add missing locales
|
// Enable redirects that add missing locales
|
||||||
// (e.g. `/pathnames` -> `/en/pathnames`)
|
// (e.g. `/pathnames` -> `/en/pathnames`)
|
||||||
'/((?!_next|_vercel|.*\\..*).*)'
|
"/((?!_next|_vercel|.*\\..*).*)",
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -40,11 +40,13 @@
|
|||||||
"welcome": "Welcome to TipiLAN 2026!",
|
"welcome": "Welcome to TipiLAN 2026!",
|
||||||
"description": "Join us at Estonia's largest student-organized LAN event. Games, competitions and much more await you!",
|
"description": "Join us at Estonia's largest student-organized LAN event. Games, competitions and much more await you!",
|
||||||
"hero": {
|
"hero": {
|
||||||
"date": "11–13 OCTOBER 2026",
|
"date": "11–13 SEPTEMBER 2026",
|
||||||
"location": "TALTECH, EHITAJATE TEE 5",
|
"location": "TALTECH, EHITAJATE TEE 5",
|
||||||
"buyTicket": "BUY TICKETS",
|
"buyTicket": "BUY TICKETS",
|
||||||
"prizePool": "PRIZE POOL",
|
"prizePool": "PRIZE POOL",
|
||||||
"award": "TALTECH STUDENT ACT OF THE YEAR 2025"
|
"awardPrefix": "TALTECH",
|
||||||
|
"awardHighlight": "STUDENT ACT OF THE YEAR",
|
||||||
|
"awardSuffix": "2025"
|
||||||
},
|
},
|
||||||
"teaser": {
|
"teaser": {
|
||||||
"heading": "COME TO TIPILAN AND...",
|
"heading": "COME TO TIPILAN AND...",
|
||||||
@@ -75,53 +77,54 @@
|
|||||||
"description": "The TipiLAN expo area hosts companies, additional activities and lectures."
|
"description": "The TipiLAN expo area hosts companies, additional activities and lectures."
|
||||||
},
|
},
|
||||||
"reserveSpot": "Reserve your spot today!",
|
"reserveSpot": "Reserve your spot today!",
|
||||||
"poweredBy": "TipiLAN is powered by...",
|
"poweredBy": "Toetajad",
|
||||||
"dateAndLocation": "11th–13th Oct. @ TalTech"
|
"dateAndLocation": "11th–13th Sep. 2026 @ TalTech"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tickets": {
|
"tickets": {
|
||||||
"title": "TICKETS AND REGISTRATION",
|
"title": "TICKETS",
|
||||||
"buyNow": "Buy now",
|
"subtitle": "TICKET",
|
||||||
"soldOut": "Sold out",
|
"buyButton": "BUY TICKET FROM FIENTA",
|
||||||
"available": "Available",
|
|
||||||
"price": "Price",
|
|
||||||
"includes": "Includes",
|
|
||||||
"computerParticipant": {
|
|
||||||
"title": "Computer Participant",
|
|
||||||
"earlyPrice": "8€",
|
|
||||||
"latePrice": "10€",
|
|
||||||
"features": [
|
|
||||||
"Personal desk, power and internet connection",
|
|
||||||
"Access to expo area",
|
|
||||||
"Tournament spectating",
|
|
||||||
"Ability to participate in mini-tournaments"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"competitor": {
|
|
||||||
"title": "Competitor",
|
|
||||||
"price": "12-15€",
|
|
||||||
"features": [
|
|
||||||
"Ability to participate in the CS2 or LoL tournament",
|
|
||||||
"Personal desk, power and internet connection",
|
|
||||||
"Access to expo area",
|
|
||||||
"Tournament spectating",
|
|
||||||
"Ability to participate in mini-tournaments"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"visitor": {
|
"visitor": {
|
||||||
"title": "Visitor",
|
"name": "VISITOR",
|
||||||
"earlyPrice": "6€",
|
"price": "8€",
|
||||||
"latePrice": "8€",
|
|
||||||
"features": [
|
"features": [
|
||||||
"Access to expo area",
|
"Area access throughout the event",
|
||||||
"Tournament spectating",
|
"Ability to enter and leave the event area",
|
||||||
"Ability to participate in mini-tournaments"
|
"Access to all TipiLAN activities, side events, good vibes and engaging atmosphere."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"buyTicket": "BUY TICKETS"
|
"supporter": {
|
||||||
|
"name": "SUPPORTER",
|
||||||
|
"price": "25+€",
|
||||||
|
"features": [
|
||||||
|
"TipiLAN t-shirt to thank you for your support",
|
||||||
|
"Opportunity to contribute to TipiLAN's development and future.",
|
||||||
|
"Everything that comes with the visitor ticket"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"lol": {
|
||||||
|
"name": "LOL TOURNAMENT",
|
||||||
|
"price": "15€",
|
||||||
|
"features": [
|
||||||
|
"Right to participate in LoL tournament",
|
||||||
|
"Personal desk, power and internet connection",
|
||||||
|
"Access to all TipiLAN activities, side events, good vibes and engaging atmosphere."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"cs2": {
|
||||||
|
"name": "CS2 TOURNAMENT",
|
||||||
|
"price": "20€",
|
||||||
|
"features": [
|
||||||
|
"Right to participate in CS2 tournament",
|
||||||
|
"Personal desk, power and internet connection",
|
||||||
|
"Access to all TipiLAN activities, side events, good vibes and engaging atmosphere."
|
||||||
|
]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"tournaments": {
|
"tournaments": {
|
||||||
"title": "Tournaments",
|
"title": "Tournaments",
|
||||||
|
"clickButton": "CLICK",
|
||||||
"register": "Register",
|
"register": "Register",
|
||||||
"participants": "Participants",
|
"participants": "Participants",
|
||||||
"prizePool": "Prize pool",
|
"prizePool": "Prize pool",
|
||||||
@@ -154,8 +157,515 @@
|
|||||||
"buyTicket": "BUY TICKETS"
|
"buyTicket": "BUY TICKETS"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"cs2page": {
|
||||||
|
"title": "COUNTER-STRIKE 2 TOURNAMENT",
|
||||||
|
"buyTicket": "BUY TICKET",
|
||||||
|
"viewGithub": "FULL RULES",
|
||||||
|
"nav": {
|
||||||
|
"intro": "Introduction",
|
||||||
|
"info": "General info",
|
||||||
|
"prizes": "Prize pool",
|
||||||
|
"format": "Tournament format",
|
||||||
|
"vrs": "VRS info",
|
||||||
|
"faq": "FAQ",
|
||||||
|
"rules": "Rules"
|
||||||
|
},
|
||||||
|
"intro": {
|
||||||
|
"title": "INTRODUCTION",
|
||||||
|
"description": "CS competitions have been organized at TalTech for nearly 20 years, from CS1.6 to CS:GO. TipiLAN organized its first CS2 tournament in 2025 at its debut event. TipiLAN 2025 CS tournament became the first in Estonian history to award VRS points, achieving Tier 2 Ranked tournament status. Teams from Estonia, Latvia, Lithuania, Finland, Norway, and Ukraine participated, proving the tournament's international reach and strong competition.",
|
||||||
|
"previousWinners": "PREVIOUS WINNERS"
|
||||||
|
},
|
||||||
|
"info": {
|
||||||
|
"title": "GENERAL INFO",
|
||||||
|
"description": "TipiLAN 2026 CS2 tournament takes place September 11-13 at TalTech, Tallinn, Estonia. This is a BYOC LAN tournament. On-site, each participant is provided with: desk, chair, wired internet connection, power connection (2 outlets)."
|
||||||
|
},
|
||||||
|
"prizes": {
|
||||||
|
"title": "PRIZE POOL",
|
||||||
|
"mainTitle": "MAIN TOURNAMENT - 6000€",
|
||||||
|
"mainNote": "Teams and players outside the prize pool will not receive additional compensation.",
|
||||||
|
"secondTitle": "SECOND CHANCE TOURNAMENT - 750€",
|
||||||
|
"secondNote": "Teams and players outside the prize pool will not receive additional compensation."
|
||||||
|
},
|
||||||
|
"format": {
|
||||||
|
"title": "TOURNAMENT FORMAT",
|
||||||
|
"description": "The entire tournament is BYOC (bring your own computer) LAN, no qualifications take place.",
|
||||||
|
"day1": "Day 1: 5 rounds Swiss style tournament (Bo1)",
|
||||||
|
"day23": "Day 2 & 3: Double Elimination playoffs (Winners' Bracket Bo3, Losers' Bracket Bo1)"
|
||||||
|
},
|
||||||
|
"vrs": {
|
||||||
|
"title": "VRS INFO, INVITES",
|
||||||
|
"description1": "TipiLAN team will submit an application to HLTV for tournament results to be recorded in VRS standings. HLTV ranking status cannot be guaranteed before official confirmation. HLTV decides which matches count towards VRS points.",
|
||||||
|
"description2": "TipiLAN CS2 tournament follows Valve Ranked Tier 2 tournament requirements. TipiLAN does not send VRS invites to teams."
|
||||||
|
},
|
||||||
|
"faq": {
|
||||||
|
"title": "FREQUENTLY ASKED QUESTIONS",
|
||||||
|
"q1": "QUESTION?",
|
||||||
|
"a1": "Answer"
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"title": "RULES",
|
||||||
|
"description": "Official TipiLAN 2026 Counter-Strike 2 tournament rules.",
|
||||||
|
"contact": "For questions, contact:",
|
||||||
|
"contactName": "Harles Kadanik",
|
||||||
|
"contactRole": "Game Official - CS2 Tournament",
|
||||||
|
"contactDiscord": "Discord: hrkruger",
|
||||||
|
"sections": [
|
||||||
|
{
|
||||||
|
"title": "General Information",
|
||||||
|
"rules": [
|
||||||
|
"The Counter-Strike 2 (CS2) tournament will take place on September 11-13, 2026 at Tallinn University of Technology (TalTech), Ehitajate tee 5, Tallinn.",
|
||||||
|
{
|
||||||
|
"main": "The prize pool of the tournament is €6750, distributed as follows:",
|
||||||
|
"sub": [
|
||||||
|
{
|
||||||
|
"main": "Main Championship TOP 3 prize pool is €6000:",
|
||||||
|
"sub": [
|
||||||
|
"1st place - €600 per player (total €3000)",
|
||||||
|
"2nd place - €400 per player (total €2000)",
|
||||||
|
"3rd place - €200 per player (total €1000)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"main": "Second Chance Tournament prize pool is €750:",
|
||||||
|
"sub": [
|
||||||
|
"1st place - €100 per player (total €500)",
|
||||||
|
"2nd place - €50 per player (total €250)"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"main": "TipiLAN is classified as a Valve Tier 2 (No Invitations) event.",
|
||||||
|
"sub": [
|
||||||
|
"Top 3 results of the Main Championship will be submitted for VRS calculation. HLTV.org will determine which matches count toward VRS. Ranking status cannot be guaranteed prior to official confirmation."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"main": "Prize money will be paid to the player's bank account within 30 days.",
|
||||||
|
"sub": [
|
||||||
|
"In the case of a minor, the prize will be paid to the parent's/guardian's bank account."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Throughout the tournament, every participant must comply with the laws of the Republic of Estonia, the TipiLAN regulations, and the event regulations.",
|
||||||
|
"By purchasing a ticket, each participant gives consent to be photographed, filmed, and recorded for documentation and marketing purposes.",
|
||||||
|
"The CS2 main tournament will be recorded and broadcasted on streaming platforms Twitch and YouTube.",
|
||||||
|
"All communication related to the tournament between team members (in-game chat, voice chat, Discord/TeamSpeak conversations) will be recorded.",
|
||||||
|
"When streaming independently, the live stream delay must be at least 5 minutes.",
|
||||||
|
"The organizers have the right to use participants' personal information solely for the purpose of carrying out the event.",
|
||||||
|
"The organizing team has the right to change and edit the rules without prior notice, provided such changes align with Valve's tournament requirements.",
|
||||||
|
"The organizing team remains impartial and fair towards all participants."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "CS2 Main Tournament Team and Team Lineup",
|
||||||
|
"rules": [
|
||||||
|
"The CS2 main tournament team consists of five core members, one of whom is the team captain.",
|
||||||
|
"By August 28th 2026 (two weeks before the tournament), each team must confirm its participation, final core roster and team name.",
|
||||||
|
{
|
||||||
|
"main": "The team captain is the team's representative, who:",
|
||||||
|
"sub": [
|
||||||
|
"Acts as the contact person for the organizing team",
|
||||||
|
"Registers the team for the tournament",
|
||||||
|
"Is responsible for the team's behavior and actions",
|
||||||
|
"Represents the team in matters of warnings, disqualifications, disputes, and timeouts"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"main": "Each team may have one substitute player, who is not part of the core roster:",
|
||||||
|
"sub": [
|
||||||
|
"The substitute must purchase a separate substitute ticket",
|
||||||
|
"The substitute may replace any of the team's core members during the tournament",
|
||||||
|
"The substitute is subject to the same rights and requirements as the core roster"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"main": "Each team may have one coach:",
|
||||||
|
"sub": [
|
||||||
|
"The coach must purchase a separate coach ticket",
|
||||||
|
"The coach can only communicate with the team between matches and during tactical timeouts"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"main": "Changes to the core roster are allowed before the team registration deadline:",
|
||||||
|
"sub": [
|
||||||
|
"Player changes must be made through Fienta",
|
||||||
|
"If a member is replaced, the team still retains the right to have a substitute",
|
||||||
|
"A replacement player is subject to the same rights and requirements as the core roster"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"If the team captain is replaced, the team itself decides who will assume the captain's rights and responsibilities.",
|
||||||
|
"If a team withdraws before the registration deadline, the participation fee will be refunded.",
|
||||||
|
"All team members may only belong to one team during the tournament and represent only themselves.",
|
||||||
|
"All team members must be at least 16 years old by the day before the tournament starts.",
|
||||||
|
"No team member may be a citizen of the Russian Federation or the Republic of Belarus.",
|
||||||
|
{
|
||||||
|
"main": "Teams are not allowed to:",
|
||||||
|
"sub": [
|
||||||
|
"Display team sponsors during the tournament",
|
||||||
|
"Play in the interest of another team or team member"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"main": "Team name:",
|
||||||
|
"sub": [
|
||||||
|
"Must not be offensive, vulgar, political, or otherwise inappropriate",
|
||||||
|
"Must not contain emojis or other symbols that are not characters",
|
||||||
|
"Must be changed if requested by the organizing team"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Equipment",
|
||||||
|
"rules": [
|
||||||
|
"The organizing team will provide participating players with internet access, an internet cable, extension cords, and a seat with a table.",
|
||||||
|
"Coaches will not be provided with dedicated seating, desk space or internet connections.",
|
||||||
|
"Each participant is responsible for bringing and ensuring the functionality of all other equipment necessary for participation."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Schedule",
|
||||||
|
"rules": [
|
||||||
|
"All team members must arrive one hour before the scheduled start of the tournament.",
|
||||||
|
"Teams competing in a match round must be ready at their designated spots 10 minutes before the start of the round.",
|
||||||
|
"Match start times are determined by the organizing team at the start of the tournament or after the previous round concludes.",
|
||||||
|
"If a player experiences technical or game-related issues, they must immediately inform the game official or the organizing team.",
|
||||||
|
"Participants will have at least a 10 minute break between matches and 8 minutes between maps in a Bo3 match.",
|
||||||
|
"The organizing team has the right to make changes to the schedule.",
|
||||||
|
"The organizing team is obliged to keep all participants informed about delays or schedule changes."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Game Version and Settings",
|
||||||
|
"rules": [
|
||||||
|
"The most recent version of CS2 will be used throughout the tournament.",
|
||||||
|
{
|
||||||
|
"main": "The following settings will be used in the CS2 tournament:",
|
||||||
|
"sub": [
|
||||||
|
"Best of 24 rounds (mp_maxrounds 24)",
|
||||||
|
"Round time: 1 minute 55 seconds (mp_roundtime 1.92)",
|
||||||
|
"Starting money: $800 (mp_startmoney 800)",
|
||||||
|
"Freeze time at the start of each round: 20 seconds (mp_freezetime 20)",
|
||||||
|
"Buy time: 20 seconds (mp_buytime 20)",
|
||||||
|
"C4 timer: 40 seconds (mp_c4timer 40)",
|
||||||
|
"Overtime rounds: Best of 6 (mp_overtime_maxrounds 6)",
|
||||||
|
"Overtime starting money: $12,500 (mp_overtime_startmoney 12500)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Overtime: If the match ends in a tie after 24 rounds, overtime will be played as Best of 6. Overtimes continue until a winner is determined.",
|
||||||
|
"Timeout: Each team is allowed to call a 30 second timeout up to 3 times during regulation rounds. Use \"!pause\" in the in-game chat.",
|
||||||
|
"Technical timeout: Each team has the right to call a technical timeout if needed. Use \".tech\" in the in-game chat. Tactical communication is forbidden during all technical timeouts."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Map Selection & Tournament Structure",
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"main": "The 32-team tournament will take place in two stages:",
|
||||||
|
"sub": [
|
||||||
|
"Swiss stage: 5 rounds, initial phase Bo1, deciding matches Bo3. 3 wins = playoffs (Top 16). 3 losses = Second Chance Tournament.",
|
||||||
|
"Playoffs: Double elimination. Upper bracket Bo3, lower bracket Bo1, Lower Final is Bo3.",
|
||||||
|
"Second Chance Tournament: Double elimination. All matches except Final are Bo1, Final is Bo3."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"main": "Seeding will be conducted according to VRS priority:",
|
||||||
|
"sub": [
|
||||||
|
"Teams with Global VRS rank will be seeded higher than unranked teams",
|
||||||
|
"Teams without Global VRS rank will be assigned initial seeds randomly",
|
||||||
|
"After the initial round, standings will be determined by the Buchholz system"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"The map pool will consist of maps from the current Valve Active Duty Map Group.",
|
||||||
|
{
|
||||||
|
"main": "Best of 1 (Bo1) veto:",
|
||||||
|
"sub": [
|
||||||
|
"Team A removes 2 maps",
|
||||||
|
"Team B removes 3 maps",
|
||||||
|
"Team A removes 1 map",
|
||||||
|
"The remaining map will be played"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"main": "Best of 3 (Bo3) veto:",
|
||||||
|
"sub": [
|
||||||
|
"Team A removes 1 map",
|
||||||
|
"Team B removes 1 map",
|
||||||
|
"Team A picks 1 map",
|
||||||
|
"Team B picks 1 map",
|
||||||
|
"Team A removes 1 map",
|
||||||
|
"Team B removes 1 map",
|
||||||
|
"The remaining map will be played as the decider if necessary"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Forbidden Actions in the CS2 Tournament",
|
||||||
|
"rules": [
|
||||||
|
"Any kind of cheating, including methods not listed here, is strictly prohibited.",
|
||||||
|
"The use of scripts is forbidden (except for weapon/grenade purchase scripts and jump-throw scripts).",
|
||||||
|
"Moving through walls, floors, and ceilings, including \"sky-walking,\" is forbidden.",
|
||||||
|
"Pixel walking - standing, crouching, walking on invisible map edges - is forbidden.",
|
||||||
|
"Bombs must be planted in such a way that they can be defused.",
|
||||||
|
"Players are not allowed to plant an armed bomb in a location where it cannot be defused.",
|
||||||
|
"Players are not allowed to assign names (nametags) to items that violate the TipiLAN regulations.",
|
||||||
|
"Custom game files/data/drivers are not allowed.",
|
||||||
|
"The use of character models (agent skins) is not allowed.",
|
||||||
|
"Exploiting in-game bugs is forbidden.",
|
||||||
|
"Any kind of match-fixing, result manipulation, fraud, or collusion is strictly forbidden and will result in immediate team disqualification."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Penalties",
|
||||||
|
"rules": [
|
||||||
|
"Violations of in-game and out-of-game rules as well as breaches of the TipiLAN regulations are punishable.",
|
||||||
|
"Teams must maintain at least 4 of their 5 originally registered core members for all matches to remain eligible.",
|
||||||
|
"A team member who violates the rules will first receive a verbal warning. For a second violation, they will receive a second warning. On the third violation, the player will be disqualified.",
|
||||||
|
"A team member who does not show up for the tournament or match round without a valid reason will be disqualified.",
|
||||||
|
"A team member who is not present and ready 10 minutes before their match (no-show situation) will be disqualified.",
|
||||||
|
"If the organizing team determines that a player has violated section 7 rules, the entire team will be immediately disqualified. The violating player will receive a permanent ban from all future TipiLAN tournaments.",
|
||||||
|
"A team has the right to voluntarily withdraw from the tournament.",
|
||||||
|
"In case of a team disqualification, the opposing team will automatically win the current match round.",
|
||||||
|
"In case of disqualification, the participation fee will not be refunded.",
|
||||||
|
{
|
||||||
|
"main": "Only the team captain may dispute a disqualification:",
|
||||||
|
"sub": [
|
||||||
|
"A dispute must be submitted within 15 minutes",
|
||||||
|
"The organizing team has up to 25 minutes to make a decision"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"main": "Teams have the right to file a protest:",
|
||||||
|
"sub": [
|
||||||
|
"A protest must be submitted within 5 minutes of discovering the issue",
|
||||||
|
"The organizing team has up to 25 minutes to make a decision"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"The game official will inform the violating player, their team, and the opposing team about the violation and its consequences.",
|
||||||
|
"The organizing team has the right to pause and resume a match round at any time if necessary.",
|
||||||
|
"The organizing team is obliged to publicly announce all eliminations, disqualifications, and subsequent changes."
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lolpage": {
|
||||||
|
"title": "LEAGUE OF LEGENDS TOURNAMENT",
|
||||||
|
"buyTicket": "BUY TICKET",
|
||||||
|
"nav": {
|
||||||
|
"intro": "Introduction",
|
||||||
|
"info": "General info",
|
||||||
|
"prizes": "Prize pool",
|
||||||
|
"format": "Tournament format",
|
||||||
|
"faq": "FAQ",
|
||||||
|
"rules": "Rules"
|
||||||
|
},
|
||||||
|
"intro": {
|
||||||
|
"title": "INTRODUCTION",
|
||||||
|
"description": "TipiLAN organized its first LoL tournament in 2025 at its debut event. Teams from Estonia and Latvia participated.",
|
||||||
|
"previousWinners": "PREVIOUS WINNERS"
|
||||||
|
},
|
||||||
|
"info": {
|
||||||
|
"title": "GENERAL INFO",
|
||||||
|
"description": "TipiLAN 2026 LoL tournament takes place September 11-13 at TalTech, Tallinn, Estonia. This is a BYOC LAN tournament. On-site, each participant is provided with: desk, chair, wired internet connection, power connection (2 outlets)."
|
||||||
|
},
|
||||||
|
"prizes": {
|
||||||
|
"title": "PRIZE POOL",
|
||||||
|
"mainTitle": "MAIN TOURNAMENT - €3000",
|
||||||
|
"place1": "1st place - €1500, €300 per player, 50% or 1/2 of the prize pool.",
|
||||||
|
"place2": "2nd place - €1000, €200 per player, 33.3...% or 1/3 of the prize pool.",
|
||||||
|
"place3": "3rd place - €500, €100 per player, 16.6...% or 1/6 of the prize pool.",
|
||||||
|
"note": "Teams and players outside the prize pool will not receive additional compensation."
|
||||||
|
},
|
||||||
|
"format": {
|
||||||
|
"title": "TOURNAMENT FORMAT",
|
||||||
|
"description": "The entire tournament is BYOC (bring your own computer) LAN, no qualifications take place.",
|
||||||
|
"day1": "Day 1: Round Robin in two groups, 5 games.",
|
||||||
|
"day2": "Day 2: Single Elimination Playoff."
|
||||||
|
},
|
||||||
|
"faq": {
|
||||||
|
"title": "FREQUENTLY ASKED QUESTIONS",
|
||||||
|
"q1": "QUESTION?",
|
||||||
|
"a1": "Answer"
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"title": "RULES",
|
||||||
|
"description": "Official TipiLAN 2026 League of Legends tournament rules.",
|
||||||
|
"contact": "For questions, contact:",
|
||||||
|
"contactName": "Nils-Hendrik Nõlvak",
|
||||||
|
"contactRole": "Game Official - LoL Tournament",
|
||||||
|
"sections": [
|
||||||
|
{
|
||||||
|
"title": "General Information",
|
||||||
|
"rules": [
|
||||||
|
"The League of Legends (LoL) tournament takes place as a two-day event on September 11-13, 2026 at Tallinn University of Technology (TalTech), Ehitajate tee 5, Tallinn.",
|
||||||
|
{
|
||||||
|
"main": "The tournament prize pool is €3000, distributed as follows:",
|
||||||
|
"sub": [
|
||||||
|
"1st place team - €300 per player",
|
||||||
|
"2nd place team - €200 per player",
|
||||||
|
"3rd place team - €100 per player"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"main": "Prize money will be paid to the player's bank account.",
|
||||||
|
"sub": [
|
||||||
|
"In the case of a minor, the prize will be paid to the parent's/guardian's bank account."
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Teams and Participants",
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"main": "A team must have:",
|
||||||
|
"sub": [
|
||||||
|
"Five members (each member referred to as Participant)",
|
||||||
|
"One member is the team Captain, who is the spokesperson for the entire team",
|
||||||
|
"All members must be at least 16 years old at the time of registration",
|
||||||
|
"Participant may not be a citizen of the Russian Federation or Republic of Belarus",
|
||||||
|
"Teams are not allowed to use a coach during the tournament",
|
||||||
|
"One team member may be substituted, who must also be registered and physically present"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Participant must provide only truthful information and be ready to verify their identity to the Organizer.",
|
||||||
|
"Team name and logo, as well as Participant's in-game alias and avatar must be appropriate, without profanity, vulgarity, political or religious messages or symbols.",
|
||||||
|
"Participant represents only themselves throughout the Tournament (i.e., no one else may compete on their behalf).",
|
||||||
|
"All Participants under 18 must be ready to provide guardian consent for tournament participation.",
|
||||||
|
"Participant must be polite throughout the tournament and respect fellow participants, organizers and visitors. TipiLAN does not tolerate hate speech, harassment, threatening, insulting or aggressive behavior.",
|
||||||
|
"Team must be registered on both the TipiLAN website and challengermode tournament page."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Pre-game Procedures",
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"main": "Tournament participation, matches and bracket work through challengermode.com.",
|
||||||
|
"sub": [
|
||||||
|
"The entire team must be registered, including substitute",
|
||||||
|
"Players must have their highest rank account linked in challengermode",
|
||||||
|
"Tournament takes place on EU West server",
|
||||||
|
"Players may not use any account other than those linked in challengermode"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Matches in challengermode are automatic. For a new match, there is 10 minutes to confirm readiness.",
|
||||||
|
{
|
||||||
|
"main": "Draft can begin when both sides have confirmed readiness.",
|
||||||
|
"sub": [
|
||||||
|
"Placeholders are not allowed. If a champion is locked in draft, it must be played",
|
||||||
|
"Before draft, players must be in role order: Top-Jungle-Mid-Bot-Support",
|
||||||
|
"Deliberate stalling is not allowed"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Only official streamers and referees may join the match lobby besides players."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "In-game Procedures",
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"main": "A game has officially started (game of record) when all 10 players are on the map and the game has reached the first real interaction. GOR conditions:",
|
||||||
|
"sub": [
|
||||||
|
"Either team successfully uses an attack or ability",
|
||||||
|
"Opponents see each other",
|
||||||
|
"Entering enemy territory",
|
||||||
|
"Game has lasted 2 minutes"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"main": "Game pause:",
|
||||||
|
"sub": [
|
||||||
|
"During pause, players are not allowed to leave the match area",
|
||||||
|
"Organizers may pause the game as needed",
|
||||||
|
"Each team has the right to take up to 15 minutes of pause total during the match for valid reasons"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"The game may be resumed only with consent of both parties or referee permission.",
|
||||||
|
"If an obstacle occurs in fair gameplay (gamebreaking bug, connection issues, etc.), the referee will determine new instructions."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Match Conclusion",
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"main": "The match winner is the team with the most games won.",
|
||||||
|
"sub": [
|
||||||
|
"Organizer will display team standings in an accessible manner",
|
||||||
|
"After each match, the bracket is updated in challengermode"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Tournament Elimination",
|
||||||
|
"rules": [
|
||||||
|
"A team may decide to end tournament participation at any time by notifying the referee and/or organizer.",
|
||||||
|
"Penalties earned until elimination remain valid until the end of the tournament.",
|
||||||
|
"If a team does not show up or is not ready by the agreed start time, the Organizer may eliminate the team.",
|
||||||
|
{
|
||||||
|
"main": "The team roster cannot be changed during the tournament.",
|
||||||
|
"sub": [
|
||||||
|
"If team size falls below the required number due to member departure, the Organizer must eliminate the team"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"If a team wishes to withdraw during a match, the team must forfeit.",
|
||||||
|
"All eliminations and disqualifications must be announced publicly immediately."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Penalties",
|
||||||
|
"rules": [
|
||||||
|
"Referees assign penalties following the guidelines in this document.",
|
||||||
|
"Only referees may assign penalties.",
|
||||||
|
"The referee will inform both the offending player, their team, and the opposing team of the offense and penalty.",
|
||||||
|
"The referee must be impartial; team skill level must not influence monitoring of offenses and penalties.",
|
||||||
|
"Penalties may be assigned to the entire team or a single team member.",
|
||||||
|
{
|
||||||
|
"main": "Penalties may include:",
|
||||||
|
"sub": [
|
||||||
|
"WARNING: a notice for a minor offense",
|
||||||
|
"BAN LOSS: Team cannot ban a certain number of champions in the following game",
|
||||||
|
"GAME LOSS: Team receives automatic loss for one game",
|
||||||
|
"MATCH LOSS: Team receives automatic match loss",
|
||||||
|
"DISQUALIFICATION: Applies to the entire team"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"main": "Inappropriate behavior categories:",
|
||||||
|
"sub": [
|
||||||
|
"MINOR OFFENSE: unpleasant, unethical or disruptive behavior - penalty warning",
|
||||||
|
"MEDIUM OFFENSE: ignoring instructions, hate speech, aggression - penalty game loss",
|
||||||
|
"MAJOR OFFENSE: clearly against rules and good practices - penalty disqualification",
|
||||||
|
"COLLUSION: agreement between two teams - penalty disqualification of both teams",
|
||||||
|
"BRIBERY AND BETTING: prohibited - penalty disqualification",
|
||||||
|
"AGGRESSIVE BEHAVIOR: aggression against people - penalty disqualification",
|
||||||
|
"CHEATING: deliberate action to gain advantage - penalty disqualification"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Tournament Format",
|
||||||
|
"rules": [
|
||||||
|
"Tournament uses Fearless draft principles. This means champions picked during a series cannot be picked in subsequent games until the series ends.",
|
||||||
|
{
|
||||||
|
"main": "Tournament uses Round Robin + Single Elimination format:",
|
||||||
|
"sub": [
|
||||||
|
"First round has 2 six-team groups where all teams play each other once",
|
||||||
|
"This determines the top 4 who advance to the next day's single elimination bracket",
|
||||||
|
"In case of group ties, the team that won the head-to-head match advances"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"schedule": {
|
"schedule": {
|
||||||
"title": "Schedule",
|
"title": "Schedule",
|
||||||
|
"comingSoon": "Coming soon",
|
||||||
"day": "Day",
|
"day": "Day",
|
||||||
"time": "Time",
|
"time": "Time",
|
||||||
"event": "Event",
|
"event": "Event",
|
||||||
@@ -225,6 +735,7 @@
|
|||||||
},
|
},
|
||||||
"expo": {
|
"expo": {
|
||||||
"title": "Expo Area",
|
"title": "Expo Area",
|
||||||
|
"comingSoon": "Coming soon",
|
||||||
"description": "The TipiLAN expo area hosts companies, additional activities and lectures.",
|
"description": "The TipiLAN expo area hosts companies, additional activities and lectures.",
|
||||||
"areas": {
|
"areas": {
|
||||||
"bar": "Bar",
|
"bar": "Bar",
|
||||||
@@ -251,7 +762,9 @@
|
|||||||
"houseRules": "House Rules",
|
"houseRules": "House Rules",
|
||||||
"cs2Rules": "CS2 Rules",
|
"cs2Rules": "CS2 Rules",
|
||||||
"lolRules": "LoL Rules",
|
"lolRules": "LoL Rules",
|
||||||
"miniRules": "Mini-tournament\n Rules"
|
"miniRules": "Mini-tournament\n Rules",
|
||||||
|
"reminder": "Participant Reminder",
|
||||||
|
"code": "Event Code of Conduct"
|
||||||
},
|
},
|
||||||
"admin": {
|
"admin": {
|
||||||
"title": "Admin",
|
"title": "Admin",
|
||||||
|
|||||||
@@ -40,11 +40,13 @@
|
|||||||
"welcome": "Tere tulemast TipiLAN 2026 sündmusele!",
|
"welcome": "Tere tulemast TipiLAN 2026 sündmusele!",
|
||||||
"description": "Liitu meiega Eesti suurimal tudengite korraldatud LAN-üritusel. Mängud, võistlused ja palju muud ootavad sind!",
|
"description": "Liitu meiega Eesti suurimal tudengite korraldatud LAN-üritusel. Mängud, võistlused ja palju muud ootavad sind!",
|
||||||
"hero": {
|
"hero": {
|
||||||
"date": "11.–13. OKTOOBER 2026",
|
"date": "11.–13. SEPTEMBER 2026",
|
||||||
"location": "TALTECH, EHITAJATE TEE 5",
|
"location": "TALTECH, EHITAJATE TEE 5",
|
||||||
"buyTicket": "OSTA PILET",
|
"buyTicket": "OSTA PILET",
|
||||||
"prizePool": "AUHINNAFOND",
|
"prizePool": "AUHINNAFOND",
|
||||||
"award": "TALTECHI AASTA TUDENGITEGU 2025"
|
"awardPrefix": "TALTECHI",
|
||||||
|
"awardHighlight": "AASTA TUDENGITEGU",
|
||||||
|
"awardSuffix": "2025"
|
||||||
},
|
},
|
||||||
"teaser": {
|
"teaser": {
|
||||||
"heading": "TULE TIPILANILE JA...",
|
"heading": "TULE TIPILANILE JA...",
|
||||||
@@ -75,53 +77,54 @@
|
|||||||
"description": "TipiLANi messialal paiknevad ettevõtted, lisategevused ja toimuvad loengud."
|
"description": "TipiLANi messialal paiknevad ettevõtted, lisategevused ja toimuvad loengud."
|
||||||
},
|
},
|
||||||
"reserveSpot": "Broneeri oma koht juba täna!",
|
"reserveSpot": "Broneeri oma koht juba täna!",
|
||||||
"poweredBy": "TipiLANi tõmbab käima...",
|
"poweredBy": "Toetajad",
|
||||||
"dateAndLocation": "11.–13. okt. TalTechis"
|
"dateAndLocation": "11.–13. sept. 2026 TalTechis"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tickets": {
|
"tickets": {
|
||||||
"title": "PILETID JA REGISTREERIMINE",
|
"title": "PILETID",
|
||||||
"buyNow": "Osta nüüd",
|
"subtitle": "PILET",
|
||||||
"soldOut": "Välja müüdud",
|
"buyButton": "OSTA PILET FIENTAST",
|
||||||
"available": "Saadaval",
|
|
||||||
"price": "Hind",
|
|
||||||
"includes": "Sisaldab",
|
|
||||||
"computerParticipant": {
|
|
||||||
"title": "Arvutiga osaleja",
|
|
||||||
"earlyPrice": "8€",
|
|
||||||
"latePrice": "10€",
|
|
||||||
"features": [
|
|
||||||
"Isiklik laud, voolu- ja internetiühendus",
|
|
||||||
"Ligipääs messialale",
|
|
||||||
"Turniiride pealt vaatamine",
|
|
||||||
"Võimalus osaleda miniturniiridel"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"competitor": {
|
|
||||||
"title": "Võistleja",
|
|
||||||
"price": "12-15€",
|
|
||||||
"features": [
|
|
||||||
"Võimalus osaleda CS2 või LoL turniiril",
|
|
||||||
"Isiklik laud, voolu- ja internetiühendus",
|
|
||||||
"Ligipääs messialale",
|
|
||||||
"Turniiride pealt vaatamine",
|
|
||||||
"Võimalus osaleda miniturniiridel"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"visitor": {
|
"visitor": {
|
||||||
"title": "Külastaja",
|
"name": "KÜLASTAJA",
|
||||||
"earlyPrice": "6€",
|
"price": "8€",
|
||||||
"latePrice": "8€",
|
|
||||||
"features": [
|
"features": [
|
||||||
"Ligipääs messialale",
|
"Alade ligipääs kogu ürituse vältel",
|
||||||
"Turniiride pealt vaatamine",
|
"Võimalus ürituse alalt sisse ja välja liikuda",
|
||||||
"Võimalus osaleda miniturniiridel"
|
"Juurdepääs kogu TipiLAN melule, tegevustele, kõrvalsündmustele, headele vibe'dele ja kaasahaaravale atmosfäärile."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"buyTicket": "OSTA PILET"
|
"supporter": {
|
||||||
|
"name": "TOETAJA",
|
||||||
|
"price": "25+€",
|
||||||
|
"features": [
|
||||||
|
"TipiLANi t-särk, et tänada sind meie toetamise eest",
|
||||||
|
"Võimalust anda oma panus TipiLANi arengusse ja tulevikku.",
|
||||||
|
"Kõik, mis kaasneb külastaja piletiga"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"lol": {
|
||||||
|
"name": "LOL TURNIIRI",
|
||||||
|
"price": "15€",
|
||||||
|
"features": [
|
||||||
|
"Õigus osaleda LoL turniiril",
|
||||||
|
"Isiklik laud, voolu- ja internetiühendus",
|
||||||
|
"Juurdepääs kogu TipiLAN melule, tegevustele, kõrvalsündmustele, headele vibe'dele ja kaasahaaravale atmosfäärile."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"cs2": {
|
||||||
|
"name": "CS2 TURNIIRI",
|
||||||
|
"price": "20€",
|
||||||
|
"features": [
|
||||||
|
"Õigus osaleda CS2 turniiril",
|
||||||
|
"Isiklik laud, voolu- ja internetiühendus",
|
||||||
|
"Juurdepääs kogu TipiLAN melule, tegevustele, kõrvalsündmustele, headele vibe'dele ja kaasahaaravale atmosfäärile."
|
||||||
|
]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"tournaments": {
|
"tournaments": {
|
||||||
"title": "Turniirid",
|
"title": "Turniirid",
|
||||||
|
"clickButton": "VAJUTA",
|
||||||
"register": "Registreeru",
|
"register": "Registreeru",
|
||||||
"participants": "Osalejad",
|
"participants": "Osalejad",
|
||||||
"prizePool": "Auhinnafond",
|
"prizePool": "Auhinnafond",
|
||||||
@@ -155,8 +158,515 @@
|
|||||||
"buyTicket": "OSTA PILET"
|
"buyTicket": "OSTA PILET"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"cs2page": {
|
||||||
|
"title": "COUNTER-STRIKE 2 TURNIIR",
|
||||||
|
"buyTicket": "OSTA PILET",
|
||||||
|
"viewGithub": "TÄISREEGLID",
|
||||||
|
"nav": {
|
||||||
|
"intro": "Sissejuhatus",
|
||||||
|
"info": "Üldine info",
|
||||||
|
"prizes": "Auhinnafond",
|
||||||
|
"format": "Turniiri formaat",
|
||||||
|
"vrs": "VRS info",
|
||||||
|
"faq": "Korduma kippuvad küsimused",
|
||||||
|
"rules": "Reeglid"
|
||||||
|
},
|
||||||
|
"intro": {
|
||||||
|
"title": "SISSEJUHATUS",
|
||||||
|
"description": "TalTechis on korraldatud CS võistlusi nii CS1.6 kui ka CS:GOs juba ligi 20 aastat. TipiLAN korraldas esimese CS2 turniiri 2025. aastal oma debüütüritusel. TipiLAN 2025 CS turniir sai esimesena Eesti ajaloos jagada välja VRS punkte, saavutades Tier 2 Ranked turniiri staatuse. Kohal käis tiime Eestist, Lätist, Leedust, Soomest, Norrast ja Ukrainast, mis tõestas turniiri rahvusvahelist haaret ja tugevat konkurentsi.",
|
||||||
|
"previousWinners": "VARASEMAD VÕITJAD"
|
||||||
|
},
|
||||||
|
"info": {
|
||||||
|
"title": "ÜLDINE INFO",
|
||||||
|
"description": "TipiLAN 2026 CS2 turniir toimub 11.-13. septembril TalTechis, Tallinnas, Eestis. Tegemist on BYOC LAN turniiriga. Kohapeal pakutakse igale osalejale: laud, tool, võrgukaabliga internetiühendus, vooluühendus (2 pesa)."
|
||||||
|
},
|
||||||
|
"prizes": {
|
||||||
|
"title": "AUHINNAFOND",
|
||||||
|
"mainTitle": "PEATURNIIR - 6000€",
|
||||||
|
"mainNote": "Auhinnafondist väljaspoole jäävatele meeskondadele ja mängijatele lisahüvitisi ei pakuta.",
|
||||||
|
"secondTitle": "TEISE VÕIMALUSE TURNIIR - 750€",
|
||||||
|
"secondNote": "Auhinnafondist väljaspoole jäävatele meeskondadele ja mängijatele lisahüvitisi ei pakuta."
|
||||||
|
},
|
||||||
|
"format": {
|
||||||
|
"title": "TURNIIRI FORMAAT",
|
||||||
|
"description": "Terve turniir on BYOC (bring your own computer) LAN, kvalifikatsioone ei toimu.",
|
||||||
|
"day1": "Päev 1: 5 raundi Swiss stiilis turniiri (Bo1)",
|
||||||
|
"day23": "Päev 2 & 3: Double Elimination playoff'id (Winners' Bracket Bo3, Losers' Bracket Bo1)"
|
||||||
|
},
|
||||||
|
"vrs": {
|
||||||
|
"title": "VRS INFO, KUTSED",
|
||||||
|
"description1": "TipiLANi meeskond esitab HLTV-le avalduse, et turniiri tulemused läheksid VRS arvestusse kirja. HLTV rankingu staatust ei saa enne ametlikku kinnitust garanteerida. HLTV otsustab, millised mängud loevad VRS punktide arvestuses.",
|
||||||
|
"description2": "TipiLANi CS2 turniir jälgib Valve Ranked Tier 2 turniiri nõudeid. TipiLAN ei saada VRS kutseid meeskondadele."
|
||||||
|
},
|
||||||
|
"faq": {
|
||||||
|
"title": "KORDUMA KIPPUVAD KÜSIMUSED",
|
||||||
|
"q1": "KÜSIMUS?",
|
||||||
|
"a1": "Vastus"
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"title": "REEGLID",
|
||||||
|
"description": "TipiLAN 2026 Counter-Strike 2 turniiri ametlikud reeglid.",
|
||||||
|
"contact": "Küsimuste korral võta ühendust:",
|
||||||
|
"contactName": "Harles Kadanik",
|
||||||
|
"contactRole": "Mänguvana - CS2 turniir",
|
||||||
|
"contactDiscord": "Discord: hrkruger",
|
||||||
|
"sections": [
|
||||||
|
{
|
||||||
|
"title": "Üldist",
|
||||||
|
"rules": [
|
||||||
|
"Counter-Strike 2 (edaspidi CS2) turniir toimub 11.-13. september, 2026 Tallinna Tehnikaülikooli (TalTech) ruumides, Ehitajate tee 5, Tallinn.",
|
||||||
|
{
|
||||||
|
"main": "Turniiri auhinnafondiks on 6750€, mis jaguneb järgnevalt:",
|
||||||
|
"sub": [
|
||||||
|
{
|
||||||
|
"main": "Põhiturniiri TOP 3 auhinnafond on 6000€:",
|
||||||
|
"sub": [
|
||||||
|
"Esimene koht - 600€ võistleja kohta (kokku 3000€)",
|
||||||
|
"Teine koht - 400€ võistleja kohta (kokku 2000€)",
|
||||||
|
"Kolmas koht - 200€ võistleja kohta (kokku 1000€)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"main": "Second Chance turniiri auhinnafond on 750€:",
|
||||||
|
"sub": [
|
||||||
|
"Esimene koht - 100€ võistleja kohta (kokku 500€)",
|
||||||
|
"Teine koht - 50€ võistleja kohta (kokku 250€)"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"main": "TipiLAN on Valve Tier 2 (No Invitations) nõuetele vastav võistlus.",
|
||||||
|
"sub": [
|
||||||
|
"Põhiturniiri 1.-3. koha tulemused esitatakse VRS-i arvutamiseks. HLTV.org (pärast meiepoolset taotlust) määrab, millised mängud lähevad VRS-i arvestusse. VRS punktide jagamist ei saa enne ametlikku kinnitust garanteerida."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"main": "Võidusumma makstakse välja võistleja pangakontole 30 päeva jooksul.",
|
||||||
|
"sub": [
|
||||||
|
"Alaealise võistleja puhul makstakse võit vanema/eestkostja pangakontole."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Terve turniiri vältel tuleb igal osalejal lähtuda Eesti Vabariigi seadusest, TipiLAN kodukorrast ja ürituse reeglistikust.",
|
||||||
|
"Piletiostuga annab iga osaleja loa end pildistada, filmida ja kasutada kogu fotograafilist, audio- ja videomaterjali ürituse jäädvustamiseks ja turundamiseks.",
|
||||||
|
"CS2 põhiturniiri salvestatakse ning kantakse üle voogedastusplatvormidel Twitch ja YouTube.",
|
||||||
|
"Kõik turniiriga seotud suhtlused tiimiliikmete vahel (nt mängusisene chat, häälvestlused, Discordi ja TeamSpeaki vestlused jne) salvestatakse.",
|
||||||
|
"Ise mängu üle kandmise puhul peab otseülekande viivis (delay) olema vähemalt 5 minutit.",
|
||||||
|
"Korraldajatel on õigus kasutada osalejate isiklikku informatsiooni vaid ürituse läbiviimise raames.",
|
||||||
|
"Korraldustiimil on õigus reegleid vajadusel muuta ja redigeerida etteteatamata, eeldusel et muudatused on kooskõlas Valve'i turniiride nõuetega.",
|
||||||
|
"Korraldustiim on kõikide osalejate suhtes erapooletu."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "CS2 põhiturniiri tiim ja tiimi koosseis",
|
||||||
|
"rules": [
|
||||||
|
"CS2 põhiturniiri tiim (edaspidi tiim) põhikoosseisu kuulub viis põhiliiget, kellest üks on tiimikapten.",
|
||||||
|
"Hiljemalt 28. augustiks 2026 (kaks nädalat enne turniiri) peab iga tiim kinnitama oma osaluse, lõpliku tiimi põhikoosseisu ning tiimi nime.",
|
||||||
|
{
|
||||||
|
"main": "Tiimikapten on tiimi esindaja, kes:",
|
||||||
|
"sub": [
|
||||||
|
"On kontaktisikuks korraldustiimile",
|
||||||
|
"Registreerib tiimi turniirile",
|
||||||
|
"Vastutab tiimi käitumise ja tegude eest",
|
||||||
|
"Esindab tiimi hoiatuste, diskvalifikatsioonide, vaidlustuste ja timeout-ide korral"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"main": "Igal tiimil võib olla üks varumängija, kes ei kuulu tiimi põhikoosseisu:",
|
||||||
|
"sub": [
|
||||||
|
"Varumängija peab soetama endale eraldi varumängija pileti",
|
||||||
|
"Varumängija võib asendada ükskõik millist tiimi põhikoosseisu liiget turniiri toimumisel ajal",
|
||||||
|
"Varumängijale kehtivad samad õigused ja nõuded, mis tiimi põhikoosseisule"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"main": "Igal tiimil võib olla üks treener:",
|
||||||
|
"sub": [
|
||||||
|
"Treener peab ostma eraldi treeneri pileti",
|
||||||
|
"Treener võib oma meeskonnaga suhelda ainult mängude vahel ja taktikaliste pauside ajal"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"main": "Tiimi põhikoosseisus on lubatud välja vahetada mängijaid enne tiimide registreerimiskuupäeva lõppemist:",
|
||||||
|
"sub": [
|
||||||
|
"Mängijate väljavahetamine toimub läbi Fienta",
|
||||||
|
"Liikme välja vahetamisel jääb tiimil jätkuvalt õigus varumängijale",
|
||||||
|
"Asendusmängijale kehtivad samad õigused ja nõuded, mis tiimi põhikoosseisule"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Tiimikapteni väljavahetamisel otsustab tiim ise, kellele tiimikapteni õigused ja kohustused tiimisiseselt üle kanduvad.",
|
||||||
|
"Kui tiim astub turniiril osalemisest tagasi enne registreerimistähtaega, makstakse osalemistasu tiimile tagasi.",
|
||||||
|
"Kõik tiimiliikmed võivad turniiril kuuluda vaid ühte tiimi korraga ja esindada vaid iseennast.",
|
||||||
|
"Kõik tiimiliikmed peavad päev enne turniiri algust olema vähemalt 16-aastased.",
|
||||||
|
"Mitte ükski tiimiliige ei tohi olla Venemaa Föderatsiooni ega Valgevene Rahvavabariigi kodanik.",
|
||||||
|
{
|
||||||
|
"main": "Tiimidel pole lubatud:",
|
||||||
|
"sub": [
|
||||||
|
"Tiimisponsorite kajastamine turniiril",
|
||||||
|
"Mängida teise tiimi või tiimiliikme huvides"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"main": "Tiimi nimi:",
|
||||||
|
"sub": [
|
||||||
|
"Ei tohi olla solvav, vulgaarne, poliitiline või muud moodi maitsetu",
|
||||||
|
"Ei tohi sisaldada emotikone ega muid sümboleid, mis pole tähemärgid",
|
||||||
|
"Tuleb korraldustiimi poolsel nõudel ära muuta"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Varustus",
|
||||||
|
"rules": [
|
||||||
|
"Korraldustiimi poolt on turniiril osalevatele mängijatele tagatud internet, internetikaabel, pikendusjuhtmed ja istekoht lauaga.",
|
||||||
|
"Treeneritele ei tagata eraldi istekohta, lauapinda ega internetiühendust.",
|
||||||
|
"Turniiril osaleja vastutab selle eest, et temal on osalemiseks muu vajalik varustus kaasas ja töötab."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Ajakava",
|
||||||
|
"rules": [
|
||||||
|
"Kõik tiimiliikmed peavad kohal olema tund aega enne ettenähtud turniiri algust.",
|
||||||
|
"Mänguvoorus võistlevad tiimid peavad 10 minutit enne vooru algust olema valmis oma ettenähtud kohtadel.",
|
||||||
|
"Mänguvoorude algusajad on korraldustiimi välja pandud turniiri alguseks või eelmise mänguvooru lõpuks.",
|
||||||
|
"Kui mängijal esinevad tehnika või mänguga seotud tehnilised probleemid, peab ta sellest koheselt teavitama mänguvana või korraldustiimi.",
|
||||||
|
"Osalejatele tagatakse mängude vahel vähemalt 10-minutiline paus ja Bo3 mängude puhul kaartide vahel vähemalt 8-minutiline paus.",
|
||||||
|
"Korraldustiimil on õigus teha ajakavas muudatusi.",
|
||||||
|
"Korraldustiimil on kohustus hoida kõiki osalejaid kursis tekkinud viivituste ja muudatusega."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Mängu versioon ja seaded",
|
||||||
|
"rules": [
|
||||||
|
"Terve turniiri jooksul kasutatakse CS2 kõige uuemat versiooni.",
|
||||||
|
{
|
||||||
|
"main": "CS2 turniiril kasutatakse järgnevaid seadeid:",
|
||||||
|
"sub": [
|
||||||
|
"Parim 24st (mp_maxrounds 24)",
|
||||||
|
"Raundi aeg: 1 minut 55 sekundit (mp_roundtime 1.92)",
|
||||||
|
"Alustusraha: $800 (mp_startmoney 800)",
|
||||||
|
"Liikumise keelu aeg raundi alguses: 20 sekundit (mp_freezetime 20)",
|
||||||
|
"Aeg ostmiseks: 20 sekundit (mp_buytime 20)",
|
||||||
|
"Pommi taimer: 40 sekundit (mp_c4timer 40)",
|
||||||
|
"Lisaajal raunde: parim kuuest (mp_overtime_maxrounds 6)",
|
||||||
|
"Lisaaja alustusraha: $12,500 (mp_overtime_startmoney 12500)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Lisaaeg: juhul, kui pärast kõigi 24 raundi mängimist on viik, mängitakse lisaaega parim kuuest. Võistkonnad jätkavad lisaaegu, kuni võitja on leitud.",
|
||||||
|
"Paus: iga tiimil on lubatud kutsuda esile paus (timeout) 30 sekundit kuni 3 korda regulatsiooniraundide ajal. Pausi saavad kutsuda osalejad kirjutades mängusisesesse chatti \"!pause\".",
|
||||||
|
"Tehniline paus: igal tiimil on vajadusel õigus kasutada tehnilist pausi. Pausi alustamiseks tuleb mängusisesesse chatti sisestada käsklus \".tech\". Taktikaline suhtlus on kõigi tehniliste pauside ajal keelatud."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Kaardivalik & turniiri struktuur",
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"main": "32 meeskonnaga turniir toimub kahes etapis:",
|
||||||
|
"sub": [
|
||||||
|
"Swiss-süsteem: 5 roundi, algfaasis Bo1, otsustavad mängud Bo3. 3 võitu = playoffid (Top 16). 3 kaotust = Second Chance turniir.",
|
||||||
|
"Playoffid: double elimination. Upper bracket Bo3, lower bracket Bo1, Lower finaal Bo3.",
|
||||||
|
"Second Chance turniir: double elimination. Kõik mängud peale finaali Bo1, finaal Bo3."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"main": "Seeding viiakse läbi vastavalt VRS-i järjestusele:",
|
||||||
|
"sub": [
|
||||||
|
"VRS edetabelis kohaga meeskonnad seeditakse kõrgemale kui edetabelikohata meeskonnad",
|
||||||
|
"Ilma VRS edetabelikohata meeskondadele määratakse esmane paigutus juhuslikult",
|
||||||
|
"Pärast esmast roundi jaotatakse tabeliseis vastavalt Buchholz süsteemile"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Mängitav kaart valitakse välja hetkel aktiivsete Valve'i kaardigrupi kaartidest.",
|
||||||
|
{
|
||||||
|
"main": "Bo1 kaardivalik:",
|
||||||
|
"sub": [
|
||||||
|
"Võistkond A eemaldab 2 kaarti",
|
||||||
|
"Võistkond B eemaldab 3 kaarti",
|
||||||
|
"Võistkond A eemaldab 1 kaardi",
|
||||||
|
"Järelejäänud kaarti mängitakse"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"main": "Bo3 kaardivalik:",
|
||||||
|
"sub": [
|
||||||
|
"Võistkond A eemaldab 1 kaardi",
|
||||||
|
"Võistkond B eemaldab 1 kaardi",
|
||||||
|
"Võistkond A valib 1 kaardi",
|
||||||
|
"Võistkond B valib 1 kaardi",
|
||||||
|
"Võistkond A eemaldab 1 kaardi",
|
||||||
|
"Võistkond B eemaldab 1 kaardi",
|
||||||
|
"Järelejäänud kaart on vajadusel otsustav"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "CS2 turniiril keelatud tegevused",
|
||||||
|
"rules": [
|
||||||
|
"Igasugune sohitegemine, sealhulgas meetodid mis siin ei ole mainitud, on keelatud.",
|
||||||
|
"Skriptide kasutamine on keelatud (v.a. relvade/granaatide ostmine, hüppeviskamine).",
|
||||||
|
"Liikumine läbi seinte, põrandate ja katuste, k.a. taevas jalutamine (sky-walking) on keelatud.",
|
||||||
|
"Piksel-jalutamine (Pixel walking) ehk seismine, kükitamine, kõndimine nähtamatutel kaardipiiridel on keelatud.",
|
||||||
|
"Pomme tuleb asetada nii, et neid saaks desarmeerida.",
|
||||||
|
"Mängijatel ei ole lubatud armeeritud pommi asetada kohta, kus seda ei saa desarmeerida.",
|
||||||
|
"Mängijatel ei ole lubatud panna esemetele nimesid (nametags), mis rikuvad TipiLAN kodukorras väljatoodut.",
|
||||||
|
"Kohandatud mängufailid/andmed/draiverid ei ole lubatud.",
|
||||||
|
"Mängukarakteri mudelite (agent skins) kasutamine ei ole lubatud.",
|
||||||
|
"Mängusiseste vigade ära kasutamine on keelatud.",
|
||||||
|
"Igasugune tulemuste kokkuleppimine, mõjutamine, pettus ja manipuleerimine on rangelt keelatud ning tähendab kohest tiimi diskvalifitseerimist."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Karistused",
|
||||||
|
"rules": [
|
||||||
|
"Karistatav on mängusiseste ja mänguväliste reeglite ning kodukorra rikkumine.",
|
||||||
|
"Meeskonnad on kohustatud säilitama vähemalt 4 viiest algselt registreeritud mängijast kõigi mängude jaoks.",
|
||||||
|
"Reegleid rikkunud tiimiliikmele tehakse esmalt esimene verbaalne hoiatus. Teise rikkumise järel tehakse teine hoiatus. Kolmandal korral saab tiimiliige diskvalifikatsiooni.",
|
||||||
|
"Tiimiliige, kes ei ilmu turniiriks ega mänguvooruks kohale või lahkub turniiri ajal mõjuva põhjuseta, saab turniirilt diskvalifikatsiooni.",
|
||||||
|
"Tiimiliige, kes pole 10 minutit enne oma mänguvooru algust kohal (no-show olukord), saab turniirilt diskvalifikatsiooni.",
|
||||||
|
"Kui korraldustiim tuvastab punktis 7 väljatoodud rikkumise, diskvalifitseeritakse kogu tiim koheselt. Reegleid rikkunud mängijale antakse TipiLAN turniiridelt igavene mängukeeld.",
|
||||||
|
"Tiimil on õigus astuda turniiril osalemisest tagasi.",
|
||||||
|
"Tiimi diskvalifitseerimise korral võidab vastastiim automaatselt käesoleva mänguvooru.",
|
||||||
|
"Diskvalifitseerimise puhul ei tagastata osalustasu.",
|
||||||
|
{
|
||||||
|
"main": "Ainult tiimikapten saab diskvalifitseerimist vaidlustada:",
|
||||||
|
"sub": [
|
||||||
|
"Vaidlustus tuleb esitada 15 minuti jooksul",
|
||||||
|
"Korraldustiimil on aega kuni 25 minutit otsuse tegemiseks"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"main": "Tiimidel on õigus esitada protest:",
|
||||||
|
"sub": [
|
||||||
|
"Protest tuleb esitada 5 minuti jooksul probleemi avastamisest",
|
||||||
|
"Korraldustiimil on aega kuni 25 minutit otsuse tegemiseks"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Mänguvana teavitab eksimusest, selle sisust ja tagajärjest reegleid rikkunud tiimiliiget, tema tiimi ja vastastiimi.",
|
||||||
|
"Korraldustiimil on õigus panna mänguvoor pausile ja lõpetada paus ükskõik millisel hetkel.",
|
||||||
|
"Korraldustiimil on kohustus kõikidest väljalangemistest ja diskvalifikatsioonidest avalikult teada anda."
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lolpage": {
|
||||||
|
"title": "LEAGUE OF LEGENDS TURNIIR",
|
||||||
|
"buyTicket": "OSTA PILET",
|
||||||
|
"nav": {
|
||||||
|
"intro": "Sissejuhatus",
|
||||||
|
"info": "Üldine info",
|
||||||
|
"prizes": "Auhinnafond",
|
||||||
|
"format": "Turniiri formaat",
|
||||||
|
"faq": "Korduma kippuvad küsimused",
|
||||||
|
"rules": "Reeglid"
|
||||||
|
},
|
||||||
|
"intro": {
|
||||||
|
"title": "SISSEJUHATUS",
|
||||||
|
"description": "TipiLAN korraldas esimese LoL turniiri 2025. aastal oma debüütüritusel. Kohal käis tiime nii Eestist kui ka Lätist.",
|
||||||
|
"previousWinners": "VARASEMAD VÕITJAD"
|
||||||
|
},
|
||||||
|
"info": {
|
||||||
|
"title": "ÜLDINE INFO",
|
||||||
|
"description": "TipiLAN 2026 LoL turniir toimub 11.-13. septembril TalTechis, Tallinnas, Eestis. Tegemist on BYOC LAN turniiriga. Kohapeal pakutakse igale osalejale: laud, tool, võrgukaabliga internetiühendus, vooluühendus (2 pesa)."
|
||||||
|
},
|
||||||
|
"prizes": {
|
||||||
|
"title": "AUHINNAFOND",
|
||||||
|
"mainTitle": "PEATURNIIR - 3000€",
|
||||||
|
"place1": "1. koht - 1500€, 300€ inimese kohta, 50% ehk 1/2 auhinnafondist.",
|
||||||
|
"place2": "2. koht - 1000€, 200€ inimese kohta, 33.3...(3)% ehk 1/3 auhinnafondist.",
|
||||||
|
"place3": "3. koht - 500€, 100€ inimese kohta, 16.6...(6)% ehk 1/6 auhinnafondist.",
|
||||||
|
"note": "Auhinnafondist väljaspoole jäävatele meeskondadele ja mängijatele lisahüvitisi ei pakuta."
|
||||||
|
},
|
||||||
|
"format": {
|
||||||
|
"title": "TURNIIRI FORMAAT",
|
||||||
|
"description": "Terve turniir on BYOC (bring your own computer) LAN, kvalifikatsioone ei toimu.",
|
||||||
|
"day1": "Päev 1: Round Robin kahes grupis, 5 mängu.",
|
||||||
|
"day2": "Päev 2: Single Elimination Playoff."
|
||||||
|
},
|
||||||
|
"faq": {
|
||||||
|
"title": "KORDUMA KIPPUVAD KÜSIMUSED",
|
||||||
|
"q1": "KÜSIMUS?",
|
||||||
|
"a1": "Vastus"
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"title": "REEGLID",
|
||||||
|
"description": "TipiLAN 2026 League of Legends turniiri ametlikud reeglid.",
|
||||||
|
"contact": "Küsimuste korral võta ühendust:",
|
||||||
|
"contactName": "Nils-Hendrik Nõlvak",
|
||||||
|
"contactRole": "Mänguvana - LoL turniir",
|
||||||
|
"sections": [
|
||||||
|
{
|
||||||
|
"title": "Üldist",
|
||||||
|
"rules": [
|
||||||
|
"League of Legends (edaspidi LoL) turniir toimub kahepäevase üritusena 11.-13. september, 2026 Tallinna Tehnikaülikooli (TalTech) ruumides, Ehitajate tee 5, Tallinn.",
|
||||||
|
{
|
||||||
|
"main": "Turniiri auhinnafondiks on 3000€, mis jaguneb järgnevalt:",
|
||||||
|
"sub": [
|
||||||
|
"Esimese koha saanud võistkond - 300€ võistleja kohta",
|
||||||
|
"Teise koha saanud võistkond - 200€ võistleja kohta",
|
||||||
|
"Kolmanda koha saanud võistkond - 100€ võistleja kohta"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"main": "Võidusumma makstakse välja võistleja pangakontole.",
|
||||||
|
"sub": [
|
||||||
|
"Alaealise võistleja puhul makstakse võit vanema/eestkostja pangakontole."
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Võistkonnad ja võistlejad",
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"main": "Võistkonnas peab olema:",
|
||||||
|
"sub": [
|
||||||
|
"Viis liiget (iga liige edaspidi eraldi kui Võistleja)",
|
||||||
|
"Liikmetest üks on võistkonna Kapten, kes on ühtlasi kogu meeskonna eestkõnelejaks",
|
||||||
|
"Kõik liikmed peavad olema võistkonna registreerumise hetkel vähemalt 16 aastat vanad",
|
||||||
|
"Võistleja ei või olla Venemaa Föderatsiooni ega Valgevene Rahvavabariigi kodanik",
|
||||||
|
"Võistkonnal pole lubatud kasutada turniiri jooksul treenerit",
|
||||||
|
"Lubatud välja vahetada üks võistkonna liige, kes peab samuti olema registreeritud ja füüsiliselt kohal"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Võistleja peab esitama enda kohta ainult tõest informatsiooni ning valmis Korraldajale tõendama enda isikut.",
|
||||||
|
"Võistkonna nimi ja logo ning Võistleja arvutimängu alias ja avatar peab olema sünnis, sh ei tohi olla kohatu, sisaldada roppusi, vulgaarsusi, poliitilisi või religioosseid sõnumeid ega sümboleid.",
|
||||||
|
"Võistleja esindab terve Turniiri vältel ainult iseennast (st. enda asemel ei või lasta kellelgi teisel võistelda).",
|
||||||
|
"Kõik alla 18-aastased Võistlejad on valmis Korraldajale esitama eestkostja nõusoleku turniiril osalemiseks.",
|
||||||
|
"Võistleja peab olema kogu turniiri vältel viisakas ning austama kaasvõistlejaid, korraldajaid ning külastajaid. TipiLAN ei tolereeri vihakõne, ahistamist, ähvardavat, solvavat või agressiivset käitumist.",
|
||||||
|
"Võistkond peab olema registreeritud nii TipiLAN lehel kui ka challengermode turniirilehel."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Mängule eelnev",
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"main": "Turniiril osalemine, matchid ning turniiripuu toimib kõik läbi challengermode.com keskkonna.",
|
||||||
|
"sub": [
|
||||||
|
"Turniirile peab olema registreeritud kogu meeskond, kaasa arvatud varumängija",
|
||||||
|
"Mängijatel peab challengermodes olema linkitud kõige kõrgema rankiga kasutaja",
|
||||||
|
"Turniir toimub EU West serveris",
|
||||||
|
"Mängijad ei tohi kasutada ühtegi teist kasutajat peale challengermodes linkitud kasutajate"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Challengermode keskkonnas on matchid automaatsed. Uue matchi puhul on valmisolekuks aega 10 minutit.",
|
||||||
|
{
|
||||||
|
"main": "Draft saab alata kui mõlemad pooled on andnud enda valmisolekust märku.",
|
||||||
|
"sub": [
|
||||||
|
"Placeholderid ei ole lubatud. Kui champion on draftis lukustatud, peab seda ka mängima",
|
||||||
|
"Enne drafti peavad mängijad olema rollidele vastavas järjekorras: Top-Jungle-Mid-Bot-Support",
|
||||||
|
"Sihilikult viivitamine ei ole lubatud"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Matchi lobbysse tohivad lisaks mängijatele liituda ainult ametlikud streamerid ja kohtunikud."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Mängusisesed protseduurid",
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"main": "Mäng on ametlikult alanud (game of record) kui kõik 10 mängijat on kaardil ning mäng on jõudnud esimese reaalse interaktsioonini. GOR'i tingimused:",
|
||||||
|
"sub": [
|
||||||
|
"Kummalgi tiimil õnnestub rünnak või võime kasutamine",
|
||||||
|
"Vastased näevad teineteist",
|
||||||
|
"Sisenetakse vastase territooriumile",
|
||||||
|
"Mäng on kestnud 2 minutit"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"main": "Mängu seiskamine:",
|
||||||
|
"sub": [
|
||||||
|
"Mängu pausile panemise ajal ei ole mängijatel lubatud lahkuda matši alalt",
|
||||||
|
"Korraldajad võivad mängu pausile panna vastavalt vajadusele",
|
||||||
|
"Kummalgi tiimil on õigus matši jooksul võtta kokku kuni 15 minutit pausi mõjuval põhjusel"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Mängu võib uuesti käima panna ainult mõlema poole nõusolekul või kohtuniku loal.",
|
||||||
|
"Kui tekib mängu ausal läbiviimisel takistus (gamebreaking bug, netiühendus, etc.), määrab kohtunik uued juhised mängu läbiviimiseks."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Match'i lõpetamine",
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"main": "Match'i võitja on tiim, kellel on kõige rohkem mänge võidetud.",
|
||||||
|
"sub": [
|
||||||
|
"Korraldaja esitab tiimide võitude ja kaotuste seisud kõigile osalejatele kättesaadaval viisil",
|
||||||
|
"Peale igat match'i uuendatakse challengermode keskkonnas turniiripuud"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Turniirilt välja langemine",
|
||||||
|
"rules": [
|
||||||
|
"Tiim võib igal ajal otsustada lõpetada turniiril osalemise andes sellest teada kohtunikule ja/või korraldajale.",
|
||||||
|
"Kuni välja kukkumiseni teenitud karistused jäävad kehtima turniiri lõpuni.",
|
||||||
|
"Kui tiim ei ilmu kohale või pole kokku lepitud algusajaks valmis, võib Korraldaja arvata tiimi turniirilt välja.",
|
||||||
|
{
|
||||||
|
"main": "Tiimi registreerunute nimekirja ei saa muuta turniiri vältel.",
|
||||||
|
"sub": [
|
||||||
|
"Kui liikme lahkumise tõttu langeb tiimis osalejate arv alla mängimiseks vajaliku, peab Korraldaja tiimi turniirilt välja arvama"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Kui tiim soovib välja langeda match'i toimumise ajal, peab tiim andma loobumisvõidu.",
|
||||||
|
"Kõik välja langemised ja välja arvamised tuleb teha koheselt avalikult teatavaks."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Karistused",
|
||||||
|
"rules": [
|
||||||
|
"Kohtunikud määravad karistusi järgides selles dokumendis toodud juhiseid.",
|
||||||
|
"Karistusi võivad määrata ainult kohtunikud.",
|
||||||
|
"Kohtunik teavitab nii eksimuse sisu kui ka määratud karistuse nii reeglite vastu eksinud mängijale, tema tiimile kui ka vastasvõistkonnale.",
|
||||||
|
"Kohtunik peab olema erapooletu, tiimi oskuste tase ei tohi olla määravaks eksimuste ja karistuste jälgimisel.",
|
||||||
|
"Karistusi võib määrata nii kogu tiimile kui ka ühele tiimiliikmele.",
|
||||||
|
{
|
||||||
|
"main": "Karistused võivad olla järgnevad:",
|
||||||
|
"sub": [
|
||||||
|
"HOIATUS: märguanne mängijale või tiimile väikese eksimuse eest",
|
||||||
|
"BAN'i KAOTUS: Tiim ei või karistusele järgneval mängul ban'ida kindel arv tegelasi",
|
||||||
|
"MÄNGU KAOTUS: Tiim saab automaatse kaotuse ühel mängul",
|
||||||
|
"MATCH'I KAOTUS: Tiim saab automaatse match'i kaotuse",
|
||||||
|
"DISKVALIFITSEERIMINE: Diskvalifikatsioon kehtib tervele tiimile"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"main": "Mittesobilik käitumine jagunevad:",
|
||||||
|
"sub": [
|
||||||
|
"KERGE EKSIMUS: ebameeldiv, ebaeetiline või häiriv käitumine - karistus hoiatus",
|
||||||
|
"KESKMINE EKSIMUS: juhiste eiramine, vihakõne, agressiivsus - karistus mängu kaotus",
|
||||||
|
"RASKE EKSIMUS: selgelt vastuolus reeglite ning heade tavadega - karistus diskvalifikatsioon",
|
||||||
|
"KOKKUMÄNG: kahe tiimi vaheline kokkulepe - karistus mõlema tiimi diskvalifitseerimine",
|
||||||
|
"ALTKÄEMAKS JA PANUSTAMINE: keelatud - karistus diskvalifitseerimine",
|
||||||
|
"AGRESSIIVNE KÄITUMINE: inimeste vastu suunatud agressioon - karistus diskvalifitseerimine",
|
||||||
|
"SOHK: teadlik tegevus eelise saavutamiseks - karistus diskvalifitseerimine"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Turniiri formaat",
|
||||||
|
"rules": [
|
||||||
|
"Turniir toimub Fearless drafti põhimõtetel. See tähendab, et seeria jooksul pickitud champione ei saa pickida järgmistes mängudes kuni seeria lõpuni.",
|
||||||
|
{
|
||||||
|
"main": "Turniir toimub Round Robin + Single Elimination formaadis:",
|
||||||
|
"sub": [
|
||||||
|
"Esimene round on 2 kuueliimelist gruppi, kus kõik tiimid mängivad üksteisega korra läbi",
|
||||||
|
"Sellega selgitatakse 4 parimat, kes lähevad edasi järgmise päeva single elimination bracketisse",
|
||||||
|
"Gruppides tekkinud viigi korral pääseb edasi võistkond, kes viigistunud tiimide vahelise matchi võitis"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"schedule": {
|
"schedule": {
|
||||||
"title": "Ajakava",
|
"title": "Ajakava",
|
||||||
|
"comingSoon": "Tuleb hiljem",
|
||||||
"day": "Päev",
|
"day": "Päev",
|
||||||
"time": "Aeg",
|
"time": "Aeg",
|
||||||
"event": "Sündmus",
|
"event": "Sündmus",
|
||||||
@@ -226,6 +736,7 @@
|
|||||||
},
|
},
|
||||||
"expo": {
|
"expo": {
|
||||||
"title": "Messiala",
|
"title": "Messiala",
|
||||||
|
"comingSoon": "Tuleb hiljem",
|
||||||
"description": "TipiLANi messialal paiknevad ettevõtted, lisategevused ja toimuvad loengud.",
|
"description": "TipiLANi messialal paiknevad ettevõtted, lisategevused ja toimuvad loengud.",
|
||||||
"areas": {
|
"areas": {
|
||||||
"bar": "Baar",
|
"bar": "Baar",
|
||||||
@@ -252,7 +763,9 @@
|
|||||||
"houseRules": "Kodukord",
|
"houseRules": "Kodukord",
|
||||||
"cs2Rules": "CS2 reeglid",
|
"cs2Rules": "CS2 reeglid",
|
||||||
"lolRules": "LoL reeglid",
|
"lolRules": "LoL reeglid",
|
||||||
"miniRules": "Miniturniiride\n reeglid"
|
"miniRules": "Miniturniiride\n reeglid",
|
||||||
|
"reminder": "Osaleja meelespea",
|
||||||
|
"code": "Ürituse kodukord"
|
||||||
},
|
},
|
||||||
"admin": {
|
"admin": {
|
||||||
"title": "Haldus",
|
"title": "Haldus",
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"jsx": "react-jsx",
|
"jsx": "preserve",
|
||||||
"incremental": true,
|
"incremental": true,
|
||||||
"plugins": [
|
"plugins": [
|
||||||
{
|
{
|
||||||
|
|||||||