diff --git a/build.zip b/build.zip deleted file mode 100644 index 681c4b4..0000000 Binary files a/build.zip and /dev/null differ diff --git a/package-lock.json b/package-lock.json index 7b41686..ac1b74d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "streaming-landing", "version": "0.1.0", "dependencies": { + "@reduxjs/toolkit": "^1.9.3", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.3.0", "@testing-library/user-event": "^13.5.0", @@ -18,14 +19,21 @@ "@types/swiper": "^6.0.0", "animejs": "^3.2.1", "aos": "^2.3.4", + "axios": "^1.3.4", "framer-motion": "^7.2.0", "i": "^0.3.7", + "i18next": "^22.4.12", + "i18next-browser-languagedetector": "^7.0.1", + "i18next-http-backend": "^2.2.0", + "js-cookie": "^3.0.1", "moment": "^2.29.4", "npm": "^8.18.0", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-i18next": "^12.2.0", "react-minimal-pie-chart": "^8.3.0", "react-player": "^2.11.0", + "react-redux": "^8.0.5", "react-router-dom": "^5.2.0", "react-scripts": "5.0.1", "swiper": "^8.3.2", @@ -38,6 +46,7 @@ "@types/animejs": "^3.1.5", "@types/aos": "^3.0.4", "@types/body-scroll-lock": "^3.1.0", + "@types/js-cookie": "^3.0.3", "@types/react-router-dom": "^5.3.3", "babel-plugin-named-exports-order": "^0.0.2", "prop-types": "^15.8.1", @@ -1837,11 +1846,11 @@ } }, "node_modules/@babel/runtime": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz", - "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz", + "integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==", "dependencies": { - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.13.11" }, "engines": { "node": ">=6.9.0" @@ -3154,6 +3163,29 @@ } } }, + "node_modules/@reduxjs/toolkit": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.3.tgz", + "integrity": "sha512-GU2TNBQVofL09VGmuSioNPQIu6Ml0YLf4EJhgj0AvBadRlCGzUWet8372LjvO4fqKZF2vH1xU0htAa7BrK9pZg==", + "dependencies": { + "immer": "^9.0.16", + "redux": "^4.2.0", + "redux-thunk": "^2.4.2", + "reselect": "^4.1.7" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18", + "react-redux": "^7.2.1 || ^8.0.2" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } + } + }, "node_modules/@rollup/plugin-babel": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", @@ -3852,6 +3884,15 @@ "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==", "dev": true }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "node_modules/@types/html-minifier-terser": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", @@ -3895,6 +3936,12 @@ "pretty-format": "^27.0.0" } }, + "node_modules/@types/js-cookie": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-3.0.3.tgz", + "integrity": "sha512-Xe7IImK09HP1sv2M/aI+48a20VX+TdRJucfq4vfRVy6nWN8PYPOEnlMRSgxJAgYQIXJVL8dZ4/ilAM7dWNaOww==", + "dev": true + }, "node_modules/@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -4054,6 +4101,11 @@ "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz", "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==" }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", + "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" + }, "node_modules/@types/ws": { "version": "8.5.3", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", @@ -4860,6 +4912,29 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz", + "integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/axobject-query": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", @@ -5822,6 +5897,14 @@ "node": ">=10" } }, + "node_modules/cross-fetch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", + "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", + "dependencies": { + "node-fetch": "2.6.7" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -8582,6 +8665,14 @@ "node": ">=12" } }, + "node_modules/html-parse-stringify": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", + "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", + "dependencies": { + "void-elements": "3.1.0" + } + }, "node_modules/html-webpack-plugin": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", @@ -8724,6 +8815,44 @@ "node": ">=0.4" } }, + "node_modules/i18next": { + "version": "22.4.12", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-22.4.12.tgz", + "integrity": "sha512-2lE+vRXxQ3lGLub1CVbwgO0IfkLHmUSDVOAVdPh22CsxttMXi+35n2qgxh2wZIkKl6t/NMzPfgFPRDiFQOmiCg==", + "funding": [ + { + "type": "individual", + "url": "https://locize.com" + }, + { + "type": "individual", + "url": "https://locize.com/i18next.html" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + } + ], + "dependencies": { + "@babel/runtime": "^7.20.6" + } + }, + "node_modules/i18next-browser-languagedetector": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-7.0.1.tgz", + "integrity": "sha512-Pa5kFwaczXJAeHE56CHG2aWzFBMJNUNghf0Pm4SwSrEMps/PTKqW90EYWlIvhuYStf3Sn1K0vw+gH3+TLdkH1g==", + "dependencies": { + "@babel/runtime": "^7.19.4" + } + }, + "node_modules/i18next-http-backend": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-2.2.0.tgz", + "integrity": "sha512-Z4sM7R6tzdLknSPER9GisEBxKPg5FkI07UrQniuroZmS15PHQrcCPLyuGKj8SS68tf+O2aEDYSUnmy1TZqZSbw==", + "dependencies": { + "cross-fetch": "3.1.5" + } + }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -8771,9 +8900,9 @@ } }, "node_modules/immer": { - "version": "9.0.15", - "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.15.tgz", - "integrity": "sha512-2eB/sswms9AEUSkOm4SbV5Y7Vmt/bKRwByd52jfLkW4OLYeaTP3EEiJ9agqU0O/tq6Dk62Zfj+TJSqfm1rLVGQ==", + "version": "9.0.19", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.19.tgz", + "integrity": "sha512-eY+Y0qcsB4TZKwgQzLaE/lqYMlKhv5J9dyd2RhhtGhNo2njPXDqU9XPfcNfa3MIDsdtZt5KlkIsirlo4dHsWdQ==", "funding": { "type": "opencollective", "url": "https://opencollective.com/immer" @@ -11237,6 +11366,14 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/js-cookie": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.1.tgz", + "integrity": "sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw==", + "engines": { + "node": ">=12" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -11868,6 +12005,44 @@ "tslib": "^2.0.3" } }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/node-forge": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", @@ -16041,6 +16216,11 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", @@ -16330,6 +16510,27 @@ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==" }, + "node_modules/react-i18next": { + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-12.2.0.tgz", + "integrity": "sha512-5XeVgSygaGfyFmDd2WcXvINRw2WEC1XviW1LXY/xLOEMzsCFRwKqfnHN+hUjla8ZipbVJR27GCMSuTr0BhBBBQ==", + "dependencies": { + "@babel/runtime": "^7.20.6", + "html-parse-stringify": "^3.0.1" + }, + "peerDependencies": { + "i18next": ">= 19.0.0", + "react": ">= 16.8.0" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -16359,6 +16560,49 @@ "react": ">=16.6.0" } }, + "node_modules/react-redux": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.5.tgz", + "integrity": "sha512-Q2f6fCKxPFpkXt1qNRZdEDLlScsDWyrgSj0mliK59qU6W5gvBiKkdMEG2lJzhd1rCctf0hb6EtePPLZ2e0m1uw==", + "dependencies": { + "@babel/runtime": "^7.12.1", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/use-sync-external-store": "^0.0.3", + "hoist-non-react-statics": "^3.3.2", + "react-is": "^18.0.0", + "use-sync-external-store": "^1.0.0" + }, + "peerDependencies": { + "@types/react": "^16.8 || ^17.0 || ^18.0", + "@types/react-dom": "^16.8 || ^17.0 || ^18.0", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0", + "react-native": ">=0.59", + "redux": "^4" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, + "node_modules/react-redux/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, "node_modules/react-refresh": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", @@ -16573,6 +16817,22 @@ "node": ">=8" } }, + "node_modules/redux": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "dependencies": { + "@babel/runtime": "^7.9.2" + } + }, + "node_modules/redux-thunk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", + "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==", + "peerDependencies": { + "redux": "^4" + } + }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -16590,9 +16850,9 @@ } }, "node_modules/regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" }, "node_modules/regenerator-transform": { "version": "0.15.0", @@ -16715,6 +16975,11 @@ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, + "node_modules/reselect": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.7.tgz", + "integrity": "sha512-Zu1xbUt3/OPwsXL46hvOOoQrap2azE7ZQbokq61BQfiXvhewsKDwhMeZjTX9sX0nvw1t/U5Audyn1I9P/m9z0A==" + }, "node_modules/resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -18302,6 +18567,14 @@ "react": "^16.0.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -18386,6 +18659,14 @@ "node": ">= 0.8" } }, + "node_modules/void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/w3c-hr-time": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", @@ -20514,11 +20795,11 @@ } }, "@babel/runtime": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz", - "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz", + "integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==", "requires": { - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.13.11" } }, "@babel/runtime-corejs3": { @@ -21418,6 +21699,17 @@ "source-map": "^0.7.3" } }, + "@reduxjs/toolkit": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.3.tgz", + "integrity": "sha512-GU2TNBQVofL09VGmuSioNPQIu6Ml0YLf4EJhgj0AvBadRlCGzUWet8372LjvO4fqKZF2vH1xU0htAa7BrK9pZg==", + "requires": { + "immer": "^9.0.16", + "redux": "^4.2.0", + "redux-thunk": "^2.4.2", + "reselect": "^4.1.7" + } + }, "@rollup/plugin-babel": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", @@ -21923,6 +22215,15 @@ "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==", "dev": true }, + "@types/hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", + "requires": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "@types/html-minifier-terser": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", @@ -21966,6 +22267,12 @@ "pretty-format": "^27.0.0" } }, + "@types/js-cookie": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-3.0.3.tgz", + "integrity": "sha512-Xe7IImK09HP1sv2M/aI+48a20VX+TdRJucfq4vfRVy6nWN8PYPOEnlMRSgxJAgYQIXJVL8dZ4/ilAM7dWNaOww==", + "dev": true + }, "@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -22124,6 +22431,11 @@ "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz", "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==" }, + "@types/use-sync-external-store": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", + "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" + }, "@types/ws": { "version": "8.5.3", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", @@ -22699,6 +23011,28 @@ "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.4.3.tgz", "integrity": "sha512-32+ub6kkdhhWick/UjvEwRchgoetXqTK14INLqbGm5U2TzBkBNF3nQtLYm8ovxSkQWArjEQvftCKryjZaATu3w==" }, + "axios": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz", + "integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==", + "requires": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + }, + "dependencies": { + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } + } + }, "axobject-query": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", @@ -23432,6 +23766,14 @@ "yaml": "^1.10.0" } }, + "cross-fetch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", + "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", + "requires": { + "node-fetch": "2.6.7" + } + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -25425,6 +25767,14 @@ "terser": "^5.10.0" } }, + "html-parse-stringify": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", + "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", + "requires": { + "void-elements": "3.1.0" + } + }, "html-webpack-plugin": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", @@ -25521,6 +25871,30 @@ "resolved": "https://registry.npmjs.org/i/-/i-0.3.7.tgz", "integrity": "sha512-FYz4wlXgkQwIPqhzC5TdNMLSE5+GS1IIDJZY/1ZiEPCT2S3COUVZeT5OW4BmW4r5LHLQuOosSwsvnroG9GR59Q==" }, + "i18next": { + "version": "22.4.12", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-22.4.12.tgz", + "integrity": "sha512-2lE+vRXxQ3lGLub1CVbwgO0IfkLHmUSDVOAVdPh22CsxttMXi+35n2qgxh2wZIkKl6t/NMzPfgFPRDiFQOmiCg==", + "requires": { + "@babel/runtime": "^7.20.6" + } + }, + "i18next-browser-languagedetector": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-7.0.1.tgz", + "integrity": "sha512-Pa5kFwaczXJAeHE56CHG2aWzFBMJNUNghf0Pm4SwSrEMps/PTKqW90EYWlIvhuYStf3Sn1K0vw+gH3+TLdkH1g==", + "requires": { + "@babel/runtime": "^7.19.4" + } + }, + "i18next-http-backend": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-2.2.0.tgz", + "integrity": "sha512-Z4sM7R6tzdLknSPER9GisEBxKPg5FkI07UrQniuroZmS15PHQrcCPLyuGKj8SS68tf+O2aEDYSUnmy1TZqZSbw==", + "requires": { + "cross-fetch": "3.1.5" + } + }, "iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -25553,9 +25927,9 @@ "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==" }, "immer": { - "version": "9.0.15", - "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.15.tgz", - "integrity": "sha512-2eB/sswms9AEUSkOm4SbV5Y7Vmt/bKRwByd52jfLkW4OLYeaTP3EEiJ9agqU0O/tq6Dk62Zfj+TJSqfm1rLVGQ==" + "version": "9.0.19", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.19.tgz", + "integrity": "sha512-eY+Y0qcsB4TZKwgQzLaE/lqYMlKhv5J9dyd2RhhtGhNo2njPXDqU9XPfcNfa3MIDsdtZt5KlkIsirlo4dHsWdQ==" }, "import-fresh": { "version": "3.3.0", @@ -27323,6 +27697,11 @@ } } }, + "js-cookie": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.1.tgz", + "integrity": "sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw==" + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -27798,6 +28177,35 @@ "tslib": "^2.0.3" } }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + }, + "dependencies": { + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } + } + }, "node-forge": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", @@ -30494,6 +30902,11 @@ } } }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", @@ -30700,6 +31113,15 @@ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==" }, + "react-i18next": { + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-12.2.0.tgz", + "integrity": "sha512-5XeVgSygaGfyFmDd2WcXvINRw2WEC1XviW1LXY/xLOEMzsCFRwKqfnHN+hUjla8ZipbVJR27GCMSuTr0BhBBBQ==", + "requires": { + "@babel/runtime": "^7.20.6", + "html-parse-stringify": "^3.0.1" + } + }, "react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -30722,6 +31144,26 @@ "react-fast-compare": "^3.0.1" } }, + "react-redux": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.5.tgz", + "integrity": "sha512-Q2f6fCKxPFpkXt1qNRZdEDLlScsDWyrgSj0mliK59qU6W5gvBiKkdMEG2lJzhd1rCctf0hb6EtePPLZ2e0m1uw==", + "requires": { + "@babel/runtime": "^7.12.1", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/use-sync-external-store": "^0.0.3", + "hoist-non-react-statics": "^3.3.2", + "react-is": "^18.0.0", + "use-sync-external-store": "^1.0.0" + }, + "dependencies": { + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + } + } + }, "react-refresh": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", @@ -30895,6 +31337,19 @@ "strip-indent": "^3.0.0" } }, + "redux": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "requires": { + "@babel/runtime": "^7.9.2" + } + }, + "redux-thunk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", + "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==" + }, "regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -30909,9 +31364,9 @@ } }, "regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" }, "regenerator-transform": { "version": "0.15.0", @@ -31006,6 +31461,11 @@ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, + "reselect": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.7.tgz", + "integrity": "sha512-Zu1xbUt3/OPwsXL46hvOOoQrap2azE7ZQbokq61BQfiXvhewsKDwhMeZjTX9sX0nvw1t/U5Audyn1I9P/m9z0A==" + }, "resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -32155,6 +32615,11 @@ "resolved": "https://registry.npmjs.org/use-screen-size/-/use-screen-size-1.1.0.tgz", "integrity": "sha512-GmHD1guO2+t5XZ1M8w/H7hlC1CO6IsD3lz1l9o+mKpmZfcm27cHKDw0J5kkj5sy3WT3hZAtno6pVaI3lRzDszA==" }, + "use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==" + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -32221,6 +32686,11 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" }, + "void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==" + }, "w3c-hr-time": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", diff --git a/package.json b/package.json index d6d9ed7..276fcfc 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { + "@reduxjs/toolkit": "^1.9.3", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.3.0", "@testing-library/user-event": "^13.5.0", @@ -13,14 +14,21 @@ "@types/swiper": "^6.0.0", "animejs": "^3.2.1", "aos": "^2.3.4", + "axios": "^1.3.4", "framer-motion": "^7.2.0", "i": "^0.3.7", + "i18next": "^22.4.12", + "i18next-browser-languagedetector": "^7.0.1", + "i18next-http-backend": "^2.2.0", + "js-cookie": "^3.0.1", "moment": "^2.29.4", "npm": "^8.18.0", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-i18next": "^12.2.0", "react-minimal-pie-chart": "^8.3.0", "react-player": "^2.11.0", + "react-redux": "^8.0.5", "react-router-dom": "^5.2.0", "react-scripts": "5.0.1", "swiper": "^8.3.2", @@ -67,6 +75,7 @@ "@types/animejs": "^3.1.5", "@types/aos": "^3.0.4", "@types/body-scroll-lock": "^3.1.0", + "@types/js-cookie": "^3.0.3", "@types/react-router-dom": "^5.3.3", "babel-plugin-named-exports-order": "^0.0.2", "prop-types": "^15.8.1", diff --git a/public/assets/locales/en/translation.json b/public/assets/locales/en/translation.json new file mode 100644 index 0000000..a4a2eab --- /dev/null +++ b/public/assets/locales/en/translation.json @@ -0,0 +1,80 @@ +{ + "main-titleCaption": "Technology for remote demonstration of a residential complex.", + "main-button": "Demoversion", + "slider-title": "", + "slider-mainBlockCaptionColor": "an uncompromising level of graphics and a complete immersion of the customer in the process of choosing an apartment.", + "slider-mainBlockCaption": "We can show your residential complex to any client from any part of the world. Location and device don't matter. All you need is internet connection.", + "slider-subtitle": "Floor plan", + "slider-subtitle1": "Architecture", + "slider-subtitle2": "Infrastructure", + "slider-subtitle3": "Landscaping", + "calendar-title": "easily embeds into an existing sales chain.", + "calendar-mainBlockCaptionColor": "developer's sales team can conduct virtual tours with potential customers", + "calendar-mainBlockCaption": "Customer able to see all the advantages of the residential complex, choose a layout and book an apartment, and the sales manager will be able to remotely help them do it.", + "calendar-mainBlockSubblock": "Our solution can be integrated to the site of the residential complex where the developer has set up advertising.", + "calendarCaption": "Pick a date", + "timepickerCaption": "Pick a time", + "formCaption": "Submit", + "appointmentTrue": "Available", + "appointmentFalse": "Not Available", + "confirm-title": "View planned", + "confirm-caption": "Additional information will be send to the email and phone number", + "buttons-plan": "Сonfirm", + "buttons-next": "Continue", + "placeholders-name": "Name", + "placeholders-phoneNumber": "Phone number", + "placeholders-email": "E-mail", + "multiplayer-title": "for teamwork.", + "multiplayer-mainBlockCaption": "The customer can walk around the residential complex together with their loved ones who connect from different devices. This is a completely new experience in the process of choosing an apartment", + "multiplayer-mainBlockSubblock": "The application easily adapts to any screen size. Our solution looks good on both mobile or large touchscreen in a developer's sales department.", + "multiplayer-button": "Demoversion", + "devices-title": "is available on any device.", + "devices-mainBlockCaption": "The application easily adapts to any screen size. Our solution looks good on both mobile or large touchscreen in a developer's sales department.", + "devices-button": "Demoversion", + "photoComponent-title": "already works in the sales departments of the developer", + "photoComponent-mainBlockCaption": "To organize an interactive presentation of a residential complex, the developer does not need to buy expensive equipment and maintain its performance.", + "photoComponent-mainBlockSubblock": "In the sales office, It is enough to place a touch table with Internet access.", + "player-title": "allows you to transfer an impressive level of graphics to the user's device", + "player-mainBlockCaption": "To work with an interactive presentation, the customer only needs a smartphone and mobile Internet.", + "player-mainBlockSubblock": "Our soultion optimaized to work smoothly even with mobile internet.", + "player-button": "Demoversion", + "analytics-title": "collects data about the users and their behavior.", + "analytics-mainBlockCaption": "The program's internal analytics system collects information about the user's behavior and prepare an analytical report that contains necessary metrics about user's behavior and manager's efficiency", + "cardLarge-title": "Converting managers to bookings", + "cardLarge-row": "Jane Cooper", + "cardLarge-row1": "Guy Hawkins", + "cardLarge-row2": "Robert Fox", + "cardLarge-row3": "Bessie Cooper", + "cardLarge1-title": "Popular types of apartments, %", + "cardLarge1-row": "Studios", + "cardLarge1-row1": "1-r. apartment", + "cardLarge1-row2": "2-r. apartment", + "cardLarge1-row3": "3-r. apartment", + "cardLarge1-row4": "4-r. apartment", + "cardLarge2-title": "Purchase funnel", + "cardLarge2-row": "Sessions", + "cardLarge2-row1": "To favorites", + "cardLarge2-row2": "Bookings", + "cardLarge2-row3": "Sales", + "cardSmall-title": "Average session time, min.", + "cardSmall1-title": "Commercial proposals, pcs.", + "cardSmall2-title": "Conversion from session to sale, %", + "cardSmall3-title": "Sold through Graff.estate, million rubles.", + "analytics-mainBlockSubblock": "Based on the report of our analytics system, you can make the process of demonstrating a residential complex even more efficient.", + "analytics-button": "Demoversion", + "cloudSolution-title": "is a cloud solution.", + "cloudSolution=mainBlockCaption": "Hosting the project on our servers allows you to quickly apply program updates and keep the product in the most efficient state.", + "footer-social": "Social Networks", + "footer-captionCompain": "About", + "footer-captionContact": "Contacts", + "footer-ru-addres": "st. Moskovskaya, 47,", + "footer-ru-city": "Yekaterinburg", + "footer-ru-country": "Russia", + "footer-uae-address": "Dubai", + "footer-uae-country": "UAE", + "card1-caption": "Write us", + "card2-caption": "Call us", + "copyright-caption": "Privacy Policy", + "copyright-caption1": "©Graff Interactive. All rights reserved", + "demo-title": "Available demonstrations" +} \ No newline at end of file diff --git a/public/assets/locales/ru/translation.json b/public/assets/locales/ru/translation.json new file mode 100644 index 0000000..79ebbd9 --- /dev/null +++ b/public/assets/locales/ru/translation.json @@ -0,0 +1,81 @@ +{ + "main-titleCaption": "Технология удаленной демонстрации жилого комплекса.", + "main-button": "Демоверсия", + "slider-title": "", + "slider-mainBlockCaptionColor": "бескомпромиссный уровень графики и полное погружение покупателя в процесс выбора квартиры.", + "slider-mainBlockCaption": "Покажем все преимущества вашего жилого комплекса клиенту из любого конца мира. Местоположение и устройство значения не имеют. Нужен только интернет.", + "slider-subtitle": "План этажа", + "slider-subtitle1": "Архитектура", + "slider-subtitle2": "Инфраструктура", + "slider-subtitle3": "Благоустройство", + "calendar-title": "легко встраивается в существующую цепочку продаж.", + "calendar-mainBlockCaptionColor": "Проведение виртуальных экскурсий сотрудниками офиса продаж.", + "calendar-mainBlockCaption": "Покупателю доступна возможность самостоятельно записаться на просмотр в удобное для него время, а менеджер отдела продаж удаленно поможет выбрать планировку и забронировать квартиру.", + "calendar-mainBlockSubblock": "Процесс записи может быть встроен на сайт жилого комплекса, на который была настроена реклама.", + "calendarCaption": "Выбор даты", + "timepickerCaption": "Выбор времени", + "formCaption": "Оформление", + "appointmentTrue": "Запись есть", + "appointmentFalse": "Записи нет", + "confirm-title": "Просмотр запланирован", + "confirm-caption": "Дополнительная информация будет отправлена на указанный почтовый адрес или номер телефона.", + "buttons-plan": "Запланировать", + "buttons-next": "Продолжить", + "placeholders-name": "Имя", + "placeholders-phoneNumber": "Номер телефона", + "placeholders-email": "E-mail", + "multiplayer-title": "для совместной работы.", + "multiplayer-mainBlockCaption": "Покупатель может вместе с близкими людьми осмотреть жилой комплекс. Это совершенно новый опыт в процессе выбора квартиры.", + "multiplayer-mainBlockSubblock": "Осмотр жилого комплекса доступен в любой момент, без необходимости предварительной записи на просмотр.", + "multiplayer-button": "Демоверсия", + "devices-title": "доступен на любых устройствах.", + "devices-mainBlockCaption": "Приложение легко адаптируется под экран любого размера и одинаково хорошо выглядит на мобильном телефоне или на большом сенсорном экране в отделе продаж застройщика.", + "devices-button": "Демоверсия", + "photoComponent-title": "уже работает в офисах продаж застройщиков.", + "photoComponent-mainBlockCaption": "Для организации интерактивной презентации жилого комплекса застройщику не нужно покупать дорогостоящее оборудование и поддерживать его работоспособность.", + "photoComponent-mainBlockSubblock": "В офисе продаж достаточно разместить сенсорный стол с доступом в интернет.", + "player-title": "позволяет передать на устройство пользователя впечатляющий уровень графики.", + "player-mainBlockCaption": "Для работы с интерактивной презентацией покупателю достаточно смартфона и мобильного интернета.", + "player-mainBlockSubblock": "Решение дополнительно оптимизированно для работы даже с мобильным интернетом.", + "player-button": "Демоверсия", + "analytics-title": "собирает данные о пользователе и его поведении.", + "analytics-mainBlockCaption": "Система внутренней аналитики программы собирает информацию о поведении пользователя и эффективности работы менеджеров для создания отчета, содержащего необходимые метрики.", + "cardLarge-title": "Конверсия менеджеров в брони", + "cardLarge-row": "К.Н. Федоров", + "cardLarge-row1": "И.Ф. Яковлева", + "cardLarge-row2": "А.М. Ташева", + "cardLarge-row3": "А.А. Фетисов", + "cardLarge1-title": "Популярные типы квартир, %", + "cardLarge1-row": "Студии", + "cardLarge1-row1": "1-к. квартиры", + "cardLarge1-row2": "2-к. квартиры", + "cardLarge1-row3": "3-к. квартиры", + "cardLarge1-row4": "4-к. квартиры", + "cardLarge2-title": "Воронка продаж", + "cardLarge2-row": "Сеансы", + "cardLarge2-row1": "В избранное", + "cardLarge2-row2": "Брони", + "cardLarge2-row3": "Продажи", + "cardSmall-title": "Среднее \n время сеанса,\n мин.", + "cardSmall1-title": "Сформировано коммерческих предложений, шт", + "cardSmall2-title": "Конверсия из сеанса в продажу, %", + "cardSmall3-title": "Продано через Graff.estate, млн.руб.", + "analytics-mainBlockSubblock": "Полученный отчет позволяет сделать процесс демонстрации жилого комплекса еще эффективнее.", + "analytics-button": "Демоверсия", + "cloudSolution-title": "это облачное решение.", + "cloudSolution=mainBlockCaption": "Необходимые для работы приложения вычисления проводятся на удаленном сервере. Это позволяет быстро применять обновления программы и поддерживать продукт в максимально эффективном состоянии.", + "footer-social": "Социальные сети", + "footer-captionCompain": "О компании", + "footer-captionContact": "Контакты", + "footer-ru-addres": "ул. Московская, 47,", + "footer-ru-city": "Екатеринбург", + "footer-ru-country": "Россия", + "footer-uae-address": "Дубаи", + "footer-uae-country": "ОАЭ", + "card1-caption": "Написать нам", + "card2-caption": "Позвонить", + "copyright-caption": "Политика конфиденциальности", + "copyright-caption1": "©Graff Interactive. Все права защищены", + "demo-title": "Доступные демонстрации" + +} \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index 6224633..19e2744 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3,14 +3,21 @@ import "./App.css" import { Route, Switch } from "react-router-dom"; import React, { useEffect, useState } from "react"; +import { useAppDispatch, useAppSelector } from "./hooks/redux"; +import cookies from "js-cookie"; + import AOS from "aos"; import "aos/dist/aos.css"; import { Main } from "./components/main/main"; import { Header } from "./components/header/header"; import { Footer } from "./components/footer/footer"; -import { Demostration } from "./components/demonstration/demonstartion"; +import useQuery from "./hooks/useQuery"; import textRU from "./utils/textRU" import textEN from "./utils/textEN"; +import { languageSlice } from "./store/reducers/languageSlice"; +import { fetchCards } from "./store/reducers/ActionCreator"; + + export type TObjct = { calendar: boolean; @@ -28,51 +35,52 @@ export const App: React.FC = () => { const savedLanguage = localStorage.getItem('savedLang') + const dispatch = useAppDispatch(); + const { handleChangeLanguage } = languageSlice.actions; + const { cards, currentCard, error } = useAppSelector((state) => state.cardReducer); + const { currentLang } = useAppSelector((state) => state.languageReducer); + + const query = useQuery() + + const langQuery = query.get('lang') + + const langArray = ['en', 'ru'] + + const browserLanguage = window.navigator.language + + const handleBrowserLanguage = () => { + return langArray.includes(browserLanguage) + } + + const handleCookiesLanguage = () => { + const language = cookies.get("i18next") + return language + } + useEffect(() => { - if (savedLanguage !== null) { - setSavedLanguage() - } else { - setInitialLanguage() + if (langArray.includes(langQuery as string)) { + dispatch(handleChangeLanguage(langQuery as string)); + return } - }, []); - - function setSavedLanguage() { - if (savedLanguage === 'RU') { - setLanguage('RU') - localStorage.setItem("lang", 'RU') - setText(textRU) - } else if (savedLanguage === 'EN') { - setLanguage('EN') - localStorage.setItem("lang", 'EN') - setText(textEN) + else if (handleCookiesLanguage()) { + const languageCookies = handleCookiesLanguage() + console.log(languageCookies) + dispatch(handleChangeLanguage(languageCookies as string)) + return } - } + let isSupported = handleBrowserLanguage() + dispatch(handleChangeLanguage(isSupported ? browserLanguage : 'en')); + }, []) - function setInitialLanguage() { - if (window.navigator.language === 'ru') { - setLanguage('RU') - localStorage.setItem("lang", 'RU') - setText(textRU) - } else { - setLanguage('EN') - setText(textEN) + useEffect(() => { + if (currentLang) { + dispatch(fetchCards(currentLang)) } - } + }, [currentLang]) + + - function changeLanguage(language: string) { - if (language === 'RU') { - setLanguage(language); - setText(textRU) - localStorage.setItem('savedLang', 'RU'); - localStorage.setItem("lang", 'RU') - } else { - setLanguage('EN') - localStorage.setItem("lang", 'EN') - localStorage.setItem("savedLang", 'EN') - setText(textEN) - } - } AOS.init({ mirror: true, @@ -81,8 +89,8 @@ export const App: React.FC = () => {
-
-
+
+
diff --git a/src/components/analytics/analytics.css b/src/components/analytics/analytics.css index e9ffc5b..117419b 100644 --- a/src/components/analytics/analytics.css +++ b/src/components/analytics/analytics.css @@ -37,7 +37,7 @@ -webkit-box-direction: normal; -ms-flex-direction: column; flex-direction: column; - background: #141414; + background: #1C1D21; border-radius: 16px; padding: 32px; font-style: normal; @@ -49,7 +49,7 @@ /* Landing/White */ - color: #EBEBEB; + color: #F2F2F2; } @@ -85,7 +85,7 @@ align-items: flex-start; gap: 20px; padding: 24px; - background: #141414; + background: #1C1D21; border-radius: 16px; -webkit-box-sizing: border-box; box-sizing: border-box; @@ -95,7 +95,7 @@ font-weight: 400; font-size: 18px; line-height: 140%; - color: #EBEBEB; + color: #F2F2F2; background-color: transparent; } @@ -104,7 +104,7 @@ font-weight: 400; font-size: 18px; line-height: 140%; - color: #EBEBEB; + color: #F2F2F2; } .graph__row_item_l { diff --git a/src/components/analytics/analytics.tsx b/src/components/analytics/analytics.tsx index a47b151..df1a9bb 100644 --- a/src/components/analytics/analytics.tsx +++ b/src/components/analytics/analytics.tsx @@ -4,21 +4,23 @@ import "../multiplayer/multiplayer.css"; import { PieChartComp } from "../piechart/piechart"; import { Button } from "../buttonDemo/buttonDemo" import { Content } from "../content/content"; +import { useTranslation } from "react-i18next"; export const Analytics: React.FC = ({ text }) => { const { cardLarge, cardLarge1, cardLarge2, cardSmall, cardSmall1, cardSmall2, cardSmall3 } = text; + const {t} = useTranslation() return (
- {cardLarge.title} + {t('cardLarge-title')}
-

{cardLarge.row}

+

{t('cardLarge-row')}

@@ -27,7 +29,7 @@ export const Analytics: React.FC = ({ text }) => {

45%

-

{cardLarge.row1}

+

{t('cardLarge-row1')}

@@ -37,7 +39,7 @@ export const Analytics: React.FC = ({ text }) => {
-

{cardLarge.row2}

+

{t('cardLarge-row2')}

@@ -47,7 +49,7 @@ export const Analytics: React.FC = ({ text }) => {
-

{cardLarge.row3}

+

{t('cardLarge-row3')}

@@ -58,57 +60,57 @@ export const Analytics: React.FC = ({ text }) => {
- {cardLarge1.title} + {t('cardLarge1-title')}
-

{cardLarge1.row}

+

{t('cardLarge1-row')}

-

{cardLarge1.row1}

+

{t('cardLarge1-row1')}

-

{cardLarge1.row2}

+

{t('cardLarge1-row2')}

-

{cardLarge1.row3}

+

{t('cardLarge1-row3')}

-

{cardLarge1.row4}

+

{t('cardLarge1-row4')}

- {cardLarge2.title} + {t('cardLarge2-title')}
-
{cardLarge2.row}
+
{t('cardLarge2-row')}
-
{cardLarge2.row1}
+
{t('cardLarge2-row1')}
93,47%
-
{cardLarge2.row2}
+
{t('cardLarge2-row2')}
45,68%
-
{cardLarge2.row3}
+
{t('cardLarge2-row3')}
29,13%
@@ -118,38 +120,38 @@ export const Analytics: React.FC = ({ text }) => {
- {cardSmall.title} + {t('cardSmall-title')}
12:45
- {cardSmall1.title} + {t('cardSmall1-title')}
856
- {cardSmall2.title} + {t('cardSmall2-title')}
12,44
- {cardSmall3.title} + {t('cardSmall3-title')}
134,5
-
{text.mainBlockSubblock}
+
{t(`${text}-mainBlockSubblock`)}
-
{text.mainBlockSubblock}
- +
{t(`${text}-mainBlockSubblock`)}
+
diff --git a/src/components/animationComponent/animationComponent.tsx b/src/components/animationComponent/animationComponent.tsx index fa89eb0..fe794b2 100644 --- a/src/components/animationComponent/animationComponent.tsx +++ b/src/components/animationComponent/animationComponent.tsx @@ -3,8 +3,11 @@ import "./animationComponent.css" import "../../styles/styles.css" import "./mapblock.css" import { Button } from "../buttonDemo/buttonDemo" +import { useTranslation } from 'react-i18next'; + +export const AnimationComponent: React.FC = () => { + const { t } = useTranslation() -export const AnimationComponent: React.FC = ({ text }) => { return (
@@ -18,9 +21,9 @@ export const AnimationComponent: React.FC = ({ text }) => {
- {text.titleCaption} + {t('main-titleCaption')} - +
diff --git a/src/components/buttonDemo/buttonDemo.tsx b/src/components/buttonDemo/buttonDemo.tsx index b5b972b..510af20 100644 --- a/src/components/buttonDemo/buttonDemo.tsx +++ b/src/components/buttonDemo/buttonDemo.tsx @@ -1,16 +1,26 @@ import { Link } from "react-router-dom"; import iconButton from '../../styles/iconButton.svg' import '../../styles/styles.css' +import { useTranslation } from 'react-i18next'; +import { useAppSelector, useAppDispatch } from "../../hooks/redux"; + + + +export const Button: React.FC = () => { + const { currentLang } = useAppSelector((state) => state.languageReducer); + + const { t } = useTranslation() + + + return ( + + + + ) +} -export const Button: React.FC = ({text}) => { - return ( - - - -) -} \ No newline at end of file diff --git a/src/components/calendar/calendar.tsx b/src/components/calendar/calendar.tsx index 22d7b91..7dde182 100644 --- a/src/components/calendar/calendar.tsx +++ b/src/components/calendar/calendar.tsx @@ -5,8 +5,9 @@ import point0 from "./Ellipse0.svg"; import point1 from "./Ellipse1.svg"; import { TProps } from "../calendarDesktop/calendarDesktop"; import { useSwiper } from 'swiper/react'; +import { useTranslation } from "react-i18next"; -export const Calendar: React.FC = ({ open, setOpen, time, onUpdate, isMobile, locale, text }) => { +export const Calendar: React.FC = ({ open, setOpen, time, onUpdate, isMobile, locale }) => { const swiper = useSwiper(); const [calendar, setCalendar] = useState([]); const [value, setValue] = useState(time); @@ -25,8 +26,8 @@ export const Calendar: React.FC = ({ open, setOpen, time, onUpdate, isMo useEffect(() => { setValue(time.locale(locale as string)) - }, [locale]) - + }, [locale]) + useEffect(() => { const day = startDay.clone().subtract("1", "day"); @@ -86,11 +87,13 @@ export const Calendar: React.FC = ({ open, setOpen, time, onUpdate, isMo } - return (
+ const { t } = useTranslation() + + return (
- {text.calendarCaption} + {t('calendarCaption')}
@@ -134,11 +137,11 @@ export const Calendar: React.FC = ({ open, setOpen, time, onUpdate, isMo
point - {text.appointmentTrue} + {t('appointmentTrue')}
point - {text.appointmentFalse} + {t('appointmentFalse')}
) diff --git a/src/components/calendarDesktop/Arrow_Left.svg b/src/components/calendarDesktop/Arrow_Left.svg deleted file mode 100644 index d30b692..0000000 --- a/src/components/calendarDesktop/Arrow_Left.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/components/calendarDesktop/calendar.css b/src/components/calendarDesktop/calendar.css index 979eb26..4ee2d52 100644 --- a/src/components/calendarDesktop/calendar.css +++ b/src/components/calendarDesktop/calendar.css @@ -3,35 +3,7 @@ } -.button__arrow_l { - cursor: pointer; - background-image: url(./Arrow_Left.svg); - background-repeat: no-repeat; - background-position: center; - height: 14px; - width: 22px; -} -.button__arrow_r { - -webkit-transform: matrix(-1, 0, 0, 1, 0, 0); - -ms-transform: matrix(-1, 0, 0, 1, 0, 0); - transform: matrix(-1, 0, 0, 1, 0, 0); - - cursor: pointer; - background-image: url(./Arrow_Left.svg); - background-repeat: no-repeat; - background-position: center; - height: 14px; - width: 22px; -} - -.button__arrow_r:hover { - background-image: url(../calendar/arrow_active1.png); -} - -.button__arrow_l:hover { - background-image: url(../calendar/arrow_active1.png); -} .button__arrow_back { cursor: pointer; @@ -139,11 +111,11 @@ -ms-flex-direction: column; flex-direction: column; - color: #ebebeb; + color: #F2F2F2; -webkit-box-sizing: border-box; box-sizing: border-box; width: 385px; - background: #141414; + background: #1C1D21; border-radius: 16px; padding: 24px 32px; box-sizing: border-box; diff --git a/src/components/calendarDesktop/calendarDesktop.tsx b/src/components/calendarDesktop/calendarDesktop.tsx index 3f16b35..90da832 100644 --- a/src/components/calendarDesktop/calendarDesktop.tsx +++ b/src/components/calendarDesktop/calendarDesktop.tsx @@ -9,6 +9,7 @@ import "../../styles/styles.css"; import { Confirm } from "../confirm/confirm"; import { Calendar } from "../calendar/calendar"; import { Content } from '../content/content' +import { useTranslation } from "react-i18next"; export type TProps = { @@ -36,14 +37,13 @@ export const CalendarDesktop: React.FC = ({ text, locale }) => { - console.log(text, 'here2') + const {t} = useTranslation() return (
- +
= ({ /> = ({ >
= ({ >
= ({
- {text.mainBlockSubblock} + {t('calendar-mainBlockSubblock')}
diff --git a/src/components/calendarMobile/calendarMobile.tsx b/src/components/calendarMobile/calendarMobile.tsx index 8ecc818..34a4e58 100644 --- a/src/components/calendarMobile/calendarMobile.tsx +++ b/src/components/calendarMobile/calendarMobile.tsx @@ -11,6 +11,7 @@ import "swiper/css/pagination"; import "swiper/css/navigation"; import "./calendarMobile.css" import { Content } from "../content/content"; +import { useTranslation } from "react-i18next"; @@ -18,11 +19,12 @@ import { Content } from "../content/content"; export const CalendarMobile: React.FC = ({ setDisplayForm, displayForm, open, setOpen, onUpdate, time, onExit, isMobile, text, locale }) => { + const { t } = useTranslation() return (
- +
= ({ setDisplayForm, displayForm,
- {text.mainBlockSubblock} + {t('calendar-mainBlockSubblock')}
diff --git a/src/components/cloudSolution/cloudSolution.css b/src/components/cloudSolution/cloudSolution.css index 1817e34..7a7038c 100644 --- a/src/components/cloudSolution/cloudSolution.css +++ b/src/components/cloudSolution/cloudSolution.css @@ -5,8 +5,7 @@ .block__container { overflow: hidden; - background: #141414; - border-radius: 32px; + background: #1C1D21; border-radius: 32px; padding: 74px 80px; -webkit-box-sizing: border-box; box-sizing: border-box; diff --git a/src/components/cloudSolution/cloudSolution.tsx b/src/components/cloudSolution/cloudSolution.tsx index 80f0833..e8b327d 100644 --- a/src/components/cloudSolution/cloudSolution.tsx +++ b/src/components/cloudSolution/cloudSolution.tsx @@ -1,9 +1,11 @@ import './cloudSolution.css' import "../../styles/styles.css"; import serverFrame from './serverFrame.png' +import { useTranslation } from 'react-i18next'; export const CloudSolution: React.FC = ({ text }) => { + const {t} = useTranslation() return (
@@ -17,11 +19,11 @@ export const CloudSolution: React.FC = ({ text }) => { Graff.estate stream –

- {text.title} + {t('cloudSolution-title')}

- {text.mainBlockCaption} + {t('cloudSolution=mainBlockCaption')}

diff --git a/src/components/confirm/confirm.tsx b/src/components/confirm/confirm.tsx index 93b7b9f..63b49a4 100644 --- a/src/components/confirm/confirm.tsx +++ b/src/components/confirm/confirm.tsx @@ -1,13 +1,16 @@ import '../form/form.css' import { TProps } from "../calendarDesktop/calendarDesktop"; import { useSwiper } from 'swiper/react'; +import { useTranslation } from 'react-i18next'; -export const Confirm: React.FC = ({ open, onExit, setOpen, isMobile, text }) => { +export const Confirm: React.FC = ({ open, onExit, setOpen, isMobile }) => { const swiper = useSwiper(); + const {t} = useTranslation() + function exit() { if (isMobile) { swiper.slideNext() @@ -20,14 +23,14 @@ export const Confirm: React.FC = ({ open, onExit, setOpen, isMobile, tex return (

- {text.confirm.title} + {t("confirm-title")}

- {text.confirm.caption} + {t("confirm-caption")}

) diff --git a/src/components/content/content.tsx b/src/components/content/content.tsx index d65e609..51c570e 100644 --- a/src/components/content/content.tsx +++ b/src/components/content/content.tsx @@ -1,20 +1,21 @@ +import { useTranslation } from "react-i18next"; import "../../styles/styles.css"; -export const Content: React.FC = ({ text, captionSmall }) => { +export const Content: React.FC = ({ text, captionSmall, isColorCaption }) => { + const { t } = useTranslation() return (

Graff.estate stream{" "} -

{text.title}

+

{t(`${text}-title`)}

- {text.mainBlockCaptionColor ?

- {text.mainBlockCaptionColor} -

- : <>} + {isColorCaption ?

+ {t(`${text}-mainBlockCaptionColor`)} +

: <>}

- {text.mainBlockCaption} + {t(`${text}-mainBlockCaption`)}

) diff --git a/src/components/demos/demos.css b/src/components/demos/demos.css index 4442563..9587427 100644 --- a/src/components/demos/demos.css +++ b/src/components/demos/demos.css @@ -35,7 +35,7 @@ } .demos_container { - background: #141414; + background: #1C1D21; border-radius: 16px; } @@ -52,6 +52,7 @@ } .demo_image { + object-fit: cover; background: -webkit-gradient(linear, left bottom, left top, from(rgba(0, 0, 0, 0.3)), to(rgba(0, 0, 0, 0.3))); background: -o-linear-gradient(bottom, rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.3)); background: linear-gradient(0deg, rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.3)); @@ -88,7 +89,7 @@ /* Landing/White */ - color: #EBEBEB; + color: #F2F2F2; } .caption { @@ -102,7 +103,7 @@ /* Landing/White */ - color: #EBEBEB; + color: #F2F2F2; opacity: 0.5; @@ -120,7 +121,7 @@ /* Landing/White */ - color: #EBEBEB; + color: #F2F2F2; } .caption2 { @@ -135,7 +136,7 @@ /* Landing/White */ - color: #EBEBEB; + color: #F2F2F2; opacity: 0.5; } @@ -232,8 +233,6 @@ .demo_image { height: auto; - -o-object-fit: cover; - object-fit: cover; width: 240px; } } diff --git a/src/components/demos/demos.tsx b/src/components/demos/demos.tsx index 6853b4d..7ca72b4 100644 --- a/src/components/demos/demos.tsx +++ b/src/components/demos/demos.tsx @@ -3,56 +3,41 @@ import "../../styles/styles.css"; import building1 from './building1.png' import building2 from './building2.png' +import { useAppDispatch, useAppSelector } from "../../hooks/redux"; import iconButton from "./iconButton.svg" +import { useTranslation } from 'react-i18next'; -export const Demos: React.FC = ({ text }) => { - const { card, card1 } = text; - console.log(text) +export const Demos: React.FC = ({ cards }) => { + const { currentLang } = useAppSelector((state) => state.languageReducer); + const {t} = useTranslation() return (

- {text.title} + {t('demo-title')}

- -
- icon -
-
- buildingImg - ) diff --git a/src/components/devices/Screen.png b/src/components/devices/Screen.png new file mode 100644 index 0000000..2f64429 Binary files /dev/null and b/src/components/devices/Screen.png differ diff --git a/src/components/devices/devices.css b/src/components/devices/devices.css index a02c693..3e94589 100644 --- a/src/components/devices/devices.css +++ b/src/components/devices/devices.css @@ -34,8 +34,7 @@ flex-direction: column; height: 380px; padding: 32px; - background: #141414; - border-radius: 16px; + background: #1C1D21; border-radius: 16px; gap: 20px; } @@ -67,7 +66,7 @@ /* Landing/White */ - color: #ebebeb; + color: #C5C7CE; opacity: 0.8; } diff --git a/src/components/devices/iPhone.png b/src/components/devices/iPhone.png index 8d097db..ef0ef0e 100644 Binary files a/src/components/devices/iPhone.png and b/src/components/devices/iPhone.png differ diff --git a/src/components/devices/image.png b/src/components/devices/image.png index 7f9b9e0..74ea777 100644 Binary files a/src/components/devices/image.png and b/src/components/devices/image.png differ diff --git a/src/components/devices/image1.png b/src/components/devices/image1.png index f24aded..bc74a77 100644 Binary files a/src/components/devices/image1.png and b/src/components/devices/image1.png differ diff --git a/src/components/footer/footer.css b/src/components/footer/footer.css index d8262b8..cfb5d45 100644 --- a/src/components/footer/footer.css +++ b/src/components/footer/footer.css @@ -9,7 +9,7 @@ -webkit-box-sizing: border-box; box-sizing: border-box; background-color: transparent; - background: #141414; + background: #1C1D21; border-radius: 32px 32px 0px 0px; color: #EBEBEB; display: -webkit-box; @@ -30,7 +30,7 @@ display: -webkit-box; display: -ms-flexbox; display: flex; - padding: 120px 120px; + padding: 0 120px; gap: 186px; -webkit-box-pack: justify; -ms-flex-pack: justify; @@ -173,7 +173,7 @@ } .footer__card_l { - background: #1E1E1E; + background: #2E3038; border-radius: 16px; padding: 32px; margin-bottom: 30px; @@ -190,6 +190,7 @@ } .footer__card_2 { + -webkit-box-sizing: border-box; box-sizing: border-box; cursor: pointer; @@ -205,8 +206,8 @@ /* Gray */ - color: #E3DDE5; - border: 1px solid #454545; + color: #FFFFFF; + border: 1px solid #73788C; border-radius: 16px; } diff --git a/src/components/footer/footer.tsx b/src/components/footer/footer.tsx index c47e01c..02df5cd 100644 --- a/src/components/footer/footer.tsx +++ b/src/components/footer/footer.tsx @@ -22,7 +22,6 @@ export const Footer: React.FC = ({ text, language }) => {

{text.social}

diff --git a/src/components/footer/logo.svg b/src/components/footer/logo.svg index 92b6e44..df0b41e 100644 --- a/src/components/footer/logo.svg +++ b/src/components/footer/logo.svg @@ -1,13 +1,61 @@ - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/form/form.css b/src/components/form/form.css index 6a41b56..f0a61ec 100644 --- a/src/components/form/form.css +++ b/src/components/form/form.css @@ -1,6 +1,5 @@ .form__container { width: 385px; - color: #ebebeb; display: -webkit-box; display: -ms-flexbox; display: flex; @@ -11,7 +10,8 @@ padding: 24px 32px; -webkit-box-sizing: border-box; box-sizing: border-box; - background: #141414; + background: #1C1D21; + color: #F2F2F2; border-radius: 16px; -webkit-box-pack: justify; -ms-flex-pack: justify; diff --git a/src/components/form/form.tsx b/src/components/form/form.tsx index 732139d..2af5278 100644 --- a/src/components/form/form.tsx +++ b/src/components/form/form.tsx @@ -3,9 +3,10 @@ import "../timepicker/timepicker.css" import "../../index.css" import { TProps } from "../calendarDesktop/calendarDesktop"; import { useSwiper } from 'swiper/react'; +import { useTranslation } from "react-i18next"; -export const Form: React.FC = ({ time, open, setOpen, displayForm, setDisplayForm, isMobile, text }) => { +export const Form: React.FC = ({ time, open, setOpen, displayForm, setDisplayForm, isMobile }) => { const swiper = useSwiper(); @@ -37,7 +38,7 @@ export const Form: React.FC = ({ time, open, setOpen, displayForm, setDi form: false, confirm: true }) - swiper.slideNext() + swiper.slideNext() } else { setDisplayForm(false) setOpen({ @@ -45,11 +46,12 @@ export const Form: React.FC = ({ time, open, setOpen, displayForm, setDi timePicker: false, form: false, confirm: true - }) + }) } } + const { t } = useTranslation() return ( @@ -58,7 +60,7 @@ export const Form: React.FC = ({ time, open, setOpen, displayForm, setDi
- {text.formCaption} + {t('formCaption')}
{!open.form ? '' : time.format("DD MMM, HH:mm")}
@@ -70,26 +72,26 @@ export const Form: React.FC = ({ time, open, setOpen, displayForm, setDi name="tel" className="form__input" type="text" - placeholder={text.placeholders.name} + placeholder={t('placeholders-phoneNumber') as string} >
); diff --git a/src/components/header/EN.svg b/src/components/header/EN.svg new file mode 100644 index 0000000..e9e4b79 --- /dev/null +++ b/src/components/header/EN.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/components/header/RU.svg b/src/components/header/RU.svg new file mode 100644 index 0000000..eee0b13 --- /dev/null +++ b/src/components/header/RU.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/components/header/header.css b/src/components/header/header.css index d4a341e..7c98b5b 100644 --- a/src/components/header/header.css +++ b/src/components/header/header.css @@ -1,32 +1,53 @@ -/* -* Prefixed by https://autoprefixer.github.io -* PostCSS: v8.4.14, -* Autoprefixer: v10.4.7 -* Browsers: last 4 version -*/ - -.header-container { - display: -webkit-box; - display: -ms-flexbox; +.header { + box-sizing: border-box; + width: 100%; display: flex; padding: 30px 40px; color: #ffffff; - -webkit-box-pack: justify; - -ms-flex-pack: justify; justify-content: space-between; - -webkit-box-align: center; - -ms-flex-align: center; align-items: center; - position: relative; +} + +.header-popup { + position: absolute; +} + +.header-button-container { + display: flex; + gap: 8px; + +} + + +.button-lang { + background: transparent; + border: none; + outline: none; + display: flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + box-sizing: border-box; + border-radius: 4px; + padding: 10px 14px; +} + + +.button-lang:hover { + background: #23242A; +} + +.button-lang-active { + cursor: pointer; + pointer-events: none; + background: #2E3038; + } .header__buttons_wrapper { - display: -webkit-box; - display: -ms-flexbox; display: flex; gap: 40px; - -webkit-box-align: center; - -ms-flex-align: center; align-items: center; } @@ -35,73 +56,48 @@ } .header-logo { - width: 31px; - height: 50px; + width: 104px; + height: 40px; + } .header-buttons { margin-right: 96px; } -.header-button { - font-family: "GilroyWebRegular"; - font-style: normal; - font-weight: 400; - font-size: 16px; - line-height: 125%; - background-color: transparent; - border: none; - color: #ffffff; - cursor: pointer; - padding: 0px; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - text-decoration: none; -} .header-lang-button-text:hover { background: rgba(235, 235, 235, 0.2); + } .wrapper__button { margin: 0; - height: 15px; } + + .header-lang-button { position: absolute; right: 40px; top: 40px; overflow: hidden; - display: -webkit-box; - display: -ms-flexbox; display: flex; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - -ms-flex-direction: column; flex-direction: column; gap: 10px; - width: 58px; - padding: 5px 12px; - -webkit-box-sizing: border-box; + width: 55px; + padding: 6px 8px 5px 12px; box-sizing: border-box; - height: 31px; - -webkit-box-pack: justify; - -ms-flex-pack: justify; + height: 29px; justify-content: space-between; box-sizing: border-box; line-height: 13px; background-color: transparent; - border: 1.2px solid #ebebeb; + border: 2px solid #ebebeb; border-radius: 12px; - -webkit-transition: height ease-in 0.3s; - -o-transition: height ease-in 0.3s; - transition: height ease-in 0.3s; - font-style: normal; - font-weight: 500; - font-size: 15px; - line-height: 130%; + transition: height ease-in 0.5s; + + } .header-lang-button_disabled { @@ -109,7 +105,7 @@ } .header-lang-button_active { - height: 53px; + height: 51px; } .header-lang-button_active { @@ -117,8 +113,8 @@ } .header-lang-button-text { + width: 100%; - -moz-appearance: none; appearance: none; background-color: transparent; color: #ebebeb; @@ -126,54 +122,29 @@ text-align: left; margin: 0; border: none; - height: 15px; + height: 13px; -webkit-appearance: none; - font-style: normal; - font-weight: 500; - font-size: 15px; - line-height: 130%; cursor: pointer; } .header-lang-button-picked { - width: 100%; - -moz-appearance: none; - appearance: none; - background-color: transparent; - color: #ebebeb; - padding: 0; - text-align: left; - margin: 0; - border: none; - height: 100%; - -webkit-appearance: none; - font-style: normal; - font-weight: 500; - font-size: 15px; - line-height: 130%; - cursor: pointer; } .header-select__icon { top: 6px; - height: 16px; + height: 13px; right: 5px; position: absolute; + + } .select { background-color: transparent; - display: -webkit-box; - display: -ms-flexbox; display: flex; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - -ms-flex-direction: column; flex-direction: column; - -ms-flex-item-align: center; - -ms-grid-row-align: center; align-self: center; gap: 11px; position: relative; @@ -187,9 +158,10 @@ .option { /* or 16px */ + /* Landing/White */ - color: #ebebeb; + color: #EBEBEB; } .burger__button { @@ -197,34 +169,31 @@ cursor: pointer; background-color: transparent; display: none; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - -ms-flex-direction: column; flex-direction: column; gap: 5px; - -webkit-box-sizing: border-box; box-sizing: border-box; + } .line__burger { - background-color: #ffffff; + background-color: #FFFFFF; width: 14px; height: 1px; } @media screen and (min-width: 1600px) { - .header-container { + .header { padding: 35px 48px; } - .header-logo { - width: 46px; - height: 74px; - cursor: pointer; - } - .header-button { font-size: 20px; + + } + + .header-lang-button_active { + height: auto; + } .header-buttons { @@ -232,45 +201,58 @@ } .header-lang-button { + width: 70px; + padding: 7px 15px; top: 57px; - width: 60px; } + } + @media screen and (max-width: 1279px) { - .header-container { + .header { padding: 30px 12px; } .header-lang-button { top: 40px; right: 12px; + } } @media screen and (max-width: 1024px) { - .header-container { + .header { padding: 15px 12px; } .header-lang-button { right: 12px; - top: 26px; + top: 25%; } } + @media screen and (max-width: 639px) { - .header-container { + + .header-logo { + width: 94px; + height: 36px; + + } + + .header-popup { + position: static; + } + + .header { padding: 8px 10px; } - .header-buttons { - display: none; - } + .burger__button { - display: -webkit-box; - display: -ms-flexbox; display: flex; } + } \ No newline at end of file diff --git a/src/components/header/header.tsx b/src/components/header/header.tsx index dbfb2bc..c0878e8 100644 --- a/src/components/header/header.tsx +++ b/src/components/header/header.tsx @@ -1,106 +1,39 @@ -import React, { CSSProperties, useState } from "react"; +import "./Header.css"; + import logo from "./logoIcon.svg"; -import chevron from "./chevronIcon.svg"; -import "./header.css"; -import "../../styles/styles.css"; -import iconClose from './closeIcon1.svg' -import { Nav } from "../Navigaton/navigation"; -import { useLocation, useHistory } from "react-router-dom"; +import ru from './RU.svg' +import en from './EN.svg' + +import { useAppSelector, useAppDispatch } from "../../hooks/redux"; +import { languageSlice } from "../../store/reducers/languageSlice"; + +import { useLocation } from "react-router-dom"; export type THeader = { language: string; changeLanguage: (language: string) => void; - text?: any; }; -export const Header: React.FC = - ({ changeLanguage, language, text }) => { - const [open, setOpen] = useState(false) - const [menuOpen, setMenuOpen] = useState(false) - const [opacity, setOpacity] = useState(false) - const userLanguage = language === 'RU'; +export const Header: React.FC = ({ }) => { + const buttons = [{ + icon: ru, value: "ru" + }, { icon: en, value: "en" }] + const location = useLocation(); - const location = useLocation() - const history = useHistory() - console.log(location, 'loc') - - function handleUpdateLanguage(value: string) { - changeLanguage(value) - setOpen(prev => !prev) - } - - function handleOpen() { - setOpen(prev => !prev) - } - - function handleOpacity() { - setOpacity(true) - } - - const style = { - background: 'rgba(235, 235, 235, 0.2' - }; - - const iconTransform = { - transform: 'rotate(-90deg)' - } as CSSProperties - - const setBackground = opacity && !open as boolean; - - function setOpacityAndMenu() { - setMenuOpen(true) - const targetElement = document.querySelector('body') as HTMLElement - targetElement.style.overflow = 'hidden' - } + const { handleChangeLanguage } = languageSlice.actions; + const { currentLang } = useAppSelector((state) => state.languageReducer); + const dispatch = useAppDispatch(); - function openMenu() { - setMenuOpen(false) - const targetElement = document.querySelector('body') as HTMLElement - targetElement.style.overflow = 'visible' - - } - - function goBack() { - if (location.pathname === '/demos') { - history.goBack() - } else { - window.location.reload(); - } - } - - return ( -
-
- company-logo -
- -
- img -
- -
- {language === 'RU' ? ( - ) : ()} -
-
- {menuOpen ? iconClose - : - } -
- + return ( +
+ company-logo + +
+ {buttons.map((item, index) => ( + ))}
- - ); - } - +
+ ); +}; \ No newline at end of file diff --git a/src/components/main/main.tsx b/src/components/main/main.tsx index 7268dce..83bc478 100644 --- a/src/components/main/main.tsx +++ b/src/components/main/main.tsx @@ -12,6 +12,7 @@ import { Demos } from "../demos/demos"; import { PhotoComponent } from "../photoComponent/photoComponent"; import { CalendarMobile } from "../calendarMobile/calendarMobile"; import moment, { Moment } from "moment"; + import useScreenSize from 'use-screen-size' @@ -28,7 +29,7 @@ export type TText = { text: object, } -export const Main: React.FC = ({ text, locale }) => { +export const Main: React.FC = ({ locale, cards }) => { const { width } = useScreenSize() const [displayForm, setDisplayForm] = useState(true); @@ -78,14 +79,15 @@ export const Main: React.FC = ({ text, locale }) => { }) setDisplayForm(true) } + return (
- - + +
= ({ text, locale }) => {
= ({ text, locale }) => { isMobile={isMobile} >
- - - - - - - + + + + + + +
); }; diff --git a/src/components/multiplayer/multiplayer.css b/src/components/multiplayer/multiplayer.css index 094a147..537b39c 100644 --- a/src/components/multiplayer/multiplayer.css +++ b/src/components/multiplayer/multiplayer.css @@ -6,7 +6,7 @@ */ .multiplayer__container { - background: #141414; + background: #1C1D21; border-radius: 32px; padding: 120px 32px 80px; margin-bottom: 160px; diff --git a/src/components/multiplayer/multiplayer.tsx b/src/components/multiplayer/multiplayer.tsx index 6ef8a49..4ab88e2 100644 --- a/src/components/multiplayer/multiplayer.tsx +++ b/src/components/multiplayer/multiplayer.tsx @@ -7,16 +7,18 @@ import photo3 from "./Photo3.png"; import { Button } from "../buttonDemo/buttonDemo" import { Content } from "../content/content"; import { useState } from "react"; +import { useTranslation } from "react-i18next"; export const Multiplayer: React.FC = ({text}) => { const [captionSmall, setCaptionSmall] = useState(true); + const {t} = useTranslation() return (
- +
device device @@ -25,7 +27,7 @@ export const Multiplayer: React.FC = ({text}) => {
- {text.mainBlockSubblock} + {t(`${text}-mainBlockSubblock`)}
diff --git a/src/components/player/player.css b/src/components/player/player.css index 6332c3c..e11f4a4 100644 --- a/src/components/player/player.css +++ b/src/components/player/player.css @@ -6,8 +6,7 @@ */ .player__container { - background: #141414; - border-radius: 32px; + background: #1C1D21; border-radius: 32px; padding: 120px 32px 80px; margin-bottom: 80px; } diff --git a/src/components/sliderComponent/sliderComponent.css b/src/components/sliderComponent/sliderComponent.css index d86d073..17b5a1e 100644 --- a/src/components/sliderComponent/sliderComponent.css +++ b/src/components/sliderComponent/sliderComponent.css @@ -9,7 +9,7 @@ width: 100%; margin: 0 auto; - background: #141414; + background: #1C1D21; border-radius: 32px; padding-top: 120px; padding-bottom: 85px; diff --git a/src/components/sliderComponent/sliderComponent.tsx b/src/components/sliderComponent/sliderComponent.tsx index fa5cb59..6c205c9 100644 --- a/src/components/sliderComponent/sliderComponent.tsx +++ b/src/components/sliderComponent/sliderComponent.tsx @@ -1,9 +1,11 @@ import React from "react"; import "./sliderComponent.css"; import "../../styles/styles.css" -import Swiper from "../swiper/swiper"; +import Swiper from "../swiper/swiper" +import { useTranslation } from 'react-i18next'; -export const SliderComponent: React.FC = React.memo(({ text }) => { +export const SliderComponent: React.FC = () => { + const { t } = useTranslation() return (
@@ -15,14 +17,13 @@ export const SliderComponent: React.FC = React.memo(({ text }) => {

stream

- {text.mainBlockCaptionColor} + {t('slider-mainBlockCaptionColor')}

-

{text.mainBlockCaption}

+

{t('slider-mainBlockCaption')}

= React.memo(({ text }) => { >
); -}); +} diff --git a/src/components/swiper/Arrow_Left.svg b/src/components/swiper/Arrow_Left.svg new file mode 100644 index 0000000..5e75fdb --- /dev/null +++ b/src/components/swiper/Arrow_Left.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/calendarDesktop/Arrow_Right.svg b/src/components/swiper/Arrow_Right.svg similarity index 100% rename from src/components/calendarDesktop/Arrow_Right.svg rename to src/components/swiper/Arrow_Right.svg diff --git a/src/components/swiper/swiper.css b/src/components/swiper/swiper.css index 146593f..ea6ca34 100644 --- a/src/components/swiper/swiper.css +++ b/src/components/swiper/swiper.css @@ -10,6 +10,29 @@ width: 536px; } +.button__arrow_l { + cursor: pointer; + background-image: url(./Arrow_Left.svg); + background-repeat: no-repeat; + background-position: center; + height: 14px; + width: 22px; +} + +.button__arrow_r { + -webkit-transform: matrix(-1, 0, 0, 1, 0, 0); + -ms-transform: matrix(-1, 0, 0, 1, 0, 0); + transform: matrix(-1, 0, 0, 1, 0, 0); + + cursor: pointer; + background-image: url(./Arrow_Left.svg); + background-repeat: no-repeat; + background-position: center; + height: 14px; + width: 22px; +} + + @@ -81,7 +104,7 @@ color: #ebebeb; display: flex; - color: #ebebeb !important; + color: #F2F2F2 !important; } .swiper__caption { diff --git a/src/components/swiper/swiper.tsx b/src/components/swiper/swiper.tsx index 89831fd..adefdf1 100644 --- a/src/components/swiper/swiper.tsx +++ b/src/components/swiper/swiper.tsx @@ -15,6 +15,7 @@ import "swiper/css"; import "swiper/css/pagination"; import "swiper/css/navigation"; import "../calendar/calendar"; +import { useTranslation } from "react-i18next"; type propsTypes = { slidesPerView: string; @@ -24,17 +25,18 @@ type propsTypes = { className: string; children: React.ReactNode; rewind: boolean; - text: any; slideToClickedSlide: boolean, }; export default function SwiperCentred(props: propsTypes) { + const { t } = useTranslation() + const size = useScreenSize(); const [caption, setCaption] = useState(); useEffect(() => { - setCaption(props.text.subtitle1); - }, [props.text]); + setCaption(t('slider-subtitle1')); + }, [t]); function updateCaption() { const slide = document @@ -82,8 +84,8 @@ export default function SwiperCentred(props: propsTypes) { {({ isActive }) => ( slide @@ -92,7 +94,7 @@ export default function SwiperCentred(props: propsTypes) { {({ isActive }) => ( slide {({ isActive }) => ( slide @@ -112,8 +114,8 @@ export default function SwiperCentred(props: propsTypes) { {({ isActive }) => ( slide diff --git a/src/components/timepicker/timepicker.tsx b/src/components/timepicker/timepicker.tsx index c8e49a1..4dc5a23 100644 --- a/src/components/timepicker/timepicker.tsx +++ b/src/components/timepicker/timepicker.tsx @@ -7,6 +7,7 @@ import point0 from "../calendar/Ellipse0.svg"; import point1 from "../calendar/Ellipse1.svg"; import { TProps } from "../calendarDesktop/calendarDesktop"; import { useSwiper } from 'swiper/react'; +import { useTranslation } from "react-i18next"; @@ -16,7 +17,6 @@ export const Timepicker: React.FC = ({ open, setOpen, isMobile, - text }) => { @@ -84,16 +84,18 @@ export const Timepicker: React.FC = ({ } } + const { t } = useTranslation() + return (
goBack()} className="button__arrow_back" >
- {text.timepickerCaption} + {t('timepickerCaption')}
-
{!open.calendar ? time.format("DD MMMM") : '' }
+
{!open.calendar ? time.format("DD MMMM") : ''}
{timePicker.map((date, i) => ( @@ -114,11 +116,11 @@ export const Timepicker: React.FC = ({
point - {text.appointmentTrue} + {t('appointmentTrue')}
point - {text.appointmentFalse} + {t('appointmentFalse')}
diff --git a/src/hooks/redux.ts b/src/hooks/redux.ts new file mode 100644 index 0000000..351b679 --- /dev/null +++ b/src/hooks/redux.ts @@ -0,0 +1,6 @@ +import { useDispatch, useSelector, TypedUseSelectorHook } from "react-redux"; +import { AppDispatch, RootState } from "../store/store"; + +export const useAppDispatch = () => useDispatch(); + +export const useAppSelector: TypedUseSelectorHook = useSelector; diff --git a/src/hooks/useQuery.ts b/src/hooks/useQuery.ts new file mode 100644 index 0000000..719d09c --- /dev/null +++ b/src/hooks/useQuery.ts @@ -0,0 +1,5 @@ +import { useLocation } from "react-router"; + +const useQuery = () => new URLSearchParams(useLocation().search); + +export default useQuery \ No newline at end of file diff --git a/src/index.css b/src/index.css index aec8b86..68e1417 100644 --- a/src/index.css +++ b/src/index.css @@ -5,7 +5,7 @@ @import url(./components/cloudSolution/cloudSolution.css); @import url(./components/devices/devices.css); @import url(./components/form/form.css); -@import url(./components/header/header.css); +@import url(./components/header/Header.css); @import url(./components/multiplayer/multiplayer.css); @import url(./components/player/player.css); @import url(./components/sliderComponent/sliderComponent.css); @@ -28,7 +28,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; -webkit-overflow-scrolling: touch; - background: #0e0e0e; + background: #151619; width: 100%; } diff --git a/src/index.tsx b/src/index.tsx index ed5fdd9..4ab499d 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -3,6 +3,34 @@ import './index.css'; import App from './App'; import reportWebVitals from './reportWebVitals'; import { BrowserRouter } from "react-router-dom"; +import { Provider } from "react-redux"; +import { setupStore } from "./store/store"; +import i18next from "i18next"; +import { initReactI18next } from "react-i18next"; +import HttpApi from "i18next-http-backend"; +import LanguageDetector from "i18next-browser-languagedetector"; + + +const store = setupStore(); + +i18next + .use(HttpApi) + .use(LanguageDetector) + .use(initReactI18next) + .init({ + supportedLngs: ["en", "ru"], + fallbackLng: "en", + debug: false, + // Options for language detector + detection: { + order: ["cookie", "navigator"], + caches: ["cookie"], + }, + // react: { useSuspense: false }, + backend: { + loadPath: "/assets/locales/{{lng}}/translation.json", + }, + }); const root = ReactDOM.createRoot( @@ -10,8 +38,10 @@ const root = ReactDOM.createRoot( ); root.render( - - + + + + ); diff --git a/src/models/ICards.ts b/src/models/ICards.ts new file mode 100644 index 0000000..16c9513 --- /dev/null +++ b/src/models/ICards.ts @@ -0,0 +1,11 @@ +export interface ICards { + app_title: string; + title: string; + language: string; + location: string; + feature_description: string; + icon: string; + logo: string; + preview: string; + _id: string; +} diff --git a/src/models/IData.ts b/src/models/IData.ts new file mode 100644 index 0000000..64cca21 --- /dev/null +++ b/src/models/IData.ts @@ -0,0 +1,5 @@ +export interface IData { + link: string; + connection_code: string; + msg: string, +} diff --git a/src/src.rar b/src/src.rar deleted file mode 100644 index 061f2e6..0000000 Binary files a/src/src.rar and /dev/null differ diff --git a/src/store/reducers/ActionCreator.ts b/src/store/reducers/ActionCreator.ts new file mode 100644 index 0000000..47e8b27 --- /dev/null +++ b/src/store/reducers/ActionCreator.ts @@ -0,0 +1,69 @@ +import { createAsyncThunk } from "@reduxjs/toolkit"; +import axios from 'axios'; + +const instance = axios.create({ + baseURL: 'https://a1.coord.graff.tech', +}); + +instance.defaults.headers.post['Content-Type'] = 'application/json'; + + + +export const fetchCards = createAsyncThunk( + "cards/FetchAll", + async (language: string, thunkApi) => { + try { + const { data } = await instance.get('/title/get_for_language/', { params: { start: 0, count: 100, language: language } }); + return data; + } catch (e: any) { + const { response } = e + if (!response) { + return thunkApi.rejectWithValue('Произошла ошибка, попробуйте позже'); + + } else { + return thunkApi.rejectWithValue(response.data.message); + + } + } + + } +); + + +export const createSession = createAsyncThunk( + "session/Create", + async (title: string, thunkApi) => { + try { + const { data } = await instance.post('/session/create', { title }); + return data; + } catch (e: any) { + const { response } = e + if (!response) { + return thunkApi.rejectWithValue('Произошла ошибка, попробуйте позже'); + + } else { + return thunkApi.rejectWithValue(response.data.message); + + } + } + } +); + +export const connectSession = createAsyncThunk( + "session/Connect", + async (code: string, thunkApi) => { + try { + const { data } = await instance.get('/session/connect', { params: { session_id: code } }); + return data; + } catch (e: any) { + const { response } = e + if (!response) { + return thunkApi.rejectWithValue('Произошла ошибка, попробуйте позже'); + + } else { + return thunkApi.rejectWithValue(response.data.message); + + } + } + } +); diff --git a/src/store/reducers/cardSlice.ts b/src/store/reducers/cardSlice.ts new file mode 100644 index 0000000..b73be67 --- /dev/null +++ b/src/store/reducers/cardSlice.ts @@ -0,0 +1,45 @@ +import { ICards } from "../../models/ICards"; +import { createSlice, PayloadAction } from "@reduxjs/toolkit"; +import { fetchCards } from "./ActionCreator"; + +interface CardState { + cards: ICards[]; + isLoading: boolean; + error: any; + currentCard: ICards | null; +} +const initialState: CardState = { + cards: [], + isLoading: false, + error: "", + currentCard: null, +}; + +export const cardSlice = createSlice({ + name: "cards", + initialState, + reducers: { + handleCurrentCard(state, action: PayloadAction) { + state.currentCard = action.payload; + }, + }, + extraReducers: { + + [fetchCards.fulfilled.type]: (state, action: PayloadAction) => { + state.isLoading = false; + state.error = ""; + state.cards = action.payload; + }, + [fetchCards.pending.type]: (state) => { + state.isLoading = true; + }, + [fetchCards.rejected.type]: (state, action: PayloadAction) => { + console.log(action.payload) + state.isLoading = false; + state.error = action.payload; + }, + }, + +}); + +export default cardSlice.reducer; diff --git a/src/store/reducers/languageSlice.ts b/src/store/reducers/languageSlice.ts new file mode 100644 index 0000000..1c12a7d --- /dev/null +++ b/src/store/reducers/languageSlice.ts @@ -0,0 +1,50 @@ +import { createSlice, PayloadAction } from "@reduxjs/toolkit"; +import i18next from "i18next"; +import { initReactI18next } from "react-i18next"; +import HttpApi from "i18next-http-backend"; +import cookies from "js-cookie"; +import LanguageDetector from "i18next-browser-languagedetector"; + +i18next + .use(HttpApi) + .use(LanguageDetector) + .use(initReactI18next) + .init({ + supportedLngs: ["en", "ru"], + fallbackLng: "en", + debug: false, + // Options for language detector + detection: { + order: ["cookie", "navigator"], + caches: ["cookie"], + }, + // react: { useSuspense: false }, + backend: { + loadPath: "/assets/locales/{{lng}}/translation.json", + }, + }); + + + +interface LanguageState { + currentLang: string; +} + +const initialState: LanguageState = { + currentLang: '', +}; + +export const languageSlice = createSlice({ + name: "language", + initialState, + reducers: { + handleChangeLanguage(state, action: PayloadAction) { + i18next.changeLanguage(action.payload) + state.currentLang = action.payload + }, + }, +}); + + +export default languageSlice.reducer; + diff --git a/src/store/reducers/sessionSlice.ts b/src/store/reducers/sessionSlice.ts new file mode 100644 index 0000000..bd98047 --- /dev/null +++ b/src/store/reducers/sessionSlice.ts @@ -0,0 +1,76 @@ +import { IData } from "../../models/IData"; +import { createSlice, PayloadAction } from "@reduxjs/toolkit"; +import { createSession, connectSession } from "./ActionCreator"; + +export interface sessionState { + isLoading: boolean; + error: any; + url: string; + id: string; + playerCount: any +} + +interface ConnectSessionResponseInterface { + websocket_url: string; +} + +interface CreateSessionResponseInterface { + session_id: string; +} + +const initialState: sessionState = { + isLoading: false, + error: null, + url: "", + id: "", + playerCount: [], +}; + +export const sessionSlice = createSlice({ + name: "data", + initialState, + reducers: { + cleanErrors(state) { + state = initialState + }, + + setUserCount(state, action) { + console.log(action.payload) + const newArr = new Array(action.payload).fill('user') + state.playerCount = newArr + } + }, + extraReducers: { + [createSession.fulfilled.type]: ( + state, + action: PayloadAction + ) => { + state.isLoading = false; + state.id = action.payload.session_id; + }, + [createSession.rejected.type]: (state, action: PayloadAction) => { + state.isLoading = false; + state.error = action.payload; + }, + [createSession.pending.type]: (state) => { + state.isLoading = true; + }, + [connectSession.fulfilled.type]: ( + state, + action: PayloadAction + ) => { + state.isLoading = false; + const url = action.payload.websocket_url; + state.url = url; + }, + [connectSession.rejected.type]: (state, action: PayloadAction) => { + state.isLoading = false; + state.error = action.payload; + }, + [connectSession.pending.type]: (state, action) => { + state.isLoading = true; + }, + }, +}); + +export default sessionSlice.reducer; diff --git a/src/store/store.ts b/src/store/store.ts new file mode 100644 index 0000000..e201925 --- /dev/null +++ b/src/store/store.ts @@ -0,0 +1,25 @@ +import { combineReducers, configureStore } from "@reduxjs/toolkit"; + +import cardReducer from "./reducers/cardSlice"; +import sessionReducer from "./reducers/sessionSlice"; +import languageReducer from "./reducers/languageSlice"; + +const rootReducer = combineReducers({ + cardReducer, + sessionReducer, + languageReducer, +}); + +export const setupStore = () => { + return configureStore({ + reducer: rootReducer, + middleware: (getDefaultMiddleware) => + getDefaultMiddleware({ serializableCheck: false }), + }); +}; + +export type RootState = ReturnType; + +export type AppStore = ReturnType; + +export type AppDispatch = AppStore["dispatch"]; diff --git a/src/styles/styles.css b/src/styles/styles.css index dca5fc7..affdf8f 100644 --- a/src/styles/styles.css +++ b/src/styles/styles.css @@ -38,7 +38,7 @@ font-size: 18px; line-height: 140%; margin: 0; - color: #ebebeb; + color: #F2F2F2; } .main-block__subblock-container { @@ -76,7 +76,7 @@ /* Landing/White */ - color: #ebebeb; + color: #F2F2F2; } .main-block__title { @@ -84,7 +84,7 @@ font-weight: 400; font-size: 64px; line-height: 100%; - color: #ebebeb; + color: #F2F2F2; margin: 0; } @@ -92,7 +92,7 @@ font-style: normal; font-weight: 400; line-height: 100%; - color: #ebebeb; + color: #F2F2F2; margin: 0; font-size: 40px; } @@ -133,7 +133,7 @@ font-size: 18px; line-height: 140%; margin: 20px 0 0 0; - color: #ebebeb; + color: #F2F2F2; } .main-block__caption_color { @@ -191,7 +191,7 @@ border: 1px solid #454545; border-radius: 32px; background-color: transparent; - color: #ebebeb; + color: #F2F2F2; cursor: pointer; } .main-block__icon {