diff --git a/index.html b/index.html index 6885e13..4e7f5c4 100644 --- a/index.html +++ b/index.html @@ -2,9 +2,9 @@ - + - Open Fund + OpenFund
diff --git a/package-lock.json b/package-lock.json index d006d92..ce656f9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,6 +30,7 @@ "eslint-plugin-react-refresh": "^0.4.20", "globals": "^16.4.0", "postcss": "^8.5.6", + "sass": "^1.97.2", "tailwindcss": "^3.4.17", "typescript": "~5.8.3", "typescript-eslint": "^8.43.0", @@ -1217,6 +1218,316 @@ "node": ">= 8" } }, + "node_modules/@parcel/watcher": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.1", + "@parcel/watcher-darwin-arm64": "2.5.1", + "@parcel/watcher-darwin-x64": "2.5.1", + "@parcel/watcher-freebsd-x64": "2.5.1", + "@parcel/watcher-linux-arm-glibc": "2.5.1", + "@parcel/watcher-linux-arm-musl": "2.5.1", + "@parcel/watcher-linux-arm64-glibc": "2.5.1", + "@parcel/watcher-linux-arm64-musl": "2.5.1", + "@parcel/watcher-linux-x64-glibc": "2.5.1", + "@parcel/watcher-linux-x64-musl": "2.5.1", + "@parcel/watcher-win32-arm64": "2.5.1", + "@parcel/watcher-win32-ia32": "2.5.1", + "@parcel/watcher-win32-x64": "2.5.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -2682,6 +2993,20 @@ "node": ">=0.4.0" } }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -3433,6 +3758,13 @@ "node": ">= 4" } }, + "node_modules/immutable": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.4.tgz", + "integrity": "sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==", + "dev": true, + "license": "MIT" + }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", @@ -3840,6 +4172,14 @@ "dev": true, "license": "MIT" }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/node-releases": { "version": "2.0.21", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz", @@ -5068,6 +5408,57 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/sass": { + "version": "1.97.2", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.97.2.tgz", + "integrity": "sha512-y5LWb0IlbO4e97Zr7c3mlpabcbBtS+ieiZ9iwDooShpFKWXf62zz5pEPdwrLYm+Bxn1fnbwFGzHuCLSA9tBmrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.0", + "immutable": "^5.0.2", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/sass/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/sass/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/scheduler": { "version": "0.26.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", diff --git a/package.json b/package.json index c8b87db..087459a 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "eslint-plugin-react-refresh": "^0.4.20", "globals": "^16.4.0", "postcss": "^8.5.6", + "sass": "^1.97.2", "tailwindcss": "^3.4.17", "typescript": "~5.8.3", "typescript-eslint": "^8.43.0", diff --git a/public/fonts/NotoSansGeorgian-Black.ttf b/public/fonts/NotoSansGeorgian-Black.ttf new file mode 100644 index 0000000..a058d05 Binary files /dev/null and b/public/fonts/NotoSansGeorgian-Black.ttf differ diff --git a/public/fonts/NotoSansGeorgian-Bold.ttf b/public/fonts/NotoSansGeorgian-Bold.ttf new file mode 100644 index 0000000..7dd4b18 Binary files /dev/null and b/public/fonts/NotoSansGeorgian-Bold.ttf differ diff --git a/public/fonts/NotoSansGeorgian-ExtraBold.ttf b/public/fonts/NotoSansGeorgian-ExtraBold.ttf new file mode 100644 index 0000000..1537cf6 Binary files /dev/null and b/public/fonts/NotoSansGeorgian-ExtraBold.ttf differ diff --git a/public/fonts/NotoSansGeorgian-Medium.ttf b/public/fonts/NotoSansGeorgian-Medium.ttf new file mode 100644 index 0000000..48aaaa1 Binary files /dev/null and b/public/fonts/NotoSansGeorgian-Medium.ttf differ diff --git a/public/fonts/NotoSansGeorgian-Regular.ttf b/public/fonts/NotoSansGeorgian-Regular.ttf new file mode 100644 index 0000000..6951c34 Binary files /dev/null and b/public/fonts/NotoSansGeorgian-Regular.ttf differ diff --git a/public/fonts/NotoSansGeorgian-SemiBold.ttf b/public/fonts/NotoSansGeorgian-SemiBold.ttf new file mode 100644 index 0000000..3ae003f Binary files /dev/null and b/public/fonts/NotoSansGeorgian-SemiBold.ttf differ diff --git a/public/images/character-1.png b/public/images/character-1.png new file mode 100644 index 0000000..ec2bd2c Binary files /dev/null and b/public/images/character-1.png differ diff --git a/public/images/character-2.png b/public/images/character-2.png new file mode 100644 index 0000000..adc9c1b Binary files /dev/null and b/public/images/character-2.png differ diff --git a/public/images/dashboard.png b/public/images/dashboard.png new file mode 100644 index 0000000..74ad9bb Binary files /dev/null and b/public/images/dashboard.png differ diff --git a/public/images/logo-icon.png b/public/images/logo-icon.png new file mode 100644 index 0000000..906f2d0 Binary files /dev/null and b/public/images/logo-icon.png differ diff --git a/public/images/logo.png b/public/images/logo.png new file mode 100644 index 0000000..6e3689c Binary files /dev/null and b/public/images/logo.png differ diff --git a/src/App.tsx b/src/App.tsx index 32fab0c..76fd69b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,6 +1,7 @@ import { BrowserRouter, Routes, Route } from "react-router-dom"; import { Layout } from "./components"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { HomePage } from "./pages"; const queryClient = new QueryClient(); @@ -10,10 +11,7 @@ function App() { - Home} - /> + } /> diff --git a/src/api/axiosConfig.ts b/src/api/axiosConfig.ts index 89f1487..188b5a8 100644 --- a/src/api/axiosConfig.ts +++ b/src/api/axiosConfig.ts @@ -1,5 +1,5 @@ import axios from "axios"; -const apiURL = import.meta.env.VITE_APP_API_URL; +const apiURL = import.meta.env.VITE_APP_API_URL || "http://localhost"; const axiosClient = axios.create({ baseURL: new URL(apiURL).toString(), @@ -16,7 +16,7 @@ axiosClient.interceptors.response.use( localStorage.removeItem("accessToken"); } return Promise.reject(err); - } + }, ); export default axiosClient; diff --git a/src/assets/svgs/YouTubeIcon24.tsx b/src/assets/svgs/YouTubeIcon24.tsx new file mode 100644 index 0000000..83175e1 --- /dev/null +++ b/src/assets/svgs/YouTubeIcon24.tsx @@ -0,0 +1,20 @@ +const YouTubeIcon24: React.FC> = (props) => { + return ( + + + + + ); +}; + +export default YouTubeIcon24; diff --git a/src/assets/svgs/index.ts b/src/assets/svgs/index.ts index 0073b8a..7ae66cb 100644 --- a/src/assets/svgs/index.ts +++ b/src/assets/svgs/index.ts @@ -1 +1,2 @@ export { default as GoogleIcon24 } from "./GoogleIcon24"; +export { default as YouTubeIcon24 } from "./YouTubeIcon24"; diff --git a/src/components/common/layout/header/Header.tsx b/src/components/common/layout/header/Header.tsx index 00889c2..98526d5 100644 --- a/src/components/common/layout/header/Header.tsx +++ b/src/components/common/layout/header/Header.tsx @@ -1,8 +1,10 @@ import { useEffect } from "react"; -import { Link } from "react-router-dom"; import AuthenticatedHeader from "./components/AuthenticatedHeader"; import UnauthenticatedHeader from "./components/UnauthenticatedHeader"; import { useAuthStore } from "@/store"; +import "@/styles/main.scss"; + +const imgLogo = "/images/logo.png"; const Header = () => { const { isAuthenticated, init } = useAuthStore(); @@ -10,34 +12,45 @@ const Header = () => { useEffect(() => { init(); }, []); + return ( - <> -
-
-

