upd
@@ -0,0 +1,2 @@
|
||||
# VITE_COORD_URL=http://localhost:4000
|
||||
VITE_COORD_URL=https://coord.graff.tech
|
||||
@@ -0,0 +1,14 @@
|
||||
module.exports = {
|
||||
env: { browser: true, es2020: true },
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:react-hooks/recommended',
|
||||
],
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
|
||||
plugins: ['react-refresh'],
|
||||
rules: {
|
||||
'react-refresh/only-export-components': 'warn',
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
@@ -0,0 +1,10 @@
|
||||
module.exports = {
|
||||
apps: [
|
||||
{
|
||||
name: "stream.graff.tech-client",
|
||||
exec_mode: "cluster",
|
||||
script: "yarn",
|
||||
args: "preview --host",
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, viewport-fit=contain"
|
||||
/>
|
||||
<meta name="theme-color" content="#131317" />
|
||||
<title>Удаленная демонстрация</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root" style="background-color: #131317"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"name": "ps2-react-client",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@epicgames-ps/lib-pixelstreamingfrontend-ue5.3": "^0.0.2",
|
||||
"@uidotdev/usehooks": "^2.0.1",
|
||||
"date-fns": "^2.30.0",
|
||||
"i18next": "^22.5.0",
|
||||
"i18next-browser-languagedetector": "^7.0.2",
|
||||
"ky": "^0.33.3",
|
||||
"react": "^18.2.0",
|
||||
"react-calendar": "^4.3.0",
|
||||
"react-countdown": "^2.3.5",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-full-screen": "^1.1.1",
|
||||
"react-i18next": "^12.3.1",
|
||||
"react-input-mask": "^2.0.4",
|
||||
"react-qr-code": "^2.0.11",
|
||||
"react-router-dom": "^6.11.2",
|
||||
"react-timeit": "^1.2.12",
|
||||
"react-toastify": "^9.1.3",
|
||||
"react-transition-group": "^4.4.5",
|
||||
"socket.io-client": "^4.6.2",
|
||||
"ua-parser-js": "^1.0.35",
|
||||
"use-clipboard-copy": "^0.2.0",
|
||||
"usehooks-ts": "^2.9.1",
|
||||
"zustand": "^4.3.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.0.28",
|
||||
"@types/react-dom": "^18.0.11",
|
||||
"@types/react-input-mask": "^3.0.2",
|
||||
"@types/react-transition-group": "^4.4.6",
|
||||
"@types/ua-parser-js": "^0.7.36",
|
||||
"@typescript-eslint/eslint-plugin": "^5.57.1",
|
||||
"@typescript-eslint/parser": "^5.57.1",
|
||||
"@vitejs/plugin-react-swc": "^3.0.0",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"eslint": "^8.38.0",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-react-refresh": "^0.3.4",
|
||||
"postcss": "^8.4.23",
|
||||
"tailwindcss": "^3.3.2",
|
||||
"typescript": "^5.0.2",
|
||||
"vite": "^4.3.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M32.0126 63.5512C49.4365 63.5512 63.5613 49.3248 63.5613 31.7756C63.5613 29.6263 63.3495 27.5269 62.9456 25.4974H43.8858V38.0965H50.0359C47.4423 45.5818 40.3713 50.952 32.055 50.952C21.5398 50.952 13.0155 42.3664 13.0155 31.7756C13.0155 21.1848 21.5398 12.5992 32.055 12.5992V0H32.0126C14.5887 0 0.463867 14.2264 0.463867 31.7756C0.463867 49.3248 14.5887 63.5512 32.0126 63.5512Z" fill="#798FFF"/>
|
||||
<path d="M30.4259 0.0385644C16.8707 2.15049 6.49512 13.931 6.49512 28.1472C6.49512 43.8574 19.1659 56.593 34.7962 56.593C50.4264 56.593 63.0972 43.8574 63.0972 28.1472C63.0972 27.2411 63.0551 26.3449 62.9727 25.4605H43.8763V38.0414H50.0302C47.435 45.5158 40.3596 50.8783 32.0382 50.8783C21.5165 50.8783 12.987 42.3051 12.987 31.7296C12.987 21.729 20.6143 13.519 30.341 12.6559C30.9001 12.6063 31.4662 12.581 32.0382 12.581V0H31.9958C31.4694 0 30.9459 0.0129614 30.4259 0.0385644Z" fill="#D375FF"/>
|
||||
<path opacity="0.3" d="M28.3436 12.9886C29.5316 12.7616 30.7581 12.6428 32.0126 12.6428V0H31.9695C29.4296 0 26.9587 0.293941 24.5894 0.849496L28.3436 12.9886Z" fill="black" fill-opacity="0.6"/>
|
||||
<path opacity="0.3" d="M11.7092 7.1846L9.74316 35.7185L12.5534 33.0414C12.5367 32.7123 12.5282 32.381 12.5282 32.0477C12.5282 21.4743 21.0241 12.8891 31.5489 12.7721L18.9277 2.7832C16.3206 3.9294 13.8958 5.41515 11.7092 7.1846Z" fill="black" fill-opacity="0.6"/>
|
||||
<path opacity="0.3" d="M0.465096 32.136L14.8417 39.8933C13.7036 37.4501 13.0683 34.7263 13.0683 31.8544C13.0683 28.9786 13.7054 26.2513 14.8464 23.8056L10.4426 8.81348C4.29975 14.5819 0.463867 22.771 0.463867 31.8544C0.463867 31.9484 0.464277 32.0422 0.465096 32.136Z" fill="black" fill-opacity="0.6"/>
|
||||
<path opacity="0.3" d="M0.463867 30.8628L19.5379 59.4596L40.3637 59.8405L32.2394 50.8874C32.1539 50.8885 32.0683 50.8891 31.9826 50.8891C21.486 50.8891 12.9769 42.3811 12.9769 31.8859C12.9769 31.4592 12.991 31.0359 13.0187 30.6162L0.472692 30.6162C0.469436 30.6983 0.466494 30.7805 0.463867 30.8628Z" fill="black" fill-opacity="0.6"/>
|
||||
<path opacity="0.3" d="M14.3097 57.985L26.4453 50.0062C18.5941 47.5782 12.9044 40.4127 12.9044 31.951C12.9044 30.8637 12.9983 29.7979 13.1787 28.7607L5.10352 48.7573C7.48783 52.4173 10.628 55.5629 14.3097 57.985Z" fill="black" fill-opacity="0.6"/>
|
||||
<path opacity="0.3" d="M42.2195 61.8851C38.982 62.9654 35.513 63.5511 31.9052 63.5511C25.2415 63.5511 19.0515 61.5529 13.9185 58.1315L26.0686 50.0986C27.9232 50.6792 29.8987 50.9925 31.9484 50.9925C32.024 50.9925 32.0995 50.9921 32.1749 50.9912L42.2195 61.8851Z" fill="black" fill-opacity="0.6"/>
|
||||
<path opacity="0.3" d="M38.9721 62.7762C36.7002 63.2834 34.3365 63.5511 31.9096 63.5511C30.858 63.5511 29.8184 63.5008 28.7929 63.4026L25.9814 50.0986C26.361 50.2208 26.7458 50.3317 27.1353 50.4308L38.9721 62.7762Z" fill="black" fill-opacity="0.4"/>
|
||||
<path opacity="0.3" d="M44.0752 30.6163L63.0972 26.1207C63.0623 25.9177 63.0255 25.7154 62.987 25.5137H44.0752V30.6163Z" fill="black" fill-opacity="0.6"/>
|
||||
<path opacity="0.3" d="M48.03 38.0384L63.0976 25.5346C63.0963 25.5276 63.0949 25.5206 63.0936 25.5137L44.5396 38.0384H48.03Z" fill="black" fill-opacity="0.4"/>
|
||||
<path opacity="0.3" d="M56.8738 25.5137L43.1475 61.2322C48.5545 59.1664 53.2472 55.6523 56.7525 51.1636L58.9218 25.5137H56.8738Z" fill="black" fill-opacity="0.6"/>
|
||||
<path opacity="0.3" d="M62.9575 25.5137L43.1475 61.2322C55.0796 56.7129 63.5613 45.1896 63.5613 31.6876C63.5613 29.5748 63.3536 27.5104 62.9575 25.5137Z" fill="black" fill-opacity="0.4"/>
|
||||
<path d="M50.5844 0H63.0972V12.5247H50.5844V0Z" fill="#798FFF"/>
|
||||
<path d="M63.0972 12.5247H50.5844L44.0752 19.019H56.1246L63.0972 12.5247Z" fill="#798FFF"/>
|
||||
<path d="M50.5844 12.5247V0L44.0752 6.95816V19.019L50.5844 12.5247Z" fill="#798FFF"/>
|
||||
<path opacity="0.3" d="M63.0972 10.7935V12.5408L56.2039 19.0187H54.7461V10.6689L63.0972 10.7935Z" fill="black" fill-opacity="0.6"/>
|
||||
<path opacity="0.3" d="M44.0752 19.0188V6.76921L52.8111 6.03027L55.674 6.78702L44.0752 19.0188Z" fill="black" fill-opacity="0.6"/>
|
||||
<path opacity="0.3" d="M49.6403 1.3916L44.0752 7.12313V19.0189H51.4984L49.6403 1.3916Z" fill="black" fill-opacity="0.6"/>
|
||||
<path opacity="0.3" d="M44.4053 6.47622L44.0752 6.83173V19.0189L51.0635 12.294L54.3347 12.1655L61.7054 13.4079L61.0044 12.3583L52.1681 2.7832L44.4053 6.47622Z" fill="#D375FF"/>
|
||||
<path opacity="0.3" d="M51.0346 0H50.5742L48.7148 1.98114L50.9086 3.24714L51.0346 0Z" fill="black" fill-opacity="0.6"/>
|
||||
<path opacity="0.3" d="M60.7778 14.9295L56.4373 19.0187H55.6743L56.3559 10.8435L59.7322 10.2051L60.7778 14.9295Z" fill="black" fill-opacity="0.6"/>
|
||||
<path d="M50.5708 12.5247H63.0975V0H50.5708V12.5247Z" fill="url(#paint0_linear_1824_27752)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_1824_27752" x1="57.6988" y1="0" x2="57.6988" y2="12.5247" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="1" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.9 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M31.655 23.6153L31.1274 21.5903C30.3175 21.8056 29.512 22.034 28.711 22.2753C28.3169 20.9203 27.8898 19.5772 27.4296 18.2461C25.1851 11.7984 22.1366 5.66681 18.3582 0L16.6423 1.17105C20.3234 6.6911 23.2924 12.6645 25.4773 18.9461C25.9315 20.2532 26.3508 21.5718 26.735 22.9019C23.2366 24.0548 19.8221 25.4541 16.5167 27.0895L22.1964 15.5586L20.346 14.6253L13.3998 28.7167C11.6785 29.6649 9.98975 30.6789 8.3335 31.7587L9.45523 33.5191C10.209 33.0275 10.9706 32.5494 11.7401 32.0848L9.12122 37.3986L10.9731 38.3333L14.9634 30.2437C18.9117 28.1101 23.0365 26.329 27.2907 24.9209C28.4348 29.3142 29.1988 33.8 29.5741 38.3274L31.6432 38.1494C31.2556 33.4711 30.4644 28.836 29.2785 24.2973C30.0667 24.06 30.8628 23.8347 31.6668 23.6213" fill="#F2F2F2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 861 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="42" height="40" viewBox="0 0 42 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M40.1481 20.6285C36.7712 23.4259 29.8511 28.4338 28.6671 27.6282C28.1306 27.2627 27.9586 25.3018 29.7203 20.6991C30.1958 19.459 30.8718 17.9739 30.1847 17.411C29.6981 17.0145 28.843 17.2976 27.5695 18.2832C25.1634 20.1488 23.1956 23.1666 21.8928 25.1631C21.3658 25.9718 20.8221 26.8067 20.6058 26.9621C20.5439 26.7615 20.5653 25.9742 21.2841 24.1442C21.663 23.1769 22.3936 21.7544 23.1632 20.2487C23.9818 18.6511 24.8274 16.9979 25.2498 15.8839C25.6461 14.838 26.4552 12.3381 25.4408 11.7077C24.2338 10.9576 21.5045 13.6305 19.2593 16.0417C18.1094 17.277 17.1195 18.4465 16.2676 19.5391C15.3689 19.0324 14.4005 20.0228 13.3758 20.9211C11.3153 22.7313 7.47242 26.1003 4.63923 27.2555V27.2516L4.59881 27.2706L4.56315 27.2872C3.86892 27.5616 3.2373 27.7019 2.71029 27.6464C2.35747 27.6228 2.0276 27.463 1.79019 27.2008C1.76569 27.1711 1.74318 27.1399 1.72282 27.1072C1.65866 27.0082 1.61966 26.8949 1.60917 26.7773C1.59867 26.6598 1.617 26.5414 1.66259 26.4325L1.11497 26.1701C1.0263 26.3699 0.98796 26.5885 1.0033 26.8066C1.01863 27.0247 1.08719 27.2358 1.20295 27.4212C1.21167 27.4355 1.22196 27.4514 1.23226 27.4664L1.22435 27.4728C1.25288 27.5108 1.28457 27.5457 1.31548 27.5814C1.55323 27.8843 2.03983 28.2617 2.96547 28.295H3.05581C3.5749 28.295 4.21207 28.1253 4.86192 27.8121C7.79418 26.6228 11.5411 23.3363 13.7776 21.3738C14.4306 20.8014 15.2247 19.8586 15.8674 20.0576C12.3851 24.6905 11.5601 26.9939 12.2742 27.9636C12.3465 28.0639 12.4418 28.1455 12.5521 28.2015C12.6624 28.2576 12.7845 28.2863 12.9082 28.2855C13.0917 28.2805 13.2716 28.2339 13.4344 28.1491C15.0717 27.3562 17.7139 22.0938 16.8715 20.1536C16.8361 20.0701 16.7923 19.9904 16.7407 19.9157C17.5673 18.8517 18.5484 17.6956 19.7079 16.4492C23.8645 11.9836 24.9019 12.082 25.1206 12.2175C25.5121 12.461 25.3196 13.973 24.6816 15.6603C24.2719 16.7434 23.4334 18.3799 22.6211 19.9633C21.8429 21.4825 21.1074 22.9184 20.7191 23.9135C19.9789 25.7982 19.7935 26.9701 20.1643 27.3959C20.2364 27.4758 20.3299 27.5333 20.4338 27.5615C20.5377 27.5896 20.6475 27.5872 20.75 27.5544C21.0939 27.468 21.4434 26.955 22.4024 25.4842C23.6823 23.5218 25.6167 20.5556 27.9419 18.7525C29.4311 17.5981 29.7821 17.8574 29.7964 17.8701C29.9723 18.0144 29.8091 18.7668 29.1727 20.4152C28.2162 22.8938 27.2327 26.5118 27.8992 27.7701C28.102 28.1531 28.6615 29.2362 31.8862 27.3126C34.2479 25.9044 38.9118 22.3079 41 20.0973L40.1481 20.6285ZM13.1697 27.606C12.8717 27.7503 12.7949 27.6464 12.7663 27.606C12.3828 27.0858 12.7941 25.1758 16.3326 20.4557C16.9587 22.139 14.5003 26.9661 13.1697 27.606Z" fill="white" stroke="#F2F2F2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
@@ -0,0 +1,8 @@
|
||||
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M36.6347 3.47181V3.3335H36.3814H20.3065H3.65591H3.37954H3.3335V36.6668H3.37954H19.9841H20.0071H20.2835H36.6117V3.77149V3.74843C36.6808 3.65623 36.6808 3.56402 36.6347 3.47181ZM3.95529 4.83188L6.60373 10.1108C5.42921 11.7245 4.50801 13.5456 3.95529 15.505V4.83188ZM3.95529 18.9398C4.16256 15.8738 5.22194 13.0615 6.88009 10.7102L19.5465 36.0905C11.2327 35.86 4.48499 29.3132 3.93227 21.0606V18.9398H3.95529ZM19.7538 35.1684L7.2716 10.18C10.1734 6.42248 14.6642 3.97895 19.7538 3.9098V35.1684ZM20.3065 35.1684V3.9098C25.3731 4.00201 29.8639 6.46858 32.7427 10.2261L20.3065 35.1684ZM33.1342 10.7563C34.9766 13.3612 36.059 16.5654 36.059 20.0002C36.059 28.6908 29.127 35.8139 20.4908 36.0675L33.1342 10.7563ZM24.3137 3.9098H35.9208L33.0421 9.67282C30.8542 6.90657 27.8143 4.85493 24.3137 3.9098ZM15.6545 3.9098C12.177 4.85493 9.16006 6.88352 6.97222 9.62672L4.13954 3.9098H15.6545ZM3.95529 36.0905V24.4723C5.52133 30.12 10.0122 34.546 15.6545 36.0905H3.95529ZM36.059 36.0905H24.3137C30.0251 34.546 34.516 30.0509 36.059 24.334V36.0905ZM36.059 15.6664C35.5293 13.6608 34.6081 11.8167 33.4336 10.18L36.059 4.90104V15.6664Z" fill="#F2F2F2"/>
|
||||
<path d="M3.3335 36.6668V23.3335C5.11827 29.815 10.2364 34.8943 16.6668 36.6668H3.3335Z" fill="#F2F2F2"/>
|
||||
<path d="M36.6665 36.6665H21.6665C30.104 34.7915 34.7915 29.1665 36.6665 21.6665V36.6665Z" fill="#F2F2F2"/>
|
||||
<path d="M16.6668 3.3335C12.6469 4.41935 9.15122 6.81834 6.62937 10.0002L3.3335 3.3335H16.6668Z" fill="#F2F2F2"/>
|
||||
<path d="M20.0002 3.3335C10.7735 3.38112 3.3335 10.8335 3.3335 20.0002C3.3335 29.1668 10.798 36.6192 20.0002 36.6668V3.3335Z" fill="#F2F2F2"/>
|
||||
<path d="M20.0002 3.3335H3.3335V36.6668H20.0002V3.3335Z" fill="#F2F2F2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 6.4 KiB |
@@ -0,0 +1,20 @@
|
||||
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_0_1351)">
|
||||
<path d="M2.43071 16.1027C2.42861 16.0889 2.42974 16.0748 2.43417 16.0615C2.43861 16.0482 2.44618 16.0361 2.45619 16.0262C2.4662 16.0163 2.47845 16.0089 2.4919 16.0045C2.50535 16.0001 2.51967 15.9989 2.53368 16.001H5.95958C5.97345 15.9992 5.98762 16.0006 6.00083 16.0052C6.01405 16.0097 6.02588 16.0172 6.03568 16.027C6.04548 16.0369 6.05284 16.0488 6.05717 16.062C6.06151 16.0751 6.06285 16.0891 6.06081 16.1027V17.0642C6.06285 17.0779 6.06151 17.0919 6.05717 17.105C6.05284 17.1181 6.04548 17.1301 6.03568 17.14C6.02588 17.1498 6.01405 17.1573 6.00083 17.1618C5.98762 17.1663 5.97345 17.1678 5.95958 17.166H2.54252C2.52851 17.1681 2.51419 17.1669 2.50074 17.1625C2.48729 17.1581 2.47504 17.1507 2.46503 17.1408C2.45502 17.1309 2.44762 17.1188 2.44319 17.1055C2.43876 17.0922 2.43745 17.0781 2.43955 17.0642L2.43071 16.1027Z" fill="#F2F2F2"/>
|
||||
<path d="M8.37724 18.0273H2.53363C2.47677 18.0273 2.43066 18.0729 2.43066 18.1291V19.0906C2.43066 19.1468 2.47677 19.1923 2.53363 19.1923H8.37724C8.4341 19.1923 8.48021 19.1468 8.48021 19.0906V18.1291C8.48021 18.0729 8.4341 18.0273 8.37724 18.0273Z" fill="#F2F2F2"/>
|
||||
<path d="M0.0007065 20.154C-0.00107872 20.14 0.000553708 20.1259 0.00521358 20.1126C0.00987345 20.0993 0.0174392 20.0873 0.0275756 20.0774C0.0377121 20.0676 0.050126 20.0602 0.0636323 20.0558C0.0771386 20.0514 0.0913404 20.0502 0.105409 20.0522H36.2695C36.2834 20.0504 36.2976 20.0519 36.3108 20.0564C36.324 20.0609 36.3358 20.0684 36.3456 20.0782C36.3554 20.0881 36.3628 20.1001 36.3671 20.1132C36.3715 20.1263 36.3728 20.1403 36.3708 20.154V21.1154C36.3728 21.1291 36.3715 21.1431 36.3671 21.1562C36.3628 21.1693 36.3554 21.1813 36.3456 21.1912C36.3358 21.201 36.324 21.2085 36.3108 21.213C36.2976 21.2175 36.2834 21.219 36.2695 21.2172H0.114424C0.100355 21.2192 0.0859794 21.218 0.0724731 21.2136C0.0589668 21.2092 0.0465529 21.2018 0.0364164 21.192C0.02628 21.1821 0.0187143 21.1701 0.0140544 21.1568C0.00939452 21.1435 0.00793544 21.1294 0.00972066 21.1154L0.0007065 20.154Z" fill="#F2F2F2"/>
|
||||
<path d="M2.43093 22.1786C2.42914 22.1649 2.43053 22.151 2.4351 22.1379C2.43966 22.1248 2.44731 22.113 2.45728 22.1033C2.46726 22.0936 2.47935 22.0863 2.49265 22.082C2.50594 22.0778 2.52007 22.0766 2.5339 22.0786H32.6235C32.6374 22.0766 32.6515 22.0778 32.6648 22.082C32.6781 22.0863 32.6902 22.0936 32.7001 22.1033C32.7101 22.113 32.7178 22.1248 32.7223 22.1379C32.7269 22.151 32.7283 22.1649 32.7265 22.1786V23.1418C32.7283 23.1555 32.7269 23.1695 32.7223 23.1825C32.7178 23.1956 32.7101 23.2074 32.7001 23.2171C32.6902 23.2268 32.6781 23.2341 32.6648 23.2384C32.6515 23.2427 32.6374 23.2438 32.6235 23.2418H2.54274C2.52891 23.2438 2.51478 23.2427 2.50149 23.2384C2.48819 23.2341 2.4761 23.2268 2.46612 23.2171C2.45615 23.2074 2.4485 23.1956 2.44394 23.1825C2.43937 23.1695 2.43798 23.1555 2.43978 23.1418L2.43093 22.1786Z" fill="#F2F2F2"/>
|
||||
<path d="M24.2413 18.1294C24.2391 18.1154 24.2403 18.1011 24.2448 18.0876C24.2493 18.0742 24.2571 18.062 24.2673 18.052C24.2775 18.0421 24.29 18.0347 24.3037 18.0305C24.3174 18.0262 24.3319 18.0253 24.346 18.0276H39.8958C39.9098 18.0256 39.9241 18.0268 39.9376 18.0312C39.951 18.0355 39.9633 18.043 39.9733 18.0529C39.9833 18.0628 39.9909 18.0748 39.9953 18.0881C39.9997 18.1014 40.0009 18.1156 39.9988 18.1294V19.0909C40.0005 19.1046 39.999 19.1186 39.9944 19.1318C39.9899 19.1449 39.9823 19.1568 39.9724 19.1666C39.9625 19.1764 39.9505 19.1839 39.9372 19.1884C39.9239 19.1929 39.9097 19.1943 39.8958 19.1926H24.346C24.3319 19.1946 24.3176 19.1934 24.3041 19.189C24.2906 19.1846 24.2783 19.1772 24.2682 19.1674C24.258 19.1576 24.2503 19.1455 24.2456 19.1322C24.241 19.119 24.2395 19.1048 24.2413 19.0909V18.1294Z" fill="#F2F2F2"/>
|
||||
<path d="M25.4537 16.1027C25.4516 16.0889 25.4527 16.0748 25.4571 16.0615C25.4616 16.0482 25.4691 16.0361 25.4791 16.0262C25.4892 16.0163 25.5014 16.0089 25.5148 16.0045C25.5283 16.0001 25.5426 15.9989 25.5566 16.001H28.9914C29.0052 15.9992 29.0194 16.0006 29.0326 16.0052C29.0458 16.0097 29.0577 16.0172 29.0675 16.027C29.0773 16.0369 29.0846 16.0488 29.089 16.062C29.0933 16.0751 29.0946 16.0891 29.0926 16.1027V17.0642C29.0946 17.0779 29.0933 17.0919 29.089 17.105C29.0846 17.1181 29.0773 17.1301 29.0675 17.14C29.0577 17.1498 29.0458 17.1573 29.0326 17.1618C29.0194 17.1663 29.0052 17.1678 28.9914 17.166H25.5601C25.5461 17.1681 25.5318 17.1669 25.5183 17.1625C25.5049 17.1581 25.4926 17.1507 25.4826 17.1408C25.4726 17.1309 25.4652 17.1188 25.4608 17.1055C25.4563 17.0922 25.455 17.0781 25.4571 17.0642L25.4537 16.1027Z" fill="#F2F2F2"/>
|
||||
<path d="M9.7017 18.1291C9.6996 18.1153 9.70074 18.1011 9.70517 18.0878C9.7096 18.0746 9.71717 18.0625 9.72719 18.0526C9.7372 18.0427 9.74945 18.0352 9.7629 18.0309C9.77635 18.0265 9.79067 18.0253 9.80467 18.0274H10.8111C10.825 18.0256 10.839 18.027 10.8522 18.0315C10.8654 18.036 10.8774 18.0435 10.8872 18.0534C10.897 18.0632 10.9044 18.0752 10.9087 18.0883C10.9131 18.1015 10.9144 18.1154 10.9124 18.1291V19.0906C10.9144 19.1043 10.9131 19.1182 10.9087 19.1314C10.9044 19.1445 10.897 19.1565 10.8872 19.1663C10.8774 19.1762 10.8654 19.1837 10.8522 19.1882C10.839 19.1927 10.825 19.1941 10.8111 19.1924H9.80467C9.79067 19.1944 9.77635 19.1932 9.7629 19.1888C9.74945 19.1845 9.7372 19.177 9.72719 19.1671C9.71717 19.1572 9.7096 19.1452 9.70517 19.1319C9.70074 19.1186 9.6996 19.1044 9.7017 19.0906V18.1291Z" fill="#F2F2F2"/>
|
||||
<path d="M12.1178 18.1294C12.1156 18.1154 12.1167 18.1011 12.1212 18.0876C12.1258 18.0742 12.1335 18.062 12.1438 18.052C12.154 18.0421 12.1665 18.0347 12.1802 18.0305C12.1939 18.0262 12.2083 18.0253 12.2225 18.0276H13.2289C13.2428 18.0259 13.2568 18.0273 13.27 18.0318C13.2832 18.0363 13.2952 18.0438 13.305 18.0537C13.3148 18.0635 13.3222 18.0755 13.3265 18.0886C13.3309 18.1018 13.332 18.1157 13.33 18.1294V19.0909C13.332 19.1046 13.3309 19.1185 13.3265 19.1316C13.3222 19.1448 13.3148 19.1568 13.305 19.1666C13.2952 19.1765 13.2832 19.184 13.27 19.1885C13.2568 19.193 13.2428 19.1944 13.2289 19.1926H12.2225C12.2084 19.1946 12.194 19.1934 12.1805 19.189C12.167 19.1846 12.1548 19.1772 12.1446 19.1674C12.1345 19.1576 12.1268 19.1455 12.1221 19.1322C12.1174 19.119 12.116 19.1048 12.1178 19.0909V18.1294Z" fill="#F2F2F2"/>
|
||||
<path d="M14.5459 18.1291C14.5438 18.1153 14.545 18.1011 14.5494 18.0878C14.5538 18.0746 14.5614 18.0625 14.5714 18.0526C14.5814 18.0427 14.5937 18.0352 14.6071 18.0309C14.6206 18.0265 14.6349 18.0253 14.6489 18.0274H15.6554C15.6694 18.0253 15.6837 18.0265 15.6972 18.0309C15.7106 18.0352 15.7229 18.0427 15.7329 18.0526C15.7429 18.0625 15.7503 18.0746 15.7547 18.0878C15.7591 18.1011 15.7604 18.1153 15.7583 18.1291V19.0906C15.7604 19.1044 15.7591 19.1186 15.7547 19.1319C15.7503 19.1452 15.7429 19.1572 15.7329 19.1671C15.7229 19.177 15.7106 19.1845 15.6972 19.1888C15.6837 19.1932 15.6694 19.1944 15.6554 19.1924H14.6489C14.6349 19.1944 14.6206 19.1932 14.6071 19.1888C14.5937 19.1845 14.5814 19.177 14.5714 19.1671C14.5614 19.1572 14.5538 19.1452 14.5494 19.1319C14.545 19.1186 14.5438 19.1044 14.5459 19.0906V18.1291Z" fill="#F2F2F2"/>
|
||||
<path d="M9.7017 16.1027C9.6996 16.0889 9.70074 16.0748 9.70517 16.0615C9.7096 16.0482 9.71717 16.0361 9.72719 16.0262C9.7372 16.0163 9.74945 16.0089 9.7629 16.0045C9.77635 16.0001 9.79067 15.9989 9.80467 16.001H10.8111C10.825 15.9992 10.839 16.0006 10.8522 16.0052C10.8654 16.0097 10.8774 16.0172 10.8872 16.027C10.897 16.0369 10.9044 16.0488 10.9087 16.062C10.9131 16.0751 10.9144 16.0891 10.9124 16.1027V17.0642C10.9144 17.0779 10.9131 17.0919 10.9087 17.105C10.9044 17.1181 10.897 17.1301 10.8872 17.14C10.8774 17.1498 10.8654 17.1573 10.8522 17.1618C10.839 17.1663 10.825 17.1678 10.8111 17.166H9.80467C9.79067 17.1681 9.77635 17.1669 9.7629 17.1625C9.74945 17.1581 9.7372 17.1507 9.72719 17.1408C9.71717 17.1309 9.7096 17.1188 9.70517 17.1055C9.70074 17.0922 9.6996 17.0781 9.7017 17.0642V16.1027Z" fill="#F2F2F2"/>
|
||||
<path d="M12.1178 16.1027C12.116 16.0888 12.1174 16.0746 12.1221 16.0613C12.1268 16.0481 12.1345 16.036 12.1446 16.0262C12.1548 16.0163 12.167 16.0089 12.1805 16.0045C12.194 16.0002 12.2084 15.9989 12.2225 16.0009H13.2289C13.2428 15.9992 13.2568 16.0006 13.27 16.0051C13.2832 16.0096 13.2952 16.0171 13.305 16.027C13.3148 16.0368 13.3222 16.0488 13.3265 16.0619C13.3309 16.0751 13.332 16.089 13.33 16.1027V17.0642C13.332 17.0778 13.3309 17.0918 13.3265 17.1049C13.3222 17.1181 13.3148 17.13 13.305 17.1399C13.2952 17.1498 13.2832 17.1572 13.27 17.1618C13.2568 17.1663 13.2428 17.1677 13.2289 17.1659H12.2225C12.2083 17.1683 12.1939 17.1673 12.1802 17.1631C12.1665 17.1589 12.154 17.1515 12.1438 17.1415C12.1335 17.1316 12.1258 17.1194 12.1212 17.1059C12.1167 17.0925 12.1156 17.0782 12.1178 17.0642V16.1027Z" fill="#F2F2F2"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_0_1351">
|
||||
<rect width="40" height="40" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.7 KiB |
|
After Width: | Height: | Size: 418 KiB |
|
After Width: | Height: | Size: 317 KiB |
|
After Width: | Height: | Size: 371 KiB |
|
After Width: | Height: | Size: 224 KiB |
@@ -0,0 +1,116 @@
|
||||
<svg width="856" height="916" viewBox="0 0 856 916" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g opacity="0.3">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M88.239 221.721C121.543 178.204 162.08 140.268 210.913 115.404C259.367 90.7331 314.377 88.9243 367.897 79.3292C428.273 68.505 494.316 25.2452 547.967 54.9773C603.326 85.6558 583.909 179.657 629.029 224.042C679.835 274.021 803.795 249.811 817.182 319.811C831.182 393.013 702.52 421.999 680.505 493.202C662.084 552.779 728.947 617.634 707.045 676.022C685.89 732.416 617.601 753.447 569.357 789.506C514.144 830.774 466.763 925.095 400.865 904.871C321.656 880.561 340.888 739.654 269.56 697.496C211.13 662.962 125.95 745.229 68.8041 708.611C17.691 675.859 27.0171 596.164 17.7843 536.164C9.21776 480.494 4.27765 424.273 16.5927 369.31C28.7013 315.269 54.5806 265.7 88.239 221.721Z" stroke="url(#paint0_linear_44_6738)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M458.626 29.2529C491.019 15.7871 525.637 7.72984 560.669 9.57498C595.429 11.4058 626.506 28.0094 659.249 39.8198C696.187 53.143 746.649 50.2979 766.876 83.9528C787.746 118.679 746.892 164.578 757.698 203.626C769.867 247.595 846.341 273.848 831.356 316.94C815.685 362.004 735.076 336.894 700.078 369.322C670.795 396.456 687.103 453.811 656.271 479.171C626.491 503.665 581.899 493.467 543.61 498.017C499.789 503.225 443.329 540.351 413.269 508.047C377.137 469.218 432.904 397.255 406.854 351.054C385.515 313.207 311.957 331.553 291.996 292.961C274.143 258.444 304.824 217.247 318.912 181.028C331.982 147.423 347.24 114.674 371.661 88.1445C395.673 62.0599 425.888 42.862 458.626 29.2529Z" stroke="url(#paint1_linear_44_6738)"/>
|
||||
<path d="M237.126 112.084C188.536 135.317 147.776 171.599 113.986 213.54C79.8369 255.928 53.1943 303.998 39.9726 356.8C26.5255 410.502 30.0018 465.864 37.0547 520.773C44.6563 579.953 33.54 658.029 82.9591 691.462C138.211 728.842 223.921 650.097 280.486 685.46C349.537 728.631 327.188 866.609 404.419 892.436C468.672 913.922 517.538 822.407 572.8 783.212C621.086 748.963 688.7 729.973 710.867 675.08C733.818 618.247 669.71 552.886 689.27 494.798C712.646 425.377 839.774 400.048 827.812 327.78C816.374 258.673 693.982 279.424 645.287 229.071C602.042 184.355 623.424 92.4691 569.782 60.9691C517.795 30.441 451.844 71.3281 392.256 80.4841C339.434 88.6005 285.339 89.0299 237.126 112.084Z" stroke="url(#paint2_linear_44_6738)"/>
|
||||
<path d="M257.471 108.703C209.174 130.335 168.248 164.974 134.031 205.344C99.4489 246.143 72.1014 292.708 57.8208 344.25C43.2967 396.67 45.3551 451.134 50.9378 505.242C56.9547 563.558 44.123 639.977 91.8464 674.028C145.203 712.098 231.322 636.85 286.016 672.971C352.785 717.067 327.452 852.051 402.681 879.311C465.268 901.99 515.509 813.295 570.75 776.149C619.019 743.691 685.898 726.693 709.017 673.317C732.953 618.055 671.584 552.282 692.219 495.704C716.882 428.087 842.375 406.322 832.396 335.043C822.853 266.882 702.124 284.266 655.526 233.614C614.144 188.631 637.397 98.8992 585.479 66.6441C535.162 35.3841 469.379 73.9304 410.623 81.4644C358.54 88.143 305.395 87.2397 257.471 108.703Z" stroke="url(#paint3_linear_44_6738)"/>
|
||||
<path d="M277.234 105.244C229.278 125.302 188.243 158.314 153.654 197.117C118.698 236.333 90.7033 281.388 75.4182 331.65C59.8725 382.768 60.56 436.3 64.7169 489.568C69.1971 546.979 54.7255 621.705 100.754 656.31C152.215 694.999 238.622 623.218 291.445 660.027C355.928 704.961 327.747 836.889 400.951 865.499C461.852 889.302 513.359 803.438 568.513 768.314C616.705 737.624 682.794 722.571 706.802 670.725C731.66 617.046 673.009 550.957 694.658 495.906C720.531 430.113 844.292 411.814 836.239 341.576C828.537 274.412 709.562 288.526 665.045 237.647C625.512 192.463 650.543 104.92 600.351 71.9756C551.708 40.0476 486.168 76.2874 428.289 82.2467C376.982 87.5292 324.818 85.3409 277.234 105.244Z" stroke="url(#paint4_linear_44_6738)"/>
|
||||
<path d="M296.419 101.686C248.854 120.202 207.764 151.603 172.861 188.847C137.586 226.487 109.003 270.029 92.7682 318.993C76.2566 368.792 75.6209 421.358 78.3974 473.749C81.3898 530.215 65.3546 603.214 109.69 638.31C159.259 677.548 245.836 609.201 296.787 646.626C358.986 692.314 328.093 821.128 399.252 851.005C458.452 875.861 511.114 792.836 566.115 759.707C614.174 730.761 679.417 717.602 704.254 667.296C729.968 615.211 674.013 548.901 696.612 495.391C723.62 431.439 845.556 416.506 839.37 347.362C833.455 281.242 716.319 292.185 673.866 241.151C636.165 195.829 662.879 110.507 614.416 76.9388C567.448 44.4066 502.225 78.3764 445.263 82.8093C394.769 86.7389 343.616 83.3139 296.419 101.686Z" stroke="url(#paint5_linear_44_6738)"/>
|
||||
<path d="M315.035 98.0128C267.906 115.018 226.816 144.827 191.654 180.52C156.118 216.593 127.004 258.621 109.874 306.273C92.4527 354.736 90.5424 406.303 91.9848 457.783C93.5393 513.266 76.0175 584.507 118.665 620.032C166.346 659.749 252.977 594.798 302.06 632.771C361.977 679.127 328.51 804.776 397.606 835.835C455.091 861.675 508.8 781.491 563.583 750.328C611.452 723.099 675.797 711.782 701.4 663.025C727.909 612.544 674.623 546.104 698.108 494.148C726.177 432.053 846.198 420.383 841.82 352.381C837.633 287.353 722.42 295.224 682.01 244.106C646.123 198.709 674.425 115.636 627.69 81.51C582.397 48.4368 517.562 80.1752 461.557 83.1314C411.911 85.7518 361.799 81.139 315.035 98.0128Z" stroke="url(#paint6_linear_44_6738)"/>
|
||||
<path d="M333.087 94.2063C286.439 109.735 245.403 137.971 210.037 172.124C174.295 206.641 144.707 247.156 126.738 293.481C108.463 340.595 105.328 391.133 105.483 441.668C105.651 496.133 86.7197 565.588 127.686 601.479C173.487 641.607 260.057 580.012 307.276 618.462C364.919 665.401 329.014 787.838 396.035 819.996C451.793 846.75 506.441 769.407 560.943 740.178C608.565 714.638 671.962 705.111 698.271 657.909C725.509 609.038 674.864 542.56 699.173 492.168C728.226 431.942 846.247 423.43 843.616 356.616C841.099 292.724 727.888 297.627 689.498 246.494C655.405 201.084 685.201 120.285 640.191 85.666C596.57 52.1147 532.193 81.6626 477.182 83.1925C428.418 84.5486 379.373 78.7978 333.087 94.2063Z" stroke="url(#paint7_linear_44_6738)"/>
|
||||
<path d="M350.588 90.2473C304.466 104.334 263.535 131.018 228.02 163.644C192.128 196.617 162.123 235.623 143.37 280.609C124.297 326.363 119.986 375.843 118.903 425.401C117.735 478.813 97.473 546.458 136.766 582.655C180.698 623.124 267.095 564.841 312.457 603.7C367.833 651.137 329.631 770.321 394.564 803.494C448.586 831.092 504.065 756.586 558.225 729.257C605.548 705.377 667.947 697.585 694.9 651.942C722.805 604.685 674.768 538.259 699.838 489.439C729.8 431.092 845.739 425.632 844.793 360.049C843.888 297.336 732.754 299.375 696.359 248.296C664.038 202.933 695.232 124.431 651.942 89.3824C609.987 55.4154 546.138 82.8158 492.157 82.9707C444.305 83.1081 396.353 76.2696 350.588 90.2473Z" stroke="url(#paint8_linear_44_6738)"/>
|
||||
<path d="M374.577 86.1146C329.022 98.7954 288.248 123.952 252.638 155.064C216.65 186.507 186.285 224.009 166.803 267.647C146.988 312.029 141.553 360.426 139.28 408.977C136.829 461.305 115.314 527.117 152.946 563.558C195.019 604.301 281.132 549.282 324.647 588.481C377.769 636.334 337.408 752.227 400.247 786.331C452.526 814.705 508.729 743.028 562.486 717.563C609.457 695.312 670.813 689.2 698.348 645.118C726.857 599.478 681.394 533.191 707.162 485.949C737.957 429.489 851.737 426.97 852.413 362.661C853.059 301.166 744.073 300.449 709.647 249.489C679.074 204.234 711.57 128.046 669.993 92.6331C629.698 58.3123 566.441 83.6101 513.524 82.4425C466.615 81.4074 419.778 73.532 374.577 86.1146Z" stroke="url(#paint9_linear_44_6738)"/>
|
||||
<path d="M390.995 81.7978C346.049 93.1098 305.482 116.763 269.83 146.377C233.799 176.308 203.13 212.311 182.974 254.593C162.475 297.597 155.966 344.885 152.552 392.402C148.873 443.614 126.183 507.573 162.165 544.2C202.395 585.149 288.116 533.344 329.796 572.816C380.677 621.002 338.298 733.572 399.037 768.524C449.568 797.603 506.39 728.744 559.685 705.106C606.253 684.452 666.521 679.962 694.578 637.442C723.628 593.418 680.701 527.357 707.104 481.697C738.658 427.128 850.206 427.44 852.439 364.445C854.575 304.205 747.804 300.839 715.317 250.066C686.466 204.976 720.169 131.118 680.296 95.4031C641.652 60.7897 579.052 84.0319 527.23 81.5948C481.293 79.4344 435.593 70.5733 390.995 81.7978Z" stroke="url(#paint10_linear_44_6738)"/>
|
||||
<path d="M406.882 77.2731C362.584 87.2548 322.274 109.43 286.632 137.565C250.61 166 219.692 200.514 198.918 241.435C177.79 283.053 170.26 329.21 165.756 375.666C160.901 425.735 137.115 487.825 171.463 524.577C209.865 565.667 295.089 517.021 334.948 556.699C383.605 605.137 339.348 714.357 397.984 750.074C446.766 779.789 504.101 713.734 556.878 691.884C602.993 672.792 662.131 669.866 690.65 628.906C720.178 586.497 679.747 520.746 706.722 476.67C738.961 423.993 848.207 427.024 851.934 365.378C855.497 306.429 751.003 300.525 720.425 250.002C693.269 205.135 728.083 133.619 689.901 97.6647C652.898 62.8193 591.018 84.0545 540.321 80.4017C495.381 77.1638 450.837 67.3688 406.882 77.2731Z" stroke="url(#paint11_linear_44_6738)"/>
|
||||
<path d="M422.257 72.5303C378.644 81.221 338.639 101.947 303.057 128.622C267.097 155.58 235.985 188.615 214.648 228.17C192.946 268.399 184.448 313.404 178.904 358.775C172.928 407.675 148.126 467.882 180.855 504.701C217.448 545.866 302.072 500.321 340.125 540.14C386.579 588.75 340.584 694.598 397.117 730.997C444.15 761.279 501.896 698.01 554.098 677.907C599.712 660.341 657.681 658.921 686.602 619.516C716.545 578.718 678.568 513.359 706.053 470.867C738.902 420.083 845.782 425.716 850.935 365.455C855.863 307.831 753.706 299.499 725.003 249.29C699.512 204.701 735.342 135.536 698.839 99.4035C663.463 64.3858 602.363 83.6648 552.819 78.851C508.901 74.5838 465.531 63.9069 422.257 72.5303Z" stroke="url(#paint12_linear_44_6738)"/>
|
||||
<path d="M437.123 67.5493C394.232 74.9896 354.578 94.2954 319.107 119.53C283.259 145.034 252.008 176.602 230.162 214.789C207.944 253.627 198.529 297.46 191.996 341.724C184.955 389.431 159.215 447.743 190.345 484.572C225.149 525.749 309.072 483.245 345.338 523.139C389.611 571.841 342.018 674.299 396.452 711.298C441.738 742.079 499.791 681.575 551.366 663.176C596.431 647.1 653.193 647.124 682.456 609.269C712.754 570.076 677.185 505.191 705.119 464.279C738.504 415.384 842.957 423.505 849.47 364.661C855.699 308.391 755.934 297.745 729.071 247.911C705.214 203.655 741.964 136.846 707.126 100.596C673.362 65.4654 613.1 82.8404 564.734 76.9207C521.861 71.6732 479.681 60.1666 437.123 67.5493Z" stroke="url(#paint13_linear_44_6738)"/>
|
||||
<path d="M451.493 62.3147C409.36 68.5461 370.102 86.4624 334.791 110.28C299.106 134.352 267.767 164.467 245.468 201.285C222.788 238.732 212.51 281.377 205.04 324.513C196.99 371.005 170.39 427.413 199.94 464.196C232.979 505.321 316.103 465.793 350.603 505.7C392.719 554.416 343.667 653.472 396.008 690.99C439.553 722.202 497.812 664.437 548.708 647.699C593.179 633.073 648.699 634.482 678.246 598.169C708.837 560.572 675.629 496.238 703.95 456.903C737.797 409.892 839.767 420.383 847.574 362.985C855.039 308.098 757.715 295.251 732.655 245.852C710.4 201.982 747.977 137.531 714.784 101.225C682.616 66.0387 623.248 81.5637 576.085 74.5938C534.278 68.4154 493.301 56.1316 451.493 62.3147Z" stroke="url(#paint14_linear_44_6738)"/>
|
||||
<path d="M470.659 56.8117C429.315 61.8766 390.497 78.4354 355.395 100.86C319.92 123.523 288.548 152.201 265.849 187.654C242.763 223.71 231.675 265.151 223.321 307.142C214.316 352.398 186.935 406.897 214.929 443.578C246.226 484.589 328.456 447.972 361.212 487.827C401.199 536.482 350.829 632.128 401.085 670.083C442.896 701.659 501.261 646.606 551.427 631.482C595.261 618.268 649.506 620.998 679.278 586.217C710.102 550.206 679.205 486.501 707.852 448.735C742.089 403.6 841.524 416.343 850.556 360.418C859.192 306.939 764.357 292.007 741.062 243.101C720.374 199.669 758.682 137.575 727.116 101.271C696.525 66.087 638.106 79.8177 592.167 71.8538C551.444 64.7942 511.682 51.786 470.659 56.8117Z" stroke="url(#paint15_linear_44_6738)"/>
|
||||
<path d="M484.074 51.0257C443.551 54.9675 405.217 70.2021 370.371 91.259C335.154 112.54 303.799 139.798 280.755 173.888C257.318 208.558 245.473 248.782 236.286 289.609C226.385 333.612 198.301 386.199 224.761 422.724C254.344 463.561 335.588 429.784 366.624 469.527C404.512 518.044 352.961 610.278 401.144 648.59C441.23 680.464 499.604 628.09 548.992 614.535C592.147 602.69 645.086 606.68 675.025 573.419C706.023 538.982 677.384 475.979 706.297 439.775C740.852 396.505 837.705 411.38 847.893 356.952C857.636 304.906 765.331 288.004 743.76 239.648C724.603 196.704 763.549 136.962 733.59 100.718C704.554 65.5929 647.135 77.5863 602.44 68.6849C562.819 60.7944 524.283 47.1145 484.074 51.0257Z" stroke="url(#paint16_linear_44_6738)"/>
|
||||
<path d="M497.029 44.9432C457.357 47.806 419.547 61.7509 385.003 81.4672C350.09 101.393 318.804 127.251 295.468 159.982C271.734 193.272 259.187 232.267 249.22 271.917C238.478 314.651 209.769 365.324 234.722 401.641C262.619 442.243 342.787 411.234 372.13 450.805C407.951 499.111 355.358 587.934 401.482 626.524C439.855 658.629 498.14 608.901 546.705 596.866C589.14 586.349 640.745 591.535 670.794 559.78C701.906 526.903 675.472 464.675 704.591 430.02C739.392 388.603 833.62 405.489 844.896 352.582C855.679 301.989 765.942 283.234 746.053 235.482C728.39 193.075 767.881 135.677 739.504 99.5513C712.003 64.5396 655.633 74.8543 612.199 65.0725C573.697 56.4013 536.394 42.1026 497.029 44.9432Z" stroke="url(#paint17_linear_44_6738)"/>
|
||||
<path d="M509.539 38.5471C470.746 40.3758 433.501 53.067 399.302 71.471C364.739 90.0708 333.572 114.549 309.997 145.93C286.02 177.845 272.823 215.603 262.13 254.062C250.604 295.512 221.349 344.276 244.82 380.331C271.062 420.642 350.068 392.325 377.746 431.664C411.535 479.687 358.037 565.106 402.119 603.896C438.793 636.168 496.895 589.047 544.592 578.481C586.268 569.249 636.513 575.567 666.617 545.303C697.785 513.969 673.498 452.586 702.764 419.468C737.741 379.889 829.305 398.665 841.6 347.297C853.356 298.176 766.222 277.685 747.971 230.591C731.762 188.769 771.706 133.704 744.887 97.7514C718.896 62.9075 663.623 71.6035 621.465 60.9983C584.095 51.5974 548.032 36.7326 509.539 38.5471Z" stroke="url(#paint18_linear_44_6738)"/>
|
||||
<path d="M521.617 31.8248C483.729 32.6651 447.087 44.1395 413.277 61.261C379.108 78.5647 348.108 101.685 324.347 131.724C300.18 162.275 286.388 198.788 275.02 236.045C262.768 276.2 233.043 323.058 255.062 358.801C279.681 398.763 357.442 373.062 383.485 412.111C415.278 459.78 361.012 541.806 403.071 580.719C438.062 613.093 495.888 568.54 542.674 559.393C583.555 551.4 632.416 558.786 662.52 529.995C693.688 500.187 671.488 439.715 700.844 408.121C735.928 370.361 824.793 390.905 838.036 341.093C850.7 293.461 766.197 271.353 749.539 224.968C734.744 183.774 775.05 131.03 749.763 95.3048C725.256 60.6817 671.124 67.8203 630.256 56.4492C594.029 46.3693 559.211 30.9911 521.617 31.8248Z" stroke="url(#paint19_linear_44_6738)"/>
|
||||
<path d="M533.275 24.7642C496.317 24.6622 460.315 34.9581 426.937 50.8282C393.204 66.8671 362.419 88.6542 338.523 117.362C314.22 146.559 299.886 181.82 287.897 217.867C274.976 256.717 244.857 301.677 265.455 337.059C288.485 376.618 364.92 353.452 389.361 392.154C419.198 439.401 364.298 518.05 404.356 557.01C437.682 589.422 495.142 547.392 540.977 539.612C581.027 532.814 628.483 541.202 658.531 513.866C689.642 485.562 669.47 426.068 698.858 395.979C733.98 360.02 820.117 382.208 834.239 333.968C847.744 287.839 765.898 264.23 750.785 218.604C737.363 178.084 777.941 127.643 754.156 92.1985C731.105 57.848 678.158 63.4915 638.591 51.4121C603.518 40.7043 569.947 24.8654 533.275 24.7642Z" stroke="url(#paint20_linear_44_6738)"/>
|
||||
<path d="M549.806 17.3515C513.801 16.3542 478.474 25.5107 445.568 40.1618C412.313 54.9688 381.79 75.4478 357.811 102.837C333.424 130.693 318.601 164.699 306.045 199.528C292.512 237.065 262.074 280.137 281.283 315.11C302.76 354.211 377.793 333.498 400.665 371.799C428.587 418.556 373.187 493.849 411.269 532.78C442.951 565.169 499.954 525.616 544.8 519.149C583.986 513.499 630.018 522.825 659.958 496.921C690.957 470.101 672.749 411.646 702.112 383.045C737.205 348.864 820.588 372.574 835.52 325.918C849.798 281.303 770.63 256.312 757.014 211.492C744.922 171.688 785.682 123.529 763.37 88.4185C741.747 54.3911 690.023 58.6031 651.768 45.873C617.856 34.5884 585.533 18.341 549.806 17.3515Z" stroke="url(#paint21_linear_44_6738)"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_44_6738" x1="445.647" y1="1012.33" x2="219.962" y2="-169.599" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.598958" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_44_6738" x1="403.699" y1="581.954" x2="656.869" y2="-145.549" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.598958" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_44_6738" x1="445.787" y1="999.116" x2="252.998" y2="-167.727" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.598958" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint3_linear_44_6738" x1="440.701" y1="985.112" x2="279.914" y2="-165.755" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.598958" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint4_linear_44_6738" x1="435.694" y1="970.322" x2="305.991" y2="-163.711" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.598958" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint5_linear_44_6738" x1="430.79" y1="954.753" x2="331.23" y2="-161.624" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.598958" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint6_linear_44_6738" x1="426.016" y1="938.415" x2="355.636" y2="-159.521" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.598958" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint7_linear_44_6738" x1="421.394" y1="921.317" x2="379.213" y2="-157.43" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.598958" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint8_linear_44_6738" x1="416.954" y1="903.469" x2="401.97" y2="-155.378" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.598958" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint9_linear_44_6738" x1="419.75" y1="884.877" x2="430.944" y2="-153.397" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.598958" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint10_linear_44_6738" x1="415.738" y1="865.559" x2="452.077" y2="-151.508" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.598958" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint11_linear_44_6738" x1="411.971" y1="845.521" x2="472.404" y2="-149.743" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.598958" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint12_linear_44_6738" x1="408.481" y1="824.783" x2="491.944" y2="-148.123" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.598958" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint13_linear_44_6738" x1="405.283" y1="803.353" x2="510.699" y2="-146.678" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.598958" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint14_linear_44_6738" x1="402.401" y1="781.246" x2="528.684" y2="-145.432" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.598958" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint15_linear_44_6738" x1="405.134" y1="758.477" x2="551.186" y2="-144.41" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.598958" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint16_linear_44_6738" x1="402.946" y1="735.064" x2="567.663" y2="-143.637" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.598958" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint17_linear_44_6738" x1="401.136" y1="711.021" x2="583.403" y2="-143.136" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.598958" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint18_linear_44_6738" x1="399.724" y1="686.364" x2="598.423" y2="-142.933" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.598958" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint19_linear_44_6738" x1="398.728" y1="661.11" x2="612.737" y2="-143.051" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.598958" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint20_linear_44_6738" x1="398.168" y1="635.279" x2="626.359" y2="-143.511" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.598958" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint21_linear_44_6738" x1="403.338" y1="608.887" x2="644.583" y2="-144.337" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.598958" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 21 KiB |
@@ -0,0 +1,266 @@
|
||||
<svg width="1079" height="951" viewBox="0 0 1079 951" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g opacity="0.2">
|
||||
<path d="M497.426 487.702L497.025 488.001L497.175 488.202L497.426 488.202C634.805 488.202 759.177 480.096 849.204 471.99C894.218 467.937 930.647 463.883 955.822 460.843C968.41 459.323 978.185 458.057 984.813 457.17C988.127 456.726 990.654 456.378 992.353 456.14C993.203 456.022 993.846 455.93 994.276 455.869C994.302 455.865 994.327 455.862 994.352 455.858C994.039 729.143 771.679 950.59 497.426 950.59C222.98 950.59 0.500036 728.831 0.500024 455.281C0.500021 375.836 8.2705 316.796 21.8501 274.29C35.4296 231.784 54.8016 205.86 77.9717 192.563C101.136 179.269 128.191 178.543 157.255 186.628C186.323 194.715 217.359 211.607 248.425 233.485C310.555 277.24 372.703 340.862 419.329 393.562C442.639 419.909 462.064 443.521 475.662 460.545C482.46 469.057 487.802 475.922 491.443 480.658C493.264 483.027 494.66 484.863 495.6 486.107C496.071 486.728 496.427 487.202 496.666 487.521C496.785 487.68 496.875 487.8 496.935 487.88L497.003 487.971L497.02 487.994C497.023 487.999 497.025 488.001 497.426 487.702Z" stroke="url(#paint0_linear_44_6762)"/>
|
||||
<path d="M544.9 316.938L544.62 316.524L544.411 316.664L544.4 316.916C540.617 400.92 542.164 477.194 544.657 532.467C545.903 560.104 547.386 582.491 548.558 597.969C549.144 605.709 549.651 611.721 550.013 615.799C550.194 617.838 550.338 619.393 550.437 620.439C550.457 620.653 550.475 620.845 550.492 621.016C550.507 621.179 550.521 621.323 550.533 621.446C383.138 613.595 253.639 471.663 261.183 304.16C268.736 136.465 410.786 6.64343 578.46 14.1952C627.178 16.3894 663.153 22.7697 688.824 32.2372C714.495 41.7047 729.813 54.2405 737.3 68.7163C744.785 83.1862 744.495 99.6905 738.746 117.206C732.995 134.726 721.794 153.215 707.527 171.596C678.993 208.358 638.274 244.587 604.67 271.637C587.871 285.159 572.857 296.382 562.043 304.224C556.636 308.145 552.279 311.22 549.275 313.316C547.772 314.363 546.608 315.165 545.82 315.706C545.426 315.976 545.125 316.181 544.924 316.318L544.696 316.472L544.639 316.511L544.625 316.521C544.621 316.523 544.62 316.524 544.9 316.938Z" stroke="url(#paint1_linear_44_6762)"/>
|
||||
<path d="M1005.64 472.054C1005.64 472.054 784.432 497.235 512.811 488.627C512.811 488.627 42.0034 -189.771 22.0162 440.879C13.4077 712.5 226.622 939.671 498.243 948.279C769.864 956.888 997.035 743.674 1005.64 472.054Z" stroke="url(#paint2_linear_44_6762)"/>
|
||||
<path d="M1017.37 488.554C1017.37 488.554 797.162 506.577 528.018 489.5C528.018 489.5 82.3611 -198.185 42.7115 426.712C25.6344 695.855 229.975 927.883 499.119 944.96C768.263 962.037 1000.29 757.697 1017.37 488.554Z" stroke="url(#paint3_linear_44_6762)"/>
|
||||
<path d="M1027.87 504.992C1027.87 504.992 808.902 515.945 542.497 490.548C542.497 490.548 122.103 -205.518 63.1347 413.019C37.7372 679.423 233.112 915.975 499.518 941.372C765.923 966.77 1002.48 771.396 1027.87 504.992Z" stroke="url(#paint4_linear_44_6762)"/>
|
||||
<path d="M1037.17 521.325C1037.17 521.325 819.661 525.306 556.249 491.744C556.249 491.744 161.199 -211.798 83.2751 399.787C49.7134 663.196 236.043 903.939 499.454 937.501C762.865 971.063 1003.61 784.735 1037.17 521.325Z" stroke="url(#paint5_linear_44_6762)"/>
|
||||
<path d="M1045.28 537.514C1045.28 537.514 829.449 534.626 569.28 493.063C569.28 493.063 199.621 -217.053 103.122 387.004C61.5604 647.172 238.777 891.772 498.946 933.334C759.115 974.896 1003.72 797.682 1045.28 537.514Z" stroke="url(#paint6_linear_44_6762)"/>
|
||||
<path d="M1052.2 553.518C1052.2 553.518 838.268 543.87 581.581 494.479C581.581 494.479 237.331 -221.315 122.655 374.656C73.2647 631.341 241.312 879.465 497.999 928.856C754.686 978.247 1002.81 810.203 1052.2 553.518Z" stroke="url(#paint7_linear_44_6762)"/>
|
||||
<path d="M1057.96 569.293C1057.96 569.293 846.131 553.003 593.159 495.961C593.159 495.961 274.304 -224.619 141.866 362.726C84.8249 615.696 243.658 867.009 496.631 924.051C749.603 981.093 1000.92 822.262 1057.96 569.293Z" stroke="url(#paint8_linear_44_6762)"/>
|
||||
<path d="M1062.57 584.808C1062.57 584.808 853.052 562 604.019 497.493C604.019 497.493 310.515 -226.989 160.744 351.208C96.238 600.237 245.826 854.408 494.858 918.915C743.891 983.422 998.064 833.837 1062.57 584.808Z" stroke="url(#paint9_linear_44_6762)"/>
|
||||
<path d="M1066.04 600.021C1066.04 600.021 859.033 570.825 614.158 499.044C614.158 499.044 345.928 -228.462 179.271 340.082C107.492 584.954 247.814 841.651 492.689 913.431C737.564 985.211 994.263 844.893 1066.04 600.021Z" stroke="url(#paint10_linear_44_6762)"/>
|
||||
<path d="M1068.4 614.895C1068.4 614.895 864.093 579.446 623.584 500.591C623.584 500.591 380.523 -229.072 197.439 329.334C118.585 569.84 249.632 828.733 490.142 907.589C730.651 986.444 989.547 855.401 1068.4 614.895Z" stroke="url(#paint11_linear_44_6762)"/>
|
||||
<path d="M1069.66 629.394C1069.66 629.394 868.244 587.834 632.301 502.107C632.301 502.107 414.276 -228.854 215.238 318.949C129.513 554.888 251.288 815.65 487.231 901.377C723.174 987.103 983.938 865.333 1069.66 629.394Z" stroke="url(#paint12_linear_44_6762)"/>
|
||||
<path d="M1069.86 643.482C1069.86 643.482 871.505 595.958 640.32 503.569C640.32 503.569 447.166 -227.842 232.662 308.913C140.275 540.093 252.793 802.397 483.977 894.786C715.162 987.175 977.469 874.662 1069.86 643.482Z" stroke="url(#paint13_linear_44_6762)"/>
|
||||
<path d="M1068.99 657.125C1068.99 657.125 873.883 603.789 647.64 504.953C647.64 504.953 479.166 -226.073 249.693 299.208C150.859 525.446 254.144 788.971 480.387 887.807C706.63 986.643 970.157 883.363 1068.99 657.125Z" stroke="url(#paint14_linear_44_6762)"/>
|
||||
<path d="M1067.1 670.289C1067.1 670.289 875.403 611.298 654.277 506.234C654.277 506.234 510.263 -223.583 266.33 289.818C161.269 510.94 255.358 775.366 476.485 880.43C697.612 985.494 962.04 891.41 1067.1 670.289Z" stroke="url(#paint15_linear_44_6762)"/>
|
||||
<path d="M1064.2 682.937C1064.2 682.937 876.076 618.454 660.231 507.387C660.231 507.387 540.43 -220.412 282.559 280.725C171.494 496.565 256.435 761.576 472.28 872.643C688.125 983.711 953.138 898.777 1064.2 682.937Z" stroke="url(#paint16_linear_44_6762)"/>
|
||||
<path d="M1060.32 695.046C1060.32 695.046 875.921 625.237 665.515 508.394C665.515 508.394 569.653 -216.591 298.373 271.919C181.533 482.32 257.384 747.604 467.791 864.447C678.198 981.29 943.483 905.447 1060.32 695.046Z" stroke="url(#paint17_linear_44_6762)"/>
|
||||
<path d="M1055.49 706.581C1055.49 706.581 874.96 631.618 670.139 509.231C670.139 509.231 597.917 -212.16 313.768 263.38C191.385 468.195 258.214 733.444 463.035 855.831C667.856 978.218 933.107 911.396 1055.49 706.581Z" stroke="url(#paint18_linear_44_6762)"/>
|
||||
<path d="M1049.73 717.514C1049.73 717.514 873.215 637.57 674.118 509.875C674.118 509.875 625.212 -207.158 328.74 255.091C201.049 454.182 258.935 719.094 458.032 846.789C657.129 974.483 922.043 916.605 1049.73 717.514Z" stroke="url(#paint19_linear_44_6762)"/>
|
||||
<path d="M1043.07 727.816C1043.07 727.816 870.699 643.07 677.455 510.306C677.455 510.306 651.517 -201.623 343.275 247.036C210.516 440.274 259.548 704.55 452.792 837.314C646.036 970.078 910.314 921.054 1043.07 727.816Z" stroke="url(#paint20_linear_44_6762)"/>
|
||||
<path d="M1035.54 737.46C1035.54 737.46 867.439 648.092 680.168 510.5C680.168 510.5 676.823 -195.596 357.374 239.197C219.787 426.461 260.064 689.81 447.335 827.401C634.607 964.993 897.956 924.725 1035.54 737.46Z" stroke="url(#paint21_linear_44_6762)"/>
|
||||
<path d="M1027.18 746.421C1027.18 746.421 863.461 652.617 682.272 510.441C682.272 510.441 701.126 -189.111 371.034 231.558C228.864 412.74 260.494 674.873 441.683 817.048C622.872 959.224 885.006 927.603 1027.18 746.421Z" stroke="url(#paint22_linear_44_6762)"/>
|
||||
<path d="M1017.99 754.673C1017.99 754.673 858.778 656.62 683.773 510.107C683.773 510.107 724.408 -182.21 384.246 224.101C237.738 399.099 260.839 659.735 435.844 806.249C610.849 952.762 871.487 929.671 1017.99 754.673Z" stroke="url(#paint23_linear_44_6762)"/>
|
||||
<path d="M1008.03 762.193C1008.03 762.193 853.42 660.082 684.69 509.478C684.69 509.478 746.666 -174.932 397.008 216.81C246.411 385.533 261.11 644.398 429.84 795.001C598.57 945.605 857.436 930.916 1008.03 762.193Z" stroke="url(#paint24_linear_44_6762)"/>
|
||||
<path d="M997.329 768.957C997.329 768.957 847.414 662.979 685.041 508.535C685.041 508.535 767.899 -167.318 409.325 209.664C254.887 372.03 261.319 628.855 423.692 783.3C586.065 937.745 842.891 931.323 997.329 768.957Z" stroke="url(#paint25_linear_44_6762)"/>
|
||||
<path d="M985.905 774.948C985.905 774.948 840.777 665.298 684.833 507.263C684.833 507.263 788.094 -159.403 421.183 202.651C263.155 358.588 261.465 613.112 417.408 771.148C573.352 929.183 827.877 930.885 985.905 774.948Z" stroke="url(#paint26_linear_44_6762)"/>
|
||||
<path d="M973.798 780.146C973.798 780.146 833.538 667.02 684.087 505.644C684.087 505.644 807.253 -151.226 432.588 195.753C271.22 345.197 261.559 597.166 411.011 758.542C560.462 919.918 812.43 929.59 973.798 780.146Z" stroke="url(#paint27_linear_44_6762)"/>
|
||||
<path d="M961.042 784.532C961.042 784.532 825.723 668.126 682.818 503.662C682.818 503.662 825.373 -142.828 443.538 188.953C279.081 331.851 261.611 581.017 404.516 745.482C547.42 909.946 796.586 927.43 961.042 784.532Z" stroke="url(#paint28_linear_44_6762)"/>
|
||||
<path d="M947.675 788.088C947.675 788.088 817.362 668.601 681.048 501.299C681.048 501.299 842.459 -134.246 454.036 182.233C286.743 318.54 261.629 564.664 397.944 731.967C534.258 899.269 780.381 924.395 947.675 788.088Z" stroke="url(#paint29_linear_44_6762)"/>
|
||||
<path d="M933.722 790.799C933.722 790.799 808.475 668.431 678.786 498.542C678.786 498.542 858.505 -125.52 464.077 175.576C294.197 305.258 261.616 548.108 391.305 717.997C520.994 887.886 763.843 920.481 933.722 790.799Z" stroke="url(#paint30_linear_44_6762)"/>
|
||||
<path d="M919.224 792.65C919.224 792.65 799.091 667.6 676.054 495.375C676.054 495.375 873.516 -116.688 473.665 168.965C301.449 291.996 261.582 531.348 384.62 703.574C507.657 875.8 747.008 915.681 919.224 792.65Z" stroke="url(#paint31_linear_44_6762)"/>
|
||||
<path d="M904.213 793.629C904.213 793.629 789.239 666.098 672.87 491.784C672.87 491.784 887.498 -107.786 482.801 162.385C308.498 278.748 261.534 514.387 377.904 688.7C494.274 863.014 729.91 909.992 904.213 793.629Z" stroke="url(#paint32_linear_44_6762)"/>
|
||||
<path d="M888.73 793.725C888.73 793.725 778.95 663.912 669.256 487.759C669.256 487.759 900.46 -98.8532 491.493 155.819C315.351 265.506 261.484 497.225 371.178 673.378C480.872 849.531 712.588 903.413 888.73 793.725Z" stroke="url(#paint33_linear_44_6762)"/>
|
||||
<path d="M872.803 792.923C872.803 792.923 768.245 661.028 665.225 483.281C665.225 483.281 912.401 -89.93 499.736 149.246C322.001 252.259 261.432 479.86 364.452 657.606C467.472 835.353 695.068 895.936 872.803 792.923Z" stroke="url(#paint34_linear_44_6762)"/>
|
||||
<path d="M856.474 791.222C856.474 791.222 757.159 657.444 660.803 478.349C660.803 478.349 923.336 -81.0445 507.539 142.66C328.455 239.009 261.391 462.301 357.747 641.397C454.103 820.492 677.39 887.571 856.474 791.222Z" stroke="url(#paint35_linear_44_6762)"/>
|
||||
<path d="M839.771 788.61C839.771 788.61 745.712 653.147 656.001 472.945C656.001 472.945 933.267 -72.2382 514.9 136.037C334.71 225.742 261.362 444.545 351.073 624.747C440.783 804.95 659.581 878.314 839.771 788.61Z" stroke="url(#paint36_linear_44_6762)"/>
|
||||
<path d="M822.735 785.081C822.735 785.081 733.937 648.132 650.844 467.061C650.844 467.061 942.208 -63.5452 521.829 129.366C340.771 212.453 261.355 426.595 344.448 607.666C427.541 788.736 641.677 868.168 822.735 785.081Z" stroke="url(#paint37_linear_44_6762)"/>
|
||||
<path d="M805.4 780.631C805.4 780.631 721.864 642.39 645.352 460.688C645.352 460.688 950.173 -55.001 528.329 122.63C346.64 199.135 261.377 408.453 337.889 590.155C414.4 771.857 623.712 857.136 805.4 780.631Z" stroke="url(#paint38_linear_44_6762)"/>
|
||||
<path d="M787.807 775.256C787.807 775.256 709.523 635.919 639.549 453.818C639.549 453.818 957.177 -46.6395 534.41 115.813C352.324 185.781 261.439 390.123 331.413 572.223C401.387 754.323 605.721 845.224 787.807 775.256Z" stroke="url(#paint39_linear_44_6762)"/>
|
||||
<path d="M769.983 768.953C769.983 768.953 696.937 628.712 633.448 446.444C633.448 446.444 963.228 -38.4943 540.071 108.901C357.818 172.385 261.54 371.606 325.028 553.874C388.517 736.142 587.73 832.437 769.983 768.953Z" stroke="url(#paint40_linear_44_6762)"/>
|
||||
<path d="M751.968 761.722C751.968 761.722 684.137 620.767 627.074 438.557C627.074 438.557 968.344 -30.5988 545.323 101.88C363.128 158.939 261.689 352.905 318.753 535.115C375.816 717.326 569.774 818.781 751.968 761.722Z" stroke="url(#paint41_linear_44_6762)"/>
|
||||
<path d="M733.802 753.561C733.802 753.561 671.157 612.08 620.451 430.15C620.451 430.15 972.546 -22.9873 550.177 94.7332C368.263 145.435 261.898 334.021 312.605 515.951C363.311 697.882 551.888 804.263 733.802 753.561Z" stroke="url(#paint42_linear_44_6762)"/>
|
||||
<path d="M715.511 744.477C715.511 744.477 658.017 602.656 613.592 421.224C613.592 421.224 975.844 -15.6855 554.632 87.452C373.216 131.873 262.164 314.964 306.589 496.396C351.014 677.828 534.095 788.898 715.511 744.477Z" stroke="url(#paint43_linear_44_6762)"/>
|
||||
<path d="M697.137 734.47C697.137 734.47 644.752 592.493 606.526 411.772C606.526 411.772 978.265 -8.72543 558.705 80.0216C378.001 118.245 262.5 295.734 300.727 476.455C338.954 657.176 516.433 772.694 697.137 734.47Z" stroke="url(#paint44_linear_44_6762)"/>
|
||||
<path d="M678.708 723.545C678.708 723.545 631.384 581.59 599.266 401.788C599.266 401.788 979.819 -2.13872 562.397 72.427C382.613 104.542 262.906 276.335 295.025 456.136C327.144 635.937 498.924 755.66 678.708 723.545Z" stroke="url(#paint45_linear_44_6762)"/>
|
||||
<path d="M660.262 711.707C660.262 711.707 617.944 569.951 591.837 391.273C591.837 391.273 980.533 4.04751 565.719 64.6573C387.059 90.7619 263.391 256.771 289.498 435.449C315.605 614.127 481.602 737.812 660.262 711.707Z" stroke="url(#paint46_linear_44_6762)"/>
|
||||
<path d="M641.833 698.963C641.833 698.963 604.461 557.579 584.261 380.222C584.261 380.222 980.427 9.80449 568.683 56.6992C391.345 76.8967 263.96 237.046 284.159 414.402C304.359 591.758 464.495 719.16 641.833 698.963Z" stroke="url(#paint47_linear_44_6762)"/>
|
||||
<path d="M623.456 685.322C623.456 685.322 590.964 544.479 576.561 368.637C576.561 368.637 979.527 15.1071 571.302 48.5425C395.479 62.9432 264.621 217.165 279.024 393.007C293.426 568.848 447.634 699.722 623.456 685.322Z" stroke="url(#paint48_linear_44_6762)"/>
|
||||
<path d="M605.16 670.791C605.16 670.791 577.474 530.655 568.753 356.515C568.753 356.515 977.852 19.9276 573.578 40.1741C399.458 48.8943 265.375 197.132 274.096 371.272C282.817 545.411 431.04 679.511 605.16 670.791Z" stroke="url(#paint49_linear_44_6762)"/>
|
||||
<path d="M586.978 655.378C586.978 655.378 564.022 516.11 560.859 343.853C560.859 343.853 975.427 24.2389 575.526 31.5807C403.289 34.7428 266.227 176.948 269.389 349.205C272.552 521.461 414.741 658.54 586.978 655.378Z" stroke="url(#paint50_linear_44_6762)"/>
|
||||
<path d="M568.945 639.102C568.945 639.102 550.635 500.86 552.903 330.662C552.903 330.662 972.279 28.0246 577.159 22.7591C406.981 20.4913 267.185 156.626 264.917 326.824C262.649 497.023 398.767 636.834 568.945 639.102Z" stroke="url(#paint51_linear_44_6762)"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_44_6762" x1="975.068" y1="971.654" x2="158.171" y2="-59.5219" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_44_6762" x1="234.918" y1="595.611" x2="888.435" y2="122.632" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_44_6762" x1="969.849" y1="983.645" x2="191.272" y2="-62.3511" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint3_linear_44_6762" x1="965.783" y1="994.863" x2="226.414" y2="-67.1101" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint4_linear_44_6762" x1="960.794" y1="1005.54" x2="260.858" y2="-70.9887" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint5_linear_44_6762" x1="954.912" y1="1015.63" x2="294.584" y2="-74.0172" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint6_linear_44_6762" x1="948.167" y1="1025.13" x2="327.57" y2="-76.2248" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint7_linear_44_6762" x1="940.578" y1="1034.01" x2="359.785" y2="-77.6446" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint8_linear_44_6762" x1="932.176" y1="1042.22" x2="391.213" y2="-78.3116" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint9_linear_44_6762" x1="922.993" y1="1049.77" x2="421.836" y2="-78.2506" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint10_linear_44_6762" x1="913.05" y1="1056.62" x2="451.625" y2="-77.4993" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint11_linear_44_6762" x1="902.381" y1="1062.75" x2="480.569" y2="-76.0904" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint12_linear_44_6762" x1="891.015" y1="1068.14" x2="508.648" y2="-74.0587" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint13_linear_44_6762" x1="878.987" y1="1072.78" x2="535.851" y2="-71.4376" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint14_linear_44_6762" x1="866.321" y1="1076.64" x2="562.158" y2="-68.263" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint15_linear_44_6762" x1="853.057" y1="1079.7" x2="587.561" y2="-64.5697" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint16_linear_44_6762" x1="839.217" y1="1081.96" x2="612.043" y2="-60.3967" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint17_linear_44_6762" x1="824.839" y1="1083.4" x2="635.596" y2="-55.7727" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint18_linear_44_6762" x1="809.955" y1="1084" x2="658.212" y2="-50.7363" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint19_linear_44_6762" x1="794.602" y1="1083.76" x2="679.886" y2="-45.3238" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint20_linear_44_6762" x1="778.804" y1="1082.65" x2="700.605" y2="-39.5709" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint21_linear_44_6762" x1="762.601" y1="1080.69" x2="720.368" y2="-33.515" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint22_linear_44_6762" x1="746.029" y1="1077.85" x2="739.174" y2="-27.1893" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint23_linear_44_6762" x1="729.113" y1="1074.13" x2="757.014" y2="-20.6319" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint24_linear_44_6762" x1="711.892" y1="1069.52" x2="773.891" y2="-13.8777" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint25_linear_44_6762" x1="694.403" y1="1064.02" x2="789.808" y2="-6.96515" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint26_linear_44_6762" x1="676.671" y1="1057.63" x2="804.76" y2="0.0752894" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint27_linear_44_6762" x1="658.735" y1="1050.34" x2="818.752" y2="7.20731" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint28_linear_44_6762" x1="640.626" y1="1042.16" x2="831.79" y2="14.3957" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint29_linear_44_6762" x1="622.383" y1="1033.08" x2="843.881" y2="21.6053" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint30_linear_44_6762" x1="604.031" y1="1023.11" x2="855.025" y2="28.8025" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint31_linear_44_6762" x1="585.605" y1="1012.25" x2="865.234" y2="35.9531" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint32_linear_44_6762" x1="567.14" y1="1000.51" x2="874.516" y2="43.0247" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint33_linear_44_6762" x1="548.669" y1="987.883" x2="882.885" y2="49.9854" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint34_linear_44_6762" x1="530.219" y1="974.38" x2="890.344" y2="56.7978" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint35_linear_44_6762" x1="511.827" y1="960.016" x2="896.914" y2="63.4397" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint36_linear_44_6762" x1="493.516" y1="944.793" x2="902.6" y2="69.8743" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint37_linear_44_6762" x1="475.32" y1="928.723" x2="907.42" y2="76.0735" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint38_linear_44_6762" x1="457.27" y1="911.816" x2="911.389" y2="82.0065" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint39_linear_44_6762" x1="439.398" y1="894.085" x2="914.528" y2="87.6454" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint40_linear_44_6762" x1="421.725" y1="875.541" x2="916.847" y2="92.9622" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint41_linear_44_6762" x1="404.284" y1="856.199" x2="918.367" y2="97.9289" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint42_linear_44_6762" x1="387.104" y1="836.071" x2="919.112" y2="102.517" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint43_linear_44_6762" x1="370.205" y1="815.178" x2="919.093" y2="106.707" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint44_linear_44_6762" x1="353.62" y1="793.536" x2="918.34" y2="110.473" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint45_linear_44_6762" x1="337.366" y1="771.159" x2="916.865" y2="113.788" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint46_linear_44_6762" x1="321.473" y1="748.067" x2="914.696" y2="116.632" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint47_linear_44_6762" x1="305.962" y1="724.278" x2="911.855" y2="118.981" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint48_linear_44_6762" x1="290.86" y1="699.814" x2="908.37" y2="120.817" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint49_linear_44_6762" x1="276.183" y1="674.694" x2="904.259" y2="122.118" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint50_linear_44_6762" x1="261.953" y1="648.936" x2="899.548" y2="122.863" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint51_linear_44_6762" x1="248.194" y1="622.569" x2="894.267" y2="123.041" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D375FF"/>
|
||||
<stop offset="0.708333" stop-color="#798FFF"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 17 KiB |
@@ -0,0 +1,30 @@
|
||||
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap");
|
||||
@import url("https://gistcdn.githack.com/mfd/09b70eb47474836f25a21660282ce0fd/raw/e06a670afcb2b861ed2ac4a1ef752d062ef6b46b/Gilroy.css");
|
||||
|
||||
.bg-gradient-card {
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgba(20, 22, 31, 0.27) 45.71%,
|
||||
rgba(20, 22, 31, 0.9) 100%
|
||||
);
|
||||
}
|
||||
|
||||
.feedback-field:focus ~ .feedback-placeholder {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.feedback-field:focus ~ .feedback-placeholder-2 {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.feedback-field:valid ~ .feedback-placeholder {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.feedback-field:valid ~ .feedback-placeholder-2 {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.feedback-field::placeholder {
|
||||
@apply lg:text-base text-sm font-semibold text-[#77787d];
|
||||
}
|
||||
@@ -0,0 +1,460 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
/* eslint-disable no-irregular-whitespace */
|
||||
import "./App.css";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import FeedbackForm from "./components/FeedbackForm";
|
||||
import ArrowRightIcon from "./components/icons/ArrowRightIcon";
|
||||
import LogoIcon from "./components/icons/LogoIcon";
|
||||
import MailIcon from "./components/icons/MailIcon";
|
||||
import PhoneIcon from "./components/icons/PhoneIcon";
|
||||
import TelegramIcon from "./components/icons/TelegramIcon";
|
||||
import VKIcon from "./components/icons/VKIcon";
|
||||
import YouTubeIcon from "./components/icons/YouTubeIcon";
|
||||
import Sidebar from "./components/Sidebar";
|
||||
import Header from "./components/Header";
|
||||
import { Transition } from "react-transition-group";
|
||||
import useSidebarStore from "./stores/useSidebarStore";
|
||||
import { useEffect, useState } from "react";
|
||||
import ky from "ky";
|
||||
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||
import { ToastContainer, toast } from "react-toastify";
|
||||
|
||||
function App() {
|
||||
const [isOpen] = useSidebarStore((state) => [state.isOpen]);
|
||||
|
||||
const navigate = useNavigate();
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [countdownTimer, setCountdownTimer] = useState(10);
|
||||
// const host = window.location.host;
|
||||
|
||||
const [searchParams] = useSearchParams();
|
||||
// const location = searchParams.get("location") || "a1";
|
||||
|
||||
const { t, i18n } = useTranslation();
|
||||
const location = i18n.language === "ru" ? "a1" : "a2";
|
||||
const build = searchParams.get("build") || null;
|
||||
|
||||
function toastError(text: string) {
|
||||
toast.error(text, {
|
||||
position: "bottom-right",
|
||||
autoClose: 5000,
|
||||
hideProgressBar: false,
|
||||
closeOnClick: true,
|
||||
pauseOnHover: true,
|
||||
draggable: true,
|
||||
progress: undefined,
|
||||
theme: "dark",
|
||||
});
|
||||
}
|
||||
|
||||
async function startStream(title: string) {
|
||||
setLoading(true);
|
||||
|
||||
const response: { stream: string } = await ky
|
||||
.get(
|
||||
`${
|
||||
import.meta.env.VITE_COORD_URL
|
||||
}/start?location=${location}&title=${title}`
|
||||
)
|
||||
.json();
|
||||
|
||||
if (response.stream) {
|
||||
setInterval(() => {
|
||||
setCountdownTimer((prev) => prev - 1);
|
||||
}, 1000);
|
||||
|
||||
setTimeout(() => {
|
||||
navigate(`/stream/${response.stream}`);
|
||||
}, 10000);
|
||||
} else {
|
||||
setLoading(false);
|
||||
toastError("Превышен лимит одновременных сессий, попробуйте позже.");
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (build) {
|
||||
void startStream(build);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("title");
|
||||
}, [i18n.language]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{!loading ? (
|
||||
<>
|
||||
<div className="min-h-screen bg-[#14161F] text-white overflow-hidden">
|
||||
<div className="container mx-auto 2xl:px-10 lg:px-8 sm:px-6 px-4 max-w-[1600px]">
|
||||
<Header
|
||||
handleChangeLang={(lang) => void i18n.changeLanguage(lang)}
|
||||
/>
|
||||
|
||||
<div className="2xl:mt-[72px] lg:mt-16 sm:mt-[88px] mt-14 relative">
|
||||
<div className="absolute flex justify-center items-center w-full -top-8 blur-sm">
|
||||
<img src="/images/shapes/shape-1.svg" alt="" className="" />
|
||||
</div>
|
||||
|
||||
<div className="grid sm:grid-cols-2 lg:gap-4 sm:gap-3 gap-4">
|
||||
<p className="2xl:text-[64px] xl:text-[52px] text-[40px] text-gradient w-fit font-gilroy leading-none font-medium">
|
||||
<Trans i18nKey={"main.title"}>
|
||||
Доступные
|
||||
<br />
|
||||
демонстрации
|
||||
</Trans>
|
||||
</p>
|
||||
<p className="lg:w-[368px] lg:text-base text-sm">
|
||||
<Trans i18nKey={"main.desc"}>
|
||||
Клиент из любой точки мира может посмотреть жилой
|
||||
комплекс, даже на нулевом этапе строительства. Он выберет
|
||||
лучшую планировку и оценит вид из окон своей будущей
|
||||
квартиры.
|
||||
</Trans>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="sm:mt-16 mt-8 grid sm:grid-cols-2 lg:gap-4 sm:gap-3 gap-2">
|
||||
<div
|
||||
className="group relative sm:h-full h-[264px] bg-gray-700 bg-no-repeat bg-center bg-cover"
|
||||
style={{ backgroundImage: `url("/images/cards/nks.jpg")` }}
|
||||
>
|
||||
<div className="bg-gradient-card h-full transition-opacity group-hover:opacity-60 duration-300"></div>
|
||||
|
||||
<div className="p-6 absolute bottom-0 space-y-6">
|
||||
<div>
|
||||
<p className="xl:text-2xl text-xl font-gilroy font-semibold">
|
||||
<Trans i18nKey={"main.cards.title1"}>
|
||||
МФК «Revolution towers»
|
||||
</Trans>
|
||||
</p>
|
||||
<p className="lg:text-sm text-xs">
|
||||
<Trans i18nKey={"main.cards.city1"}>
|
||||
Россия, Екатеринбург
|
||||
</Trans>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={() => void startStream("nksJukovaDev")}
|
||||
className="flex bg-gradient rounded-full p-2 gap-0 group-hover:gap-1 group-hover:pr-4 group-hover:pl-6 transition-all duration-300"
|
||||
>
|
||||
<span className="font-medium w-0 opacity-0 group-hover:w-[82px] group-hover:opacity-100 overflow-hidden transition-all duration-300">
|
||||
<Trans i18nKey={"main.cards.button"}>Запустить</Trans>
|
||||
</span>
|
||||
<ArrowRightIcon />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid lg:grid-cols-2 lg:gap-4 sm:gap-3 gap-2">
|
||||
{i18n.language === "ru" ? (
|
||||
<>
|
||||
<div
|
||||
className="group relative lg:h-[508px] sm:h-[284px] h-[264px] col-span-1 bg-gray-700 bg-no-repeat bg-center bg-cover"
|
||||
style={{
|
||||
backgroundImage: `url("/images/cards/liferes.jpg")`,
|
||||
}}
|
||||
>
|
||||
<div className="bg-gradient-card h-full transition-opacity group-hover:opacity-50 duration-300"></div>
|
||||
|
||||
<div className="p-6 absolute bottom-0 space-y-6">
|
||||
<div>
|
||||
<p className="xl:text-2xl text-xl font-gilroy font-semibold">
|
||||
<Trans i18nKey={"main.cards.title2"}>
|
||||
ЖК «Life Резиденция»
|
||||
</Trans>
|
||||
</p>
|
||||
<p className="lg:text-sm text-xs">
|
||||
<Trans i18nKey={"main.cards.city2"}>
|
||||
Россия, Тюмень
|
||||
</Trans>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={() => void startStream("lifeResidence")}
|
||||
className="flex bg-gradient rounded-full p-2 gap-0 group-hover:gap-1 group-hover:pr-4 group-hover:pl-6 transition-all duration-300"
|
||||
>
|
||||
<span className="font-medium w-0 opacity-0 group-hover:w-[82px] group-hover:opacity-100 overflow-hidden transition-all duration-300">
|
||||
<Trans i18nKey={"main.cards.button"}>
|
||||
Запустить
|
||||
</Trans>
|
||||
</span>
|
||||
<ArrowRightIcon />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="group relative lg:h-[508px] sm:h-[284px] h-[264px] col-span-1 bg-gray-700 bg-no-repeat bg-center bg-cover"
|
||||
style={{
|
||||
backgroundImage: `url("/images/cards/aivaz.jpg")`,
|
||||
}}
|
||||
>
|
||||
<div className="bg-gradient-card h-full transition-opacity group-hover:opacity-90 duration-300"></div>
|
||||
|
||||
<div className="p-6 absolute bottom-0 space-y-6">
|
||||
<div>
|
||||
<p className="xl:text-2xl text-xl font-gilroy font-semibold">
|
||||
<Trans i18nKey={"main.cards.title3"}>
|
||||
ЖК «Айвазовский City»
|
||||
</Trans>
|
||||
</p>
|
||||
<p className="lg:text-sm text-xs">
|
||||
<Trans i18nKey={"main.cards.city2"}>
|
||||
Россия, Тюмень
|
||||
</Trans>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={() => void startStream("Ivazowsky")}
|
||||
className="flex bg-gradient rounded-full p-2 gap-0 group-hover:gap-1 group-hover:pr-4 group-hover:pl-6 transition-all duration-300"
|
||||
>
|
||||
<span className="font-medium w-0 opacity-0 group-hover:w-[82px] group-hover:opacity-100 overflow-hidden transition-all duration-300">
|
||||
<Trans i18nKey={"main.cards.button"}>
|
||||
Запустить
|
||||
</Trans>
|
||||
</span>
|
||||
<ArrowRightIcon />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div
|
||||
className="group relative lg:h-[508px] sm:h-[284px] h-[264px] col-span-2 bg-gray-700 bg-no-repeat bg-center bg-cover"
|
||||
style={{
|
||||
backgroundImage: `url("/images/cards/shipyard.jpg")`,
|
||||
}}
|
||||
>
|
||||
<div className="bg-gradient-card h-full transition-opacity group-hover:opacity-90 duration-300"></div>
|
||||
|
||||
<div className="p-6 absolute bottom-0 space-y-6">
|
||||
<div>
|
||||
<p className="xl:text-2xl text-xl font-gilroy font-semibold">
|
||||
{/* <Trans i18nKey={"main.cards.title3"}> */}
|
||||
IMI Saudi Shipyard
|
||||
{/* </Trans> */}
|
||||
</p>
|
||||
<p className="lg:text-sm text-xs">
|
||||
{/* <Trans i18nKey={"main.cards.city2"}> */}
|
||||
Saudi Arabia
|
||||
{/* </Trans> */}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={() => void startStream("ShipyardSaudiDev")}
|
||||
className="flex bg-gradient rounded-full p-2 gap-0 group-hover:gap-1 group-hover:pr-4 group-hover:pl-6 transition-all duration-300"
|
||||
>
|
||||
<span className="font-medium w-0 opacity-0 group-hover:w-[82px] group-hover:opacity-100 overflow-hidden transition-all duration-300">
|
||||
<Trans i18nKey={"main.cards.button"}>
|
||||
Запустить
|
||||
</Trans>
|
||||
</span>
|
||||
<ArrowRightIcon />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="lg:mt-40 sm:mt-[120px] mt-[88px]">
|
||||
<div className="grid lg:grid-cols-4 grid-cols-1 lg:gap-4 gap-6">
|
||||
<div className="col-span-1">
|
||||
<div className="grid lg:grid-cols-1 sm:grid-cols-2 lg:gap-6 gap-4">
|
||||
<p className="2xl:text-[64px] xl:text-5xl text-[40px] font-gilroy text-gradient font-medium w-fit leading-none">
|
||||
<Trans i18nKey={"feedback.title"}>
|
||||
Свяжитесь
|
||||
<br />с нами
|
||||
</Trans>
|
||||
</p>
|
||||
<p className="2xl:text-xl lg:text-lg font-gilroy font-semibold leading-tight">
|
||||
<Trans i18nKey={"feedback.desc"}>
|
||||
Хотите увеличить конверсию?
|
||||
<br />
|
||||
Давайте обсудим детали!
|
||||
</Trans>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="lg:col-span-3">
|
||||
<FeedbackForm />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-[104px] relative">
|
||||
<div className="absolute flex justify-center items-center w-full -top-8 left-32 blur-md">
|
||||
<img src="/images/shapes/shape-2.svg" alt="" className="" />
|
||||
</div>
|
||||
|
||||
<div className="relative grid lg:grid-cols-4 gap-4">
|
||||
<div className="flex gap-4">
|
||||
<p className="2xl:text-xl font-gilroy font-semibold">
|
||||
<Trans i18nKey={"contacts.title"}>Горячая линия</Trans>
|
||||
</p>
|
||||
<div className="w-full h-px bg-[#3D425C]"></div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2 2xl:pr-4 xl:pr-2">
|
||||
<a
|
||||
href="mailto:info@graff.tech"
|
||||
className="2xl:h-16 h-14 px-6 py-4 2xl:text-base text-sm border rounded-full font-medium flex justify-between items-center w-full border-[#52587A] opacity-80 hover:opacity-100 transition-all"
|
||||
>
|
||||
<span>
|
||||
<Trans i18nKey={"contacts.button1"}>Написать</Trans>
|
||||
</span>
|
||||
<MailIcon className="lg:w-8 lg:h-8 w-6 h-6" />
|
||||
</a>
|
||||
<a
|
||||
href="tel:88007700076"
|
||||
className="2xl:h-16 h-14 px-6 py-4 2xl:text-base text-sm border rounded-full font-medium flex justify-between items-center w-full border-[#52587A] opacity-80 hover:opacity-100 transition-all"
|
||||
>
|
||||
<span>
|
||||
<Trans i18nKey={"contacts.button2"}>Позвонить</Trans>
|
||||
</span>
|
||||
<PhoneIcon className="lg:w-8 lg:h-8 w-6 h-6" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="sm:col-span-2 flex sm:justify-end lg:mt-0 mt-10">
|
||||
<div className="lg:w-auto sm:w-1/2 w-full flex justify-between 2xl:gap-8 lg:gap-6 gap-4">
|
||||
<p className="2xl:text-xl font-gilroy font-semibold 2xl:-mt-1.5 -mt-1">
|
||||
<Trans i18nKey={"contacts.social.title"}>
|
||||
Социальные
|
||||
<br />
|
||||
сети
|
||||
</Trans>
|
||||
</p>
|
||||
<div className="flex gap-2 h-fit">
|
||||
<a
|
||||
href="https://www.youtube.com/@GRAFFtech"
|
||||
target="_blank"
|
||||
className="group border border-[#3D425C] xl:p-4 p-3 rounded-full hover:border-[#52587A] transition-all"
|
||||
>
|
||||
<YouTubeIcon className="2xl:w-8 2xl:h-8 w-6 h-6" />
|
||||
</a>
|
||||
<a
|
||||
href="https://vk.com/graff.interactive"
|
||||
target="_blank"
|
||||
className="group border border-[#3D425C] xl:p-4 p-3 rounded-full hover:border-[#52587A] transition-all"
|
||||
>
|
||||
<VKIcon className="2xl:w-8 2xl:h-8 w-6 h-6" />
|
||||
</a>
|
||||
<a
|
||||
href="https://t.me/GRAFFinteractive"
|
||||
target="_blank"
|
||||
className="border rounded-full border-[#52587A] xl:p-4 p-3 opacity-80 hover:opacity-100 transition-all"
|
||||
>
|
||||
<TelegramIcon className="2xl:w-8 2xl:h-8 w-6 h-6" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-[50px] relative border-t border-[#3D425C] text-sm">
|
||||
<div className="container mx-auto xl:px-8 max-w-[1600px]">
|
||||
<div className="grid lg:grid-cols-4">
|
||||
<div className="sm:col-span-2 lg:order-none order-last py-6 xl:px-0 px-6 flex sm:flex-row flex-col sm:gap-6 gap-4 lg:border-t-0 border-t border-[#3D425C]">
|
||||
<div>
|
||||
<LogoIcon />
|
||||
</div>
|
||||
<div className="flex flex-col sm:gap-1 gap-4">
|
||||
<p className="flex sm:flex-row flex-col sm:gap-4 gap-1">
|
||||
<a href="#" className="">
|
||||
<Trans i18nKey={"footer.link"}>
|
||||
Политика конфиденциальности
|
||||
</Trans>
|
||||
</a>
|
||||
<a href="https://estate.graff.tech" className="">
|
||||
estate.graff.tech
|
||||
</a>
|
||||
</p>
|
||||
<p className="text-xs text-[#C5C7CE]">
|
||||
© 2023 GRAFF interactive.{" "}
|
||||
<Trans i18nKey={"footer.text"}>
|
||||
Все права защищены.
|
||||
</Trans>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col-span-1 lg:border-l sm:border-b-0 border-b border-[#3D425C] xl:px-8 sm:px-6 px-4 py-6 flex flex-col justify-center">
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="text-[#EBEBEB] flex flex-col gap-1">
|
||||
<a href="mailto:info@graff.tech">info@graff.tech</a>
|
||||
<a href="tel:88007700076">8 800 770 00 76</a>
|
||||
</div>
|
||||
<div className="w-12 h-12 border border-[#3D425C] rounded-full flex justify-center items-center">
|
||||
RU
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col-span-1 sm:border-l border-[#3D425C] xl:pl-8 xl:pr-0 sm:px-6 px-4 py-6 flex flex-col justify-center">
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="text-[#EBEBEB] flex flex-col gap-1">
|
||||
<a href="mailto:waseem@graff.tech">waseem@graff.tech</a>
|
||||
<a href="tel:+971509388902">+971 50 938 8902</a>
|
||||
</div>
|
||||
<div className="w-12 h-12 border border-[#3D425C] rounded-full flex justify-center items-center">
|
||||
UAE
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Transition in={isOpen} timeout={200} mountOnEnter unmountOnExit>
|
||||
{(state) => <Sidebar className={state} />}
|
||||
</Transition>
|
||||
|
||||
<ToastContainer />
|
||||
</>
|
||||
) : (
|
||||
<div className="bg-[#14161F] h-screen flex justify-center items-center">
|
||||
<p className="self-center text-white flex items-center w-fit font-gilroy">
|
||||
<svg
|
||||
className="animate-spin -ml-1 mr-3 h-5 w-5 text-white"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<circle
|
||||
className="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
strokeWidth="4"
|
||||
></circle>
|
||||
<path
|
||||
className="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
></path>
|
||||
</svg>
|
||||
<span>
|
||||
<Trans i18nKey="loading">Загрузка</Trans>
|
||||
... {countdownTimer} <Trans i18nKey="loadingSub">сек</Trans>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
@@ -0,0 +1,45 @@
|
||||
.react-calendar {
|
||||
font-family: "Gilroy";
|
||||
@apply bg-[#151619] text-white border-none p-4
|
||||
rounded-lg;
|
||||
}
|
||||
|
||||
.react-calendar__navigation button:enabled:hover,
|
||||
.react-calendar__navigation button:enabled:focus,
|
||||
.react-calendar__tile:enabled:hover {
|
||||
@apply bg-[rgba(255,255,255,0.05)];
|
||||
}
|
||||
|
||||
.react-calendar__tile:enabled:focus {
|
||||
@apply bg-[rgba(255,255,255,0.2)];
|
||||
}
|
||||
|
||||
.react-calendar__month-view__days__day--weekend {
|
||||
@apply text-[#F2F2F2];
|
||||
}
|
||||
|
||||
.react-calendar__month-view__weekdays {
|
||||
@apply font-normal lowercase text-base;
|
||||
}
|
||||
|
||||
abbr[title] {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.react-calendar__navigation button,
|
||||
.react-calendar__tile {
|
||||
@apply rounded;
|
||||
}
|
||||
|
||||
.react-calendar__tile--now:enabled {
|
||||
background-color: #212121;
|
||||
}
|
||||
|
||||
.react-calendar__tile--active {
|
||||
background: #1087ff !important;
|
||||
}
|
||||
|
||||
.react-calendar__navigation button:disabled,
|
||||
.react-calendar__tile:disabled {
|
||||
@apply text-zinc-800 bg-transparent;
|
||||
}
|
||||
@@ -0,0 +1,361 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { FormEvent, useEffect, useState } from "react";
|
||||
import Calendar from "react-calendar";
|
||||
import "react-calendar/dist/Calendar.css";
|
||||
import "./CalendarPage.css";
|
||||
import {
|
||||
format,
|
||||
isBefore,
|
||||
differenceInBusinessDays,
|
||||
setHours,
|
||||
getHours,
|
||||
setMinutes,
|
||||
getMinutes,
|
||||
setSeconds,
|
||||
setMilliseconds,
|
||||
} from "date-fns";
|
||||
import ru from "date-fns/locale/ru";
|
||||
import ky from "ky";
|
||||
import { useParams } from "react-router-dom";
|
||||
import InputMask from "react-input-mask";
|
||||
|
||||
function CalendarPage() {
|
||||
const params = useParams();
|
||||
const [step, setStep] = useState<number>(1);
|
||||
const [date, setDate] = useState<Date>(new Date());
|
||||
const [name, setName] = useState<string>("");
|
||||
const [email, setEmail] = useState<string>("");
|
||||
const [phone, setPhone] = useState<string>("");
|
||||
const [link, setLink] = useState<string>("");
|
||||
const [datesAndTimes, setDatesAndTimes] = useState<any[]>([]);
|
||||
const [scheduledSessions, setScheduledSessions] = useState<any[]>([]);
|
||||
|
||||
async function selectDate(value: any) {
|
||||
await getScheduledSessions(value);
|
||||
|
||||
setDate(value);
|
||||
setStep((prev) => prev + 1);
|
||||
}
|
||||
|
||||
async function getScheduledSessions(value: Date) {
|
||||
const username = params.username;
|
||||
|
||||
const result: any[] = await ky
|
||||
.get(
|
||||
`${
|
||||
import.meta.env.VITE_COORD_URL
|
||||
}/scheduled_sessions/${username}?date=${value.toISOString()}`
|
||||
)
|
||||
.json();
|
||||
|
||||
console.log(scheduledSessions);
|
||||
|
||||
setScheduledSessions(result);
|
||||
}
|
||||
|
||||
function selectTime(value: any) {
|
||||
let newDate = date;
|
||||
newDate = setHours(date, getHours(value));
|
||||
newDate = setMinutes(newDate, getMinutes(value));
|
||||
newDate = setSeconds(newDate, 0);
|
||||
newDate = setMilliseconds(newDate, 0);
|
||||
|
||||
setDate(newDate);
|
||||
setStep((prev) => prev + 1);
|
||||
}
|
||||
|
||||
async function addSchedule(e: FormEvent) {
|
||||
e.preventDefault();
|
||||
|
||||
const username = params.username;
|
||||
const title = "nksJukovaDev";
|
||||
const startAt = date;
|
||||
|
||||
try {
|
||||
const result: any = await ky
|
||||
.post(`${import.meta.env.VITE_COORD_URL}/scheduled_sessions`, {
|
||||
json: {
|
||||
username,
|
||||
name,
|
||||
email,
|
||||
phone,
|
||||
title,
|
||||
startAt,
|
||||
},
|
||||
})
|
||||
.json();
|
||||
|
||||
if (!result.userInviteLink) {
|
||||
alert(result.error);
|
||||
return;
|
||||
}
|
||||
|
||||
setLink(result.userInviteLink);
|
||||
setName("");
|
||||
setEmail("");
|
||||
setPhone("");
|
||||
setStep(4);
|
||||
} catch (error: any) {
|
||||
alert(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
async function getSessionScheduleSettings() {
|
||||
const username = params.username;
|
||||
|
||||
try {
|
||||
const result: any = await ky
|
||||
.get(
|
||||
`${
|
||||
import.meta.env.VITE_COORD_URL
|
||||
}/session_schedule_settings/${username}`
|
||||
)
|
||||
.json();
|
||||
|
||||
if (result.error) {
|
||||
console.log("Error: ", result.error);
|
||||
return;
|
||||
}
|
||||
|
||||
const { datesAndTimes } = result;
|
||||
|
||||
console.log(datesAndTimes);
|
||||
|
||||
setDatesAndTimes(datesAndTimes);
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
console.log(error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getSessionScheduleSettings();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex justify-center items-center p-8 rounded-lg text-white">
|
||||
{step === 1 && (
|
||||
<div className="space-y-8 w-80">
|
||||
<p className="text-4xl font-gilroy">
|
||||
Выберите
|
||||
<br />
|
||||
дату
|
||||
</p>
|
||||
<Calendar onChange={selectDate} value={date} minDate={new Date()} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{step === 2 && (
|
||||
<div className="space-y-8">
|
||||
<p className="text-4xl font-gilroy">
|
||||
Выберите
|
||||
<br />
|
||||
время
|
||||
</p>
|
||||
|
||||
<div className="space-y-4">
|
||||
<button
|
||||
onClick={() => setStep((prev) => prev - 1)}
|
||||
className="text-[#C5C7CE] flex items-center gap-1 bg-[#1C1D21] p-1 pr-4 text-xs rounded"
|
||||
>
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M15 19L8 12L15 5"
|
||||
stroke="#C5C7CE"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
<span>Выбор даты</span>
|
||||
</button>
|
||||
|
||||
<p className="text-xl font-gilroy">
|
||||
{format(date, "dd MMMM", { locale: ru })}
|
||||
</p>
|
||||
|
||||
<div className="grid grid-cols-4">
|
||||
{datesAndTimes.map(
|
||||
(dateAndTime: { value: Date; active: true }, index: number) =>
|
||||
!differenceInBusinessDays(date, new Date()) ? (
|
||||
isBefore(new Date(), new Date(dateAndTime.value)) && (
|
||||
<button
|
||||
key={index}
|
||||
onClick={() => selectTime(new Date(dateAndTime.value))}
|
||||
className="px-3 py-2 text-center rounded hover:bg-[#23242A] disabled:hover:bg-inherit disabled:opacity-25"
|
||||
disabled={
|
||||
scheduledSessions.filter(
|
||||
(session) => session.startAt === dateAndTime.value
|
||||
).length >= 3
|
||||
}
|
||||
>
|
||||
{format(new Date(dateAndTime.value), "HH:mm")}
|
||||
</button>
|
||||
)
|
||||
) : (
|
||||
<button
|
||||
key={index}
|
||||
onClick={() => selectTime(new Date(dateAndTime.value))}
|
||||
className="px-3 py-2 text-center rounded hover:bg-[#23242A] disabled:hover:bg-inherit disabled:opacity-25"
|
||||
disabled={
|
||||
scheduledSessions.filter(
|
||||
(session) => session.startAt === dateAndTime.value
|
||||
).length >= 3
|
||||
}
|
||||
>
|
||||
{format(new Date(dateAndTime.value), "HH:mm")}
|
||||
</button>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{step === 3 && (
|
||||
<div className="space-y-8 w-80">
|
||||
<p className="text-4xl font-gilroy">
|
||||
Расскажите
|
||||
<br />о себе
|
||||
</p>
|
||||
|
||||
<div className="space-y-4">
|
||||
<button
|
||||
onClick={() => setStep((prev) => prev - 1)}
|
||||
className="text-[#C5C7CE] flex items-center gap-1 bg-[#1C1D21] p-1 pr-4 text-xs rounded"
|
||||
>
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M15 19L8 12L15 5"
|
||||
stroke="#C5C7CE"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
<span>Выбор времени</span>
|
||||
</button>
|
||||
|
||||
<p className="text-xl font-gilroy">
|
||||
{format(date, "dd MMMM HH:mm", { locale: ru })}
|
||||
</p>
|
||||
|
||||
<form onSubmit={addSchedule} className="space-y-12">
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-1">
|
||||
<p className="text-[#C5C7CE]">Имя</p>
|
||||
<input
|
||||
required
|
||||
type="text"
|
||||
className="px-4 py-3 bg-[#23242A] rounded outline-none w-full"
|
||||
placeholder="Константин"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1">
|
||||
<p className="text-[#C5C7CE]">Email</p>
|
||||
<input
|
||||
required
|
||||
type="email"
|
||||
className="px-4 py-3 bg-[#23242A] rounded outline-none w-full"
|
||||
placeholder="example@mail.ru"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1">
|
||||
<p className="text-[#C5C7CE]">Телефон</p>
|
||||
<InputMask
|
||||
mask={"+999999999999999"}
|
||||
maskChar={null}
|
||||
required
|
||||
type="tel"
|
||||
className="px-4 py-3 bg-[#23242A] rounded outline-none w-full"
|
||||
placeholder="+79009998877"
|
||||
value={phone}
|
||||
onChange={(e) => setPhone(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
className="px-4 py-2 bg-gradient w-full rounded"
|
||||
>
|
||||
Запланировать
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{step === 4 && (
|
||||
<div className="space-y-8 w-80">
|
||||
<p className="text-4xl font-gilroy">
|
||||
Просмотр
|
||||
<br />
|
||||
запланирован
|
||||
</p>
|
||||
|
||||
<div className="space-y-12">
|
||||
<p className="text-[#C5C7CE]">
|
||||
Ссылка для подключения и другая дополнительная информация будут
|
||||
отправлены на ваш почтовый адрес.
|
||||
</p>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-1">
|
||||
<p>Скопируйте ссылку для поключения</p>
|
||||
<input
|
||||
type="text"
|
||||
className="px-4 py-3 bg-[#23242A] rounded outline-none w-full"
|
||||
defaultValue={link}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<p className="text-center">или</p>
|
||||
|
||||
<a
|
||||
href={link}
|
||||
target="_blank"
|
||||
className="inline-block px-4 py-2 bg-gradient w-full rounded text-center"
|
||||
>
|
||||
Подключиться
|
||||
</a>
|
||||
|
||||
<div className="h-0.5 bg-[#23242A]"></div>
|
||||
|
||||
<a
|
||||
onClick={() => setStep(1)}
|
||||
href="#"
|
||||
className="inline-block px-4 py-2 w-full rounded text-center bg-[#1C1D21] text-[#C5C7CE]"
|
||||
>
|
||||
На сайт жилого комплекса
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default CalendarPage;
|
||||
@@ -0,0 +1,39 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import ky from "ky";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
function HistoryPage() {
|
||||
const [history, setHistory] = useState([]);
|
||||
|
||||
async function getHistory() {
|
||||
const result: any = await ky
|
||||
.get(`${import.meta.env.VITE_COORD_URL}/session_history`)
|
||||
.json();
|
||||
|
||||
setHistory(result);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getHistory();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="text-[#F2F2F2] space-y-4 p-4">
|
||||
<p>Всего сессий: {history.length}</p>
|
||||
{history.map((item: any) => (
|
||||
<div key={item.id} className="p-4 rounded-lg bg-[#22222A]">
|
||||
<p>
|
||||
Дата и время запуска: {new Date(item.createdAt).toLocaleString()}
|
||||
</p>
|
||||
<p>Сборка: "{item.title}"</p>
|
||||
<p>Сервер: "{item.server}"</p>
|
||||
<p>IP клиента: {item.headers["x-forwarded-for"]}</p>
|
||||
<p>Город: {item.city}</p>
|
||||
<p>Устройство: {item.headers["user-agent"]}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default HistoryPage;
|
||||
@@ -0,0 +1,149 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { differenceInSeconds, parseISO } from "date-fns";
|
||||
import ky from "ky";
|
||||
import { useEffect, useState } from "react";
|
||||
import QRCode from "react-qr-code";
|
||||
|
||||
function MonitoringPage() {
|
||||
const [sessions, setSessions] = useState<any>([]);
|
||||
const [servers, setServers] = useState<any>([]);
|
||||
|
||||
async function getSessionServers() {
|
||||
const response = await ky
|
||||
.get(`${import.meta.env.VITE_COORD_URL}/session_servers`)
|
||||
.json();
|
||||
|
||||
setServers(response);
|
||||
|
||||
setTimeout(() => {
|
||||
getSessionServers();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
async function getActiveSessions() {
|
||||
const response = await ky
|
||||
.get(`${import.meta.env.VITE_COORD_URL}/active_sessions`)
|
||||
.json();
|
||||
|
||||
setSessions(response);
|
||||
|
||||
setTimeout(() => {
|
||||
getActiveSessions();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
async function endActiveSession(
|
||||
location: string,
|
||||
server: string,
|
||||
uePort: number,
|
||||
cirrusPort: number
|
||||
) {
|
||||
await ky
|
||||
.get(
|
||||
`${
|
||||
import.meta.env.VITE_COORD_URL
|
||||
}/end?location=${location}&server=${server}&uePort=${uePort}&cirrusPort=${cirrusPort}`
|
||||
)
|
||||
.json();
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getActiveSessions();
|
||||
getSessionServers();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen text-[#F2F2F2] p-4 gap-4 flex flex-col">
|
||||
{servers.map((server: any) => (
|
||||
<div key={server.id} className="p-4 bg-[#22222A]">
|
||||
<div>
|
||||
{differenceInSeconds(new Date(), parseISO(server.updatedAt)) >=
|
||||
10 ? (
|
||||
<p className="flex items-center gap-2">
|
||||
<span className="h-2 w-2 rounded-full bg-red-500"></span>
|
||||
<span>Не в сети</span>
|
||||
</p>
|
||||
) : (
|
||||
<p className="flex items-center gap-2">
|
||||
<span className="h-2 w-2 rounded-full bg-green-500"></span>
|
||||
<span>В сети</span>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<p>Локация: "{server.location}"</p>
|
||||
<p>Имя сервера: "{server.name}"</p>
|
||||
<p>Лимит процессов: {server.limit_process}</p>
|
||||
<div>
|
||||
<p>CPU: {server.cpu}</p>
|
||||
<p>RAM: {server.ram}</p>
|
||||
<p className="flex space-x-2">
|
||||
<span>GPU: </span>
|
||||
<span className="flex space-x-4">
|
||||
{server.gpu?.map((item: any, index: number) => (
|
||||
<span key={index}>
|
||||
{item} {index === 2 && "°C"}
|
||||
</span>
|
||||
))}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{sessions.map((session: any) => (
|
||||
<div
|
||||
key={session.id}
|
||||
className="flex flex-wrap gap-4 justify-between items-center p-4 bg-[#22222A] rounded"
|
||||
>
|
||||
<div className="flex gap-4">
|
||||
<div className="">
|
||||
<QRCode
|
||||
bgColor="#22222A"
|
||||
fgColor="#F2F2F2"
|
||||
size={128}
|
||||
value={`https://stream.graff.tech/stream/?data=wss://${session.location}.sess.stream.graff.tech/${session.server}/${session.cirrusPort}/`}
|
||||
viewBox={`0 0 128 128`}
|
||||
/>
|
||||
</div>
|
||||
<div className="sm:text-base text-sm">
|
||||
<p>Локация: "{session.location}"</p>
|
||||
<p>Сервер: "{session.server}"</p>
|
||||
<p>Сборка: "{session.title}"</p>
|
||||
<p>Порт: {session.cirrusPort}</p>
|
||||
<p>Пользователи: {session.connectedPlayersCount || 0}</p>
|
||||
<p>
|
||||
Время запуска: {new Date(session.createdAt).toLocaleString()}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-x-4">
|
||||
<a
|
||||
href={`https://stream.graff.tech/stream/${session.id}`}
|
||||
target="_blank"
|
||||
className="inline-block px-4 py-2 bg-blue-600 hover:bg-blue-500 transition-colors rounded"
|
||||
>
|
||||
Открыть в новом окне
|
||||
</a>
|
||||
<button
|
||||
onClick={() =>
|
||||
endActiveSession(
|
||||
session.location,
|
||||
session.server,
|
||||
session.uePort,
|
||||
session.cirrusPort
|
||||
)
|
||||
}
|
||||
className="px-4 py-2 bg-red-600 hover:bg-red-500 transition-colors rounded"
|
||||
>
|
||||
Завершить сессию
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default MonitoringPage;
|
||||
@@ -0,0 +1,303 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
// import { ChangeEvent, useEffect, useState } from "react";
|
||||
// import useAuthStore from "./stores/useAuthStore";
|
||||
// import {
|
||||
// addHours,
|
||||
// eachMinuteOfInterval,
|
||||
// format,
|
||||
// isBefore,
|
||||
// parse,
|
||||
// } from "date-fns";
|
||||
// import ky from "ky";
|
||||
import SessionScheduleSettings from "./components/SessionScheduleSettings";
|
||||
|
||||
function PersonalAreaDashboardPage() {
|
||||
// const [user, removeAuthStore, accessToken] = useAuthStore((state) => [
|
||||
// state.user,
|
||||
// state.removeAuthStore,
|
||||
// state.accessToken,
|
||||
// ]);
|
||||
// const [startTime, setStartTime] = useState<string>("09:00");
|
||||
// const [endTime, setEndTime] = useState<string>("20:00");
|
||||
// const [duration, setDuration] = useState<number>(30);
|
||||
// const [datesAndTimes, setDatesAndTimes] = useState<any[]>([]);
|
||||
// const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
|
||||
// function handleDuration(e: ChangeEvent<HTMLInputElement>) {
|
||||
// e.preventDefault();
|
||||
|
||||
// let value = parseInt(e.target.value);
|
||||
// const min = parseInt(e.target.min);
|
||||
// const max = parseInt(e.target.max);
|
||||
|
||||
// if (!value || value < min) value = min;
|
||||
// if (value > max) value = max;
|
||||
|
||||
// setDuration(value);
|
||||
// }
|
||||
|
||||
// function calculateTimes() {
|
||||
// if (startTime && endTime) {
|
||||
// const startDateTime = parse(startTime, "HH:mm", new Date());
|
||||
// const endDateTime = parse(endTime, "HH:mm", new Date());
|
||||
|
||||
// if (!isBefore(startDateTime, endDateTime)) {
|
||||
// setEndTime(format(addHours(startDateTime, 1), "HH:mm"));
|
||||
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// const newDateAndTimes: any[] = eachMinuteOfInterval(
|
||||
// {
|
||||
// start: startDateTime,
|
||||
// end: endDateTime,
|
||||
// },
|
||||
// { step: duration }
|
||||
// );
|
||||
|
||||
// newDateAndTimes.forEach((value: Date, index: number) => {
|
||||
// newDateAndTimes[index] = { value, active: true };
|
||||
// });
|
||||
|
||||
// setDatesAndTimes(newDateAndTimes);
|
||||
// }
|
||||
// }
|
||||
|
||||
// async function getSessionScheduleSettings() {
|
||||
// setIsLoading(true);
|
||||
|
||||
// try {
|
||||
// const result: any = await ky
|
||||
// .get(
|
||||
// `${import.meta.env.VITE_COORD_URL}/users/session_schedule_settings`,
|
||||
// {
|
||||
// headers: {
|
||||
// Authorization: `Bearer ${accessToken}`,
|
||||
// },
|
||||
// }
|
||||
// )
|
||||
// .json();
|
||||
|
||||
// if (result.error) {
|
||||
// console.log("Error: ", result.error);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// const { datesAndTimes, startTime, endTime, duration } = result;
|
||||
|
||||
// console.log("datesAndTimes", datesAndTimes);
|
||||
|
||||
// setStartTime(startTime);
|
||||
// setEndTime(endTime);
|
||||
// setDuration(duration);
|
||||
// setIsLoading(false);
|
||||
// } catch (error) {
|
||||
// if (error instanceof Error) {
|
||||
// console.log(error.message);
|
||||
// }
|
||||
|
||||
// setIsLoading(false);
|
||||
// }
|
||||
// }
|
||||
|
||||
// async function saveSessionScheduleSettings() {
|
||||
// try {
|
||||
// await ky
|
||||
// .post(
|
||||
// `${import.meta.env.VITE_COORD_URL}/users/session_schedule_settings`,
|
||||
// {
|
||||
// headers: {
|
||||
// Authorization: `Bearer ${accessToken}`,
|
||||
// },
|
||||
// json: { startTime, endTime, duration, datesAndTimes },
|
||||
// }
|
||||
// )
|
||||
// .json();
|
||||
|
||||
// alert("Изменения сохранены!");
|
||||
// } catch (error) {
|
||||
// if (error instanceof Error) {
|
||||
// console.log(error.message);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// function changeActive(dateAndTimeValue: Date) {
|
||||
// const newDatesAndTimes = datesAndTimes.map((dateAndTime) => {
|
||||
// if (dateAndTime.value === dateAndTimeValue) {
|
||||
// dateAndTime.active = !dateAndTime.active;
|
||||
// }
|
||||
|
||||
// return dateAndTime;
|
||||
// });
|
||||
|
||||
// setDatesAndTimes(newDatesAndTimes);
|
||||
// }
|
||||
|
||||
// function logout() {
|
||||
// removeAuthStore();
|
||||
// }
|
||||
|
||||
// useEffect(() => {
|
||||
// calculateTimes();
|
||||
// }, [startTime, endTime, duration]);
|
||||
|
||||
// useEffect(() => {
|
||||
// getSessionScheduleSettings();
|
||||
// }, []);
|
||||
|
||||
// return (
|
||||
// <div className="p-8 min-h-screen flex flex-col justify-center items-center text-[#F2F2F2] space-y-8">
|
||||
// <div className="space-y-4">
|
||||
// <p>
|
||||
// <span className="text-[#C5C7CE] text-sm">Вы вошли как</span>{" "}
|
||||
// {user?.username}
|
||||
// </p>
|
||||
// <button onClick={logout} className="text-yellow-600">
|
||||
// Выйти
|
||||
// </button>
|
||||
// </div>
|
||||
|
||||
// <div className="flex gap-4">
|
||||
// <div className="relative space-y-8 w-80 bg-[#151619] p-8 rounded-lg shadow min-h-[378px]">
|
||||
// <p className="text-2xl font-gilroy">Настройки расписания сеансов</p>
|
||||
|
||||
// {!isLoading ? (
|
||||
// <form
|
||||
// onSubmit={(e) => e.preventDefault()}
|
||||
// className="flex flex-col space-y-8"
|
||||
// >
|
||||
// <div className="space-y-4">
|
||||
// <div className="flex gap-4 items-center">
|
||||
// <div className="space-y-1">
|
||||
// <p className="text-[#C5C7CE] text-sm">Начало</p>
|
||||
// <input
|
||||
// // ref={startTimeRef}
|
||||
// required
|
||||
// type="time"
|
||||
// value={startTime}
|
||||
// onChange={(e) => setStartTime(e.target.value)}
|
||||
// className="px-3 py-2 rounded bg-[#1C1D21] outline-none focus:outline-[#BC75FF] w-full transition-all"
|
||||
// />
|
||||
// </div>
|
||||
// <span className="mt-6">—</span>
|
||||
// <div className="space-y-1">
|
||||
// <p className="text-[#C5C7CE] text-sm">Конец</p>
|
||||
// <input
|
||||
// // ref={startTimeRef}
|
||||
// required
|
||||
// type="time"
|
||||
// value={endTime}
|
||||
// onChange={(e) => setEndTime(e.target.value)}
|
||||
// className="px-3 py-2 rounded bg-[#1C1D21] outline-none focus:outline-[#BC75FF] w-full transition-all"
|
||||
// />
|
||||
// </div>
|
||||
// </div>
|
||||
// <div className="space-y-1">
|
||||
// <p className="text-[#C5C7CE] text-sm">Длительность сеанса</p>
|
||||
// <input
|
||||
// // ref={sessionDurationRef}
|
||||
// required
|
||||
// type="number"
|
||||
// min={15}
|
||||
// max={60}
|
||||
// step={5}
|
||||
// value={duration}
|
||||
// onChange={handleDuration}
|
||||
// className="px-3 py-2 rounded bg-[#1C1D21] outline-none focus:outline-[#BC75FF] w-full transition-all"
|
||||
// />
|
||||
// </div>
|
||||
// </div>
|
||||
|
||||
// <button
|
||||
// onClick={saveSessionScheduleSettings}
|
||||
// type="submit"
|
||||
// className="px-4 py-2 rounded bg-gradient outline-none opacity-95 hover:opacity-100 transition-all disabled:opacity-50 flex justify-center items-center h-10 self-end"
|
||||
// >
|
||||
// Сохранить
|
||||
// </button>
|
||||
// </form>
|
||||
// ) : (
|
||||
// <div className="absolute top-0 left-0 w-full h-full flex justify-center items-center">
|
||||
// <svg
|
||||
// className="animate-spin h-5 w-5"
|
||||
// xmlns="http://www.w3.org/2000/svg"
|
||||
// fill="none"
|
||||
// viewBox="0 0 24 24"
|
||||
// >
|
||||
// <circle
|
||||
// className="opacity-25"
|
||||
// cx="12"
|
||||
// cy="12"
|
||||
// r="10"
|
||||
// stroke="currentColor"
|
||||
// strokeWidth="4"
|
||||
// ></circle>
|
||||
// <path
|
||||
// className="opacity-75"
|
||||
// fill="currentColor"
|
||||
// d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
// ></path>
|
||||
// </svg>
|
||||
// </div>
|
||||
// )}
|
||||
// </div>
|
||||
|
||||
// <div className="relative space-y-8 w-80 bg-[#151619] p-8 rounded-lg shadow min-h-[378px]">
|
||||
// <p className="text-xl font-gilroy">Предпросмотр</p>
|
||||
|
||||
// {!isLoading ? (
|
||||
// <div className="grid grid-cols-4">
|
||||
// {datesAndTimes.map(
|
||||
// (dateAndTime: { value: Date; active: true }, index: number) => (
|
||||
// <button
|
||||
// key={index}
|
||||
// onClick={() => changeActive(dateAndTime.value)}
|
||||
// className={[
|
||||
// "px-3 py-2 text-center rounded hover:bg-[#23242A]",
|
||||
// !dateAndTime.active ? "opacity-25" : "opacity-100",
|
||||
// ].join(" ")}
|
||||
// >
|
||||
// {format(dateAndTime.value, "HH:mm")}
|
||||
// </button>
|
||||
// )
|
||||
// )}
|
||||
// </div>
|
||||
// ) : (
|
||||
// <div className="absolute top-0 left-0 w-full h-full flex justify-center items-center">
|
||||
// <svg
|
||||
// className="animate-spin h-5 w-5"
|
||||
// xmlns="http://www.w3.org/2000/svg"
|
||||
// fill="none"
|
||||
// viewBox="0 0 24 24"
|
||||
// >
|
||||
// <circle
|
||||
// className="opacity-25"
|
||||
// cx="12"
|
||||
// cy="12"
|
||||
// r="10"
|
||||
// stroke="currentColor"
|
||||
// strokeWidth="4"
|
||||
// ></circle>
|
||||
// <path
|
||||
// className="opacity-75"
|
||||
// fill="currentColor"
|
||||
// d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
// ></path>
|
||||
// </svg>
|
||||
// </div>
|
||||
// )}
|
||||
// </div>
|
||||
// </div>
|
||||
// </div>
|
||||
// );
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col justify-center items-center p-8">
|
||||
<SessionScheduleSettings />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default PersonalAreaDashboardPage;
|
||||
@@ -0,0 +1,140 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import ky from "ky";
|
||||
import useAuthStore from "./stores/useAuthStore";
|
||||
import { FormEvent, useRef, useState } from "react";
|
||||
|
||||
type User = {
|
||||
id: string;
|
||||
username: string;
|
||||
};
|
||||
|
||||
interface IResult {
|
||||
error?: number;
|
||||
accessToken?: string;
|
||||
user?: User;
|
||||
}
|
||||
|
||||
function PersonalAreaLoginPage() {
|
||||
const [setAccessToken, setUser] = useAuthStore((state) => [
|
||||
state.setAccessToken,
|
||||
state.setUser,
|
||||
]);
|
||||
|
||||
const [username, setUsername] = useState<string>("");
|
||||
const [password, setPassword] = useState<string>("");
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
const usernameRef = useRef<HTMLInputElement>(null);
|
||||
const passwordRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
async function auth(e: FormEvent<HTMLFormElement>) {
|
||||
e.preventDefault();
|
||||
|
||||
setIsLoading(true);
|
||||
|
||||
try {
|
||||
const result: IResult = await ky
|
||||
.post(import.meta.env.VITE_COORD_URL + "/login", {
|
||||
json: { username, password },
|
||||
})
|
||||
.json();
|
||||
|
||||
setIsLoading(false);
|
||||
|
||||
if (result.error) {
|
||||
passwordRef.current?.focus();
|
||||
|
||||
setPassword("");
|
||||
setError("Неверное имя пользователя или пароль");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result.accessToken || !result.user) {
|
||||
setError("Не удалось получить данные");
|
||||
return;
|
||||
}
|
||||
|
||||
setAccessToken(result.accessToken);
|
||||
setUser(result.user);
|
||||
} catch (error) {
|
||||
setIsLoading(false);
|
||||
|
||||
if (error instanceof Error) {
|
||||
if (error.message === "Failed to fetch") {
|
||||
setError("Нет соединения с сервером, попробуйте позже");
|
||||
} else {
|
||||
setError(error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="p-8 min-h-screen flex flex-col justify-center items-center text-[#F2F2F2]">
|
||||
<div className="space-y-12 w-[400px] bg-[#151619] p-8 rounded-lg shadow">
|
||||
<p className="text-2xl font-gilroy">Вход в личный кабинет</p>
|
||||
<form onSubmit={auth} className="flex flex-col gap-12">
|
||||
<div className="flex flex-col gap-8">
|
||||
<div className="space-y-1">
|
||||
<p className="text-[#C5C7CE] text-sm">Имя пользователя</p>
|
||||
<input
|
||||
ref={usernameRef}
|
||||
required
|
||||
type="text"
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
className="px-3 py-2 rounded bg-[#1C1D21] outline-none focus:outline-[#BC75FF] w-full transition-all"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<p className="text-[#C5C7CE] text-sm">Пароль</p>
|
||||
<input
|
||||
ref={passwordRef}
|
||||
required
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
className="px-3 py-2 rounded bg-[#1C1D21] outline-none focus:outline-[#BC75FF] w-full transition-all"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isLoading}
|
||||
className="px-4 py-2 rounded bg-gradient outline-none opacity-95 hover:opacity-100 transition-all disabled:opacity-50 flex justify-center items-center h-10"
|
||||
>
|
||||
{isLoading ? (
|
||||
<svg
|
||||
className="animate-spin h-5 w-5"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<circle
|
||||
className="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
strokeWidth="4"
|
||||
></circle>
|
||||
<path
|
||||
className="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
></path>
|
||||
</svg>
|
||||
) : (
|
||||
<span>Войти</span>
|
||||
)}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<p className="text-sm text-red-500 min-h-[40px]">{error && error}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default PersonalAreaLoginPage;
|
||||
@@ -0,0 +1,65 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import ky from "ky";
|
||||
import { useEffect, useState } from "react";
|
||||
import Countdown from "react-countdown";
|
||||
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||
|
||||
function ScheduledPage() {
|
||||
const [searchParams] = useSearchParams();
|
||||
const navigate = useNavigate();
|
||||
const [countdownSeconds, setCountdownSeconds] = useState<number>();
|
||||
|
||||
async function connect() {
|
||||
const userInviteKey = searchParams.get("userInviteKey");
|
||||
|
||||
try {
|
||||
const result: any = await ky
|
||||
.get(
|
||||
`${
|
||||
import.meta.env.VITE_COORD_URL
|
||||
}/active_sessions/scheduled?userInviteKey=${userInviteKey}`
|
||||
)
|
||||
.json();
|
||||
|
||||
if (!result.id) {
|
||||
setCountdownSeconds(result.countdownSeconds);
|
||||
return;
|
||||
}
|
||||
|
||||
navigate(`/stream/${result.id}`);
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
console.log(error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (searchParams.get("userInviteKey")) {
|
||||
// console.log(searchParams.get("userInviteKey"));
|
||||
|
||||
connect();
|
||||
} else {
|
||||
navigate("/");
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen p-8 text-[#F2F2F2] flex flex-col justify-center items-center">
|
||||
{countdownSeconds && (
|
||||
<div className="space-y-4 w-[340px]">
|
||||
<p className="text-2xl font-gilroy">Сеанс начнется через:</p>
|
||||
<p className="text-6xl font-gilroy">
|
||||
<Countdown
|
||||
date={Date.now() + countdownSeconds + 10000}
|
||||
onComplete={() => window.location.reload()}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ScheduledPage;
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
@@ -0,0 +1,128 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
add,
|
||||
eachDayOfInterval,
|
||||
endOfMonth,
|
||||
format,
|
||||
getDay,
|
||||
isEqual,
|
||||
parse,
|
||||
startOfToday,
|
||||
} from "date-fns";
|
||||
import { enUS, ru } from "date-fns/locale";
|
||||
import { useEffect, useState } from "react";
|
||||
import ChevronRightIcon from "./icons/ChevronRightIcon";
|
||||
import ChevronLeftIcon from "./icons/ChevronLeftIcon";
|
||||
import { Trans } from "react-i18next";
|
||||
import i18n from "../i18n";
|
||||
|
||||
interface CalendarProps {
|
||||
handleSelect: (day: Date) => void;
|
||||
}
|
||||
|
||||
function classNames(...classes: (string | boolean)[]) {
|
||||
return classes.filter(Boolean).join(" ");
|
||||
}
|
||||
|
||||
function Calendar({ handleSelect }: CalendarProps) {
|
||||
const today = startOfToday();
|
||||
const [selectedDay, setSelectedDay] = useState<Date | null>(null);
|
||||
const [currentMonth, setCurrentMonth] = useState(format(today, "MMM-yyyy"));
|
||||
const firstDayCurrentMonth = parse(currentMonth, "MMM-yyyy", new Date());
|
||||
|
||||
const days = eachDayOfInterval({
|
||||
start: firstDayCurrentMonth,
|
||||
end: endOfMonth(firstDayCurrentMonth),
|
||||
});
|
||||
|
||||
function previousMonth() {
|
||||
const firstDayNextMonth = add(firstDayCurrentMonth, { months: -1 });
|
||||
setCurrentMonth(format(firstDayNextMonth, "MMM-yyyy"));
|
||||
}
|
||||
|
||||
function nextMonth() {
|
||||
const firstDayNextMonth = add(firstDayCurrentMonth, { months: 1 });
|
||||
setCurrentMonth(format(firstDayNextMonth, "MMM-yyyy"));
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedDay !== null) {
|
||||
handleSelect(selectedDay);
|
||||
}
|
||||
}, [selectedDay]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-center justify-between">
|
||||
<button onClick={previousMonth}>
|
||||
<ChevronLeftIcon />
|
||||
</button>
|
||||
|
||||
<p className="text-sm text-white first-letter:uppercase w-fit">
|
||||
{format(
|
||||
firstDayCurrentMonth,
|
||||
"LLLL, yyyy",
|
||||
i18n.language === "ru" ? { locale: ru } : { locale: enUS }
|
||||
)}
|
||||
</p>
|
||||
|
||||
<button onClick={nextMonth}>
|
||||
<ChevronRightIcon />
|
||||
</button>
|
||||
</div>
|
||||
<div className="sm:mt-8 mt-5 grid grid-cols-7 gap-2 font-gilroy text-sm text-center text-white font-semibold">
|
||||
<div>{i18n.language === "ru" ? "пн" : "Mo"}</div>
|
||||
<div>{i18n.language === "ru" ? "вт" : "Tu"}</div>
|
||||
<div>{i18n.language === "ru" ? "ср" : "We"}</div>
|
||||
<div>{i18n.language === "ru" ? "чт" : "Th"}</div>
|
||||
<div>{i18n.language === "ru" ? "пт" : "Fr"}</div>
|
||||
<div>{i18n.language === "ru" ? "сб" : "Sa"}</div>
|
||||
<div>{i18n.language === "ru" ? "вс" : "Su"}</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-7 gap-2 mt-2 text-sm font-semibold">
|
||||
{days.map((day, dayIdx) => (
|
||||
<div
|
||||
key={day.toString()}
|
||||
className={classNames(
|
||||
dayIdx === 0 && colStartClasses[getDay(day) - 1]
|
||||
)}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
disabled={day < today}
|
||||
onClick={() => setSelectedDay(day)}
|
||||
className={classNames(
|
||||
selectedDay !== null &&
|
||||
isEqual(day, selectedDay) &&
|
||||
"bg-[#798FFF] text-white hover:bg-opacity-100",
|
||||
"mx-auto flex min-w-[40px] h-10 items-center justify-center rounded-full transition-all text-[#798FFF] border border-[#798FFF] hover:bg-[#798FFF] hover:bg-opacity-20 disabled:text-[#3D425C] disabled:border-transparent disabled:hover:bg-transparent"
|
||||
)}
|
||||
>
|
||||
<time dateTime={format(day, "yyyy-MM-dd")}>
|
||||
{format(day, "d")}
|
||||
</time>
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="mt-8 flex items-center gap-2">
|
||||
<div className="w-4 h-4 bg-[#798FFF] rounded"></div>
|
||||
<p className="text-[#798FFF]">
|
||||
- <Trans i18nKey={"sidebar.available"}>запись доступна</Trans>
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const colStartClasses = [
|
||||
"",
|
||||
"col-start-2",
|
||||
"col-start-3",
|
||||
"col-start-4",
|
||||
"col-start-5",
|
||||
"col-start-6",
|
||||
"col-start-7",
|
||||
];
|
||||
|
||||
export default Calendar;
|
||||
@@ -0,0 +1,37 @@
|
||||
import { Trans } from "react-i18next";
|
||||
|
||||
interface CardProps {
|
||||
icon: string;
|
||||
image: string;
|
||||
title: string | JSX.Element;
|
||||
location: string | JSX.Element;
|
||||
handleClick: () => void;
|
||||
}
|
||||
|
||||
function Card({ icon, image, title, location, handleClick }: CardProps) {
|
||||
return (
|
||||
<div className="rounded-lg overflow-hidden flex flex-col justify-between bg-[#22222A]">
|
||||
<div
|
||||
className="aspect-video bg-no-repeat bg-center bg-cover"
|
||||
style={{ backgroundImage: `url('${image}')` }}
|
||||
></div>
|
||||
<div className="p-8 space-y-8">
|
||||
<div className="flex items-center space-x-4">
|
||||
<img src={icon} alt="" className="w-12 h-12" />
|
||||
<div>
|
||||
<p className="sm:text-xl font-gilroy">{title}</p>
|
||||
<p className="sm:text-base text-sm text-[#ABABBA]">{location}</p>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleClick}
|
||||
className="px-4 py-2 bg-gradient rounded w-full opacity-90 hover:opacity-100 transition-opacity font-gilroy"
|
||||
>
|
||||
<Trans i18nKey="button" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Card;
|
||||
@@ -0,0 +1,20 @@
|
||||
.contacts-field:focus ~ .contacts-placeholder {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.contacts-field:focus ~ .contacts-placeholder-2 {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.contacts-field:valid ~ .contacts-placeholder {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.contacts-field:valid ~ .contacts-placeholder-2 {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.contacts-field::placeholder {
|
||||
font-weight: 600;
|
||||
color: #77787d;
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
import { ChangeEvent } from "react";
|
||||
import { Trans } from "react-i18next";
|
||||
import AsteriskIcon from "./icons/AsteriskIcon";
|
||||
import InputMask from "react-input-mask";
|
||||
import "./ContactsForm.css";
|
||||
import useSidebarStore from "../stores/useSidebarStore";
|
||||
|
||||
function ContactsForm() {
|
||||
const [name, setName, phone, setPhone, email, setEmail] = useSidebarStore(
|
||||
(state) => [
|
||||
state.name,
|
||||
state.setName,
|
||||
state.phone,
|
||||
state.setPhone,
|
||||
state.email,
|
||||
state.setEmail,
|
||||
]
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="relative">
|
||||
<input
|
||||
required
|
||||
type="text"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
className="feedback-field bg-transparent border border-[#3D425C] rounded-none sm:pt-12 sm:pb-4 sm:px-4 pt-8 pb-3 px-3 outline-none outline-1 -outline-offset-1 focus:outline-[#D375FF] transition-all w-full"
|
||||
/>
|
||||
<p className="feedback-placeholder lg:text-base text-sm absolute sm:pt-4 sm:pb-4 sm:px-4 sm:top-4 pt-3 pb-3 px-3 top-3 w-full opacity-50 transition-all pointer-events-none flex justify-between items-center">
|
||||
<span>
|
||||
<Trans i18nKey={"feedback.form.field1"}>Имя</Trans>
|
||||
</span>
|
||||
<AsteriskIcon />
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="relative">
|
||||
<InputMask
|
||||
required
|
||||
type="tel"
|
||||
mask={"+999999999999999"}
|
||||
maskChar={null}
|
||||
value={phone}
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
||||
setPhone(e.target.value)
|
||||
}
|
||||
className={[
|
||||
"feedback-field bg-transparent border rounded-none border-t-0 border-[#3D425C] sm:pt-12 sm:pb-4 sm:px-4 pt-8 pb-3 px-3 outline-none outline-1 -outline-offset-1 focus:outline-[#D375FF] transition-all w-full",
|
||||
].join(" ")}
|
||||
/>
|
||||
<p className="feedback-placeholder lg:text-base text-sm absolute sm:pt-4 sm:pb-4 sm:px-4 sm:top-4 pt-3 pb-3 px-3 top-3 w-full opacity-50 transition-all pointer-events-none flex justify-between items-center">
|
||||
<span>
|
||||
<Trans i18nKey={"feedback.form.field2"}>Телефон</Trans>
|
||||
</span>
|
||||
<AsteriskIcon />
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="relative">
|
||||
<input
|
||||
required
|
||||
type="text"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
className="feedback-field bg-transparent border rounded-none border-t-0 border-[#3D425C] sm:pt-12 sm:pb-4 sm:px-4 pt-8 pb-3 px-3 outline-none outline-1 -outline-offset-1 focus:outline-[#D375FF] transition-all w-full"
|
||||
/>
|
||||
<p className="feedback-placeholder lg:text-base text-sm absolute sm:pt-4 sm:pb-4 sm:px-4 sm:top-4 pt-3 pb-3 px-3 top-3 w-full opacity-50 transition-all pointer-events-none flex justify-between items-center">
|
||||
<span>Email</span>
|
||||
<AsteriskIcon />
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="border border-t-0 border-[#3D425C] sm:px-4 sm:py-6 px-3 py-4 text-xs flex items-center gap-2">
|
||||
<div className="flex gap-2">
|
||||
<div className="">
|
||||
<AsteriskIcon />
|
||||
</div>
|
||||
<p>—</p>
|
||||
<p>
|
||||
<Trans i18nKey={"feedback.form.desc2"}>
|
||||
Звездочкой отмечены обязательные
|
||||
<br />
|
||||
для заполнения поля
|
||||
</Trans>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ContactsForm;
|
||||
@@ -0,0 +1,187 @@
|
||||
import ky from "ky";
|
||||
import { ChangeEvent, FormEvent, useState } from "react";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import InputMask from "react-input-mask";
|
||||
import AsteriskIcon from "./icons/AsteriskIcon";
|
||||
import SendIcon from "./icons/SendIcon";
|
||||
import CheckGradientIcon from "./icons/CheckGradientIcon";
|
||||
import LoaderIcon from "./icons/LoaderIcon";
|
||||
|
||||
function FeedbackForm() {
|
||||
const { t } = useTranslation();
|
||||
const [name, setName] = useState<string>("");
|
||||
const [phone, setPhone] = useState<string>("");
|
||||
const [email, setEmail] = useState<string>("");
|
||||
const [description, setDescription] = useState<string>("");
|
||||
const [isSend, setIsSend] = useState<boolean>(false);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
|
||||
async function sendMail(e: FormEvent<HTMLFormElement>) {
|
||||
e.preventDefault();
|
||||
|
||||
setIsLoading(true);
|
||||
|
||||
try {
|
||||
await ky
|
||||
.post(`https://estate.graff.tech/api/mail`, {
|
||||
json: {
|
||||
fullname: name,
|
||||
phone,
|
||||
email,
|
||||
request: description,
|
||||
},
|
||||
})
|
||||
.json();
|
||||
|
||||
setIsSend(true);
|
||||
setIsLoading(false);
|
||||
} catch (error) {
|
||||
setIsLoading(false);
|
||||
if (error instanceof Error) {
|
||||
alert(error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<form
|
||||
className="grid lg:grid-cols-3 sm:grid-cols-2 relative"
|
||||
onSubmit={(e) => void sendMail(e)}
|
||||
>
|
||||
<div className="relative col-span-1">
|
||||
<input
|
||||
required
|
||||
type="text"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
className="feedback-field bg-transparent border border-[#3D425C] rounded-none lg:p-6 lg:pt-14 p-4 pt-12 outline-none outline-1 -outline-offset-1 focus:outline-[#D375FF] transition-all w-full"
|
||||
/>
|
||||
<p className="feedback-placeholder lg:text-base text-sm absolute lg:top-4 top-5 left-0 w-full lg:p-6 p-4 opacity-50 transition-all pointer-events-none flex justify-between">
|
||||
<span>
|
||||
<Trans i18nKey={"feedback.form.field1"}>Имя</Trans>
|
||||
</span>
|
||||
<AsteriskIcon />
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="relative">
|
||||
<InputMask
|
||||
required
|
||||
type="tel"
|
||||
mask={"+999999999999999"}
|
||||
maskChar={null}
|
||||
value={phone}
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
||||
setPhone(e.target.value)
|
||||
}
|
||||
className={[
|
||||
"feedback-field bg-transparent border rounded-none sm:border-l-0 sm:border-t border-t-0 border-l border-[#3D425C] lg:p-6 lg:pt-14 p-4 pt-12 outline-none outline-1 -outline-offset-1 focus:outline-[#D375FF] transition-all w-full",
|
||||
].join(" ")}
|
||||
/>
|
||||
<p className="feedback-placeholder lg:text-base text-sm absolute lg:top-4 top-5 left-0 w-full lg:p-6 p-4 opacity-50 transition-all pointer-events-none flex justify-between">
|
||||
<span>
|
||||
<Trans i18nKey={"feedback.form.field2"}>Телефон</Trans>
|
||||
</span>
|
||||
<AsteriskIcon />
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="relative lg:col-span-1 sm:col-span-2 col-span-1">
|
||||
<input
|
||||
required
|
||||
type="text"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
className="feedback-field bg-transparent border rounded-none lg:border-l-0 lg:border-t border-t-0 border-[#3D425C] lg:p-6 lg:pt-14 p-4 pt-12 outline-none outline-1 -outline-offset-1 focus:outline-[#D375FF] transition-all w-full"
|
||||
/>
|
||||
<p className="feedback-placeholder lg:text-base text-sm absolute lg:top-4 top-5 left-0 w-full lg:p-6 p-4 opacity-50 transition-all pointer-events-none flex justify-between">
|
||||
<span>Email</span>
|
||||
<AsteriskIcon />
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="relative lg:col-span-3 sm:col-span-2 h-[194px]">
|
||||
<textarea
|
||||
placeholder={t("feedback.form.field3").toString()}
|
||||
value={description}
|
||||
onChange={(e) => setDescription(e.target.value)}
|
||||
className="feedback-field bg-transparent resize-none border rounded-none border-t-0 border-[#3D425C] lg:p-6 p-4 h-full outline-none outline-1 -outline-offset-1 focus:outline-[#D375FF] transition-all w-full"
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<div className="2xl:pt-6 2xl:pr-6 pt-4 sm:pr-4 lg:order-none order-last flex items-center">
|
||||
<button
|
||||
disabled={isLoading}
|
||||
className="group relative px-6 py-4 2xl:text-base text-sm bg-gradient rounded-full font-medium flex justify-between items-center w-full transition-opacity disabled:opacity-75"
|
||||
>
|
||||
<div className="absolute top-0 left-0 w-full h-full rounded-full bg-black opacity-0 group-hover:opacity-10 transition-all"></div>
|
||||
<span className="relative">
|
||||
<Trans i18nKey={"feedback.form.button"}>Отправить</Trans>
|
||||
</span>
|
||||
{!isLoading ? (
|
||||
<SendIcon className="relative 2xl:w-8 2xl:h-8 w-6 h-6" />
|
||||
) : (
|
||||
<LoaderIcon className="relative 2xl:w-8 2xl:h-8 w-6 h-6 animate-spin" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="border sm:border-t-0 border-t border-[#3D425C] 2xl:p-6 p-4 sm:mt-0 mt-6 flex items-center">
|
||||
<div className="text-xs leading-tight">
|
||||
<Trans i18nKey={"feedback.form.desc1.text1"}>
|
||||
Нажимая кнопку отправить, вы принимаете
|
||||
</Trans>{" "}
|
||||
<a className="text-[#798FFF] cursor-pointer opacity-95 hover:opacity-100 transition-all">
|
||||
<Trans i18nKey={"feedback.form.desc1.link1"}>
|
||||
условия использования
|
||||
</Trans>
|
||||
</a>{" "}
|
||||
<Trans i18nKey={"feedback.form.desc1.text2"}>и</Trans>{" "}
|
||||
<a className="text-[#798FFF] cursor-pointer opacity-95 hover:opacity-100 transition-all">
|
||||
<Trans i18nKey={"feedback.form.desc1.link2"}>
|
||||
политику конфиденциальности
|
||||
</Trans>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="border border-t-0 sm:border-l-0 border-[#3D425C] 2xl:p-6 p-4 text-xs flex items-center gap-2">
|
||||
<div className="flex gap-2">
|
||||
<div className="">
|
||||
<AsteriskIcon />
|
||||
</div>
|
||||
<p>—</p>
|
||||
<p className="leading-tight">
|
||||
<Trans i18nKey={"feedback.form.desc2"}>
|
||||
Звездочкой отмечены обязательные
|
||||
<br />
|
||||
для заполнения поля
|
||||
</Trans>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isSend && (
|
||||
<div className="absolute top-0 left-0 w-full h-full bg-[#14161F] border border-[#3D425C] p-6 flex flex-col justify-between">
|
||||
<p className="text-gradient text-xl font-gilroy leading-tight font-semibold flex items-center gap-2">
|
||||
<span>Заявка отправлена</span>
|
||||
<CheckGradientIcon className="lg:w-8 lg:h-8 w-6 h-6" />
|
||||
</p>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
<p className="font-gilroy leading-snug lg:text-2xl text-xl font-semibold">
|
||||
Спасибо за подачу заявки!
|
||||
</p>
|
||||
|
||||
<p className="lg:w-1/2 sm:w-2/3 lg:text-base text-sm">
|
||||
Мы ценим ваш интерес к нашей компании и в ближайшее время свяжемся
|
||||
с вами для уточнения деталей проекта.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
export default FeedbackForm;
|
||||
@@ -0,0 +1,64 @@
|
||||
import { Trans } from "react-i18next";
|
||||
import LogoIcon from "./icons/LogoIcon";
|
||||
import LogoMobileIcon from "./icons/LogoMobileIcon";
|
||||
import i18n from "../i18n";
|
||||
import useSidebarStore from "../stores/useSidebarStore";
|
||||
|
||||
interface HeaderProps {
|
||||
handleChangeLang: (lang: string) => void;
|
||||
}
|
||||
|
||||
function Header({ handleChangeLang }: HeaderProps) {
|
||||
const [setIsOpen] = useSidebarStore((state) => [state.setIsOpen]);
|
||||
|
||||
return (
|
||||
<header className="sm:py-6 py-4 flex justify-between">
|
||||
<a href="/" className="sm:block hidden">
|
||||
<LogoIcon />
|
||||
</a>
|
||||
<a href="/" className="sm:hidden block">
|
||||
<LogoMobileIcon />
|
||||
</a>
|
||||
<div className="flex sm:gap-8 gap-2">
|
||||
<button
|
||||
onClick={() => setIsOpen(true)}
|
||||
className="group relative sm:px-8 px-6 py-2 bg-gradient rounded-full lg:text-base text-sm font-medium leading-normal"
|
||||
>
|
||||
<div className="absolute top-0 left-0 w-full h-full rounded-full bg-black opacity-0 group-hover:opacity-10 transition-all"></div>
|
||||
<span className="relative">
|
||||
<Trans i18nKey={"header.buttonFirst"}>Записаться</Trans>{" "}
|
||||
<span className="sm:inline hidden">
|
||||
<Trans i18nKey={"header.buttonSecond"}>на демонстрацию</Trans>
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
<div className="flex gap-1">
|
||||
<button
|
||||
className={[
|
||||
"px-3 py-1.5 border rounded-full",
|
||||
i18n.language === "ru"
|
||||
? "border-[#D375FF]"
|
||||
: "border-transparent hover:bg-[#3D425C] transition-colors",
|
||||
].join(" ")}
|
||||
onClick={() => handleChangeLang("ru")}
|
||||
>
|
||||
RU
|
||||
</button>
|
||||
<button
|
||||
className={[
|
||||
"px-3 py-1.5 border rounded-full",
|
||||
i18n.language === "en"
|
||||
? "border-[#D375FF]"
|
||||
: "border-transparent hover:bg-[#3D425C] transition-colors",
|
||||
].join(" ")}
|
||||
onClick={() => handleChangeLang("en")}
|
||||
>
|
||||
EN
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
export default Header;
|
||||
@@ -0,0 +1,15 @@
|
||||
.entering {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.entered {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.exiting {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.exited {
|
||||
opacity: 0;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { useEffect } from "react";
|
||||
import useModalStore from "../stores/useModalStore";
|
||||
import "./ModalContainer.css";
|
||||
|
||||
interface ModalContainerProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function ModalContainer({ className }: ModalContainerProps) {
|
||||
const [modal, setModal] = useModalStore((state) => [
|
||||
state.modal,
|
||||
state.setModal,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
function handleKeyDown(e: KeyboardEvent) {
|
||||
if (e.code === "Escape") {
|
||||
setModal(null);
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("keydown", handleKeyDown);
|
||||
return () => document.removeEventListener("keydown", handleKeyDown);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
onClick={() => setModal(null)}
|
||||
className={[
|
||||
"absolute w-full min-h-screen top-0 left-0 flex flex-col justify-center items-center p-8 bg-black bg-opacity-75 transition-opacity cursor-pointer",
|
||||
className,
|
||||
].join(" ")}
|
||||
>
|
||||
<div onClick={(e) => e.stopPropagation()} className="cursor-default">
|
||||
{modal}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ModalContainer;
|
||||
@@ -0,0 +1,43 @@
|
||||
import { ChangeEvent, useState } from "react";
|
||||
|
||||
interface NumberInputProps {
|
||||
min?: number;
|
||||
max?: number;
|
||||
step?: number;
|
||||
defaultValue?: number;
|
||||
onChange: (value: number) => void;
|
||||
}
|
||||
|
||||
function NumberInput({
|
||||
min = 1,
|
||||
max = 100,
|
||||
step = 1,
|
||||
defaultValue = 1,
|
||||
onChange,
|
||||
}: NumberInputProps) {
|
||||
const [value, setValue] = useState<number>(defaultValue);
|
||||
|
||||
function handleChange(e: ChangeEvent<HTMLInputElement>) {
|
||||
let value = +e.target.value;
|
||||
|
||||
if (value < min) value = min;
|
||||
if (value > max) value = max;
|
||||
|
||||
setValue(value);
|
||||
onChange(value);
|
||||
}
|
||||
|
||||
return (
|
||||
<input
|
||||
type="number"
|
||||
min={min}
|
||||
max={max}
|
||||
step={step}
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
className="px-3 py-2 rounded w-full outline-none"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default NumberInput;
|
||||
@@ -0,0 +1,159 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
Config,
|
||||
AllSettings,
|
||||
PixelStreaming,
|
||||
} from "@epicgames-ps/lib-pixelstreamingfrontend-ue5.3";
|
||||
import { Trans } from "react-i18next";
|
||||
|
||||
export interface PixelStreamingWrapperProps {
|
||||
initialSettings?: Partial<AllSettings>;
|
||||
}
|
||||
|
||||
export const PixelStreamingWrapper = ({
|
||||
initialSettings,
|
||||
}: PixelStreamingWrapperProps) => {
|
||||
// A reference to parent div element that the Pixel Streaming library attaches into:
|
||||
const videoParent = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Pixel streaming library instance is stored into this state variable after initialization:
|
||||
const [pixelStreaming, setPixelStreaming] = useState<PixelStreaming>();
|
||||
|
||||
// A boolean state variable that determines if the Click to play overlay is shown:
|
||||
const [clickToPlayVisible, setClickToPlayVisible] = useState<boolean>(false);
|
||||
const [videoInitialized, setVideoInitialized] = useState<boolean>(false);
|
||||
|
||||
// Run on component mount:
|
||||
useEffect(() => {
|
||||
if (videoParent.current) {
|
||||
// Attach Pixel Streaming library to videoParent element:
|
||||
const config = new Config({ initialSettings });
|
||||
const streaming = new PixelStreaming(config, {
|
||||
videoElementParent: videoParent.current,
|
||||
});
|
||||
|
||||
streaming.addEventListener("videoInitialized", () => {
|
||||
setVideoInitialized(true);
|
||||
});
|
||||
|
||||
// register a playStreamRejected handler to show Click to play overlay if needed:
|
||||
streaming.addEventListener("playStreamRejected", () => {
|
||||
setClickToPlayVisible(true);
|
||||
});
|
||||
|
||||
// Save the library instance into component state so that it can be accessed later:
|
||||
setPixelStreaming(streaming);
|
||||
|
||||
document.getElementById("hiddenInput")?.remove();
|
||||
document.getElementById("editTextButton")?.remove();
|
||||
|
||||
// Clean up on component unmount:
|
||||
return () => {
|
||||
try {
|
||||
streaming.disconnect();
|
||||
} catch {
|
||||
//
|
||||
}
|
||||
};
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="relative w-screen h-screen">
|
||||
<div className="w-full h-[100svh]" ref={videoParent} />
|
||||
{!videoInitialized && (
|
||||
<div className="absolute top-0 left-0 w-full h-full flex justify-center items-center">
|
||||
<Trans i18nKey="streamBuffering">Буферизация потока</Trans>
|
||||
</div>
|
||||
)}
|
||||
{clickToPlayVisible && (
|
||||
<div className="absolute top-0 left-0 w-full h-full flex justify-center items-center z-10 bg-[#131317]">
|
||||
<div className="flex flex-col justify-center items-center w-[400px] p-10 space-y-10 rounded-lg">
|
||||
<div className="space-y-4 text-center">
|
||||
<p className="text-4xl font-gilroy">
|
||||
<Trans i18nKey="demoStarted">Демонстрация начата</Trans>
|
||||
</p>
|
||||
<p className="text-[#C5C7CE]">
|
||||
<Trans i18nKey="clickToContinue">
|
||||
Нажмите, чтобы продолжить
|
||||
</Trans>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={() => {
|
||||
pixelStreaming?.play();
|
||||
setClickToPlayVisible(false);
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
width="88"
|
||||
height="88"
|
||||
viewBox="0 0 88 88"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g filter="url(#filter0_b_0_1121)">
|
||||
<path
|
||||
d="M55.6667 43.9999L34.6668 57.9999L34.6668 30L55.6667 43.9999Z"
|
||||
fill="#F2F2F2"
|
||||
stroke="#F2F2F2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<rect
|
||||
x="0.5"
|
||||
y="0.5"
|
||||
width="87"
|
||||
height="87"
|
||||
rx="43.5"
|
||||
stroke="url(#paint0_linear_0_1121)"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter
|
||||
id="filter0_b_0_1121"
|
||||
x="-20"
|
||||
y="-20"
|
||||
width="128"
|
||||
height="128"
|
||||
filterUnits="userSpaceOnUse"
|
||||
colorInterpolationFilters="sRGB"
|
||||
>
|
||||
<feFlood floodOpacity="0" result="BackgroundImageFix" />
|
||||
<feGaussianBlur in="BackgroundImageFix" stdDeviation="10" />
|
||||
<feComposite
|
||||
in2="SourceAlpha"
|
||||
operator="in"
|
||||
result="effect1_backgroundBlur_0_1121"
|
||||
/>
|
||||
<feBlend
|
||||
mode="normal"
|
||||
in="SourceGraphic"
|
||||
in2="effect1_backgroundBlur_0_1121"
|
||||
result="shape"
|
||||
/>
|
||||
</filter>
|
||||
<linearGradient
|
||||
id="paint0_linear_0_1121"
|
||||
x1="88"
|
||||
y1="-2.6226e-06"
|
||||
x2="2.6226e-06"
|
||||
y2="88"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#BC75FF" />
|
||||
<stop offset="1" stopColor="#798FFF" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
import { PixelStreamingWrapper } from "./PixelStreamingWrapper";
|
||||
|
||||
interface PlayerProps {
|
||||
ss: string;
|
||||
}
|
||||
|
||||
export const Player = ({ ss }: PlayerProps) => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<PixelStreamingWrapper
|
||||
initialSettings={{
|
||||
AutoPlayVideo: true,
|
||||
AutoConnect: true,
|
||||
ss,
|
||||
StartVideoMuted: false,
|
||||
HoveringMouse: true,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
import NumberInput from "./NumberInput";
|
||||
import TimePicker from "./TimePicker";
|
||||
|
||||
function SessionScheduleSettings() {
|
||||
return (
|
||||
<div className="text-white flex gap-4">
|
||||
<div className="bg-[#212121] rounded p-8 w-80 h-fit shadow space-y-8">
|
||||
<p className="text-2xl font-gilroy">Настройки расписания сеансов</p>
|
||||
<form className="space-y-4">
|
||||
<div className="flex gap-2">
|
||||
<div className="space-y-1">
|
||||
<p>Время начала:</p>
|
||||
<TimePicker />
|
||||
</div>
|
||||
<div>—</div>
|
||||
<div className="space-y-1">
|
||||
<p>Время конца:</p>
|
||||
<TimePicker />
|
||||
</div>
|
||||
</div>
|
||||
<div className="">
|
||||
<p>Длительность:</p>
|
||||
<NumberInput
|
||||
min={15}
|
||||
max={60}
|
||||
step={5}
|
||||
defaultValue={30}
|
||||
onChange={(value) => console.log(value)}
|
||||
/>
|
||||
</div>
|
||||
<button type="submit" className="px-4 py-2 bg-gradient rounded">
|
||||
Сохранить
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<div className="bg-[#212121] rounded p-4 w-80 h-fit shadow">preview</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default SessionScheduleSettings;
|
||||
@@ -0,0 +1,33 @@
|
||||
.sidebar.entering {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.sidebar.entered {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.sidebar.exiting {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.sidebar.exited {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeOut {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/* eslint-disable no-irregular-whitespace */
|
||||
import "./Sidebar.css";
|
||||
import useSidebarTabStore from "../stores/useSidebarStore";
|
||||
import SidebarTab1 from "./SidebarTab1";
|
||||
import SidebarTab2 from "./SidebarTab2";
|
||||
import SidebarTab3 from "./SidebarTab3";
|
||||
import SidebarTab4 from "./SidebarTab4";
|
||||
import SidebarTab5 from "./SidebarTab5";
|
||||
|
||||
interface SidebarProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function Sidebar({ className }: SidebarProps) {
|
||||
const [currentTab] = useSidebarTabStore((state) => [state.currentTab]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={[
|
||||
"sidebar fixed top-0 left-0 w-full h-full bg-[#14161F] bg-opacity-90 transition-opacity text-white",
|
||||
className,
|
||||
].join(" ")}
|
||||
>
|
||||
<div className="absolute right-0 h-full sm:w-[408px] w-full bg-[#14161F] overflow-y-auto">
|
||||
{currentTab === 1 && <SidebarTab1 />}
|
||||
{currentTab === 2 && <SidebarTab2 />}
|
||||
{currentTab === 3 && <SidebarTab3 />}
|
||||
{currentTab === 4 && <SidebarTab4 />}
|
||||
{currentTab === 5 && <SidebarTab5 />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Sidebar;
|
||||
@@ -0,0 +1,69 @@
|
||||
/* eslint-disable no-irregular-whitespace */
|
||||
import { Trans } from "react-i18next";
|
||||
import useSidebarTabStore from "../stores/useSidebarStore";
|
||||
import Calendar from "./Calendar";
|
||||
import CloseIcon from "./icons/CloseIcon";
|
||||
|
||||
function SidebarTab1() {
|
||||
const [currentTab, setCurrentTab, setIsOpen, setSelectedDay] =
|
||||
useSidebarTabStore((state) => [
|
||||
state.currentTab,
|
||||
state.setCurrentTab,
|
||||
state.setIsOpen,
|
||||
state.setSelectedDay,
|
||||
]);
|
||||
|
||||
function handleSelectDay(day: Date) {
|
||||
setSelectedDay(day);
|
||||
setCurrentTab(currentTab + 1);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="sm:p-8 p-6 flex flex-col justify-between sm:gap-8 gap-6 min-h-full">
|
||||
<div>
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
<p className="text-2xl text-gradient font-semibold font-gilroy w-fit leading-snug">
|
||||
<Trans i18nKey={"sidebar.title1"}>Дата и время</Trans>
|
||||
</p>
|
||||
<button
|
||||
onClick={() => setIsOpen(false)}
|
||||
className="transition-opacity hover:opacity-50"
|
||||
>
|
||||
<CloseIcon />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="mt-2">
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<div className="border-b border-[#798FFF] p-4 text-center">
|
||||
<p className="leading-none font-gilroy font-semibold text-sm">
|
||||
<Trans i18nKey={"sidebar.date"}>Дата</Trans>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="border-b border-[#3D425C] p-4 text-center">
|
||||
<p className="leading-none font-gilroy font-semibold text-sm">
|
||||
<Trans i18nKey={"sidebar.time"}>Время</Trans>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="sm:mt-8 mt-6">
|
||||
<Calendar handleSelect={(day) => handleSelectDay(day)} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col sm:gap-6 gap-4">
|
||||
<p className="text-center text-xs opacity-50 leading-tight">
|
||||
<Trans i18nKey={"sidebar.notice"}>
|
||||
Запись на демонстрацию работает в ознакомительном режиме и не
|
||||
сохраняет введенные данные
|
||||
</Trans>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default SidebarTab1;
|
||||
@@ -0,0 +1,102 @@
|
||||
/* eslint-disable no-irregular-whitespace */
|
||||
import { Trans } from "react-i18next";
|
||||
import useSidebarTabStore from "../stores/useSidebarStore";
|
||||
import TimeSelector from "./TimeSelector";
|
||||
import CloseIcon from "./icons/CloseIcon";
|
||||
import { format } from "date-fns";
|
||||
import i18n from "../i18n";
|
||||
import { enUS, ru } from "date-fns/locale";
|
||||
|
||||
function SidebarTab2() {
|
||||
const [
|
||||
currentTab,
|
||||
setCurrentTab,
|
||||
setIsOpen,
|
||||
setSelectedTime,
|
||||
selectedDay,
|
||||
selectedTime,
|
||||
] = useSidebarTabStore((state) => [
|
||||
state.currentTab,
|
||||
state.setCurrentTab,
|
||||
state.setIsOpen,
|
||||
state.setSelectedTime,
|
||||
state.selectedDay,
|
||||
state.selectedTime,
|
||||
]);
|
||||
|
||||
function handleSelectTime(time: string) {
|
||||
setSelectedTime(time);
|
||||
setCurrentTab(currentTab + 1);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="sm:p-8 p-6 flex flex-col justify-between sm:gap-8 gap-6 min-h-full">
|
||||
<div>
|
||||
<div className="flex items-start justify-between">
|
||||
<p className="text-2xl text-gradient font-semibold font-gilroy w-fit leading-snug">
|
||||
<Trans i18nKey={"sidebar.title1"}>Дата и время</Trans>
|
||||
</p>
|
||||
<button
|
||||
onClick={() => setIsOpen(false)}
|
||||
className="transition-opacity hover:opacity-50"
|
||||
>
|
||||
<CloseIcon />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="mt-2">
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<div className="border-b border-[#3D425C] p-4 text-center">
|
||||
<p className="leading-none font-gilroy font-semibold text-sm">
|
||||
{selectedDay &&
|
||||
format(
|
||||
selectedDay,
|
||||
"dd MMMM",
|
||||
i18n.language === "ru" ? { locale: ru } : { locale: enUS }
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="border-b border-[#798FFF] p-4 text-center">
|
||||
<p className="leading-none font-gilroy font-semibold text-sm">
|
||||
{selectedTime ? (
|
||||
selectedTime
|
||||
) : (
|
||||
<Trans i18nKey={"sidebar.time"}>Время</Trans>
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-6">
|
||||
<TimeSelector
|
||||
handleSelect={(time: string) => handleSelectTime(time)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col sm:gap-6 gap-4">
|
||||
<p className="text-center text-xs opacity-50 leading-tight">
|
||||
<Trans i18nKey={"sidebar.notice"}>
|
||||
Запись на демонстрацию работает в ознакомительном режиме и не
|
||||
сохраняет введенные данные
|
||||
</Trans>
|
||||
</p>
|
||||
|
||||
<div className="flex sm:gap-4 gap-2">
|
||||
<button
|
||||
onClick={() => setCurrentTab(currentTab - 1)}
|
||||
className="px-6 sm:py-4 py-3.5 border border-[#3D425C] rounded-full font-medium group w-full"
|
||||
>
|
||||
<span className="opacity-80 transition-opacity group-hover:opacity-100 sm:text-base text-sm">
|
||||
<Trans i18nKey={"sidebar.buttonBack"}>Назад</Trans>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default SidebarTab2;
|
||||
@@ -0,0 +1,91 @@
|
||||
/* eslint-disable no-irregular-whitespace */
|
||||
import { Trans } from "react-i18next";
|
||||
import useSidebarTabStore from "../stores/useSidebarStore";
|
||||
import ContactsForm from "./ContactsForm";
|
||||
import ArrowRightIcon from "./icons/ArrowRightIcon";
|
||||
import CloseIcon from "./icons/CloseIcon";
|
||||
|
||||
function SidebarTab3() {
|
||||
const [
|
||||
currentTab,
|
||||
setCurrentTab,
|
||||
setIsOpen,
|
||||
name,
|
||||
phone,
|
||||
email,
|
||||
] = useSidebarTabStore((state) => [
|
||||
state.currentTab,
|
||||
state.setCurrentTab,
|
||||
state.setIsOpen,
|
||||
state.name,
|
||||
state.phone,
|
||||
state.email,
|
||||
state.selectedDay,
|
||||
state.selectedTime,
|
||||
]);
|
||||
|
||||
function handleSubmit() {
|
||||
if (!name || !phone || !email) {
|
||||
return;
|
||||
}
|
||||
|
||||
setCurrentTab(currentTab + 1);
|
||||
}
|
||||
|
||||
return (
|
||||
<form
|
||||
onSubmit={handleSubmit}
|
||||
className="sm:p-8 p-6 flex flex-col justify-between sm:gap-8 gap-6 min-h-full"
|
||||
>
|
||||
<div>
|
||||
<div className="flex items-start justify-between">
|
||||
<p className="text-2xl text-gradient font-semibold font-gilroy w-fit leading-snug">
|
||||
<Trans i18nKey={"sidebar.title2"}>Контакты</Trans>
|
||||
</p>
|
||||
<button
|
||||
onClick={() => setIsOpen(false)}
|
||||
className="transition-opacity hover:opacity-50"
|
||||
>
|
||||
<CloseIcon />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="sm:mt-6 mt-4">
|
||||
<ContactsForm />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col sm:gap-6 gap-4">
|
||||
<p className="text-center text-xs opacity-50 leading-tight">
|
||||
<Trans i18nKey={"sidebar.notice"}>
|
||||
Запись на демонстрацию работает в ознакомительном режиме и не
|
||||
сохраняет введенные данные
|
||||
</Trans>
|
||||
</p>
|
||||
|
||||
<div className="flex sm:gap-4 gap-2">
|
||||
<button
|
||||
onClick={() => setCurrentTab(currentTab - 1)}
|
||||
className="px-6 py-4 border border-[#3D425C] rounded-full sm:text-base text-sm font-medium group w-fit"
|
||||
>
|
||||
<span className="opacity-80 transition-opacity group-hover:opacity-100">
|
||||
<Trans i18nKey={"sidebar.buttonBack"}>Назад</Trans>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
className="px-6 py-3 bg-gradient rounded-full sm:text-base text-sm font-medium flex items-center justify-between w-full"
|
||||
>
|
||||
<span>
|
||||
<Trans i18nKey={"sidebar.buttonNext"}>Далее</Trans>
|
||||
</span>
|
||||
<ArrowRightIcon className="sm:w-8 sm:h-8 w-6 h-6" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
export default SidebarTab3;
|
||||
@@ -0,0 +1,198 @@
|
||||
/* eslint-disable no-irregular-whitespace */
|
||||
import { format, parse } from "date-fns";
|
||||
import useSidebarTabStore from "../stores/useSidebarStore";
|
||||
import ArrowRightIcon from "./icons/ArrowRightIcon";
|
||||
import CloseIcon from "./icons/CloseIcon";
|
||||
import { enUS, ru } from "date-fns/locale";
|
||||
import ky from "ky";
|
||||
import { Trans } from "react-i18next";
|
||||
import i18n from "../i18n";
|
||||
import { useState } from "react";
|
||||
import LoaderIcon from "./icons/LoaderIcon";
|
||||
|
||||
function SidebarTab4() {
|
||||
const [
|
||||
currentTab,
|
||||
setCurrentTab,
|
||||
setIsOpen,
|
||||
selectedDay,
|
||||
selectedTime,
|
||||
name,
|
||||
phone,
|
||||
email,
|
||||
] = useSidebarTabStore((state) => [
|
||||
state.currentTab,
|
||||
state.setCurrentTab,
|
||||
state.setIsOpen,
|
||||
state.selectedDay,
|
||||
state.selectedTime,
|
||||
state.name,
|
||||
state.phone,
|
||||
state.email,
|
||||
]);
|
||||
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
|
||||
async function handleClickSignUp() {
|
||||
if (!selectedTime || !selectedDay) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
|
||||
const startAt = parse(selectedTime, "HH:mm", selectedDay);
|
||||
|
||||
try {
|
||||
await ky
|
||||
.post("https://coord.graff.tech/scheduled_sessions", {
|
||||
json: {
|
||||
username: "test",
|
||||
name,
|
||||
phone,
|
||||
email,
|
||||
title: "nksJukovaDev",
|
||||
startAt,
|
||||
},
|
||||
})
|
||||
.json();
|
||||
|
||||
setCurrentTab(currentTab + 1);
|
||||
|
||||
setIsLoading(false);
|
||||
} catch (error) {
|
||||
setIsLoading(false);
|
||||
if (error instanceof Error) {
|
||||
alert(error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="sm:p-8 p-6 flex flex-col justify-between sm:gap-8 gap-6 min-h-full">
|
||||
<div>
|
||||
<div className="flex items-start justify-between">
|
||||
<p className="text-2xl text-gradient font-semibold font-gilroy w-fit leading-snug">
|
||||
<Trans i18nKey={"sidebar.title3"}>Проверка заявки</Trans>
|
||||
</p>
|
||||
<button
|
||||
onClick={() => setIsOpen(false)}
|
||||
className="transition-opacity hover:opacity-50"
|
||||
>
|
||||
<CloseIcon />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="sm:mt-6 mt-4">
|
||||
<div className="sm:mt-6 mt-4 sm:p-6 p-4 flex flex-col gap-6 font-semibold font-gilroy border border-[#3D425C] ">
|
||||
<p className="leading-tight">
|
||||
<Trans i18nKey={"sidebar.sessionDetails"}>Детали сеанса</Trans>
|
||||
</p>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex justify-between sm:text-base text-sm">
|
||||
<p className="opacity-50">
|
||||
<Trans i18nKey={"sidebar.date"}>Дата</Trans>
|
||||
</p>
|
||||
<p>
|
||||
{selectedDay &&
|
||||
format(
|
||||
selectedDay,
|
||||
"dd MMMM",
|
||||
i18n.language === "ru" ? { locale: ru } : { locale: enUS }
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex justify-between sm:text-base text-sm">
|
||||
<p className="opacity-50">
|
||||
<Trans i18nKey={"sidebar.time"}>Время</Trans>
|
||||
</p>
|
||||
<p>{selectedTime}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="sm:p-6 p-4 flex flex-col gap-6 font-semibold font-gilroy border border-[#3D425C] border-t-0">
|
||||
<p className="leading-tight">
|
||||
<Trans i18nKey={"sidebar.contactDetails"}>
|
||||
Контактные данные
|
||||
</Trans>
|
||||
</p>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex justify-between sm:text-base text-sm">
|
||||
<p className="opacity-50">
|
||||
<Trans i18nKey={"sidebar.name"}>Имя</Trans>
|
||||
</p>
|
||||
<p>{name}</p>
|
||||
</div>
|
||||
<div className="flex justify-between sm:text-base text-sm">
|
||||
<p className="opacity-50">
|
||||
<Trans i18nKey={"sidebar.phone"}>Телефон</Trans>
|
||||
</p>
|
||||
<p>{phone}</p>
|
||||
</div>
|
||||
<div className="flex justify-between sm:text-base text-sm">
|
||||
<p className="opacity-50">Email</p>
|
||||
<p>{email}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="sm:p-6 p-4 border border-t-0 border-[#3D425C]">
|
||||
<p className="text-xs">
|
||||
<Trans i18nKey={"sidebar.submitNotice1"}>
|
||||
Нажимая кнопку записаться, вы принимаете
|
||||
</Trans>{" "}
|
||||
<a href="#" className="text-[#798FFF]">
|
||||
<Trans i18nKey={"sidebar.submitNotice2"}>
|
||||
условия использования
|
||||
</Trans>
|
||||
</a>{" "}
|
||||
<Trans i18nKey={"sidebar.submitNotice3"}>и</Trans>{" "}
|
||||
<a href="#" className="text-[#798FFF]">
|
||||
<Trans i18nKey={"sidebar.submitNotice4"}>
|
||||
политику конфиденциальности
|
||||
</Trans>
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col sm:gap-6 gap-4">
|
||||
<p className="text-center text-xs opacity-50 leading-tight">
|
||||
<Trans i18nKey={"sidebar.notice"}>
|
||||
Запись на демонстрацию работает в ознакомительном режиме и не
|
||||
сохраняет введенные данные
|
||||
</Trans>
|
||||
</p>
|
||||
|
||||
<div className="flex sm:gap-4 gap-2">
|
||||
<button
|
||||
onClick={() => setCurrentTab(currentTab - 1)}
|
||||
className="px-6 py-4 border border-[#3D425C] rounded-full sm:text-base text-sm font-medium group w-fit"
|
||||
>
|
||||
<span className="opacity-80 transition-opacity group-hover:opacity-100">
|
||||
<Trans i18nKey={"sidebar.buttonBack"}>Назад</Trans>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
disabled={isLoading}
|
||||
onClick={() => void handleClickSignUp()}
|
||||
className="px-6 py-3 bg-gradient rounded-full sm:text-base text-sm font-medium flex items-center justify-between w-full disabled:opacity-75"
|
||||
>
|
||||
<span>
|
||||
<Trans i18nKey={"sidebar.buttonSignUp"}>Записаться</Trans>
|
||||
</span>
|
||||
{!isLoading ? (
|
||||
<ArrowRightIcon className="sm:w-8 sm:h-8 w-6 h-6" />
|
||||
) : (
|
||||
<LoaderIcon className="sm:w-8 sm:h-8 w-6 h-6 animate-spin" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default SidebarTab4;
|
||||
@@ -0,0 +1,97 @@
|
||||
/* eslint-disable no-irregular-whitespace */
|
||||
import { Trans } from "react-i18next";
|
||||
import useSidebarTabStore from "../stores/useSidebarStore";
|
||||
import ArrowRightIcon from "./icons/ArrowRightIcon";
|
||||
import MailGradientIcon from "./icons/MailGradientIcon";
|
||||
import PhoneGradientIcon from "./icons/PhoneGradientIcon";
|
||||
import WebGradientIcon from "./icons/WebGradientIcon";
|
||||
|
||||
function SidebarTab5() {
|
||||
const [setIsOpen, name] = useSidebarTabStore((state) => [
|
||||
state.setIsOpen,
|
||||
state.name,
|
||||
]);
|
||||
|
||||
return (
|
||||
<div className="sm:p-8 p-6 flex flex-col justify-between sm:gap-8 gap-6 min-h-full">
|
||||
<div>
|
||||
<div className="flex items-start justify-between">
|
||||
<p className="text-2xl font-semibold font-gilroy w-fit leading-snug">
|
||||
<span className="text-gradient">{name},</span>
|
||||
<br />
|
||||
<span className="text-gradient">
|
||||
<Trans i18nKey={"sidebar.title4_1"}>спасибо за запись</Trans>
|
||||
</span>
|
||||
<br />
|
||||
<Trans i18nKey={"sidebar.title4_2"}>
|
||||
на удаленную демонстрацию!
|
||||
</Trans>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p className="sm:mt-6 mt-4 text-sm">
|
||||
<Trans i18nKey={"sidebar.tab5text1"}>
|
||||
В ближайшее время мы отправим на ваш почтовый адрес всю
|
||||
дополнительную информацию о сеансе и ссылку для подключения.
|
||||
</Trans>
|
||||
</p>
|
||||
|
||||
<div className="mt-8 pb-6 font-gilroy font-semibold border-b border-[#3D425C]">
|
||||
<p>
|
||||
<Trans i18nKey={"sidebar.tab5text2"}>Возникли вопросы?</Trans>
|
||||
</p>
|
||||
|
||||
<div className="mt-6 flex justify-between">
|
||||
<div>
|
||||
<p className="opacity-50 text-sm leading-none">
|
||||
<Trans i18nKey={"sidebar.tab5text3"}>Свяжитесь с нами</Trans>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
<p className="text-sm font-gilroy flex gap-3 items-center justify-end leading-none">
|
||||
<a href="tel:88007700076">8 800 770 00 76</a>
|
||||
<PhoneGradientIcon />
|
||||
</p>
|
||||
|
||||
<p className="text-sm font-gilroy flex gap-3 items-center justify-end leading-none">
|
||||
<a href="mailto:info@graff.tech">info@graff.tech</a>
|
||||
<MailGradientIcon />
|
||||
</p>
|
||||
|
||||
<p className="text-sm font-gilroy flex gap-3 items-center justify-end leading-none">
|
||||
<a href="https://estate.graff.tech" target="_blank">
|
||||
estate.graff.tech
|
||||
</a>
|
||||
<WebGradientIcon />
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col sm:gap-6 gap-4">
|
||||
<p className="text-center text-xs opacity-50 leading-tight">
|
||||
<Trans i18nKey={"sidebar.notice"}>
|
||||
Запись на демонстрацию работает в ознакомительном режиме и не
|
||||
сохраняет введенные данные
|
||||
</Trans>
|
||||
</p>
|
||||
|
||||
<div className="flex sm:gap-4 gap-2">
|
||||
<button
|
||||
onClick={() => setIsOpen(false)}
|
||||
className="px-6 py-3 bg-gradient rounded-full sm:text-base text-sm font-medium flex items-center justify-between w-full"
|
||||
>
|
||||
<span>
|
||||
<Trans i18nKey={"sidebar.buttonHome"}>На главную</Trans>
|
||||
</span>
|
||||
<ArrowRightIcon className="w-8 h-8" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default SidebarTab5;
|
||||
@@ -0,0 +1,5 @@
|
||||
function TimePicker() {
|
||||
return <div>TimePicker</div>;
|
||||
}
|
||||
|
||||
export default TimePicker;
|
||||
@@ -0,0 +1,60 @@
|
||||
import { Trans } from "react-i18next";
|
||||
import useSidebarStore from "../stores/useSidebarStore";
|
||||
|
||||
interface TimeSelectorProps {
|
||||
handleSelect: (time: string) => void;
|
||||
}
|
||||
|
||||
function TimeSelector({ handleSelect }: TimeSelectorProps) {
|
||||
const [selectedTime] = useSidebarStore((state) => [state.selectedTime]);
|
||||
|
||||
const times = [
|
||||
{ value: "10:00", active: true },
|
||||
{ value: "10:30", active: false },
|
||||
{ value: "11:00", active: true },
|
||||
{ value: "11:30", active: true },
|
||||
{ value: "12:00", active: true },
|
||||
{ value: "12:30", active: true },
|
||||
{ value: "13:00", active: true },
|
||||
{ value: "13:30", active: true },
|
||||
{ value: "14:00", active: false },
|
||||
{ value: "14:30", active: false },
|
||||
{ value: "15:30", active: true },
|
||||
{ value: "15:00", active: true },
|
||||
{ value: "16:30", active: true },
|
||||
{ value: "16:00", active: true },
|
||||
{ value: "17:30", active: true },
|
||||
{ value: "17:00", active: true },
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="grid grid-cols-4 gap-2 font-medium">
|
||||
{times.map((time, index) => (
|
||||
<button
|
||||
key={index}
|
||||
onClick={() => handleSelect(time.value)}
|
||||
disabled={!time.active}
|
||||
className={[
|
||||
"min-w-[40px] h-10 text-[#798FFF] text-sm rounded-full border border-[#798FFF] flex justify-center items-center transition-colors hover:bg-[#798FFF] hover:bg-opacity-20 hover:text-white disabled:text-[#3D425C] disabled:hover:bg-transparent disabled:border-transparent",
|
||||
time.value === selectedTime
|
||||
? "bg-[#798FFF] text-white"
|
||||
: "text-[#798FFF]",
|
||||
].join(" ")}
|
||||
>
|
||||
{time.value}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="mt-8 flex items-center gap-2">
|
||||
<div className="w-4 h-4 bg-[#798FFF] rounded"></div>
|
||||
<p className="text-[#798FFF]">
|
||||
- <Trans i18nKey={"sidebar.available"}>запись доступна</Trans>
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default TimeSelector;
|
||||
@@ -0,0 +1,28 @@
|
||||
interface IconProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function ArrowRightIcon({ className }: IconProps) {
|
||||
return (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={className}
|
||||
>
|
||||
<g id="Icon/Arrow_Right">
|
||||
<path
|
||||
id="Vector 106"
|
||||
d="M18 12L6 12M18 12L11.6364 18M18 12L11.6364 6"
|
||||
stroke="white"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="square"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default ArrowRightIcon;
|
||||
@@ -0,0 +1,18 @@
|
||||
function AsteriskIcon() {
|
||||
return (
|
||||
<svg
|
||||
width="12"
|
||||
height="13"
|
||||
viewBox="0 0 12 13"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M4.81534 12.2727L4.9858 7.58523L1.02273 10.0994L0 8.30966L4.17614 6.13636L0 3.96307L1.02273 2.1733L4.9858 4.6875L4.81534 0H6.8608L6.69034 4.6875L10.6534 2.1733L11.6761 3.96307L7.5 6.13636L11.6761 8.30966L10.6534 10.0994L6.69034 7.58523L6.8608 12.2727H4.81534Z"
|
||||
fill="white"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default AsteriskIcon;
|
||||
@@ -0,0 +1,41 @@
|
||||
interface IconProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function CheckGradientIcon({ className }: IconProps) {
|
||||
return (
|
||||
<svg
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 32 32"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={className}
|
||||
>
|
||||
<g id="Icon/Check">
|
||||
<path
|
||||
id="Vector 1836 (Stroke)"
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M26.3298 9.78103C26.819 10.3314 26.7694 11.1742 26.2191 11.6634L14.2191 22.3301C13.6914 22.7991 12.8896 22.7755 12.3904 22.2763L5.72378 15.6097C5.20308 15.089 5.20308 14.2447 5.72378 13.724C6.24448 13.2033 7.0887 13.2033 7.60939 13.724L13.3871 19.5017L24.4474 9.6703C24.9978 9.18107 25.8406 9.23065 26.3298 9.78103Z"
|
||||
fill="url(#paint0_linear_53_10278)"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="paint0_linear_53_10278"
|
||||
x1="5.33325"
|
||||
y1="32.1907"
|
||||
x2="29.4088"
|
||||
y2="29.927"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop offset="0.167052" stopColor="#798FFF" />
|
||||
<stop offset="0.963542" stopColor="#D375FF" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default CheckGradientIcon;
|
||||
@@ -0,0 +1,24 @@
|
||||
function ChevronLeftIcon() {
|
||||
return (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g id="Icon/Chevron_Left">
|
||||
<path
|
||||
id="Vector 106"
|
||||
d="M15 19L8 12L15 5"
|
||||
stroke="white"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default ChevronLeftIcon;
|
||||
@@ -0,0 +1,24 @@
|
||||
function ChevronRightIcon() {
|
||||
return (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g id="Icon/Chevron_Right">
|
||||
<path
|
||||
id="Vector 106"
|
||||
d="M9.00002 19L16 12L9.00002 5"
|
||||
stroke="white"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default ChevronRightIcon;
|
||||
@@ -0,0 +1,24 @@
|
||||
function CloseIcon() {
|
||||
return (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g id="Icon/Close">
|
||||
<path
|
||||
id="Vector 106"
|
||||
d="M12.0002 11.9999L17.6572 6.34331M12.0002 11.9999L6.34337 6.34302M12.0002 11.9999L17.6571 17.6567M12.0002 11.9999L6.34326 17.6568"
|
||||
stroke="white"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default CloseIcon;
|
||||
@@ -0,0 +1,39 @@
|
||||
interface IconProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function LoaderIcon({ className }: IconProps) {
|
||||
return (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={className}
|
||||
>
|
||||
<g id="Icon/Circle">
|
||||
<path
|
||||
id="Ellipse 221"
|
||||
d="M18.9999 12C19.5523 12 20.0064 11.5505 19.9376 11.0025C19.745 9.46994 19.1116 8.01808 18.1039 6.82871C16.8797 5.38372 15.1826 4.41989 13.3144 4.10872C11.4463 3.79755 9.52839 4.15922 7.90189 5.12938C6.27539 6.09953 5.04582 7.61525 4.43194 9.40685C3.81806 11.1985 3.85968 13.1497 4.54941 14.9135C5.23914 16.6773 6.53224 18.1392 8.19863 19.0391C9.86502 19.9391 11.7966 20.2186 13.6498 19.828C15.1751 19.5066 16.5658 18.7483 17.6578 17.6559C18.0483 17.2653 17.9652 16.6317 17.529 16.2929C17.0927 15.9542 16.4693 16.0409 16.0629 16.4149C15.2735 17.1413 14.2989 17.6472 13.2373 17.8709C11.8475 18.1638 10.3988 17.9541 9.14904 17.2792C7.89928 16.6043 6.92948 15.5079 6.4122 14.1851C5.89491 12.8623 5.86369 11.3989 6.32409 10.0552C6.78449 8.71152 7.70665 7.57476 8.92649 6.84716C10.1463 6.11956 11.5847 5.84832 12.9858 6.08169C14.3869 6.31506 15.6597 7.03791 16.5778 8.12163C17.2791 8.94938 17.7387 9.94667 17.9167 11.0045C18.0083 11.5492 18.4476 12 18.9999 12Z"
|
||||
fill="url(#paint0_angular_16_1186)"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<radialGradient
|
||||
id="paint0_angular_16_1186"
|
||||
cx="0"
|
||||
cy="0"
|
||||
r="1"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(12 12) rotate(45) scale(7.9196)"
|
||||
>
|
||||
<stop offset="0.874517" stopColor="white" />
|
||||
<stop offset="0.982613" stopColor="white" stopOpacity="0" />
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default LoaderIcon;
|
||||
@@ -0,0 +1,175 @@
|
||||
function LogoMobileIcon() {
|
||||
return (
|
||||
<svg
|
||||
width="40"
|
||||
height="40"
|
||||
viewBox="0 0 40 40"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g id="LogoMobile_GRAFFinteractive">
|
||||
<path
|
||||
id="G Base"
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M20.008 39.7195C30.8979 39.7195 39.7259 30.828 39.7259 19.8597C39.7259 18.5165 39.5935 17.2043 39.3411 15.9358H27.4287V23.8103H31.2726C29.6515 28.4886 25.2322 31.845 20.0345 31.845C13.4625 31.845 8.13482 26.479 8.13482 19.8597C8.13482 13.2405 13.4625 7.8745 20.0345 7.8745V0H20.008C9.11807 0 0.290039 8.89152 0.290039 19.8597C0.290039 30.828 9.11807 39.7195 20.008 39.7195Z"
|
||||
fill="#798FFF"
|
||||
/>
|
||||
<path
|
||||
id="Inner G"
|
||||
d="M19.0163 0.0241028C10.5443 1.34406 4.05957 8.70687 4.05957 17.592C4.05957 27.4109 11.9788 35.3706 21.7477 35.3706C31.5166 35.3706 39.4359 27.4109 39.4359 17.592C39.4359 17.0257 39.4096 16.4656 39.358 15.9128H27.4228V23.7759H31.269C29.647 28.4474 25.2249 31.7989 20.024 31.7989C13.4479 31.7989 8.11698 26.4407 8.11698 19.831C8.11698 13.5806 12.8841 8.44938 18.9632 7.90996C19.3127 7.87896 19.6665 7.8631 20.024 7.8631V0H19.9975C19.6685 0 19.3413 0.00810087 19.0163 0.0241028Z"
|
||||
fill="#D375FF"
|
||||
/>
|
||||
<path
|
||||
id="G_01"
|
||||
opacity="0.3"
|
||||
d="M17.7148 8.11785C18.4573 7.97602 19.2239 7.90176 20.0079 7.90176V0H19.981C18.3936 0 16.8493 0.183713 15.3684 0.530935L17.7148 8.11785Z"
|
||||
fill="black"
|
||||
fillOpacity="0.6"
|
||||
/>
|
||||
<path
|
||||
id="G_02"
|
||||
opacity="0.3"
|
||||
d="M7.31813 4.49037L6.08936 22.3241L7.84573 20.6509C7.8353 20.4452 7.83003 20.2381 7.83003 20.0298C7.83003 13.4214 13.14 8.05568 19.7179 7.98257L11.8297 1.7395C10.2002 2.45588 8.68475 3.38447 7.31813 4.49037Z"
|
||||
fill="black"
|
||||
fillOpacity="0.6"
|
||||
/>
|
||||
<path
|
||||
id="G_03"
|
||||
opacity="0.3"
|
||||
d="M0.290807 20.0851L9.2762 24.9334C8.5649 23.4064 8.16779 21.7041 8.16779 19.9091C8.16779 18.1118 8.56599 16.4072 9.27911 14.8786L6.52677 5.50854C2.68747 9.11382 0.290039 14.232 0.290039 19.9091C0.290039 19.9679 0.290296 20.0265 0.290807 20.0851Z"
|
||||
fill="black"
|
||||
fillOpacity="0.6"
|
||||
/>
|
||||
<path
|
||||
id="G_04"
|
||||
opacity="0.3"
|
||||
d="M0.290039 19.2891L12.2113 37.1621L25.2275 37.4002L20.1497 31.8045C20.0963 31.8052 20.0428 31.8056 19.9892 31.8056C13.4289 31.8056 8.1107 26.488 8.1107 19.9286C8.1107 19.6619 8.11948 19.3973 8.13679 19.135L0.295555 19.135C0.293519 19.1863 0.291681 19.2377 0.290039 19.2891Z"
|
||||
fill="black"
|
||||
fillOpacity="0.6"
|
||||
/>
|
||||
<path
|
||||
id="G_05"
|
||||
opacity="0.3"
|
||||
d="M8.94359 36.2405L16.5283 31.2537C11.6213 29.7362 8.06526 25.2578 8.06526 19.9692C8.06526 19.2897 8.12397 18.6236 8.23671 17.9753L3.1897 30.4732C4.6799 32.7607 6.6425 34.7267 8.94359 36.2405Z"
|
||||
fill="black"
|
||||
fillOpacity="0.6"
|
||||
/>
|
||||
<path
|
||||
id="G_06"
|
||||
opacity="0.3"
|
||||
d="M26.3874 38.6783C24.3639 39.3535 22.1958 39.7195 19.9409 39.7195C15.7761 39.7195 11.9074 38.4707 8.69922 36.3323L16.2931 31.3118C17.4522 31.6746 18.6869 31.8704 19.9679 31.8704C20.0152 31.8704 20.0624 31.8702 20.1095 31.8696L26.3874 38.6783Z"
|
||||
fill="black"
|
||||
fillOpacity="0.6"
|
||||
/>
|
||||
<path
|
||||
id="G_07"
|
||||
opacity="0.3"
|
||||
d="M24.3574 39.2353C22.9375 39.5522 21.4602 39.7195 19.9434 39.7195C19.2861 39.7195 18.6364 39.6881 17.9954 39.6268L16.2383 31.3118C16.4755 31.3881 16.716 31.4574 16.9594 31.5194L24.3574 39.2353Z"
|
||||
fill="black"
|
||||
fillOpacity="0.4"
|
||||
/>
|
||||
<path
|
||||
id="G_08"
|
||||
opacity="0.3"
|
||||
d="M27.5471 19.135L39.4359 16.3252C39.414 16.1983 39.3911 16.0719 39.367 15.9458H27.5471V19.135Z"
|
||||
fill="black"
|
||||
fillOpacity="0.6"
|
||||
/>
|
||||
<path
|
||||
id="G_09"
|
||||
opacity="0.3"
|
||||
d="M30.0187 23.7737L39.436 15.9589C39.4351 15.9545 39.4343 15.9502 39.4334 15.9458L27.8372 23.7737H30.0187Z"
|
||||
fill="black"
|
||||
fillOpacity="0.4"
|
||||
/>
|
||||
<path
|
||||
id="G_10"
|
||||
opacity="0.3"
|
||||
d="M35.5462 15.9458L26.9673 38.2699C30.3467 36.9788 33.2796 34.7825 35.4704 31.977L36.8263 15.9458H35.5462Z"
|
||||
fill="black"
|
||||
fillOpacity="0.6"
|
||||
/>
|
||||
<path
|
||||
id="G_11"
|
||||
opacity="0.3"
|
||||
d="M39.3486 15.9458L26.9673 38.2699C34.4249 35.4453 39.726 28.2432 39.726 19.8045C39.726 18.484 39.5961 17.1937 39.3486 15.9458Z"
|
||||
fill="black"
|
||||
fillOpacity="0.4"
|
||||
/>
|
||||
<g id="Cube base">
|
||||
<path d="M31.6154 0H39.4359V7.82793H31.6154V0Z" fill="#798FFF" />
|
||||
<path
|
||||
d="M39.4359 7.82793H31.6154L27.5471 11.8869H35.078L39.4359 7.82793Z"
|
||||
fill="#798FFF"
|
||||
/>
|
||||
<path
|
||||
d="M31.6154 7.82793V0L27.5471 4.34885V11.8869L31.6154 7.82793Z"
|
||||
fill="#798FFF"
|
||||
/>
|
||||
</g>
|
||||
<path
|
||||
id="Cube_01"
|
||||
opacity="0.3"
|
||||
d="M39.436 6.74606V7.83813L35.1277 11.8868H34.2166V6.66821L39.436 6.74606Z"
|
||||
fill="black"
|
||||
fillOpacity="0.6"
|
||||
/>
|
||||
<path
|
||||
id="Cube_02"
|
||||
opacity="0.3"
|
||||
d="M27.5471 11.8869V4.23088L33.0071 3.76904L34.7964 4.24201L27.5471 11.8869Z"
|
||||
fill="black"
|
||||
fillOpacity="0.6"
|
||||
/>
|
||||
<path
|
||||
id="Cube_03"
|
||||
opacity="0.3"
|
||||
d="M31.0253 0.869873L27.5471 4.45208V11.887H32.1866L31.0253 0.869873Z"
|
||||
fill="black"
|
||||
fillOpacity="0.6"
|
||||
/>
|
||||
<path
|
||||
id="Cube_04"
|
||||
opacity="0.3"
|
||||
d="M27.7534 4.04764L27.5471 4.26983V11.8868L31.9148 7.68376L33.9593 7.60343L38.566 8.37993L38.1279 7.72392L32.6052 1.7395L27.7534 4.04764Z"
|
||||
fill="#D375FF"
|
||||
/>
|
||||
<path
|
||||
id="Cube_05"
|
||||
opacity="0.3"
|
||||
d="M31.8966 0H31.6089L30.4468 1.23821L31.8179 2.02946L31.8966 0Z"
|
||||
fill="black"
|
||||
fillOpacity="0.6"
|
||||
/>
|
||||
<path
|
||||
id="Cube_06"
|
||||
opacity="0.3"
|
||||
d="M37.9861 9.33119L35.2733 11.887H34.7964L35.2224 6.7774L37.3326 6.37842L37.9861 9.33119Z"
|
||||
fill="black"
|
||||
fillOpacity="0.6"
|
||||
/>
|
||||
<path
|
||||
id="Cube"
|
||||
d="M31.6067 7.82793H39.4359V0H31.6067V7.82793Z"
|
||||
fill="url(#paint0_linear_112_1536)"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="paint0_linear_112_1536"
|
||||
x1="36.0617"
|
||||
y1="0"
|
||||
x2="36.0617"
|
||||
y2="7.82793"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#D375FF" />
|
||||
<stop offset="1" stopColor="#798FFF" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default LogoMobileIcon;
|
||||
@@ -0,0 +1,50 @@
|
||||
function MailGradientIcon() {
|
||||
return (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g id="Icon/Mail">
|
||||
<g id="Vector">
|
||||
<path
|
||||
d="M3 8.48345C3 8.09481 3.42397 7.85476 3.75723 8.0547L11.4855 12.6913C11.8022 12.8813 12.1978 12.8813 12.5145 12.6913L20.2428 8.05435C20.576 7.85439 21 8.09445 21 8.4831V17C21 17.5523 20.5523 18 20 18H4C3.44772 18 3 17.5523 3 17V8.48345Z"
|
||||
fill="url(#paint0_linear_56_11108)"
|
||||
/>
|
||||
<path
|
||||
d="M3.54791 5.92875C3.11307 5.66784 3.29805 5 3.80516 5H20.1948C20.702 5 20.8869 5.66784 20.4521 5.92875L12.5145 10.6913C12.1978 10.8813 11.8022 10.8813 11.4855 10.6913L3.54791 5.92875Z"
|
||||
fill="url(#paint1_linear_56_11108)"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="paint0_linear_56_11108"
|
||||
x1="3"
|
||||
y1="27.2857"
|
||||
x2="23.3585"
|
||||
y2="25.6292"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop offset="0.167052" stopColor="#798FFF" />
|
||||
<stop offset="0.963542" stopColor="#D375FF" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint1_linear_56_11108"
|
||||
x1="3"
|
||||
y1="27.2857"
|
||||
x2="23.3585"
|
||||
y2="25.6292"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop offset="0.167052" stopColor="#798FFF" />
|
||||
<stop offset="0.963542" stopColor="#D375FF" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default MailGradientIcon;
|
||||
@@ -0,0 +1,31 @@
|
||||
interface IconProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function MailIcon({ className }: IconProps) {
|
||||
return (
|
||||
<svg
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 32 32"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={className}
|
||||
>
|
||||
<g id="Icon/Mail" opacity="0.8">
|
||||
<g id="Vector">
|
||||
<path
|
||||
d="M4 11.3111C4 10.7929 4.5653 10.4728 5.00965 10.7394L15.314 16.9216C15.7363 17.1749 16.2637 17.1749 16.686 16.9216L26.9903 10.739C27.4347 10.4724 28 10.7924 28 11.3106V22.6665C28 23.4029 27.403 23.9998 26.6667 23.9998H5.33333C4.59695 23.9998 4 23.4029 4 22.6665V11.3111Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M4.73055 7.90483C4.15076 7.55696 4.3974 6.6665 5.07354 6.6665H26.9265C27.6026 6.6665 27.8492 7.55696 27.2695 7.90483L16.686 14.2549C16.2638 14.5083 15.7362 14.5083 15.314 14.2549L4.73055 7.90483Z"
|
||||
fill="white"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default MailIcon;
|
||||
@@ -0,0 +1,34 @@
|
||||
function PhoneGradientIcon() {
|
||||
return (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g id="Icon/Phone">
|
||||
<path
|
||||
id="phone"
|
||||
d="M9.93505 6.37967L7.83082 4.2815C7.45441 3.90617 6.84412 3.90617 6.4677 4.2815L4.72211 6.02206C2.54982 8.18809 5.73849 13.3499 8.21649 15.8207C10.6796 18.2768 15.7998 21.4441 17.9721 19.2781L19.7177 17.5375C20.0941 17.1622 20.0941 16.5537 19.7177 16.1783L17.6134 14.0802C17.237 13.7048 16.6267 13.7048 16.2503 14.0802L14.6522 15.6737C14.555 15.7706 14.4313 15.8284 14.3036 15.7778C13.9576 15.6405 13.0744 15.1026 10.9904 13.0548C8.89823 10.999 8.36142 10.0733 8.22847 9.70017C8.17833 9.55945 8.24399 9.42505 8.34989 9.31945L9.9355 7.73841C10.3119 7.36308 10.3115 6.755 9.93505 6.37967Z"
|
||||
fill="url(#paint0_linear_56_11105)"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="paint0_linear_56_11105"
|
||||
x1="4"
|
||||
y1="31.4286"
|
||||
x2="22.1536"
|
||||
y2="30.3618"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop offset="0.167052" stopColor="#798FFF" />
|
||||
<stop offset="0.963542" stopColor="#D375FF" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default PhoneGradientIcon;
|
||||
@@ -0,0 +1,26 @@
|
||||
interface IconProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function PhoneIcon({ className }: IconProps) {
|
||||
return (
|
||||
<svg
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 32 32"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={className}
|
||||
>
|
||||
<g id="Icon/Phone" opacity="0.8">
|
||||
<path
|
||||
id="phone"
|
||||
d="M13.2467 8.50638L10.4411 5.70883C9.93919 5.20838 9.12547 5.20839 8.62358 5.70883L6.29613 8.02957C3.39974 10.9176 7.6513 17.8 10.9553 21.0945C14.2395 24.3692 21.0664 28.5923 23.9628 25.7043L26.2902 23.3835C26.7921 22.8831 26.7921 22.0717 26.2902 21.5713L23.4846 18.7737C22.9827 18.2733 22.169 18.2733 21.6671 18.7737L19.5362 20.8984C19.4067 21.0276 19.2417 21.1047 19.0714 21.0372C18.6101 20.8542 17.4325 20.137 14.6538 17.4066C11.8643 14.6655 11.1485 13.4312 10.9713 12.9337C10.9044 12.7461 10.992 12.5669 11.1332 12.4261L13.2473 10.318C13.7492 9.8176 13.7486 9.00683 13.2467 8.50638Z"
|
||||
fill="white"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default PhoneIcon;
|
||||
@@ -0,0 +1,28 @@
|
||||
interface IconProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function SendIcon({ className }: IconProps) {
|
||||
return (
|
||||
<svg
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 32 32"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={className}
|
||||
>
|
||||
<g id="Icon/Send">
|
||||
<path
|
||||
id="Vector 164 (Stroke)"
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M25.2649 8.4215C25.432 7.92033 25.2874 7.36779 24.8963 7.01269C24.5051 6.65759 23.9412 6.56689 23.4585 6.78145L6.56115 14.2914C4.82296 15.0639 5.043 17.5979 6.88835 18.0593L10.0482 18.8492C10.6608 19.0024 11.3097 18.8572 11.7987 18.4577L19.8248 11.8996C20.0112 11.7473 20.2583 11.9935 20.1068 12.1805L14.0759 19.62C13.5817 20.2296 13.4898 21.0719 13.8407 21.7738L15.8654 25.8233C16.6623 27.417 18.9882 27.2516 19.5517 25.5613L25.2649 8.4215Z"
|
||||
fill="white"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default SendIcon;
|
||||
@@ -0,0 +1,25 @@
|
||||
function ShareIcon() {
|
||||
return (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g id="Icon/Share">
|
||||
<path
|
||||
id="Vector 1835"
|
||||
d="M16 18L5 12L16 6"
|
||||
stroke="#F2F2F2"
|
||||
stroke-width="2"
|
||||
/>
|
||||
<circle id="Ellipse 226" cx="5" cy="12" r="3" fill="#F2F2F2" />
|
||||
<circle id="Ellipse 227" cx="16" cy="6" r="3" fill="#F2F2F2" />
|
||||
<circle id="Ellipse 228" cx="16" cy="18" r="3" fill="#F2F2F2" />
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default ShareIcon;
|
||||
@@ -0,0 +1,28 @@
|
||||
interface IconProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function TelegramIcon({ className }: IconProps) {
|
||||
return (
|
||||
<svg
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 32 32"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={className}
|
||||
>
|
||||
<g id="Icon/Telegram" opacity="0.8">
|
||||
<path
|
||||
id="Path-3"
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M4.35909 15.8481C10.7925 12.8123 15.0929 10.8501 17.2259 9.88748C23.3497 7.1478 24.6226 6.6665 25.4483 6.6665C25.6203 6.6665 26.0331 6.70353 26.3083 6.92566C26.5148 7.11078 26.5836 7.36994 26.618 7.55505C26.6524 7.74016 26.6868 8.14741 26.6524 8.48062C26.3083 12.2199 24.8978 21.3645 24.1409 25.5481C23.8313 27.3252 23.2121 27.9176 22.6272 27.9916C21.3543 28.1027 20.3566 27.066 19.1181 26.2145C17.1915 24.8447 16.0906 23.9932 14.1984 22.6603C12.031 21.1054 13.4415 20.2539 14.6801 18.884C14.9897 18.5138 20.6662 12.9974 20.7694 12.4791C20.7694 12.405 20.8038 12.1829 20.6662 12.0718C20.5286 11.9608 20.3566 11.9978 20.219 12.0348C20.0126 12.0718 16.9163 14.2932 10.8957 18.6619C10.0012 19.3283 9.20995 19.6245 8.48748 19.6245C7.69621 19.6245 6.18246 19.1432 5.04716 18.7359C3.67103 18.2546 2.57013 17.9955 2.67334 17.181C2.77655 16.7367 3.327 16.2924 4.35909 15.8481Z"
|
||||
fill="white"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default TelegramIcon;
|
||||
@@ -0,0 +1,28 @@
|
||||
interface IconProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function VKIcon({ className }: IconProps) {
|
||||
return (
|
||||
<svg
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 32 32"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={className}
|
||||
>
|
||||
<g id="Icon/VK" opacity="0.8">
|
||||
<path
|
||||
id="Vector"
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M5.44687 8H2.38363C1.50842 8 1.33337 8.39579 1.33337 8.83368C1.33337 9.61432 2.37137 13.4863 6.16892 18.6072C8.70003 22.1044 12.2665 24 15.5118 24C17.4592 24 17.6998 23.5789 17.6998 22.8539V20.2105C17.6998 19.3684 17.8836 19.2 18.5007 19.2C18.9558 19.2 19.7347 19.4189 21.5516 21.1048C23.6285 23.1032 23.9707 24 25.1391 24H28.2024C29.0776 24 29.5152 23.5789 29.2623 22.7478C28.9866 21.92 27.9949 20.7183 26.6786 19.2943C25.9645 18.4825 24.8932 17.6076 24.5694 17.1705C24.1143 16.6088 24.2455 16.3587 24.5694 15.8594C24.5694 15.8594 28.3013 10.8008 28.6916 9.08379C28.8859 8.45895 28.6916 8 27.7639 8H24.7015C23.9226 8 23.5638 8.39579 23.3695 8.83368C23.3695 8.83368 21.8116 12.4867 19.6052 14.8598C18.891 15.5469 18.5663 15.7659 18.1768 15.7659C17.9825 15.7659 17.7007 15.5469 17.7007 14.9229V9.08379C17.7007 8.33432 17.474 8 16.8255 8H12.0118C11.5252 8 11.2329 8.34779 11.2329 8.6779C11.2329 9.38779 12.3357 9.552 12.4494 11.5495V15.8905C12.4494 16.8421 12.2709 17.0147 11.8805 17.0147C10.8425 17.0147 8.31669 13.3448 6.81833 9.14611C6.52425 8.32842 6.22931 8 5.44687 8Z"
|
||||
fill="white"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default VKIcon;
|
||||
@@ -0,0 +1,36 @@
|
||||
function WebGradientIcon() {
|
||||
return (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g id="Icon/Web">
|
||||
<path
|
||||
id="globe"
|
||||
d="M21 12C21 16.9706 16.9706 21 12 21M21 12C21 7.02944 16.9706 3 12 3M21 12H3M12 21C7.02944 21 3 16.9706 3 12M12 21V3M12 21C14.7614 21 17 16.9706 17 12M12 21C9.23858 21 7.00001 16.9706 7.00001 12M3 12C3 7.02944 7.02944 3 12 3M12 3C9.23858 3 7.00001 7.02944 7.00001 12M12 3C14.7614 3 17 7.02944 17 12M17 12H7.00001"
|
||||
stroke="url(#paint0_linear_56_11111)"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="square"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="paint0_linear_56_11111"
|
||||
x1="3"
|
||||
y1="33.8571"
|
||||
x2="23.4228"
|
||||
y2="32.657"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop offset="0.167052" stopColor="#798FFF" />
|
||||
<stop offset="0.963542" stopColor="#D375FF" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default WebGradientIcon;
|
||||
@@ -0,0 +1,28 @@
|
||||
interface IconProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function YouTubeIcon({ className }: IconProps) {
|
||||
return (
|
||||
<svg
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 32 32"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={className}
|
||||
>
|
||||
<g id="Icon/YouTube" opacity="0.8">
|
||||
<path
|
||||
id="Exclude"
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M26.4187 7.2304C27.566 7.54067 28.4695 8.45509 28.7762 9.61601C29.3333 11.7204 29.3333 16.1109 29.3333 16.1109C29.3333 16.1109 29.3333 20.5015 28.7762 22.6058C28.4695 23.7666 27.566 24.6811 26.4187 24.9913C24.339 25.5554 16 25.5554 16 25.5554C16 25.5554 7.66111 25.5554 5.58145 24.9913C4.43418 24.6811 3.53051 23.7666 3.22388 22.6058C2.66663 20.5015 2.66663 16.1109 2.66663 16.1109C2.66663 16.1109 2.66663 11.7204 3.22388 9.61601C3.53051 8.45509 4.43418 7.54067 5.58145 7.2304C7.66111 6.6665 16 6.6665 16 6.6665C16 6.6665 24.339 6.6665 26.4187 7.2304ZM13.7777 12.2219V19.9997L20.4444 16.1109L13.7777 12.2219Z"
|
||||
fill="white"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default YouTubeIcon;
|
||||
@@ -0,0 +1,72 @@
|
||||
import { useClipboard } from "use-clipboard-copy";
|
||||
import ShareIcon from "../icons/ShareIcon";
|
||||
import { ToastContainer, toast } from "react-toastify";
|
||||
import "react-toastify/dist/ReactToastify.css";
|
||||
import useModalStore from "../../stores/useModalStore";
|
||||
import CloseIcon from "../icons/CloseIcon";
|
||||
|
||||
function ShareModal() {
|
||||
const [setModal] = useModalStore((state) => [state.setModal]);
|
||||
const clipboard = useClipboard();
|
||||
|
||||
function toastInfo(text: string) {
|
||||
toast.info(text, {
|
||||
position: "bottom-right",
|
||||
autoClose: 5000,
|
||||
hideProgressBar: false,
|
||||
closeOnClick: true,
|
||||
pauseOnHover: true,
|
||||
draggable: true,
|
||||
progress: undefined,
|
||||
theme: "dark",
|
||||
});
|
||||
}
|
||||
|
||||
function handleClickCopy() {
|
||||
clipboard.copy();
|
||||
toastInfo("Ссылка скопирована в буфер обмена");
|
||||
setModal(null);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="relative p-10 bg-[#131317] rounded shadow-lg w-[320px]">
|
||||
<div className="flex flex-col gap-8">
|
||||
<p className="font-gilroy text-2xl">
|
||||
Пригласить
|
||||
<br />
|
||||
на демонстрацию
|
||||
</p>
|
||||
<div className="flex flex-col gap-4">
|
||||
<p className="font-gilroy text-xl">Ссылка для подключения</p>
|
||||
<input
|
||||
ref={clipboard.target}
|
||||
readOnly
|
||||
type="text"
|
||||
className="bg-[#23242A] rounded px-4 py-3 outline-none"
|
||||
value={window.location.href}
|
||||
/>
|
||||
<button
|
||||
onClick={handleClickCopy}
|
||||
className="pl-3 pr-4 py-2 bg-gradient rounded flex items-center gap-1 w-fit"
|
||||
>
|
||||
<ShareIcon />
|
||||
<span className="text-sm">Скопировать</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={() => setModal(null)}
|
||||
className="absolute top-3 right-3 p-2 rounded-full transition-colors hover:bg-white hover:bg-opacity-5"
|
||||
>
|
||||
<CloseIcon />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ToastContainer />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default ShareModal;
|
||||
@@ -0,0 +1,210 @@
|
||||
import i18n from "i18next";
|
||||
import { initReactI18next } from "react-i18next";
|
||||
import detector from "i18next-browser-languagedetector";
|
||||
|
||||
// the translations
|
||||
// (tip move them in a JSON file and import them,
|
||||
// or even better, manage them separated from your code: https://react.i18next.com/guides/multiple-translation-files)
|
||||
const resources = {
|
||||
ru: {
|
||||
translation: {
|
||||
loading: "Загрузка",
|
||||
loadingSub: "сек",
|
||||
streamWaiting: "Ожидание потока",
|
||||
streamBuffering: "Буферизация потока",
|
||||
streamEnded: "Трансляция была завершена",
|
||||
demoStarted: "Демонстрация начата",
|
||||
clickToContinue: "Нажмите, чтобы продолжить",
|
||||
fullscreenMode: "Полноэкранный режим",
|
||||
windowedMode: "Оконный режим",
|
||||
inviteByQR: "Пригласить по QR",
|
||||
scanQRCode:
|
||||
"Отсканируйте QR-код<br />чтобы подключиться<br />к текущей демонстрации",
|
||||
|
||||
title: "Удаленная демонстрация",
|
||||
header: {
|
||||
buttonFirst: "Записаться",
|
||||
buttonSecond: "на демонстрацию",
|
||||
},
|
||||
main: {
|
||||
title: "Доступные<br />демонстрации",
|
||||
desc: "Клиент из любой точки мира может посмотреть жилой комплекс, даже на нулевом этапе строительства. Он выберет лучшую планировку и оценит вид из окон своей будущей квартиры.",
|
||||
cards: {
|
||||
title1: "МФК «Revolution towers»",
|
||||
title2: "ЖК «Life Резиденция»",
|
||||
title3: "ЖК «Айвазовский City»",
|
||||
city1: "Россия, Екатеринбург",
|
||||
city2: "Россия, Тюмень",
|
||||
button: "Запустить",
|
||||
},
|
||||
},
|
||||
feedback: {
|
||||
title: "Свяжитесь<br />с нами",
|
||||
desc: "Хотите увеличить конверсию?<br />Давайте обсудим детали!",
|
||||
form: {
|
||||
field1: "Имя",
|
||||
field2: "Телефон",
|
||||
field3: "Опишите вашу задачу",
|
||||
button: "Отправить",
|
||||
desc1: {
|
||||
text1: "Нажимая кнопку «Отправить», вы принимаете",
|
||||
text1_1: "Нажимая кнопку «Записаться», вы принимаете",
|
||||
link1: "условия использования",
|
||||
text2: "и",
|
||||
link2: "политику конфиденциальности",
|
||||
},
|
||||
desc2: "Звездочкой отмечены обязательные<br />для заполнения поля",
|
||||
},
|
||||
},
|
||||
contacts: {
|
||||
title: "Горячая линия",
|
||||
button1: "Написать",
|
||||
button2: "Позвонить",
|
||||
social: {
|
||||
title: "Социальные<br />сети",
|
||||
},
|
||||
},
|
||||
footer: {
|
||||
link: "Политика конфиденциальности",
|
||||
text: "Все права защищены.",
|
||||
},
|
||||
sidebar: {
|
||||
title1: "Дата и время",
|
||||
title2: "Контакты",
|
||||
title3: "Проверка данных",
|
||||
title4_1: "спасибо за запись",
|
||||
title4_2: "на удаленную демонстрацию",
|
||||
date: "Дата",
|
||||
time: "Время",
|
||||
contacts: "Контакты",
|
||||
submitNotice1: "Нажимая кнопку «Записаться», вы принимаете",
|
||||
submitNotice2: "условия использования",
|
||||
submitNotice3: "и",
|
||||
submitNotice4: "политику конфиденциальности",
|
||||
available: "запись доступна",
|
||||
buttonBack: "Назад",
|
||||
buttonNext: "Далее",
|
||||
buttonSignUp: "Записаться",
|
||||
buttonHome: "На главную",
|
||||
checkData: "Проверка заявки",
|
||||
sessionDetails: "Детали сеанса",
|
||||
contactDetails: "Контактные данные",
|
||||
name: "Имя",
|
||||
phone: "Телефон",
|
||||
tab5text1:
|
||||
"В ближайшее время мы отправим на ваш почтовый адрес всю дополнительную информацию о сеансе и ссылку для подключения.",
|
||||
tab5text2: "Возникли вопросы?",
|
||||
tab5text3: "Свяжитесь с нами",
|
||||
notice:
|
||||
"Запись на демонстрацию работает в ознакомительном режиме и не сохраняет введенные данные",
|
||||
},
|
||||
},
|
||||
},
|
||||
en: {
|
||||
translation: {
|
||||
loading: "Loading",
|
||||
loadingSub: "sec",
|
||||
streamWaiting: "Stream waiting",
|
||||
streamBuffering: "Stream buffering",
|
||||
streamEnded: "Stream has been ended",
|
||||
demoStarted: "Demo started",
|
||||
clickToContinue: "Click to continue",
|
||||
fullscreenMode: "Fullscreen mode",
|
||||
windowedMode: "Windowed mode",
|
||||
inviteByQR: "Invite by QR code",
|
||||
scanQRCode: "Scan the QR code to connect<br /> to the current demo",
|
||||
|
||||
title: "Remote demonstration",
|
||||
header: {
|
||||
buttonFirst: "Sign up",
|
||||
buttonSecond: "for a demo",
|
||||
},
|
||||
main: {
|
||||
title: "Available<br />demos",
|
||||
desc: "A client from anywhere in the world can view the residential complex, even at the zero construction stage. He will choose the best layout and evaluate the view from the windows of his future apartment.",
|
||||
cards: {
|
||||
title1: "Revolution towers",
|
||||
title2: "Life Residence",
|
||||
title3: "Aivazovsky City",
|
||||
city1: "Russia, Yekaterinburg",
|
||||
city2: "Russia, Tyumen",
|
||||
button: "Run demo",
|
||||
},
|
||||
},
|
||||
feedback: {
|
||||
title: "Contact us",
|
||||
desc: "Want to increase conversion?<br />Let's discuss the details!",
|
||||
form: {
|
||||
field1: "Name",
|
||||
field2: "Phone",
|
||||
field3: "Describe your task",
|
||||
button: "Send",
|
||||
desc1: {
|
||||
text1: 'By clicking the "Submit" button, you accept the',
|
||||
text1_1: 'By clicking the "Sign up" button, you accept the',
|
||||
link1: "terms of use",
|
||||
text2: "and",
|
||||
link2: "privacy policy",
|
||||
},
|
||||
desc2: "Required fields are marked<br />with an asterisk",
|
||||
},
|
||||
},
|
||||
contacts: {
|
||||
title: "Hot line",
|
||||
button1: "Write to us",
|
||||
button2: "Call us",
|
||||
social: {
|
||||
title: "Social<br />media",
|
||||
},
|
||||
},
|
||||
footer: {
|
||||
link: "Privacy policy",
|
||||
text: "All rights reserved.",
|
||||
},
|
||||
sidebar: {
|
||||
title1: "Date and time",
|
||||
title2: "Contacts",
|
||||
title3: "Data checking",
|
||||
title4_1: "thank you for signing up",
|
||||
title4_2: "for a remote demonstration",
|
||||
date: "Date",
|
||||
time: "Time",
|
||||
contacts: "Contacts",
|
||||
submitNotice1: 'By clicking the "sign up" button, you accept the',
|
||||
submitNotice2: "terms of use",
|
||||
submitNotice3: "and",
|
||||
submitNotice4: "privacy policy",
|
||||
available: "available",
|
||||
buttonBack: "Back",
|
||||
buttonNext: "Next",
|
||||
buttonSignUp: "Sign up",
|
||||
buttonHome: "Home",
|
||||
checkData: "Check the data",
|
||||
sessionDetails: "Session details",
|
||||
contactDetails: "Contact details",
|
||||
name: "Name",
|
||||
phone: "Phone",
|
||||
tab5text1:
|
||||
"In the near future we will send all additional information about the session and a link to connect to your email address.",
|
||||
tab5text2: "Any questions?",
|
||||
tab5text3: "Contact us",
|
||||
notice:
|
||||
"Registration for the demonstration<br />is carried out in a trial mode",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
void i18n
|
||||
.use(detector)
|
||||
.use(initReactI18next) // passes i18n down to react-i18next
|
||||
.init({
|
||||
resources,
|
||||
fallbackLng: ["ru", "en"],
|
||||
interpolation: {
|
||||
escapeValue: false, // react already safes from xss
|
||||
},
|
||||
debug: true,
|
||||
});
|
||||
|
||||
export default i18n;
|
||||
@@ -0,0 +1,28 @@
|
||||
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap");
|
||||
@import url("https://gistcdn.githack.com/mfd/09b70eb47474836f25a21660282ce0fd/raw/e06a670afcb2b861ed2ac4a1ef752d062ef6b46b/Gilroy.css");
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
body {
|
||||
font-family: "Inter", sans-serif;
|
||||
}
|
||||
|
||||
.font-gilroy {
|
||||
font-family: "Gilroy", sans-serif;
|
||||
}
|
||||
|
||||
input {
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
.bg-gradient {
|
||||
background: linear-gradient(23deg, #798fff 16.71%, #d375ff 96.35%) !important;
|
||||
}
|
||||
|
||||
.text-gradient {
|
||||
background: linear-gradient(23deg, #798fff 16.71%, #d375ff 96.35%);
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
// import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import {
|
||||
createBrowserRouter,
|
||||
Navigate,
|
||||
RouterProvider,
|
||||
} from "react-router-dom";
|
||||
import "./index.css";
|
||||
import App from "./App";
|
||||
import "./i18n";
|
||||
import StreamPage from "./StreamPage";
|
||||
import MonitoringPage from "./MonitoringPage";
|
||||
import HistoryPage from "./HistoryPage";
|
||||
import ScheduledPage from "./ScheduledPage";
|
||||
import CalendarPage from "./CalendarPage";
|
||||
import useAuthStore from "./stores/useAuthStore";
|
||||
import PersonalAreaLoginPage from "./PersonalAreaLoginPage";
|
||||
import PersonalAreaDashboardPage from "./PersonalAreaDashboardPage";
|
||||
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
path: "/",
|
||||
element: <App />,
|
||||
},
|
||||
{
|
||||
path: "/stream/:id",
|
||||
element: <StreamPage />,
|
||||
},
|
||||
{
|
||||
path: "/monitoring",
|
||||
element: <MonitoringPage />,
|
||||
},
|
||||
{
|
||||
path: "/history",
|
||||
element: <HistoryPage />,
|
||||
},
|
||||
{
|
||||
path: "/scheduled",
|
||||
element: <ScheduledPage />,
|
||||
},
|
||||
{
|
||||
path: "/calendar/:username",
|
||||
element: <CalendarPage />,
|
||||
},
|
||||
{
|
||||
path: "/personal-area",
|
||||
element: <Navigate to="/personal-area/login" replace />,
|
||||
},
|
||||
{
|
||||
path: "/personal-area/login",
|
||||
element: (
|
||||
<ProtectedRoute>
|
||||
<PersonalAreaLoginPage />
|
||||
</ProtectedRoute>
|
||||
),
|
||||
},
|
||||
{
|
||||
path: "/personal-area/dashboard",
|
||||
element: (
|
||||
<ProtectedRoute>
|
||||
<PersonalAreaDashboardPage />
|
||||
</ProtectedRoute>
|
||||
),
|
||||
},
|
||||
]);
|
||||
|
||||
export function ProtectedRoute({ children }: { children: JSX.Element }) {
|
||||
const accessToken = useAuthStore((state) => state.accessToken);
|
||||
|
||||
if (accessToken) {
|
||||
// console.log("location.pathname", location.pathname);
|
||||
|
||||
if (location.pathname === "/personal-area/login") {
|
||||
return <Navigate to="/personal-area/dashboard" replace />;
|
||||
} else {
|
||||
return <>{children}</>;
|
||||
}
|
||||
} else {
|
||||
if (location.pathname !== "/personal-area/login") {
|
||||
return <Navigate to="/personal-area/login" replace />;
|
||||
} else {
|
||||
return <>{children}</>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
||||
// <React.StrictMode>
|
||||
<RouterProvider router={router} />
|
||||
// </React.StrictMode>,
|
||||
);
|
||||
@@ -0,0 +1,37 @@
|
||||
import { create } from "zustand";
|
||||
import { devtools, persist } from "zustand/middleware";
|
||||
|
||||
type User = {
|
||||
id: string;
|
||||
username: string;
|
||||
};
|
||||
|
||||
interface State {
|
||||
accessToken: string | null;
|
||||
user: User | null;
|
||||
}
|
||||
|
||||
interface Actions {
|
||||
setAccessToken: (accessToken: string) => void;
|
||||
setUser: (user: User) => void;
|
||||
removeAuthStore: () => void;
|
||||
}
|
||||
|
||||
const useAuthStore = create<State & Actions>()(
|
||||
devtools(
|
||||
persist(
|
||||
(set) => ({
|
||||
accessToken: null,
|
||||
user: null,
|
||||
setAccessToken: (accessToken) => set({ accessToken }),
|
||||
setUser: (user) => set({ user }),
|
||||
removeAuthStore: () => set({ accessToken: null, user: null }),
|
||||
}),
|
||||
{
|
||||
name: "auth",
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
export default useAuthStore;
|
||||
@@ -0,0 +1,16 @@
|
||||
import { create } from "zustand";
|
||||
import { devtools } from "zustand/middleware";
|
||||
|
||||
interface ModalState {
|
||||
modal: JSX.Element | null;
|
||||
setModal: (modal: JSX.Element | null) => void;
|
||||
}
|
||||
|
||||
const useModalStore = create<ModalState>()(
|
||||
devtools((set) => ({
|
||||
modal: null,
|
||||
setModal: (modal) => set({ modal }),
|
||||
}))
|
||||
);
|
||||
|
||||
export default useModalStore;
|
||||
@@ -0,0 +1,47 @@
|
||||
import { create } from "zustand";
|
||||
import { devtools } from "zustand/middleware";
|
||||
|
||||
interface SidebarState {
|
||||
isOpen: boolean;
|
||||
setIsOpen: (value: boolean) => void;
|
||||
currentTab: number;
|
||||
setCurrentTab: (tab: number) => void;
|
||||
selectedDay: Date | null;
|
||||
setSelectedDay: (day: Date) => void;
|
||||
selectedTime: string | null;
|
||||
setSelectedTime: (time: string) => void;
|
||||
name: string;
|
||||
setName: (name: string) => void;
|
||||
phone: string;
|
||||
setPhone: (phone: string) => void;
|
||||
email: string;
|
||||
setEmail: (email: string) => void;
|
||||
}
|
||||
|
||||
const useSidebarStore = create<SidebarState>()(
|
||||
devtools(
|
||||
// persist(
|
||||
(set) => ({
|
||||
isOpen: false,
|
||||
setIsOpen: (value) => set(() => ({ isOpen: value })),
|
||||
currentTab: 1,
|
||||
setCurrentTab: (tab) => set(() => ({ currentTab: tab })),
|
||||
selectedDay: null,
|
||||
setSelectedDay: (day) => set(() => ({ selectedDay: day })),
|
||||
selectedTime: null,
|
||||
setSelectedTime: (time) => set(() => ({ selectedTime: time })),
|
||||
name: "",
|
||||
setName: (name) => set(() => ({ name })),
|
||||
phone: "",
|
||||
setPhone: (phone) => set(() => ({ phone })),
|
||||
email: "",
|
||||
setEmail: (email) => set(() => ({ email })),
|
||||
})
|
||||
// {
|
||||
// name: "tab-storage",
|
||||
// }
|
||||
// )
|
||||
)
|
||||
);
|
||||
|
||||
export default useSidebarStore;
|
||||
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
@@ -0,0 +1,12 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: [
|
||||
"./index.html",
|
||||
"./src/**/*.{js,ts,jsx,tsx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["src"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"skipLibCheck": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react-swc";
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
server: {
|
||||
port: 5000,
|
||||
},
|
||||
preview: {
|
||||
port: 5000,
|
||||
},
|
||||
});
|
||||