├── src ├── index.css ├── assets │ ├── images │ │ ├── join.png │ │ ├── review.png │ │ ├── resources.png │ │ ├── discussions.png │ │ ├── leaderboard.png │ │ └── placeholder.jpg │ └── icons │ │ ├── logout.svg │ │ ├── link.svg │ │ ├── edit.svg │ │ ├── discuss.svg │ │ ├── down-arrow.svg │ │ ├── trophy.svg │ │ ├── loader.svg │ │ ├── resource.svg │ │ ├── home.svg │ │ ├── twitterIcon.svg │ │ ├── linkedinIcon.svg │ │ ├── twitter.svg │ │ ├── githubIcon.svg │ │ ├── rocket.svg │ │ └── profile.svg ├── setupTests.js ├── App.test.js ├── components │ ├── Loader.js │ ├── routes │ │ └── PrivateRoute.js │ ├── MakerCard.js │ ├── DiscussPostCard.js │ ├── Sidebar.js │ ├── ShowcasePostCard.js │ └── Navbar.js ├── reportWebVitals.js ├── utils │ └── setAuthToken.js ├── store.js ├── reducers │ ├── index.js │ ├── resources.js │ ├── auth.js │ ├── showcasePost.js │ ├── profile.js │ └── discussPost.js ├── index.js ├── actions │ ├── resources.js │ ├── types.js │ ├── auth.js │ ├── discussPost.js │ ├── profile.js │ └── showcasePost.js ├── pages │ ├── PostNewDiscussion.js │ ├── Discussions.js │ ├── LeaderBoard.js │ ├── Login.js │ ├── EditProfile.js │ ├── Signup.js │ ├── Home.js │ ├── UserProfile.js │ ├── DiscussPost.js │ ├── Feedback.js │ ├── Resources.js │ └── ShowcasePost.js └── App.js ├── public ├── robots.txt ├── favicon.png ├── manifest.json └── index.html ├── craco.config.js ├── tailwind.config.js ├── .gitignore ├── README.md └── package.json /src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rutikwankhade/themakersclub/HEAD/public/favicon.png -------------------------------------------------------------------------------- /src/assets/images/join.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rutikwankhade/themakersclub/HEAD/src/assets/images/join.png -------------------------------------------------------------------------------- /src/assets/images/review.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rutikwankhade/themakersclub/HEAD/src/assets/images/review.png -------------------------------------------------------------------------------- /src/assets/images/resources.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rutikwankhade/themakersclub/HEAD/src/assets/images/resources.png -------------------------------------------------------------------------------- /src/assets/images/discussions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rutikwankhade/themakersclub/HEAD/src/assets/images/discussions.png -------------------------------------------------------------------------------- /src/assets/images/leaderboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rutikwankhade/themakersclub/HEAD/src/assets/images/leaderboard.png -------------------------------------------------------------------------------- /src/assets/images/placeholder.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rutikwankhade/themakersclub/HEAD/src/assets/images/placeholder.jpg -------------------------------------------------------------------------------- /craco.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | style: { 3 | postcss: { 4 | plugins: [ 5 | require('tailwindcss'), 6 | require('autoprefixer'), 7 | ], 8 | }, 9 | }, 10 | } -------------------------------------------------------------------------------- /src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /src/assets/icons/logout.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /src/assets/icons/link.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | purge: ['./src/**/*.js', './public/index.html'], 3 | darkMode: false, // or 'media' or 'class' 4 | theme: { 5 | extend: {} 6 | }, 7 | variants: {}, 8 | plugins: [ 9 | require('@tailwindcss/typography'), 10 | ] 11 | } -------------------------------------------------------------------------------- /src/assets/icons/edit.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/discuss.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/down-arrow.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/Loader.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import loaderIcon from '../assets/icons/loader.svg'; 3 | const Loader = () => { 4 | return ( 5 | Loading... 8 | ); 9 | } 10 | 11 | export default Loader; -------------------------------------------------------------------------------- /src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /src/utils/setAuthToken.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | const setAuthToken = token => { 4 | if (token) { 5 | axios.defaults.headers.common['x-auth-token'] = token; 6 | localStorage.setItem('token', token); 7 | 8 | 9 | } else { 10 | delete axios.defaults.headers.common['x-auth-token']; 11 | localStorage.removeItem('token'); 12 | 13 | } 14 | } 15 | 16 | 17 | export default setAuthToken; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | .env 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | .eslintcache 26 | -------------------------------------------------------------------------------- /src/assets/icons/trophy.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/store.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware } from "redux"; 2 | import { composeWithDevTools } from "redux-devtools-extension"; 3 | import thunk from "redux-thunk"; 4 | 5 | import rootReducer from './reducers'; 6 | 7 | 8 | const initialState = {}; 9 | const middleware = [thunk]; 10 | 11 | const store = createStore( 12 | rootReducer, 13 | initialState, 14 | composeWithDevTools(applyMiddleware(...middleware)) 15 | ); 16 | 17 | 18 | export default store; 19 | 20 | -------------------------------------------------------------------------------- /src/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from "redux"; 2 | import authReducer from "./auth"; 3 | import discussPostReducer from "./discussPost"; 4 | import resourcesReducer from "./resources"; 5 | import showcasePostsReducer from "./showcasePost"; 6 | import profileReducer from "./profile"; 7 | 8 | 9 | 10 | 11 | 12 | export default combineReducers({ 13 | authReducer, 14 | discussPostReducer, 15 | resourcesReducer, 16 | showcasePostsReducer, 17 | profileReducer 18 | }); -------------------------------------------------------------------------------- /src/assets/icons/loader.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/resource.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById('root') 12 | ); 13 | 14 | // If you want to start measuring performance in your app, pass a function 15 | // to log results (for example: reportWebVitals(console.log)) 16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 17 | reportWebVitals(); 18 | -------------------------------------------------------------------------------- /src/assets/icons/home.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /src/components/routes/PrivateRoute.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'react-redux' 3 | import { Route, Redirect } from 'react-router'; 4 | 5 | const PrivateRoute = ({ component: Component, isAuthenticated, loading, ...rest }) => ( 6 | 7 | !isAuthenticated && !loading ? 9 | () 10 | : ()} /> 11 | ) 12 | 13 | const mapStateToProps = state => ({ 14 | isAuthenticated: state.authReducer.isAuthenticated, 15 | loading: state.authReducer.loading 16 | }); 17 | 18 | export default connect(mapStateToProps)(PrivateRoute); -------------------------------------------------------------------------------- /src/assets/icons/twitterIcon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/linkedinIcon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/twitter.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/MakerCard.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | const MakerCard = ({maker, rank}) => { 3 | return ( 4 |
5 |
6 | #{ rank} 7 | 8 |
9 | profile 10 |

