diff --git a/.gitignore b/.gitignore index 2cb58a7..839b242 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,4 @@ coverage/ # Temporary files *.tmp *.temp +docs/ diff --git a/st-app-frontend/components.json b/st-app-frontend/components.json new file mode 100644 index 0000000..421c026 --- /dev/null +++ b/st-app-frontend/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/app/globals.css", + "baseColor": "gray", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} \ No newline at end of file diff --git a/st-app-frontend/package-lock.json b/st-app-frontend/package-lock.json index c7de021..6fcfe1f 100644 --- a/st-app-frontend/package-lock.json +++ b/st-app-frontend/package-lock.json @@ -8,9 +8,14 @@ "name": "st-app-frontend", "version": "0.1.0", "dependencies": { + "@radix-ui/react-slot": "^1.2.3", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "lucide-react": "^0.534.0", "next": "15.3.4", "react": "^19.0.0", - "react-dom": "^19.0.0" + "react-dom": "^19.0.0", + "tailwind-merge": "^3.3.1" }, "devDependencies": { "@eslint/eslintrc": "^3", @@ -21,6 +26,7 @@ "eslint": "^9", "eslint-config-next": "15.3.4", "tailwindcss": "^4", + "tw-animate-css": "^1.3.6", "typescript": "^5" } }, @@ -957,6 +963,39 @@ "node": ">=12.4.0" } }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -1308,7 +1347,7 @@ "version": "19.1.8", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz", "integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "csstype": "^3.0.2" @@ -2315,12 +2354,33 @@ "node": ">=18" } }, + "node_modules/class-variance-authority": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "license": "Apache-2.0", + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } + }, "node_modules/client-only": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", "license": "MIT" }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/color": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", @@ -2392,7 +2452,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/damerau-levenshtein": { @@ -4499,6 +4559,15 @@ "loose-envify": "cli.js" } }, + "node_modules/lucide-react": { + "version": "0.534.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.534.0.tgz", + "integrity": "sha512-4Bz7rujQ/mXHqCwjx09ih/Q9SCizz9CjBV5repw9YSHZZZaop9/Oj0RgCDt6WdEaeAPfbcZ8l2b4jzApStqgNw==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/magic-string": { "version": "0.30.17", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", @@ -5722,6 +5791,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tailwind-merge": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.3.1.tgz", + "integrity": "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, "node_modules/tailwindcss": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.11.tgz", @@ -5847,6 +5926,16 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, + "node_modules/tw-animate-css": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.3.6.tgz", + "integrity": "sha512-9dy0R9UsYEGmgf26L8UcHiLmSFTHa9+D7+dAt/G/sF5dCnPePZbfgDYinc7/UzAM7g/baVrmS6m9yEpU46d+LA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Wombosvideo" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/st-app-frontend/package.json b/st-app-frontend/package.json index 1684b39..52bc8fd 100644 --- a/st-app-frontend/package.json +++ b/st-app-frontend/package.json @@ -9,19 +9,25 @@ "lint": "next lint" }, "dependencies": { + "@radix-ui/react-slot": "^1.2.3", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "lucide-react": "^0.534.0", + "next": "15.3.4", "react": "^19.0.0", "react-dom": "^19.0.0", - "next": "15.3.4" + "tailwind-merge": "^3.3.1" }, "devDependencies": { - "typescript": "^5", + "@eslint/eslintrc": "^3", + "@tailwindcss/postcss": "^4", "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", - "@tailwindcss/postcss": "^4", - "tailwindcss": "^4", "eslint": "^9", "eslint-config-next": "15.3.4", - "@eslint/eslintrc": "^3" + "tailwindcss": "^4", + "tw-animate-css": "^1.3.6", + "typescript": "^5" } } diff --git a/st-app-frontend/public/logo-rsschool3.png b/st-app-frontend/public/logo-rsschool3.png new file mode 100644 index 0000000..a68bb96 Binary files /dev/null and b/st-app-frontend/public/logo-rsschool3.png differ diff --git a/st-app-frontend/public/static/images/expert.png b/st-app-frontend/public/static/images/expert.png new file mode 100644 index 0000000..7a499a2 Binary files /dev/null and b/st-app-frontend/public/static/images/expert.png differ diff --git a/st-app-frontend/public/static/images/logo-rsschool-st.png b/st-app-frontend/public/static/images/logo-rsschool-st.png new file mode 100644 index 0000000..6a7cd81 Binary files /dev/null and b/st-app-frontend/public/static/images/logo-rsschool-st.png differ diff --git a/st-app-frontend/public/static/images/logo-rsschool3.png b/st-app-frontend/public/static/images/logo-rsschool3.png new file mode 100644 index 0000000..a68bb96 Binary files /dev/null and b/st-app-frontend/public/static/images/logo-rsschool3.png differ diff --git a/st-app-frontend/public/static/svg/solidarity-Ukraine.svg b/st-app-frontend/public/static/svg/solidarity-Ukraine.svg new file mode 100644 index 0000000..17189d7 --- /dev/null +++ b/st-app-frontend/public/static/svg/solidarity-Ukraine.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/st-app-frontend/src/app/auth/login/LoginPage.module.css b/st-app-frontend/src/app/auth/login/LoginPage.module.css new file mode 100644 index 0000000..3000e3d --- /dev/null +++ b/st-app-frontend/src/app/auth/login/LoginPage.module.css @@ -0,0 +1,59 @@ +.container { + display: flex; + height: 100vh; + font-family: -apple-system, blinkmacsystemfont, "Segoe UI", "Noto Sans", + Helvetica, Arial, sans-serif; +} + +.left { + background-color: rgb(255, 218, 31); + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 2rem; +} + +.left img { + max-width: 200px; + height: auto; + margin-bottom: 2rem; +} + +.left p { + font-size: 1.2rem; + text-align: center; + color: #000; +} + +.right { + flex: 1; + display: flex; + align-items: center; + justify-content: center; +} + +.loginBox { + text-align: center; +} + +.loginBox h1 { + margin-bottom: 2rem; + font-size: 2rem; +} + +.githubButton { + padding: 1rem 2rem; + background-color: #24292e; + color: #fff; + border: none; + font-size: 1rem; + cursor: pointer; + border-radius: 5px; + transition: background-color 0.3s ease; +} + +.githubButton:hover { + background-color: #000; +} diff --git a/st-app-frontend/src/app/auth/login/page.tsx b/st-app-frontend/src/app/auth/login/page.tsx new file mode 100644 index 0000000..8096acb --- /dev/null +++ b/st-app-frontend/src/app/auth/login/page.tsx @@ -0,0 +1,46 @@ +"use client"; + +import styles from "./LoginPage.module.css"; +import Image from "next/image"; + +const GITHUB_CLIENT_ID = "YOUR_GITHUB_CLIENT_ID"; + +const LoginPage = () => { + const redirectToGitHub = () => { + const githubAuthUrl = "https://github.com/login/oauth/authorize"; + const redirectUri = encodeURIComponent( + "http://localhost:3000/auth/github/callback" + ); + const scope = "read:user user:email"; + window.location.href = `${githubAuthUrl}?client_id=${GITHUB_CLIENT_ID}&redirect_uri=${redirectUri}&scope=${scope}`; + }; + + return ( +
+
+ RS School Logo +

