├── .gitignore
├── react-client
├── src
│ ├── index.jsx
│ ├── components
│ │ ├── Header.js
│ │ ├── Grid.js
│ │ ├── Picker.js
│ │ └── Title.js
│ ├── app.jsx
│ └── app.css
└── dist
│ └── index.html
├── server
└── index.js
├── database-mongo
├── schema.js
└── index.js
├── webpack.config.js
├── package.json
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | react-client/dist/bundle.js
3 | package-lock.json
--------------------------------------------------------------------------------
/react-client/src/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App.jsx';
4 |
5 | ReactDOM.render( , document.getElementById('root'));
6 |
--------------------------------------------------------------------------------
/react-client/dist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Moodboard.io
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/react-client/src/components/Header.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Title from './Title.js';
3 | import Picker from './Picker.js';
4 | import { Row, Col } from 'antd';
5 |
6 | export default class Header extends React.Component {
7 |
8 | render() {
9 | return (
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | )
19 | }
20 | }
21 |
22 |
--------------------------------------------------------------------------------
/server/index.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var path = require('path');
3 | var bodyParser = require('body-parser');
4 | var items = require('../database-mongo');
5 |
6 | const port = process.env.PORT || 3000;
7 |
8 | var app = express();
9 |
10 | app.use(express.static(__dirname + '/../react-client/dist/'));
11 |
12 | app.get('/', function (req, res) {
13 | items.selectAll(function(err, data) {
14 | if(err) {
15 | res.sendStatus(500);
16 | } else {
17 | res.json(data);
18 | }
19 | });
20 | });
21 |
22 | app.listen(port, function() {
23 | console.log('listening on port ${`port`}!');
24 | });
25 |
26 |
--------------------------------------------------------------------------------
/react-client/src/components/Grid.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Draggable from 'react-draggable';
3 |
4 |
5 | export default class Grid extends React.Component {
6 | constructor(props) {
7 | super(props);
8 | }
9 |
10 | render() {
11 | let imgs = this.props.imageList.map( (img, i) => {
12 | return
13 | })
14 |
15 | return (
16 |
17 | { !this.props.imageList.length ?
18 |
19 | Start by adding an image!
20 | :
21 |
22 | {imgs}
23 |
24 | }
25 |
26 | )
27 | }
28 | }
--------------------------------------------------------------------------------
/database-mongo/schema.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 | const Schema = mongoose.Schema;
3 |
4 | const userSchema = new Schema({
5 | username: String,
6 | password: String,
7 | boards: Array // of boardIds
8 | });
9 |
10 | const boardSchema = new Schema({
11 | title: String,
12 | images: [ { path: String } ],
13 | userId: String
14 | });
15 |
16 | // const imageSchema = new Schema({
17 | // path: String,
18 | // userId: String,
19 | // boardId: String
20 | // });
21 |
22 | const User = mongoose.model('User', userSchema);
23 | const Board = mongoose.model('Board', boardSchema);
24 | // const Image = mongoose.model('Image', imageSchema);
25 |
26 | module.exports = {
27 | User,
28 | Board,
29 | // Image
30 | };
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var SRC_DIR = path.join(__dirname, '/react-client/src');
3 | var DIST_DIR = path.join(__dirname, '/react-client/dist');
4 |
5 | module.exports = {
6 | entry: `${SRC_DIR}/index.jsx`,
7 | output: {
8 | filename: 'bundle.js',
9 | path: DIST_DIR
10 | },
11 | module : {
12 | rules: [
13 | {
14 | test: /\.css$/,
15 | use: [
16 | { loader: 'style-loader' },
17 | { loader: 'css-loader' },
18 | ]
19 | },
20 | {
21 | test : /\.jsx?/,
22 | include : SRC_DIR,
23 | loader : 'babel-loader',
24 | query: {
25 | presets: ['react', 'es2015']
26 | }
27 | }
28 | ]
29 | }
30 | };
--------------------------------------------------------------------------------
/react-client/src/app.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import {Row} from 'antd';
3 | import './app.css';
4 | import Header from './components/Header.js';
5 | import Grid from './components/Grid.js';
6 |
7 | export default class App extends React.Component {
8 | constructor() {
9 | super();
10 |
11 | this.state = {
12 | imageList: []
13 | };
14 |
15 | this.addImage = this.addImage.bind(this);
16 |
17 | }
18 |
19 | addImage(imgObj) {
20 | this.setState({
21 | imageList: [...this.state.imageList, imgObj]
22 | });
23 | }
24 |
25 | render() {
26 | return (
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | );
37 | }
38 | }
--------------------------------------------------------------------------------
/database-mongo/index.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 | mongoose.connect('mongodb://localhost/test');
3 |
4 | const db = mongoose.connection;
5 | const Schema = mongoose.Schema;
6 |
7 | db.on('error', function() {
8 | console.log('ERROR: Mongoose connection error!');
9 | });
10 |
11 | db.once('open', function() {
12 | console.log('SUCCESS: Mongoose connected successfully!');
13 | });
14 |
15 | /************ HELPER FUNCTIONS *************/
16 |
17 | const boardSchema = new Schema({
18 | title: String,
19 | images: [ { path: String } ],
20 | userId: String
21 | });
22 |
23 | let Board = mongoose.model('Board', boardSchema);
24 |
25 | let saveBoard = (board) => {
26 | new Board ({
27 | id: board.id,
28 | title: board.title,
29 | images: board.images
30 | })
31 | .save()
32 | .then( () => {
33 | console.log(board.name, 'was saved to the database.')
34 | });
35 | }
--------------------------------------------------------------------------------
/react-client/src/components/Picker.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactFilestack from 'filestack-react';
3 |
4 | export default class Picker extends React.Component {
5 | constructor(props) {
6 | super(props);
7 |
8 | this.handleUpload = this.handleUpload.bind(this);
9 | }
10 |
11 | handleUpload(result) {
12 | var that = this;
13 | if (result.filesUploaded.length) {
14 | result.filesUploaded.forEach( (file) => {
15 | var url = file.url.slice(0, 33)+ 'resize=width:300/' + file.url.slice(33);
16 | that.props.addImage(url);
17 | });
18 | }
19 | }
20 |
21 | render() {
22 | return (
23 |
24 |
30 |
31 | )
32 | }
33 | }
34 |
35 |
--------------------------------------------------------------------------------
/react-client/src/app.css:
--------------------------------------------------------------------------------
1 | @import '~antd/dist/antd.css';
2 | @import url('https://fonts.googleapis.com/css?family=Inconsolata');
3 |
4 | .App {
5 | text-align: center;
6 | }
7 |
8 | /********* TITLE **********/
9 |
10 | #input {
11 | font-family: 'Inconsolata', monospace;
12 | font-size: 32px;
13 | height: 75px;
14 | width: 670px;
15 | margin-left: 5vw;
16 | }
17 |
18 | #title {
19 | font-family: 'Inconsolata', monospace;
20 | font-size: 36px;
21 | color: #333333;
22 | text-align: left;
23 | margin-left: 5.8vw;
24 | margin-top: 2vh;
25 | }
26 |
27 | #placeholder {
28 | font-family: 'Inconsolata', monospace;
29 | font-size: 50px;
30 | font-weight: bold;
31 | color: #dbdbdb;
32 | width: 400px;
33 | position: absolute;
34 | top: 25vh;
35 | left: 37.5vw;
36 | }
37 |
38 | /********* BUTTON **********/
39 |
40 | .btn {
41 | font-family: Courier New;
42 | font-size: 50px;
43 | color: #333333;
44 |
45 | background: #ebebeb;
46 | border: none;
47 | text-decoration: none;
48 | position: relative;
49 | padding: 0px 24px 0px 24px;
50 |
51 | margin-left: 10vw;
52 | }
53 |
54 | .btn:hover {
55 | background: #dbdbdb;
56 | text-decoration: none;
57 | }
58 |
--------------------------------------------------------------------------------
/react-client/src/components/Title.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Input from 'antd/lib/input';
3 |
4 | export default class Title extends React.Component {
5 | constructor() {
6 | super();
7 |
8 | this.state = {
9 | title: '',
10 | editable: true
11 | };
12 |
13 | this.handleTitle = this.handleTitle.bind(this);
14 | this.toggleEditable = this.toggleEditable.bind(this);
15 |
16 | }
17 |
18 | handleTitle(e) {
19 | this.setState({title: e.target.value});
20 | }
21 |
22 | toggleEditable() {
23 | if (!this.state.title.length) {
24 | return;
25 | } else {
26 | this.setState({editable: !this.state.editable});
27 | }
28 | }
29 |
30 | render() {
31 | return (
32 |
33 | { this.state.editable ?
34 | :
40 |
42 | {this.state.title}
43 |
44 | }
45 |
46 | )
47 | }
48 | }
49 |
50 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "moodboard",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "react-dev": "webpack -d --watch",
8 | "server-dev": "nodemon server/index.js",
9 | "start": "nodemon server/index.js",
10 | "postinstall": "webpack -p --config ./webpack.config.js --progress"
11 | },
12 | "author": "Beth Johnson",
13 | "license": "ISC",
14 | "devDependencies": {
15 | "babel-core": "^6.23.1",
16 | "babel-loader": "^6.3.2",
17 | "babel-preset-es2015": "^6.22.0",
18 | "babel-preset-react": "^6.23.0",
19 | "webpack": "^2.2.1"
20 | },
21 | "dependencies": {
22 | "antd": "^3.0.2",
23 | "babel-core": "^6.23.1",
24 | "babel-loader": "^6.3.2",
25 | "babel-preset-es2015": "^6.22.0",
26 | "babel-preset-react": "^6.23.0",
27 | "body-parser": "^1.18.2",
28 | "css-loader": "^0.28.9",
29 | "express": "^4.16.2",
30 | "filestack-react": "^1.3.8",
31 | "mongoose": "^4.13.7",
32 | "morgan": "^1.9.0",
33 | "react": "^16.2.0",
34 | "react-dom": "^16.2.0",
35 | "react-draggable": "^3.0.4",
36 | "react-scripts": "1.0.17",
37 | "webpack": "^2.2.1"
38 | },
39 | "engines": {
40 | "npm": "4.6.1",
41 | "node": "9.4.0"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This was built as my first coding project at Hack Reactor.
2 |
3 | ## A minimalist moodboard ##
4 | #### Demo here: [Moodboard](http://moodboarded.herokuapp.com) ####
5 |
6 | Gather one, gather all (images).
7 |
8 | For the person who needs to curate their spring/summer vs. fall/winter aestheti*que*.
9 | For the person upgrading their interior design to a Scandinavian post-modern architecture that doesn't scream "IKEA".
10 | But mainly for the person trying to draw inspiration from a bunch of images mish-mashed together in a semi-random, pleasing manner.
11 |
12 | Pinterest is painfully clunky - and between friends planning their future wedding, ridiculously obvious life 'hacks', and various recipes that require thirty ingredients and five hours to make, you realize it's probably not the platform for you.
13 | Photoshop and Sketch are expensive, and you don't feel like saving a photo just to upload it onto a Powerpoint slide and then deleting it from your hard drive.
14 |
15 | Moodboard.io is a simple, bare-bones moodboard generator that grabs any image URL you feed it and renders it on a blank page, until your page is filled with only images that you've carefully curated.
16 |
17 | > "My summer 2018 aesthetic is hoe... but make it fashion." -Jackie Fu, developer
18 |
19 | > "Moodboard cleared my acne, watered my crops, and cured my depression." -Someone, probably
20 |
--------------------------------------------------------------------------------