├── .env.local ├── .eslintignore ├── static ├── robots.txt ├── favicon.ico └── images │ └── img_share.png ├── .vscode └── settings.json ├── rle-api.zip ├── prettier.config.js ├── images ├── clone.png ├── fork.png ├── welcome.png ├── congrats.gif ├── gssoc_logo.jpeg ├── open_source.png ├── pull_request.png └── git_cheat_sheet.jpg ├── src ├── images │ ├── FAQ.png │ ├── icon.png │ ├── menu.png │ ├── signIn.png │ ├── signUp.png │ ├── aboutUs.png │ ├── brandLogo.png │ ├── figuringOut.png │ ├── pathEmpty.png │ ├── LoginPageImg.png │ ├── footsteps │ │ ├── audio.png │ │ ├── blog.png │ │ ├── book.png │ │ ├── course.png │ │ ├── page.png │ │ ├── plan.png │ │ ├── quiz.png │ │ ├── video.png │ │ └── internet.png │ ├── results │ │ ├── upArrow.png │ │ └── downArrow.png │ ├── roadSign.svg │ └── profile.svg ├── styles │ ├── about.module.css │ ├── search.module.css │ ├── footer.module.css │ ├── header.module.css │ ├── layout.module.css │ ├── searchbar.module.css │ ├── loader.css │ ├── resultFootsteps.module.css │ ├── landing.module.css │ ├── global.css │ ├── login.module.css │ ├── menu.module.css │ ├── settings.module.css │ ├── result.module.css │ ├── footsteps.module.css │ ├── add.module.css │ ├── signUp.module.css │ └── user.module.css ├── context │ └── userContext.js ├── components │ ├── SEO │ │ ├── index.js │ │ ├── Twitter.js │ │ ├── Facebook.js │ │ └── SEO.js │ ├── Layout │ │ ├── loader.js │ │ ├── footer.js │ │ ├── menu.js │ │ ├── sideDrawer.js │ │ └── layout.js │ ├── User │ │ ├── follow.js │ │ ├── learningPaths.js │ │ ├── userProgressBar.js │ │ ├── learningPathCard.js │ │ ├── footstepsCard.js │ │ └── index.js │ ├── image.js │ ├── Settings │ │ └── index.js │ ├── Landing │ │ ├── index.js │ │ └── searchbar.js │ ├── Login │ │ ├── skills.js │ │ └── loginFlow.js │ ├── EditPath │ │ ├── index.js │ │ └── editFootstep.js │ ├── Search │ │ ├── resultPathCard.js │ │ ├── searchResult.js │ │ └── resultFootstepCard.js │ └── Create │ │ └── addFootstep.js ├── utils │ └── typography.js ├── pages │ ├── user.js │ ├── profile.js │ ├── add.js │ ├── index.js │ ├── editPath.js │ ├── settings.js │ ├── 404.js │ ├── about.js │ └── search.js ├── apollo │ └── client.js └── firebase │ └── config.js ├── .eslintrc.json ├── .prettierrc ├── gatsby-ssr.js ├── gatsby-browser.js ├── .github ├── workflows │ ├── greetings.yml │ └── foul-language.yml ├── ISSUE_TEMPLATE │ ├── feature_request_template.md │ ├── bug_report_template.md │ └── pull_request_template.md └── FUNDING.yml ├── .gitignore ├── gatsby-config.js ├── package.json ├── CODE_OF_CONDUCT.md ├── Guidelines.md └── README.md /.env.local: -------------------------------------------------------------------------------- 1 | REACT_EDITOR=vscode -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | public 2 | static 3 | .cache 4 | content -------------------------------------------------------------------------------- /static/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true 3 | } -------------------------------------------------------------------------------- /rle-api.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnplus/footsteps-app/HEAD/rle-api.zip -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('@upstatement/prettier-config'); 2 | -------------------------------------------------------------------------------- /images/clone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnplus/footsteps-app/HEAD/images/clone.png -------------------------------------------------------------------------------- /images/fork.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnplus/footsteps-app/HEAD/images/fork.png -------------------------------------------------------------------------------- /images/welcome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnplus/footsteps-app/HEAD/images/welcome.png -------------------------------------------------------------------------------- /src/images/FAQ.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnplus/footsteps-app/HEAD/src/images/FAQ.png -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnplus/footsteps-app/HEAD/static/favicon.ico -------------------------------------------------------------------------------- /images/congrats.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnplus/footsteps-app/HEAD/images/congrats.gif -------------------------------------------------------------------------------- /src/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnplus/footsteps-app/HEAD/src/images/icon.png -------------------------------------------------------------------------------- /src/images/menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnplus/footsteps-app/HEAD/src/images/menu.png -------------------------------------------------------------------------------- /src/images/signIn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnplus/footsteps-app/HEAD/src/images/signIn.png -------------------------------------------------------------------------------- /src/images/signUp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnplus/footsteps-app/HEAD/src/images/signUp.png -------------------------------------------------------------------------------- /images/gssoc_logo.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnplus/footsteps-app/HEAD/images/gssoc_logo.jpeg -------------------------------------------------------------------------------- /images/open_source.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnplus/footsteps-app/HEAD/images/open_source.png -------------------------------------------------------------------------------- /images/pull_request.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnplus/footsteps-app/HEAD/images/pull_request.png -------------------------------------------------------------------------------- /src/images/aboutUs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnplus/footsteps-app/HEAD/src/images/aboutUs.png -------------------------------------------------------------------------------- /images/git_cheat_sheet.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnplus/footsteps-app/HEAD/images/git_cheat_sheet.jpg -------------------------------------------------------------------------------- /src/images/brandLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnplus/footsteps-app/HEAD/src/images/brandLogo.png -------------------------------------------------------------------------------- /src/images/figuringOut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnplus/footsteps-app/HEAD/src/images/figuringOut.png -------------------------------------------------------------------------------- /src/images/pathEmpty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnplus/footsteps-app/HEAD/src/images/pathEmpty.png -------------------------------------------------------------------------------- /src/images/LoginPageImg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnplus/footsteps-app/HEAD/src/images/LoginPageImg.png -------------------------------------------------------------------------------- /static/images/img_share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnplus/footsteps-app/HEAD/static/images/img_share.png -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "gatsby-standard", 3 | "rules": { 4 | "camelcase": "off" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/images/footsteps/audio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnplus/footsteps-app/HEAD/src/images/footsteps/audio.png -------------------------------------------------------------------------------- /src/images/footsteps/blog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnplus/footsteps-app/HEAD/src/images/footsteps/blog.png -------------------------------------------------------------------------------- /src/images/footsteps/book.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnplus/footsteps-app/HEAD/src/images/footsteps/book.png -------------------------------------------------------------------------------- /src/images/footsteps/course.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnplus/footsteps-app/HEAD/src/images/footsteps/course.png -------------------------------------------------------------------------------- /src/images/footsteps/page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnplus/footsteps-app/HEAD/src/images/footsteps/page.png -------------------------------------------------------------------------------- /src/images/footsteps/plan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnplus/footsteps-app/HEAD/src/images/footsteps/plan.png -------------------------------------------------------------------------------- /src/images/footsteps/quiz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnplus/footsteps-app/HEAD/src/images/footsteps/quiz.png -------------------------------------------------------------------------------- /src/images/footsteps/video.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnplus/footsteps-app/HEAD/src/images/footsteps/video.png -------------------------------------------------------------------------------- /src/images/results/upArrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnplus/footsteps-app/HEAD/src/images/results/upArrow.png -------------------------------------------------------------------------------- /src/images/footsteps/internet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnplus/footsteps-app/HEAD/src/images/footsteps/internet.png -------------------------------------------------------------------------------- /src/images/results/downArrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnplus/footsteps-app/HEAD/src/images/results/downArrow.png -------------------------------------------------------------------------------- /src/styles/about.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | width: 85%; 3 | display: block; 4 | margin-left: auto; 5 | margin-right: auto; 6 | } 7 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "endOfLine": "lf", 3 | "semi": false, 4 | "singleQuote": false, 5 | "tabWidth": 2, 6 | "trailingComma": "es5" 7 | } 8 | -------------------------------------------------------------------------------- /src/context/userContext.js: -------------------------------------------------------------------------------- 1 | import { createContext } from "react" 2 | 3 | const UserContext = createContext() 4 | 5 | export default UserContext 6 | -------------------------------------------------------------------------------- /src/styles/search.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | width: 85%; 3 | display: block; 4 | margin-left: auto; 5 | margin-right: auto; 6 | margin-top: 30px; 7 | } 8 | -------------------------------------------------------------------------------- /src/components/SEO/index.js: -------------------------------------------------------------------------------- 1 | import SEO from "./SEO" 2 | import Facebook from "./Facebook" 3 | import Twitter from "./Twitter" 4 | 5 | export { Facebook, Twitter } 6 | 7 | export default SEO 8 | -------------------------------------------------------------------------------- /src/utils/typography.js: -------------------------------------------------------------------------------- 1 | import Typography from "typography" 2 | import grandViewTheme from "typography-theme-grand-view" 3 | const typography = new Typography(grandViewTheme) 4 | export default typography 5 | -------------------------------------------------------------------------------- /gatsby-ssr.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { ApolloProvider } from "react-apollo" 3 | import { client } from "./src/apollo/client" 4 | 5 | export const wrapRootElement = ({ element }) => ( 6 | {element} 7 | ) 8 | -------------------------------------------------------------------------------- /src/pages/user.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { Router } from "@reach/router" 3 | 4 | import PublicProfile from "../components/publicProfile" 5 | 6 | const User = () => ( 7 | 8 | 9 | 10 | ) 11 | 12 | export default User 13 | -------------------------------------------------------------------------------- /gatsby-browser.js: -------------------------------------------------------------------------------- 1 | import "./src/styles/global.css" 2 | import React from "react" 3 | import { ApolloProvider } from "react-apollo" 4 | import { client } from "./src/apollo/client" 5 | 6 | export const wrapRootElement = ({ element }) => ( 7 | {element} 8 | ) 9 | -------------------------------------------------------------------------------- /src/apollo/client.js: -------------------------------------------------------------------------------- 1 | import ApolloClient from "apollo-boost" 2 | import fetch from "isomorphic-fetch" 3 | 4 | export const client = new ApolloClient({ 5 | uri: process.env.GATSBY_HASURA_GRAPHQL_URL, 6 | headers: { 7 | "x-hasura-admin-secret": process.env.GATSBY_HASURA_GRAPHQL_ADMIN_SECRET, 8 | }, 9 | fetch, 10 | }) 11 | -------------------------------------------------------------------------------- /src/pages/profile.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react" 2 | import Layout from "../components/Layout/layout" 3 | import User from "../components/User" 4 | 5 | export class profile extends Component { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ) 12 | } 13 | } 14 | 15 | export default profile 16 | -------------------------------------------------------------------------------- /src/pages/add.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react" 2 | 3 | import Layout from "../components/Layout/layout" 4 | import AddPath from "../components/Create/addPath" 5 | 6 | export class add extends Component { 7 | render() { 8 | return ( 9 | 10 | 11 | 12 | ) 13 | } 14 | } 15 | 16 | export default add 17 | -------------------------------------------------------------------------------- /src/pages/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react" 2 | 3 | import Layout from "../components/Layout/layout" 4 | import Landing from "../components/Landing" 5 | 6 | export class index extends Component { 7 | render() { 8 | return ( 9 | 10 | 11 | 12 | ) 13 | } 14 | } 15 | 16 | export default index 17 | -------------------------------------------------------------------------------- /src/pages/editPath.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react" 2 | import { Router } from "@reach/router" 3 | 4 | import EditPath from "../components/EditPath" 5 | 6 | export class editPath extends Component { 7 | render() { 8 | return ( 9 | 10 | 11 | 12 | ) 13 | } 14 | } 15 | 16 | export default editPath 17 | -------------------------------------------------------------------------------- /src/components/Layout/loader.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react" 2 | 3 | import "../../styles/loader.css" 4 | 5 | export class loader extends Component { 6 | render() { 7 | return ( 8 |
9 |
10 |
11 |
12 |
13 |
14 | ) 15 | } 16 | } 17 | 18 | export default loader 19 | -------------------------------------------------------------------------------- /src/pages/settings.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react" 2 | 3 | import Layout from "../components/Layout/layout" 4 | import Settings from "../components/Settings" 5 | 6 | export class settings extends Component { 7 | render() { 8 | return ( 9 |
10 | 11 | 12 | 13 |
14 | ) 15 | } 16 | } 17 | 18 | export default settings 19 | -------------------------------------------------------------------------------- /.github/workflows/greetings.yml: -------------------------------------------------------------------------------- 1 | name: Greetings 2 | 3 | on: [pull_request, issues] 4 | 5 | jobs: 6 | greeting: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/first-interaction@v1 10 | with: 11 | repo-token: ${{ secrets.GITHUB_TOKEN }} 12 | issue-message: 'Thanks for contributing to this project' 13 | pr-message: 'Thanks for your input. Please give us some time to review. We will be in touch soon.' 14 | -------------------------------------------------------------------------------- /src/firebase/config.js: -------------------------------------------------------------------------------- 1 | var firebaseConfig = { 2 | apiKey: process.env.GATSBY_FIREBASE_API_KEY, 3 | authDomain: process.env.GATSBY_FIREBASE_AUTH_DOMAIN, 4 | databaseURL: process.env.GATSBY_FIREBASE_DATABASE_URL, 5 | projectId: process.env.GATSBY_FIREBASE_PROJECT_ID, 6 | storageBucket: process.env.GATSBY_FIREBASE_STORAGE_BUCKET, 7 | messagingSenderId: process.env.GATSBY_FIREBASE_MESSAGING_SENDER_ID, 8 | appId: process.env.GATSBY_FIREBASE_APP_ID, 9 | } 10 | 11 | export default firebaseConfig 12 | -------------------------------------------------------------------------------- /.github/workflows/foul-language.yml: -------------------------------------------------------------------------------- 1 | name: Mind your language 2 | on: 3 | issues: 4 | types: 5 | - opened 6 | - edited 7 | issue_comment: 8 | types: 9 | - created 10 | - edited 11 | pull_request_review_comment: 12 | types: 13 | - created 14 | - edited 15 | jobs: 16 | echo_issue_comment: 17 | runs-on: ubuntu-latest 18 | name: profanity check 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@v2 22 | - name: Profanity check step 23 | uses: tailaiw/mind-your-language-action@v1.0.3 24 | env: 25 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request_template.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. -------------------------------------------------------------------------------- /src/styles/footer.module.css: -------------------------------------------------------------------------------- 1 | footer { 2 | height: 40px; 3 | box-sizing: border-box; 4 | } 5 | 6 | .container { 7 | margin: 0 90px; 8 | } 9 | 10 | .link { 11 | color: rgb(0, 0, 0, 0.65); 12 | font-weight: 700; 13 | } 14 | 15 | .link:hover { 16 | color: #029843; 17 | } 18 | 19 | .footer_center { 20 | text-align: center; 21 | } 22 | 23 | .footer_right { 24 | text-align: end; 25 | } 26 | 27 | @media only screen and (max-width: 768px) { 28 | .container { 29 | margin: 0 50px; 30 | } 31 | 32 | footer { 33 | text-align: center; 34 | height: 52px; 35 | } 36 | 37 | .footer_center { 38 | text-align: center; 39 | } 40 | 41 | .footer_right { 42 | text-align: center; 43 | margin: 10px 0; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: ['paypal.me/abhiuniyal'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /src/pages/404.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { Link } from "gatsby" 3 | 4 | const NotFound = () => ( 5 |
14 |

15 | Hmmmm...

16 | Looks like you
have stumbled on
an off beaten path 17 | 21 | Go back to track 22 | 23 |

24 |
25 | ) 26 | 27 | export default NotFound 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report_template.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Additional context** 32 | Add any other context about the problem here. -------------------------------------------------------------------------------- /src/components/SEO/Twitter.js: -------------------------------------------------------------------------------- 1 | import PropTypes from "prop-types" 2 | import React from "react" 3 | import Helmet from "react-helmet" 4 | 5 | const Twitter = ({ type, username, title, desc, image }) => ( 6 | 7 | {username && } 8 | 9 | 10 | 11 | 12 | 13 | 14 | ) 15 | 16 | Twitter.propTypes = { 17 | type: PropTypes.string, 18 | username: PropTypes.string, 19 | title: PropTypes.string.isRequired, 20 | desc: PropTypes.string.isRequired, 21 | image: PropTypes.string.isRequired, 22 | } 23 | 24 | Twitter.defaultProps = { 25 | type: "summary_large_image", 26 | username: "@fnplusofficial", 27 | } 28 | 29 | export default Twitter 30 | -------------------------------------------------------------------------------- /src/styles/header.module.css: -------------------------------------------------------------------------------- 1 | .navContainer { 2 | width: 85%; 3 | display: block; 4 | margin-left: auto; 5 | margin-right: auto; 6 | padding-top: 30px; 7 | } 8 | 9 | .logo img { 10 | width: 60px; 11 | } 12 | 13 | .logo img:hover { 14 | cursor: pointer; 15 | } 16 | 17 | .mobile_toggle { 18 | display: none; 19 | float: right; 20 | border: none; 21 | font-size: 25px; 22 | margin-top: 3px; 23 | padding-right: 5px; 24 | } 25 | 26 | .mobile_toggle img { 27 | width: 25px; 28 | margin-top: 10px; 29 | } 30 | 31 | @media only screen and (max-width: 768px) { 32 | .navDesktop { 33 | display: none; 34 | } 35 | 36 | .mobile_toggle { 37 | display: inline-block; 38 | } 39 | 40 | .navItem { 41 | text-align: center; 42 | font-family: "Montserrat"; 43 | font-weight: 600; 44 | font-size: 18px; 45 | margin-bottom: 20px; 46 | margin-top: 10px; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/components/User/follow.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { Col } from "antd" 3 | 4 | import styles from "../../styles/user.module.css" 5 | import { Link } from "gatsby" 6 | 7 | export default ({ users, type }) => { 8 | if (users.length === 0) 9 | return ( 10 |
11 |

Nothing to show here

12 |
13 | ) 14 | let users_info_list = users.map((user) => 15 | type ? user.follower_info : user.follow_info 16 | ) 17 | return ( 18 |
19 | {users_info_list.map((user) => ( 20 | 21 | 22 | 23 |

24 | {user.first_name + " " + user.last_name} 25 |

26 | 27 | 28 | ))} 29 |
30 | ) 31 | } 32 | -------------------------------------------------------------------------------- /src/styles/layout.module.css: -------------------------------------------------------------------------------- 1 | .content { 2 | min-height: 100vh; 3 | margin-bottom: -40px; 4 | padding-bottom: 40px; 5 | } 6 | 7 | /* Public Profile */ 8 | 9 | .public_not_found { 10 | text-align: center; 11 | font-size: 40px; 12 | font-weight: 600; 13 | margin: 100px 50px; 14 | } 15 | 16 | .public_not_found_username { 17 | color: #029843; 18 | } 19 | 20 | .public_not_found_go_home { 21 | display: inline-block; 22 | text-align: center; 23 | background: #029843; 24 | font-size: 25px; 25 | color: white; 26 | font-weight: 600; 27 | padding: 5px 20px; 28 | border-radius: 40px; 29 | font-family: "Montserrat", sans-serif; 30 | transition: transform 0.2s; 31 | } 32 | 33 | .public_not_found_go_home:hover { 34 | cursor: pointer; 35 | transform: scale(1.1); 36 | } 37 | 38 | @media only screen and (max-width: 768px) { 39 | .public_not_found { 40 | margin: 80px 20px; 41 | } 42 | 43 | .content { 44 | margin-bottom: -62px; 45 | padding-bottom: 62px; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/components/image.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import Img from "gatsby-image" 3 | import { StaticQuery, graphql } from "gatsby" 4 | 5 | const Image = function (props) { 6 | return ( 7 | { 26 | const image = data.images.edges.find( 27 | (image) => image.node.relativePath === props.src 28 | ) 29 | return ( 30 | 34 | ) 35 | }} 36 | /> 37 | ) 38 | } 39 | 40 | export default Image 41 | -------------------------------------------------------------------------------- /src/pages/about.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react" 2 | import { Row, Col } from "antd" 3 | 4 | import Layout from "../components/Layout/layout" 5 | import styles from "../styles/about.module.css" 6 | 7 | export class about extends Component { 8 | render() { 9 | return ( 10 | 11 |
12 | 13 | 14 |

About Footsteps

15 |

16 | Every expert was a beginner once. They tried and tested things 17 | and didn't give up. You can learn a lot from their learnings. 18 | Join footsteps to learn from experts and not make the same 19 | mistakes as they did. 20 |

21 | 22 | 23 | Customised learning paths 27 | 28 |
29 |
30 |
31 | ) 32 | } 33 | } 34 | 35 | export default about 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Issue that this pull request solves 2 | 3 | Closes: # (issue number) 4 | 5 | ## Proposed changes 6 | 7 | Brief description of what is fixed or changed 8 | 9 | ## Types of changes 10 | 11 | _Put an `x` in the boxes that apply_ 12 | 13 | - [ ] Bugfix (non-breaking change which fixes an issue) 14 | - [ ] New feature (non-breaking change which adds functionality) 15 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 16 | - [ ] Documentation update (Documentation content changed) 17 | - [ ] Other (please describe): 18 | 19 | ## Checklist 20 | 21 | _Put an `x` in the boxes that apply_ 22 | 23 | - [ ] My code follows the style guidelines of this project 24 | - [ ] I have performed a self-review of my own code 25 | - [ ] I have commented my code, particularly in hard-to-understand areas 26 | - [ ] I have made corresponding changes to the documentation 27 | - [ ] My changes generate no new warnings 28 | 29 | ## Screenshots 30 | 31 | Please attach the screenshots of the changes made in case of change in user interface 32 | 33 | ## Other information 34 | 35 | Any other information that is important to this pull request -------------------------------------------------------------------------------- /src/styles/searchbar.module.css: -------------------------------------------------------------------------------- 1 | input[type="search"] { 2 | border: none; 3 | text-align: center; 4 | flex: 1 1 auto; 5 | margin: 0 20px; 6 | font-size: 20px; 7 | } 8 | 9 | input[type="search"]:focus { 10 | outline: none; 11 | } 12 | 13 | .searchBtn { 14 | border: none; 15 | color: black; 16 | margin: 5px; 17 | padding: 0 4px; 18 | background-color: #029843; 19 | color: white; 20 | border-radius: 25px; 21 | width: 150px; 22 | text-align: center; 23 | font-size: 24px; 24 | font-weight: 600; 25 | } 26 | 27 | .searchBtn:focus { 28 | outline: none; 29 | } 30 | 31 | .searchBtn:hover { 32 | cursor: pointer; 33 | } 34 | 35 | .container { 36 | display: flex; 37 | flex-direction: row; 38 | justify-content: space-between; 39 | border: 2px solid #000; 40 | width: 100%; 41 | border-radius: 25px; 42 | font-family: "Montserrat", serif; 43 | } 44 | 45 | @media only screen and (max-width: 768px) { 46 | input[type="search"] { 47 | font-size: 16px; 48 | margin: 0 0 0 20px; 49 | } 50 | 51 | .searchBtn { 52 | font-size: 16px; 53 | } 54 | } 55 | 56 | .errorText { 57 | color: #fe5e44; 58 | text-align: center; 59 | margin-top: 20px; 60 | font-family: "Montserrat", sans-serif; 61 | font-weight: 700; 62 | } 63 | -------------------------------------------------------------------------------- /src/components/SEO/Facebook.js: -------------------------------------------------------------------------------- 1 | import PropTypes from "prop-types" 2 | import React from "react" 3 | import Helmet from "react-helmet" 4 | 5 | const Facebook = ({ url, name, type, title, desc, image, locale, appId }) => ( 6 | 7 | {name && } 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | ) 18 | 19 | Facebook.propTypes = { 20 | url: PropTypes.string.isRequired, 21 | locale: PropTypes.string.isRequired, 22 | type: PropTypes.string, 23 | title: PropTypes.string.isRequired, 24 | desc: PropTypes.string.isRequired, 25 | image: PropTypes.string.isRequired, 26 | name: PropTypes.string, 27 | appId: PropTypes.string, 28 | } 29 | 30 | Facebook.defaultProps = { 31 | type: `website`, 32 | name: `fnplusofficial`, 33 | image: `/images/img_share.png`, 34 | appId: `218750542018813`, 35 | } 36 | 37 | export default Facebook 38 | -------------------------------------------------------------------------------- /src/styles/loader.css: -------------------------------------------------------------------------------- 1 | .lds-ellipsis { 2 | display: inline-block; 3 | position: relative; 4 | width: 64px; 5 | height: 64px; 6 | left: 50%; 7 | transform: translateX(-50%); 8 | margin-top: 15%; 9 | } 10 | .lds-ellipsis div { 11 | position: absolute; 12 | top: 27px; 13 | width: 11px; 14 | height: 11px; 15 | border-radius: 50%; 16 | background: #000; 17 | animation-timing-function: cubic-bezier(0, 1, 1, 0); 18 | } 19 | .lds-ellipsis div:nth-child(1) { 20 | left: 6px; 21 | animation: lds-ellipsis1 0.6s infinite; 22 | } 23 | .lds-ellipsis div:nth-child(2) { 24 | left: 6px; 25 | animation: lds-ellipsis2 0.6s infinite; 26 | } 27 | .lds-ellipsis div:nth-child(3) { 28 | left: 26px; 29 | animation: lds-ellipsis2 0.6s infinite; 30 | } 31 | .lds-ellipsis div:nth-child(4) { 32 | left: 45px; 33 | animation: lds-ellipsis3 0.6s infinite; 34 | } 35 | @keyframes lds-ellipsis1 { 36 | 0% { 37 | transform: scale(0); 38 | } 39 | 100% { 40 | transform: scale(1); 41 | } 42 | } 43 | @keyframes lds-ellipsis3 { 44 | 0% { 45 | transform: scale(1); 46 | } 47 | 100% { 48 | transform: scale(0); 49 | } 50 | } 51 | @keyframes lds-ellipsis2 { 52 | 0% { 53 | transform: translate(0, 0); 54 | } 55 | 100% { 56 | transform: translate(19px, 0); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # dotenv environment variables file 55 | .env 56 | 57 | # gatsby files 58 | .cache/ 59 | public 60 | 61 | # Mac files 62 | .DS_Store 63 | 64 | # Yarn 65 | yarn-error.log 66 | .pnp/ 67 | .pnp.js 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # Environment Variables 72 | .env.development 73 | .env.production 74 | 75 | # VS Code 76 | .vs/ -------------------------------------------------------------------------------- /src/components/Settings/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react" 2 | import gql from "graphql-tag" 3 | import { client } from "../../apollo/client" 4 | import Loader from "../Layout/loader" 5 | import Settings from "./settings" 6 | 7 | export default function () { 8 | const [state, setState] = useState({ 9 | user_id: "", 10 | loading: true, 11 | data: {}, 12 | }) 13 | 14 | useEffect(() => { 15 | if (typeof window !== "undefined") { 16 | const user_id = localStorage.getItem("userId") 17 | client 18 | .query({ 19 | query: GET_USER_DETAILS_QUERY_APOLLO, 20 | variables: { 21 | id: user_id, 22 | }, 23 | }) 24 | .then((res) => { 25 | const data = res.data.Users[0] 26 | console.log("received data:", data) 27 | setState({ 28 | user_id, 29 | loading: false, 30 | data, 31 | }) 32 | }) 33 | } 34 | }, []) 35 | 36 | return <>{state.loading ? : } 37 | } 38 | 39 | export const GET_USER_DETAILS_QUERY_APOLLO = gql` 40 | query get_user_details($id: uuid!) { 41 | Users(where: { id: { _eq: $id } }) { 42 | id 43 | about 44 | bio 45 | email 46 | facebook 47 | first_name 48 | github 49 | last_name 50 | linkedin 51 | twitter 52 | profile_pic 53 | skills 54 | username 55 | } 56 | } 57 | ` 58 | -------------------------------------------------------------------------------- /src/components/User/learningPaths.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react" 2 | 3 | import styles from "../../styles/footsteps.module.css" 4 | 5 | import LearningPathCard from "./learningPathCard" 6 | 7 | export class learningPaths extends Component { 8 | state = { 9 | show_edit: false, 10 | } 11 | 12 | componentDidMount() { 13 | if (typeof window !== "undefined") { 14 | if (localStorage.getItem("userId") === this.props.user_id) { 15 | this.setState((oldState) => ({ 16 | show_edit: !oldState.show_edit, 17 | })) 18 | } 19 | } 20 | } 21 | 22 | render() { 23 | const data = this.props.learning_paths 24 | 25 | return ( 26 |
27 |