{maker.user.name}

11 |
12 | 13 | {maker.points} 14 | 🚀 15 |
16 |
17 | ); 18 | } 19 | 20 | export default MakerCard; -------------------------------------------------------------------------------- /src/assets/icons/githubIcon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🎓 themakersclub 2 | themakersclub is a club of Ideas, feedbacks and resources. 3 | See live https://themakersclub.vercel.app 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | ## ⚡ Features 12 | - Showcase what you built and get constructive feedback from the members of the makersclub. 13 | - Start a discuusion thread on any topic and invite people to share their thoughts and ideas. 14 | - A collection of resources shared by members of the makers club. 15 | 16 | ## 👨‍💻 Tech stack 17 | - React 18 | - Nodejs 19 | - Express 20 | - HarperDB 21 | - Redux 22 | 23 | Built while participating in [Hashnode x HarperDB Hackathon](https://hashnode.com/n/harperdbhackathon) 24 | 25 | Don't forget to leave a ⭐ if you found this useful. 26 | 27 | Buy Me A Coffee 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/reducers/resources.js: -------------------------------------------------------------------------------- 1 | import { 2 | ADD_RESOURCE, 3 | RESOURCE_ERROR, 4 | GET_ALL_RESOURCES 5 | } from "../actions/types"; 6 | 7 | 8 | const initialState = { 9 | resources: [], 10 | loading: true, 11 | error:{} 12 | } 13 | 14 | 15 | export default function resourcesReducer(state = initialState, action) { 16 | const { type, payload } = action; 17 | 18 | switch (type) { 19 | case ADD_RESOURCE: 20 | return { 21 | ...state, 22 | resources:[...state.resources, payload] , 23 | loading: false 24 | 25 | }; 26 | 27 | case RESOURCE_ERROR: 28 | return { 29 | ...state, 30 | error: payload, 31 | loading: false 32 | 33 | }; 34 | 35 | case GET_ALL_RESOURCES: 36 | return { 37 | ...state, 38 | resources:payload , 39 | loading: false 40 | }; 41 | 42 | default: return state; 43 | 44 | } 45 | 46 | } -------------------------------------------------------------------------------- /src/components/DiscussPostCard.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactMarkdown from 'react-markdown'; 3 | import discussIcon from '../assets/icons/discuss.svg' 4 | 5 | 6 | const DiscussPostcard = ({ post: { title, replies, user, avatar, name, text } }) => { 7 | 8 | 9 | return ( 10 |
11 |
12 | 13 |
14 |

