Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
a915b36967 | |||
337b12ad29 |
27
README.md
27
README.md
@ -1,18 +1,19 @@
|
||||
you need postgres!
|
||||
# TODO for refactoring
|
||||
|
||||
- localhost needs to be trusted
|
||||
- update renderProject to renderProjectView
|
||||
- update renderProjects to renderProjectsView
|
||||
- create ProjectsView, ProjectTasksView, UserProjectsView
|
||||
|
||||
```
|
||||
local all all trust
|
||||
host all all 127.0.0.1/32 trust
|
||||
host all all ::1/128 trust
|
||||
```
|
||||
- create TasksView, UserTasksView
|
||||
|
||||
- create a user
|
||||
`sudo -u postgres createuser --interactive`
|
||||
# User Stories
|
||||
|
||||
- database: tasks-mockup
|
||||
`createdb tasks-backend`
|
||||
As a user, I want to:
|
||||
|
||||
- seed database
|
||||
`npm run seed`
|
||||
- View all available projects (should we assume all is public for now?)
|
||||
- View all available tasks
|
||||
- View all available profiles (e.g., to find potential collaborators)
|
||||
|
||||
- View a single project with its tasks (including unassigned)
|
||||
- View a single profile with tasks assigned (to see my tasks, to see anothers' tasks)
|
||||
- Hide completed tasks
|
||||
|
745
package-lock.json
generated
745
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
15
package.json
15
package.json
@ -1,26 +1,17 @@
|
||||
{
|
||||
"name": "tasks-mockup",
|
||||
"name": "my-app",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"axios": "^0.19.0",
|
||||
"concurrently": "^4.0.1",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.17.1",
|
||||
"morgan": "^1.9.1",
|
||||
"nodemon": "^1.19.1",
|
||||
"pg": "^7.11.0",
|
||||
"sequelize": "^5.8.11",
|
||||
"bootstrap-imageupload": "^1.1.3",
|
||||
"http-proxy-middleware": "^0.19.1",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6",
|
||||
"react-scripts": "3.0.1",
|
||||
"uuid": "^3.3.2"
|
||||
"react-scripts": "3.0.1"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "concurrently \"react-scripts start\" \"nodemon server\"",
|
||||
"seed": "node server/db/seed.js",
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
|
@ -1,3 +0,0 @@
|
||||
.replies {
|
||||
margin-left: 1em;
|
||||
}
|
@ -25,8 +25,7 @@
|
||||
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
<link rel="stylesheet" type="text/css" href="css/custom.css" />
|
||||
<title>Anarchy Planet</title>
|
||||
<title>React App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
|
@ -1,56 +0,0 @@
|
||||
const router = require('express').Router()
|
||||
const { Project, Task, Article, Tag } = require('../db/models')
|
||||
module.exports = router
|
||||
|
||||
/* CREATE */
|
||||
router.post('/', async (req, res, next) => {
|
||||
try {
|
||||
const article = await Article.create(req.body)
|
||||
res.json(article)
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
/* READ */
|
||||
router.get('/', async (req, res, next) => {
|
||||
try {
|
||||
const articles = await Article.findAll({
|
||||
include: { model: Tag },
|
||||
})
|
||||
res.send(articles)
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
router.get('/:id', async (req, res, next) => {
|
||||
try {
|
||||
const article = await Article.findByPk(req.params.id)
|
||||
res.json(article)
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
/* UPDATE */
|
||||
router.put('/:id', async (req, res, next) => {
|
||||
try {
|
||||
const article = await Article.findByPk(req.params.id)
|
||||
await article.update(req.body)
|
||||
res.json(article)
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
/* DELETE */
|
||||
router.post('/:id/delete', async (req, res, next) => {
|
||||
try {
|
||||
const article = await Article.findByPk(req.params.id)
|
||||
await article.destroy({ force: true })
|
||||
res.json(article)
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
@ -1,35 +0,0 @@
|
||||
const router = require('express').Router()
|
||||
const { Comment, User, Vote } = require('../db/models')
|
||||
|
||||
module.exports = router
|
||||
|
||||
router.get('/', async (req, res, next) => {
|
||||
try {
|
||||
const comments = await Comment.findAll({
|
||||
include: ['replies', 'user'],
|
||||
})
|
||||
res.send(comments)
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
router.get('/:id', async (req, res, next) => {
|
||||
try {
|
||||
const comment = await Comment.findByPk(req.params.id, {
|
||||
attributes: ['id', 'text'],
|
||||
})
|
||||
res.json(comment)
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
router.post('/', async (req, res, next) => {
|
||||
try {
|
||||
const comment = await Comment.create(req.body)
|
||||
res.json(comment)
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
@ -1,27 +0,0 @@
|
||||
const router = require('express').Router()
|
||||
module.exports = router
|
||||
const ascii = require('../ascii')
|
||||
|
||||
router.use('/tasks', require('./tasks'))
|
||||
router.use('/projects', require('./projects'))
|
||||
router.use('/articles', require('./articles'))
|
||||
router.use('/tags', require('./tags'))
|
||||
router.use('/comments', require('./comments'))
|
||||
router.use('/votes', require('./votes'))
|
||||
router.use('/users', require('./users'))
|
||||
|
||||
router.get('/', async (req, res, next) => {
|
||||
try {
|
||||
res.json({ ascii })
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
next()
|
||||
}
|
||||
})
|
||||
|
||||
router.use((req, res, next) => {
|
||||
const error = new Error(`Not Found: ${req.url}`)
|
||||
console.log(error.message)
|
||||
error.status = 404
|
||||
next()
|
||||
})
|
@ -1,59 +0,0 @@
|
||||
const router = require('express').Router()
|
||||
const { Project, Task, Article, Tag } = require('../db/models')
|
||||
module.exports = router
|
||||
|
||||
/* CREATE */
|
||||
router.post('/', async (req, res, next) => {
|
||||
try {
|
||||
const project = await Project.create(req.body)
|
||||
res.json(project)
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
/* READ */
|
||||
router.get('/', async (req, res, next) => {
|
||||
try {
|
||||
const projects = await Project.findAll()
|
||||
res.send(projects)
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
router.get('/:id', async (req, res, next) => {
|
||||
try {
|
||||
const project = await Project.findByPk(req.params.id)
|
||||
res.json(project)
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
/* UPDATE */
|
||||
router.put('/:id', async (req, res, next) => {
|
||||
try {
|
||||
const project = await Project.findByPk(req.params.id)
|
||||
await project.update(req.body)
|
||||
res.json(project)
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
/* DELETE */
|
||||
router.post('/:id/delete', async (req, res, next) => {
|
||||
try {
|
||||
const project = await Project.findByPk(req.params.id)
|
||||
await project.destroy({ force: true })
|
||||
await Task.destroy({
|
||||
where: {
|
||||
projectId: req.params.id,
|
||||
},
|
||||
})
|
||||
res.json(project)
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
@ -1,100 +0,0 @@
|
||||
const router = require('express').Router()
|
||||
const { Project, Task, Article, Tag } = require('../db/models')
|
||||
module.exports = router
|
||||
|
||||
/* CREATE */
|
||||
router.post('/', async (req, res, next) => {
|
||||
try {
|
||||
// find or create tag and add article relation
|
||||
var tag = await Tag.findOne({
|
||||
where: { name: req.body.name },
|
||||
include: { model: Article },
|
||||
})
|
||||
if (!tag) {
|
||||
const newTag = { name: req.body.name }
|
||||
tag = await Tag.create(newTag, { include: { model: Article } })
|
||||
}
|
||||
tag.addArticle(req.body.articleId)
|
||||
|
||||
// find selected article and add tag relation
|
||||
var article = await Article.findByPk(req.body.articleId, {
|
||||
include: { model: Tag },
|
||||
})
|
||||
article.addTag(tag.id)
|
||||
|
||||
// update article and tag after adding the relation
|
||||
article = await Article.findByPk(req.body.articleId, {
|
||||
include: { model: Tag },
|
||||
})
|
||||
tag = await Tag.findOne({
|
||||
where: { name: req.body.name },
|
||||
include: { model: Article },
|
||||
})
|
||||
res.json({ tag: tag, article: article })
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
/* READ */
|
||||
router.get('/', async (req, res, next) => {
|
||||
try {
|
||||
const tags = await Tag.findAll({
|
||||
include: { model: Article },
|
||||
})
|
||||
res.send(tags)
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
router.get('/:id', async (req, res, next) => {
|
||||
try {
|
||||
const tag = await Tag.findByPk(req.params.id)
|
||||
res.json(tag)
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
}) // remove tag from article
|
||||
|
||||
/* UPDATE */ router.put('/:id', async (req, res, next) => {
|
||||
try {
|
||||
//const tag = await Tag.findByPk(req.params.id)
|
||||
//await tag.update(req.body)
|
||||
//res.json(tag)
|
||||
|
||||
// find or create tag and add article relation
|
||||
var tag = await Tag.findByPk(req.body.id, {
|
||||
include: { model: Article },
|
||||
})
|
||||
tag.removeArticle(req.body.articleId)
|
||||
|
||||
// find selected article and add tag relation
|
||||
var article = await Article.findByPk(req.body.articleId, {
|
||||
include: { model: Tag },
|
||||
})
|
||||
article.removeTag(tag.id)
|
||||
|
||||
// update article and tag after adding the relation
|
||||
article = await Article.findByPk(req.body.articleId, {
|
||||
include: { model: Tag },
|
||||
})
|
||||
tag = await Tag.findByPk(req.body.id, {
|
||||
include: { model: Article },
|
||||
})
|
||||
res.json({ tag: tag, article: article })
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
/* DELETE */
|
||||
router.post('/:id/delete', async (req, res, next) => {
|
||||
try {
|
||||
const tag = await Tag.findByPk(req.params.id)
|
||||
await tag.destroy({ force: true })
|
||||
res.json(tag)
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
@ -1,61 +0,0 @@
|
||||
const router = require('express').Router()
|
||||
const { Project, Task, Article, Tag } = require('../db/models')
|
||||
module.exports = router
|
||||
|
||||
/* CREATE */
|
||||
router.post('/', async (req, res, next) => {
|
||||
try {
|
||||
const task = await Task.create(req.body)
|
||||
|
||||
if (req.body.projectId) {
|
||||
const project = await Project.findOne({
|
||||
where: { id: req.body.projectId },
|
||||
})
|
||||
await task.setProject(project.id)
|
||||
}
|
||||
res.json(task)
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
/* READ */
|
||||
router.get('/', async (req, res, next) => {
|
||||
try {
|
||||
const tasks = await Task.findAll()
|
||||
res.send(tasks)
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
router.get('/:id', async (req, res, next) => {
|
||||
try {
|
||||
const task = await Task.findByPk(req.params.id)
|
||||
res.json(task)
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
/* UPDATE */
|
||||
router.put('/:id', async (req, res, next) => {
|
||||
try {
|
||||
const task = await Task.findByPk(req.params.id)
|
||||
await task.update(req.body)
|
||||
res.json(task)
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
/* DELETE */
|
||||
router.post('/:id/delete', async (req, res, next) => {
|
||||
try {
|
||||
const task = await Task.findByPk(req.params.id)
|
||||
await task.destroy({ force: true })
|
||||
res.json(task)
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
@ -1,54 +0,0 @@
|
||||
const router = require('express').Router()
|
||||
const { User } = require('../db/models')
|
||||
module.exports = router
|
||||
|
||||
/* CREATE */
|
||||
router.post('/', async (req, res, next) => {
|
||||
try {
|
||||
const task = await User.create(req.body)
|
||||
res.json(task)
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
/* READ */
|
||||
router.get('/', async (req, res, next) => {
|
||||
try {
|
||||
const tasks = await User.findAll()
|
||||
res.send(tasks)
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
router.get('/:id', async (req, res, next) => {
|
||||
try {
|
||||
const task = await User.findByPk(req.params.id)
|
||||
res.json(task)
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
/* UPDATE */
|
||||
router.put('/:id', async (req, res, next) => {
|
||||
try {
|
||||
const task = await User.findByPk(req.params.id)
|
||||
await task.update(req.body)
|
||||
res.json(task)
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
/* DELETE */
|
||||
router.post('/:id/delete', async (req, res, next) => {
|
||||
try {
|
||||
const task = await User.findByPk(req.params.id)
|
||||
task.destroy({ force: true })
|
||||
res.json(task)
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
@ -1,53 +0,0 @@
|
||||
const router = require('express').Router()
|
||||
const { Comment, User, Vote } = require('../db/models')
|
||||
module.exports = router
|
||||
|
||||
router.get('/', async (req, res, next) => {
|
||||
try {
|
||||
const votes = await Vote.findAll()
|
||||
res.send(votes)
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
router.post('/', async (req, res, next) => {
|
||||
const {userId, commentId, upvote, downvote} = req.body
|
||||
try {
|
||||
const votes = await Vote.create({userId, commentId, upvote, downvote})
|
||||
res.send(votes)
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
router.get('/:id', async (req, res, next) => {
|
||||
try {
|
||||
const vote = await Vote.findByPk(+req.params.id)
|
||||
res.json(vote)
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
router.post('/:id/delete', async (req, res, next) => {
|
||||
try {
|
||||
const vote = await Vote.findByPk(+req.params.id)
|
||||
await vote.destroy()
|
||||
res.json(vote)
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
router.put('/:id/update', async (req, res, next) => {
|
||||
const upvote = req.body.downvote
|
||||
const downvote = req.body.upvote
|
||||
try {
|
||||
const vote = await Vote.findByPk(+req.params.id)
|
||||
await vote.update({upvote, downvote})
|
||||
res.json(vote)
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
@ -1,26 +0,0 @@
|
||||
const ascii = String.raw`
|
||||
|
||||
. .
|
||||
* . . . . *
|
||||
. . . . . .
|
||||
o . .
|
||||
. . . .
|
||||
0 .
|
||||
. . , , ,
|
||||
. \ . .
|
||||
. . \ ,
|
||||
. o . . .
|
||||
. . . \ , .
|
||||
#\##\# . .
|
||||
# #O##\### .
|
||||
. . #*# #\##\### .
|
||||
. ##*# #\##\## .
|
||||
. . ##*# #o##\# .
|
||||
. *# #\# . .
|
||||
\ . .
|
||||
____^/\___^--____/\____O______________/\/\---/\___________--
|
||||
/\^ ^ ^ ^ ^^ ^ '\ ^ ^
|
||||
-- - -- - - --- __ ^
|
||||
-- __ ___-- ^ ^`
|
||||
|
||||
module.exports = ascii
|
@ -1,18 +0,0 @@
|
||||
const Sequelize = require('sequelize')
|
||||
const pkg = require('../../package.json')
|
||||
|
||||
const databaseName = pkg.name
|
||||
|
||||
const createDB = () => {
|
||||
const db = new Sequelize(
|
||||
process.env.DATABASE_URL || `postgres://localhost:5432/${databaseName}`,
|
||||
{
|
||||
logging: false,
|
||||
},
|
||||
)
|
||||
return db
|
||||
}
|
||||
|
||||
const db = createDB()
|
||||
|
||||
module.exports = db
|
@ -1,6 +0,0 @@
|
||||
const db = require('./db')
|
||||
|
||||
// register models
|
||||
require('./models')
|
||||
|
||||
module.exports = db
|
@ -1,15 +0,0 @@
|
||||
const Sequelize = require('sequelize')
|
||||
const db = require('../db')
|
||||
|
||||
const Article = db.define('articles', {
|
||||
title: {
|
||||
type: Sequelize.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
text: {
|
||||
type: Sequelize.TEXT,
|
||||
allowNull: true,
|
||||
},
|
||||
})
|
||||
|
||||
module.exports = Article
|
@ -1,11 +0,0 @@
|
||||
const Sequelize = require('sequelize')
|
||||
const db = require('../db')
|
||||
|
||||
const Comment = db.define('comments', {
|
||||
text: {
|
||||
type: Sequelize.TEXT,
|
||||
allowNull: false,
|
||||
},
|
||||
})
|
||||
|
||||
module.exports = Comment
|
@ -1,43 +0,0 @@
|
||||
const Task = require('./task')
|
||||
const Project = require('./project')
|
||||
const User = require('./user')
|
||||
const Article = require('./article')
|
||||
const Tag = require('./tag')
|
||||
const Comment = require('./comment')
|
||||
const Vote = require('./vote')
|
||||
|
||||
User.hasMany(Article)
|
||||
User.hasMany(Comment)
|
||||
User.hasMany(Vote)
|
||||
Project.hasMany(Task)
|
||||
Task.belongsTo(Project)
|
||||
Article.hasMany(Comment)
|
||||
Article.belongsToMany(Tag, { through: 'articleTags' })
|
||||
Article.belongsTo(User)
|
||||
Tag.belongsToMany(Article, { through: 'articleTags' })
|
||||
Comment.belongsTo(Article)
|
||||
Comment.belongsTo(User)
|
||||
Comment.hasMany(Vote)
|
||||
Comment.belongsTo(Comment, { as: 'parent' })
|
||||
Comment.hasMany(Comment, {
|
||||
as: { singular: 'reply', plural: 'replies' },
|
||||
foreignKey: 'parentId',
|
||||
})
|
||||
Vote.belongsTo(Comment)
|
||||
Vote.belongsTo(User)
|
||||
|
||||
User.belongsToMany(Project, { through: 'projectUser' })
|
||||
Project.hasMany(User)
|
||||
|
||||
Task.belongsToMany(User, { through: 'userTask' })
|
||||
User.hasMany(Task)
|
||||
|
||||
module.exports = {
|
||||
Task,
|
||||
Project,
|
||||
User,
|
||||
Article,
|
||||
Tag,
|
||||
Comment,
|
||||
Vote,
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
const Sequelize = require('sequelize')
|
||||
const db = require('../db')
|
||||
|
||||
const Project = db.define('projects', {
|
||||
name: {
|
||||
type: Sequelize.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
})
|
||||
|
||||
module.exports = Project
|
@ -1,11 +0,0 @@
|
||||
const Sequelize = require('sequelize')
|
||||
const db = require('../db')
|
||||
|
||||
const Tag = db.define('tags', {
|
||||
name: {
|
||||
type: Sequelize.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
})
|
||||
|
||||
module.exports = Tag
|
@ -1,16 +0,0 @@
|
||||
const Sequelize = require('sequelize')
|
||||
const db = require('../db')
|
||||
|
||||
const Task = db.define('tasks', {
|
||||
desc: {
|
||||
type: Sequelize.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
|
||||
completed: {
|
||||
type: Sequelize.BOOLEAN,
|
||||
defaultValue: false,
|
||||
},
|
||||
})
|
||||
|
||||
module.exports = Task
|
@ -1,20 +0,0 @@
|
||||
const Sequelize = require('sequelize')
|
||||
const db = require('../db')
|
||||
|
||||
const User = db.define('users', {
|
||||
name: {
|
||||
type: Sequelize.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
|
||||
email: {
|
||||
type: Sequelize.STRING,
|
||||
},
|
||||
|
||||
avatar: {
|
||||
type: Sequelize.STRING,
|
||||
defaultValue: 'default-user-img.png',
|
||||
},
|
||||
})
|
||||
|
||||
module.exports = User
|
@ -1,9 +0,0 @@
|
||||
const Sequelize = require('sequelize')
|
||||
const db = require('../db')
|
||||
|
||||
const Vote = db.define('votes', {
|
||||
upvote: Sequelize.INTEGER,
|
||||
downvote: Sequelize.INTEGER,
|
||||
})
|
||||
|
||||
module.exports = Vote
|
@ -1,102 +0,0 @@
|
||||
const db = require('../db')
|
||||
const { Task, Project, User, Article, Tag, Comment, Vote } = require('./models')
|
||||
|
||||
const testTasks = [
|
||||
{
|
||||
desc: 'make app',
|
||||
completed: false,
|
||||
projectId: 1,
|
||||
},
|
||||
{
|
||||
desc: 'update backend',
|
||||
completed: false,
|
||||
projectId: 1,
|
||||
},
|
||||
{
|
||||
desc: 'eat dinner',
|
||||
completed: false,
|
||||
projectId: 1,
|
||||
},
|
||||
]
|
||||
|
||||
const testProjects = [{ name: 'Anarchy Planet' }, { name: 'Tilde' }]
|
||||
|
||||
const testUsers = [
|
||||
{ name: 'nn', email: 'nn@ap.org' },
|
||||
{ name: 'dn', email: 'dn@ap.org' },
|
||||
]
|
||||
|
||||
const testArticles = [{ title: 'latest news', text: 'waddup?!' }]
|
||||
|
||||
const testTags = [{ name: 'dox' }]
|
||||
|
||||
const tc1 = { text: 'pretty good' }
|
||||
const tc2 = { text: 'yeah!' }
|
||||
const tc3 = { text: 'could be more detailed tho' }
|
||||
const tc4 = { text: 'BUT THE INSECTS' }
|
||||
const tc5 = { text: 'HAHAHAHAHA' }
|
||||
const tc6 = { text: 'oh shut up' }
|
||||
|
||||
const testVotes = [{ upvote: 0, downvote: 1 }]
|
||||
|
||||
async function runSeed() {
|
||||
await db.sync({ force: true })
|
||||
console.log('db synced!')
|
||||
console.log('seeding...')
|
||||
try {
|
||||
const p1 = await Project.create(testProjects[0])
|
||||
const p2 = await Project.create(testProjects[1])
|
||||
|
||||
const u1 = await User.create(testUsers[0])
|
||||
const u2 = await User.create(testUsers[1])
|
||||
|
||||
const t1 = await Task.create(testTasks[0])
|
||||
const t2 = await Task.create(testTasks[1])
|
||||
const t3 = await Task.create(testTasks[2])
|
||||
|
||||
await p1.addTask(t1)
|
||||
await p1.addTask(t2)
|
||||
await p2.addTask(t3)
|
||||
|
||||
await u1.addTasks([t1, t2])
|
||||
await u2.addTask(t3)
|
||||
|
||||
const a1 = await Article.create(testArticles[0])
|
||||
const t4 = await Tag.create(testTags[0])
|
||||
const c1 = await Comment.create(tc1)
|
||||
const c2 = await Comment.create(tc2)
|
||||
const c3 = await Comment.create(tc3)
|
||||
const c4 = await Comment.create(tc4)
|
||||
const c5 = await Comment.create(tc5)
|
||||
const c6 = await Comment.create(tc6)
|
||||
const v1 = await Vote.create(testVotes[0])
|
||||
|
||||
await a1.setUser(u1)
|
||||
await a1.addTag(t4)
|
||||
await a1.addComments(c1, c2, c3, c4, c5, c6)
|
||||
|
||||
await c1.setUser(u2)
|
||||
await c2.setUser(u1)
|
||||
await c3.setUser(u2)
|
||||
await c4.setUser(u1)
|
||||
await c5.setUser(u1)
|
||||
await c6.setUser(u2)
|
||||
|
||||
await c1.addVote(v1)
|
||||
await c1.addReply(2)
|
||||
await c2.addReplies([c3, c4])
|
||||
await c5.setParent(c4)
|
||||
await c6.setParent(c1)
|
||||
|
||||
console.log('seeded successfully')
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
process.exitCode = 1
|
||||
} finally {
|
||||
console.log('closing db connection')
|
||||
await db.close()
|
||||
console.log('db connection closed')
|
||||
}
|
||||
}
|
||||
|
||||
runSeed()
|
@ -1,34 +0,0 @@
|
||||
const express = require('express')
|
||||
const path = require('path')
|
||||
const app = express()
|
||||
const morgan = require('morgan')
|
||||
const ascii = require('./ascii')
|
||||
const cors = require('cors')
|
||||
const port = process.env.PORT || 1337
|
||||
|
||||
app.use(morgan('tiny'))
|
||||
app.use(cors())
|
||||
// body parsing middleware
|
||||
app.use(express.json())
|
||||
app.use(express.urlencoded({ extended: true }))
|
||||
app.use(require('body-parser').text())
|
||||
app.use('/api', require('./api'))
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
// Express will serve up production assets
|
||||
app.use(express.static(path.join(__dirname, 'dist')))
|
||||
}
|
||||
app.use('/', express.static(path.resolve(__dirname, '..', 'build')))
|
||||
|
||||
// error handling endware
|
||||
app.use((err, req, res, next) => {
|
||||
console.error(err)
|
||||
console.error(err.stack)
|
||||
res.status(err.status || 500).send(err.message || 'Internal server error.')
|
||||
next()
|
||||
})
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log('\n' + ascii + '\n')
|
||||
console.log(`Doin' haxor stuff on port ${port}`)
|
||||
})
|
143
src/App.js
143
src/App.js
@ -1,15 +1,6 @@
|
||||
import React from 'react'
|
||||
import axios from 'axios'
|
||||
|
||||
import {
|
||||
createArticle,
|
||||
updateArticle,
|
||||
deleteArticle,
|
||||
selectArticle,
|
||||
} from './controllers/articles'
|
||||
|
||||
import { addTag, selectTag, removeTag } from './controllers/tags'
|
||||
|
||||
import {
|
||||
createTask,
|
||||
updateTask,
|
||||
@ -28,10 +19,8 @@ import {
|
||||
import { updateProfile } from './controllers/profile'
|
||||
|
||||
import {
|
||||
Login,
|
||||
About,
|
||||
Articles,
|
||||
Article,
|
||||
UpdateArticle,
|
||||
Tasks,
|
||||
UpdateTask,
|
||||
Profile,
|
||||
@ -45,16 +34,15 @@ console.log('Using API at ' + API)
|
||||
|
||||
const defaultState = {
|
||||
loading: true,
|
||||
user: { name: 'Scott' },
|
||||
user: {},
|
||||
tasks: [],
|
||||
projects: [],
|
||||
component: 'projects',
|
||||
// TODO try to get rid of:
|
||||
search: '',
|
||||
newArticle: '',
|
||||
newTask: '',
|
||||
newProject: '',
|
||||
project: {},
|
||||
newTag: '',
|
||||
}
|
||||
|
||||
class App extends React.Component {
|
||||
@ -65,12 +53,7 @@ class App extends React.Component {
|
||||
this.handleChange = this.handleChange.bind(this)
|
||||
this.handleSubmit = this.handleSubmit.bind(this)
|
||||
|
||||
this.createArticle = createArticle.bind(this)
|
||||
this.updateArticle = updateArticle.bind(this)
|
||||
this.deleteArticle = deleteArticle.bind(this)
|
||||
|
||||
this.addTag = addTag.bind(this)
|
||||
this.removeTag = removeTag.bind(this)
|
||||
this.login = this.login.bind(this)
|
||||
|
||||
this.createTask = createTask.bind(this)
|
||||
this.updateTask = updateTask.bind(this)
|
||||
@ -82,22 +65,10 @@ class App extends React.Component {
|
||||
|
||||
this.updateProfile = updateProfile.bind(this)
|
||||
|
||||
this.selectArticle = selectArticle.bind(this)
|
||||
this.selectTag = selectTag.bind(this)
|
||||
this.selectTask = selectTask.bind(this)
|
||||
this.selectProject = selectProject.bind(this)
|
||||
}
|
||||
|
||||
async fetchArticles() {
|
||||
try {
|
||||
const { data } = await axios.get(API + '/api/articles')
|
||||
return data
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
this.setState({ error })
|
||||
}
|
||||
}
|
||||
|
||||
async fetchTasks() {
|
||||
try {
|
||||
const { data } = await axios.get(API + '/api/tasks')
|
||||
@ -118,52 +89,23 @@ class App extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
async fetchComments() {
|
||||
async login(userData) {
|
||||
try {
|
||||
const { data } = await axios.get(API + '/api/comments')
|
||||
return data
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
this.setState({ error })
|
||||
}
|
||||
}
|
||||
|
||||
async fetchTags() {
|
||||
try {
|
||||
const { data } = await axios.get(API + '/api/tags')
|
||||
return data
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
this.setState({ error })
|
||||
}
|
||||
}
|
||||
|
||||
async fetchVotes() {
|
||||
try {
|
||||
const { data } = await axios.get(API + '/api/votes')
|
||||
return data
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
this.setState({ error })
|
||||
const { data } = await axios.post(API + '/login', userData)
|
||||
console.log('login retrieved data:', data)
|
||||
if (data.name) {
|
||||
this.setState({ user: data })
|
||||
this.navigate('projects')
|
||||
} else this.setState({ error: 'no user found.' })
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
|
||||
async fetchData() {
|
||||
const articles = await this.fetchArticles()
|
||||
const tags = await this.fetchTags()
|
||||
const tasks = await this.fetchTasks()
|
||||
const projects = await this.fetchProjects()
|
||||
const comments = await this.fetchComments()
|
||||
const votes = await this.fetchVotes()
|
||||
await this.setState({
|
||||
loading: false,
|
||||
articles,
|
||||
tags,
|
||||
tasks,
|
||||
projects,
|
||||
comments,
|
||||
votes,
|
||||
})
|
||||
await this.setState({ loading: false, tasks, projects })
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@ -186,9 +128,9 @@ class App extends React.Component {
|
||||
return <div>Loading...</div>
|
||||
}
|
||||
renderError() {
|
||||
if (!this.state.error) {
|
||||
return <div>There was an error.</div>
|
||||
} else if (this.state.error.message === 'Network Error') {
|
||||
const { error } = this.state
|
||||
if (error) {
|
||||
if (this.state.error.message === 'Network Error') {
|
||||
alert(`Failed to reach backend at\n${this.state.error.config.url}.`)
|
||||
} else {
|
||||
return (
|
||||
@ -205,6 +147,8 @@ class App extends React.Component {
|
||||
)
|
||||
}
|
||||
}
|
||||
return <div>Uncaught error. You probably forgot to render something.</div>
|
||||
}
|
||||
renderProfile() {
|
||||
return (
|
||||
<Profile
|
||||
@ -215,43 +159,6 @@ class App extends React.Component {
|
||||
/>
|
||||
)
|
||||
}
|
||||
renderArticles() {
|
||||
return (
|
||||
<Articles
|
||||
navigate={this.navigate}
|
||||
handleChange={this.handleChange}
|
||||
createArticle={this.createArticle}
|
||||
selectArticle={this.selectArticle}
|
||||
deleteArticle={this.deleteArticle}
|
||||
selectTag={this.selectTag}
|
||||
{...this.state}
|
||||
/>
|
||||
)
|
||||
}
|
||||
renderArticle() {
|
||||
return (
|
||||
<Article
|
||||
navigate={this.navigate}
|
||||
addTag={this.addTag}
|
||||
removeTag={this.removeTag}
|
||||
deleteArticle={this.deleteArticle}
|
||||
selectTag={this.selectTag}
|
||||
handleChange={this.handleChange}
|
||||
{...this.state}
|
||||
/>
|
||||
)
|
||||
}
|
||||
renderUpdateArticle() {
|
||||
return (
|
||||
<UpdateArticle
|
||||
handleChange={this.handleChange}
|
||||
updateArticle={this.updateArticle}
|
||||
navigate={this.navigate}
|
||||
selectTag={this.selectTag}
|
||||
{...this.state}
|
||||
/>
|
||||
)
|
||||
}
|
||||
renderProjects() {
|
||||
return (
|
||||
<Projects
|
||||
@ -283,6 +190,10 @@ class App extends React.Component {
|
||||
renderAbout() {
|
||||
return <About {...this.state} />
|
||||
}
|
||||
|
||||
renderLogin() {
|
||||
return <Login login={this.login} {...this.state} />
|
||||
}
|
||||
renderTasks(filtered, completed) {
|
||||
return (
|
||||
<Tasks
|
||||
@ -330,12 +241,6 @@ class App extends React.Component {
|
||||
? this.renderLoading()
|
||||
: this.state.error
|
||||
? this.renderError()
|
||||
: this.state.component === 'articles'
|
||||
? this.renderArticles()
|
||||
: this.state.component === 'article'
|
||||
? this.renderArticle()
|
||||
: this.state.component === 'update-article'
|
||||
? this.renderUpdateArticle()
|
||||
: this.state.component === 'tasks'
|
||||
? this.renderTasks(filtered, completed)
|
||||
: this.state.component === 'task'
|
||||
@ -348,6 +253,8 @@ class App extends React.Component {
|
||||
? this.renderProjects()
|
||||
: this.state.component === 'project'
|
||||
? this.renderProject()
|
||||
: this.state.component === 'login'
|
||||
? this.renderLogin()
|
||||
: this.renderError()
|
||||
|
||||
return (
|
||||
|
@ -1,26 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
const CollapsedComment = props => {
|
||||
return (
|
||||
<li key={props.id}>
|
||||
<div className="media-body ml-3 py-2 my-auto">
|
||||
<img className="px-2" alt="profile" src="anon.jpeg" height={16} />
|
||||
<a href="#user">username</a>
|
||||
<small className="px-3">One day ago</small>
|
||||
<span
|
||||
className="a"
|
||||
style={{
|
||||
fontVariant: 'small-caps',
|
||||
color: 'blue',
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
onClick={() => props.toggleCollapse(props.id)}
|
||||
>
|
||||
expand
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
export default CollapsedComment
|
@ -1,47 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
const ExpandedComment = props => {
|
||||
return (
|
||||
<li
|
||||
className=""
|
||||
key={props.id}
|
||||
onMouseEnter={() => props.showCollapse(props.id, true)}
|
||||
onMouseLeave={() => props.showCollapse(props.id, false)}
|
||||
>
|
||||
<div className="media bg bg-dark">
|
||||
<img alt="profile" src={props.user.avatar} height={64} />
|
||||
|
||||
<div className="media-body ml-3 my-auto">
|
||||
<a className="" href="#user">
|
||||
{props.user.name}
|
||||
</a>
|
||||
</div>
|
||||
<div className="white">
|
||||
<small>{props.createdAt}</small>
|
||||
{props.replies.length > 0 ? (
|
||||
<span
|
||||
className="a ml-3"
|
||||
style={{
|
||||
fontVariant: 'small-caps',
|
||||
color: 'blue',
|
||||
cursor: 'pointer',
|
||||
display: props.displayCollapse || 'none',
|
||||
}}
|
||||
onClick={() => props.toggleCollapse(props.id, !props.collapsed)}
|
||||
>
|
||||
{props.collapsed ? 'expand' : 'collapse'}
|
||||
</span>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
<a className="float-right ml-2" href="#replies">
|
||||
reply
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="">{props.text}</div>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
export default ExpandedComment
|
@ -1,10 +0,0 @@
|
||||
import React from 'react'
|
||||
import ExpandedComment from './ExpandedComment'
|
||||
import CollapsedComment from './CollapsedComment'
|
||||
|
||||
const SingleComment = props => {
|
||||
if (props.hidden) return <CollapsedComment {...props} />
|
||||
return <ExpandedComment {...props} />
|
||||
}
|
||||
|
||||
export default SingleComment
|
@ -1,92 +0,0 @@
|
||||
import React from 'react'
|
||||
import SingleComment from './SingleComment'
|
||||
import uuid from 'uuid'
|
||||
|
||||
class ThreadList extends React.Component {
|
||||
constructor() {
|
||||
super()
|
||||
this.toggleCollapse = this.toggleCollapse.bind(this)
|
||||
this.showCollapse = this.showCollapse.bind(this)
|
||||
}
|
||||
|
||||
showCollapse(id, state) {
|
||||
this.setState({ displayCollapse: 'block' })
|
||||
const otherComments = this.props.comments.filter(c => c.id !== id)
|
||||
const comment = this.props.comments.find(c => c.id === id)
|
||||
comment['displayCollapse'] = state
|
||||
//console.log('displayed collapse for: ', comment)
|
||||
this.setState({ comments: otherComments.concat(comment) })
|
||||
}
|
||||
|
||||
toggleCollapse(id, state) {
|
||||
const otherComments = this.props.comments.filter(c => c.id !== id)
|
||||
var parent = this.props.comments.find(c => c.id === id)
|
||||
console.log('toggling', parent)
|
||||
parent['collapsed'] = state
|
||||
if (!state) {
|
||||
// unhide parent when unhiding thread
|
||||
parent['hidden'] = false
|
||||
}
|
||||
this.setState({ comments: otherComments.concat(parent) })
|
||||
parent.replies.map(comment => this.toggleReplies(comment.id, state))
|
||||
}
|
||||
toggleReplies(id, state) {
|
||||
const otherComments = this.props.comments.filter(c => c.id !== id)
|
||||
var comment = this.props.comments.find(c => c.id === id)
|
||||
comment['hidden'] = state
|
||||
this.setState({ comments: otherComments.concat(comment) })
|
||||
if (comment.replies) {
|
||||
comment.replies.map(c => this.toggleReplies(c.id, state))
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const comments = this.props.comments.filter(
|
||||
// only show comments for this parent / thread
|
||||
c =>
|
||||
(!c.parentId && !this.props.threadLevel) ||
|
||||
c.parentId === this.props.threadLevel,
|
||||
)
|
||||
if (comments.length === 0) {
|
||||
return ''
|
||||
}
|
||||
console.log('showing comments for thread', this.props.threadLevel, comments)
|
||||
return (
|
||||
<ul className="list-unstyled">
|
||||
{comments.map(comment => {
|
||||
// TODO comments aren't sorted by time
|
||||
if (comment.replies && comment.replies.length === 0) {
|
||||
return (
|
||||
<div className="ml-3" key={uuid()}>
|
||||
<SingleComment
|
||||
threadLevel={this.props.threadLevel}
|
||||
showCollapse={this.showCollapse}
|
||||
toggleCollapse={this.toggleCollapse}
|
||||
{...comment}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<div className="ml-3" key={uuid()}>
|
||||
<SingleComment
|
||||
threadLevel={this.props.threadLevel}
|
||||
showCollapse={this.showCollapse}
|
||||
toggleCollapse={this.toggleCollapse}
|
||||
{...comment}
|
||||
/>
|
||||
<ThreadList
|
||||
threadLevel={comment.id}
|
||||
showCollapse={this.showCollapse}
|
||||
comments={this.props.comments}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default ThreadList
|
23
src/components/ProjectView.js
Normal file
23
src/components/ProjectView.js
Normal file
@ -0,0 +1,23 @@
|
||||
import React from 'react'
|
||||
import Tasks from './tasks'
|
||||
|
||||
function Project(props) {
|
||||
// if (!props.selectedProjectId) {
|
||||
// props.navigate('projects')
|
||||
// return null
|
||||
// }
|
||||
// const project = props.projects.find(p => p.id === props.selectedProjectId)
|
||||
// const filtered = props.tasks.filter(t => t.projectId === props.selectedProjectId && !t.completed)
|
||||
// const completed = props.tasks.filter(
|
||||
// t => (t.projectId === props.selectedProjectId && t.completed === true) || null,
|
||||
// )
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>{props.project.name}</h2>
|
||||
<Tasks filtered={props.filtered} completed={props.completed} {...props} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Project
|
@ -1,38 +0,0 @@
|
||||
import React from 'react'
|
||||
import Tags from './tags'
|
||||
import Comments from './Comments'
|
||||
|
||||
class Article extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
const article = props.articles.find(t => t.id === props.selectedArticleId)
|
||||
this.state = article
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Tags {...this.props} />
|
||||
<h2>{this.state.title}</h2>
|
||||
<div className="mb-3">{this.state.text}</div>
|
||||
<div>
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
onClick={() => this.props.navigate('articles')}
|
||||
>
|
||||
Back
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-primary ml-1"
|
||||
onClick={() => this.props.navigate('update-article')}
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
<h2 className="mt-3">Comments</h2>
|
||||
<Comments {...this.props} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
export default Article
|
@ -1,83 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
function Articles(props) {
|
||||
var articles = props.articles || []
|
||||
if (props.selectedTagId) {
|
||||
articles = props.articles.filter(a =>
|
||||
filterArticles(a, props.selectedTagId),
|
||||
)
|
||||
}
|
||||
function filterArticles(article, tagId) {
|
||||
if (article.tags && article.tags.filter(t => t.id === tagId).length !== 0) {
|
||||
return article
|
||||
}
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<h3 className="mt-4">Articles</h3>
|
||||
{props.selectedTagId ? (
|
||||
<button
|
||||
className="btn btn-primary mb-1"
|
||||
onClick={() => props.selectTag()}
|
||||
>
|
||||
Show all
|
||||
</button>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
<ul className="list-group">
|
||||
{articles.map(article => (
|
||||
<li className="list-group-item" key={article.id}>
|
||||
<button
|
||||
className="btn btn-outline-danger mr-1"
|
||||
onClick={() => props.deleteArticle(article.id)}
|
||||
>
|
||||
X
|
||||
</button>
|
||||
<button
|
||||
className="btn ml-1"
|
||||
onClick={() => props.selectArticle(article.id)}
|
||||
>
|
||||
{article.title}{' '}
|
||||
</button>
|
||||
<button className="btn ml-1 float-right">
|
||||
{props.comments.map(c => c.articleId === article.id).length}{' '}
|
||||
comment(s)
|
||||
</button>
|
||||
{article.tags &&
|
||||
article.tags.map(tag => (
|
||||
<button
|
||||
className="btn btn-outline-success mr-1 float-right"
|
||||
key={article.id + tag.id}
|
||||
onClick={e => props.selectTag(tag.id)}
|
||||
>
|
||||
{tag.name}
|
||||
</button>
|
||||
))}
|
||||
</li>
|
||||
))}
|
||||
|
||||
<li className="list-group-item">
|
||||
<form className="form-group row" onSubmit={props.createArticle}>
|
||||
<input
|
||||
className="form-control col input-sm"
|
||||
placeholder="Create Article"
|
||||
name="newArticle"
|
||||
value={props.newArticle}
|
||||
onChange={props.handleChange}
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
className="form-control col col-sm-2 ml-1 btn btn-primary"
|
||||
onClick={props.createArticle}
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Articles
|
@ -1,12 +1,8 @@
|
||||
export { default as Login } from './login'
|
||||
export { default as Navbar } from './navbar'
|
||||
export { default as About } from './about'
|
||||
export { default as Articles } from './articles'
|
||||
export { default as Article } from './article'
|
||||
export { default as UpdateArticle } from './update-article'
|
||||
export { default as Tags } from './tags'
|
||||
export { default as Tasks } from './tasks'
|
||||
export { default as UpdateTask } from './update-task'
|
||||
export { default as Profile } from './profile'
|
||||
export { default as Projects } from './projects'
|
||||
export { default as Project } from './project'
|
||||
export { default as Comments } from './Comments'
|
||||
|
62
src/components/login.js
Normal file
62
src/components/login.js
Normal file
@ -0,0 +1,62 @@
|
||||
import React from 'react'
|
||||
|
||||
const initialState = { email: '', password: '' }
|
||||
|
||||
class Login extends React.Component {
|
||||
constructor() {
|
||||
super()
|
||||
this.state = initialState
|
||||
this.handleChange = this.handleChange.bind(this)
|
||||
this.handleSubmit = this.handleSubmit.bind(this)
|
||||
}
|
||||
|
||||
handleChange(e) {
|
||||
this.setState({ [e.target.name]: e.target.value })
|
||||
}
|
||||
|
||||
handleSubmit(e) {
|
||||
e.preventDefault()
|
||||
this.setState(initialState)
|
||||
const email = e.target.email.value
|
||||
const password = e.target.password.value // hash dis b4 send on frontend
|
||||
return this.props.login({ email, password })
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<div className="form-group row">
|
||||
<label htmlFor="email" className="col-sm-2 col-form-label">
|
||||
Email
|
||||
</label>
|
||||
<div className="col-sm-10">
|
||||
<input
|
||||
type="email"
|
||||
className="form-control"
|
||||
id="email"
|
||||
placeholder="Email"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-group row">
|
||||
<label htmlFor="password" className="col-sm-2 col-form-label">
|
||||
Password
|
||||
</label>
|
||||
<div className="col-sm-10">
|
||||
<input
|
||||
type="password"
|
||||
className="form-control"
|
||||
id="password"
|
||||
placeholder="Password"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" onSubmit={this.handleSubmit}>
|
||||
Log In
|
||||
</button>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Login
|
@ -3,6 +3,7 @@ import React from 'react'
|
||||
function Navbar(props) {
|
||||
return (
|
||||
<nav className="navbar navbar-expand-lg navbar-light bg-light">
|
||||
{props.user && props.user.name ? (
|
||||
<a
|
||||
className="navbar-brand"
|
||||
href="#profile"
|
||||
@ -10,6 +11,15 @@ function Navbar(props) {
|
||||
>
|
||||
{`Hello, ${props.user.name}!`}
|
||||
</a>
|
||||
) : (
|
||||
<a
|
||||
className="navbar-brand"
|
||||
href="#login"
|
||||
onClick={() => props.navigate('login')}
|
||||
>
|
||||
Log in
|
||||
</a>
|
||||
)}
|
||||
<button
|
||||
className="navbar-toggler"
|
||||
type="button"
|
||||
@ -23,23 +33,13 @@ function Navbar(props) {
|
||||
</button>
|
||||
<div className="collapse navbar-collapse" id="navbarText">
|
||||
<ul className="navbar-nav mr-auto">
|
||||
<li
|
||||
className={`nav-item ${
|
||||
props.component === 'about' ? 'active' : ''
|
||||
}`}
|
||||
onClick={() => props.navigate('articles')}
|
||||
>
|
||||
<a className="nav-link" href="#articles">
|
||||
Articles
|
||||
</a>
|
||||
</li>
|
||||
<li
|
||||
className={`nav-item ${
|
||||
props.component === 'project' ? 'active' : ''
|
||||
}`}
|
||||
onClick={() => props.navigate('projects')}
|
||||
onClick={() => props.navigate('project')}
|
||||
>
|
||||
<a className="nav-link" href="#projects">
|
||||
<a className="nav-link" href="#project">
|
||||
Projects
|
||||
</a>
|
||||
</li>
|
||||
@ -63,6 +63,19 @@ function Navbar(props) {
|
||||
About
|
||||
</a>
|
||||
</li>
|
||||
{props.user && props.user.name ? (
|
||||
<li className="nav-item" onClick={() => props.logout()}>
|
||||
<a className="nav-link" href="#logout">
|
||||
Logout
|
||||
</a>
|
||||
</li>
|
||||
) : (
|
||||
<li className="nav-item" onClick={() => props.navigate('login')}>
|
||||
<a className="nav-link" href="#login">
|
||||
Login
|
||||
</a>
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
@ -14,7 +14,6 @@ function Project(props) {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<span className="btn btn-primary" onClick={() => props.navigate('projects')}>Back</span>
|
||||
<h2>{project.name}</h2>
|
||||
<Tasks filtered={filtered} completed={completed} {...props} />
|
||||
</div>
|
||||
|
@ -1,42 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
function Tags(props) {
|
||||
const article = props.articles.filter(
|
||||
a => a.id === props.selectedArticleId,
|
||||
)[0]
|
||||
return (
|
||||
<div>
|
||||
{article.tags &&
|
||||
article.tags.map(tag => (
|
||||
<span key={tag.id}>
|
||||
<button
|
||||
className="btn btn-outline-success ml-1"
|
||||
onClick={e => props.selectTag(tag.id)}
|
||||
>
|
||||
{tag.name}
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-danger mr-1"
|
||||
onClick={e => props.removeTag(tag.id)}
|
||||
>
|
||||
-
|
||||
</button>
|
||||
</span>
|
||||
))}
|
||||
<form className="mt-2 mb-2" onSubmit={props.addTag}>
|
||||
<input
|
||||
className="col input-sm col-sm-3 ml-3 mr-1"
|
||||
placeholder="Add tag"
|
||||
name="newTag"
|
||||
value={props.newTag}
|
||||
onChange={props.handleChange}
|
||||
/>
|
||||
<button className="col btn btn-primary col-sm-1" onClick={props.addTag}>
|
||||
+
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Tags
|
@ -1,78 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
class UpdateArticle extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
const article = props.articles.find(t => t.id === props.selectedArticleId)
|
||||
this.state = article
|
||||
this.handleChange = this.handleChange.bind(this)
|
||||
|
||||
if (!props.selectedArticleId) {
|
||||
props.navigate('articles')
|
||||
}
|
||||
}
|
||||
|
||||
handleChange(e) {
|
||||
this.setState({ [e.target.name]: e.target.value })
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<button className="btn" onClick={() => this.props.navigate('article')}>
|
||||
back
|
||||
</button>
|
||||
<form>
|
||||
<h2 className="form-group row">Article {this.state.id}</h2>
|
||||
<div className="form-group row">
|
||||
<input
|
||||
type="text"
|
||||
className="form-control mb-1"
|
||||
name="title"
|
||||
value={this.state.title}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form-group row">
|
||||
<textarea
|
||||
type="text"
|
||||
className="form-control"
|
||||
name="text"
|
||||
rows="25"
|
||||
value={this.state.text || ''}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form-group row">
|
||||
<label htmlFor="createdAt" className="col-sm-2 col-form-label">
|
||||
Created:
|
||||
</label>
|
||||
<div className="col-sm-10">
|
||||
<input
|
||||
className="form-control"
|
||||
name="createdAt"
|
||||
value={this.state.createdAt}
|
||||
readOnly
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="form-group row">
|
||||
<div className="col-sm-10">
|
||||
<button
|
||||
type="submit"
|
||||
className="btn btn-primary"
|
||||
onClick={e => this.props.updateArticle(e, this.state)}
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
export default UpdateArticle
|
@ -1,85 +0,0 @@
|
||||
import axios from 'axios'
|
||||
const API = process.env.REACT_APP_API || 'http://localhost:1337'
|
||||
|
||||
export async function createArticle(e) {
|
||||
e.preventDefault()
|
||||
if (this.state.newArticle === '') {
|
||||
alert('You forgot the title.')
|
||||
return
|
||||
}
|
||||
const article = { title: this.state.newArticle }
|
||||
try {
|
||||
const { data, error } = await axios.post(API + '/api/articles', article)
|
||||
if (error) {
|
||||
alert(`Received error when creating article ${article.name}: ${error}`)
|
||||
} else if (data.id) {
|
||||
this.setState({
|
||||
articles: this.state.articles.concat(data),
|
||||
newArticle: '',
|
||||
})
|
||||
console.log(`Added article: `, data)
|
||||
} else {
|
||||
alert(`Failed to add article '${article}'.`)
|
||||
}
|
||||
} catch (e) {
|
||||
alert(`Failed to add article '${this.state.newArticle}'.`)
|
||||
}
|
||||
}
|
||||
|
||||
export function selectArticle(selectedArticleId) {
|
||||
this.setState({ selectedArticleId })
|
||||
this.navigate('article')
|
||||
}
|
||||
|
||||
export async function updateArticle(e, updatedArticle) {
|
||||
e.preventDefault()
|
||||
const oldArticle = this.state.articles.find(t => t.id === updatedArticle.id)
|
||||
if (JSON.stringify(oldArticle) === JSON.stringify(updatedArticle)) {
|
||||
this.navigate('articles')
|
||||
return
|
||||
}
|
||||
try {
|
||||
const { data, error } = await axios.put(
|
||||
API + `/api/articles/${oldArticle.id}`,
|
||||
updatedArticle,
|
||||
)
|
||||
if (error) {
|
||||
alert('Received error when updating article: ', error)
|
||||
} else if (data.title) {
|
||||
const oldArticles = this.state.articles.filter(t => t.id !== updatedArticle.id)
|
||||
this.setState({ articles: oldArticles.concat(updatedArticle) })
|
||||
console.log('Successfully updated article:', data)
|
||||
this.navigate('articles')
|
||||
} else {
|
||||
console.log('Received malformed data when updating article: ', data)
|
||||
}
|
||||
} catch (e) {
|
||||
alert(`Updating article failed: ${e}`)
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteArticle(id) {
|
||||
const article = this.state.articles.find(p => p.id === id)
|
||||
if (!article.id) {
|
||||
alert(`Could not find article with id ${id}.`)
|
||||
return
|
||||
}
|
||||
try {
|
||||
const { data, error } = await axios.post(
|
||||
API + `/api/articles/${article.id}/delete`,
|
||||
)
|
||||
if (error) {
|
||||
alert(`Received error when deleting article ${article.title}: ${error}`)
|
||||
} else if (data.title) {
|
||||
console.log('Deleted article:', data)
|
||||
this.setState({
|
||||
articles: this.state.articles.filter(p => p.id !== article.id),
|
||||
})
|
||||
this.navigate('articles')
|
||||
} else {
|
||||
alert(`Failed to delete article '${article.title}'.`)
|
||||
}
|
||||
} catch (e) {
|
||||
alert(`Failed to delete article '${article.title}': ${e}`)
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
import axios from 'axios'
|
||||
const API = process.env.REACT_APP_API || 'http://localhost:1337'
|
||||
|
||||
export async function replyToComment(e) {
|
||||
e.preventDefault()
|
||||
const user = {} // TODO update properties from form
|
||||
console.log(e.target)
|
||||
try {
|
||||
const { data, error } = await axios.put(API + `/api/user`, user)
|
||||
console.log('Trying to update user profile: ', user)
|
||||
if (error) {
|
||||
alert('Received error when updating user profile: ', error)
|
||||
} else if (data) {
|
||||
this.setState({ user: data })
|
||||
console.log('Successfully updated user profile:', data)
|
||||
}
|
||||
} catch (e) {
|
||||
alert(`Updating user profile failed: ${e}`)
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@ export async function createProject(e) {
|
||||
alert(`Received error when creating project ${project.name}: ${error}`)
|
||||
} else if (data.id) {
|
||||
this.setState({
|
||||
// projects: this.state.projects.concat(data),
|
||||
projects: this.state.projects.concat(data),
|
||||
newProject: '',
|
||||
})
|
||||
console.log(`Added project: `, data)
|
||||
|
@ -1,95 +0,0 @@
|
||||
import axios from 'axios'
|
||||
const API = process.env.REACT_APP_API || 'http://localhost:1337'
|
||||
|
||||
export async function addTag(e) {
|
||||
e.preventDefault()
|
||||
if (this.state.newTag === '') {
|
||||
return alert('Refusing to add an empty tag.')
|
||||
}
|
||||
const tag = {
|
||||
name: this.state.newTag,
|
||||
articleId: this.state.selectedArticleId,
|
||||
}
|
||||
try {
|
||||
const { data, error } = await axios.post(API + '/api/tags', tag)
|
||||
if (error) {
|
||||
alert(`Received error when adding tag ${tag.name} to article: ${error}`)
|
||||
} else if (data.tag && data.article) {
|
||||
const oldTags = this.state.tags.filter(t => t.id !== data.tag.id)
|
||||
const oldArticles = this.state.articles.filter(
|
||||
a => a.id !== data.article.id,
|
||||
)
|
||||
this.setState({
|
||||
tags: oldTags.concat(data.tag),
|
||||
articles: oldArticles.concat(data.article),
|
||||
newTag: '',
|
||||
})
|
||||
console.log(`Added tag to article: `, data)
|
||||
} else {
|
||||
alert(`Failed to add tag to article '${tag}'.`)
|
||||
}
|
||||
} catch (e) {
|
||||
alert(`Failed to add tag to article '${this.state.newTag}'.`)
|
||||
}
|
||||
}
|
||||
|
||||
export function selectTag(selectedTagId) {
|
||||
this.setState({ selectedTagId })
|
||||
this.navigate('articles')
|
||||
}
|
||||
|
||||
export async function removeTag(id) {
|
||||
const tag = this.state.tags.find(p => p.id === id)
|
||||
if (!tag.id) {
|
||||
alert(`Could not find tag with id ${id}.`)
|
||||
return
|
||||
}
|
||||
tag['articleId'] = this.state.selectedArticleId
|
||||
try {
|
||||
const { data, error } = await axios.put(
|
||||
API + `/api/tags/${(tag, id)}}`,
|
||||
tag,
|
||||
)
|
||||
if (error) {
|
||||
alert(`Received error when removing tag ${tag.name}: ${error}`)
|
||||
} else if (data.tag && data.article) {
|
||||
console.log(data)
|
||||
const oldTags = this.state.tags.filter(t => t.id !== data.tag.id)
|
||||
const oldArticles = this.state.articles.filter(
|
||||
a => a.id !== data.article.id,
|
||||
)
|
||||
this.setState({
|
||||
tags: oldTags.concat(data.tag),
|
||||
articles: oldArticles.concat(data.article),
|
||||
})
|
||||
console.log('Removed tag from article:', data)
|
||||
} else {
|
||||
alert(`Failed to remove tag '${tag.name}' from article .`)
|
||||
}
|
||||
} catch (e) {
|
||||
alert(`Failed to remove tag '${tag.name} from article ': ${e}`)
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteTag(id) {
|
||||
const tag = this.state.tags.find(p => p.id === id)
|
||||
if (!tag.id) {
|
||||
alert(`Could not find tag with id ${id}.`)
|
||||
return
|
||||
}
|
||||
try {
|
||||
const { data, error } = await axios.post(API + `/api/tags/${tag.id}/delete`)
|
||||
if (error) {
|
||||
alert(`Received error when deleting tag ${tag.name}: ${error}`)
|
||||
} else if (data.name) {
|
||||
console.log('Deleted tag:', data)
|
||||
this.setState({
|
||||
tags: this.state.tags.filter(p => p.id !== tag.id),
|
||||
})
|
||||
} else {
|
||||
alert(`Failed to delete tag '${tag.name}'.`)
|
||||
}
|
||||
} catch (e) {
|
||||
alert(`Failed to delete tag '${tag.name}': ${e}`)
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@ export async function createTask(e) {
|
||||
alert("Why don't you enter a name for this task?")
|
||||
return
|
||||
}
|
||||
|
||||
const newTask = {
|
||||
desc: this.state.newTask,
|
||||
projectId: this.state.selectedProjectId,
|
||||
@ -15,6 +16,7 @@ export async function createTask(e) {
|
||||
alert('Task description is empty.')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('Adding task: ', newTask)
|
||||
const { data, error } = await axios.post(API + '/api/tasks', newTask)
|
||||
@ -45,6 +47,8 @@ export async function updateTask(e, updatedTask) {
|
||||
if (error) {
|
||||
alert('Received error when updating task: ', error)
|
||||
} else if (data.desc && data.projectId) {
|
||||
const oldTasks = this.state.tasks.filter(t => t.id !== updatedTask.id)
|
||||
this.setState({ tasks: oldTasks.concat(updatedTask) })
|
||||
console.log('Successfully updated task:', data)
|
||||
} else {
|
||||
console.log('Received malformed data when updating task: ', data)
|
||||
|
Loading…
x
Reference in New Issue
Block a user