28 | Learning Paths ({data.length}) 29 |

30 | 31 | {data.length === 0 ? ( 32 |
33 | Path Empty 34 |

35 | No Learning Paths found.
Create one now! 36 |

37 |
38 | ) : ( 39 | "" 40 | )} 41 | 42 | {data.map((path, index) => ( 43 | 48 | ))} 49 |
50 | ) 51 | } 52 | } 53 | 54 | export default learningPaths 55 | -------------------------------------------------------------------------------- /src/components/Landing/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react" 2 | import { Row, Col } from "antd" 3 | import Searchbar from "./searchbar" 4 | import styles from "../../styles/landing.module.css" 5 | 6 | export class index extends Component { 7 | render() { 8 | return ( 9 |
10 | {/* First page */} 11 |
12 | 13 | 14 |

15 | Struggling with learning a new concept?
16 | Does the plethora of resources overwhelm you? 17 |
18 | Are you scratching your head, wondering how some else did it? 19 |
20 |
21 |

22 | Well, fret no more! 23 |

24 |

25 |

Welcome to Footsteps

26 |
27 |
28 | 29 |
30 |
31 | Road Sign 36 | 37 | 38 | Road Sign 39 | 40 |
41 |
42 |
43 | ) 44 | } 45 | } 46 | 47 | export default index 48 | -------------------------------------------------------------------------------- /src/styles/resultFootsteps.module.css: -------------------------------------------------------------------------------- 1 | .cardContainer { 2 | width: 250px; 3 | height: 380px; 4 | margin: 80px 20px 50px 20px; 5 | border-radius: 40px; 6 | box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.3); 7 | } 8 | 9 | .icon { 10 | width: 80px; 11 | height: 80px; 12 | display: block; 13 | margin-left: auto; 14 | margin-right: auto; 15 | margin-top: -40px; 16 | margin-bottom: 10px; 17 | border-radius: 50%; 18 | box-shadow: 0px 3px 5px rgba(0, 0, 0, 0.3); 19 | } 20 | 21 | .title { 22 | font-size: 25px; 23 | font-weight: 700; 24 | text-align: center; 25 | } 26 | 27 | .detailsContainer { 28 | padding: 0 10px; 29 | font-family: "Montserrat", sans-serif; 30 | height: 180px; 31 | } 32 | 33 | .description { 34 | padding: 20px 10px 10px 10px; 35 | text-align: justify; 36 | font-size: 15px; 37 | } 38 | 39 | .typeContainer { 40 | height: 30px; 41 | padding: 0 30px; 42 | } 43 | 44 | .typeImage { 45 | width: 30px; 46 | height: 30px; 47 | margin-bottom: 0; 48 | } 49 | 50 | .typeTitle { 51 | line-height: 30px; 52 | text-align: right; 53 | font-family: "Montserrat", sans-serif; 54 | font-size: 16px; 55 | font-weight: 700; 56 | } 57 | 58 | .levelContainer { 59 | margin-top: 20px; 60 | padding: 0 30px; 61 | font-family: "Montserrat", sans-serif; 62 | } 63 | 64 | .levelTitle { 65 | font-size: 16px; 66 | font-weight: 700; 67 | } 68 | 69 | .levelType { 70 | text-align: right; 71 | font-size: 16px; 72 | } 73 | 74 | .resourceLink { 75 | padding: 0 30px; 76 | margin-top: 30px; 77 | } 78 | 79 | .resourceLink:hover { 80 | cursor: pointer; 81 | } 82 | 83 | .linkArrow { 84 | text-align: right; 85 | } 86 | -------------------------------------------------------------------------------- /src/components/Landing/searchbar.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react" 2 | import searchbarStyles from "../../styles/searchbar.module.css" 3 | import { navigate } from "gatsby" 4 | 5 | export class searchbar extends Component { 6 | state = { 7 | query: "", 8 | error: false, 9 | } 10 | 11 | updateQuery = (e) => { 12 | this.setState({ 13 | query: e.target.value, 14 | error: false, 15 | }) 16 | } 17 | 18 | pressEnter = (e) => { 19 | if (e.key === "Enter") { 20 | this.performSearch() 21 | } 22 | } 23 | 24 | performSearch() { 25 | if (this.state.query.length === 0) { 26 | this.setState({ 27 | error: true, 28 | }) 29 | } else { 30 | navigate(`/search?q=${this.state.query}`) 31 | } 32 | } 33 | 34 | render() { 35 | return ( 36 |
44 |
45 | 53 |
57 | Search 58 |
59 |
60 |

