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 (
+
+
+
+
+ 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
+
+ - First element
+ - Second element
+ - Third element
+ - Fourth element
+ - Fifth element
+
+
+ );
+ }
+}
+
+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 (
+
+ );
+ }
+}
+
+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 = () => (
+
+);
+
+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 = () => (
+
+);
+
+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 (
+
+ );
+}
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 (
+
+ );
+}
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 (
-
-
-
-
- -
- Get started by editing{" "}
-
- src/app/page.tsx
-
- .
-
- -
- Save and see your changes instantly.
-
-
-
-
-
-
-
+
);
}
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))
+}