- OpenFund -

+
+
+ {/* Logo */} +
+ OpenFund +
-
- - Link - - - Link - - - Link - -
+ {/* Right side */} +
+ {/* Settings Icon */} + + {/* Auth Buttons */} {isAuthenticated ? ( ) : ( )}
-
- +
+
); }; diff --git a/src/components/common/layout/header/components/AuthenticatedHeader.tsx b/src/components/common/layout/header/components/AuthenticatedHeader.tsx index de766c3..7f1bfce 100644 --- a/src/components/common/layout/header/components/AuthenticatedHeader.tsx +++ b/src/components/common/layout/header/components/AuthenticatedHeader.tsx @@ -16,8 +16,8 @@ const AuthenticatedHeader = () => { ], }} > -
-

P

+
+

P

diff --git a/src/components/common/layout/header/components/UnauthenticatedHeader.tsx b/src/components/common/layout/header/components/UnauthenticatedHeader.tsx index 68e1740..5207532 100644 --- a/src/components/common/layout/header/components/UnauthenticatedHeader.tsx +++ b/src/components/common/layout/header/components/UnauthenticatedHeader.tsx @@ -1,44 +1,27 @@ -import { Button, Modal } from "antd"; -import { LoginModalContent, SignUpModalContent } from "./modal-contents"; -import { useState } from "react"; +import { Modal } from "antd"; +import { LoginModalContent } from "./modal-contents"; +import { useLoginModalStore } from "@/store"; const UnauthenticatedHeader = () => { - const [isModalOpen, setIsModalOpen] = useState(false); - const [isLogin, setIsLogin] = useState(false); + const { isOpen, open, close } = useLoginModalStore(); - const handleModalOpen = (login: boolean) => { - setIsModalOpen(true); - setIsLogin(login); - }; - - const handleCancel = () => { - setIsModalOpen(false); - }; return ( <> - {isLogin ? ( - - ) : ( - - )} + -
- - +
+
); diff --git a/src/components/common/layout/header/components/modal-contents/LoginModalContent.tsx b/src/components/common/layout/header/components/modal-contents/LoginModalContent.tsx index 99a3e3e..9e386f8 100644 --- a/src/components/common/layout/header/components/modal-contents/LoginModalContent.tsx +++ b/src/components/common/layout/header/components/modal-contents/LoginModalContent.tsx @@ -1,112 +1,54 @@ -import { Button, Input, message } from "antd"; import type { LoginModalContentT } from "./types"; -import { GoogleIcon24 } from "../../../../../../assets"; -import { useAuthFormStore, useAuthStore } from "@/store"; -import { usePostLoginService } from "@/api"; +import { GoogleIcon24, YouTubeIcon24 } from "../../../../../../assets"; +import { useGetGoogleLinkService } from "@/api"; -const LoginModalContent: React.FC = ({ - setIsLogin, - handleCancel, -}) => { - const { data, setData } = useAuthFormStore(); - const { mutate: loginHandler } = usePostLoginService(); - const { login } = useAuthStore(); +interface LoginModalContentProps extends LoginModalContentT { + onClose?: () => void; +} - const handleClose = () => { - setData({ - email: "", - password: "", - userName: "", - }); - handleCancel(); - }; +const LoginModalContent: React.FC = ({ onClose }) => { + const { data: googleLinkData } = useGetGoogleLinkService(); - const handleChange = (e: React.ChangeEvent) => { - const { name, value } = e.target; - setData({ ...data, [name]: value }); - }; - - const handleLogin = () => { - loginHandler( - { - email: data.email ?? "", - password: data.password ?? "", - }, - { - onSuccess: (res) => { - message.success("Login successful!"); - login(res.data.accessToken); - handleClose(); - }, - onError: (err) => { - const errorRenrerer = () => { - if (err.response?.data?.errors) { - return Object.entries(err.response.data.errors).map( - ([field, messages]) => ( -
- {field}: {messages.join(", ")} -
- ) - ); - } - - return "Login failed. Please try again."; - }; - message.error(errorRenrerer()); - }, - } - ); + const handleGoogleSignIn = () => { + if (googleLinkData?.url) { + window.location.href = googleLinkData.url; + } }; return ( -
-

Login

- -
-
-

Email:

- -
- -
-

Password:

- -
- -
-

setIsLogin(false)} +

+
+

შესვლა

+
- - + + +
-
- - +
+ + +
); diff --git a/src/components/common/layout/header/components/modal-contents/types.ts b/src/components/common/layout/header/components/modal-contents/types.ts index a8f564c..9430e2d 100644 --- a/src/components/common/layout/header/components/modal-contents/types.ts +++ b/src/components/common/layout/header/components/modal-contents/types.ts @@ -1,4 +1,4 @@ export interface LoginModalContentT { - setIsLogin: React.Dispatch>; - handleCancel: () => void; + setIsLogin?: React.Dispatch>; + handleCancel?: () => void; } diff --git a/src/index.css b/src/index.css index bd6213e..39e80d9 100644 --- a/src/index.css +++ b/src/index.css @@ -1,3 +1,122 @@ @tailwind base; @tailwind components; -@tailwind utilities; \ No newline at end of file +@tailwind utilities; + +/* ============================================ + CSS Variables for Theming + ============================================ */ +:root { + /* Brand Colors */ + --color-primary: #b594ff; + --color-primary-dark: #a680fa; + --color-primary-light: #c1aaf7; + + /* Background Colors */ + --color-bg-page: #ffffff; + --color-bg-cream: #f6f5f2; + --color-bg-light: #f9f4f2; + --color-bg-card: #ffffff; + + /* Text Colors */ + --color-text-primary: #202020; + --color-text-secondary: #19171c; + --color-text-muted: #999999; + --color-text-inverse: #ffffff; + + /* Border Colors */ + --color-border: #e5e7eb; + --color-border-light: #f9f4f2; + + /* Layout */ + --container-max-width: 1650px; + --section-padding-y: 50px; + --section-padding-x: 109px; + + /* Typography */ + --font-family-primary: "noto-sans-georgian", sans-serif; + + /* Border Radius */ + --radius-sm: 4px; + --radius-md: 24px; + --radius-lg: 30px; + --radius-full: 9999px; + + /* Transitions */ + --transition-fast: 150ms ease; + --transition-normal: 200ms ease; +} + +/* Dark Mode Preparation */ +[data-theme="dark"] { + --color-bg-page: #0f0f0f; + --color-bg-cream: #1a1a1a; + --color-bg-light: #242424; + --color-bg-card: #1f1f1f; + + --color-text-primary: #f5f5f5; + --color-text-secondary: #e0e0e0; + --color-text-muted: #888888; + + --color-border: #333333; + --color-border-light: #2a2a2a; +} + +@font-face { + font-family: "noto-sans-georgian"; + src: url("/fonts/NotoSansGeorgian-Regular.ttf") format("truetype"); + font-weight: 400; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: "noto-sans-georgian"; + src: url("/fonts/NotoSansGeorgian-Medium.ttf") format("truetype"); + font-weight: 500; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: "noto-sans-georgian"; + src: url("/fonts/NotoSansGeorgian-SemiBold.ttf") format("truetype"); + font-weight: 600; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: "noto-sans-georgian"; + src: url("/fonts/NotoSansGeorgian-Bold.ttf") format("truetype"); + font-weight: 700; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: "noto-sans-georgian"; + src: url("/fonts/NotoSansGeorgian-ExtraBold.ttf") format("truetype"); + font-weight: 800; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: "noto-sans-georgian"; + src: url("/fonts/NotoSansGeorgian-Black.ttf") format("truetype"); + font-weight: 900; + font-style: normal; + font-display: swap; +} + +@layer base { + body { + margin: 0; + padding: 0; + font-family: "noto-sans-georgian", sans-serif; + } + + * { + box-sizing: border-box; + } +} diff --git a/src/main.tsx b/src/main.tsx index cd17bfb..31a504a 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -7,5 +7,5 @@ import "@ant-design/v5-patch-for-react-19"; createRoot(document.getElementById("root")!).render( - + , ); diff --git a/src/pages/HomePage.tsx b/src/pages/HomePage.tsx new file mode 100644 index 0000000..07c09cf --- /dev/null +++ b/src/pages/HomePage.tsx @@ -0,0 +1,325 @@ +import "../styles/main.scss"; +import { useLoginModalStore } from "@/store"; +import { useState } from "react"; + +// Local asset paths +const imgDashboard = "/images/dashboard.png"; +const imgLogoIcon = "/images/logo-icon.png"; +const imgCharacter = "/images/character-1.png"; +const imgCharacter2 = "/images/character-2.png"; + +const faqData = [ + { + id: 1, + question: "რამდენია საკომისიო?", + answer: ( + <> +

+ კრეატორისთვის საკომისიო არის 0%. თანხა ისახება მომენტალურად თქვენს + საბანკო ანგარიშზე. +

+

დონატორისგან ბანკი იღებს მაქსიმუმ 2.5%-ს

+ + ), + }, + { + id: 2, + question: ( + <> + როგორ გამოვიყენო +
+ სტრიმზე? + + ), + answer:

ტექსტი

, + }, + { + id: 3, + question: "რამდენად დაცულია?", + answer:

ტექსტი

, + }, + { + id: 4, + question: "როგორ გავიტანო თანხა?", + answer:

ტექსტი

, + }, + { + id: 5, + question: ( + <> + რით განსხვავდება სხვა +
+ პლატფორმებისგან? + + ), + answer:

ტექსტი

, + }, +]; + +const HomePage = () => { + const { open: openLoginModal } = useLoginModalStore(); + const [activeFaqIds, setActiveFaqIds] = useState([1]); + const [closingFaqIds, setClosingFaqIds] = useState([]); + + const toggleFaq = (id: number) => { + if (activeFaqIds.includes(id)) { + setClosingFaqIds((prev) => [...prev, id]); + setTimeout(() => { + setActiveFaqIds((prev) => prev.filter((faqId) => faqId !== id)); + setClosingFaqIds((prev) => prev.filter((faqId) => faqId !== id)); + }, 200); + } else { + setActiveFaqIds((prev) => [...prev, id]); + } + }; + return ( +
+ {/* Hero Section */} +
+
+ {/* Left Decoration - Character with donation */} +
+ +
+

გიორგი - 10₾

+

კარგი სტრიმია!

+
+
+ + {/* Center Content */} +
+

+ მიიღე დონაციები +
+ საკომისიოს გარეშე +

+ +
+ + +
+
+ + {/* Right Decoration - Gift goal */} +
+ +
+

ახალი კომპიუტერი

+
+
+

3000 / 6000 ₾ (50%)

+
+
+
+
+ + {/* Dashboard Preview */} +
+
+ OpenFund Dashboard +
+
+
+ + {/* FAQ Section */} +
+
+ {/* Left side - Title */} +
+
+ + ხშირად დასმული კითხვები + +
+

+ გაიგეთ როგორ +
+ მუშაობს ჩვენი +
+ პლატფორმა +

+
+ + {/* Right side - FAQ Items */} +
+ {faqData.map((faq) => { + const isActive = activeFaqIds.includes(faq.id); + const isClosing = closingFaqIds.includes(faq.id); + return ( +
+
+ {isActive && ( +
+ )} +
+ +
toggleFaq(faq.id)} + style={{ cursor: "pointer" }} + > +

{faq.question}

+ +
+ + {isActive && ( +
+ {faq.answer} +
+ )} +
+ ); + })} +
+
+
+ + {/* Footer */} + +
+ ); +}; + +export default HomePage; diff --git a/src/pages/index.ts b/src/pages/index.ts index 431473d..6ba9fc8 100644 --- a/src/pages/index.ts +++ b/src/pages/index.ts @@ -1 +1 @@ -// Here will be exported other pages +export { default as HomePage } from './HomePage'; diff --git a/src/store/index.ts b/src/store/index.ts index 96a071a..b2abd19 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -1,2 +1,3 @@ export * from "./authFormStore"; export * from "./useAuthStore"; +export * from "./useLoginModalStore"; diff --git a/src/store/useLoginModalStore.ts b/src/store/useLoginModalStore.ts new file mode 100644 index 0000000..bf4ccc4 --- /dev/null +++ b/src/store/useLoginModalStore.ts @@ -0,0 +1,13 @@ +import { create } from "zustand"; + +interface LoginModalStore { + isOpen: boolean; + open: () => void; + close: () => void; +} + +export const useLoginModalStore = create((set) => ({ + isOpen: false, + open: () => set({ isOpen: true }), + close: () => set({ isOpen: false }), +})); diff --git a/src/styles/_variables.scss b/src/styles/_variables.scss new file mode 100644 index 0000000..8b57ba6 --- /dev/null +++ b/src/styles/_variables.scss @@ -0,0 +1,70 @@ +// Breakpoints +$breakpoint-xl: 1440px; +$breakpoint-lg: 1200px; +$breakpoint-md: 992px; +$breakpoint-sm: 768px; +$breakpoint-xs: 480px; + +// Mixins for responsive design +@mixin desktop-large { + @media (max-width: $breakpoint-xl) { + @content; + } +} + +@mixin desktop { + @media (max-width: $breakpoint-lg) { + @content; + } +} + +@mixin tablet { + @media (max-width: $breakpoint-md) { + @content; + } +} + +@mixin mobile { + @media (max-width: $breakpoint-sm) { + @content; + } +} + +@mixin mobile-small { + @media (max-width: $breakpoint-xs) { + @content; + } +} + +// Container mixin +@mixin container { + width: 100%; + max-width: var(--container-max-width); + margin: 0 auto; + padding-left: var(--section-padding-x); + padding-right: var(--section-padding-x); + + @include tablet { + padding-left: 40px; + padding-right: 40px; + } + + @include mobile { + padding-left: 20px; + padding-right: 20px; + } +} + +// Button base mixin +@mixin button-base { + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: var(--radius-full); + font-weight: 600; + text-transform: uppercase; + cursor: pointer; + transition: all var(--transition-normal); + border: none; + text-decoration: none; +} diff --git a/src/styles/components/_header.scss b/src/styles/components/_header.scss new file mode 100644 index 0000000..fac2556 --- /dev/null +++ b/src/styles/components/_header.scss @@ -0,0 +1,149 @@ +@use "../variables" as *; + +.header { + width: 100%; + background-color: #f6f5f2; + height: 110px; + display: flex; + align-items: center; + padding: 0 48px; + + @include tablet { + padding: 0 32px; + } + + @include mobile { + height: 80px; + padding: 0 20px; + } + + &__container { + width: 100%; + max-width: 1650px; + margin: 0 auto; + display: flex; + align-items: center; + justify-content: space-between; + } + + // Logo section + &__logo-section { + display: flex; + align-items: center; + gap: 12px; + } + + &__logo-img { + width: 185px; + + @include mobile { + width: 130px; + } + } + + &__logo-text { + font-size: 30px; + font-weight: 700; + line-height: 72px; + color: #000; + font-family: "Lota Grotesque Alt 3", sans-serif; + + @include mobile { + font-size: 24px; + line-height: 1.2; + } + } + + // Right side section + &__actions { + display: flex; + align-items: center; + gap: 20px; + + @include mobile { + gap: 16px; + } + } + + &__settings-btn { + width: 52px; + height: 52px; + display: flex; + align-items: center; + justify-content: center; + background: none; + border: none; + cursor: pointer; + padding: 0; + border: 1px solid rgba(0, 0, 0, 0.65); + border-radius: 100px; + + @include mobile { + width: 40px; + height: 40px; + svg { + width: 15px; + } + } + + img { + width: 100%; + height: 100%; + } + } + + // Authenticated user profile + &__profile { + width: 40px; + height: 40px; + border-radius: 50%; + box-shadow: + 0 4px 6px -1px rgba(0, 0, 0, 0.1), + 0 2px 4px -1px rgba(0, 0, 0, 0.06); + display: flex; + justify-content: center; + align-items: center; + border: 2px solid #2563eb; + cursor: pointer; + background: white; + } + + &__profile-letter { + font-size: 16px; + font-weight: 700; + color: #000; + } + + // Unauthenticated buttons + &__auth-buttons { + display: flex; + align-items: center; + gap: 24px; + } + + &__login-btn { + background-color: #19171c; + color: white; + font-weight: 600; + font-size: 18px; + text-transform: uppercase; + padding: 0 36px; + height: 52px; + border-radius: 100px; + border: none; + cursor: pointer; + transition: background-color 0.2s ease; + font-family: "noto-sans-georgian", sans-serif; + font-feature-settings: "case" on; + + &:hover { + background-color: #000; + } + + @include mobile { + font-size: 16px; + height: 44px; + padding: 0 20px; + } + } +} diff --git a/src/styles/components/_login-modal.scss b/src/styles/components/_login-modal.scss new file mode 100644 index 0000000..f386aad --- /dev/null +++ b/src/styles/components/_login-modal.scss @@ -0,0 +1,111 @@ +@use "../variables" as *; + +.login-modal { + &__content { + display: flex; + flex-direction: column; + align-items: center; + padding: 29px 33px 40px; + gap: 51px; + } + + &__header { + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + } + + &__title { + font-family: "Noto Sans Georgian", sans-serif; + font-weight: 600; + font-size: 24px; + line-height: 33px; + font-feature-settings: "case" on; + text-transform: uppercase; + color: #19171c; + margin: 0; + } + + &__close-btn { + display: flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + border: 1px solid #595959; + border-radius: 100px; + background: transparent; + cursor: pointer; + transition: all 0.2s ease; + + &:hover { + background: #f5f5f5; + } + + svg { + width: 12px; + height: 12px; + } + } + + &__buttons { + display: flex; + flex-direction: column; + align-items: center; + gap: 25px; + width: 100%; + max-width: 408px; + } + + &__btn { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + padding: 18px 24px; + gap: 10px; + width: 100%; + height: 64px; + border: 1px solid #818382; + border-radius: 100px; + background: transparent; + cursor: pointer; + transition: all 0.2s ease; + + &:hover { + background: #f5f5f5; + border-color: #595959; + } + + span { + font-family: "Noto Sans Georgian", sans-serif; + font-weight: 400; + font-size: 16px; + line-height: 100%; + letter-spacing: 0.04em; + color: #000000; + } + + svg { + flex-shrink: 0; + } + } +} + +.ant-modal.login-modal-wrapper { + .ant-modal-content { + background: #ffffff; + border-radius: 24px; + padding: 0; + overflow: hidden; + } + + .ant-modal-body { + padding: 0; + } + + .ant-modal-close { + display: none; + } +} diff --git a/src/styles/main.scss b/src/styles/main.scss new file mode 100644 index 0000000..47c67ff --- /dev/null +++ b/src/styles/main.scss @@ -0,0 +1,9 @@ +// Variables & Mixins +@use "./variables" as *; + +// Components +@use "./components/header"; +@use "./components/login-modal"; + +// Pages +@use "./pages/home"; diff --git a/src/styles/pages/_home.scss b/src/styles/pages/_home.scss new file mode 100644 index 0000000..d70917f --- /dev/null +++ b/src/styles/pages/_home.scss @@ -0,0 +1,681 @@ +@use "../variables" as *; + +.home { + width: 100%; + background-color: var(--color-bg-page); + + // ========================================== + // HERO SECTION + // ========================================== + &__hero { + position: relative; + width: 100%; + background: linear-gradient( + 180deg, + var(--color-bg-cream) 11%, + var(--color-bg-cream) 47%, + var(--color-bg-page) 100% + ); + overflow: hidden; + padding-top: 80px; + + @include mobile { + padding-top: 40px; + } + } + + &__hero-container { + @include container; + display: grid; + grid-template-columns: 200px 1fr 260px; + gap: 20px; + align-items: start; + + @include tablet { + grid-template-columns: 1fr; + gap: 40px; + } + } + + // Left decoration (character with donation) + &__hero-decoration { + position: relative; + + &--left { + @include tablet { + display: none; + } + } + + &--right { + display: flex; + flex-direction: column; + align-items: center; + + @include tablet { + display: none; + } + } + } + + &__character-img { + display: block; + width: 188px; + height: 142px; + } + + &__donation-bubble { + margin-top: 20px; + text-align: center; + } + + &__donation-name { + font-weight: 700; + font-size: 19px; + font-feature-settings: "case" on; + color: var(--color-primary-dark); + margin-bottom: 4px; + } + + &__donation-message { + font-size: 14px; + color: var(--color-text-primary); + } + + // Right decoration (gift goal) + &__gift-icon { + width: 82px; + height: 114px; + margin-bottom: 20px; + } + + &__goal-box { + text-align: center; + } + + &__goal-title { + font-weight: 600; + font-size: 15px; + font-feature-settings: "case" on; + color: var(--color-text-primary); + margin-bottom: 8px; + } + + &__goal-progress { + width: 237px; + height: 38px; + background: var(--color-bg-card); + border-radius: var(--radius-full); + position: relative; + overflow: hidden; + } + + &__goal-bar { + position: absolute; + left: 0; + top: 0; + height: 100%; + width: 50%; + background: var(--color-primary); + border-radius: var(--radius-full) 0 0 var(--radius-full); + } + + &__goal-text { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + font-weight: 600; + font-size: 14px; + text-transform: uppercase; + color: var(--color-text-primary); + } + + // Center content + &__hero-content { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + } + + &__hero-title { + font-weight: 900; + font-size: 57px; + line-height: 1.26; + text-transform: uppercase; + color: var(--color-text-primary); + margin: 0 0 30px; + font-feature-settings: "case" on; + + @media (min-width: 1600px) { + font-size: 64px; + } + + @include desktop { + font-size: 48px; + } + + @include tablet { + font-size: 40px; + margin-bottom: 40px; + } + + @include mobile { + font-size: 32px; + margin-bottom: 30px; + } + } + + &__hero-buttons { + display: flex; + align-items: center; + gap: 23px; + + @include mobile { + flex-direction: column; + gap: 16px; + width: 100%; + } + } + + &__btn { + @include button-base; + height: 62px; + padding: 16px 14px; + font-size: 20px; + font-feature-settings: "case" on; + + @include mobile { + width: 100%; + height: 56px; + font-size: 18px; + } + + &--primary { + width: 244px; + background-color: var(--color-primary); + color: var(--color-text-primary); + + @include mobile { + width: 100%; + } + } + + &--secondary { + width: 277px; + background-color: var(--color-bg-card); + border: 1px solid var(--color-border); + color: var(--color-text-primary); + font-weight: 500; + + @include mobile { + width: 100%; + } + } + } + + // Dashboard preview + &__dashboard-preview { + position: relative; + width: 100%; + margin-top: 70px; + padding-bottom: 100px; + + @include tablet { + margin-top: 40px; + padding-bottom: 60px; + } + } + + &__dashboard-img { + position: relative; + display: block; + width: 100%; + margin: 0 auto; + border-radius: var(--radius-lg); + overflow: hidden; + + img { + width: 100%; + height: auto; + display: block; + border-radius: var(--radius-lg); + } + } + + // ========================================== + // FAQ SECTION + // ========================================== + &__faq { + padding: var(--section-padding-y) 0; + + @include tablet { + padding: 20px 0; + } + } + + &__faq-container { + @include container; + display: flex; + justify-content: space-between; + gap: 40px; + + @include tablet { + flex-direction: column; + } + } + + &__faq-header { + max-width: 50%; + @include desktop { + width: 300px; + max-width: 100%; + } + + @include tablet { + width: 100%; + } + } + + &__faq-badge { + display: inline-block; + background-color: var(--color-primary); + padding: 2px 14px; + border-radius: var(--radius-md); + margin-bottom: 15px; + + @include mobile { + margin-bottom: 24px; + } + } + + &__faq-badge-text { + font-weight: 500; + font-size: 16px; + line-height: 26px; + color: var(--color-text-primary); + } + + &__faq-title { + font-weight: 700; + font-size: 50px; + line-height: 1.4; + text-transform: uppercase; + color: var(--color-text-primary); + margin: 0; + font-feature-settings: "case" on; + + @include desktop { + font-size: 40px; + } + + @include tablet { + font-size: 36px; + } + + @include mobile { + font-size: 28px; + } + } + + &__faq-list { + display: flex; + flex-direction: column; + gap: 25px; + width: 50%; + + @include mobile { + gap: 15px; + width: 100%; + } + } + + &__faq-item { + &--active { + .home__faq-question { + color: var(--color-text-primary); + } + } + + &--closing { + .home__faq-question { + color: var(--color-text-muted); + } + } + } + + &__faq-divider { + position: relative; + width: 100%; + height: 4px; + background-color: var(--color-border-light); + border-radius: 2px; + margin-bottom: 25px; + overflow: hidden; + + @include mobile { + margin-bottom: 15px; + } + } + + &__faq-divider-progress { + position: absolute; + top: 0; + left: 0; + height: 100%; + background-color: var(--color-primary); + border-radius: 2px; + animation: faqProgressBar 0.4s ease-out forwards; + + &--closing { + animation: faqProgressBarClose 0.4s ease-out forwards; + } + } + + @keyframes faqProgressBar { + from { + width: 0%; + } + to { + width: 100%; + } + } + + @keyframes faqProgressBarClose { + from { + width: 100%; + } + to { + width: 0%; + } + } + + &__faq-row { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 20px; + padding: 5px 0; + } + + &__faq-question { + font-weight: 600; + font-size: 30px; + line-height: 1.5; + text-transform: uppercase; + color: var(--color-text-muted); + margin: 0; + font-feature-settings: "case" on; + + @include desktop { + font-size: 24px; + } + + @include mobile { + font-size: 20px; + } + } + + &__faq-toggle { + width: 44px; + height: 44px; + padding: 0; + cursor: pointer; + display: flex; + justify-content: center; + align-items: center; + border: 1px solid #9a98a2; + border-radius: 17px; + transition: box-shadow 0.3s ease; + + img { + width: 100%; + height: 100%; + } + + @include mobile { + width: 36px; + height: 36px; + border-radius: 14px; + } + + &--active { + box-shadow: 0.3rem 0.3rem 0.5rem #c6b9ed66; + border: none; + svg { + transform: rotate(180deg); + animation: fadeRotate 0.2s ease-out forwards; + } + } + + &--closing { + svg { + animation: fadeRotateBack 0.2s ease-out forwards; + } + } + } + + @keyframes fadeRotate { + 0% { + opacity: 0; + transform: rotate(0deg); + } + 100% { + opacity: 1; + transform: rotate(180deg); + } + } + + @keyframes fadeRotateBack { + 0% { + opacity: 1; + transform: rotate(180deg); + } + 100% { + opacity: 0; + transform: rotate(0deg); + } + } + + &__faq-answer { + position: relative; + margin-top: 18px; + font-size: 15px; + line-height: 1.57; + color: var(--color-text-primary); + overflow: hidden; + + @include mobile { + margin-top: 10px; + } + + p { + margin: 0 0 1em; + + &:last-child { + margin-bottom: 0; + } + } + + @include mobile { + font-size: 14px; + } + + &::after { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #ffffff; + animation: faqAnswerReveal 0.4s ease-out forwards; + } + + &--closing::after { + animation: faqAnswerHide 0.4s ease-out forwards; + } + } + + @keyframes faqAnswerReveal { + from { + top: 0; + } + to { + top: 100%; + } + } + + @keyframes faqAnswerHide { + from { + top: 100%; + bottom: auto; + height: 100%; + } + to { + top: 0; + bottom: auto; + height: 100%; + } + } + + // ========================================== + // FOOTER SECTION + // ========================================== + &__footer { + background-color: var(--color-primary-light); + padding: 56px 80px 20px; + display: flex; + flex-direction: column; + align-items: center; + margin-top: 100px; + + @include mobile { + padding: 40px 20px; + } + } + + &__footer-card { + width: 100%; + max-width: 1650px; + background-color: var(--color-bg-card); + border-radius: var(--radius-md); + padding: 38px 37px; + + @include tablet { + padding: 30px 24px; + } + + @include mobile { + padding: 24px 20px; + } + } + + &__footer-content { + display: grid; + grid-template-columns: auto 1fr 1fr 1fr; + gap: 40px; + + @include tablet { + grid-template-columns: repeat(2, 1fr); + } + + @include mobile { + grid-template-columns: 1fr; + gap: 32px; + } + } + + &__footer-logo { + width: 39px; + height: 36px; + } + + &__footer-section { + min-width: 150px; + } + + &__footer-heading { + font-weight: 600; + font-size: 20px; + line-height: 1.4; + text-transform: uppercase; + color: var(--color-text-primary); + margin: 0 0 14px; + + @include mobile { + font-size: 18px; + margin-bottom: 10px; + } + } + + &__footer-links { + list-style: none; + padding: 0; + margin: 0; + display: flex; + flex-direction: column; + gap: 8px; + } + + &__footer-link { + font-size: 17px; + line-height: 1.65; + color: var(--color-text-primary); + text-decoration: none; + transition: color var(--transition-fast); + + &:hover { + color: var(--color-primary); + } + + @include mobile { + font-size: 15px; + } + } + + &__footer-social { + display: flex; + align-items: center; + gap: 10px; + } + + &__social-link { + width: 43px; + height: 43px; + border: 1px solid #cdcbd0; + border-radius: 16px; + display: flex; + justify-content: center; + align-items: center; + + &:hover { + opacity: 0.8; + } + + img { + width: 100%; + height: 100%; + } + + @include mobile { + width: 38px; + height: 38px; + } + } + + &__copyright { + margin-top: 20px; + font-weight: 600; + font-size: 16px; + color: var(--color-text-primary); + text-align: center; + + @include mobile { + font-size: 14px; + margin-top: 16px; + } + } +} diff --git a/tailwind.config.js b/tailwind.config.js index 89a305e..8c5422a 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,11 +1,48 @@ /** @type {import('tailwindcss').Config} */ export default { - content: [ - "./index.html", - "./src/**/*.{js,ts,jsx,tsx}", - ], + content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], theme: { - extend: {}, + extend: { + colors: { + primary: { + DEFAULT: "var(--color-primary)", + dark: "var(--color-primary-dark)", + light: "var(--color-primary-light)", + }, + background: { + page: "var(--color-bg-page)", + cream: "var(--color-bg-cream)", + light: "var(--color-bg-light)", + card: "var(--color-bg-card)", + }, + text: { + primary: "var(--color-text-primary)", + secondary: "var(--color-text-secondary)", + muted: "var(--color-text-muted)", + inverse: "var(--color-text-inverse)", + }, + border: { + DEFAULT: "var(--color-border)", + light: "var(--color-border-light)", + }, + }, + fontFamily: { + primary: ["var(--font-family-primary)"], + }, + borderRadius: { + sm: "var(--radius-sm)", + md: "var(--radius-md)", + lg: "var(--radius-lg)", + full: "var(--radius-full)", + }, + transitionDuration: { + fast: "var(--transition-fast)", + normal: "var(--transition-normal)", + }, + maxWidth: { + container: "var(--container-max-width)", + }, + }, }, plugins: [], -} \ No newline at end of file +};