64 | Please Enter a Query 65 |

66 |
67 | ) 68 | } 69 | } 70 | 71 | export default searchbar 72 | -------------------------------------------------------------------------------- /src/styles/landing.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | width: 85%; 3 | display: block; 4 | margin: 1% auto 10%; 5 | } 6 | 7 | .imageContainer img { 8 | width: 80%; 9 | display: block; 10 | margin-left: auto; 11 | margin-right: auto; 12 | } 13 | 14 | .heading { 15 | font-size: 40px; 16 | text-align: center; 17 | margin-bottom: 10px; 18 | font-weight: 600; 19 | color: #099268; 20 | text-shadow: 2px 4px 3px rgba(0, 0, 0, 0.2); 21 | } 22 | 23 | .subHeading { 24 | font-size: 19px; 25 | text-align: left; 26 | margin-bottom: 10px; 27 | padding: 5%; 28 | color: #868e96; 29 | font-family: "Montserrat", serif; 30 | } 31 | 32 | .subHeading2 { 33 | font-size: 20px; 34 | text-align: center; 35 | } 36 | 37 | .mobileImage { 38 | display: none; 39 | } 40 | 41 | .searchBtn { 42 | background-color: black; 43 | color: white; 44 | border: none; 45 | margin-top: 40px; 46 | border-radius: 0px; 47 | height: 35px; 48 | font-size: 22px; 49 | font-family: "Montserrat", serif; 50 | display: block; 51 | margin-left: auto; 52 | margin-right: auto; 53 | } 54 | 55 | .searchBtn:hover { 56 | background-color: black; 57 | color: white; 58 | } 59 | 60 | .searchBtn:focus { 61 | background-color: black; 62 | color: white; 63 | } 64 | 65 | @media only screen and (max-width: 768px) { 66 | .imageContainer { 67 | display: none; 68 | } 69 | 70 | .container { 71 | width: 100%; 72 | margin-top: 2vh; 73 | } 74 | 75 | .heading { 76 | font-size: 40px; 77 | } 78 | 79 | .subHeading { 80 | margin-bottom: 4vh; 81 | font-size: 15px; 82 | } 83 | 84 | .mobileImage { 85 | width: 70%; 86 | display: block; 87 | margin-left: auto; 88 | margin-right: auto; 89 | margin-bottom: 6vh; 90 | } 91 | 92 | .searchBtn { 93 | font-size: 15px; 94 | height: auto; 95 | padding: 5px 10px; 96 | margin-top: 25px; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/components/Layout/footer.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react" 2 | import { Row, Col } from "antd" 3 | 4 | import styles from "../../styles/footer.module.css" 5 | 6 | export class footer extends Component { 7 | render() { 8 | return ( 9 | 57 | ) 58 | } 59 | } 60 | 61 | export default footer 62 | -------------------------------------------------------------------------------- /src/components/User/userProgressBar.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { Steps, Progress } from "antd" 3 | const { Step } = Steps 4 | 5 | const UserProgress = ({ data, type }) => { 6 | const { about, skills, linkedin, github, facebook } = data 7 | 8 | const progress = [about, skills, github, linkedin, facebook] 9 | 10 | let progressVal = 0 11 | for (let i = 0; i < 5; i++) { 12 | const ele = progress[i] 13 | if ( 14 | ele !== "" && 15 | ele !== "https://github.com/" && 16 | ele !== "https://linkedin.com/in/" && 17 | ele !== "https://facebook.com/" 18 | ) 19 | progressVal++ 20 | else break 21 | } 22 | 23 | if (progressVal === 5) return <> 24 | else 25 | return ( 26 |
27 |

Profile Strength

28 | {type === "mobile" ? ( 29 | <> 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | ) : ( 39 | <> 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 57 | 58 | )} 59 |
60 | ) 61 | } 62 | 63 | export default UserProgress 64 | -------------------------------------------------------------------------------- /src/components/Login/skills.js: -------------------------------------------------------------------------------- 1 | const skills = [ 2 | "Algorithms", 3 | "Analytical Skills", 4 | "Big Data", 5 | "Calculating", 6 | "Compiling Statistics", 7 | "Data Analytics", 8 | "Data Mining", 9 | "Database Design", 10 | "Database Management", 11 | "Documentation", 12 | "Modeling", 13 | "Modification", 14 | "Needs Analysis", 15 | "Quantitative Research", 16 | "Quantitative Reports", 17 | "Statistical Analysis", 18 | "Applications", 19 | "Certifications", 20 | "Coding", 21 | "Computing", 22 | "Configuration", 23 | "Customer Support", 24 | "Debugging", 25 | "Design", 26 | "Development", 27 | "Hardware", 28 | "Implementation", 29 | "Information Technology", 30 | "Infrastructure", 31 | "Languages", 32 | "Maintenance", 33 | "Network Architecture", 34 | "Network Security", 35 | "Networking", 36 | "New Technologies", 37 | "Operating Systems", 38 | "Programming", 39 | "Restoration", 40 | "Security", 41 | "Servers", 42 | "Software", 43 | "Solution Delivery", 44 | "Storage", 45 | "Structures", 46 | "Systems Analysis", 47 | "Technical Support", 48 | "Technology", 49 | "Testing", 50 | "Tools", 51 | "Training", 52 | "Troubleshooting", 53 | "Usability", 54 | "Benchmarking", 55 | "Budget Planning", 56 | "Engineering", 57 | "Fabrication", 58 | "Following Specifications", 59 | "Operations", 60 | "Performance Review", 61 | "Project Planning", 62 | "Quality Assurance", 63 | "Quality Control", 64 | "Scheduling", 65 | "Task Delegation", 66 | "Task Management", 67 | "Blogging", 68 | "Digital Photography", 69 | "Digital Media", 70 | "Facebook", 71 | "Instagram", 72 | "Networking", 73 | "Pinterest", 74 | "SEO", 75 | "Social Media Platforms", 76 | "Twitter", 77 | "Web Analytics", 78 | "Client Relations", 79 | "Email", 80 | "Requirements Gathering", 81 | "Research", 82 | "Subject Matter Experts (SMEs)", 83 | "Technical Documentation", 84 | ] 85 | 86 | module.exports = skills.map((name, id) => ({ id, name })) 87 | -------------------------------------------------------------------------------- /gatsby-config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Configure your Gatsby site with this file. 3 | * 4 | * See: https://www.gatsbyjs.org/docs/gatsby-config/ 5 | */ 6 | module.exports = { 7 | /* Your site config here */ 8 | pathPrefix: "/", 9 | siteMetadata: { 10 | title: `Footsteps`, 11 | titleAlt: `Footsteps App`, 12 | siteLanguage: `en`, 13 | ogLanguage: `en_US`, 14 | titleTemplate: `%s | Community-made 🧑‍🤝‍🧑learning paths`, 15 | description: `Every expert was a beginner once. They tried and learnt things their own way. Join footsteps to learn from the journey of experts.`, 16 | shortName: `Footsteps`, 17 | url: `https://www.footsteps.dev`, // No trailing slash allowed! 18 | siteUrl: `https://www.footsteps.dev`, 19 | image: `/images/img_share.png`, // Path to your image you placed in the 'static' folder 20 | favicon: `/favicon.ico`, 21 | author: `@fnplusofficial`, 22 | twitter: `@fnplusofficial`, 23 | facebook: `fnplusofficial`, 24 | twitterUsername: `@fnplusofficial`, 25 | }, 26 | plugins: [ 27 | `gatsby-plugin-netlify`, 28 | `gatsby-plugin-sitemap`, 29 | `gatsby-plugin-react-helmet`, 30 | { 31 | resolve: `gatsby-plugin-google-analytics`, 32 | options: { 33 | trackingId: process.env.GATSBY_GOOGLE_TRACKING_ID, 34 | head: true, 35 | }, 36 | }, 37 | { 38 | resolve: `gatsby-plugin-create-client-paths`, 39 | options: { prefixes: [`/user/*`, `/editPath/*`] }, 40 | }, 41 | { 42 | resolve: `gatsby-plugin-typography`, 43 | options: { 44 | pathToConfigModule: `src/utils/typography`, 45 | }, 46 | }, 47 | { 48 | resolve: `gatsby-plugin-manifest`, 49 | options: { 50 | name: `Footsteps: Community-made 🧑‍🤝‍🧑learning paths`, 51 | short_name: `Footsteps`, 52 | start_url: `/`, 53 | icon: `src/images/icon.png`, 54 | background_color: `#f7f0eb`, 55 | theme_color: `#a2466c`, 56 | display: `standalone`, 57 | }, 58 | }, 59 | `gatsby-plugin-offline`, 60 | { 61 | resolve: `gatsby-source-filesystem`, 62 | options: { 63 | name: `images`, 64 | path: `${__dirname}/src/images/`, 65 | }, 66 | }, 67 | "gatsby-transformer-sharp", 68 | "gatsby-plugin-sharp", 69 | ], 70 | } 71 | -------------------------------------------------------------------------------- /src/styles/global.css: -------------------------------------------------------------------------------- 1 | @import url("https://fonts.googleapis.com/css?family=Montserrat:400,600,700&display=swap"); 2 | 3 | html, 4 | body, 5 | #___gatsby, 6 | #___gatsby > div { 7 | min-height: 100vh; 8 | padding: 0; 9 | margin: 0; 10 | } 11 | 12 | h1, 13 | h2, 14 | h3 { 15 | margin-top: 0; 16 | } 17 | 18 | .absolute-center { 19 | display: flex; 20 | flex-direction: row; 21 | justify-content: center; 22 | align-items: center; 23 | width: 100%; 24 | min-height: 100vh; 25 | } 26 | 27 | .button { 28 | appearance: button; 29 | border: 1px solid #000; 30 | padding: 8px 32px; 31 | color: white; 32 | transition: all 0.2s; 33 | background-color: black; 34 | width: max-content; 35 | border-radius: 25px; 36 | } 37 | 38 | .button:hover { 39 | background-color: #2f2f2f; 40 | color: white; 41 | } 42 | 43 | ul { 44 | list-style-type: none; 45 | margin: 0; 46 | padding: 0; 47 | } 48 | 49 | .container { 50 | display: flex; 51 | justify-content: space-evenly; 52 | } 53 | 54 | .row { 55 | flex-direction: row; 56 | } 57 | 58 | .column { 59 | flex-direction: column; 60 | } 61 | 62 | .heading { 63 | margin: 20; 64 | padding: 5; 65 | } 66 | 67 | /* ! Skills Tags */ 68 | 69 | div.ReactTags__tags { 70 | position: relative; 71 | } 72 | 73 | /* Styles for the input */ 74 | div.ReactTags__tagInput { 75 | width: 200px; 76 | display: inline-block; 77 | font-size: 20px; 78 | font-family: "Montserrat", sans-serif; 79 | margin-top: 10px; 80 | margin-bottom: 10px; 81 | } 82 | 83 | div.ReactTags__tagInput input.ReactTags__tagInputField, 84 | div.ReactTags__tagInput input.ReactTags__tagInputField:focus { 85 | height: auto; 86 | margin: 0; 87 | font-size: 20px; 88 | width: 100%; 89 | border: none; 90 | border-bottom: 1px solid #868686; 91 | padding: 0 4px; 92 | } 93 | 94 | /* Styles for selected tags */ 95 | div.ReactTags__selected span.ReactTags__tag { 96 | border: 1px solid#ddd; 97 | background: #eee; 98 | font-size: 16px; 99 | display: inline-block; 100 | padding: 5px; 101 | margin: 5px 10px 5px 0; 102 | border-radius: 2px; 103 | } 104 | div.ReactTags__selected a.ReactTags__remove { 105 | color: #aaa; 106 | margin-left: 5px; 107 | cursor: pointer; 108 | } 109 | -------------------------------------------------------------------------------- /src/components/EditPath/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react" 2 | import gql from "graphql-tag" 3 | import { Query } from "react-apollo" 4 | import { navigate } from "gatsby" 5 | 6 | import Layout from "../Layout/layout" 7 | import Loader from "../Layout/loader" 8 | import EditPath from "./editPath" 9 | 10 | export class index extends Component { 11 | state = { 12 | user_id: "", 13 | } 14 | 15 | componentDidMount() { 16 | if (typeof window !== "undefined") { 17 | this.setState({ 18 | user_id: localStorage.getItem("userId"), 19 | }) 20 | } 21 | } 22 | render() { 23 | return ( 24 | 28 | {({ data, loading, error }) => { 29 | if (loading) 30 | return ( 31 | 32 | 33 | 34 | ) 35 | if (error) 36 | return ( 37 | 38 |

Error Loading User

39 |
40 | ) 41 | if (data) { 42 | if (this.state.user_id === "") { 43 | return ( 44 | 45 | 46 | 47 | ) 48 | } else { 49 | if (data.Learning_Paths[0].author === this.state.user_id) { 50 | return ( 51 | 52 | 53 | 54 | ) 55 | } else { 56 | return {navigate("/")} 57 | } 58 | } 59 | } 60 | }} 61 |
62 | ) 63 | } 64 | } 65 | 66 | export default index 67 | 68 | export const GET_PATH_INFO_QUERY_APOLLO = gql` 69 | query getPath($id: Int) { 70 | Learning_Paths(where: { id: { _eq: $id } }) { 71 | author 72 | description 73 | isPrivate 74 | footsteps { 75 | description 76 | id 77 | learning_path 78 | level 79 | resource_icon 80 | resource_type 81 | resource_url 82 | title 83 | } 84 | icon 85 | id 86 | tags 87 | title 88 | user { 89 | id 90 | } 91 | } 92 | } 93 | ` 94 | -------------------------------------------------------------------------------- /src/components/User/learningPathCard.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react" 2 | import { DownOutlined, EditOutlined } from "@ant-design/icons" 3 | import { Row, Col, Progress } from "antd" 4 | import { navigate } from "gatsby" 5 | 6 | import FootstepCard from "./footstepsCard" 7 | 8 | import styles from "../../styles/footsteps.module.css" 9 | 10 | export class learningPathCard extends Component { 11 | state = { 12 | expand: false, 13 | } 14 | 15 | expand = () => { 16 | this.setState({ 17 | expand: !this.state.expand, 18 | }) 19 | } 20 | 21 | render() { 22 | let path = this.props.path 23 | 24 | return ( 25 |
26 | {this.props.show_edit ? ( 27 |
navigate(`/editPath/${path.id}`)} 31 | > 32 | 33 |
34 | ) : ( 35 | "" 36 | )} 37 | 38 |
this.expand()}> 39 | 40 | 41 | 42 | 43 | 44 |

