Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
],
"plugins": ["transform-class-properties", "@babel/plugin-transform-runtime"]
}
79 changes: 79 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
{
"extends": [
"airbnb",
"plugin:import/errors",
"plugin:import/warnings"
],
"plugins": [
"react"
],
"parser": "babel-eslint",
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
}
},
"env": {
"es6": true,
"browser": true,
"node": true,
"jquery": true,
"mocha": true,
"jest": true
},
"rules": {
"quotes": 0,
"no-console": 1,
"no-debugger": 1,
"no-var": 1,
"semi": [1, "always"],
"no-trailing-spaces": 0,
"eol-last": 0,
"no-unused-vars": 0,
"no-underscore-dangle": 0,
"no-alert": 0,
"no-lone-blocks": 0,
"jsx-quotes": 1,
"react/display-name": [ 1, {"ignoreTranspilerName": false }],
"react/forbid-prop-types": [1, {"forbid": ["any"]}],
"react/jsx-boolean-value": 1,
"react/jsx-closing-bracket-location": 0,
"react/jsx-curly-spacing": 1,
"react/jsx-indent-props": 0,
"react/jsx-key": 1,
"react/jsx-max-props-per-line": 0,
"react/jsx-no-bind": 1,
"react/jsx-no-duplicate-props": 1,
"react/jsx-no-literals": 0,
"react/jsx-no-undef": 1,
"react/jsx-pascal-case": 1,
"react/jsx-sort-prop-types": 0,
"react/jsx-sort-props": 0,
"react/jsx-uses-react": 1,
"react/jsx-uses-vars": 1,
"react/no-danger": 1,
"react/no-did-mount-set-state": 1,
"react/no-did-update-set-state": 1,
"react/no-direct-mutation-state": 1,
"react/no-multi-comp": 1,
"react/no-set-state": 0,
"react/no-unknown-property": 1,
"react/prefer-es6-class": 1,
"react/prop-types": 1,
"react/react-in-jsx-scope": 1,
"react/require-extension": 1,
"react/self-closing-comp": 1,
"react/sort-comp": 1,
"react/wrap-multilines": 1,
"react/jsx-filename-extension": 0,
"react/prefer-stateless-function": 0,
"react/destructuring-assignment": 0,
"react/no-unused-state": 0,
"no-param-reassign": 1,
"prefer-promise-reject-errors": 1,
"import/default": 1,
"jsx-a11y/label-has-for": 1
}
}
23 changes: 23 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#dependencies
/node_modules

# testing
/coverage

# production
/build

# misc
.DS_Store
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
.idea/
.vscode/
/dist
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.nyc_output/
35 changes: 33 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,33 @@
# git-cheat-sheet
A MERN stack application with curated git commands for everyday development.
**A simple MERN stack application**

## Description

Git cheat sheet is a simple React app for the frontend, basic API for requests and Redux for state management that gives users common git commands
and their description

## Features

Git cheat sheet is a git command application list.
- Users can create an account
- Users can sign in to their account
- Users can copy a command to their clipboard
- Users can search the list of commands

## Installation

- Clone this repository
- Navigate to the directory
- Create a .env file using the format in .env.example
- Install all dependencies with `yarn install`
- Start the client side with the command `yarn start:dev`
- Start the server with the command `yarn start`
- Visit `localhost:8080` on your browser
- Run client side test with the command `yarn test:client`
- Run server side test with the command `yarn test:server`

## Contribution

- Fork the repository
- Make your contributions
- Write test cases for your contributions
- Create Pull request against the develop branch.
41 changes: 41 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import express from 'express';
import path from 'path';
import logger from 'morgan';
import bodyParser from 'body-parser';
import cors from 'cors';

import mongoose from './server/db';
import config from './server/config';
import router from './server/routes';

const app = express();
const PORT = process.env.PORT || 7777;

let db = mongoose.connection;
db.once('open', () => console.log(`Connected to the database: ${config.database}`));
db.on("error", console.error.bind(console, "MongoDB connection error:"));

app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, 'dist/')));
app.use(cors());
app.get('/api/v1', (req, res) => {
res.status(200).json({ message: 'Welcome to the coolest API on earth!' });
});
app.use(router);

app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'dist/index.html'));
});

app.all('*', (req, res) => res.status(404).json({
status: 'error',
message: 'Route not found',
}));

if (process.env.NODE_ENV !== 'test') {
app.listen(PORT, () => console.log(`LISTENING ON PORT ${PORT}`));
}

export default app;
4 changes: 4 additions & 0 deletions client/actions/actionTypes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const AUTH = 'AUTH';
export const CATEGORIES = 'CATEGORIES';
export const SEARCH_RESULTS = "SEARCH_RESULTS";
export const LOGOUT = "LOGOUT";
29 changes: 29 additions & 0 deletions client/actions/authAction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import axios from 'axios';
import toastr from "toastr";
import { AUTH, LOGOUT } from "./actionTypes";

import { hostUrl } from "../helpers/utils";

const baseUrl = '/api/v1/users/';

const authenticateUser = token => ({
type: AUTH,
token,
});

export const authUser = (type, userData) => async (dispatch) => {
try {
const response = await axios.post(`${hostUrl}${baseUrl}/${type}`, userData);
const { data: { data: { token }, message } } = response;
localStorage.setItem('token', token);
dispatch(authenticateUser(token));
toastr.success(message);
} catch (e) {
throw (e);
}
};

export const signOut = () => (dispatch) => {
localStorage.removeItem('token');
dispatch({ type: LOGOUT });
};
30 changes: 30 additions & 0 deletions client/actions/categoryAction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import axios from 'axios';
import toastr from "toastr";
import { CATEGORIES, SEARCH_RESULTS } from "./actionTypes";

import { hostUrl } from "../helpers/utils";

const baseUrl = '/api/v1/categories';

const categories = data => ({ type: CATEGORIES, data });
const search = data => ({ type: SEARCH_RESULTS, data });

export const fetchCategories = () => async (dispatch) => {
try {
const response = await axios.get(`${hostUrl}${baseUrl}`);
const { data: { data, message } } = response;
dispatch(categories(data));
toastr.success(message);
} catch (e) {
throw (e);
}
};

export const searchCategory = keyword => (dispatch, getState) => {
const searchKeyword = keyword.trim();
const searchResult = getState().categories
.filter(category => category.title.includes(searchKeyword)
|| category.cheats.map(cheat => cheat.description.toLowerCase())
.some(description => description.includes(searchKeyword)));
dispatch(search(searchResult));
};
18 changes: 18 additions & 0 deletions client/components/App.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React, { Component } from 'react';
import SideNav from "./SideNav";
import Categories from "./Categories";
import SearchBar from "./SearchBar";

export default class App extends Component {
render() {
return (
<div className="row">
<SideNav />
<div className="container">
<SearchBar />
<Categories />
</div>
</div>
)
}
}
Loading