{title}

15 |
16 | {`${text.substring(0, 130)}..`} 17 | 18 | 19 |
20 | profile 21 | 22 | {name} 23 |
24 | 25 |
26 | 27 |
28 | {replies.length} 29 | replies 30 |
31 | 32 |
33 | ); 34 | } 35 | 36 | export default DiscussPostcard; -------------------------------------------------------------------------------- /src/assets/icons/rocket.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/actions/resources.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | import { 4 | ADD_RESOURCE, 5 | RESOURCE_ERROR, 6 | GET_ALL_RESOURCES 7 | } from '../actions/types' 8 | 9 | 10 | //post a new resource 11 | export const postResource = ({ resourceUrl, resourceCategory }) => async dispatch => { 12 | 13 | const config = { 14 | headers: { 15 | 'Content-Type': 'application/json' 16 | } 17 | } 18 | 19 | const body = JSON.stringify({ resourceUrl, resourceCategory }) 20 | 21 | try { 22 | const res = await axios.post('https://themakersclubapp.herokuapp.com/api/resources', body, config); 23 | 24 | dispatch({ 25 | type: ADD_RESOURCE, 26 | payload: res.data 27 | }) 28 | return res; 29 | 30 | } catch (err) { 31 | dispatch({ 32 | type: RESOURCE_ERROR, 33 | payload: { msg: err } 34 | }) 35 | 36 | } 37 | 38 | } 39 | 40 | //get all resources 41 | export const getAllResources = () => async dispatch => { 42 | 43 | try { 44 | 45 | const res = await axios.get('https://themakersclubapp.herokuapp.com/api/resources'); 46 | 47 | dispatch({ 48 | type: GET_ALL_RESOURCES, 49 | payload: res.data 50 | }) 51 | 52 | } catch (err) { 53 | dispatch({ 54 | type: RESOURCE_ERROR, 55 | payload: { msg: err } 56 | }) 57 | 58 | } 59 | } 60 | 61 | -------------------------------------------------------------------------------- /src/actions/types.js: -------------------------------------------------------------------------------- 1 | export const REGISTER_SUCCESS = "REGISTER_SUCCESS"; 2 | export const REGISTER_FAIL = "REGISTER_FAIL" 3 | export const USER_LOADED = "USER_LOADED"; 4 | export const AUTH_ERROR = "AUTH_ERROR" 5 | export const LOGIN_SUCCESS = "LOGIN_SUCCESS" 6 | export const LOGIN_FAIL = "LOGIN_FAIL" 7 | export const LOGOUT = "LOGOUT" 8 | 9 | 10 | export const GET_DISCUSS_POSTS = "GET_DISCUSS_POSTS" 11 | export const GET_DISCUSS_POST = "GET_DISCUSS_POST" 12 | export const DISCUSS_POST_ERROR = "DISCUSS_POST_ERROR" 13 | export const ADD_DISCUSS_POST = "ADD_DISCUSS_POST" 14 | export const ADD_DISCUSS_COMMENT = "ADD_DISCUSS_COMMENT" 15 | 16 | export const ADD_RESOURCE = "ADD_RESOURCE" 17 | export const RESOURCE_ERROR = "RESOURCE_ERROR" 18 | export const GET_ALL_RESOURCES = "GET_ALL_RESOURCES" 19 | 20 | export const ADD_SHOWCASE_POST = "ADD_SHOWCASE_POST" 21 | export const GET_SHOWCASE_POSTS = "GET_SHOWCASE_POSTS" 22 | export const SHOWCASE_POST_ERROR = "SHOWCASE_POST_ERROR" 23 | export const GET_SHOWCASE_POST = "GET_SHOWCASE_POST" 24 | export const ADD_FEEDBACK = "ADD_FEEDBACK" 25 | 26 | export const EDIT_PROFILE = "EDIT_PROFILE" 27 | export const EDIT_PROFILE_ERROR = "EDIT_PROFILE_ERROR" 28 | export const UPDATE_POINTS = "UPDATE_POINTS" 29 | 30 | export const GET_TOP_MAKERS = "GET_TOP_MAKERS" 31 | export const TOP_MAKERS_ERROR = "TOP_MAKERS_ERROR" 32 | export const GET_MAKER_PROFILE = "GET_MAKER_PROFILE" 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/reducers/auth.js: -------------------------------------------------------------------------------- 1 | import { 2 | REGISTER_SUCCESS, 3 | REGISTER_FAIL, 4 | USER_LOADED, 5 | AUTH_ERROR, 6 | LOGIN_SUCCESS, 7 | LOGIN_FAIL, 8 | LOGOUT 9 | } from "../actions/types"; 10 | 11 | const initialState = { 12 | token: localStorage.getItem('token'), 13 | isAuthenticated: null, 14 | loading: true, 15 | user: null 16 | } 17 | 18 | 19 | export default function authReducer(state = initialState, action) { 20 | 21 | const { type, payload } = action; 22 | 23 | switch (type) { 24 | 25 | case USER_LOADED: 26 | return { 27 | ...state, 28 | isAuthenticated: true, 29 | loading: false, 30 | user: payload 31 | } 32 | 33 | case REGISTER_SUCCESS: 34 | case LOGIN_SUCCESS: 35 | localStorage.setItem('token', payload.token); 36 | 37 | return { 38 | ...state, 39 | ...payload, 40 | isAuthenticated: true, 41 | loading: false 42 | } 43 | 44 | case REGISTER_FAIL: 45 | case AUTH_ERROR: 46 | case LOGIN_FAIL: 47 | case LOGOUT: 48 | localStorage.removeItem('token'); 49 | return { 50 | ...state, 51 | token: null, 52 | isAuthenticated: false, 53 | loading: false 54 | } 55 | 56 | default: 57 | return state; 58 | 59 | } 60 | 61 | } -------------------------------------------------------------------------------- /src/reducers/showcasePost.js: -------------------------------------------------------------------------------- 1 | import { 2 | ADD_SHOWCASE_POST, 3 | SHOWCASE_POST_ERROR, 4 | GET_SHOWCASE_POSTS, 5 | GET_SHOWCASE_POST, 6 | ADD_FEEDBACK 7 | 8 | } from "../actions/types"; 9 | 10 | 11 | const initialState = { 12 | showcasePosts: [], 13 | showcasePost: null, 14 | loading: true, 15 | error: {} 16 | 17 | } 18 | 19 | 20 | export default function showcasePostReducer( state = initialState, action) { 21 | 22 | const { type, payload } = action; 23 | 24 | switch (type) { 25 | 26 | case ADD_SHOWCASE_POST: 27 | return { 28 | ...state, 29 | showcasePosts: [...state.showcasePosts, payload], 30 | loading: false 31 | } 32 | 33 | case SHOWCASE_POST_ERROR: 34 | return { 35 | ...state, 36 | error:payload, 37 | loading: false 38 | } 39 | 40 | case GET_SHOWCASE_POSTS: 41 | return { 42 | ...state, 43 | showcasePosts:payload, 44 | loading: false 45 | } 46 | 47 | case GET_SHOWCASE_POST: 48 | return { 49 | ...state, 50 | showcasePost:payload, 51 | loading: false 52 | } 53 | case ADD_FEEDBACK: 54 | return { 55 | ...state, 56 | loading: false 57 | } 58 | 59 | 60 | default: return state; 61 | } 62 | 63 | 64 | } 65 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cra-tailwind-starter", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@craco/craco": "^5.9.0", 7 | "@dhaiwat10/react-link-preview": "^1.7.2", 8 | "@tailwindcss/typography": "^0.4.1", 9 | "@testing-library/jest-dom": "^5.11.6", 10 | "@testing-library/react": "^11.2.2", 11 | "@testing-library/user-event": "^12.3.0", 12 | "autoprefixer": "^9.8.6", 13 | "axios": "^0.21.1", 14 | "date-fns": "^2.22.1", 15 | "dotenv": "^10.0.0", 16 | "postcss": "^7.0.35", 17 | "react": "^17.0.1", 18 | "react-dom": "^17.0.1", 19 | "react-hook-form": "^7.9.0", 20 | "react-loading-skeleton": "^2.2.0", 21 | "react-markdown": "^6.0.2", 22 | "react-redux": "^7.2.4", 23 | "react-router-dom": "^5.2.0", 24 | "react-scripts": "4.0.1", 25 | "redux": "^4.1.0", 26 | "redux-devtools-extension": "^2.13.9", 27 | "redux-thunk": "^2.3.0", 28 | "tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.0.1", 29 | "web-vitals": "^0.2.4" 30 | }, 31 | "scripts": { 32 | "start": "craco start", 33 | "build": "craco build", 34 | "test": "craco test", 35 | "eject": "react-scripts eject" 36 | }, 37 | "eslintConfig": { 38 | "extends": [ 39 | "react-app", 40 | "react-app/jest" 41 | ] 42 | }, 43 | "browserslist": { 44 | "production": [ 45 | ">0.2%", 46 | "not dead", 47 | "not op_mini all" 48 | ], 49 | "development": [ 50 | "last 1 chrome version", 51 | "last 1 firefox version", 52 | "last 1 safari version" 53 | ] 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/components/Sidebar.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { NavLink } from 'react-router-dom' 3 | 4 | import trophyIcon from '../assets/icons/trophy.svg' 5 | import rocketIcon from '../assets/icons/rocket.svg' 6 | import discussIcon from '../assets/icons/discuss.svg' 7 | import resourceIcon from '../assets/icons/resource.svg' 8 | 9 | const Sidebar = () => { 10 | return ( 11 |
12 | 13 | 14 | 15 | 16 | Home 17 | 18 | 19 | 20 | Home 21 | 22 | 23 | 24 | Home 25 | 26 | 27 | 28 | Home 29 | 30 | 31 |
32 | ); 33 | } 34 | 35 | export default Sidebar; -------------------------------------------------------------------------------- /src/assets/icons/profile.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/reducers/profile.js: -------------------------------------------------------------------------------- 1 | import { 2 | EDIT_PROFILE, 3 | EDIT_PROFILE_ERROR, 4 | UPDATE_POINTS, 5 | GET_TOP_MAKERS, 6 | TOP_MAKERS_ERROR, 7 | GET_MAKER_PROFILE 8 | } from '../actions/types' 9 | 10 | 11 | const initialState = { 12 | userProfile: {}, 13 | topMakers: [], 14 | makerProfile:{}, 15 | loading: true, 16 | error: {} 17 | } 18 | 19 | 20 | 21 | export default function profileReducer(state = initialState, action) { 22 | const { type, payload } = action; 23 | 24 | switch (type) { 25 | 26 | case EDIT_PROFILE: 27 | return { 28 | ...state, 29 | userProfile: payload, 30 | loading: false 31 | }; 32 | 33 | case UPDATE_POINTS: 34 | return { 35 | ...state, 36 | userProfile: payload, 37 | loading: false 38 | }; 39 | 40 | case EDIT_PROFILE_ERROR: 41 | return { 42 | ...state, 43 | loading: false 44 | }; 45 | 46 | case GET_TOP_MAKERS: 47 | return { 48 | ...state, 49 | topMakers:payload , 50 | loading: false 51 | }; 52 | 53 | case GET_MAKER_PROFILE: 54 | return { 55 | ...state, 56 | makerProfile:payload , 57 | loading: false 58 | }; 59 | 60 | case TOP_MAKERS_ERROR: 61 | return { 62 | ...state, 63 | loading: false 64 | }; 65 | 66 | default: return state; 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/reducers/discussPost.js: -------------------------------------------------------------------------------- 1 | import { 2 | GET_DISCUSS_POSTS, 3 | DISCUSS_POST_ERROR, 4 | ADD_DISCUSS_POST, 5 | GET_DISCUSS_POST, 6 | ADD_DISCUSS_COMMENT 7 | } from "../actions/types"; 8 | 9 | 10 | const initialState = { 11 | discussPosts: [], 12 | discussPost: null, 13 | loading: true, 14 | error:{} 15 | 16 | } 17 | 18 | 19 | 20 | 21 | export default function discussPostReducer(state = initialState, action) { 22 | const { type, payload } = action; 23 | 24 | switch (type) { 25 | 26 | case GET_DISCUSS_POSTS: 27 | return { 28 | ...state, 29 | discussPosts: payload, 30 | loading: false 31 | 32 | }; 33 | 34 | case GET_DISCUSS_POST: 35 | return { 36 | ...state, 37 | discussPost: payload, 38 | loading: false 39 | 40 | }; 41 | 42 | case DISCUSS_POST_ERROR: 43 | return { 44 | ...state, 45 | error: payload, 46 | loading: false 47 | 48 | }; 49 | 50 | case ADD_DISCUSS_POST: 51 | return { 52 | ...state, 53 | discussPosts:[...state.discussPosts, payload] , 54 | loading: false 55 | 56 | }; 57 | 58 | case ADD_DISCUSS_COMMENT: 59 | return { 60 | ...state, 61 | // discussPost: payload , 62 | loading: false 63 | 64 | }; 65 | 66 | default: return state; 67 | 68 | } 69 | 70 | 71 | 72 | 73 | } -------------------------------------------------------------------------------- /src/components/ShowcasePostCard.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom' 3 | 4 | 5 | const ShowcasePostcard = ({ post: { _id, title, url, user, name, desc,feedbacks } }) => { 6 | 7 | 8 | return ( 9 |
10 | 11 | 12 |
13 | 14 | 15 | 16 |
17 |

{title && title}

18 |

{desc}

19 |
20 | 21 | {/*
22 | 12 23 | 24 | ❤ 25 | 26 |
*/} 27 | 28 | {/*
29 | screenshot 30 |
*/} 31 | 32 |
33 | 34 | {feedbacks.length ? 35 |
36 | {feedbacks.length} 37 | Feedbacks 38 |
39 | : 40 |
41 | 42 |
43 | } 44 |
45 | 46 | 47 |
48 | 49 | 50 | 51 |
52 | ); 53 | } 54 | 55 | export default ShowcasePostcard; -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 25 | 26 | 35 | themakersclub 36 | 37 | 38 | 39 |
40 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /src/actions/auth.js: -------------------------------------------------------------------------------- 1 | import axios from "axios" 2 | import setAuthToken from '../utils/setAuthToken' 3 | import { 4 | REGISTER_SUCCESS, 5 | REGISTER_FAIL, 6 | USER_LOADED, 7 | AUTH_ERROR, 8 | LOGIN_SUCCESS, 9 | LOGIN_FAIL, 10 | LOGOUT 11 | } from './types'; 12 | 13 | 14 | 15 | //load user 16 | export const loadUser = () => async dispatch => { 17 | if (localStorage.token) { 18 | setAuthToken(localStorage.token); 19 | } 20 | 21 | try { 22 | const res = await axios.get('https://themakersclubapp.herokuapp.com/api/auth'); 23 | dispatch({ 24 | type: USER_LOADED, 25 | payload: res.data 26 | }) 27 | 28 | } catch (err) { 29 | dispatch({ 30 | type: AUTH_ERROR 31 | }) 32 | 33 | } 34 | } 35 | 36 | 37 | 38 | //register user 39 | 40 | export const register = ({ name, email, password }) => async dispatch => { 41 | 42 | const config = { 43 | headers: { 44 | 'Content-Type': 'application/json' 45 | } 46 | } 47 | 48 | const body = JSON.stringify({ name, email, password }) 49 | 50 | try { 51 | const res = await axios.post('https://themakersclubapp.herokuapp.com/api/users', body, config); 52 | 53 | dispatch({ 54 | type: REGISTER_SUCCESS, 55 | payload: res.data 56 | }) 57 | 58 | dispatch(loadUser()); 59 | 60 | } catch (err) { 61 | 62 | dispatch({ 63 | type: REGISTER_FAIL 64 | }) 65 | 66 | } 67 | } 68 | 69 | 70 | 71 | //login user 72 | 73 | export const login = ( email, password ) => async dispatch => { 74 | 75 | const config = { 76 | headers: { 77 | 'Content-Type': 'application/json' 78 | } 79 | } 80 | 81 | const body = JSON.stringify({ email, password }) 82 | 83 | try { 84 | const res = await axios.post('https://themakersclubapp.herokuapp.com/api/auth', body, config); 85 | 86 | dispatch({ 87 | type: LOGIN_SUCCESS, 88 | payload: res.data 89 | }) 90 | 91 | dispatch(loadUser()); 92 | 93 | } catch (err) { 94 | 95 | dispatch({ 96 | type: LOGIN_FAIL 97 | }) 98 | 99 | } 100 | } 101 | 102 | 103 | //LOGOUT USER 104 | export const logout = () => dispatch => { 105 | dispatch({type:LOGOUT}) 106 | 107 | } -------------------------------------------------------------------------------- /src/pages/PostNewDiscussion.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { useForm } from "react-hook-form"; 4 | import PropTypes from 'prop-types'; 5 | import { useHistory } from 'react-router-dom' 6 | 7 | 8 | import { addDiscussPost } from '../actions/discussPost'; 9 | import { updatePoints } from '../actions/profile' 10 | 11 | 12 | 13 | const PostNewDiscussion = ({ addDiscussPost, updatePoints, loading, points }) => { 14 | const history = useHistory(); 15 | 16 | const { register, handleSubmit } = useForm(); 17 | 18 | 19 | const handlePostSubmit = (data) => { 20 | 21 | console.log(data); 22 | addDiscussPost(data); 23 | 24 | updatePoints({ 25 | points: points, 26 | boost: 5 27 | }); 28 | 29 | 30 | setTimeout(() => { 31 | history.push('/discussions') 32 | }, 3000) 33 | 34 | } 35 | 36 | 37 | return ( 38 |
39 | 40 |
41 |

Start a discussion

42 |
45 | 52 |