{path.title}

45 |
{path.description}
46 | 47 | 48 | 57 | 58 | 59 |
60 |
61 |
68 | {path.footsteps.map((footstep, index) => ( 69 | 75 | ))} 76 |
77 |
78 | ) 79 | } 80 | } 81 | 82 | export default learningPathCard 83 | -------------------------------------------------------------------------------- /src/components/Layout/menu.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useContext } from "react" 2 | import { SettingFilled } from "@ant-design/icons" 3 | import { Row, Col } from "antd" 4 | import { Link, navigate } from "gatsby" 5 | import firebase from "firebase/app" 6 | import "firebase/auth" 7 | 8 | import styles from "../../styles/menu.module.css" 9 | import UserContext from "../../context/userContext" 10 | 11 | function NavMenu() { 12 | const [expand, setExpand] = useState(false) 13 | const { user } = useContext(UserContext) 14 | 15 | const expandMenu = () => { 16 | setExpand((expand) => !expand) 17 | } 18 | 19 | const profileClick = () => { 20 | navigate("/profile/") 21 | expandMenu() 22 | } 23 | 24 | const logoutClick = () => { 25 | firebase.auth().signOut() 26 | expandMenu() 27 | navigate("/") 28 | } 29 | 30 | return ( 31 | 32 |
    33 |
  • 34 | 40 |
  • 41 | 42 |
  • 43 | Create Path 44 |
  • 45 |
  • 46 | Home 47 |
  • 48 |
49 | 50 |
55 | 56 | 57 | 58 | 59 | 60 |

61 | {user.first_name} {user.last_name} 62 |

63 |
{user.email}
64 | 65 | 66 |
67 | My Profile 68 |
69 | 70 | 71 |
navigate("/settings/")} 74 | > 75 | 76 |
77 | 78 |
79 | 80 |
81 |
82 | Logout 83 |
84 |
85 |
86 | ) 87 | } 88 | 89 | export default NavMenu 90 | -------------------------------------------------------------------------------- /src/components/Layout/sideDrawer.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react" 2 | import { Drawer, Row, Col } from "antd" 3 | import { navigate } from "gatsby" 4 | import firebase from "firebase/app" 5 | import "firebase/auth" 6 | 7 | import styles from "../../styles/header.module.css" 8 | 9 | import NavMenu from "./menu" 10 | export function header({ show = false }) { 11 | const [visible, setVisible] = useState(false) 12 | 13 | const showDrawer = () => { 14 | setVisible((visible) => !visible) 15 | } 16 | 17 | const onClose = () => { 18 | setVisible(false) 19 | } 20 | 21 | const logoutClick = () => { 22 | firebase.auth().signOut() 23 | navigate("/") 24 | } 25 | 26 | return ( 27 | 91 | ) 92 | } 93 | 94 | export default header 95 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "footsteps-app", 3 | "private": true, 4 | "description": "Crowdsourced Learning Paths Aggregator", 5 | "version": "0.1.1", 6 | "license": "GPL v3", 7 | "scripts": { 8 | "robots": "cp src/components/SEO/robots.txt public/", 9 | "build": "gatsby build", 10 | "develop": "gatsby develop", 11 | "format": "prettier --write src/**/*.{js,jsx}", 12 | "start": "npm run develop", 13 | "serve": "gatsby serve", 14 | "test": "echo \"Write tests! -> https://gatsby.dev/unit-testing\"", 15 | "snyk-protect": "snyk protect", 16 | "prepare": "npm run snyk-protect" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/fnplus/footsteps-app" 21 | }, 22 | "keywords": [ 23 | "gatsby", 24 | "react", 25 | "graphql", 26 | "hasura" 27 | ], 28 | "author": "@fnplusofficial", 29 | "bugs": { 30 | "url": "https://github.com/fnplus/footsteps-app/issues" 31 | }, 32 | "homepage": "https://github.com/fnplus/footsteps-app#readme", 33 | "husky": { 34 | "hooks": { 35 | "pre-commit": "pretty-quick --staged && lint-staged" 36 | } 37 | }, 38 | "lint-staged": { 39 | "*.js": [ 40 | "eslint --fix" 41 | ] 42 | }, 43 | "dependencies": { 44 | "@ant-design/icons": "^4.2.2", 45 | "antd": "^4.5.4", 46 | "apollo-boost": "^0.4.9", 47 | "apollo-link-http": "^1.5.17", 48 | "firebase": "^7.18.0", 49 | "gatsby": "^2.24.47", 50 | "gatsby-cli": "^2.12.87", 51 | "gatsby-image": "^2.4.16", 52 | "gatsby-plugin-create-client-paths": "^2.3.10", 53 | "gatsby-plugin-google-analytics": "^2.3.13", 54 | "gatsby-plugin-manifest": "^2.4.23", 55 | "gatsby-plugin-netlify": "^2.3.13", 56 | "gatsby-plugin-offline": "^3.2.23", 57 | "gatsby-plugin-react-helmet": "^3.3.10", 58 | "gatsby-plugin-sharp": "^2.6.27", 59 | "gatsby-plugin-sitemap": "^2.4.11", 60 | "gatsby-plugin-typography": "^2.5.10", 61 | "gatsby-source-filesystem": "^2.3.24", 62 | "gatsby-source-graphql": "^2.7.1", 63 | "gatsby-transformer-sharp": "^2.5.13", 64 | "isomorphic-fetch": "^2.2.1", 65 | "query-string": "^6.13.1", 66 | "react": "^16.13.1", 67 | "react-apollo": "^3.1.5", 68 | "react-dnd": "^5.0.0", 69 | "react-dnd-html5-backend": "^3.0.2", 70 | "react-dom": "^16.13.1", 71 | "react-firebase-file-uploader": "^2.4.3", 72 | "react-firebaseui": "^4.1.0", 73 | "react-helmet": "^6.1.0", 74 | "react-tag-input": "^6.4.3", 75 | "react-typography": "^0.16.19", 76 | "snyk": "^1.377.0", 77 | "typography": "^0.16.19", 78 | "typography-theme-grand-view": "^0.16.19", 79 | "uuid": "^8.3.0" 80 | }, 81 | "devDependencies": { 82 | "eslint": "^7.7.0", 83 | "eslint-config-gatsby-standard": "^2.2.0", 84 | "husky": "^4.2.5", 85 | "lint-staged": "^10.2.11", 86 | "prettier": "^2.0.5", 87 | "pretty-quick": "^2.0.1" 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/styles/login.module.css: -------------------------------------------------------------------------------- 1 | .login h1 { 2 | font-size: 40px; 3 | color: #099268; 4 | text-shadow: 2px 4px 3px rgba(0, 0, 0, 0.1); 5 | } 6 | 7 | .login h3 { 8 | text-align: center; 9 | color: #495057; 10 | padding: 7%; 11 | } 12 | 13 | .login p { 14 | text-align: center; 15 | color: #868e96; 16 | } 17 | .container { 18 | width: 300px; 19 | display: block; 20 | margin-left: auto; 21 | margin-right: auto; 22 | margin-top: 20px; 23 | } 24 | 25 | .image, .imageAbt { 26 | width: 60%; 27 | height: 100%; 28 | display: block; 29 | margin-left: 5%; 30 | margin-right: 5%; 31 | } 32 | 33 | .imageAbt { 34 | width: 100%; 35 | } 36 | 37 | /* About us section */ 38 | .AboutUs { 39 | width: 100%; 40 | height: 100%; 41 | background-color: #222222; 42 | color: #adb5bd; 43 | padding: 4%; 44 | font-size: 18px; 45 | clip-path: polygon(0 0, 100% 0, 100% 100%, 0 calc(100% - 5vw)); 46 | } 47 | 48 | .AboutUsVideo { 49 | padding: 5%; 50 | padding-top: 0%; 51 | } 52 | 53 | .AboutUs h1 { 54 | color: white; 55 | font-size: 40px; 56 | text-shadow: 2px 2px 4px #000000; 57 | font-weight: 600; 58 | } 59 | 60 | .AboutDesc { 61 | padding: 4%; 62 | } 63 | 64 | /* Working of footsteps section */ 65 | .working { 66 | padding: 7%; 67 | margin-top: 2%; 68 | font-size: 19px; 69 | } 70 | 71 | .working h1 { 72 | font-size: 40px; 73 | font-weight: 600; 74 | color: #099268; 75 | text-shadow: 2px 4px 3px rgba(0, 0, 0, 0.1); 76 | } 77 | 78 | .Cards { 79 | background-color: #dee2e6; 80 | padding: 1%; 81 | } 82 | 83 | /* FAQ section */ 84 | .FAQ { 85 | padding: 5%; 86 | background-color: #222222; 87 | color: #adb5bd; 88 | font-size: 18px; 89 | clip-path: polygon(0 0, 100% 0, 100% 100%, 0 calc(100% - 5vw)); 90 | } 91 | 92 | .FAQ h1 { 93 | color: white; 94 | font-size: 40px; 95 | text-shadow: 2px 2px 4px #000000; 96 | font-weight: 600; 97 | } 98 | 99 | .FAQimg { 100 | background-color: #099268; 101 | height: 25vw; 102 | display: block; 103 | margin: auto; 104 | clip-path: polygon(0 0, 100% 0, 100% 100%, 0 calc(100% - 5vw)); 105 | } 106 | 107 | /* Media queries */ 108 | @media only screen and (max-width: 768px) { 109 | .image, .imageAbt { 110 | width: 90%; 111 | margin-left: auto; 112 | margin-right: auto; 113 | } 114 | 115 | .imageAbt { 116 | width: 80%; 117 | } 118 | 119 | .login h1 { 120 | text-align: center; 121 | margin: 5%; 122 | } 123 | 124 | .login h2 { 125 | text-align: center; 126 | margin: 15px; 127 | } 128 | 129 | .AboutUsVideo { 130 | width: 80%; 131 | } 132 | 133 | .FAQ, 134 | .working, 135 | .AboutUs { 136 | font-size: 18px; 137 | } 138 | 139 | .FAQimg { 140 | height: 55vw; 141 | } 142 | } 143 | 144 | @media (min-width:769px) and (max-width:991px) { 145 | .imageAbt { 146 | width:70%; 147 | display:block; 148 | margin:auto; 149 | } 150 | 151 | .FAQimg { 152 | height: 45vw; 153 | max-width: 70vw; 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/pages/search.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react" 2 | import queryString from "query-string" 3 | import { Query } from "react-apollo" 4 | import gql from "graphql-tag" 5 | 6 | import Layout from "../components/Layout/layout" 7 | import SearchResult from "../components/Search/searchResult" 8 | import Loader from "../components/Layout/loader" 9 | import styles from "../styles/search.module.css" 10 | 11 | export class search extends Component { 12 | state = { 13 | query: "", 14 | } 15 | 16 | componentDidMount() { 17 | const { q } = queryString.parse(this.props.location.search) 18 | this.setState({ query: q }) 19 | } 20 | 21 | render() { 22 | return ( 23 | 24 |
25 |

26 | Search results for "{this.state.query}" 27 |

