├── .travis.yml
├── .babelrc
├── src
├── client
│ ├── components
│ │ ├── Tools
│ │ │ ├── files
│ │ │ │ ├── babel.png
│ │ │ │ ├── mocha.png
│ │ │ │ ├── redux.png
│ │ │ │ ├── reactjs.png
│ │ │ │ ├── webpack.png
│ │ │ │ └── bootstrap.png
│ │ │ └── index.es
│ │ ├── TopImage
│ │ │ ├── files
│ │ │ │ └── nyc.jpg
│ │ │ └── index.es
│ │ ├── Footer.react.es
│ │ ├── Items.react.es
│ │ ├── AddItem.react.es
│ │ ├── Modals
│ │ │ └── AddPhoneModal.react.es
│ │ └── Header.react.es
│ ├── constants
│ │ └── ActionTypes
│ │ │ ├── Computers.es
│ │ │ ├── Items.es
│ │ │ ├── Example.es
│ │ │ └── Phones.es
│ ├── actions
│ │ ├── items.es
│ │ ├── exampleActions.es
│ │ ├── computers.es
│ │ └── phones.es
│ ├── styles
│ │ ├── components.scss
│ │ ├── index.scss
│ │ └── header.scss
│ ├── reducers
│ │ ├── computers.es
│ │ ├── index.es
│ │ ├── example.es
│ │ ├── __tests__
│ │ │ ├── example-reducer.spec.js
│ │ │ ├── computers-reducer.spec.js
│ │ │ ├── items-reducer.spec.js
│ │ │ └── phones-reducer.spec.js
│ │ ├── phones.es
│ │ └── items.es
│ ├── containers
│ │ ├── AppContainer.react.es
│ │ ├── HomeContainer.react.es
│ │ ├── ExampleContainer.react.es
│ │ ├── ListContainer.react.es
│ │ ├── PhonesContainer.react.es
│ │ └── ComputersContainer.react.es
│ ├── index.es
│ ├── routes.es
│ ├── store
│ │ └── configureStore.es
│ └── utils
│ │ └── request.es
└── server
│ ├── index.js
│ ├── helpers
│ ├── __tests__
│ │ └── example.spec.js
│ └── hookFunc.js
│ ├── routes
│ └── statics.js
│ ├── models
│ ├── log.js
│ ├── func.js
│ ├── index.js
│ ├── printers.js
│ ├── phones.js
│ └── computers.js
│ ├── views
│ └── index.ejs
│ ├── controllers
│ ├── phones.js
│ ├── printers.js
│ ├── computers.js
│ └── common.js
│ ├── middleware
│ ├── server.js
│ ├── mongo.js
│ └── express.js
│ ├── app.js
│ └── schema
│ ├── printerType.js
│ ├── phoneType.js
│ ├── computerType.js
│ └── schema.js
├── TODO.md
├── init_data
├── getRandomInt.js
├── generator.js
├── printers.js
├── phones.js
└── computers.js
├── .gitignore
├── env
├── development.json
└── index.js
├── mocha_settings
├── client-inject.js
└── server-inject.js
├── webpack.js
├── README.md
├── webpack
├── utils
│ └── app-server.js
└── webpack.config.js
├── init_db.js
└── package.json
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 4.3.1
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015", "stage-0", "react"]
3 | }
4 |
--------------------------------------------------------------------------------
/src/client/components/Tools/files/babel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yankouskia/Shop/HEAD/src/client/components/Tools/files/babel.png
--------------------------------------------------------------------------------
/src/client/components/Tools/files/mocha.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yankouskia/Shop/HEAD/src/client/components/Tools/files/mocha.png
--------------------------------------------------------------------------------
/src/client/components/Tools/files/redux.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yankouskia/Shop/HEAD/src/client/components/Tools/files/redux.png
--------------------------------------------------------------------------------
/src/client/components/Tools/files/reactjs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yankouskia/Shop/HEAD/src/client/components/Tools/files/reactjs.png
--------------------------------------------------------------------------------
/src/client/components/Tools/files/webpack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yankouskia/Shop/HEAD/src/client/components/Tools/files/webpack.png
--------------------------------------------------------------------------------
/src/client/components/TopImage/files/nyc.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yankouskia/Shop/HEAD/src/client/components/TopImage/files/nyc.jpg
--------------------------------------------------------------------------------
/src/client/constants/ActionTypes/Computers.es:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | export default {
4 | GET_ALL_COMPUTERS: 'GET_ALL_COMPUTERS'
5 | };
6 |
--------------------------------------------------------------------------------
/src/client/components/Tools/files/bootstrap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yankouskia/Shop/HEAD/src/client/components/Tools/files/bootstrap.png
--------------------------------------------------------------------------------
/src/client/constants/ActionTypes/Items.es:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | export default {
4 | ADD_ITEM: 'ADD_ITEM',
5 | DELETE_ITEM: 'DELETE_ITEM'
6 | };
7 |
--------------------------------------------------------------------------------
/src/server/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | require('babel-core/register');
4 |
5 | delete process.env.BROWSER;
6 |
7 | require('./app.js').start();
8 |
--------------------------------------------------------------------------------
/TODO.md:
--------------------------------------------------------------------------------
1 | Compare products
2 | Search products
3 | Getting data and present it in table
4 | Db logs
5 | Db hooks
6 | Db error handling
7 | Create frame in css
8 |
--------------------------------------------------------------------------------
/src/client/constants/ActionTypes/Example.es:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | export default {
4 | INCREMENT_COUNTER: 'INCREMENT_COUNTER',
5 | DECREMENT_COUNTER: 'DECREMENT_COUNTER'
6 | };
7 |
--------------------------------------------------------------------------------
/src/client/constants/ActionTypes/Phones.es:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | export default {
4 | ADD_PHONE: 'ADD_PHONE',
5 | DELETE_PHONE: 'DELETE_PHONE',
6 | GET_ALL_PHONES: 'GET_ALL_PHONES'
7 | };
8 |
--------------------------------------------------------------------------------
/src/server/helpers/__tests__/example.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Example server test', () => {
4 |
5 | it('should calculate 1+1', () => {
6 | expect(1+1).to.be.equal(2);
7 | });
8 | });
9 |
--------------------------------------------------------------------------------
/init_data/getRandomInt.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function randomInteger(min, max) {
4 | var rand = min - 0.5 + Math.random() * (max - min + 1)
5 | rand = Math.round(rand);
6 | return rand;
7 | };
8 |
--------------------------------------------------------------------------------
/src/server/routes/statics.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var express = require('express');
4 | var router = express.Router();
5 |
6 | router.use('/', function (req, res) {
7 | res.render('index');
8 | });
9 |
10 | module.exports = router;
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # temporary files
2 | *~
3 |
4 | # apt-cacher-ng and sinopia caches
5 | caches/
6 |
7 | # log files
8 | *.log
9 |
10 | # npm modules
11 | node_modules/
12 |
13 | # current client build
14 | client/build/
15 |
16 | # npm errors file
17 | npm-debug.log
18 |
--------------------------------------------------------------------------------
/env/development.json:
--------------------------------------------------------------------------------
1 | {
2 | "mongo": {
3 | "db": "Shop",
4 | "host": "localhost",
5 | "port": 27017,
6 | "options": {
7 | "safe": true
8 | }
9 | },
10 | "express": {
11 | "port": 3003
12 | },
13 | "app": {
14 | "title": "Shop",
15 | "api": "/api/v1"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/server/models/log.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import mongoose from 'mongoose';
4 |
5 | let LogShema = new mongoose.Schema({
6 | info: { type: String, required: true, default: 'no data, but inserting was' }
7 | }, {
8 | versionKey: false
9 | });
10 |
11 | export default mongoose.model('Log', LogShema);
12 |
--------------------------------------------------------------------------------
/src/server/models/func.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import mongoose from 'mongoose';
4 |
5 | let FunctionSchema = new mongoose.Schema({
6 | func: { type: String, required: true, default: 'function() {return 0;}' },
7 | }, {
8 | versionKey: false
9 | });
10 |
11 | export default mongoose.model('Func', FunctionSchema);
12 |
--------------------------------------------------------------------------------
/src/server/models/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import Func from './func';
4 | import Log from './log';
5 | import Phones from './phones';
6 | import Computers from './computers';
7 | import Printers from './printers';
8 |
9 | export default {
10 | Func,
11 | Log,
12 | Phones,
13 | Computers,
14 | Printers
15 | }
16 |
--------------------------------------------------------------------------------
/src/client/actions/items.es:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import types from 'constants/ActionTypes/Items';
4 |
5 | export function addItem(fields) {
6 | return {
7 | type: types.ADD_ITEM,
8 | fields,
9 | };
10 | }
11 |
12 | export function delItem(index) {
13 | return {
14 | type: types.DELETE_ITEM,
15 | index,
16 | };
17 | }
18 |
--------------------------------------------------------------------------------
/src/server/views/index.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Shop
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/server/controllers/phones.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import PhoneModel from '../models/phones';
4 | import commonCtrl from './common';
5 |
6 | const getAll = (mark) => commonCtrl.getAll(PhoneModel, mark);
7 |
8 | const add = (phone) => commonCtrl.add(PhoneModel, phone);
9 |
10 | export default {
11 | getAll,
12 | add
13 | };
14 |
--------------------------------------------------------------------------------
/src/server/controllers/printers.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import PrinterModel from '../models/printers';
4 | import commonCtrl from './common';
5 |
6 | const getAll = (mark) => commonCtrl.getAll(PrinterModel, mark);
7 |
8 | const add = (printer) => commonCtrl.add(PrinterModel, printer);
9 |
10 | export default {
11 | getAll,
12 | add
13 | };
14 |
--------------------------------------------------------------------------------
/mocha_settings/client-inject.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | process.env.NODE_PATH = 'src/client';
4 |
5 | require('module').Module._initPaths();
6 |
7 | import chai from 'chai';
8 | import chaiSubSet from 'chai-subset';
9 | import sinonChai from 'sinon-chai';
10 |
11 | chai.use(sinonChai);
12 | chai.use(chaiSubSet);
13 |
14 | global.expect = chai.expect;
15 |
--------------------------------------------------------------------------------
/mocha_settings/server-inject.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | process.env.NODE_PATH = 'src/server';
4 |
5 | require('module').Module._initPaths();
6 |
7 | import chai from 'chai';
8 | import chaiSubSet from 'chai-subset';
9 | import sinonChai from 'sinon-chai';
10 |
11 | chai.use(sinonChai);
12 | chai.use(chaiSubSet);
13 |
14 | global.expect = chai.expect;
15 |
--------------------------------------------------------------------------------
/src/server/controllers/computers.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import ComputerModel from '../models/computers';
4 | import commonCtrl from './common';
5 |
6 | const getAll = (mark) => commonCtrl.getAll(ComputerModel, mark);
7 |
8 | const add = (computer) => commonCtrl.add(ComputerModel, computer);
9 |
10 | export default {
11 | getAll,
12 | add
13 | };
14 |
--------------------------------------------------------------------------------
/src/client/styles/components.scss:
--------------------------------------------------------------------------------
1 | .header__search-input {
2 | margin-right: 5px;
3 | }
4 |
5 | .footer {
6 | position:fixed;
7 | left:0px;
8 | bottom:0px;
9 | height:33px;
10 | width:100%;
11 | background:#999;
12 | background-color: #D8D8D8;
13 | }
14 |
15 | .footer__star-button {
16 | margin-left: 10px;
17 | margin-right: 10px;
18 | }
--------------------------------------------------------------------------------
/src/server/middleware/server.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import Promise from 'bluebird';
4 | import expressConfig from './express';
5 |
6 | export default (app) => {
7 | return new Promise((resolve) => {
8 | expressConfig(app);
9 |
10 | app.listen(app.get('port'), () => {
11 | console.log('START LISTEN PORT - ', app.get('port'));
12 | resolve();
13 | });
14 | });
15 | }
16 |
--------------------------------------------------------------------------------
/src/client/actions/exampleActions.es:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import types from 'constants/ActionTypes/Example';
4 | import request from 'utils/request';
5 |
6 | export function increment() {
7 | return dispatch => {
8 | dispatch({ type: types.INCREMENT_COUNTER });
9 | }
10 | return { type: INCREMENT_COUNTER };
11 | }
12 |
13 | export function decrement() {
14 | return (dispatch) => {
15 | dispatch({ type: types.DECREMENT_COUNTER });
16 | };
17 | }
--------------------------------------------------------------------------------
/init_data/generator.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var getRandomInt = require('./getRandomInt');
4 |
5 | module.exports = function(model, number) {
6 | var items = [],
7 | transformed;
8 |
9 | for(var i = 0; i < number; i++) {
10 | transformed = {};
11 | Object.keys(model).forEach(function(key) {
12 | var randIndex = getRandomInt(0, model[key].length - 1);
13 | transformed[key] = model[key][randIndex];
14 | });
15 | items.push(transformed);
16 | }
17 | return items;
18 | }
19 |
--------------------------------------------------------------------------------
/src/client/reducers/computers.es:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import types from 'constants/ActionTypes/Computers';
4 |
5 | const initialState = {
6 | computers: []
7 | };
8 |
9 | export function computers(state = initialState, action) {
10 | switch (action.type) {
11 | case types.GET_ALL_COMPUTERS:
12 | return {
13 | ...state,
14 | computers: action.computers,
15 | };
16 |
17 | default:
18 | return state;
19 | }
20 | }
--------------------------------------------------------------------------------
/src/client/reducers/index.es:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import { combineReducers } from 'redux';
4 | import { reducer as formReducer } from 'redux-form';
5 | import { items } from './items';
6 | import { phones } from './phones';
7 | import { example } from './example';
8 | import { computers } from './computers';
9 |
10 | const rootReducer = combineReducers({
11 | form: formReducer,
12 | /* your reducers */
13 | items,
14 | phones,
15 | example,
16 | computers
17 | });
18 |
19 | export default rootReducer;
20 |
--------------------------------------------------------------------------------
/src/client/styles/index.scss:
--------------------------------------------------------------------------------
1 | $icon-font-path: '~bootstrap-sass/assets/fonts/bootstrap/';
2 | @import "~bootstrap-sass/assets/stylesheets/_bootstrap.scss";
3 | @import './header.scss';
4 | @import './components.scss';
5 |
6 | .btn {
7 | &:active:focus, &:focus {
8 | outline: none;
9 | }
10 | }
11 |
12 | .btn-default
13 | {
14 | &:active:focus
15 | {
16 | outline: none;
17 | background-color: darken( #fff, 10%);
18 | }
19 | &:focus
20 | {
21 | outline: none;
22 | background-color: #fff;
23 | }
24 | }
--------------------------------------------------------------------------------
/src/client/containers/AppContainer.react.es:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React, { Component } from 'react';
4 | import Header from 'components/Header.react';
5 | import Footer from 'components/Footer.react';
6 |
7 | export default class App extends Component {
8 | static propTypes = {
9 | children: React.PropTypes.any,
10 | };
11 |
12 | render() {
13 | return (
14 |
15 |
16 | {this.props.children}
17 |
18 |
19 | );
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/client/reducers/example.es:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import types from 'constants/ActionTypes/Example';
4 |
5 | const initialState = {
6 | counter: 0,
7 | };
8 |
9 | export function example( state = initialState, action ) {
10 | switch (action.type) {
11 | case types.INCREMENT_COUNTER:
12 | return {
13 | ...state,
14 | counter: state.counter + 1
15 | };
16 |
17 | case types.DECREMENT_COUNTER:
18 | return {
19 | ...state,
20 | counter: state.counter - 1
21 | };
22 |
23 | default:
24 | return state;
25 | }
26 | }
--------------------------------------------------------------------------------
/src/client/index.es:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React from 'react';
4 | import ReactDOM from 'react-dom';
5 | import { Provider } from 'react-redux';
6 | import { Router, useRouterHistory } from 'react-router';
7 | import { createHashHistory } from 'history';
8 | import configureStore from './store/configureStore';
9 | import routes from './routes';
10 | import 'styles/index.scss';
11 |
12 | const history = useRouterHistory(createHashHistory)({ queryKey: false });
13 | const store = configureStore();
14 |
15 | ReactDOM.render(
16 |
17 |
18 | ,
19 | document.getElementById('react-container')
20 | );
21 |
--------------------------------------------------------------------------------
/src/client/components/Footer.react.es:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React, { Component } from 'react';
4 | import { Button, Glyphicon } from 'react-bootstrap';
5 |
6 | export default class Footer extends Component {
7 |
8 | render() {
9 | return (
10 |
20 | );
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/server/helpers/hookFunc.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import Log from '../models/log';
4 | import Func from '../models/func';
5 |
6 | export default function(next, done, self, modelName) {
7 | let currentDate = new Date();
8 | let date = `${currentDate.getDay()}.${currentDate.getMonth()}.${currentDate.getFullYear()}`;
9 | let time = `${currentDate.getHours()}:${currentDate.getMinutes()}:${currentDate.getSeconds()}`;
10 | let log = new Log({
11 | info: `In collection: "${modelName}" was written new item on ${date} in ${time}`
12 | })
13 |
14 | Func.findOne({}, (err, res) => {
15 | eval(res);
16 | log.save(() => {
17 | next();
18 | })
19 | });
20 | }
21 |
--------------------------------------------------------------------------------
/init_data/printers.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var generator = require('./generator');
4 |
5 | var printerModel = {
6 | mark: ['HP', 'Canon', 'Epson', 'Samsung'],
7 | model: ['L800', 'Mita FS-1040', '111SU', 'Mita FS-1020MFP', 'LaserJet Pro MFP M177fw'],
8 | color: ['black', 'white', 'green', 'grey', 'yellow', 'blue', 'red', 'orange', 'violet', 'purple'],
9 | wifi: ['802.11ac', '802.11a', '802.11c'],
10 | isMFP: [true, false],
11 | isBlackWhite: [true, false],
12 | pageVelocityPerMinute: [22, 23, 24, 25, 26, 30, 33, 36, 37, 40, 42, 45, 60],
13 | printTechnology: ['jet', 'laser'],
14 | price: [80, 110, 130, 140, 155, 160, 187, 210, 220, 254, 270, 310, 340],
15 | };
16 |
17 | module.exports = generator(printerModel, 15);
18 |
--------------------------------------------------------------------------------
/src/server/app.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import express from 'express';
4 | import mongoMiddleware from './middleware/mongo';
5 | import serverMiddleware from './middleware/server';
6 | import Promise from 'bluebird';
7 |
8 | const app = express();
9 | let isAppStarted = false;
10 |
11 | app.start = () => {
12 | if(!isAppStarted) {
13 | isAppStarted = true;
14 | Promise.all([
15 | serverMiddleware(app),
16 | mongoMiddleware(app)
17 | ]).then(() => {
18 | if (process.send) {
19 | process.send('online');
20 | }
21 | }, (error) => {
22 | console.error(error);
23 | });
24 | }
25 | };
26 |
27 | if (!module.parent) {
28 | try {
29 | app.start();
30 | } catch(e) {}
31 | }
32 |
33 | module.exports = app;
34 |
--------------------------------------------------------------------------------
/src/client/containers/HomeContainer.react.es:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React, { Component } from 'react';
4 | import {
5 | Accordion,
6 | Panel
7 | } from 'react-bootstrap';
8 |
9 | import { TopImage } from 'components/TopImage';
10 |
11 | export default class Home extends Component {
12 | render() {
13 | return (
14 |
15 |
16 |
17 |
18 | All you want :)
19 |
20 |
21 | Best personal, the lowest prices
22 |
23 |
24 | Best programmers
25 |
26 |
27 |
28 |
29 |
30 | );
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/client/routes.es:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React from 'react';
4 | import { Route, IndexRoute } from 'react-router';
5 |
6 | /* containers */
7 | import App from 'containers/AppContainer.react';
8 | import Home from 'containers/HomeContainer.react';
9 | import List from 'containers/ListContainer.react';
10 | import Phones from 'containers/PhonesContainer.react';
11 | import Example from 'containers/ExampleContainer.react';
12 | import Computers from 'containers/ComputersContainer.react';
13 |
14 | export default (
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | );
24 |
--------------------------------------------------------------------------------
/webpack.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var Webpack = require('webpack'),
4 | WebpackDevServer = require('webpack-dev-server'),
5 | webpackConfig = Webpack(require('./webpack/webpack.config.js')),
6 | server = {
7 | port: 3000,
8 | host: 'localhost',
9 |
10 | options: {
11 | hot: true,
12 |
13 | publicPath: '/',
14 | stats: {
15 | colors: true
16 | },
17 |
18 | noInfo: true,
19 | proxy: [{
20 | path: '*',
21 | target: 'http://localhost:3003'
22 | }],
23 | historyApiFallback: true
24 | }
25 | },
26 | devServer;
27 |
28 | devServer = new WebpackDevServer(webpackConfig, server.options);
29 |
30 | devServer.listen(server.port, server.host, function () {
31 | console.log('\nstarting app\n');
32 | });
33 |
--------------------------------------------------------------------------------
/src/client/actions/computers.es:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import request from 'utils/request';
4 | import types from 'constants/ActionTypes/Computers';
5 |
6 | export function getAllComputers(mark = '') {
7 | const searchByMark = mark ? `(mark:"${mark}")` : '',
8 | query = `query{computers ${searchByMark} {
9 | mark
10 | model
11 | color
12 | wifi
13 | isLaptop
14 | diagonal
15 | coresNumber
16 | usb2
17 | usb3
18 | ram
19 | memory
20 | videocard
21 | videomemory
22 | processor
23 | operatingSystem
24 | price
25 | }}`;
26 |
27 | return dispatch => {
28 | request.get('/', {query}).then(({data}) => {
29 | dispatch({ type: types.GET_ALL_COMPUTERS, computers: data.computers });
30 | });
31 | };
32 | }
33 |
--------------------------------------------------------------------------------
/src/client/reducers/__tests__/example-reducer.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import { example } from '../example';
4 |
5 | describe('Example reducer:', () => {
6 |
7 | const initialState = {
8 | counter: 0
9 | };
10 |
11 | it('should return the initial state', () => {
12 | expect(example(initialState, {})).to.deep.equal(initialState);
13 | });
14 |
15 | it('should handle INCREMENT_COUNTER', () => {
16 | const expectedState = {
17 | counter: 1
18 | };
19 |
20 | expect(example(initialState, {
21 | type: 'INCREMENT_COUNTER'
22 | })).to.deep.equal(expectedState);
23 | });
24 |
25 | it('should handle DECREMENT_COUNTER', () => {
26 | const expectedState = {
27 | counter: -1
28 | };
29 |
30 | expect(example(initialState, {
31 | type: 'DECREMENT_COUNTER'
32 | })).to.deep.equal(expectedState);
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Shop [](https://travis-ci.org/yankouskia/Shop)
2 |
3 | ## Preparation
4 |
5 | > Install nodejs
6 |
7 | > Install mongodb
8 |
9 | ## Run project
10 |
11 | ```sh
12 | npm install
13 | npm run add-db (for adding initial data)
14 | npm start
15 | ```
16 |
17 | ## Run tests
18 |
19 | ```sh
20 | npm test
21 | ```
22 |
23 | ### Fill db
24 |
25 | ```sh
26 | npm run add-db
27 | ```
28 |
29 | ### Technologies
30 |
31 | - [x] React
32 | - [x] Redux
33 | - [x] GraphQL
34 | - [x] Immutable
35 | - [x] Babel 6
36 | - [x] Webpack
37 | - [x] Webpack-dev-server
38 | - [x] express
39 | - [x] Mongoose
40 | - [x] react-bootstrap
41 | - [x] react-redux
42 | - [x] sass
43 | - [x] and other ...
44 |
45 |
46 | ### About
47 | * Project represents internet shop. It can easy help people to understand above mentioned technologies. Or just use it as boilerplate in their projects.
48 |
49 | **_Open for PRs_** :+1:
50 |
51 |
--------------------------------------------------------------------------------
/src/client/store/configureStore.es:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import { applyMiddleware, createStore } from 'redux';
4 | import thunkMiddleware from 'redux-thunk';
5 | import createLogger from 'redux-logger';
6 | import rootReducer from '../reducers';
7 |
8 | export default function configureStore(initialState) {
9 | const logger = createLogger({
10 | collapsed: true,
11 | predicate: () => process.env.NODE_ENV === `development` // eslint-disable-line no-unused-vars
12 | });
13 |
14 | const middleware = applyMiddleware(thunkMiddleware, logger);
15 |
16 | const store = middleware(createStore)(rootReducer, initialState);
17 |
18 | if (module.hot) {
19 | // Enable Webpack hot module replacement for reducers
20 | module.hot.accept('../reducers', () => {
21 | const nextRootReducer = require('../reducers').default;
22 | store.replaceReducer(nextRootReducer);
23 | });
24 | }
25 |
26 | return store;
27 | }
28 |
--------------------------------------------------------------------------------
/env/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var nconf = require('nconf');
4 | var path = require('path');
5 | var mDockerHost = process.env.IPLN_MONGO_PORT_27017_TCP_ADDR;
6 | var mDockerPort = process.env.IPLN_MONGO_PORT_27017_TCP_PORT;
7 |
8 | if(!process.env.SHOP) {
9 | process.env.SHOP = 'development';
10 | }
11 |
12 | var env = process.env.SHOP;
13 |
14 | if (env !== undefined) {
15 | env = env.trim();
16 | } else {
17 | env = 'development';
18 | }
19 |
20 | nconf
21 | .argv()
22 | .env()
23 | .file({
24 | file: path.join(__dirname, env + '.json')
25 | });
26 |
27 | if(mDockerHost !== undefined && mDockerPort !== undefined) {
28 | nconf.set('mongo:host', mDockerHost);
29 | nconf.set('mongo:port', mDockerPort);
30 | }
31 |
32 | var mongoURI = 'mongodb://' +
33 | nconf.get('mongo:host') + ':' +
34 | nconf.get('mongo:port') + '/' +
35 | nconf.get('mongo:db');
36 |
37 | nconf.set('mongo:uri', mongoURI);
38 |
39 | module.exports = nconf;
40 |
--------------------------------------------------------------------------------
/init_data/phones.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var generator = require('./generator');
4 |
5 | var phoneModel = {
6 | mark: ['iPhone', 'Samsung', 'HTC', 'Huawey', 'Lenovo', 'Sony', 'Nokia', 'Xaomi', 'LG', 'ZTE'],
7 | model: ['6s', '5s', 'Galaxy', 'P70-A', 'Light Dual', 'Vibe', 'P1M', '650', 'Grand Prime', 'MX5'],
8 | color: ['black', 'white', 'green', 'grey', 'yellow', 'blue', 'red', 'orange', 'violet', 'purple'],
9 | wifi: ['802.11ac', '802.11a', '802.11c'],
10 | gps: [true, false],
11 | coresNumber: [1, 2, 4, 8],
12 | ram: [1, 2, 4, 8],
13 | memory: [2, 4, 8, 16, 32, 64],
14 | camera: [5, 8, 10, 12, 13, 15, 18],
15 | diagonal: [4, 4.1, 4.2, 4.4, 4.5, 4.7, 4.8, 4.9, 5.0, 5.1, 5.2, 5.3, 5.5, 5.7, 6.0, 6.1, 6.3],
16 | operatingSystem: ['Android', 'iOS', 'Window Phone'],
17 | price: [200, 220, 240, 260, 280, 300, 330, 360, 390, 420, 450, 480, 500, 540, 560, 580, 600, 620, 660, 710, 730, 800]
18 | };
19 |
20 | module.exports = generator(phoneModel, 20);
21 |
--------------------------------------------------------------------------------
/src/client/reducers/__tests__/computers-reducer.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import { computers } from '../computers';
4 |
5 | describe('Computers reducer:', () => {
6 |
7 | const initialState = {
8 | computers: []
9 | };
10 |
11 | it('should return the initial state', () => {
12 | expect(computers(initialState, {})).to.deep.equal(initialState);
13 | });
14 |
15 | it('should handle GET_ALL_COMPUTERS', () => {
16 | const expectedState = {
17 | computers: [{
18 | name: 'macbook'
19 | }, {
20 | name: 'asus'
21 | }]
22 | };
23 |
24 | const computersToGet = [{
25 | name: 'macbook'
26 | }, {
27 | name: 'asus'
28 | }];
29 |
30 | expect(computers(initialState, {
31 | type: 'GET_ALL_COMPUTERS',
32 | computers: computersToGet,
33 | })).to.deep.equal(expectedState);
34 | });
35 | });
36 |
--------------------------------------------------------------------------------
/src/client/reducers/phones.es:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import types from 'constants/ActionTypes/Phones';
4 |
5 | const initialState = {
6 | phones: []
7 | };
8 |
9 | export function phones(state = initialState, action) {
10 | switch (action.type) {
11 | case types.ADD_PHONE:
12 | return {
13 | ...state,
14 | phones: [
15 | ...state.phones,
16 | action.phone
17 | ]
18 | };
19 |
20 | case types.DELETE_PHONE:
21 | return {
22 | ...state,
23 | phones: [
24 | ...state.phones.slice(0, action.index),
25 | ...state.phones.slice(+action.index + 1),
26 | ]
27 | };
28 |
29 | case types.GET_ALL_PHONES:
30 | return {
31 | ...state,
32 | phones: action.phones
33 | };
34 |
35 | default:
36 | return state;
37 | }
38 | }
--------------------------------------------------------------------------------
/src/client/actions/phones.es:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import request from 'utils/request';
4 | import types from 'constants/ActionTypes/Phones';
5 |
6 | export function getAllPhones(mark = '') {
7 | const searchByMark = mark ? `(mark:"${mark}")` : '',
8 | query = `query{phones ${searchByMark} {
9 | mark
10 | model
11 | color
12 | wifi
13 | gps
14 | coresNumber
15 | ram
16 | memory
17 | camera
18 | diagonal
19 | operatingSystem
20 | price
21 | }}`;
22 |
23 | return dispatch => {
24 | request.get('/', {query}).then(({data}) => {
25 | dispatch({ type: types.GET_ALL_PHONES, phones: data.phones });
26 | });
27 | };
28 | }
29 |
30 | export function addPhone(fields) {
31 | return {
32 | type: types.ADD_PHONE,
33 | fields,
34 | };
35 | }
36 |
37 | export function deletePhone(index) {
38 | return {
39 | type: types.DELETE_PHONE,
40 | index,
41 | };
42 | }
43 |
--------------------------------------------------------------------------------
/src/server/controllers/common.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const getAll = (Model, mark) => {
4 | return new Promise((resolve, reject) => {
5 | Model.find({}, function(err, docs) {
6 | if(!err) {
7 | if (mark) { // TODO: improve working with filters, use generic for filtering by more params!
8 | mark = mark.trim().toLowerCase();
9 |
10 | docs = docs.filter(pc => pc.mark.toLowerCase() === mark);
11 | }
12 |
13 | resolve(docs);
14 | } else {
15 | reject({error: err});
16 | }
17 | });
18 | });
19 | };
20 |
21 | const add = (Model, entity) => {
22 | return new Promise((resolve, reject) => {
23 | const model = new Model(entity);
24 |
25 | model.save(function(err, docs) {
26 | if(!err) {
27 | resolve(model);
28 | }
29 | else {
30 | reject({ error: err });
31 | }
32 | });
33 | });
34 | };
35 |
36 | export default {
37 | getAll,
38 | add
39 | };
40 |
--------------------------------------------------------------------------------
/src/client/reducers/__tests__/items-reducer.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import { items } from '../items';
4 |
5 | describe('Items reducer:', () => {
6 |
7 | const initialState = {
8 | items: []
9 | };
10 |
11 | it('should return the initial state', () => {
12 | expect(items(initialState, {})).to.deep.equal(initialState);
13 | });
14 |
15 | it('should handle ADD', () => {
16 | const expectedState = {
17 | items: [{
18 | text: 'test'
19 | }],
20 | };
21 | const fields = { name: { value: 'test'}};
22 | expect(items(initialState, {
23 | type: 'ADD_ITEM',
24 | fields: fields,
25 | })).to.deep.equal(expectedState);
26 | });
27 |
28 | it('should handle DELETE', () => {
29 | const stateWithItem = {
30 | items: [{
31 | text: 'test'
32 | }],
33 | };
34 | expect(items(stateWithItem, {
35 | type: 'DELETE_ITEM',
36 | index: 0
37 | })).to.deep.equal(initialState);
38 | });
39 | });
40 |
--------------------------------------------------------------------------------
/src/server/middleware/mongo.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import Promise from 'bluebird';
4 | import mongoose from 'mongoose';
5 | import async from 'async';
6 |
7 | import env from '../../../env';
8 |
9 | export default (app) => {
10 | return new Promise((resolve, reject) => {
11 | if (app.mongo) {
12 | mongoose.connection.close();
13 | app.mongo = null;
14 | }
15 |
16 | if (env.get('SHOP') === 'development') {
17 | mongoose.set('debug', (collection, method, query, doc) => {
18 | console.log('mongo data: ', collection, method, query, doc);
19 | });
20 | }
21 |
22 | mongoose.connection.once('open', () => {
23 | app.mongo = mongoose.connection;
24 | console.log('connected to mongo');
25 | resolve();
26 | });
27 |
28 | mongoose.connection.on('error', (err) => {
29 | console.log('error connection');
30 | reject();
31 | });
32 |
33 |
34 | mongoose.connect(env.get('mongo:uri'), env.get('mongo:options'));
35 | });
36 | };
37 |
--------------------------------------------------------------------------------
/src/server/models/printers.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import mongoose from 'mongoose';
4 | import hookFunc from '../helpers/hookFunc';
5 |
6 | const PRINTER_SCHEMA_NAME = 'Printer';
7 |
8 | let PrinterSchema = new mongoose.Schema({
9 | mark: { type: String, required: true, default: '' },
10 | model: { type: String, required: true, default: '' },
11 | color: { type: String, required: true, default: '' },
12 | wifi: { type: String, required: true, default: '' },
13 | isMFP: { type: Boolean, required: true, default: false},
14 | isBlackWhite: { type: Boolean, required: true, default: true},
15 | pageVelocityPerMinute: { type: Number, required: true, default: 0 },
16 | printTechnology: { type: String, required: true, default: '' },
17 | price: { type: Number, required: true, default: 0 }
18 | }, {
19 | versionKey: false
20 | });
21 |
22 | PrinterSchema.plugin(require('mongoose-timestamp'));
23 |
24 | PrinterSchema.pre('save', function(next, done) {
25 | hookFunc(next, done, this, PRINTER_SCHEMA_NAME);
26 | })
27 |
28 | export default mongoose.model(PRINTER_SCHEMA_NAME, PrinterSchema);
29 |
--------------------------------------------------------------------------------
/src/server/models/phones.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import mongoose from 'mongoose';
4 | import hookFunc from '../helpers/hookFunc';
5 |
6 | const PHONE_SCHEMA_NAME = 'Phone';
7 |
8 | let PhoneSchema = new mongoose.Schema({
9 | mark: { type: String, required: true, default: '' },
10 | model: { type: String, required: true, default: '' },
11 | color: { type: String, required: true, default: '' },
12 | wifi: { type: String, required: true, default: '' },
13 | gps: { type: Boolean, required: true, default: false},
14 | coresNumber: { type: Number, required: true, default: 0 },
15 | ram: { type: Number, required: true, default: 0 },
16 | memory: { type: Number, required: true, default: 0 },
17 | camera: { type: Number, required: true, default: 0 },
18 | diagonal: { type: Number, required: true, default: 0 },
19 | operatingSystem: { type: String, required: true, default: 'Android' },
20 | price: { type: Number, required: true, default: 0 }
21 | }, {
22 | versionKey: false
23 | });
24 |
25 | PhoneSchema.plugin(require('mongoose-timestamp'));
26 |
27 | PhoneSchema.pre('save', function(next, done) {
28 | hookFunc(next, done, this, PHONE_SCHEMA_NAME);
29 | })
30 |
31 | export default mongoose.model(PHONE_SCHEMA_NAME, PhoneSchema);
32 |
--------------------------------------------------------------------------------
/src/client/styles/header.scss:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | }
4 |
5 | .drop-target {
6 | border: 4px dashed #FFF;
7 | }
8 |
9 | .notes-shell {
10 | display: inline-block;
11 |
12 | .note {
13 | display: flex;
14 | align-items: center;
15 | flex-direction: columns;
16 | width: 200px;
17 | height: 250px;
18 | }
19 | }
20 |
21 | .container {
22 | display: flex;
23 |
24 | .list {
25 | width: 200px;
26 | }
27 | }
28 |
29 | .notes {
30 | width: 100%;
31 |
32 | .notes-container, .add-notes {
33 | display: flex;
34 | justify-content: space-around;
35 | .note-title {
36 | border-bottom: 1px solid #000;
37 | display: flex;
38 | align-items: center;
39 | justify-content: space-around;
40 | }
41 |
42 | .note-body {
43 | display: flex;
44 | justify-content: space-around;
45 | height: 190px;
46 |
47 | .note-text {
48 | background: none;
49 | border: none;
50 | outline: none;
51 | resize: none;
52 | height: 100%;
53 | width: 100%;
54 | max-height: 180px;
55 | }
56 | }
57 | }
58 | }
--------------------------------------------------------------------------------
/webpack/utils/app-server.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var cp = require('child_process');
4 | var path = require('path');
5 | var watch = require('node-watch');
6 | var opener = require('opener');
7 | var _ = require('lodash');
8 |
9 | var APP_PATH = path.join(__dirname, '../../src/server');
10 | var WATCH_PATH = path.join(__dirname, '../../src/server');
11 |
12 | var app;
13 | var isStarted;
14 |
15 | var startApp = function () {
16 | var env = _.assign({NODE_ENV: 'development', WEBPACK_SERVE: true}, process.env);
17 | app = cp.fork(APP_PATH, {env: env});
18 | app.once('message', function(message) {
19 | if (message.match(/^online$/)) {
20 | if(!isStarted) {
21 | isStarted = true;
22 | opener('http://localhost:3000');
23 |
24 | watch(
25 | WATCH_PATH,
26 | function(file) {
27 | console.log('restarting app');
28 | app.kill('SIGTERM');
29 | return startApp();
30 | }
31 | );
32 | }
33 | }
34 | });
35 | };
36 |
37 | process.on('exit', function() {
38 | app.kill('SIGTERM');
39 | });
40 |
41 | module.exports = function() {
42 | if(!app) {
43 | return startApp();
44 | }
45 | };
46 |
--------------------------------------------------------------------------------
/init_data/computers.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var generator = require('./generator');
4 |
5 | var computerModel = {
6 | mark: ['ASUS', 'Lenovo', 'HP', 'Apple', 'MSI', 'Acer', 'Dell', 'Toshiba', 'Sony', 'DEXP'],
7 | model: ['GX-700', '6QE-053', '9F4-5', 'DM036T', 'Zenbook Pro', 'GL552VW-DM321T', 'EeeBook', 'Surface', 'Redhost', 'MEDIA CENTER 1574', 'Mac mini', 'Mac Pro', 'Z800', 'IdeaPad', 'y580', 'optima', 'GAMER 1748'],
8 | color: ['black', 'white', 'green', 'grey', 'yellow', 'blue', 'red', 'orange', 'violet', 'purple'],
9 | wifi: ['802.11ac', '802.11a', '802.11c'],
10 | isLaptop: [true, false],
11 | diagonal: [11.1, 11.3, 13.3, 13.7, 14.0, 15.6, 17.1, 17.3, 18.6, 20.1],
12 | coresNumber: [1, 2, 4, 8, 16],
13 | usb2: [0, 1, 2, 3],
14 | usb3: [0, 1, 2, 3, 4],
15 | ram: [1, 2, 4, 8, 16, 32, 64],
16 | memory: [128, 256, 512, 1024, 2048, 4096, 8192],
17 | videocard: ['Intel HD Graphics 3000', 'NVidia GeForce GTX 640', 'Intel HD Graphics 4000', 'NVidia GeForce GTX 730', 'NVidia GeForce GTX 950'],
18 | videomemory: [1, 2, 4, 8, 16, 32, 64],
19 | processor: ['Intel', 'AMD', 'Mac'],
20 | operatingSystem: ['Windows', 'Linux', 'Mac OS X'],
21 | price: [300, 340, 380, 420, 460, 495, 510, 530, 560, 580, 640, 670, 725, 760, 780, 840, 850, 860, 920, 940, 1020, 1150, 1280, 1500]
22 | };
23 |
24 | module.exports = generator(computerModel, 30);
25 |
--------------------------------------------------------------------------------
/src/client/containers/ExampleContainer.react.es:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React, { PropTypes } from 'react';
4 | import { bindActionCreators } from 'redux';
5 | import { connect } from 'react-redux';
6 | import * as counterActions from 'actions/exampleActions';
7 | import { Button } from 'react-bootstrap';
8 |
9 | function mapStateToProps(state) {
10 | return {
11 | counter: state.example.counter,
12 | };
13 | }
14 |
15 | function mapDispatchToProps(dispatch) {
16 | return bindActionCreators({
17 | ...counterActions,
18 | }, dispatch);
19 | }
20 |
21 | class HomeRoute extends React.Component {
22 | static propTypes: {[key: string]: Function};
23 |
24 | render() {
25 | return (
26 |
27 |
28 |
31 |
34 |
35 | {this.props.counter}
36 |
37 |
38 | );
39 | }
40 | }
41 |
42 | HomeRoute.propTypes = {
43 | increment: PropTypes.func.isRequired,
44 | counter: PropTypes.number.isRequired,
45 | };
46 |
47 | export default connect(mapStateToProps, mapDispatchToProps)(HomeRoute);
48 |
--------------------------------------------------------------------------------
/src/client/utils/request.es:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import request from 'superagent';
4 |
5 | const API_URL = '/api/v1';
6 | const HEADERS = { Accept: 'application/json' };
7 | const setPrefix = (prefix) => {
8 | return (req) => {
9 | if (req.url && req.url.indexOf(prefix) === -1) {
10 | req.url = `${prefix}${req.url}`;
11 | }
12 |
13 | return req;
14 | };
15 | };
16 |
17 | var builder = (httpMethod, apiMethod, params, headers=HEADERS) => {
18 | let paramsTransport = httpMethod === 'get' ?
19 | 'query' :
20 | 'send';
21 |
22 | return new Promise((resolve, reject) => {
23 | request[httpMethod](apiMethod)
24 | .set(headers)
25 | [paramsTransport](params)
26 | .use(setPrefix(API_URL))
27 | .end((err, res) => {
28 | if (err || !res || !res.ok) {
29 | reject(err);
30 | } else {
31 | resolve(res.body, res);
32 | }
33 | });
34 | });
35 | };
36 |
37 | export default {
38 | fetch(httpMethod, apiMethod, params) {
39 | return builder(httpMethod, apiMethod, params);
40 | },
41 |
42 | get(apiMethod, params, queueName) {
43 | return builder('get', apiMethod, params);
44 | },
45 |
46 | post(apiMethod, params, queueName) {
47 | return builder('post', apiMethod, params);
48 | }
49 | };
50 |
--------------------------------------------------------------------------------
/src/server/schema/printerType.js:
--------------------------------------------------------------------------------
1 | import {
2 | GraphQLObjectType,
3 | GraphQLBoolean,
4 | GraphQLString,
5 | GraphQLFloat,
6 | } from 'graphql';
7 |
8 | export const PrinterType = new GraphQLObjectType({
9 | name: 'printer',
10 | description: 'A phone.',
11 | fields: () => ({
12 | mark: {
13 | type: GraphQLString,
14 | description: 'The mark of printer.'
15 | },
16 | model: {
17 | type: GraphQLString,
18 | description: 'The model of printer.'
19 | },
20 | color: {
21 | type: GraphQLString,
22 | description: 'The color of printer.'
23 | },
24 | wifi: {
25 | type: GraphQLString,
26 | description: 'The wifi of printer.'
27 | },
28 | isMFP: {
29 | type: GraphQLBoolean,
30 | description: 'The isMFP of printer.'
31 | },
32 | isBlackWhite: {
33 | type: GraphQLBoolean,
34 | description: 'The isBlackWhite of printer.'
35 | },
36 | pageVelocityPerMinute: {
37 | type: GraphQLFloat,
38 | description: 'The pageVelocityPerMinute of printer.'
39 | },
40 | printTechnology: {
41 | type: GraphQLString,
42 | description: 'The printTechnology of printer.'
43 | },
44 | price: {
45 | type: GraphQLFloat,
46 | description: 'The price of printer.'
47 | }
48 | })
49 | })
50 |
--------------------------------------------------------------------------------
/src/client/containers/ListContainer.react.es:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React, { Component } from 'react';
4 | import { bindActionCreators } from 'redux';
5 | import { connect } from 'react-redux';
6 |
7 | import Items from 'components/Items.react';
8 | import AddItem from 'components/AddItem.react';
9 | import Tools from 'components/Tools';
10 |
11 | /* actions */
12 | import * as actionCreators from 'actions/items';
13 |
14 |
15 | @connect(
16 | state => state.items,
17 | dispatch => bindActionCreators(actionCreators, dispatch)
18 | )
19 | export default class List extends Component {
20 | constructor(props) {
21 | super(props);
22 | }
23 |
24 | render() {
25 | return (
26 |
27 |
28 |
29 |
30 |
31 |
32 | Shop contains technologies:
33 |
34 |
35 |
36 |
39 |
40 |
41 |
42 | );
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/server/models/computers.js:
--------------------------------------------------------------------------------
1 |
2 | 'use strict';
3 |
4 | import mongoose from 'mongoose';
5 | import hookFunc from '../helpers/hookFunc';
6 |
7 | const COMPUTER_SCHEMA_NAME = 'Computer';
8 |
9 | let ComputerSchema = new mongoose.Schema({
10 | mark: { type: String, required: true, default: '' },
11 | model: { type: String, required: true, default: '' },
12 | color: { type: String, required: true, default: '' },
13 | wifi: { type: String, required: true, default: '' },
14 | isLaptop: { type: Boolean, required: true, default: true},
15 | diagonal: { type: Number, required: true, default: 0 },
16 | coresNumber: { type: Number, required: true, default: 0 },
17 | usb2: { type: Number, required: true, default: 0 },
18 | usb3: { type: Number, required: true, default: 0 },
19 | ram: { type: Number, required: true, default: 0 },
20 | memory: { type: Number, required: true, default: 0 },
21 | videocard: { type: String, required: true, default: '' },
22 | videomemory: { type: Number, required: true, default: 0 },
23 | processor: { type: String, required: true, default: '' },
24 | operatingSystem: { type: String, required: true, default: '' },
25 | price: { type: Number, required: true, default: 0 }
26 | }, {
27 | versionKey: false
28 | });
29 |
30 | ComputerSchema.plugin(require('mongoose-timestamp'));
31 |
32 | ComputerSchema.pre('save', function(next, done) {
33 | hookFunc(next, done, this, COMPUTER_SCHEMA_NAME);
34 | })
35 |
36 | export default mongoose.model(COMPUTER_SCHEMA_NAME, ComputerSchema);
37 |
--------------------------------------------------------------------------------
/src/client/components/Items.react.es:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React, { Component } from 'react';
4 |
5 | export default class Items extends Component {
6 |
7 | static propTypes = {
8 | items: React.PropTypes.array,
9 | delItem: React.PropTypes.func,
10 | };
11 |
12 | constructor(props) {
13 | super(props);
14 | }
15 |
16 | onDelete = (event) => {
17 | event.preventDefault();
18 | const index = event.currentTarget.dataset.index;
19 | this.props.delItem(index);
20 | };
21 |
22 | render() {
23 | const { items } = this.props;
24 |
25 | return (
26 |
27 | {!items.length &&
Array is empty}
28 | {
29 | items.map((item, index) =>
30 |
31 |
43 |
44 | )
45 | }
46 |
47 | );
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/client/components/TopImage/index.es:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React, { Component } from 'react';
4 | import LoadingOrderAnimation from 'react-loading-order-with-animation';
5 |
6 | export class TopImage extends Component {
7 |
8 | constructor(props) {
9 | super(props);
10 | }
11 |
12 | render() {
13 | return (
14 |
15 |
16 |
17 |
23 |
24 | Welcome to our shop!!!
25 |
26 |
27 |
33 |
34 | Start spend your money! :)
35 |
36 |
37 |
38 |
39 |
40 | );
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/server/middleware/express.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import path from 'path';
4 | import helmet from 'helmet';
5 | import hpp from 'hpp';
6 | import bodyParser from 'body-parser';
7 | import staticsRouter from '../routes/statics';
8 | import env from '../../../env';
9 | import GraphQLHTTP from 'express-graphql';
10 | import schema from '../schema/schema';
11 |
12 | const errorHandler = (err, req, res, next) => {
13 | let errData = err.message ? err.message : err;
14 |
15 | res.status(err.status || 500);
16 |
17 | if (req.accepts('json')) {
18 | res.json({error: true, data: errData});
19 | } else {
20 | res.render('error', {
21 | message: err.message,
22 | error: {},
23 | title: 'error'
24 | });
25 | }
26 | };
27 |
28 | export default (app) => {
29 | app.set('views', path.join(__dirname, '../views'));
30 | app.set('view engine', 'ejs');
31 | app.set('port', env.get('express:port'));
32 |
33 | // some secure headers
34 | app.use(helmet.xssFilter());
35 | app.use(helmet.frameguard('sameorigin'));
36 | app.use(helmet.hidePoweredBy());
37 | app.use(helmet.ieNoOpen());
38 | app.use(helmet.noSniff());
39 |
40 | app.use(bodyParser.urlencoded({
41 | extended: true
42 | }));
43 |
44 | app.use(bodyParser.json());
45 |
46 | app.use(hpp());
47 | app.use(env.get('app:api'), GraphQLHTTP({
48 | schema,
49 | graphiql: true // TODO: enable only for dev
50 | })
51 | );
52 | app.use('/', staticsRouter);
53 |
54 | app.use(errorHandler);
55 |
56 | console.log('express configured');
57 | };
58 |
--------------------------------------------------------------------------------
/src/client/components/AddItem.react.es:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React, { Component } from 'react';
4 | import { reduxForm } from 'redux-form';
5 | import { reset } from 'redux-form';
6 |
7 | class AddItem extends Component {
8 |
9 | static propTypes = {
10 | dispatch: React.PropTypes.func,
11 | fields: React.PropTypes.object.isRequired,
12 | items: React.PropTypes.array,
13 | addItem: React.PropTypes.func,
14 | };
15 |
16 | constructor(props) {
17 | super(props);
18 | }
19 |
20 | onAdd = (event) => {
21 | if (this.props.fields.name.value) {
22 | this.props.addItem(this.props.fields);
23 | this.props.dispatch(reset('addItem'));
24 | }
25 | event.preventDefault();
26 | };
27 |
28 | render() {
29 | const {
30 | fields: { name },
31 | } = this.props;
32 |
33 | return (
34 |
49 | );
50 | }
51 | }
52 |
53 | AddItem = reduxForm({
54 | form: 'addItem',
55 | fields: ['name'],
56 | destroyOnUnmount: false,
57 | })(AddItem);
58 |
59 | export default AddItem;
60 |
--------------------------------------------------------------------------------
/src/server/schema/phoneType.js:
--------------------------------------------------------------------------------
1 | import {
2 | GraphQLObjectType,
3 | GraphQLBoolean,
4 | GraphQLString,
5 | GraphQLFloat,
6 | GraphQLInt,
7 | } from 'graphql';
8 |
9 | export const PhoneType = new GraphQLObjectType({
10 | name: 'phone',
11 | description: 'A phone.',
12 | fields: () => ({
13 | mark: {
14 | type: GraphQLString,
15 | description: 'The mark of phone.'
16 | },
17 | model: {
18 | type: GraphQLString,
19 | description: 'The model of phone.'
20 | },
21 | color: {
22 | type: GraphQLString,
23 | description: 'The color of phone.'
24 | },
25 | wifi: {
26 | type: GraphQLString,
27 | description: 'The wifi of phone.'
28 | },
29 | gps: {
30 | type: GraphQLBoolean,
31 | description: 'The gps of phone.'
32 | },
33 | coresNumber: {
34 | type: GraphQLInt,
35 | description: 'The coresNumber of phone.'
36 | },
37 | ram: {
38 | type: GraphQLInt,
39 | description: 'The ram of phone.'
40 | },
41 | memory: {
42 | type: GraphQLInt,
43 | description: 'The memory of phone.'
44 | },
45 | camera: {
46 | type: GraphQLInt,
47 | description: 'The camera of phone.'
48 | },
49 | diagonal: {
50 | type: GraphQLFloat,
51 | description: 'The diagonal of phone.'
52 | },
53 | operatingSystem: {
54 | type: GraphQLString,
55 | description: 'The operatingSystem of phone.'
56 | },
57 | price: {
58 | type: GraphQLFloat,
59 | description: 'The price of phone.'
60 | },
61 | })
62 | });
63 |
--------------------------------------------------------------------------------
/src/client/reducers/items.es:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import types from 'constants/ActionTypes/Items';
4 |
5 | const initialState = {
6 | items: [{
7 | text: 'React',
8 | done: true
9 | }, {
10 | text: 'Redux',
11 | done: true
12 | }, {
13 | text: 'React router',
14 | done: true
15 | }, {
16 | text: 'Bootstrap webpack',
17 | done: true
18 | }, {
19 | text: 'Sass modules (sass-loader css-loader style-loader)',
20 | done: true
21 | }, {
22 | text: 'React transform',
23 | done: true
24 | }, {
25 | text: 'Redux logger',
26 | done: true
27 | }, {
28 | text: 'React document meta',
29 | done: true
30 | }, {
31 | text: 'Redux form',
32 | done: true
33 | }, {
34 | text: 'Karma',
35 | done: true
36 | }, {
37 | text: 'Mocha',
38 | done: true
39 | }, {
40 | text: 'Server-side rendering',
41 | done: false
42 | }]
43 | };
44 |
45 | export function items(state = initialState, action) {
46 | switch (action.type) {
47 | case types.ADD_ITEM:
48 | return {
49 | ...state,
50 | items: [
51 | ...state.items, {
52 | text: action.fields.name.value
53 | }
54 | ]
55 | };
56 |
57 | case types.DELETE_ITEM:
58 | return {
59 | ...state,
60 | items: [
61 | ...state.items.slice(0, action.index),
62 | ...state.items.slice(+action.index + 1)
63 | ]
64 | };
65 |
66 | default:
67 | return state;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/init_db.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | require('babel-core/register');
4 |
5 | var mongoose = require('mongoose'),
6 | env = require('./env'),
7 | models = require('./src/server/models').default;
8 |
9 | //data
10 | var phones = require('./init_data/phones'),
11 | computers = require('./init_data/computers'),
12 | printers = require('./init_data/printers');
13 |
14 | mongoose.connection.once('open', function () {
15 |
16 | var Func = models.Func,
17 | Log = models.Log,
18 | Phones = models.Phones,
19 | Computers = models.Computers,
20 | Printers = models.Printers,
21 | Notes = models.Notes;
22 |
23 | //DATA FOR FILLING
24 | var funcs = [{
25 | func: 'function() {return 0;}'
26 | }];
27 |
28 | //remove models functions
29 | var removeFuncs = function() {
30 | Func.remove({}).then(removeLogs);
31 | };
32 | var removeLogs = function() {
33 | Log.remove({}).then(removePhones);
34 | };
35 | var removePhones = function() {
36 | Phones.remove({}).then(removeComputers);
37 | };
38 | var removeComputers = function() {
39 | Computers.remove({}).then(removePrinters);
40 | };
41 | var removePrinters = function() {
42 | Printers.remove({}).then(addPhones);
43 | };
44 |
45 | //add data functions
46 | var addPhones = function() {
47 | Phones.collection.insert(phones, addComputers);
48 | };
49 | var addComputers = function() {
50 | Computers.collection.insert(computers, addPrinters);
51 | };
52 | var addPrinters = function() {
53 | Printers.collection.insert(printers, addInnerFunc);
54 | };
55 | var addInnerFunc = function() {
56 | Func.collection.insert(funcs, function() {
57 | console.log('data updated, connection is closed');
58 | mongoose.connection.close();
59 | });
60 | };
61 |
62 | //function to start working with db
63 | var startInitDb = function() {
64 | removeFuncs();
65 | };
66 | startInitDb();
67 |
68 | });
69 |
70 | mongoose.connect(env.get('mongo:uri'), env.get('mongo:options'));
71 |
--------------------------------------------------------------------------------
/src/client/reducers/__tests__/phones-reducer.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import { phones } from '../phones';
4 |
5 | describe('Phones reducer:', () => {
6 |
7 | const initialState = {
8 | phones: []
9 | };
10 |
11 | it('should return the initial state', () => {
12 | expect(phones(initialState, {})).to.deep.equal(initialState);
13 | });
14 |
15 | it('should handle ADD_PHONE', () => {
16 | const expectedState = {
17 | phones: [{
18 | name: 'iphone'
19 | }]
20 | };
21 |
22 | const phoneToAdd = {
23 | name: 'iphone'
24 | };
25 |
26 | expect(phones(initialState, {
27 | type: 'ADD_PHONE',
28 | phone: phoneToAdd,
29 | })).to.deep.equal(expectedState);
30 | });
31 |
32 | it('should handle DELETE_PHONE', () => {
33 | const expectedState = {
34 | phones: [{
35 | name: 'iphone'
36 | }]
37 | };
38 |
39 | const stateWithPhones = {
40 | phones: [{
41 | name: 'iphone'
42 | }, {
43 | name: 'samsung'
44 | }]
45 | };
46 |
47 | expect(phones(stateWithPhones, {
48 | type: 'DELETE_PHONE',
49 | index: 1
50 | })).to.deep.equal(expectedState);
51 | });
52 |
53 | it('should handle GET_ALL_PHONES', () => {
54 | const phonesToGet = [{
55 | name: 'iphone'
56 | }, {
57 | name: 'samsung'
58 | }];
59 |
60 | const expectedState = {
61 | phones: [{
62 | name: 'iphone'
63 | }, {
64 | name: 'samsung'
65 | }]
66 | };
67 |
68 | expect(phones(initialState, {
69 | type: 'GET_ALL_PHONES',
70 | phones: phonesToGet
71 | })).to.deep.equal(expectedState);
72 | });
73 | });
74 |
--------------------------------------------------------------------------------
/src/server/schema/computerType.js:
--------------------------------------------------------------------------------
1 | import {
2 | GraphQLObjectType,
3 | GraphQLBoolean,
4 | GraphQLString,
5 | GraphQLFloat,
6 | GraphQLInt,
7 | } from 'graphql';
8 |
9 | export const ComputerType = new GraphQLObjectType({
10 | name: 'Computer',
11 | description: 'A computer',
12 | fields: () => ({
13 | mark: {
14 | type: GraphQLString,
15 | description: 'The mark of computer',
16 | },
17 | model: {
18 | type: GraphQLString,
19 | description: 'The model of computer',
20 | },
21 | color: {
22 | type: GraphQLString,
23 | description: 'The color of computer'
24 | },
25 | wifi: {
26 | type: GraphQLString,
27 | description: 'The type of wifi'
28 | },
29 | isLaptop: {
30 | type: GraphQLBoolean,
31 | description: 'The computer is laptop'
32 | },
33 | diagonal: {
34 | type: GraphQLFloat,
35 | description: 'The monitor diagonal'
36 | },
37 | coresNumber: {
38 | type: GraphQLInt,
39 | description: 'The cpu core count'
40 | },
41 | usb2: {
42 | type: GraphQLInt,
43 | description: 'The usb2 ports count'
44 | },
45 | usb3: {
46 | type: GraphQLInt,
47 | description: 'The usb3 ports count'
48 | },
49 | ram: {
50 | type: GraphQLInt,
51 | description: 'The ram capacity'
52 | },
53 | memory: {
54 | type: GraphQLInt,
55 | description: 'The memory capacity'
56 | },
57 | videocard: {
58 | type: GraphQLString,
59 | description: 'The type of video card in computer'
60 | },
61 | videomemory: {
62 | type: GraphQLInt,
63 | description: 'The video memory capacity computer'
64 | },
65 | processor: {
66 | type: GraphQLString,
67 | description: 'The type of processor in computer'
68 | },
69 | operatingSystem: {
70 | type: GraphQLString,
71 | description: 'The OS on computer'
72 | },
73 | price: {
74 | type: GraphQLFloat,
75 | description: 'The price of computer'
76 | },
77 | })
78 | });
79 |
--------------------------------------------------------------------------------
/src/client/components/Modals/AddPhoneModal.react.es:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React from 'react';
4 | import {
5 | Button,
6 | Modal,
7 | Input
8 | } from 'react-bootstrap';
9 |
10 | export default class BaseModal extends React.Component {
11 | constructor(props) {
12 | super(props);
13 | this.showModal = false;
14 | }
15 |
16 | close = async () => {
17 | console.log('close modal add phone');
18 | this.showModal = false;
19 | this.forceUpdate();
20 | }
21 |
22 | open = () => {
23 | this.showModal = true;
24 | this.forceUpdate();
25 | }
26 |
27 | render() {
28 |
29 | return (
30 |
31 |
32 |
38 |
39 |
40 |
41 | Please add data about new phone
42 |
43 |
44 | Please, type fill information
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 | );
65 | }
66 | }
--------------------------------------------------------------------------------
/src/client/components/Tools/index.es:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React, { Component } from 'react';
4 | import { Image, ResponsiveEmbed } from 'react-bootstrap';
5 |
6 | /* images */
7 | const reactjs = require('./files/reactjs.png');
8 | const redux = require('./files/redux.png');
9 | const babel = require('./files/babel.png');
10 | const webpack = require('./files/webpack.png');
11 | const bootstrap = require('./files/bootstrap.png');
12 | const mocha = require('./files/mocha.png');
13 |
14 | export default class Tools extends Component {
15 |
16 | render() {
17 | return (
18 |
59 | );
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/client/components/Header.react.es:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React from 'react';
4 | import { Navbar, Nav, NavItem, NavDropdown, MenuItem, Input, Button, PageHeader } from 'react-bootstrap';
5 | import { Link } from 'react-router'
6 |
7 | export default class Header extends React.Component {
8 | render() {
9 | return (
10 |
11 |
12 |
13 |
14 |
15 | Go to Main page
16 |
17 |
18 |
19 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | );
56 | }
57 | }
--------------------------------------------------------------------------------
/webpack/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var Webpack = require('webpack');
3 | var server = require('./utils/app-server');
4 | var ExtractTextPlugin = require("extract-text-webpack-plugin");
5 |
6 | var NODE_ENV = process.env.NODE_ENV; //variable of the scope
7 |
8 | module.exports = {
9 | entry: {
10 | shop: [
11 | 'webpack-dev-server/client',
12 | 'webpack/hot/dev-server',
13 | path.join(__dirname, '../src/client/')
14 | ]
15 | },
16 |
17 | output: {
18 | path: path.join(__dirname, '../src/client/build'),
19 | filename: '[name]-build.js',
20 | publicPath: '/'
21 | },
22 |
23 | module: {
24 | loaders: [{ test: /\.html/, loader: 'html-loader' },
25 | {loader: "babel-loader",
26 | test: /\.(jsx|es)$/,
27 | query: {
28 | plugins: ['transform-runtime', 'transform-decorators-legacy'],
29 | presets: ['es2015', 'stage-0', 'react'],
30 | }
31 | },
32 | //{ test: /\.(jsx|es)$/, loader: 'babel-loader?presets[]=react,presets[]=es2015,presets[]=stage-0' },
33 | { test: /\.json$/, loader: 'json-loader' },
34 | { test: /\.json5$/, loader: 'json5-loader' },
35 | { test: /\.(png|jpg|jpeg|gif)$/, loader: 'file-loader' },
36 | { test: /\.(woff|woff2)$/, loader: 'url-loader?limit=100000' },
37 | { test: /\.(ttf|eot|wav|mp3|svg|eot|woff|woff2)$/, loader: 'file?name=[name].[ext][hash]' },
38 | { test: /\.(wav|mp3)$/, loader: 'file-loader' },
39 | { test: /\.less$/, loader: ExtractTextPlugin.extract('style', 'css-loader?minimize!less-loader') },
40 | { test: /\.(sass|scss)$/, loader: ExtractTextPlugin.extract('style-loader', 'css-loader?minimize!sass-loader') },
41 | { test: /\.css$/, loader: ExtractTextPlugin.extract('style-loader', 'css-loader?minimize') }
42 | ]
43 | },
44 |
45 | progress: true,
46 | devtool: NODE_ENV === 'development' ? "cheap-inline-module-source-map" : null,
47 |
48 | resolve: {
49 | modulesDirectories: ['node_modules', 'src/client', 'src/server'],
50 | extensions: ['', '.jsx', '.js', '.es', '.json', 'css', 'scss']
51 | },
52 |
53 | plugins: [
54 | new ExtractTextPlugin('[name]-build.css'),
55 | new Webpack.HotModuleReplacementPlugin(),
56 | new Webpack.NoErrorsPlugin(),
57 |
58 | new Webpack.DefinePlugin({
59 | 'process.env': {
60 | BROWSER: JSON.stringify(true),
61 | NODE_ENV: JSON.stringify('development')
62 | }
63 | }),
64 |
65 | function () {
66 | this.plugin("done", function(stats) {
67 | if (stats.compilation.errors && stats.compilation.errors.length && process.argv.indexOf('--watch') == -1)
68 | {
69 | console.log(stats.compilation.errors);
70 | } else {
71 | console.log('success build');
72 | server();
73 | }
74 | });
75 | }
76 | ]
77 | };
78 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Shop",
3 | "version": "0.0.6",
4 | "description": "Shop",
5 | "main": "index.html",
6 | "scripts": {
7 | "start": "node ./webpack.js",
8 | "build": "./node_modules/.bin/webpack --config ./webpack/webpack.config.js",
9 | "add-db": "node ./init_db.js",
10 | "clean": "rm -r ./client/build",
11 | "test-client": "mocha --timeout 10000 --require=\"mocha_settings/client-inject.js\" --compilers js:babel-core/register \"src/client/**/__tests__/**/*.spec.js\"",
12 | "test-server": "mocha --timeout 10000 --require=\"mocha_settings/server-inject.js\" --compilers js:babel-core/register \"src/server/**/__tests__/**/*.spec.js\"",
13 | "test": "npm run test-client && npm run test-server"
14 | },
15 | "repository": {
16 | "type": "git",
17 | "url": "https://github.com/yankouskia/shop"
18 | },
19 | "author": "yankouskia",
20 | "license": "ISC",
21 | "devDependencies": {
22 | "babel-core": "6.10.4",
23 | "babel-eslint": "3.0.1",
24 | "babel-loader": "6.2.3",
25 | "babel-plugin-transform-decorators-legacy": "1.3.4",
26 | "babel-plugin-transform-runtime": "6.5.2",
27 | "babel-polyfill": "6.5.0",
28 | "babel-preset-es2015": "6.5.0",
29 | "babel-preset-react": "6.5.0",
30 | "babel-preset-stage-0": "6.5.0",
31 | "babel-runtime": "6.5.0",
32 | "bluebird": "3.3.4",
33 | "chai": "3.5.0",
34 | "chai-subset": "1.2.1",
35 | "classnames": "2.2.3",
36 | "css-loader": "0.15.4",
37 | "ejs": "2.3.4",
38 | "eslint": "0.22.0",
39 | "eslint-plugin-react": "2.2.0",
40 | "express": "4.13.1",
41 | "extract-text-webpack-plugin": "0.8.2",
42 | "file-loader": "0.8.4",
43 | "history": "2.0.0-rc2",
44 | "html-loader": "0.4.3",
45 | "immutable": "3.7.4",
46 | "json-loader": "0.5.2",
47 | "json5": "0.4.0",
48 | "json5-loader": "0.6.0",
49 | "lodash": "4.6.1",
50 | "mocha": "2.4.5",
51 | "mongoose-timestamp": "0.5.0",
52 | "node-libs-browser": "0.5.2",
53 | "node-sass": "3.10.0",
54 | "node-watch": "0.3.5",
55 | "nskeymirror": "0.1.2",
56 | "opener": "1.4.1",
57 | "react": "0.14.7",
58 | "react-dnd": "2.0.2",
59 | "react-loading-order-with-animation": "1.0.0",
60 | "react-dnd-html5-backend": "2.1.2",
61 | "react-document-meta": "2.0.0-rc2",
62 | "react-dom": "0.14.7",
63 | "react-redux": "4.0.6",
64 | "redux": "3.1.4",
65 | "redux-form": "4.1.5",
66 | "redux-logger": "2.4.0",
67 | "react-router": "2.0.0-rc5",
68 | "redux-thunk": "0.1.0",
69 | "reselect": "2.2.1",
70 | "sass-loader": "1.0.2",
71 | "sinon-chai": "2.8.0",
72 | "style-loader": "0.12.3",
73 | "superagent": "1.8.1",
74 | "url-loader": "0.5.6",
75 | "webpack": "1.10.1"
76 | },
77 | "dependencies": {
78 | "async": "1.5.0",
79 | "body-parser": "1.14.1",
80 | "bootstrap": "3.3.6",
81 | "bootstrap-sass": "3.3.6",
82 | "express-graphql": "^0.5.4",
83 | "graphql": "^0.7.2",
84 | "helmet": "0.14.0",
85 | "hpp": "0.2.0",
86 | "material-ui": "0.14.0",
87 | "mongoose": "4.4.8",
88 | "nconf": "0.8.2",
89 | "react-bootstrap": "0.28.3",
90 | "webpack-dev-server": "1.12.1"
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/src/client/containers/PhonesContainer.react.es:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React, { Component, PropTypes } from 'react';
4 | import {
5 | Accordion,
6 | Panel,
7 | Well,
8 | ListGroupItem,
9 | ListGroup
10 | } from 'react-bootstrap';
11 | import PhonesAddModal from 'components/Modals/AddPhoneModal.react';
12 | import { bindActionCreators } from 'redux';
13 | import { connect } from 'react-redux';
14 |
15 | import * as phonesActions from 'actions/phones';
16 |
17 | function mapStateToProps(state) {
18 | return {
19 | phones: state.phones.phones,
20 | };
21 | }
22 |
23 | function mapDispatchToProps(dispatch) {
24 | return bindActionCreators({
25 | ...phonesActions,
26 | }, dispatch);
27 | }
28 |
29 | class PhonesTable extends Component {
30 | constructor(props) {
31 | super(props);
32 | this.props.getAllPhones();
33 | }
34 |
35 | render() {
36 | let phones = this.props.phones;
37 | return (
38 |
39 | Here is all list of our phones
40 |
41 |
42 |
43 | {
44 | phones.sort((left, right) => {return left.mark.toLowerCase() > right.mark.toLowerCase() ? 1 : -1}).map((phone, index) => {
45 | const header = `${phone.mark} ${phone.model}; System: ${phone.operatingSystem}; Color: ${phone.color}`;
46 | return
47 |
48 | {phone.ram}
49 | {phone.camera}
50 | {phone.color}
51 | {phone.coresNumber}
52 | {phone.gps}
53 | {phone.wifi}
54 | {phone.diagonal}
55 | {phone.operatingSystem}
56 | {phone.price}
57 |
58 |
59 | })
60 | }
61 |
62 |
63 |
64 | );
65 | }
66 | }
67 |
68 | PhonesTable.propTypes = {
69 | getAllPhones: PropTypes.func.isRequired,
70 | addPhone: PropTypes.func.isRequired,
71 | deletePhone: PropTypes.func.isRequired,
72 | phones: PropTypes.array.isRequired
73 | };
74 |
75 | export default connect(mapStateToProps, mapDispatchToProps)(PhonesTable);
--------------------------------------------------------------------------------
/src/client/containers/ComputersContainer.react.es:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React, { Component, PropTypes } from 'react';
4 | import {
5 | Accordion,
6 | Panel,
7 | Well,
8 | ListGroupItem,
9 | ListGroup
10 | } from 'react-bootstrap';
11 | import { bindActionCreators } from 'redux';
12 | import { connect } from 'react-redux';
13 |
14 | import * as computersActions from 'actions/computers';
15 |
16 | function mapStateToProps(state) {
17 | return {
18 | computers: state.computers.computers,
19 | };
20 | }
21 |
22 | function mapDispatchToProps(dispatch) {
23 | return bindActionCreators({
24 | ...computersActions,
25 | }, dispatch);
26 | }
27 |
28 | class ComputersTable extends Component {
29 | constructor(props) {
30 | super(props);
31 | this.props.getAllComputers();
32 | }
33 |
34 | render() {
35 | let computers = this.props.computers;
36 | return (
37 |
38 | Here is all list of our computers
39 |
40 |
41 | {
42 | computers.sort((left, right) => {return left.mark.toLowerCase() > right.mark.toLowerCase() ? 1 : -1}).map((computer, index) => {
43 | const header = `${computer.mark} ${computer.model}; Operating System: ${computer.operatingSystem}; Memory(GB): ${computer.memory}`;
44 | return
45 |
46 | {computer.color}
47 | {computer.wifi}
48 | {computer.isLaptop ? 'Yes' : 'No'}
49 | {computer.diagonal}
50 | {computer.coresNumber}
51 | {computer.usb2}
52 | {computer.usb3}
53 | {computer.ram}
54 | {computer.memory}
55 | {computer.videocard}
56 | {computer.videomemory}
57 | {computer.operatingSystem}
58 | {computer.price}
59 |
60 |
61 | })
62 | }
63 |
64 |
65 |
66 | );
67 | }
68 | }
69 |
70 | ComputersTable.propTypes = {
71 | getAllComputers: PropTypes.func.isRequired,
72 | computers: PropTypes.array.isRequired
73 | };
74 |
75 | export default connect(mapStateToProps, mapDispatchToProps)(ComputersTable);
--------------------------------------------------------------------------------
/src/server/schema/schema.js:
--------------------------------------------------------------------------------
1 | import {
2 | GraphQLObjectType,
3 | GraphQLSchema,
4 | GraphQLList,
5 | GraphQLString,
6 | GraphQLBoolean,
7 | GraphQLInt,
8 | GraphQLFloat,
9 | GraphQLNonNull
10 | } from 'graphql';
11 |
12 | import { ComputerType } from './computerType';
13 | import { PhoneType } from './phoneType';
14 | import { PrinterType } from './printerType';
15 |
16 | import ComputerModel from '../models/computers';
17 | import PhoneModel from '../models/phones';
18 | import PrinterModel from '../models/printers';
19 |
20 | import computersCtrl from '../controllers/computers'
21 | import phonesCtrl from '../controllers/phones';
22 | import printersCtrl from '../controllers/printers';
23 |
24 |
25 | let schema = new GraphQLSchema({
26 | query: new GraphQLObjectType({
27 | name: 'RootQueryType',
28 | fields: {
29 | computers: {
30 | type: new GraphQLList(ComputerType),
31 | args: {
32 | mark: {
33 | description: 'The mark of computer',
34 | type: GraphQLString
35 | }
36 | },
37 | resolve: (root, { mark }) => computersCtrl.getAll(mark)
38 | },
39 | phones: {
40 | type: new GraphQLList(PhoneType),
41 | args: {
42 | mark: {
43 | type: GraphQLString,
44 | description: 'The mark of phone for search.'
45 | }
46 | },
47 | resolve: (root, { mark }) => phonesCtrl.getAll(mark)
48 | },
49 | printers: {
50 | type: new GraphQLList(PrinterType),
51 | args: {
52 | mark: {
53 | type: GraphQLString,
54 | description: 'The mark of printer for search.'
55 | }
56 | },
57 | resolve: (root, { mark }) => printersCtrl.getAll(mark)
58 | }
59 | }
60 | }),
61 |
62 | mutation: new GraphQLObjectType({
63 | name: 'Mutation',
64 | fields: {
65 | insertComputer: {
66 | type: ComputerType,
67 | args: {
68 | mark: {
69 | type: new GraphQLNonNull(GraphQLString),
70 | description: 'The mark of computer',
71 | },
72 | model: {
73 | type: new GraphQLNonNull(GraphQLString),
74 | description: 'The model of computer',
75 | },
76 | color: {
77 | type: new GraphQLNonNull(GraphQLString),
78 | description: 'The color of computer'
79 | },
80 | wifi: {
81 | type: new GraphQLNonNull(GraphQLString),
82 | description: 'The type of wifi'
83 | },
84 | isLaptop: {
85 | type: new GraphQLNonNull(GraphQLBoolean),
86 | description: 'The computer is laptop'
87 | },
88 | diagonal: {
89 | type: new GraphQLNonNull(GraphQLFloat),
90 | description: 'The monitor diagonal'
91 | },
92 | coresNumber: {
93 | type: new GraphQLNonNull(GraphQLInt),
94 | description: 'The cpu core count'
95 | },
96 | usb2: {
97 | type: new GraphQLNonNull(GraphQLInt),
98 | description: 'The usb2 ports count'
99 | },
100 | usb3: {
101 | type: new GraphQLNonNull(GraphQLInt),
102 | description: 'The usb3 ports count'
103 | },
104 | ram: {
105 | type: new GraphQLNonNull(GraphQLInt),
106 | description: 'The ram capacity'
107 | },
108 | memory: {
109 | type: new GraphQLNonNull(GraphQLInt),
110 | description: 'The memory capacity'
111 | },
112 | videocard: {
113 | type: new GraphQLNonNull(GraphQLString),
114 | description: 'The type of video card in computer'
115 | },
116 | videomemory: {
117 | type: new GraphQLNonNull(GraphQLInt),
118 | description: 'The video memory capacity computer'
119 | },
120 | processor: {
121 | type: new GraphQLNonNull(GraphQLString),
122 | description: 'The type of processor in computer'
123 | },
124 | operatingSystem: {
125 | type: new GraphQLNonNull(GraphQLString),
126 | description: 'The OS on computer'
127 | },
128 | price: {
129 | type: new GraphQLNonNull(GraphQLFloat),
130 | description: 'The price of computer'
131 | }
132 | },
133 | resolve: (obj, computer ) => computersCtrl.add(computer)
134 | },
135 | insertPhone: {
136 | type: PhoneType,
137 | args: {
138 | mark: {
139 | type: new GraphQLNonNull(GraphQLString),
140 | description: 'The mark of phone.'
141 | },
142 | model: {
143 | type: new GraphQLNonNull(GraphQLString),
144 | description: 'The model of phone.'
145 | },
146 | color: {
147 | type: new GraphQLNonNull(GraphQLString),
148 | description: 'The color of phone.'
149 | },
150 | wifi: {
151 | type: new GraphQLNonNull(GraphQLString),
152 | description: 'The wifi of phone.'
153 | },
154 | gps: {
155 | type: new GraphQLNonNull(GraphQLBoolean),
156 | description: 'The gps of phone.'
157 | },
158 | coresNumber: {
159 | type: new GraphQLNonNull(GraphQLInt),
160 | description: 'The coresNumber of phone.'
161 | },
162 | ram: {
163 | type: new GraphQLNonNull(GraphQLInt),
164 | description: 'The ram of phone.'
165 | },
166 | memory: {
167 | type: new GraphQLNonNull(GraphQLInt),
168 | description: 'The memory of phone.'
169 | },
170 | camera: {
171 | type: new GraphQLNonNull(GraphQLInt),
172 | description: 'The camera of phone.'
173 | },
174 | diagonal: {
175 | type: new GraphQLNonNull(GraphQLFloat),
176 | description: 'The diagonal of phone.'
177 | },
178 | operatingSystem: {
179 | type: new GraphQLNonNull(GraphQLString),
180 | description: 'The operatingSystem of phone.'
181 | },
182 | price: {
183 | type: new GraphQLNonNull(GraphQLFloat),
184 | description: 'The price of phone.'
185 | }
186 | },
187 | resolve: (obj, phone) => phonesCtrl.add(phone)
188 | },
189 | insertPrinter: {
190 | type: PrinterType,
191 | args: {
192 | mark: {
193 | type: new GraphQLNonNull(GraphQLString),
194 | description: 'The mark of printer.'
195 | },
196 | model: {
197 | type: new GraphQLNonNull(GraphQLString),
198 | description: 'The model of printer.'
199 | },
200 | color: {
201 | type: new GraphQLNonNull(GraphQLString),
202 | description: 'The color of printer.'
203 | },
204 | wifi: {
205 | type: new GraphQLNonNull(GraphQLString),
206 | description: 'The wifi of printer.'
207 | },
208 | isMFP: {
209 | type: new GraphQLNonNull(GraphQLBoolean),
210 | description: 'The isMFP of printer.'
211 | },
212 | isBlackWhite: {
213 | type: new GraphQLNonNull(GraphQLBoolean),
214 | description: 'The isBlackWhite of printer.'
215 | },
216 | pageVelocityPerMinute: {
217 | type: new GraphQLNonNull(GraphQLFloat),
218 | description: 'The pageVelocityPerMinute of printer.'
219 | },
220 | printTechnology: {
221 | type: new GraphQLNonNull(GraphQLString),
222 | description: 'The printTechnology of printer.'
223 | },
224 | price: {
225 | type: new GraphQLNonNull(GraphQLFloat),
226 | description: 'The price of printer.'
227 | }
228 | },
229 | resolve: (obj, printer) => printersCtrl.add(printer)
230 | }
231 | }
232 | })
233 | });
234 |
235 | export default schema;
236 |
--------------------------------------------------------------------------------