+ Welcome to RS School! Learn, code, and grow with our open education + platform. +

+
+
+
+

Log In

+ +
+
+
+ ); +}; + +export default LoginPage; diff --git a/st-app-frontend/src/app/components/Content/ContentLayout.tsx b/st-app-frontend/src/app/components/Content/ContentLayout.tsx new file mode 100644 index 0000000..7425d2c --- /dev/null +++ b/st-app-frontend/src/app/components/Content/ContentLayout.tsx @@ -0,0 +1,20 @@ +import React from "react"; + +class ContentLayout extends React.Component{ + render() { + return ( +
+

Title

+ +
+ ); + } +} + +export { ContentLayout }; diff --git a/st-app-frontend/src/app/components/Content/index.tsx b/st-app-frontend/src/app/components/Content/index.tsx new file mode 100644 index 0000000..5e7e45b --- /dev/null +++ b/st-app-frontend/src/app/components/Content/index.tsx @@ -0,0 +1 @@ +export { ContentLayout } from "./ContentLayout"; diff --git a/st-app-frontend/src/app/components/Footer/Donation.tsx b/st-app-frontend/src/app/components/Footer/Donation.tsx new file mode 100644 index 0000000..0dcac5c --- /dev/null +++ b/st-app-frontend/src/app/components/Footer/Donation.tsx @@ -0,0 +1,51 @@ +import React from "react"; +import { Button } from "@/components/ui/button"; +import Link from "next/link"; +import { Heart } from "lucide-react"; + +type Props = { + maxDonatorsShown: number; +}; + +function Donation(props: Props) { + const { maxDonatorsShown } = props; + + const widgetUrlPartial = `https://opencollective.com/rsschool/backers.svg?avatarHeight=36&button=false&width=300&limit=${maxDonatorsShown}`; + + return ( + <> +

Thank you for your support!

+

+ Top {maxDonatorsShown} donators: +

+

+ +

+

+ +

+ + ); +} + +export { Donation }; diff --git a/st-app-frontend/src/app/components/Footer/Feedback.tsx b/st-app-frontend/src/app/components/Footer/Feedback.tsx new file mode 100644 index 0000000..10ed410 --- /dev/null +++ b/st-app-frontend/src/app/components/Footer/Feedback.tsx @@ -0,0 +1,51 @@ +import React from "react"; +import { ThumbsUp } from "lucide-react"; +import { Trophy } from "lucide-react"; +import { Heart } from "lucide-react"; + +import { Menu } from "./Menu"; + +const publicRoutes = [ + { + icon: ( + + ), + name: "Say Thank you (Discord >> #gratitude)", + link: `/gratitude`, + newTab: false, + }, + { + icon: ( + + ), + name: "Heroes page", + link: `/heroes`, + newTab: false, + }, + { + icon: ( + + ), + name: "Feedback on RS School", + link: `https://docs.google.com/forms/d/1F4NeS0oBq-CY805aqiPVp6CIrl4_nIYJ7Z_vUcMOFrQ/viewform`, + newTab: true, + }, +]; + +export const Feedback = function () { + return ( + + ); +}; diff --git a/st-app-frontend/src/app/components/Footer/FooterLayout.tsx b/st-app-frontend/src/app/components/Footer/FooterLayout.tsx new file mode 100644 index 0000000..92752ec --- /dev/null +++ b/st-app-frontend/src/app/components/Footer/FooterLayout.tsx @@ -0,0 +1,39 @@ +import * as React from "react"; + +import { Feedback } from "./Feedback"; +import { Help } from "./Help"; +import { SocialNetworks } from "./SocialNetworks"; +import { Donation } from "./Donation"; + +const maxDonatorsShown = 21; + +class FooterLayout extends React.Component { + getYear() { + const date = new Date(); + return date.getFullYear(); + } + + render() { + return ( +
+
+
+ + +
+
+ +
+
+ + + © The Rolling Scopes {this.getYear()} + +
+
+
+ ); + } +} + +export { FooterLayout }; diff --git a/st-app-frontend/src/app/components/Footer/Help.tsx b/st-app-frontend/src/app/components/Footer/Help.tsx new file mode 100644 index 0000000..682f120 --- /dev/null +++ b/st-app-frontend/src/app/components/Footer/Help.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import { Files } from 'lucide-react'; +import { Bug } from 'lucide-react'; +import { Menu } from './Menu'; + +const githubIssuesUrl = 'https://github.com/rolling-scopes/rsschool-app/issues'; +const publicRoutes = [ + { + icon: , + name: 'Docs', + link: 'https://docs.rs.school', + newTab: true, + }, + { + icon: , + name: 'Report a bug', + link: `${githubIssuesUrl}/new?assignees=apalchys&labels=&template=bug-report.md`, + newTab: true, + }, +]; + +export const Help = function () { + return ; +}; diff --git a/st-app-frontend/src/app/components/Footer/Menu.tsx b/st-app-frontend/src/app/components/Footer/Menu.tsx new file mode 100644 index 0000000..071d11e --- /dev/null +++ b/st-app-frontend/src/app/components/Footer/Menu.tsx @@ -0,0 +1,45 @@ +import React from "react"; +import Link from "next/link"; + +type MenuProps = { + title: string; + data: LinkInfo[]; +}; + +type LinkInfo = { + icon: React.ReactNode; + name: string; + link: string; + newTab: boolean; +}; + +class Menu extends React.Component { + render() { + const { title, data } = this.props; + return ( +
+

{title}

+
    + {data.map((linkInfo) => ( +
  • + + {linkInfo.icon} + {linkInfo.name} + +
  • + ))} +
+
+ ); + } +} + +export { Menu }; diff --git a/st-app-frontend/src/app/components/Footer/SocialNetworks.tsx b/st-app-frontend/src/app/components/Footer/SocialNetworks.tsx new file mode 100644 index 0000000..296ba64 --- /dev/null +++ b/st-app-frontend/src/app/components/Footer/SocialNetworks.tsx @@ -0,0 +1,57 @@ +import * as React from 'react'; + +import { DiscordOutlined } from '@/app/components/Icons/DiscordOutlined'; +import { LinkedInOutlined } from '@/app/components/Icons/LinkedInOutlined'; +import Link from 'next/link'; +import { GitHubOutlined } from '@/app/components/Icons/GitHubOutlined'; +import { YouTubeOutlined } from '@/app/components/Icons/YouTubeOutlined'; + +type LinkInfo = { icon: React.ReactNode; name: string; link: string; newTab: boolean }; + +function SocialNetworks() { + return ( +
+ {socialLinks.map((linkInfo: LinkInfo) => ( + + {linkInfo.icon} + {linkInfo.name} + + ))} +
+ ); +} + +export { SocialNetworks }; + +const socialLinks = [ + { + icon: , + name: 'GitHub', + link: `https://github.com/rolling-scopes/rsschool-app`, + newTab: true, + }, + { + icon: , + name: 'YouTube', + link: `https://www.youtube.com/c/rollingscopesschool`, + newTab: true, + }, + { + icon: , + name: 'Discord', + link: `https://discord.gg/PRADsJB`, + newTab: true, + }, + { + icon: , + name: 'LinkedIn', + link: `https://www.linkedin.com/company/the-rolling-scopes-school/`, + newTab: true, + }, +]; diff --git a/st-app-frontend/src/app/components/Footer/index.tsx b/st-app-frontend/src/app/components/Footer/index.tsx new file mode 100644 index 0000000..ae345ec --- /dev/null +++ b/st-app-frontend/src/app/components/Footer/index.tsx @@ -0,0 +1 @@ +export { FooterLayout } from "./FooterLayout"; diff --git a/st-app-frontend/src/app/components/Header.tsx b/st-app-frontend/src/app/components/Header.tsx new file mode 100644 index 0000000..05ebc9c --- /dev/null +++ b/st-app-frontend/src/app/components/Header.tsx @@ -0,0 +1,23 @@ +import Link from "next/link"; +import { SolidarityUkraine } from "./SolidarityUkraine"; +import { ShortTrack } from "./ShortTrack"; + +export function Header() { + return ( +
+ +
+ ); +} diff --git a/st-app-frontend/src/app/components/Icons/DiscordOutlined.tsx b/st-app-frontend/src/app/components/Icons/DiscordOutlined.tsx new file mode 100644 index 0000000..735575d --- /dev/null +++ b/st-app-frontend/src/app/components/Icons/DiscordOutlined.tsx @@ -0,0 +1,31 @@ +import * as React from "react"; + +const svg = () => ( + + + + + +); + +export const DiscordOutlined = ( + props: React.HTMLAttributes & { size?: number | string } +) => { + const { size, style, ...restProps } = props; + const iconStyle = { ...style, fontSize: size }; + + return ( + + {svg()} + + ); +}; diff --git a/st-app-frontend/src/app/components/Icons/GitHubOutlined.tsx b/st-app-frontend/src/app/components/Icons/GitHubOutlined.tsx new file mode 100644 index 0000000..a6672ca --- /dev/null +++ b/st-app-frontend/src/app/components/Icons/GitHubOutlined.tsx @@ -0,0 +1,31 @@ +import * as React from "react"; + +const svg = () => ( + + GitHub + + +); + +export const GitHubOutlined = ( + props: React.HTMLAttributes & { size?: number | string } +) => { + const { size, style, ...restProps } = props; + const iconStyle = { ...style, fontSize: size }; + + return ( + + {svg()} + + ); +}; diff --git a/st-app-frontend/src/app/components/Icons/LinkedInOutlined.tsx b/st-app-frontend/src/app/components/Icons/LinkedInOutlined.tsx new file mode 100644 index 0000000..9bab66b --- /dev/null +++ b/st-app-frontend/src/app/components/Icons/LinkedInOutlined.tsx @@ -0,0 +1,48 @@ +import * as React from "react"; + +const svg = () => ( + + + + + {" "} + {" "} + {" "} + {" "} + {" "} + + +); + +export const LinkedInOutlined = ( + props: React.HTMLAttributes & { size?: number | string } +) => { + const { size, style, ...restProps } = props; + const iconStyle = { ...style, fontSize: size }; + + return ( + + {svg()} + + ); +}; diff --git a/st-app-frontend/src/app/components/Icons/YouTubeOutlined.tsx b/st-app-frontend/src/app/components/Icons/YouTubeOutlined.tsx new file mode 100644 index 0000000..0de2c65 --- /dev/null +++ b/st-app-frontend/src/app/components/Icons/YouTubeOutlined.tsx @@ -0,0 +1,31 @@ +import * as React from "react"; + +const svg = () => ( + + YouTube + + +); + +export const YouTubeOutlined = ( + props: React.HTMLAttributes & { size?: number | string } +) => { + const { size, style, ...restProps } = props; + const iconStyle = { ...style, fontSize: size }; + + return ( + + {svg()} + + ); +}; diff --git a/st-app-frontend/src/app/components/ShortTrack.tsx b/st-app-frontend/src/app/components/ShortTrack.tsx new file mode 100644 index 0000000..96366de --- /dev/null +++ b/st-app-frontend/src/app/components/ShortTrack.tsx @@ -0,0 +1,13 @@ +import Image from "next/image"; + +export function ShortTrack() { + return ( + Short Track + ); +} diff --git a/st-app-frontend/src/app/components/SolidarityUkraine.tsx b/st-app-frontend/src/app/components/SolidarityUkraine.tsx new file mode 100644 index 0000000..0cae2ab --- /dev/null +++ b/st-app-frontend/src/app/components/SolidarityUkraine.tsx @@ -0,0 +1,12 @@ +import Image from "next/image"; + +export function SolidarityUkraine() { + return ( + Stand With Ukraine + ); +} diff --git a/st-app-frontend/src/app/favicon.ico b/st-app-frontend/src/app/favicon.ico index 718d6fe..d7ecb37 100644 Binary files a/st-app-frontend/src/app/favicon.ico and b/st-app-frontend/src/app/favicon.ico differ diff --git a/st-app-frontend/src/app/globals.css b/st-app-frontend/src/app/globals.css index a2dc41e..4fced5d 100644 --- a/st-app-frontend/src/app/globals.css +++ b/st-app-frontend/src/app/globals.css @@ -1,26 +1,122 @@ @import "tailwindcss"; +@import "tw-animate-css"; -:root { - --background: #ffffff; - --foreground: #171717; -} +@custom-variant dark (&:is(.dark *)); @theme inline { --color-background: var(--background); --color-foreground: var(--foreground); --font-sans: var(--font-geist-sans); --font-mono: var(--font-geist-mono); + --color-sidebar-ring: var(--sidebar-ring); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar: var(--sidebar); + --color-chart-5: var(--chart-5); + --color-chart-4: var(--chart-4); + --color-chart-3: var(--chart-3); + --color-chart-2: var(--chart-2); + --color-chart-1: var(--chart-1); + --color-ring: var(--ring); + --color-input: var(--input); + --color-border: var(--border); + --color-destructive: var(--destructive); + --color-accent-foreground: var(--accent-foreground); + --color-accent: var(--accent); + --color-muted-foreground: var(--muted-foreground); + --color-muted: var(--muted); + --color-secondary-foreground: var(--secondary-foreground); + --color-secondary: var(--secondary); + --color-primary-foreground: var(--primary-foreground); + --color-primary: var(--primary); + --color-popover-foreground: var(--popover-foreground); + --color-popover: var(--popover); + --color-card-foreground: var(--card-foreground); + --color-card: var(--card); + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); } -@media (prefers-color-scheme: dark) { - :root { - --background: #0a0a0a; - --foreground: #ededed; - } +:root { + --radius: 0.625rem; + --background: oklch(1 0 0); + --foreground: oklch(0.13 0.028 261.692); + --card: oklch(1 0 0); + --card-foreground: oklch(0.13 0.028 261.692); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.13 0.028 261.692); + --primary: oklch(0.21 0.034 264.665); + --primary-foreground: oklch(0.985 0.002 247.839); + --secondary: oklch(0.967 0.003 264.542); + --secondary-foreground: oklch(0.21 0.034 264.665); + --muted: oklch(0.967 0.003 264.542); + --muted-foreground: oklch(0.551 0.027 264.364); + --accent: oklch(0.967 0.003 264.542); + --accent-foreground: oklch(0.21 0.034 264.665); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.928 0.006 264.531); + --input: oklch(0.928 0.006 264.531); + --ring: oklch(0.707 0.022 261.325); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); + --sidebar: oklch(0.985 0.002 247.839); + --sidebar-foreground: oklch(0.13 0.028 261.692); + --sidebar-primary: oklch(0.21 0.034 264.665); + --sidebar-primary-foreground: oklch(0.985 0.002 247.839); + --sidebar-accent: oklch(0.967 0.003 264.542); + --sidebar-accent-foreground: oklch(0.21 0.034 264.665); + --sidebar-border: oklch(0.928 0.006 264.531); + --sidebar-ring: oklch(0.707 0.022 261.325); } -body { - background: var(--background); - color: var(--foreground); - font-family: Arial, Helvetica, sans-serif; +.dark { + --background: oklch(0.13 0.028 261.692); + --foreground: oklch(0.985 0.002 247.839); + --card: oklch(0.21 0.034 264.665); + --card-foreground: oklch(0.985 0.002 247.839); + --popover: oklch(0.21 0.034 264.665); + --popover-foreground: oklch(0.985 0.002 247.839); + --primary: oklch(0.928 0.006 264.531); + --primary-foreground: oklch(0.21 0.034 264.665); + --secondary: oklch(0.278 0.033 256.848); + --secondary-foreground: oklch(0.985 0.002 247.839); + --muted: oklch(0.278 0.033 256.848); + --muted-foreground: oklch(0.707 0.022 261.325); + --accent: oklch(0.278 0.033 256.848); + --accent-foreground: oklch(0.985 0.002 247.839); + --destructive: oklch(0.704 0.191 22.216); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 15%); + --ring: oklch(0.551 0.027 264.364); + --chart-1: oklch(0.488 0.243 264.376); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.21 0.034 264.665); + --sidebar-foreground: oklch(0.985 0.002 247.839); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0.002 247.839); + --sidebar-accent: oklch(0.278 0.033 256.848); + --sidebar-accent-foreground: oklch(0.985 0.002 247.839); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.551 0.027 264.364); +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; + } } diff --git a/st-app-frontend/src/app/modules/Main/pages/index.tsx b/st-app-frontend/src/app/modules/Main/pages/index.tsx new file mode 100644 index 0000000..a4f6dc7 --- /dev/null +++ b/st-app-frontend/src/app/modules/Main/pages/index.tsx @@ -0,0 +1,18 @@ +"use client"; +import { ContentLayout } from "@/app/components/Content"; +import { FooterLayout } from "@/app/components/Footer"; +import { Header } from "@/app/components/Header"; + +export function MainPage() { + return ( +
+
+
+
+ +
+
+ +
+ ); +} diff --git a/st-app-frontend/src/app/page.tsx b/st-app-frontend/src/app/page.tsx index e68abe6..26fd7f1 100644 --- a/st-app-frontend/src/app/page.tsx +++ b/st-app-frontend/src/app/page.tsx @@ -1,103 +1,7 @@ -import Image from "next/image"; +import { MainPage } from "./modules/Main/pages"; export default function Home() { return ( -
-
- Next.js logo -
    -
  1. - Get started by editing{" "} - - src/app/page.tsx - - . -
  2. -
  3. - Save and see your changes instantly. -
  4. -
- - -
- -
+ ); } diff --git a/st-app-frontend/src/components/ui/button.tsx b/st-app-frontend/src/components/ui/button.tsx new file mode 100644 index 0000000..a2df8dc --- /dev/null +++ b/st-app-frontend/src/components/ui/button.tsx @@ -0,0 +1,59 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const buttonVariants = cva( + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", + { + variants: { + variant: { + default: + "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90", + destructive: + "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", + outline: + "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50", + secondary: + "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80", + ghost: + "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-9 px-4 py-2 has-[>svg]:px-3", + sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5", + lg: "h-10 rounded-md px-6 has-[>svg]:px-4", + icon: "size-9", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +function Button({ + className, + variant, + size, + asChild = false, + ...props +}: React.ComponentProps<"button"> & + VariantProps & { + asChild?: boolean + }) { + const Comp = asChild ? Slot : "button" + + return ( + + ) +} + +export { Button, buttonVariants } diff --git a/st-app-frontend/src/lib/utils.ts b/st-app-frontend/src/lib/utils.ts new file mode 100644 index 0000000..bd0c391 --- /dev/null +++ b/st-app-frontend/src/lib/utils.ts @@ -0,0 +1,6 @@ +import { clsx, type ClassValue } from "clsx" +import { twMerge } from "tailwind-merge" + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)) +}