28 |
29 | 33 | {({ data, loading, error }) => { 34 | if (loading) return 35 | if (error) return

Error Loading Results

36 | 37 | if (data) { 38 | if ( 39 | data.Learning_Paths.length !== 0 || 40 | data.Footsteps.length !== 0 41 | ) { 42 | return ( 43 | 47 | ) 48 | } else { 49 | return

No Results Found

50 | } 51 | } 52 | }} 53 |
54 |
55 |
56 |
57 | ) 58 | } 59 | } 60 | 61 | export default search 62 | 63 | export const SEARCH_QUERY_APOLLO = gql` 64 | query searchPaths($query: String!) { 65 | Learning_Paths( 66 | where: { 67 | _or: [{ title: { _ilike: $query } }, { tags: { _ilike: $query } }] 68 | isPrivate: { _eq: false } 69 | } 70 | ) { 71 | title 72 | author 73 | description 74 | footsteps { 75 | id 76 | description 77 | learning_path 78 | title 79 | resource_url 80 | resource_type 81 | resource_icon 82 | level 83 | } 84 | icon 85 | id 86 | user { 87 | first_name 88 | last_name 89 | id 90 | username 91 | profile_pic 92 | } 93 | votes { 94 | learning_path 95 | id 96 | user 97 | } 98 | votes_aggregate { 99 | aggregate { 100 | count(columns: id) 101 | } 102 | } 103 | } 104 | Footsteps(where: { title: { _ilike: $query } }) { 105 | description 106 | id 107 | learning_path 108 | level 109 | resource_icon 110 | resource_type 111 | resource_url 112 | title 113 | } 114 | } 115 | ` 116 | -------------------------------------------------------------------------------- /src/styles/menu.module.css: -------------------------------------------------------------------------------- 1 | .menuItems { 2 | font-size: 20px; 3 | margin: 0 40px; 4 | font-family: "Montserrat", sans-serif; 5 | display: inline; 6 | float: right; 7 | line-height: 60px; 8 | font-weight: 600; 9 | } 10 | 11 | .menuItems a { 12 | color: #3f3f3f; 13 | } 14 | 15 | .menuItems a:hover { 16 | color: black !important; 17 | } 18 | 19 | .menuBtn { 20 | background-color: black; 21 | border-radius: 40px; 22 | } 23 | 24 | .menuBtn:hover { 25 | background-color: black !important; 26 | border-bottom: 2px solid black !important; 27 | } 28 | 29 | .menuBtn a:hover { 30 | color: white !important; 31 | } 32 | 33 | .menuBtn a { 34 | color: white !important; 35 | } 36 | 37 | .menuProfile { 38 | margin: 0 0 0 40px; 39 | display: inline; 40 | float: right; 41 | } 42 | 43 | .menuProfile:hover { 44 | border-bottom: none !important; 45 | } 46 | 47 | .menuProfileImg { 48 | width: 60px; 49 | border-radius: 50%; 50 | } 51 | 52 | .menuProfileImg:hover { 53 | cursor: pointer; 54 | } 55 | 56 | .dropDown { 57 | position: absolute; 58 | width: 400px; 59 | height: 140px; 60 | right: 0px; 61 | top: 80px; 62 | z-index: 99; 63 | font-size: 16px; 64 | padding: 10px; 65 | opacity: 0; 66 | transition: opacity 0.3s; 67 | border-radius: 20px; 68 | box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.2); 69 | font-family: "Montserrat", sans-serif; 70 | background: white; 71 | } 72 | 73 | .dropDownShow { 74 | opacity: 1; 75 | } 76 | 77 | .dropDownInfo { 78 | height: 90px !important; 79 | } 80 | 81 | .dropDownInfo img { 82 | width: 90px; 83 | border-radius: 50%; 84 | display: block; 85 | margin-left: auto; 86 | margin-right: auto; 87 | } 88 | 89 | .dropDownInfo h3 { 90 | font-weight: 700; 91 | margin-top: 5px; 92 | margin-left: 15px; 93 | margin-bottom: 2px; 94 | } 95 | 96 | .profileBtn { 97 | margin-left: 15px; 98 | padding: 0 10px; 99 | background: #029843; 100 | display: inline-block; 101 | border-radius: 5px; 102 | line-height: 30px; 103 | height: 30px; 104 | color: white; 105 | font-size: 14px; 106 | font-weight: 600; 107 | transition: transform 0.2s; 108 | } 109 | 110 | .settingsBtn { 111 | display: inline-block; 112 | background: #e9e9e9; 113 | line-height: 30px; 114 | padding: 0 10px; 115 | border-radius: 5px; 116 | transition: transform 0.2s; 117 | } 118 | 119 | .logoutBtn { 120 | float: right; 121 | margin-right: 5px; 122 | background: #fe5e44; 123 | border-radius: 5px; 124 | line-height: 30px; 125 | padding: 0 10px; 126 | height: 30px; 127 | color: white; 128 | font-size: 14px; 129 | font-weight: 600; 130 | transition: transform 0.2s; 131 | } 132 | 133 | .profileBtn:hover { 134 | cursor: pointer; 135 | transform: scale(1.05); 136 | box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.3); 137 | } 138 | 139 | .settingsBtn:hover { 140 | cursor: pointer; 141 | transform: scale(1.05); 142 | box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.3); 143 | } 144 | 145 | .logoutBtn:hover { 146 | cursor: pointer; 147 | transform: scale(1.05); 148 | box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.3); 149 | } 150 | 151 | .dropDownInfo h5 { 152 | margin-left: 15px; 153 | } 154 | 155 | @media only screen and (max-width: 768px) { 156 | .menuBtn { 157 | padding-top: 30px; 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/components/SEO/SEO.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { Helmet } from "react-helmet" 3 | import PropTypes from "prop-types" 4 | import { StaticQuery, graphql } from "gatsby" 5 | import Facebook from "./Facebook" 6 | import Twitter from "./Twitter" 7 | 8 | const SEO = ({ title, description, image, pathname, article }) => ( 9 | { 28 | const seo = { 29 | title: title || defaultTitle, 30 | description: description || defaultDescription, 31 | image: `${siteUrl}${image || defaultImage}`, 32 | url: `${siteUrl}${pathname || "/"}`, 33 | siteLanguage: siteLanguage, 34 | author: author, 35 | } 36 | 37 | return ( 38 | <> 39 | 40 | 41 | 42 | {seo.url && } 43 | {seo.title && } 44 | {seo.description && ( 45 | 46 | )} 47 | {seo.image && } 48 | 49 | {twitterUsername && ( 50 | 51 | )} 52 | {seo.title && } 53 | {seo.description && ( 54 | 55 | )} 56 | {seo.image && } 57 | 58 | 67 | 73 | 74 | ) 75 | }} 76 | /> 77 | ) 78 | 79 | SEO.propTypes = { 80 | title: PropTypes.string, 81 | description: PropTypes.string, 82 | image: PropTypes.string, 83 | pathname: PropTypes.string, 84 | } 85 | 86 | SEO.defaultProps = { 87 | title: `Footsteps App`, 88 | ogLanguage: `en_US`, 89 | image: `/images/img_share.png`, 90 | } 91 | 92 | export default SEO 93 | 94 | const query = graphql` 95 | query SEO { 96 | site { 97 | siteMetadata { 98 | defaultTitle: title 99 | titleTemplate 100 | defaultDescription: description 101 | siteUrl: url 102 | defaultImage: image 103 | twitterUsername 104 | siteLanguage 105 | ogLanguage 106 | author 107 | twitter 108 | facebook 109 | } 110 | } 111 | } 112 | ` 113 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at support@fnplus.tech. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /src/styles/settings.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | margin: 30px 150px; 3 | padding: 0 50px; 4 | } 5 | 6 | .heading { 7 | text-align: center; 8 | font-size: 40px; 9 | font-weight: 700; 10 | } 11 | 12 | .sub_heading { 13 | margin-top: 100px; 14 | font-weight: 600; 15 | font-size: 30px; 16 | margin-bottom: 0; 17 | } 18 | 19 | .input { 20 | border: none; 21 | border-bottom: 1px solid #868686; 22 | font-size: 20px; 23 | font-family: "Montserrat", sans-serif; 24 | color: black; 25 | width: 400px; 26 | } 27 | 28 | input:focus, 29 | select:focus, 30 | textarea:focus { 31 | outline: none; 32 | } 33 | 34 | .input_label { 35 | font-weight: 600; 36 | margin-top: 35px; 37 | font-family: "Montserrat", sans-serif; 38 | color: #029843; 39 | } 40 | 41 | .pic_container { 42 | width: 250px; 43 | display: block; 44 | margin-left: auto; 45 | margin-right: auto; 46 | margin-top: 40px; 47 | position: relative; 48 | } 49 | 50 | .profile_pic { 51 | border-radius: 20px; 52 | width: 100%; 53 | } 54 | 55 | .add_image_btn:hover { 56 | cursor: pointer; 57 | transform: scale(1.1); 58 | box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.3); 59 | } 60 | 61 | .add_image_btn { 62 | background: #029843; 63 | padding: 5px 10px; 64 | color: white; 65 | font-size: 18px; 66 | position: absolute; 67 | bottom: 33px; 68 | right: 8px; 69 | border-radius: 50%; 70 | transition: transform 0.2s; 71 | } 72 | 73 | .btn_container { 74 | display: flex; 75 | justify-content: center; 76 | align-content: center; 77 | } 78 | 79 | .update_btn { 80 | display: inline-block; 81 | background: #029843; 82 | padding: 5px 40px; 83 | border-radius: 20px; 84 | font-size: 20px; 85 | font-family: "Montserrat", sans-serif; 86 | font-weight: 700; 87 | color: white; 88 | margin: 20px 50px 50px 50px; 89 | transition: transform 0.2s; 90 | } 91 | 92 | .update_btn:hover { 93 | cursor: pointer; 94 | transform: scale(1.1); 95 | box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.3); 96 | } 97 | 98 | .error { 99 | text-align: center; 100 | color: #fe5e44; 101 | font-family: "Montserrat", sans-serif; 102 | font-weight: 600; 103 | font-size: 18px; 104 | } 105 | 106 | .error_container { 107 | margin-top: 50px; 108 | } 109 | 110 | @media only screen and (max-width: 768px) { 111 | .container { 112 | margin: 30px; 113 | padding: 0; 114 | } 115 | .input { 116 | width: 100%; 117 | } 118 | .sub_heading { 119 | margin-top: 40px; 120 | } 121 | } 122 | 123 | .errorshake { 124 | border: none; 125 | border-bottom: 1px solid red; 126 | font-size: 20px; 127 | font-family: "Montserrat", sans-serif; 128 | color: blue; 129 | width: 400px; 130 | animation: shake 0.5s; 131 | animation-iteration-count: 3s; 132 | } 133 | 134 | @keyframes shake { 135 | 0% { 136 | transform: translate(1px, 1px) rotate(0deg); 137 | } 138 | 10% { 139 | transform: translate(-1px, -2px) rotate(-1deg); 140 | } 141 | 20% { 142 | transform: translate(-3px, 0px) rotate(1deg); 143 | } 144 | 30% { 145 | transform: translate(3px, 2px) rotate(0deg); 146 | } 147 | 40% { 148 | transform: translate(1px, -1px) rotate(1deg); 149 | } 150 | 50% { 151 | transform: translate(-1px, 2px) rotate(-1deg); 152 | } 153 | 60% { 154 | transform: translate(-3px, 1px) rotate(0deg); 155 | } 156 | 70% { 157 | transform: translate(3px, 1px) rotate(-1deg); 158 | } 159 | 80% { 160 | transform: translate(-1px, -1px) rotate(1deg); 161 | } 162 | 90% { 163 | transform: translate(1px, 2px) rotate(0deg); 164 | } 165 | 100% { 166 | transform: translate(1px, -2px) rotate(-1deg); 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/components/Search/resultPathCard.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react" 2 | import { DownOutlined } from "@ant-design/icons" 3 | import { Row, Col } from "antd" 4 | 5 | import FootstepCard from "../User/footstepsCard" 6 | import styles from "../../styles/result.module.css" 7 | 8 | export class resultPathCard extends Component { 9 | state = { 10 | expand: false, 11 | } 12 | 13 | expand = () => { 14 | this.setState({ 15 | expand: !this.state.expand, 16 | }) 17 | } 18 | 19 | render() { 20 | const { data } = this.props 21 | 22 | return ( 23 |
24 | this.expand()}> 25 | 26 |
27 | 28 |
29 | 30 | 31 | 32 | 33 |

{data.title}

34 |

{data.description}

35 | 36 |
37 |

38 | By:{" "} 39 | 40 | {data.user.first_name + " " + data.user.last_name} 41 | 42 |

43 | 44 |

45 | {" "} 46 | Votes: {data.votes_aggregate.aggregate.count} 47 |

48 |
49 | 50 | 58 | 59 | 60 |
61 | 62 |
63 |
64 | this.expand()}> 65 | 71 |

{data.title}

72 |

{data.description}

73 | 74 | 82 | this.expand()} /> 83 | 84 |
85 | 86 | 87 |

88 | By:{" "} 89 | {data.user.first_name + " " + data.user.last_name} 90 |

