├── .eslintrc.js ├── .firebaserc ├── .github └── workflows │ └── deploy-client.yml ├── .gitignore ├── .prettierrc ├── README.md ├── firebase.json ├── package.json ├── pre-build.js ├── public ├── favicon.ico ├── favicon.png ├── index.html ├── manifest.json └── robots.txt ├── src ├── appConstants │ ├── api.ts │ ├── colors.ts │ └── index.ts ├── assets │ ├── fonts │ │ ├── Poppins-Bold-700.eot │ │ ├── Poppins-Bold-700.otf │ │ ├── Poppins-Bold-700.ttf │ │ ├── Poppins-Bold-700.woff │ │ ├── Poppins-Bold-700.woff2 │ │ ├── Poppins-Medium-500.eot │ │ ├── Poppins-Medium-500.otf │ │ ├── Poppins-Medium-500.ttf │ │ ├── Poppins-Medium-500.woff │ │ ├── Poppins-Medium-500.woff2 │ │ ├── Poppins-Regular-400.eot │ │ ├── Poppins-Regular-400.otf │ │ ├── Poppins-Regular-400.ttf │ │ ├── Poppins-Regular-400.woff │ │ ├── Poppins-Regular-400.woff2 │ │ ├── Poppins-SemiBold-600.eot │ │ ├── Poppins-SemiBold-600.otf │ │ ├── Poppins-SemiBold-600.ttf │ │ ├── Poppins-SemiBold-600.woff │ │ └── Poppins-SemiBold-600.woff2 │ ├── images │ │ ├── editProfileExampleEN.png │ │ ├── editProfileExampleRU.png │ │ ├── myTeamExampleEN.png │ │ ├── myTeamExampleRU.png │ │ ├── teamActionsExampleEN.png │ │ └── teamActionsExampleRU.png │ └── svg │ │ ├── check.svg │ │ ├── chevron-arrow.svg │ │ ├── copy.svg │ │ ├── copy2clip.svg │ │ ├── coursesSelectArrow.svg │ │ ├── cross-small.svg │ │ ├── cross.svg │ │ ├── edit.svg │ │ ├── filterIcon.svg │ │ ├── headerActiveElement.svg │ │ ├── info.svg │ │ ├── loginImage.svg │ │ ├── menuToggle.svg │ │ ├── paginateArrowLeft.svg │ │ ├── paginateArrowRight.svg │ │ ├── plus.svg │ │ ├── rslogo.svg │ │ ├── team-header-background-pattern.svg │ │ ├── team-header-decorations.svg │ │ └── teams-man.svg ├── components │ ├── App │ │ ├── index.tsx │ │ └── styled.ts │ ├── CommonSelectList │ │ ├── index.tsx │ │ └── styled.ts │ ├── CourseField │ │ ├── index.tsx │ │ └── styled.ts │ ├── ErrorBoundary │ │ ├── index.tsx │ │ └── styled.ts │ ├── ErrorModal │ │ └── index.tsx │ ├── FilterForm │ │ ├── filterFormFields.ts │ │ ├── index.tsx │ │ └── styled.ts │ ├── FilterSelect │ │ └── index.tsx │ ├── Footer │ │ ├── components │ │ │ ├── FooterContent │ │ │ │ └── index.tsx │ │ │ └── index.ts │ │ ├── index.tsx │ │ └── styled.ts │ ├── Header │ │ ├── components │ │ │ ├── BurgerMenu │ │ │ │ ├── index.tsx │ │ │ │ └── styled.ts │ │ │ ├── MenuWrapper │ │ │ │ ├── components │ │ │ │ │ ├── CoursesSelect │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── LangSelect │ │ │ │ │ │ └── index.tsx │ │ │ │ │ └── Nav │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── styled.ts │ │ │ │ ├── index.tsx │ │ │ │ └── styled.ts │ │ │ └── index.ts │ │ ├── index.tsx │ │ └── styled.ts │ ├── InputField │ │ ├── index.tsx │ │ └── styled.ts │ ├── Loader │ │ ├── index.tsx │ │ └── styled.ts │ ├── Modal │ │ ├── index.module.css │ │ └── index.tsx │ ├── ModalCreateEditTeam │ │ └── index.tsx │ ├── ModalCreated │ │ └── index.tsx │ ├── ModalEditCourse │ │ └── index.tsx │ ├── ModalExpel │ │ └── index.tsx │ ├── ModalJoin │ │ └── index.tsx │ ├── Pagination │ │ ├── index.tsx │ │ └── style.css │ ├── PrivateRoute │ │ └── index.tsx │ ├── SelectField │ │ ├── index.tsx │ │ └── styled.ts │ ├── TablePopup │ │ ├── index.tsx │ │ └── styled.ts │ ├── TourGuide │ │ ├── index.tsx │ │ ├── style.css │ │ ├── styled.ts │ │ └── tourConfig.tsx │ └── index.ts ├── graphql │ ├── mutations │ │ ├── addUserToTeamMutation.ts │ │ ├── createCourseMutation.ts │ │ ├── createTeamMutation.ts │ │ ├── index.ts │ │ ├── removeUserFromCourseMutation.ts │ │ ├── removeUserFromTeamMutation.ts │ │ ├── sortStudentsMutation.ts │ │ ├── updUserMutation.ts │ │ ├── updateCourseMutation.ts │ │ └── updateTeamMutation.ts │ └── queries │ │ ├── coursesQuery.ts │ │ ├── index.ts │ │ ├── teamsQuery.ts │ │ ├── usersQuery.ts │ │ └── whoAmIQuery.ts ├── hooks │ └── graphql │ │ ├── index.ts │ │ ├── mutations │ │ ├── useAddUserToTeamMutation.ts │ │ ├── useCreateCourseMutation.ts │ │ ├── useCreateTeamMutation.ts │ │ ├── useExpelUserFromTeamMutation.ts │ │ ├── useRemoveUserFromCourseMutation.ts │ │ ├── useRemoveUserFromTeamMutation.ts │ │ ├── useSortStudentsMutation.ts │ │ ├── useUpdUserMutation.ts │ │ ├── useUpdateCourseMutation.ts │ │ └── useUpdateTeamMutation.ts │ │ └── queries │ │ ├── useCoursesQuery.ts │ │ ├── useTeamsQuery.ts │ │ ├── useUsersQuery.ts │ │ └── useWhoAmIQuery.ts ├── index.tsx ├── modules │ ├── AdminPage │ │ ├── components │ │ │ ├── ContentWrapper │ │ │ │ ├── components │ │ │ │ │ ├── AddCourseBlock │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ └── InputsBlock │ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ │ └── styled.ts │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── styled.ts │ │ │ │ │ ├── CoursesList │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ └── Course │ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ │ └── styled.ts │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── styled.ts │ │ │ │ │ └── ShowCourseSelect │ │ │ │ │ │ └── index.tsx │ │ │ │ ├── index.tsx │ │ │ │ └── styled.ts │ │ │ └── index.ts │ │ └── index.tsx │ ├── EditProfile │ │ ├── components │ │ │ └── UserCourseListItem │ │ │ │ ├── index.tsx │ │ │ │ └── styled.ts │ │ ├── formFields.ts │ │ ├── index.tsx │ │ └── styled.ts │ ├── LoginPage │ │ ├── components │ │ │ ├── LoginInfoBlock │ │ │ │ ├── index.tsx │ │ │ │ └── styled.ts │ │ │ └── index.ts │ │ ├── index.tsx │ │ ├── loginPageMiddleware.ts │ │ ├── loginPageReducer.ts │ │ ├── selectors.ts │ │ └── styled.ts │ ├── NotFoundPage │ │ ├── index.tsx │ │ └── styled.ts │ ├── StudentsTable │ │ ├── components │ │ │ └── Dashboard │ │ │ │ ├── components │ │ │ │ ├── TableBody │ │ │ │ │ ├── components │ │ │ │ │ │ └── TableRow │ │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ └── TableItem │ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ │ └── styled.ts │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ └── styled.ts │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── styled.ts │ │ │ │ │ └── styles.css │ │ │ │ ├── TableHead │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── styled.ts │ │ │ │ └── index.ts │ │ │ │ ├── index.tsx │ │ │ │ └── styled.ts │ │ ├── index.tsx │ │ ├── selectors.ts │ │ ├── studentsTableReducer.ts │ │ └── styled.ts │ ├── TeamsList │ │ ├── components │ │ │ ├── TeamListModals │ │ │ │ ├── index.tsx │ │ │ │ └── useCommonMutations.ts │ │ │ ├── Teams │ │ │ │ ├── components │ │ │ │ │ ├── MemberListToggle │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── styled.ts │ │ │ │ │ ├── MyTeam │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ └── MyTeamInfoBlock │ │ │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ │ ├── MyTeamInfoLine │ │ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ │ │ └── styled.tsx │ │ │ │ │ │ │ │ └── NotificationPopup │ │ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ │ │ └── styled.ts │ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ │ └── styled.ts │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── styled.ts │ │ │ │ │ ├── TeamItem │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── styled.tsx │ │ │ │ │ ├── TeamUserTable │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ └── TableRow │ │ │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ │ ├── ExpelButton │ │ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ │ │ └── styled.ts │ │ │ │ │ │ │ │ ├── TableCell │ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── styled.tsx │ │ │ │ │ ├── TeamsHeader │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── styled.ts │ │ │ │ │ └── index.ts │ │ │ │ └── index.tsx │ │ │ └── index.ts │ │ ├── index.tsx │ │ ├── selectors.ts │ │ ├── styled.ts │ │ └── teamsListReducer.ts │ ├── TokenPage │ │ └── index.tsx │ ├── TutorialPage │ │ ├── components │ │ │ ├── NoteBlock │ │ │ │ ├── index.tsx │ │ │ │ └── styled.ts │ │ │ ├── StepBlock │ │ │ │ ├── index.tsx │ │ │ │ └── styled.ts │ │ │ └── index.ts │ │ ├── index.tsx │ │ ├── styled.ts │ │ └── tutorialPageInfo.tsx │ └── index.ts ├── react-app-env.d.ts ├── reportWebVitals.ts ├── setupTests.ts ├── store │ └── index.tsx ├── translation │ ├── en │ │ └── en.json │ ├── resources.ts │ └── ru │ │ └── ru.json ├── types.ts ├── typography │ ├── common.css │ ├── fonts.css │ ├── index.ts │ └── normalize.css └── utils │ └── isFieldValid.ts ├── tsconfig.json └── yarn.lock /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', 3 | extends: [ 4 | 'plugin:react/recommended', 5 | 'plugin:@typescript-eslint/recommended', 6 | 'prettier/@typescript-eslint', 7 | 'plugin:react-hooks/recommended', 8 | 'plugin:prettier/recommended', 9 | ], 10 | plugins: ['@typescript-eslint', 'react', 'prettier', 'react-hooks'], 11 | parserOptions: { 12 | ecmaVersion: 2018, 13 | sourceType: 'module', 14 | ecmaFeatures: { 15 | jsx: true, 16 | }, 17 | }, 18 | 19 | rules: { 20 | 'react-hooks/rules-of-hooks': 'error', 21 | 'react-hooks/exhaustive-deps': 'warn', 22 | 'comma-dangle': ['error', 'only-multiline'], 23 | 'react/prop-types': 'off', 24 | 'react/display-name': 'off', 25 | '@typescript-eslint/explicit-function-return-type': 'off', 26 | 'prettier/prettier': ['error', { endOfLine: 'auto' }], 27 | '@typescript-eslint/interface-name-prefix': 'off', 28 | '@typescript-eslint/ban-ts-ignore': 'off', 29 | '@typescript-eslint/explicit-module-boundary-types': 'off', 30 | '@typescript-eslint/no-empty-function': 'off', 31 | '@typescript-eslint/no-explicit-any': 'off', 32 | '@typescript-eslint/no-var-reqiures': 'off', 33 | }, 34 | 35 | settings: { 36 | react: { 37 | version: 'detect', 38 | }, 39 | }, 40 | globals: { React: 'writable' }, 41 | }; 42 | -------------------------------------------------------------------------------- /.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "default": "rss-teams" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.github/workflows/deploy-client.yml: -------------------------------------------------------------------------------- 1 | name: Build and Deploy 2 | on: 3 | push: 4 | branches: 5 | - master 6 | 7 | jobs: 8 | admin: 9 | name: Deploy PROD 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout Repo 13 | uses: actions/checkout@master 14 | - name: Install Dependencies 15 | run: npm install 16 | - name: Build 17 | run: npm run build 18 | - name: Archive Production Artifact 19 | uses: actions/upload-artifact@master 20 | with: 21 | name: build 22 | path: build 23 | - name: Deploy to Firebase 24 | uses: w9jds/firebase-action@master 25 | with: 26 | args: deploy --only hosting 27 | env: 28 | FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }} 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | /.eslintcache* 8 | 9 | # testing 10 | /coverage 11 | 12 | # production 13 | /build 14 | /.firebase 15 | 16 | # misc 17 | .DS_Store 18 | .env.local 19 | .env.development.local 20 | .env.test.local 21 | .env.production.local 22 | 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | .eslintcache* 27 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "endOfLine": "auto", 4 | "semi": true, 5 | "singleQuote": true, 6 | "tabWidth": 2, 7 | "trailingComma": "es5", 8 | "printWidth": 100 9 | } -------------------------------------------------------------------------------- /firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "build", 4 | "ignore": [ 5 | "firebase.json", 6 | "**/.*", 7 | "**/node_modules/**" 8 | ], 9 | "rewrites": [ 10 | { 11 | "source": "**", 12 | "destination": "/index.html" 13 | } 14 | ] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rss-teams-fe", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@apollo/client": "^3.3.7", 7 | "@testing-library/jest-dom": "^5.11.4", 8 | "@testing-library/react": "^11.1.0", 9 | "@testing-library/user-event": "^12.1.10", 10 | "@types/jest": "^26.0.15", 11 | "@types/node": "^12.0.0", 12 | "@types/react": "^16.9.53", 13 | "@types/react-dom": "^16.9.8", 14 | "@types/react-router-dom": "^5.1.7", 15 | "@types/react-virtualized-auto-sizer": "^1.0.0", 16 | "@types/react-window": "^1.8.2", 17 | "@types/reactour": "^1.18.1", 18 | "@types/redux-actions": "^2.6.1", 19 | "@types/styled-components": "^5.1.7", 20 | "graphql": "^15.5.0", 21 | "i18next": "^19.9.1", 22 | "react": "^17.0.1", 23 | "react-dom": "^17.0.1", 24 | "react-hook-form": "^6.15.1", 25 | "react-i18next": "^11.8.8", 26 | "react-paginate": "^7.0.0", 27 | "react-redux": "^7.2.2", 28 | "react-router-dom": "^5.2.0", 29 | "react-scripts": "4.0.3", 30 | "react-virtualized-auto-sizer": "^1.0.4", 31 | "react-window": "^1.8.6", 32 | "reactour": "^1.18.3", 33 | "redux": "^4.0.5", 34 | "redux-actions": "^2.6.5", 35 | "redux-thunk": "^2.3.0", 36 | "styled-components": "^5.2.1", 37 | "typescript": "4.2.4", 38 | "web-vitals": "^0.2.4" 39 | }, 40 | "scripts": { 41 | "start": "react-scripts start", 42 | "build": "npm run pre-build && react-scripts build", 43 | "test": "react-scripts test", 44 | "eject": "react-scripts eject", 45 | "pre-build": "node pre-build", 46 | "deploy": "firebase deploy", 47 | "lint:eslint": "eslint \"{,!(node_modules)/**/}*.{ts,tsx}\"", 48 | "fix:prettier": "prettier --write \"{,!(node_modules)/**/}*.{ts,tsx}\"", 49 | "fix:eslint": "eslint --fix \"{,!(node_modules)/**/}*.{{ts,tsx}\"" 50 | }, 51 | "eslintConfig": { 52 | "extends": [ 53 | "react-app", 54 | "react-app/jest" 55 | ] 56 | }, 57 | "browserslist": { 58 | "production": [ 59 | ">0.2%", 60 | "not dead", 61 | "not op_mini all" 62 | ], 63 | "development": [ 64 | "last 1 chrome version", 65 | "last 1 firefox version", 66 | "last 1 safari version" 67 | ] 68 | }, 69 | "devDependencies": { 70 | "@types/react-paginate": "^6.2.1", 71 | "@types/react-redux": "^7.1.16", 72 | "@types/redux": "^3.6.0", 73 | "@typescript-eslint/eslint-plugin": "^4.14.1", 74 | "@typescript-eslint/parser": "^4.14.1", 75 | "eslint": "^7.19.0", 76 | "eslint-config-prettier": "^7.2.0", 77 | "eslint-plugin-prettier": "^3.3.1", 78 | "eslint-plugin-react": "^7.22.0", 79 | "eslint-plugin-react-hooks": "^4.2.0", 80 | "prettier": "^2.2.1", 81 | "redux-devtools-extension": "^2.13.8" 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /pre-build.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-var-requires */ 2 | const fs = require('fs'); 3 | require('dotenv').config(); 4 | 5 | const BACKEND_LINK = `export const BACKEND_LINK = 'https://rss-teams.herokuapp.com/graphql'; 6 | `; 7 | 8 | const AUTH_BACKEND_LINK = `export const AUTH_BACKEND_LINK = 'https://rss-teams.herokuapp.com/auth/github/'; 9 | `; 10 | 11 | fs.writeFileSync('./src/appConstants/api.ts', BACKEND_LINK + AUTH_BACKEND_LINK); 12 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes-school/RSS-Teams-FE/87986aad70b2aa2fc7e7ae1b729e7f4963533261/public/favicon.ico -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes-school/RSS-Teams-FE/87986aad70b2aa2fc7e7ae1b729e7f4963533261/public/favicon.png -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | RSS Teams 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "RSS Teams", 3 | "name": "The Rolling Scopes School Teams", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "favicon.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "favicon.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/appConstants/api.ts: -------------------------------------------------------------------------------- 1 | export const BACKEND_LINK = 'https://rss-teams-dev.herokuapp.com/graphql'; 2 | export const AUTH_BACKEND_LINK = 'https://rss-teams-dev.herokuapp.com/auth/github/'; 3 | -------------------------------------------------------------------------------- /src/appConstants/colors.ts: -------------------------------------------------------------------------------- 1 | export const WHITE_COLOR = '#FFFFFF'; 2 | export const BG_COLOR = '#F2F8FD'; 3 | export const MAIN1_COLOR = '#6550F6'; // violet 4 | export const MAIN1_DARK_COLOR = '#5039EF'; // dark violet 5 | export const MAIN2_COLOR = '#FA6678'; // red 6 | export const MAIN2_LIGHT_COLOR = '#FE7888'; // light red 7 | export const LIGHT_TEXT_COLOR = '#7E96C2'; 8 | export const DARK_TEXT_COLOR = '#363D48'; 9 | export const OVERLAY_COLOR = '#363D4866'; 10 | export const DASHBOARD_HEADER_BG_COLOR = '#E1EEFA'; 11 | export const TABLE_SCROLLBAR_BG_COLOR = '#F9F9FD'; 12 | export const TABLE_SCROLLBAR_THUMB_COLOR = '#1E33570D'; 13 | export const TABLE_POPUP_BORDER_COLOR = '#363D4833'; 14 | export const FOOTER_NAMES_COLOR = '#9CA5B5'; 15 | 16 | export const ALERT_COLOR = '#FA6678'; // red 17 | -------------------------------------------------------------------------------- /src/assets/fonts/Poppins-Bold-700.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes-school/RSS-Teams-FE/87986aad70b2aa2fc7e7ae1b729e7f4963533261/src/assets/fonts/Poppins-Bold-700.eot -------------------------------------------------------------------------------- /src/assets/fonts/Poppins-Bold-700.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes-school/RSS-Teams-FE/87986aad70b2aa2fc7e7ae1b729e7f4963533261/src/assets/fonts/Poppins-Bold-700.otf -------------------------------------------------------------------------------- /src/assets/fonts/Poppins-Bold-700.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes-school/RSS-Teams-FE/87986aad70b2aa2fc7e7ae1b729e7f4963533261/src/assets/fonts/Poppins-Bold-700.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Poppins-Bold-700.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes-school/RSS-Teams-FE/87986aad70b2aa2fc7e7ae1b729e7f4963533261/src/assets/fonts/Poppins-Bold-700.woff -------------------------------------------------------------------------------- /src/assets/fonts/Poppins-Bold-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes-school/RSS-Teams-FE/87986aad70b2aa2fc7e7ae1b729e7f4963533261/src/assets/fonts/Poppins-Bold-700.woff2 -------------------------------------------------------------------------------- /src/assets/fonts/Poppins-Medium-500.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes-school/RSS-Teams-FE/87986aad70b2aa2fc7e7ae1b729e7f4963533261/src/assets/fonts/Poppins-Medium-500.eot -------------------------------------------------------------------------------- /src/assets/fonts/Poppins-Medium-500.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes-school/RSS-Teams-FE/87986aad70b2aa2fc7e7ae1b729e7f4963533261/src/assets/fonts/Poppins-Medium-500.otf -------------------------------------------------------------------------------- /src/assets/fonts/Poppins-Medium-500.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes-school/RSS-Teams-FE/87986aad70b2aa2fc7e7ae1b729e7f4963533261/src/assets/fonts/Poppins-Medium-500.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Poppins-Medium-500.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes-school/RSS-Teams-FE/87986aad70b2aa2fc7e7ae1b729e7f4963533261/src/assets/fonts/Poppins-Medium-500.woff -------------------------------------------------------------------------------- /src/assets/fonts/Poppins-Medium-500.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes-school/RSS-Teams-FE/87986aad70b2aa2fc7e7ae1b729e7f4963533261/src/assets/fonts/Poppins-Medium-500.woff2 -------------------------------------------------------------------------------- /src/assets/fonts/Poppins-Regular-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes-school/RSS-Teams-FE/87986aad70b2aa2fc7e7ae1b729e7f4963533261/src/assets/fonts/Poppins-Regular-400.eot -------------------------------------------------------------------------------- /src/assets/fonts/Poppins-Regular-400.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes-school/RSS-Teams-FE/87986aad70b2aa2fc7e7ae1b729e7f4963533261/src/assets/fonts/Poppins-Regular-400.otf -------------------------------------------------------------------------------- /src/assets/fonts/Poppins-Regular-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes-school/RSS-Teams-FE/87986aad70b2aa2fc7e7ae1b729e7f4963533261/src/assets/fonts/Poppins-Regular-400.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Poppins-Regular-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes-school/RSS-Teams-FE/87986aad70b2aa2fc7e7ae1b729e7f4963533261/src/assets/fonts/Poppins-Regular-400.woff -------------------------------------------------------------------------------- /src/assets/fonts/Poppins-Regular-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes-school/RSS-Teams-FE/87986aad70b2aa2fc7e7ae1b729e7f4963533261/src/assets/fonts/Poppins-Regular-400.woff2 -------------------------------------------------------------------------------- /src/assets/fonts/Poppins-SemiBold-600.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes-school/RSS-Teams-FE/87986aad70b2aa2fc7e7ae1b729e7f4963533261/src/assets/fonts/Poppins-SemiBold-600.eot -------------------------------------------------------------------------------- /src/assets/fonts/Poppins-SemiBold-600.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes-school/RSS-Teams-FE/87986aad70b2aa2fc7e7ae1b729e7f4963533261/src/assets/fonts/Poppins-SemiBold-600.otf -------------------------------------------------------------------------------- /src/assets/fonts/Poppins-SemiBold-600.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes-school/RSS-Teams-FE/87986aad70b2aa2fc7e7ae1b729e7f4963533261/src/assets/fonts/Poppins-SemiBold-600.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Poppins-SemiBold-600.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes-school/RSS-Teams-FE/87986aad70b2aa2fc7e7ae1b729e7f4963533261/src/assets/fonts/Poppins-SemiBold-600.woff -------------------------------------------------------------------------------- /src/assets/fonts/Poppins-SemiBold-600.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes-school/RSS-Teams-FE/87986aad70b2aa2fc7e7ae1b729e7f4963533261/src/assets/fonts/Poppins-SemiBold-600.woff2 -------------------------------------------------------------------------------- /src/assets/images/editProfileExampleEN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes-school/RSS-Teams-FE/87986aad70b2aa2fc7e7ae1b729e7f4963533261/src/assets/images/editProfileExampleEN.png -------------------------------------------------------------------------------- /src/assets/images/editProfileExampleRU.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes-school/RSS-Teams-FE/87986aad70b2aa2fc7e7ae1b729e7f4963533261/src/assets/images/editProfileExampleRU.png -------------------------------------------------------------------------------- /src/assets/images/myTeamExampleEN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes-school/RSS-Teams-FE/87986aad70b2aa2fc7e7ae1b729e7f4963533261/src/assets/images/myTeamExampleEN.png -------------------------------------------------------------------------------- /src/assets/images/myTeamExampleRU.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes-school/RSS-Teams-FE/87986aad70b2aa2fc7e7ae1b729e7f4963533261/src/assets/images/myTeamExampleRU.png -------------------------------------------------------------------------------- /src/assets/images/teamActionsExampleEN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes-school/RSS-Teams-FE/87986aad70b2aa2fc7e7ae1b729e7f4963533261/src/assets/images/teamActionsExampleEN.png -------------------------------------------------------------------------------- /src/assets/images/teamActionsExampleRU.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rolling-scopes-school/RSS-Teams-FE/87986aad70b2aa2fc7e7ae1b729e7f4963533261/src/assets/images/teamActionsExampleRU.png -------------------------------------------------------------------------------- /src/assets/svg/check.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/svg/chevron-arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/svg/copy.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/svg/copy2clip.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/svg/coursesSelectArrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/svg/cross-small.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/svg/cross.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/svg/edit.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/svg/filterIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/svg/headerActiveElement.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/svg/info.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/svg/menuToggle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/svg/paginateArrowLeft.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/svg/paginateArrowRight.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/svg/plus.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/svg/team-header-background-pattern.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/assets/svg/team-header-decorations.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/components/App/styled.ts: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const AppStyled = styled.div` 4 | position: relative; 5 | height: 100vh; 6 | min-height: 100vh; 7 | display: flex; 8 | flex-direction: column; 9 | align-items: center; 10 | `; 11 | -------------------------------------------------------------------------------- /src/components/CourseField/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, SelectHTMLAttributes } from 'react'; 2 | import { Label, Select, SelectInner } from 'typography'; 3 | import { FieldWrapper, SelectCourse } from './styled'; 4 | import { ValidationAlert } from '../InputField/styled'; 5 | import { useTranslation } from 'react-i18next'; 6 | 7 | type Course = { 8 | id: string; 9 | name: string; 10 | }; 11 | 12 | interface SelectFieldProps extends SelectHTMLAttributes { 13 | labelText?: string; 14 | placeholder: string; 15 | multi?: boolean; 16 | register: any; 17 | courses: Course[]; 18 | onAdd?: any; 19 | isValid?: boolean; 20 | } 21 | 22 | export const CourseField: FC = ({ 23 | labelText, 24 | placeholder, 25 | register, 26 | courses, 27 | onAdd, 28 | isValid, 29 | ...rest 30 | }) => { 31 | const { t } = useTranslation(); 32 | const courseOptions = courses 33 | ? courses.map((course: Course) => { 34 | return ( 35 | 38 | ); 39 | }) 40 | : null; 41 | return ( 42 | 43 | {labelText && } 44 | 45 | 61 | 62 | {!isValid && {t('You need to choose at least one course')}} 63 | 64 | ); 65 | }; 66 | -------------------------------------------------------------------------------- /src/components/CourseField/styled.ts: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { BG_COLOR, LIGHT_TEXT_COLOR, MAIN1_COLOR, WHITE_COLOR } from 'appConstants/colors'; 3 | import { SVGParamsAdaptive } from 'typography'; 4 | 5 | type TPlusButton = { 6 | active?: boolean; 7 | }; 8 | 9 | export const FieldWrapper = styled.div` 10 | display: flex; 11 | flex-direction: column; 12 | margin-bottom: 20px; 13 | `; 14 | 15 | export const SelectCourse = styled.div` 16 | width: 300px; 17 | margin-bottom: 0; 18 | display: flex; 19 | flex-direction: row; 20 | justify-content: space-between; 21 | @media (max-width: 440px) { 22 | width: 100%; 23 | } 24 | `; 25 | 26 | export const CourseButton = styled.button` 27 | width: 40px; 28 | height: 40px; 29 | margin-left: 10px; 30 | padding: 10px; 31 | outline: none; 32 | border-radius: 10px; 33 | border: none; 34 | cursor: pointer; 35 | 36 | svg { 37 | ${SVGParamsAdaptive}; 38 | } 39 | `; 40 | 41 | export const PlusButton = styled(CourseButton)` 42 | background-color: ${({ active }) => (active ? MAIN1_COLOR : BG_COLOR)}; 43 | 44 | path { 45 | stroke: ${({ active }) => (active ? WHITE_COLOR : LIGHT_TEXT_COLOR)}; 46 | } 47 | `; 48 | 49 | export const CrossButton = styled(CourseButton)` 50 | background: ${BG_COLOR} 51 | url("data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M3.05029 3.05054L12.9499 12.9501' stroke='%237E96C2' stroke-width='2' stroke-linecap='round'/%3E%3Cpath d='M12.9497 3.05029L3.0501 12.9499' stroke='%237E96C2' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E%0A") 52 | no-repeat center center; 53 | `; 54 | 55 | export const PlaceholderOption = styled.option` 56 | display: none; 57 | `; 58 | -------------------------------------------------------------------------------- /src/components/ErrorBoundary/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { BoundryContainer } from './styled'; 3 | 4 | interface ErrorBoundaryState { 5 | error: any; 6 | errorInfo: any; 7 | } 8 | 9 | class ErrorBoundary extends Component { 10 | state = { error: null, errorInfo: null }; 11 | 12 | static getDerivedStateFromError(error: any, errorInfo: any) { 13 | return { error: error, errorInfo: errorInfo }; 14 | } 15 | 16 | componentDidCatch(error: any, errorInfo: any) { 17 | // Catch errors in any components below and re-render with error message 18 | this.setState({ 19 | error: error, 20 | errorInfo: errorInfo, 21 | }); 22 | // You can also log error messages to an error reporting service here 23 | } 24 | 25 | handleReload() { 26 | location.reload(); 27 | } 28 | 29 | render() { 30 | const { children } = this.props; 31 | 32 | if (this.state.errorInfo) { 33 | return ( 34 | 35 | Something went wrong. 36 |
Click on the page to reload it. 37 |
38 | ); 39 | } 40 | 41 | return <>{children}; 42 | } 43 | } 44 | 45 | export default ErrorBoundary; 46 | -------------------------------------------------------------------------------- /src/components/ErrorBoundary/styled.ts: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { PageTitle } from 'typography'; 3 | 4 | export const BoundryContainer = styled(PageTitle)` 5 | width: 100vw; 6 | height: 100vh; 7 | display: flex; 8 | justify-content: center; 9 | align-items: center; 10 | font-size: 30px; 11 | text-align: center; 12 | `; 13 | -------------------------------------------------------------------------------- /src/components/ErrorModal/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | import { Modal } from 'components'; 3 | import { ApolloError } from '@apollo/client'; 4 | import { UNAUTHORIZED_ERROR_MESSAGE } from 'appConstants'; 5 | 6 | type Props = { 7 | title?: string; 8 | text?: string; 9 | text2?: string; 10 | open?: boolean; 11 | cancelText?: string; 12 | isCrossIconVisible?: boolean; 13 | error?: ApolloError; 14 | }; 15 | 16 | export const ErrorModal: FC = ({ 17 | title = 'Something went wrong!', 18 | text = 'Please, try again later.', 19 | text2 = '@besovadevka or @MadaShindeInai', 20 | open = true, 21 | isCrossIconVisible = false, 22 | cancelText = 'Ok', 23 | error, 24 | }) => { 25 | const isUserUnauthorized = !!error?.graphQLErrors.find( 26 | ({ message }) => message === UNAUTHORIZED_ERROR_MESSAGE 27 | ); 28 | 29 | if (isUserUnauthorized) { 30 | return null; 31 | } 32 | 33 | const onClose = () => { 34 | location.reload(); 35 | }; 36 | 37 | return ( 38 | 43 | ); 44 | }; 45 | -------------------------------------------------------------------------------- /src/components/FilterForm/filterFormFields.ts: -------------------------------------------------------------------------------- 1 | import { TFilterForm } from 'types'; 2 | import { InputFieldProps } from '../../components/InputField'; 3 | 4 | export const filterFormFields: InputFieldProps[] = [ 5 | { 6 | name: 'discord', 7 | labelText: 'Discord', 8 | placeholder: 'Enter discord name', 9 | register: { 10 | pattern: { 11 | value: /^[A-Za-z0-9@#-_() ]+$/i, 12 | message: 'This input is letters and digits only.', 13 | }, 14 | maxLength: { 15 | value: 30, 16 | message: 'This input exceed maxLength.', 17 | }, 18 | }, 19 | }, 20 | { 21 | name: 'github', 22 | labelText: 'GitHub', 23 | placeholder: 'Enter github name', 24 | register: { 25 | pattern: { 26 | value: /^[A-Za-z0-9-_ ]+$/i, 27 | message: 'This input is letters and digits only.', 28 | }, 29 | maxLength: { 30 | value: 30, 31 | message: 'This input exceed maxLength.', 32 | }, 33 | }, 34 | }, 35 | { 36 | name: 'location', 37 | labelText: 'Location', 38 | placeholder: 'Enter location', 39 | register: { 40 | pattern: { 41 | value: /^[A-Za-z\- ]+$/i, 42 | message: 'This input is letters only.', 43 | }, 44 | maxLength: { 45 | value: 30, 46 | message: 'This input exceed maxLength.', 47 | }, 48 | }, 49 | }, 50 | { 51 | name: 'courseName', 52 | labelText: 'Course', 53 | placeholder: 'Enter course name', 54 | register: { 55 | pattern: { 56 | value: /^[A-Za-z\- ]+$/i, 57 | message: 'This input is letters only.', 58 | }, 59 | maxLength: { 60 | value: 30, 61 | message: 'This input exceed maxLength.', 62 | }, 63 | }, 64 | }, 65 | ]; 66 | 67 | export const filterSelectFields: [string, [string, string | boolean][], string, string][] = [ 68 | [ 69 | 'Sort by score', 70 | [ 71 | ['Max score', 'DESC'], 72 | ['Min score', 'ASC'], 73 | ], 74 | '100%', 75 | 'sortingOrder', 76 | ], 77 | [ 78 | 'Sort by team', 79 | [ 80 | ['All', false], 81 | ['Without team', true], 82 | ], 83 | '100%', 84 | 'teamFilter', 85 | ], 86 | ]; 87 | 88 | export const defaultFilterData: TFilterForm = { 89 | discord: null, 90 | github: null, 91 | location: null, 92 | courseName: null, 93 | sortingOrder: filterSelectFields[0][1][0][0], 94 | teamFilter: filterSelectFields[1][1][0][0], 95 | }; 96 | -------------------------------------------------------------------------------- /src/components/FilterForm/styled.ts: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { MAIN1_COLOR } from 'appConstants/colors'; 3 | import { EditProfileWrapper } from 'modules/EditProfile/styled'; 4 | import { Button } from 'typography'; 5 | 6 | type TFilerButtonProps = { 7 | bgColor?: string | undefined; 8 | clearBtn?: boolean; 9 | outerBtn?: boolean; 10 | }; 11 | 12 | export const FilterFormBase = styled(EditProfileWrapper)` 13 | z-index: 2; 14 | position: absolute; 15 | top: 25px; 16 | right: 0; 17 | display: flex; 18 | flex-direction: column; 19 | justify-content: space-between; 20 | height: 412px; 21 | @media screen and (max-width: 768px) { 22 | height: auto; 23 | } 24 | @media (max-width: 440px) { 25 | top: 5px; 26 | right: -13px; 27 | width: 320px; 28 | } 29 | `; 30 | 31 | export const FilterButton = styled(Button)` 32 | background-color: ${({ bgColor }) => bgColor ?? 'transparent'}; 33 | color: ${MAIN1_COLOR}; 34 | display: flex; 35 | align-items: center; 36 | gap: ${({ clearBtn = false }) => (clearBtn ? '11px' : '20px')}; 37 | margin-left: ${({ outerBtn = false }) => (outerBtn ? 'auto' : '0')}; 38 | padding: ${({ clearBtn = false }) => (clearBtn ? '13px 20px 13px 0' : '20px 30px')}; 39 | 40 | @media (max-width: 1200px) { 41 | padding: ${({ clearBtn = false }) => (clearBtn ? '12px 16px 12px 0' : '18px 28px')}; 42 | gap: ${({ clearBtn = false }) => (clearBtn ? '10px' : '18px')}; 43 | } 44 | @media (max-width: 992px) { 45 | padding: ${({ clearBtn = false }) => (clearBtn ? '13px 12px 13px 0' : '14px 26px')}; 46 | gap: ${({ clearBtn = false }) => (clearBtn ? '10px' : '17px')}; 47 | } 48 | @media (max-width: 768px) { 49 | padding: ${({ clearBtn = false }) => (clearBtn ? '13px 8px 13px 0' : '8px 26px')}; 50 | gap: ${({ clearBtn = false }) => (clearBtn ? '10px' : '16px')}; 51 | } 52 | @media (max-width: 550px) { 53 | padding: ${({ clearBtn = false }) => (clearBtn ? '11px 5px 11px 0' : '5px 25px')}; 54 | gap: ${({ clearBtn = false }) => (clearBtn ? '8px' : '13px')}; 55 | } 56 | @media (max-width: 440px) { 57 | padding: ${({ clearBtn = false }) => (clearBtn ? '9px 5px 9px 0' : '5px 20px')}; 58 | gap: ${({ clearBtn = false }) => (clearBtn ? '6px' : '10px')}; 59 | } 60 | 61 | img { 62 | filter: invert(100%) sepia() saturate(10000%) hue-rotate(-110deg); 63 | 64 | @media (max-width: 992px) { 65 | width: 15px; 66 | } 67 | @media (max-width: 768px) { 68 | width: 14px; 69 | } 70 | @media (max-width: 550px) { 71 | width: 12px; 72 | } 73 | @media (max-width: 440px) { 74 | width: 10px; 75 | } 76 | } 77 | `; 78 | 79 | export const FilterButtonsWrapper = styled.div` 80 | width: 100%; 81 | display: flex; 82 | align-items: center; 83 | 84 | .SecondButtonForm { 85 | margin-left: auto; 86 | } 87 | `; 88 | -------------------------------------------------------------------------------- /src/components/FilterSelect/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, SelectHTMLAttributes } from 'react'; 2 | import { Label, Select, SelectInner } from 'typography'; 3 | import { FieldWrapper, SelectCourse } from 'components/CourseField/styled'; 4 | import { useTranslation } from 'react-i18next'; 5 | interface SelectFieldProps extends SelectHTMLAttributes { 6 | labelText?: string; 7 | placeholder: string; 8 | register: any; 9 | options: string[]; 10 | currentOption: string; 11 | } 12 | 13 | export const FilterSelect: FC = ({ 14 | labelText, 15 | placeholder, 16 | register, 17 | options, 18 | currentOption, 19 | ...rest 20 | }) => { 21 | const { t } = useTranslation(); 22 | const filterFieldOptions = 23 | options.map((option: string) => { 24 | return ( 25 | 28 | ); 29 | }) ?? null; 30 | return ( 31 | 32 | {labelText && } 33 | 34 | 39 | 40 | 41 | ); 42 | }; 43 | -------------------------------------------------------------------------------- /src/components/Footer/components/FooterContent/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | import { FooterContentBlock, FooterContentWrapper, FooterTitle } from 'components/Footer/styled'; 3 | import { FOOTER_INFO, LINK_TO_DESIGN_BLOCK } from 'appConstants'; 4 | import { useTranslation } from 'react-i18next'; 5 | 6 | export const FooterContent: FC = () => { 7 | const { t } = useTranslation(); 8 | return ( 9 | 10 | {FOOTER_INFO.map((item, index) => { 11 | return ( 12 | 13 | {t(item.title)} 14 |
15 | {item.members.map((item: string) => { 16 | return ( 17 | 24 | {item} 25 | 26 | ); 27 | })} 28 |
29 |
30 | ); 31 | })} 32 |
33 | ); 34 | }; 35 | -------------------------------------------------------------------------------- /src/components/Footer/components/index.ts: -------------------------------------------------------------------------------- 1 | export { FooterContent } from './FooterContent'; 2 | -------------------------------------------------------------------------------- /src/components/Footer/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | import { RSLogo } from 'typography'; 3 | import { StyledFooter, FooterWrapper, FooterContentBlockLogo } from './styled'; 4 | import { Link } from 'react-router-dom'; 5 | import { FooterContent } from './components'; 6 | 7 | export const Footer: FC = () => { 8 | return ( 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | ); 20 | }; 21 | -------------------------------------------------------------------------------- /src/components/Footer/styled.ts: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { DARK_TEXT_COLOR, FOOTER_NAMES_COLOR, WHITE_COLOR } from 'appConstants/colors'; 3 | import { GeneralAdaptiveFont } from 'typography'; 4 | 5 | export const StyledFooter = styled.footer` 6 | position: sticky; 7 | top: 100%; 8 | z-index: 2; 9 | display: flex; 10 | justify-content: center; 11 | align-items: center; 12 | width: 100%; 13 | height: 110px; 14 | padding: 0 4.2%; 15 | background-color: ${DARK_TEXT_COLOR}; 16 | 17 | @media (max-width: 992px) { 18 | height: 100px; 19 | } 20 | @media (max-width: 880px) { 21 | height: 80px; 22 | } 23 | @media (max-width: 768px) { 24 | height: 65px; 25 | } 26 | @media (max-width: 550px) { 27 | height: 60px; 28 | } 29 | @media (max-width: 440px) { 30 | height: 50px; 31 | } 32 | `; 33 | 34 | export const FooterWrapper = styled.div` 35 | display: flex; 36 | justify-content: space-between; 37 | width: 100%; 38 | max-width: 1320px; 39 | height: 100%; 40 | `; 41 | 42 | export const FooterContentWrapper = styled.div` 43 | display: flex; 44 | justify-content: flex-end; 45 | width: 100%; 46 | gap: 5.6%; 47 | 48 | @media (max-width: 880px) { 49 | display: none; 50 | } 51 | `; 52 | 53 | export const FooterContentBlock = styled.div` 54 | display: flex; 55 | flex-direction: column; 56 | justify-content: center; 57 | gap: 13px; 58 | 59 | .contentBlock { 60 | display: flex; 61 | flex-wrap: wrap; 62 | min-height: 24px; 63 | gap: 30px; 64 | 65 | @media (max-width: 992px) { 66 | gap: 20px; 67 | } 68 | 69 | .contentItem { 70 | height: 100%; 71 | margin-bottom: -20px; 72 | font: 400 1rem/24px 'Poppins', sans-serif; 73 | color: ${FOOTER_NAMES_COLOR}; 74 | text-decoration: none; 75 | outline: none; 76 | ${GeneralAdaptiveFont}; 77 | 78 | &:hover { 79 | color: ${WHITE_COLOR}; 80 | } 81 | 82 | @media (max-width: 992px) { 83 | margin-bottom: 15px; 84 | } 85 | } 86 | } 87 | 88 | .contentBlock.designBlock { 89 | width: auto; 90 | } 91 | 92 | .contentItem.designItem { 93 | width: 140px; 94 | } 95 | `; 96 | 97 | export const FooterContentBlockLogo = styled.div` 98 | display: flex; 99 | justify-content: space-between; 100 | align-items: center; 101 | 102 | a { 103 | height: 100%; 104 | display: flex; 105 | align-items: center; 106 | } 107 | `; 108 | 109 | export const FooterTitle = styled.h1` 110 | font: 600 1rem/24px 'Poppins', sans-serif; 111 | color: ${WHITE_COLOR}; 112 | margin: 0; 113 | ${GeneralAdaptiveFont}; 114 | `; 115 | -------------------------------------------------------------------------------- /src/components/Header/components/BurgerMenu/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | import { useDispatch, useSelector } from 'react-redux'; 3 | import { NavLink } from 'react-router-dom'; 4 | import { selectIsBurgerMenuOpen } from 'modules/LoginPage/selectors'; 5 | import { 6 | BurgerMenuWrapper, 7 | BurgerMenuLayout, 8 | CrossButton, 9 | BurgerMenuNavList, 10 | BurgerMenuNavListItem, 11 | BurgerMenuOverlay, 12 | } from './styled'; 13 | import { setBurgerMenuOpen } from 'modules/LoginPage/loginPageReducer'; 14 | import { APP_NAVIGATION_LINKS } from 'appConstants'; 15 | import { useTranslation } from 'react-i18next'; 16 | import { TNavLink } from 'types'; 17 | import { LangSelect } from '../MenuWrapper/components/LangSelect'; 18 | 19 | type BurgerMenuProps = { 20 | newUserCheck: boolean; 21 | navOnClickHandler: (e: React.MouseEvent, path: string) => void; 22 | }; 23 | 24 | export const BurgerMenu: FC = ({ newUserCheck, navOnClickHandler }) => { 25 | const dispatch = useDispatch(); 26 | const { t } = useTranslation(); 27 | const isBurgerMenuOpen = useSelector(selectIsBurgerMenuOpen); 28 | 29 | const onClickMenuToggle = () => { 30 | dispatch(setBurgerMenuOpen(!isBurgerMenuOpen)); 31 | }; 32 | 33 | return ( 34 | 35 | 36 | 37 | 38 | 39 | {Object.values(APP_NAVIGATION_LINKS).map((link: TNavLink, index: number) => { 40 | if (+newUserCheck + +link.isAlwaysVisible) { 41 | return ( 42 | 43 | { 48 | onClickMenuToggle(); 49 | navOnClickHandler(e, Object.keys(APP_NAVIGATION_LINKS)[index]); 50 | }} 51 | > 52 | {t(link.name)} 53 | 54 | 55 | ); 56 | } 57 | })} 58 | 59 | 60 | 61 | 62 | ); 63 | }; 64 | -------------------------------------------------------------------------------- /src/components/Header/components/BurgerMenu/styled.ts: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { DARK_TEXT_COLOR, OVERLAY_COLOR, WHITE_COLOR } from 'appConstants/colors'; 3 | import { ReactComponent as IconClose } from 'assets/svg/cross.svg'; 4 | 5 | type BurgerMenuProps = { 6 | isBurgerMenuOpen: boolean; 7 | }; 8 | 9 | export const BurgerMenuWrapper = styled.div` 10 | position: fixed; 11 | z-index: 3; 12 | top: 0; 13 | display: flex; 14 | width: 100vw; 15 | height: 100vh; 16 | transition: transform 0.6s ease-in-out; 17 | transform: ${({ isBurgerMenuOpen }) => (isBurgerMenuOpen ? 'translateX(0)' : 'translateX(100%)')}; 18 | `; 19 | 20 | export const BurgerMenuOverlay = styled.div` 21 | width: calc(100% - 250px); 22 | height: 100%; 23 | background-color: ${({ isBurgerMenuOpen }) => (isBurgerMenuOpen ? OVERLAY_COLOR : 'none')}; 24 | transition: background-color 0.6s ease-in-out; 25 | 26 | @media (max-width: 440px) { 27 | width: calc(100% - 180px); 28 | } 29 | `; 30 | 31 | export const BurgerMenuLayout = styled.nav` 32 | display: flex; 33 | flex-direction: column; 34 | gap: 40px; 35 | width: 250px; 36 | height: 100%; 37 | padding: 20px 20px 20px 40px; 38 | background: ${WHITE_COLOR}; 39 | 40 | @media (max-width: 440px) { 41 | width: 180px; 42 | padding-left: 20px; 43 | } 44 | `; 45 | 46 | export const CrossButton = styled(IconClose)` 47 | width: 16px; 48 | height: 16px; 49 | align-self: flex-end; 50 | cursor: pointer; 51 | `; 52 | 53 | export const BurgerMenuNavList = styled.ul` 54 | display: flex; 55 | flex-direction: column; 56 | gap: 40px; 57 | margin: 0; 58 | padding-inline-start: 0; 59 | `; 60 | 61 | export const BurgerMenuNavListItem = styled.li` 62 | list-style-type: none; 63 | 64 | a { 65 | text-decoration: none; 66 | color: ${DARK_TEXT_COLOR}; 67 | } 68 | 69 | a.activeNavLink, 70 | a:hover { 71 | font-weight: 700; 72 | } 73 | `; 74 | -------------------------------------------------------------------------------- /src/components/Header/components/MenuWrapper/components/CoursesSelect/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useState } from 'react'; 2 | import { useDispatch, useSelector } from 'react-redux'; 3 | import { selectCurrCourse } from 'modules/LoginPage/selectors'; 4 | import { selectUserData } from 'modules/StudentsTable/selectors'; 5 | import { Course } from 'types'; 6 | import { CommonSelectList } from 'components'; 7 | import { setCourse } from 'modules/LoginPage/loginPageMiddleware'; 8 | 9 | export const CoursesSelect: FC = () => { 10 | const [isCourseSelectOpen, setCourseSelectOpen] = useState(false); 11 | const dispatch = useDispatch(); 12 | const currCourse = useSelector(selectCurrCourse); 13 | const userData = useSelector(selectUserData); 14 | 15 | const userCourses = userData.courses.filter((item) => item.id !== currCourse.id) ?? null; 16 | 17 | const onCourseChange = (course: Course) => { 18 | setCourseSelectOpen(false); 19 | dispatch(setCourse(course)); 20 | }; 21 | 22 | return ( 23 | 31 | ); 32 | }; 33 | -------------------------------------------------------------------------------- /src/components/Header/components/MenuWrapper/components/LangSelect/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useState } from 'react'; 2 | import { useDispatch, useSelector } from 'react-redux'; 3 | import { LANGUAGES } from 'appConstants'; 4 | import { selectCurrLanguage } from 'modules/LoginPage/selectors'; 5 | import i18n from 'translation/resources'; 6 | import { CommonSelectList } from 'components'; 7 | import { setLanguage } from 'modules/LoginPage/loginPageMiddleware'; 8 | 9 | type LangSelectProps = { 10 | menuToggle?: boolean; 11 | customStyle?: boolean; 12 | }; 13 | 14 | export const LangSelect: FC = ({ menuToggle, customStyle }) => { 15 | const [displayLangList, setDisplayLangList] = useState(false); 16 | const dispatch = useDispatch(); 17 | const currentLanguage = useSelector(selectCurrLanguage); 18 | 19 | const onLangChange = (item: string) => { 20 | setDisplayLangList(false); 21 | dispatch(setLanguage(item)); 22 | i18n.changeLanguage(item); 23 | }; 24 | 25 | const languages: string[] = LANGUAGES.filter((lang) => lang !== currentLanguage); 26 | 27 | return ( 28 | 37 | ); 38 | }; 39 | -------------------------------------------------------------------------------- /src/components/Header/components/MenuWrapper/components/Nav/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | import { useTranslation } from 'react-i18next'; 3 | import { APP_NAVIGATION_LINKS } from 'appConstants'; 4 | import { NavLink } from 'react-router-dom'; 5 | import { StyledNav, StyledNavList, StyledNavListItem, StyledHeaderActiveElement } from './styled'; 6 | import { TNavLink } from 'types'; 7 | 8 | type NavProps = { 9 | newUserCheck: boolean; 10 | navOnClickHandler: (e: React.MouseEvent, path: string) => void; 11 | isUserAdmin: boolean; 12 | }; 13 | 14 | export const Nav: FC = ({ newUserCheck, navOnClickHandler, isUserAdmin }) => { 15 | const { t } = useTranslation(); 16 | 17 | return ( 18 | 19 | 20 | {Object.values(APP_NAVIGATION_LINKS).map((link: TNavLink, index: number) => { 21 | const isNavLinkAvailable = !!(+newUserCheck + +link.isAlwaysVisible); 22 | if (isNavLinkAvailable) { 23 | if (link.name === 'Admin' && !isUserAdmin) return null; 24 | return ( 25 | 26 | navOnClickHandler(e, Object.keys(APP_NAVIGATION_LINKS)[index])} 31 | > 32 | {t(link.name)} 33 | 34 | 35 | 36 | ); 37 | } 38 | })} 39 | 40 | 41 | ); 42 | }; 43 | -------------------------------------------------------------------------------- /src/components/Header/components/MenuWrapper/components/Nav/styled.ts: -------------------------------------------------------------------------------- 1 | import { WHITE_COLOR } from 'appConstants/colors'; 2 | import styled, { keyframes } from 'styled-components'; 3 | import { ReactComponent as HeaderActiveElement } from 'assets/svg/headerActiveElement.svg'; 4 | 5 | type TStyledNavListItemProps = { 6 | newUserCheck: boolean; 7 | }; 8 | 9 | const open = keyframes` 10 | from: { height: 0 } 11 | to: { height: 100%} 12 | `; 13 | 14 | export const StyledNav = styled.nav` 15 | display: flex; 16 | justify-content: center; 17 | align-items: center; 18 | align-self: flex-end; 19 | margin-right: 60px; 20 | font: 400 1rem/24px 'Poppins', sans-serif; 21 | `; 22 | 23 | export const StyledNavList = styled.ul` 24 | @media (max-width: 1430px) { 25 | display: none; 26 | } 27 | 28 | display: flex; 29 | justify-content: center; 30 | align-items: center; 31 | margin: 0; 32 | padding-inline-start: 0; 33 | gap: 60px; 34 | `; 35 | 36 | export const StyledHeaderActiveElement = styled(HeaderActiveElement)` 37 | width: 87px; 38 | height: 0; 39 | `; 40 | 41 | export const StyledNavListItem = styled.li` 42 | list-style-type: none; 43 | 44 | a { 45 | display: flex; 46 | flex-direction: column; 47 | justify-content: space-between; 48 | align-items: center; 49 | height: 52px; 50 | color: ${WHITE_COLOR}; 51 | text-decoration: none; 52 | 53 | &:hover { 54 | font-weight: 700; 55 | } 56 | 57 | svg { 58 | height: 0; 59 | margin-bottom: -5px; 60 | transition: all 0.5s ease-in-out; 61 | animation: ${open} 0.5s ease-in-out; 62 | } 63 | } 64 | 65 | a.activeNavLink { 66 | font-weight: 700; 67 | 68 | svg { 69 | height: 22px; 70 | margin-bottom: 0; 71 | } 72 | } 73 | `; 74 | -------------------------------------------------------------------------------- /src/components/Header/components/MenuWrapper/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | import { useDispatch, useSelector } from 'react-redux'; 3 | import { selectUserData } from 'modules/StudentsTable/selectors'; 4 | import { CoursesSelect } from './components/CoursesSelect'; 5 | import { Nav } from './components/Nav'; 6 | import { MenuButton, StyledMenuWrapper } from './styled'; 7 | import { LangSelect } from './components/LangSelect'; 8 | import { setBurgerMenuOpen } from 'modules/LoginPage/loginPageReducer'; 9 | import { selectIsBurgerMenuOpen } from 'modules/LoginPage/selectors'; 10 | 11 | type MenuWrapperProps = { 12 | navOnClickHandler: (e: React.MouseEvent, path: string) => void; 13 | }; 14 | 15 | export const MenuWrapper: FC = ({ navOnClickHandler }) => { 16 | const dispatch = useDispatch(); 17 | const userData = useSelector(selectUserData); 18 | const isBurgerMenuOpen = useSelector(selectIsBurgerMenuOpen); 19 | 20 | const newUserCheck = !!userData?.courses.length; 21 | const isUserAdmin = !!userData?.isAdmin; 22 | 23 | const onClickMenuToggle = () => { 24 | dispatch(setBurgerMenuOpen(!isBurgerMenuOpen)); 25 | }; 26 | 27 | return ( 28 | 29 |