91 | 92 | 93 | Votes: {data.votes_aggregate.aggregate.count} 94 | 95 |
96 |
97 |
104 | {data.footsteps.map((footstep, index) => ( 105 | 111 | ))} 112 |
113 |
114 | ) 115 | } 116 | } 117 | 118 | export default resultPathCard 119 | -------------------------------------------------------------------------------- /Guidelines.md: -------------------------------------------------------------------------------- 1 | # Guidelines 2 | ![image](./images/welcome.png) 3 | ## Contributing to 👣 Fnplus Open Source Projects 4 | Fnplus welcomes contributions to open source projects on Github. When contributing, please follow the [Code of Conduct](/CODE_OF_CONDUCT.md). 5 | ## Issues 6 | Feel free to submit issues and enhancement requests. All requests would be entertained with respect to CODE OF CONDUCT. We welcome your ideas and creativity. 7 | ## Contributing 8 | ### Setting Up GitHub And Git For The First Time 9 | #### Get a github account 10 | #### Download and install git 11 | #### Check by typing git version in the terminal 12 | #### Set up git with your user name and email 13 | ## Open a terminal/shell and type 14 | ```bash 15 | git config --global user.name "Your name here" 16 | ``` 17 | ```bash 18 | git config --global user.email "your_email@example.com" 19 | ``` 20 | (Don’t type the $; that just indicates that you’re doing this at the command line.) 21 | For any technical issues while setting up visit this page by Github — 22 | [Getting Started With Github](https://help.github.com/en/github/getting-started-with-github) 23 | ## 1. Fork the project 24 | Click on the FORK icon on top right of your screen . 25 | ![fork-image](./images/fork.png) 26 | ## 2. Clone the Project 27 | After the step 1, you have to click on the Green Colored “Clone or download” button and copy the web-URL . 28 | ![clone_url-image](./images/clone.png) 29 | ## 3. Open GitBash Software 30 | Open Terminal/Command Line and write the command 31 | ```bash 32 | git clone e.g 33 | ``` 34 | ```bash 35 | git clone https://github.com/YOUR-USERNAME/ProjectName 36 | ``` 37 | Press Enter. Your local clone will be created. 38 | Then, go to footsteps-app (cd footsteps-app) and do ``` git remote add upstream https://github.com/fnplus/footsteps-app.git ``` (use git@github.com:fnplus/footsteps-app.git if using SSH) to add a remote called upstream and link it to Footsteps's main repository (this will be used to merge with the latest version on develop and also to prevent running lint checks on merge commits when you later update your local branch with upstream/master). 39 | If your upstream is not set correctly, the lint checks will run on merge commits as well. The way to fix this will be to update the upstream URL by running the following command: ```git remote set-url upstream https://github.com/fnplus/footsteps-app.git ``` (use git@github.com:fnplus/footsteps-app.git if using SSH) 40 | 41 | ## 4. Update your local repository and create your separate Branch 42 | To create a separate branch write this command on Github — 43 | 44 | ```bash 45 | git fetch upstream 46 | git checkout develop 47 | git merge upstream/develop 48 | git checkout -b your-branch-name 49 | ``` 50 | 51 | This will update your local repo with latest changes and also create a new branch with name as your-branch-name. 52 | If you find any difficulty, please check [Fork a repo-Github Help](https://help.github.com/en/github/getting-started-with-github/fork-a-repo) 53 | 54 | ### Tip: Always work on a new branch and don’t mess up the master branch 55 | ## 5. Open the project 56 | Now, Open the project in your local device. It will be in the directory where you cloned the project. 57 | After opening the project, make some meaningful changes to the project(Add features, Modify existing code, Add files/Readme.md) . 58 | ## 6. Add your changes 59 | To add all the changes you made write the command 60 | ```bash 61 | git add . 62 | ``` 63 | ## 7. Commit Changes 64 | Commit all your changes using the command 65 | ```bash 66 | git commit -m "commit message" 67 | ``` 68 | ## 9. Push Your Code 69 | ```bash 70 | git push -u origin 71 | ``` 72 | ## 10. Pull Request 73 | To create a pull request that is ready for review, click Create Pull Request. For more information about creating pull requests, see “About pull requests.” 74 | ### ![pull_request-image](./images/pull_request.png) 75 | ### ![git_cheet_sheet-image](./images/git_cheat_sheet.jpg) 76 | ### 👍 Hurrah! You are all set to contribute to the Open Source Community 77 | ![open_source-image](./images/open_source.png) 78 | ## ⚡️⚡️⚡️⚡️ Enjoy Contributing 79 | -------------------------------------------------------------------------------- /src/styles/result.module.css: -------------------------------------------------------------------------------- 1 | .cardContainer { 2 | height: 200px; 3 | margin-top: 50px; 4 | cursor: pointer; 5 | } 6 | 7 | .iconContainer { 8 | position: relative; 9 | width: 120px; 10 | height: 120px; 11 | } 12 | 13 | .icon { 14 | position: relative; 15 | top: 40px; 16 | left: 40px; 17 | border-radius: 50%; 18 | opacity: 1; 19 | z-index: 9; 20 | } 21 | 22 | .contentContainer { 23 | height: 100%; 24 | padding: 20px 0px 20px 90px; 25 | border-radius: 20px; 26 | box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.3); 27 | } 28 | 29 | .pathExpand { 30 | text-align: center; 31 | font-size: 30px; 32 | line-height: 150px; 33 | -moz-transition: all 0.2s linear; 34 | -webkit-transition: all 0.2s linear; 35 | transition: all 0.2s linear; 36 | } 37 | 38 | .expandRotate { 39 | -moz-transform: rotate(180deg); 40 | -webkit-transform: rotate(180deg); 41 | transform: rotate(180deg); 42 | } 43 | 44 | .footstepsContainer { 45 | display: flex; 46 | flex-wrap: nowrap; 47 | overflow-x: auto; 48 | margin: 10px 50px; 49 | opacity: 1; 50 | box-sizing: border-box; 51 | transition: height 200ms 0ms, opacity 200ms 200ms; 52 | height: 500px; 53 | } 54 | 55 | .footstepsContainer::-webkit-scrollbar { 56 | display: none; 57 | } 58 | 59 | .footstepsHide { 60 | transition: height 200ms 200ms, opacity 200ms 0ms; 61 | opacity: 0; 62 | height: 0px; 63 | } 64 | 65 | .pathDescription { 66 | position: relative; 67 | height: 160px; 68 | } 69 | 70 | .pathDescription h1 { 71 | font-weight: 600; 72 | } 73 | 74 | .pathInfo { 75 | width: 100%; 76 | display: flex; 77 | justify-content: space-between; 78 | position: absolute; 79 | bottom: 0px; 80 | } 81 | 82 | .pathInfo p { 83 | margin-bottom: 0; 84 | font-size: 16px; 85 | font-family: "Montserrat", sans-serif; 86 | } 87 | 88 | .pathInfo p span { 89 | font-weight: 700; 90 | } 91 | 92 | .mobCardContainer { 93 | display: none; 94 | } 95 | 96 | .resultPath { 97 | margin-top: 50px; 98 | } 99 | 100 | .resultPath h1 { 101 | font-weight: 700; 102 | } 103 | 104 | .resultFootsteps { 105 | margin-top: 80px; 106 | } 107 | 108 | .resultFootsteps h1 { 109 | font-weight: 700; 110 | } 111 | 112 | .noResults { 113 | margin: 80px 0; 114 | text-align: center; 115 | font-size: 30px; 116 | color: black; 117 | font-weight: 700; 118 | font-family: "Montserrat", sans-serif; 119 | } 120 | 121 | .filterCard { 122 | height: 50px; 123 | width: 220px; 124 | background: white; 125 | display: flex; 126 | justify-content: space-between; 127 | align-items: center; 128 | padding: 10px; 129 | border-radius: 5px; 130 | box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.2); 131 | margin-top: 30px; 132 | } 133 | 134 | .filterArrow { 135 | width: 20px; 136 | line-height: 50px; 137 | margin-right: 10px; 138 | transition: transform 0.2s; 139 | } 140 | 141 | .filterArrow:hover { 142 | cursor: pointer; 143 | transform: scale(1.2); 144 | } 145 | 146 | .filterArrowContainer { 147 | margin-top: 25px; 148 | } 149 | 150 | .filterCard h4 { 151 | margin-top: 5px; 152 | font-weight: 700; 153 | } 154 | 155 | .filterCard h4 span { 156 | font-weight: 400; 157 | } 158 | 159 | .filterCardFootsteps { 160 | height: 50px; 161 | width: 310px; 162 | background: white; 163 | display: flex; 164 | justify-content: space-between; 165 | padding: 10px; 166 | border-radius: 5px; 167 | box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.2); 168 | margin-top: 30px; 169 | margin-bottom: 30px; 170 | } 171 | 172 | .filterCardFootsteps h4 { 173 | line-height: 30px; 174 | font-weight: 700; 175 | } 176 | 177 | .filterCardFootsteps h4 span { 178 | font-weight: 500; 179 | margin-left: 10px; 180 | } 181 | 182 | @media only screen and (max-width: 768px) { 183 | .cardContainer { 184 | display: none; 185 | } 186 | 187 | .footstepsContainer { 188 | margin: 10px 0; 189 | } 190 | 191 | .mobCardContainer { 192 | display: block; 193 | border-radius: 20px; 194 | box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.3); 195 | padding: 20px; 196 | margin-top: 50px; 197 | } 198 | 199 | .mobCardContainer h1 { 200 | font-weight: 600; 201 | } 202 | 203 | .mobCardContainer h3 { 204 | font-size: 16px; 205 | } 206 | 207 | .mobInfoBy p { 208 | margin-bottom: 0; 209 | } 210 | 211 | .mobInfoVotes { 212 | text-align: right; 213 | margin-bottom: 0; 214 | } 215 | 216 | .mobCardContainer span { 217 | font-weight: 700; 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /src/styles/footsteps.module.css: -------------------------------------------------------------------------------- 1 | .footstepsContainer { 2 | display: flex; 3 | flex-wrap: nowrap; 4 | overflow-x: auto; 5 | margin: 10px 50px; 6 | opacity: 1; 7 | box-sizing: border-box; 8 | transition: height 200ms 0ms, opacity 200ms 200ms; 9 | height: 500px; 10 | } 11 | 12 | .footstepsContainer::-webkit-scrollbar { 13 | display: none; 14 | } 15 | 16 | .footstepsHide { 17 | transition: height 200ms 200ms, opacity 200ms 0ms; 18 | opacity: 0; 19 | height: 0px; 20 | } 21 | 22 | .cardContainer { 23 | flex: 0 0 auto; 24 | } 25 | 26 | .cardContainer { 27 | width: 250px; 28 | height: 380px; 29 | margin: 60px 20px 10px 20px; 30 | border-radius: 40px; 31 | box-shadow: 3px 3px 17px rgba(34, 34, 34, 0.11); 32 | } 33 | 34 | .icon { 35 | width: 80px; 36 | height: 80px; 37 | display: block; 38 | margin-left: auto; 39 | margin-right: auto; 40 | margin-top: -40px; 41 | margin-bottom: 10px; 42 | border-radius: 50%; 43 | box-shadow: 0px 3px 17px rgba(34, 34, 34, 0.11); 44 | } 45 | 46 | .title { 47 | font-size: 25px; 48 | font-weight: 700; 49 | text-align: left; 50 | padding-left: 10px; 51 | } 52 | 53 | .detailsContainer { 54 | padding: 0 10px; 55 | font-family: "Montserrat", sans-serif; 56 | height: 190px; 57 | } 58 | 59 | .description { 60 | padding: 10px 10px 10px 10px; 61 | text-align: justify; 62 | font-size: 15px; 63 | } 64 | 65 | .typeContainer { 66 | height: 23px; 67 | padding: 0 30px; 68 | } 69 | 70 | .doneButton { 71 | display: block; 72 | margin-right: 10px; 73 | float: right; 74 | background-color: #ffffff; 75 | padding-bottom: 2px; 76 | border-color: #ffffff; 77 | } 78 | 79 | .iconDone { 80 | padding-bottom: 5px; 81 | } 82 | .typeImage { 83 | width: 30px; 84 | height: 30px; 85 | margin-bottom: 0; 86 | } 87 | 88 | .typeTitle { 89 | line-height: 30px; 90 | text-align: center; 91 | font-family: "Montserrat", sans-serif; 92 | font-size: 16px; 93 | font-weight: 700; 94 | } 95 | 96 | .levelContainer { 97 | margin-top: 20px; 98 | padding: 0 30px; 99 | font-family: "Montserrat", sans-serif; 100 | } 101 | 102 | .levelTitle { 103 | font-size: 16px; 104 | font-weight: 700; 105 | } 106 | 107 | .levelType { 108 | text-align: right; 109 | font-size: 16px; 110 | } 111 | 112 | .resourceLink { 113 | padding: 0 30px; 114 | margin-top: 30px; 115 | } 116 | 117 | .resourceLink:hover { 118 | cursor: pointer; 119 | } 120 | 121 | .linkArrow { 122 | text-align: right; 123 | } 124 | 125 | .learningPathsContainer { 126 | margin-top: 50px; 127 | } 128 | 129 | .learningPathHeading { 130 | font-weight: 700; 131 | font-size: 38px; 132 | margin-bottom: 60px; 133 | } 134 | 135 | .learningPathEmpty img { 136 | width: 300px; 137 | opacity: 0.8; 138 | display: block; 139 | margin-left: auto; 140 | margin-right: auto; 141 | } 142 | 143 | .learningPathEmpty h1 { 144 | text-align: center; 145 | font-weight: 600; 146 | margin-bottom: 60px; 147 | } 148 | 149 | .learningPathCard { 150 | width: 95%; 151 | display: block; 152 | margin-left: auto; 153 | margin-right: auto; 154 | box-shadow: 3px 3px 17px rgba(34, 34, 34, 0.11); 155 | padding: 20px; 156 | margin: 0 30px 30px 30px; 157 | border-radius: 50px; 158 | cursor: pointer; 159 | } 160 | 161 | .editBtn { 162 | box-shadow: 1px 1px 17px rgba(34, 34, 34, 0.11); 163 | border-radius: 50%; 164 | display: inline-block; 165 | padding: 5px 8px; 166 | transform: scale(1.2); 167 | cursor: pointer; 168 | transition: transform 0.2s; 169 | } 170 | 171 | .editBtn:hover { 172 | transform: scale(1.4); 173 | } 174 | 175 | .pathIcon { 176 | margin-bottom: 0; 177 | width: 150px; 178 | } 179 | 180 | .pathExpand { 181 | text-align: center; 182 | font-size: 30px; 183 | line-height: 150px; 184 | -moz-transition: all 0.2s linear; 185 | -webkit-transition: all 0.2s linear; 186 | transition: all 0.2s linear; 187 | } 188 | 189 | .expandRotate { 190 | -moz-transform: rotate(180deg); 191 | -webkit-transform: rotate(180deg); 192 | transform: rotate(180deg); 193 | } 194 | 195 | .pathHeading { 196 | padding: 10px; 197 | } 198 | 199 | .timelineDot { 200 | display: block; 201 | width: 10px; 202 | height: 10px; 203 | background: #029843; 204 | border-radius: 50%; 205 | margin-left: auto; 206 | margin-right: auto; 207 | margin-top: 40px; 208 | } 209 | 210 | .timelineBox { 211 | width: 260px; 212 | background: #029843; 213 | height: 5px; 214 | position: relative; 215 | top: -7.5px; 216 | left: 140px; 217 | } 218 | 219 | @media only screen and (max-width: 768px) { 220 | .footstepsContainer { 221 | margin: 10px 0; 222 | } 223 | 224 | .pathIcon { 225 | margin-top: 40px; 226 | } 227 | 228 | .learningPathHeading { 229 | font-size: 30px; 230 | } 231 | 232 | .learningPathCard { 233 | margin: 15px 0 30px 0; 234 | padding: 0 10px; 235 | width: 100%; 236 | } 237 | 238 | .pathIcon { 239 | display: block; 240 | margin-left: auto; 241 | margin-right: auto; 242 | margin-bottom: 20px; 243 | } 244 | 245 | .pathExpand { 246 | line-height: 90px; 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /src/components/Search/searchResult.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react" 2 | import { Row, Select } from "antd" 3 | 4 | import ResultPathCard from "./resultPathCard" 5 | import ResultFootstepCard from "./resultFootstepCard" 6 | import styles from "../../styles/result.module.css" 7 | 8 | const { Option } = Select 9 | 10 | export class SearchResult extends Component { 11 | state = { 12 | currPaths: [], 13 | currFootsteps: [], 14 | } 15 | 16 | componentDidMount() { 17 | this.setState({ 18 | currFootsteps: this.props.footsteps, 19 | currPaths: this.props.paths, 20 | }) 21 | } 22 | 23 | sortPathsAscending = () => { 24 | let { currPaths } = this.state 25 | 26 | let sortedArray = currPaths.sort((a, b) => { 27 | return ( 28 | a.votes_aggregate.aggregate.count - b.votes_aggregate.aggregate.count 29 | ) 30 | }) 31 | 32 | this.setState({ 33 | currPaths: sortedArray, 34 | }) 35 | } 36 | 37 | sortPathsDescending = () => { 38 | let { currPaths } = this.state 39 | 40 | let sortedArray = currPaths.sort((a, b) => { 41 | return ( 42 | a.votes_aggregate.aggregate.count + b.votes_aggregate.aggregate.count 43 | ) 44 | }) 45 | 46 | this.setState({ 47 | currPaths: sortedArray, 48 | }) 49 | } 50 | 51 | onSelectChange = (value) => { 52 | if (value === "a") { 53 | this.setState({ 54 | currFootsteps: this.props.footsteps, 55 | }) 56 | } else if (value === "0") { 57 | let tempArray = this.props.footsteps.filter( 58 | (footstep) => footstep.level === 0 59 | ) 60 | this.setState({ currFootsteps: tempArray }) 61 | } else if (value === "1") { 62 | let tempArray = this.props.footsteps.filter( 63 | (footstep) => footstep.level === 1 64 | ) 65 | this.setState({ currFootsteps: tempArray }) 66 | } else if (value === "2") { 67 | let tempArray = this.props.footsteps.filter( 68 | (footstep) => footstep.level === 2 69 | ) 70 | this.setState({ currFootsteps: tempArray }) 71 | } 72 | } 73 | 74 | render() { 75 | return ( 76 |
77 |
78 |

Learning Paths ({this.props.paths.length})

79 | 80 | {this.props.paths.length !== 0 ? ( 81 |
82 |
83 |

84 | Sort by: Votes 85 |

86 |
87 | this.sortPathsAscending} 92 | />{" "} 93 | this.sortPathsDescending} 98 | />{" "} 99 |
100 |
101 | {this.state.currPaths.map((path, index) => { 102 | return 103 | })} 104 |
105 | ) : ( 106 |
No Paths Found
107 | )} 108 |
109 | 110 |
111 |

Footsteps ({this.props.footsteps.length})

112 | 113 | {this.props.footsteps.length !== 0 ? ( 114 |
115 |
116 |

117 | Filter By: Level 118 |

119 | 131 |
132 | {this.state.currFootsteps.length !== 0 ? ( 133 |
134 | {this.state.currFootsteps.map((footstep, index) => { 135 | return 136 | })} 137 |
138 | ) : ( 139 |
No Footsteps Found
140 | )} 141 |
142 | ) : ( 143 |
No Footsteps Found
144 | )} 145 |
146 |
147 |
148 | ) 149 | } 150 | } 151 | 152 | export default SearchResult 153 | -------------------------------------------------------------------------------- /src/styles/add.module.css: -------------------------------------------------------------------------------- 1 | #switch .ant-switch-checked { 2 | background-color: #029843; 3 | } 4 | 5 | .container { 6 | margin: 0 100px; 7 | } 8 | 9 | .heading { 10 | text-align: center; 11 | font-weight: 700; 12 | margin: 20px; 13 | font-size: 40px; 14 | margin-bottom: 60px; 15 | } 16 | 17 | .input { 18 | border: none; 19 | border-bottom: 1px solid #868686; 20 | font-size: 20px; 21 | font-family: "Montserrat", sans-serif; 22 | color: black; 23 | width: 400px; 24 | } 25 | 26 | input:focus, 27 | select:focus, 28 | textarea:focus { 29 | outline: none; 30 | } 31 | 32 | .checkbox_input { 33 | padding: 10px; 34 | font-size: 30px; 35 | font-weight: 600; 36 | margin-top: 15px; 37 | font-family: "Montserrat", sans-serif; 38 | color: #029843; 39 | } 40 | 41 | .input_label { 42 | font-weight: 600; 43 | margin-top: 35px; 44 | font-family: "Montserrat", sans-serif; 45 | color: #029843; 46 | } 47 | 48 | .icon_input_container { 49 | display: block; 50 | margin-left: auto; 51 | margin-right: auto; 52 | width: 400px; 53 | } 54 | 55 | .icon_container { 56 | width: 150px; 57 | height: 150px; 58 | border-radius: 50%; 59 | display: block; 60 | margin-left: auto; 61 | margin-right: auto; 62 | box-shadow: 0px 3px 5px rgba(0, 0, 0, 0.3); 63 | } 64 | 65 | .icon_container img { 66 | border-radius: 50%; 67 | width: 150px; 68 | height: 150px; 69 | } 70 | 71 | .add_image_btn { 72 | background: #029843; 73 | font-family: "Montserrat"; 74 | color: white; 75 | font-weight: 600; 76 | padding: 5px 10px; 77 | margin: 20px 0 0 20px; 78 | display: inline-block; 79 | border-radius: 20px; 80 | cursor: pointer; 81 | } 82 | 83 | @media only screen and (max-width: 768px) { 84 | .container { 85 | margin: 0 30px; 86 | } 87 | 88 | .heading { 89 | margin-bottom: 30px; 90 | } 91 | 92 | .input { 93 | width: 100%; 94 | } 95 | 96 | .icon_container { 97 | width: 120px; 98 | height: 120px; 99 | margin-top: 50px; 100 | } 101 | 102 | .icon_container img { 103 | width: 120px; 104 | height: 120px; 105 | } 106 | 107 | .icon_input_container { 108 | width: 100%; 109 | } 110 | } 111 | 112 | .footsteps_container { 113 | margin-top: 50px; 114 | } 115 | 116 | .footsteps_heading { 117 | font-weight: 600; 118 | font-size: 30px; 119 | margin-top: 100px; 120 | } 121 | 122 | .footsteps_counter_container { 123 | height: 50px; 124 | width: 150px; 125 | background: white; 126 | display: flex; 127 | justify-content: space-between; 128 | padding: 10px; 129 | border-radius: 5px; 130 | box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.2); 131 | margin-top: 30px; 132 | margin-bottom: 30px; 133 | } 134 | 135 | .footsteps_counter_input { 136 | width: 60px; 137 | } 138 | 139 | .footsteps_counter_container h4 { 140 | line-height: 32px; 141 | font-weight: 600; 142 | } 143 | 144 | /* Add Footsteps */ 145 | 146 | .footstep_container { 147 | padding: 30px 60px 40px 60px; 148 | border-radius: 40px; 149 | box-shadow: 0px 3px 10px rgba(0, 0, 0, 0.3); 150 | margin: 50px 0; 151 | } 152 | 153 | .footstep_count { 154 | position: absolute; 155 | top: 0; 156 | right: 0; 157 | font-size: 30px; 158 | font-weight: 700; 159 | } 160 | .footsteps_new_container { 161 | display: flex; 162 | justify-content: center; 163 | align-items: center; 164 | border: 4px #545353 dashed; 165 | padding: 50px 0; 166 | margin: 50px; 167 | } 168 | 169 | .footsteps_new { 170 | display: inline-block; 171 | background: #029843; 172 | padding: 10px 30px; 173 | font-family: "Montserrat", sans-serif; 174 | border-radius: 24px; 175 | color: white; 176 | font-weight: 700; 177 | font-size: 16px; 178 | transition: transform 0.2s; 179 | text-align: center; 180 | } 181 | 182 | .footsteps_new:hover { 183 | transform: scale(1.1); 184 | cursor: pointer; 185 | } 186 | 187 | .footstep_remove_btn { 188 | text-align: right; 189 | font-weight: 700; 190 | } 191 | 192 | .footstep_remove_btn:hover { 193 | cursor: pointer; 194 | } 195 | 196 | .footstep_counter { 197 | font-size: 25px; 198 | font-weight: 700; 199 | font-family: "Montserrat", sans-serif; 200 | } 201 | 202 | .path_submit_container { 203 | display: flex; 204 | justify-content: center; 205 | align-items: center; 206 | margin: 30px 0 80px 0; 207 | } 208 | 209 | .path_submit { 210 | background: #029843; 211 | padding: 10px 30px; 212 | font-family: "Montserrat", sans-serif; 213 | border-radius: 24px; 214 | color: white; 215 | font-weight: 700; 216 | font-size: 20px; 217 | transition: transform 0.2s; 218 | text-align: center; 219 | width: 40%; 220 | } 221 | 222 | .path_submit:hover { 223 | transform: scale(1.1); 224 | cursor: pointer; 225 | } 226 | 227 | .error_message { 228 | text-align: center; 229 | color: #fe5e44; 230 | font-family: "Montserrat", sans-serif; 231 | font-weight: 600; 232 | font-size: 18px; 233 | } 234 | 235 | @media only screen and (max-width: 768px) { 236 | .footstep_container { 237 | padding: 30px 20px; 238 | } 239 | 240 | .footsteps_new_container { 241 | margin: 50px 0; 242 | padding: 50px 10px; 243 | } 244 | 245 | .path_submit { 246 | width: 100%; 247 | } 248 | } 249 | 250 | /* Edit Path Styles */ 251 | 252 | .deletePath { 253 | background: #fe5e44; 254 | color: white; 255 | font-size: 18px; 256 | font-weight: 600; 257 | display: inline-block; 258 | padding: 5px 20px; 259 | border-radius: 20px; 260 | font-family: "Montserrat", sans-serif; 261 | transition: transform 0.2s; 262 | cursor: pointer; 263 | } 264 | 265 | .deletePath:hover { 266 | color: white; 267 | transform: scale(1.05); 268 | } 269 | 270 | @media only screen and (max-width: 768px) { 271 | .deletePath { 272 | margin: 30px 0 10px 0; 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /src/styles/signUp.module.css: -------------------------------------------------------------------------------- 1 | .step0_container { 2 | margin: 0px 50px; 3 | } 4 | 5 | .step0_img { 6 | width: 70%; 7 | display: block; 8 | margin-left: auto; 9 | margin-right: auto; 10 | margin-top: 60px; 11 | } 12 | 13 | .step0_content { 14 | padding: 50px 0; 15 | } 16 | 17 | .step0_content h1 { 18 | font-weight: 700; 19 | font-size: 45px; 20 | } 21 | 22 | .step0_content h2 { 23 | font-size: 36px; 24 | font-weight: 600; 25 | } 26 | 27 | .step0_content h3 { 28 | margin-top: 80px; 29 | } 30 | 31 | .step0_content div { 32 | background: #029843; 33 | color: white; 34 | font-weight: 700; 35 | font-size: 18px; 36 | display: inline-block; 37 | padding: 10px 20px; 38 | border-radius: 25px; 39 | float: right; 40 | margin-top: 30px; 41 | margin-right: 20px; 42 | transition: transform 0.2s; 43 | font-family: "Montserrat", sans-serif; 44 | } 45 | 46 | .step0_content div:hover { 47 | cursor: pointer; 48 | transform: scale(1.05); 49 | } 50 | 51 | /* ! Step 1 */ 52 | 53 | .input { 54 | border: none; 55 | border-bottom: 1px solid #868686; 56 | font-size: 20px; 57 | font-family: "Montserrat", sans-serif; 58 | color: black; 59 | width: 400px; 60 | } 61 | 62 | input:focus, 63 | select:focus, 64 | textarea:focus { 65 | outline: none; 66 | } 67 | 68 | .input_label { 69 | font-weight: 600; 70 | margin-top: 35px; 71 | font-family: "Montserrat", sans-serif; 72 | color: #029843; 73 | } 74 | 75 | .step1_container { 76 | margin: 0 100px; 77 | margin-bottom: 50px; 78 | } 79 | 80 | .steps_content { 81 | padding-top: 30px; 82 | } 83 | 84 | .steps_content h1 { 85 | font-size: 100px; 86 | font-weight: 700; 87 | display: inline-block; 88 | color: #029843; 89 | } 90 | 91 | .steps_content h2 { 92 | display: inline-block; 93 | margin-left: 10px; 94 | margin-bottom: 120px; 95 | opacity: 0.5; 96 | } 97 | 98 | .steps_content h3 { 99 | font-size: 20px; 100 | font-weight: 600; 101 | opacity: 0.5; 102 | } 103 | 104 | .username_error { 105 | color: #fe5e44; 106 | font-weight: 600; 107 | font-size: 14px; 108 | margin-top: 10px; 109 | font-family: "Montserrat", sans-serif; 110 | } 111 | 112 | .stepBtn { 113 | background: #029843; 114 | color: white; 115 | font-weight: 700; 116 | font-size: 18px; 117 | display: inline-block; 118 | padding: 10px 20px; 119 | border-radius: 25px; 120 | margin-top: 30px; 121 | margin-right: 20px; 122 | transition: transform 0.2s; 123 | font-family: "Montserrat", sans-serif; 124 | margin-bottom: 30px; 125 | } 126 | 127 | .stepBtn:hover { 128 | cursor: pointer; 129 | transform: scale(1.1); 130 | } 131 | 132 | .step_error { 133 | color: #fe5e44; 134 | font-weight: 600; 135 | font-size: 14px; 136 | margin-top: 10px; 137 | font-family: "Montserrat", sans-serif; 138 | } 139 | 140 | .step_heading { 141 | font-size: 50px; 142 | font-weight: 600; 143 | } 144 | 145 | .step2_container { 146 | margin: 0 100px; 147 | } 148 | 149 | .step3_container { 150 | margin: 0 100px; 151 | } 152 | 153 | .preview_container { 154 | margin: 0 100px; 155 | } 156 | 157 | .preview_heading { 158 | text-align: center; 159 | font-weight: 700; 160 | font-size: 50px; 161 | margin-top: 80px; 162 | margin-bottom: 100px; 163 | } 164 | 165 | .preview_btn { 166 | display: inline-block; 167 | font-size: 20px; 168 | font-weight: 600; 169 | padding: 5px 20px; 170 | border-radius: 20px; 171 | color: white; 172 | font-family: "Montserrat", sans-serif; 173 | margin: 0 50px; 174 | transition: transform 0.2s; 175 | } 176 | 177 | .preview_btn:hover { 178 | cursor: pointer; 179 | transform: scale(1.1); 180 | } 181 | 182 | .no_btn { 183 | background: #fe5e44; 184 | } 185 | 186 | .yes_btn { 187 | background: #029843; 188 | float: right; 189 | } 190 | 191 | .input_label_comment { 192 | font-size: 13px; 193 | color: #868686; 194 | } 195 | 196 | @media only screen and (max-width: 768px) { 197 | .step0_img { 198 | width: 90%; 199 | margin-top: 40px; 200 | } 201 | .step0_container { 202 | margin: 0px 0px; 203 | } 204 | .step0_content { 205 | padding: 20px; 206 | margin-bottom: 50px; 207 | } 208 | .step0_content h1 { 209 | font-size: 40px; 210 | } 211 | .step0_content h2 { 212 | font-size: 32px; 213 | } 214 | .step0_content h3 { 215 | margin-top: 40px; 216 | } 217 | .step1_container { 218 | margin: 0 20px; 219 | margin-bottom: 50px; 220 | } 221 | .step2_container { 222 | margin: 0 20px; 223 | margin-bottom: 50px; 224 | } 225 | .step3_container { 226 | margin: 0 20px; 227 | margin-bottom: 50px; 228 | } 229 | .preview_container { 230 | margin: 0 20px; 231 | margin-bottom: 50px; 232 | } 233 | .input { 234 | width: 90%; 235 | } 236 | .steps_content h2 { 237 | margin-bottom: 30px; 238 | } 239 | .steps_content { 240 | margin-bottom: 70px; 241 | } 242 | .preview_heading { 243 | font-size: 45px; 244 | margin-top: 60px; 245 | } 246 | .preview_btn { 247 | display: block; 248 | text-align: center; 249 | margin: 0 40px; 250 | } 251 | .yes_btn { 252 | float: none; 253 | margin-top: 40px; 254 | } 255 | } 256 | 257 | .errorshake { 258 | border: none; 259 | border-bottom: 1px solid red; 260 | font-size: 20px; 261 | font-family: "Montserrat", sans-serif; 262 | color: blue; 263 | width: 400px; 264 | animation: shake 0.5s; 265 | animation-iteration-count: 3s; 266 | } 267 | 268 | @keyframes shake { 269 | 0% { 270 | transform: translate(1px, 1px) rotate(0deg); 271 | } 272 | 10% { 273 | transform: translate(-1px, -2px) rotate(-1deg); 274 | } 275 | 20% { 276 | transform: translate(-3px, 0px) rotate(1deg); 277 | } 278 | 30% { 279 | transform: translate(3px, 2px) rotate(0deg); 280 | } 281 | 40% { 282 | transform: translate(1px, -1px) rotate(1deg); 283 | } 284 | 50% { 285 | transform: translate(-1px, 2px) rotate(-1deg); 286 | } 287 | 60% { 288 | transform: translate(-3px, 1px) rotate(0deg); 289 | } 290 | 70% { 291 | transform: translate(3px, 1px) rotate(-1deg); 292 | } 293 | 80% { 294 | transform: translate(-1px, -1px) rotate(1deg); 295 | } 296 | 90% { 297 | transform: translate(1px, 2px) rotate(0deg); 298 | } 299 | 100% { 300 | transform: translate(1px, -2px) rotate(-1deg); 301 | } 302 | } 303 | -------------------------------------------------------------------------------- /src/components/Layout/layout.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react" 2 | import { Helmet } from "react-helmet" 3 | import gql from "graphql-tag" 4 | import { navigate } from "gatsby" 5 | 6 | import firebase from "firebase/app" 7 | import "firebase/auth" 8 | import firebaseConfig from "../../firebase/config" 9 | 10 | import styles from "../../styles/layout.module.css" 11 | import "antd/dist/antd.css" 12 | 13 | import Header from "./sideDrawer" 14 | import Footer from "./footer" 15 | import Login from "../Login/loginFlow" 16 | import Loader from "./loader" 17 | import SignUp from "../Login/signUpFlow" 18 | 19 | import SEO from "../SEO" 20 | 21 | import { client } from "../../apollo/client" 22 | 23 | import UserContext from "../../context/userContext" 24 | 25 | export class layout extends Component { 26 | state = { 27 | isSignedIn: null, 28 | userId: "", 29 | user: {}, 30 | signUp: false, 31 | } 32 | 33 | componentDidMount() { 34 | if (!firebase.apps.length) { 35 | firebase.initializeApp(firebaseConfig) 36 | } 37 | 38 | this.unregisterAuthObserver = firebase.auth().onAuthStateChanged((user) => { 39 | if (user) { 40 | client 41 | .query({ 42 | query: USER_EMAIL_QUERY_APOLLO, 43 | variables: { 44 | email: firebase.auth().currentUser.email, 45 | }, 46 | }) 47 | .then((response) => { 48 | if (response.data.Users.length === 0) { 49 | this.setState({ isSignedIn: false, signUp: true }) 50 | } else { 51 | this.setState({ 52 | isSignedIn: true, 53 | userId: response.data.Users[0].id, 54 | user: response.data.Users[0], 55 | }) 56 | if (typeof window !== "undefined") { 57 | localStorage.setItem("userId", response.data.Users[0].id) 58 | } 59 | } 60 | }) 61 | } else { 62 | this.setState({ isSignedIn: false }) 63 | } 64 | }) 65 | } 66 | 67 | updateUser = (newUser) => { 68 | this.setState({ 69 | user: newUser, 70 | }) 71 | } 72 | 73 | componentWillUnmount() { 74 | this.unregisterAuthObserver() 75 | } 76 | 77 | updateUserId = (userId, isSignedIn) => { 78 | navigate("/") 79 | if (typeof window !== "undefined") { 80 | window.location.reload() 81 | localStorage.setItem("userId", userId) 82 | } 83 | this.setState({ userId: userId, isSignedIn: isSignedIn }) 84 | } 85 | 86 | render() { 87 | return ( 88 | <> 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 101 | 116 | 117 | 118 |
119 | {this.state.isSignedIn === null ? ( 120 |
121 |
122 | 123 |
124 | ) : this.state.isSignedIn === false ? ( 125 | <> 126 |
127 |
128 | {!this.state.signUp ? ( 129 | 130 | ) : ( 131 | 132 | )} 133 |
134 |
135 | 136 | ) : ( 137 | 140 |
141 |
142 |
{this.props.children}
143 |
144 |
145 | )} 146 |
147 | 148 | ) 149 | } 150 | } 151 | 152 | export default layout 153 | 154 | export const USER_EMAIL_QUERY_APOLLO = gql` 155 | query getUser($email: String!) { 156 | Users(where: { email: { _like: $email } }) { 157 | id 158 | first_name 159 | last_name 160 | username 161 | skills 162 | profile_pic 163 | following_aggregate { 164 | aggregate { 165 | count 166 | } 167 | } 168 | following { 169 | follower_info { 170 | username 171 | first_name 172 | last_name 173 | profile_pic 174 | id 175 | } 176 | } 177 | followers_aggregate { 178 | aggregate { 179 | count 180 | } 181 | } 182 | followers { 183 | follow_info { 184 | username 185 | profile_pic 186 | first_name 187 | last_name 188 | id 189 | } 190 | } 191 | about 192 | bio 193 | linkedin 194 | github 195 | facebook 196 | twitter 197 | learning_paths { 198 | id 199 | title 200 | description 201 | icon 202 | votes_aggregate { 203 | aggregate { 204 | count 205 | } 206 | } 207 | footsteps { 208 | id 209 | title 210 | resource_icon 211 | resource_url 212 | resource_type 213 | level 214 | description 215 | } 216 | } 217 | learning_paths_aggregate { 218 | aggregate { 219 | count 220 | } 221 | } 222 | } 223 | } 224 | ` 225 | -------------------------------------------------------------------------------- /src/styles/user.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | width: 90%; 3 | display: block; 4 | margin-left: auto; 5 | margin-right: auto; 6 | font-family: "Montserrat", sans-serif; 7 | } 8 | 9 | .menu { 10 | text-align: center; 11 | margin-top: 50px; 12 | } 13 | 14 | .menuItem { 15 | font-size: 20px; 16 | font-family: "Montserrat", sans-serif; 17 | font-weight: 700; 18 | transition: transform 0.2s; 19 | padding-bottom: 10px; 20 | max-width: 160px; 21 | margin-right: 30px; 22 | } 23 | 24 | .menuItem:hover { 25 | transform: scale(1.1); 26 | cursor: pointer; 27 | } 28 | 29 | .active { 30 | border-bottom: 2px #3f3f3f solid; 31 | } 32 | 33 | .followContainer { 34 | width: 90%; 35 | display: block; 36 | margin-left: auto; 37 | margin-right: auto; 38 | margin-top: 40px; 39 | } 40 | 41 | .userContainer { 42 | padding: 20px; 43 | height: 250px; 44 | } 45 | 46 | .userImg { 47 | border-radius: 50%; 48 | } 49 | 50 | .userName { 51 | text-align: center; 52 | font-weight: 700; 53 | } 54 | 55 | @media only screen and (max-width: 768px) { 56 | .menuItem { 57 | font-size: 14px; 58 | margin-right: 20px; 59 | width: auto; 60 | } 61 | 62 | .userContainer { 63 | padding: 10px; 64 | } 65 | 66 | .followContainer { 67 | width: 100%; 68 | } 69 | } 70 | 71 | /* Profile Card Styles */ 72 | 73 | .profileContainer { 74 | margin-top: 50px; 75 | } 76 | 77 | .profileDetail { 78 | width: 100%; 79 | position: relative; 80 | top: -290px; 81 | border-radius: 50px; 82 | box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.3); 83 | } 84 | 85 | .profileLearningPath h1 { 86 | font-weight: 700; 87 | padding: 30px; 88 | margin-top: 20px; 89 | width: 100%; 90 | position: relative; 91 | top: -290px; 92 | } 93 | 94 | .profilePath { 95 | width: 100%; 96 | position: relative; 97 | top: -290px; 98 | border-radius: 50px; 99 | } 100 | 101 | .profileImgContainer { 102 | position: relative; 103 | top: -125px; 104 | left: 50px; 105 | z-index: 10; 106 | width: 250px; 107 | } 108 | 109 | .profileImg { 110 | border-radius: 10px; 111 | width: 250px; 112 | } 113 | 114 | .profileInfo { 115 | padding: 0 0 0 30px; 116 | margin-left: 300px; 117 | } 118 | 119 | .profileInfo h1 { 120 | font-weight: 700; 121 | margin-bottom: 0; 122 | font-size: 40px; 123 | } 124 | 125 | .profileInfo h2 { 126 | font-size: 18px; 127 | font-weight: 600; 128 | color: #767676; 129 | margin-bottom: 15px; 130 | } 131 | 132 | .profileAbout { 133 | margin-left: 300px; 134 | padding: 20px 20px 0 30px; 135 | min-height: 180px; 136 | } 137 | 138 | .profileAlign { 139 | display: inline-block; 140 | } 141 | 142 | .profileLink { 143 | display: inline-block; 144 | margin-left: 100px; 145 | padding: 20px; 146 | background-color: #e7e7e7; 147 | color: black; 148 | float: right; 149 | border-radius: 50%; 150 | border: 2px solid black; 151 | } 152 | 153 | .skills { 154 | margin-top: 10px; 155 | } 156 | 157 | .skills h1 { 158 | font-size: 25px; 159 | margin-bottom: 20px; 160 | } 161 | 162 | .skill { 163 | display: inline-block; 164 | padding: 0 10px; 165 | border: 1px black solid; 166 | border-radius: 20px; 167 | color: black; 168 | margin: 0 10px 0 0; 169 | font-size: 10pt; 170 | margin-bottom: 6px; 171 | } 172 | 173 | .social { 174 | text-align: center; 175 | margin-top: 0px; 176 | min-height: 25px; 177 | } 178 | 179 | .social_row { 180 | display: flex; 181 | justify-content: center; 182 | align-content: center; 183 | } 184 | 185 | .icon { 186 | font-size: 22px; 187 | } 188 | 189 | .userInfo { 190 | padding: 20px; 191 | font-family: "Montserrat", sans-serif; 192 | } 193 | 194 | .count { 195 | text-align: center; 196 | font-size: 35px; 197 | font-weight: 700; 198 | } 199 | 200 | .text { 201 | margin-left: 10px; 202 | font-weight: 500; 203 | font-size: 20px; 204 | } 205 | 206 | .bioContent { 207 | font-size: 16px; 208 | } 209 | 210 | .menuContainer { 211 | margin-top: -260px; 212 | width: 100%; 213 | display: inline-block; 214 | margin-bottom: 50px; 215 | } 216 | 217 | .mobileProfile { 218 | display: none; 219 | } 220 | 221 | @media only screen and (max-width: 768px) { 222 | .mobileProfile { 223 | display: block; 224 | } 225 | 226 | .desktopProfile { 227 | display: none; 228 | } 229 | 230 | .menuContainer { 231 | margin-top: 20px; 232 | } 233 | 234 | .profile { 235 | padding: 40px; 236 | } 237 | 238 | .mobileProfile { 239 | margin-top: 80px; 240 | } 241 | 242 | .profileContainer { 243 | border-radius: 40px; 244 | padding: 30px 0; 245 | box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.3); 246 | } 247 | .mobileProfileLearningPath h1 { 248 | font-weight: 600; 249 | text-align: center; 250 | padding: 10px; 251 | width: 100%; 252 | } 253 | .avatar { 254 | border-radius: 20px; 255 | width: 150px; 256 | height: 150px; 257 | object-fit: cover; 258 | display: block; 259 | margin-left: auto; 260 | margin-right: auto; 261 | margin-bottom: 20px; 262 | margin-top: -105px; 263 | } 264 | 265 | .userDetails { 266 | text-align: center; 267 | } 268 | 269 | .userDetails h2 { 270 | margin: 5px; 271 | padding: 0; 272 | font-size: 18pt; 273 | font-weight: 600; 274 | color: #3f3f3f; 275 | } 276 | 277 | .userDetails h4 { 278 | margin: 3px; 279 | padding: 0; 280 | font-size: 11pt; 281 | color: #aaa; 282 | font-weight: 400; 283 | } 284 | 285 | .count { 286 | font-size: 25px; 287 | font-weight: 700; 288 | text-align: center; 289 | } 290 | 291 | .text { 292 | font-size: 16px; 293 | text-align: center; 294 | } 295 | 296 | .userInfo { 297 | margin-top: 15px; 298 | font-family: "Montserrat", sans-serif; 299 | } 300 | 301 | .bio { 302 | margin-top: 20px; 303 | padding: 0 20px; 304 | text-align: justify; 305 | font-family: "Montserrat", sans-serif; 306 | } 307 | 308 | .social { 309 | text-align: center; 310 | margin-top: 50px; 311 | } 312 | 313 | .icon { 314 | font-size: 30px; 315 | transition: transform 0.2s; 316 | } 317 | 318 | .icon:hover { 319 | transform: scale(1.4); 320 | } 321 | 322 | .skills { 323 | margin-top: 30px; 324 | padding: 0 20px; 325 | } 326 | 327 | .skills h2 { 328 | text-align: center; 329 | font-size: 15pt; 330 | font-weight: 600; 331 | color: #3f3f3f; 332 | margin-bottom: 20px; 333 | } 334 | 335 | .skill { 336 | display: inline-block; 337 | padding: 0 10px; 338 | border: 1px black solid; 339 | border-radius: 20px; 340 | color: black; 341 | margin: 0 5px; 342 | font-size: 10pt; 343 | margin-bottom: 6px; 344 | } 345 | } 346 | -------------------------------------------------------------------------------- /src/images/roadSign.svg: -------------------------------------------------------------------------------- 1 | road sign -------------------------------------------------------------------------------- /src/components/Login/loginFlow.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react" 2 | import { Row, Col, Card } from "antd" 3 | import firebase from "firebase/app" 4 | import "firebase/auth" 5 | import Image from "../image" 6 | import styles from "../../styles/login.module.css" 7 | import StyledFirebaseAuth from "react-firebaseui/StyledFirebaseAuth" 8 | 9 | const uiConfig = { 10 | signInFlow: "popup", 11 | signInOptions: [ 12 | firebase.auth.EmailAuthProvider.PROVIDER_ID, 13 | firebase.auth.GoogleAuthProvider.PROVIDER_ID, 14 | firebase.auth.GithubAuthProvider.PROVIDER_ID, 15 | ], 16 | callbacks: { 17 | // Ignore signInSuccessUrl 18 | signInSuccessWithAuthResult: () => false, 19 | }, 20 | } 21 | export class login extends Component { 22 | render() { 23 | return ( 24 |
25 | 26 |

27 | Welcome to Footsteps 28 |

29 |
30 | 31 |

Search 🔎 community-made 🧑‍🤝‍🧑learning paths here!

32 |
33 | 34 | 35 | 36 | 37 | 38 |
39 |

40 | A journey of a thousand miles
41 | begins with a single footstep 42 |

43 |

44 | Start your learning journey today 45 |

46 |
47 | 51 |
52 | 53 |
54 | 55 | {/* About footsteps */} 56 |
57 | 58 | 59 | 60 | 61 | 62 |

63 | About Footsteps
64 |

65 | In today's era, a new knowledge is a few mouse clicks away. As the 66 | internet is flooded with a plethora of resources, it raises a new 67 | problem for a modern-day learner. It becomes difficult for him/her 68 | to choose the right resources suited for his/her needs. Footsteps 69 | is here to tackle this issue 70 |
71 |
Footsteps is a search engine of community-made 🧑‍🤝‍🧑learning 72 | resources for the 21st-century learner. Now, you can learn by 73 | following the footsteps (journey) of experts 74 | 75 |
76 |
77 | 78 | {/* How footsteps work */} 79 |
80 | 81 | 82 |

How footsteps work?

83 |

84 | The internet is filled with a huge amount of resources and hence 85 | it becomes difficult for a person to find the right resource 86 | suited for his/her requirement. With footsteps, you can see the 87 | exact path followed by an expert to reach his/her current 88 | position. You can see the resources and materials that they had 89 | referred in a chronological order. 90 |
91 |
92 | Furthermore, footsteps can also be used as a personal progress 93 | tracker for an individual. It would help him/her keep track of 94 | the skills that he/she has acquired over time 95 |

96 | 97 | 98 | 99 | 100 |
101 |
102 |
103 | 104 | 105 | 106 | With the Footsteps app, we take a mentor/subject knowledge 107 | expert first approach to solve the problem. The domain 108 | experts save their learning journey with our app & make it 109 | available for everyone else to follow 110 | 111 | 112 | 113 | 114 | Every resource is called a 'footstep' and a collection of 115 | footsteps makes up a learning path. The learners can search 116 | for their topic of interest in the search bar and most 117 | recommended path to learn that topic shows up in the result 118 | 119 | 120 | 121 | 122 | The learner can "fork" the learning path made by the expert 123 | and keep a track of his progress. The app suggests resources 124 | and keeps an updated list with users upvotes & collaborative 125 | filtering. 126 | 127 | 128 | 129 |
130 |
131 |
132 | 133 | {/* FAQ Section */} 134 |
135 | 136 | 137 | 138 | 139 | 140 |

141 | FAQ
142 |

143 | Is footsteps a free/paid app ? 144 |
145 | Footsteps is completely free 146 |
147 |
148 | I am a beginner. Can I use footsteps ? 149 |
150 | Yes. Footsteps is benefital to people in all level (Beginners, 151 | Intermediate and Advanced). It is also helpful for people who are 152 | thinking of switching to a different tech stack 153 |
154 |
155 | Is footsteps avaliable offline ? 156 |
157 | As off now footsteps is not avaiable offline 158 |
159 |
160 | Other than footsteps web, what are the other options ? 161 |
162 | In addition to the website, footsteps also provides an option for 163 | a from extension and a flutter app 164 |
165 |
166 | 167 | I liked the site and would love to contribute to it. How can I 168 | get started ? 169 | 170 |
171 | Footsteps has a vibrant community of developers. To get started 172 | visit{" "} 173 | 174 | footsteps github 175 | 176 |
177 |
178 | 179 |
180 |
181 |
182 | ) 183 | } 184 | } 185 | 186 | export default login 187 | -------------------------------------------------------------------------------- /src/components/Create/addFootstep.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react" 2 | import { CloudUploadOutlined } from "@ant-design/icons" 3 | import { Row, Col, Select } from "antd" 4 | import FileUploader from "react-firebase-file-uploader" 5 | import firebase from "firebase/app" 6 | import "firebase/storage" 7 | 8 | import styles from "../../styles/add.module.css" 9 | 10 | const { Option } = Select 11 | 12 | export class addFootstep extends Component { 13 | state = { 14 | title: "", 15 | description: "", 16 | type: "Website", 17 | url: "", 18 | level: 0, 19 | icon: "", 20 | icon_url: "https://i.imgur.com/frwNB0V.png", 21 | isUploading: false, 22 | progress: 0, 23 | } 24 | 25 | updateFootstepArray = () => { 26 | let newFootstepContent = { 27 | id: this.props.data.id, 28 | title: this.state.title, 29 | description: this.state.description, 30 | resource_type: this.state.type, 31 | resource_url: this.state.url, 32 | learning_path: this.props.pathId, 33 | level: this.state.level, 34 | resource_icon: this.state.icon_url, 35 | } 36 | 37 | this.props.update(newFootstepContent) 38 | } 39 | 40 | handleInputChange = (e) => { 41 | const target = e.target 42 | this.setState( 43 | { 44 | [target.name]: target.value, 45 | }, 46 | () => { 47 | this.updateFootstepArray() 48 | } 49 | ) 50 | } 51 | 52 | handleTypeChange = (value) => { 53 | this.setState( 54 | { 55 | type: value, 56 | }, 57 | () => { 58 | this.updateFootstepArray() 59 | } 60 | ) 61 | } 62 | 63 | handleLevelChange = (value) => { 64 | this.setState( 65 | { 66 | level: value, 67 | }, 68 | () => { 69 | this.updateFootstepArray() 70 | } 71 | ) 72 | } 73 | 74 | // Image Upload Functions 75 | 76 | handleUploadStart = () => this.setState({ isUploading: true, progress: 0 }) 77 | handleProgress = (progress) => this.setState({ progress }) 78 | 79 | handleUploadError = (error) => { 80 | this.setState({ isUploading: false }) 81 | console.error(error) 82 | } 83 | 84 | handleUploadSuccess = (filename) => { 85 | this.setState({ icon: filename, progress: 100, isUploading: false }) 86 | firebase 87 | .storage() 88 | .ref("Footsteps") 89 | .child(filename) 90 | .getDownloadURL() 91 | .then((url) => 92 | this.setState({ icon_url: url }, () => { 93 | this.updateFootstepArray() 94 | }) 95 | ) 96 | } 97 | 98 | render() { 99 | return ( 100 |
101 | 102 | 103 |
{this.props.index}
104 | 105 | 106 | 107 |
this.props.remove(this.props.data.id)} 109 | className={styles.footstep_remove_btn} 110 | > 111 | X 112 |
113 | 114 |
115 | 116 | 117 | 118 |
Title
119 | 126 | 127 |
Description
128 |