diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 00000000..4136c3fe Binary files /dev/null and b/.DS_Store differ diff --git a/api/.DS_Store b/api/.DS_Store new file mode 100644 index 00000000..88d87a41 Binary files /dev/null and b/api/.DS_Store differ diff --git a/api/entities/category.js b/api/entities/category.js new file mode 100644 index 00000000..90fae6c2 --- /dev/null +++ b/api/entities/category.js @@ -0,0 +1,25 @@ +/* eslint-disable import/no-cycle */ +import { + Entity, + Column, + PrimaryGeneratedColumn, + OneToMany, + ManyToOne, + } from 'typeorm'; + import ToDo from './todo'; + import USER from './user'; + + @Entity() + export default class category { + @PrimaryGeneratedColumn() + id + + @Column({ type: 'varchar', unique: true }) + kind + + @OneToMany(() => ToDo, (todo) => todo.categorys, { eager: true }) + category + + @ManyToOne(() => USER, (user) => user.kind) + userCategory + } \ No newline at end of file diff --git a/api/entities/todo.js b/api/entities/todo.js new file mode 100644 index 00000000..141351a9 --- /dev/null +++ b/api/entities/todo.js @@ -0,0 +1,26 @@ +import { + Entity, + Column, + PrimaryGeneratedColumn, + ManyToOne, +} from 'typeorm'; +import User from './user'; +import Category from './category'; + +@Entity() +export default class ToDo { + @PrimaryGeneratedColumn() + id + + @Column({ type: 'boolean' }) + done + + @Column({ type: 'varchar' }) + title + + @ManyToOne(() => User, (user) => user.todos) + user + + @ManyToOne(() => Category, (kind) => kind.category) + categorys +} diff --git a/api/entities/user.js b/api/entities/user.js index 11f74768..0053cc80 100644 --- a/api/entities/user.js +++ b/api/entities/user.js @@ -1,4 +1,12 @@ -import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'; +/* eslint-disable import/no-cycle */ +import { + Entity, + Column, + PrimaryGeneratedColumn, + OneToMany, +} from 'typeorm'; +import ToDo from './todo'; +import category from './category'; @Entity() export default class User { @@ -10,4 +18,10 @@ export default class User { @Column({ type: 'varchar', nullable: false }) password + + @OneToMany(() => ToDo, (todo) => todo.user, { eager: true }) + todos + + @OneToMany(() => category, (kind) => kind.userCategory, { eager: true }) + category } diff --git a/api/middleware/isAuthenticated.js b/api/middleware/isAuthenticated.js index eac97a6a..a1e6ffd0 100644 --- a/api/middleware/isAuthenticated.js +++ b/api/middleware/isAuthenticated.js @@ -1,6 +1,8 @@ -export default (req, res, next) => { +const authenticated = (req, res, next) => { if (!req.isAuthenticated()) { return res.status(401).send('You are not authenticated'); } return next(); }; + +export default authenticated; diff --git a/api/ormconfig.json.example b/api/ormconfig.json.example deleted file mode 100644 index b4843325..00000000 --- a/api/ormconfig.json.example +++ /dev/null @@ -1,11 +0,0 @@ -{ - "type": "mysql", - "host": "localhost", - "port": 3306, - "username": "admin", - "password": "test", - "database": "test", - "entities": ["entities/*.js"], - "logging": true, - "synchronize": true - } \ No newline at end of file diff --git a/api/routes/category.js b/api/routes/category.js new file mode 100644 index 00000000..9dce4080 --- /dev/null +++ b/api/routes/category.js @@ -0,0 +1,22 @@ +import { Router } from 'express'; +import { getRepository, getManager } from 'typeorm'; +import isAuthenticated from '../middleware/isAuthenticated'; +import category from '../entities/category'; + +const router = Router(); +router.route('/category') + .all(isAuthenticated) + .get((req, res) => { + res.send(req.user.category); + }) + .post((req, res) => { + const { done, title, kind} = req.body; + const manager = getManager(); + const todo = manager.create(ToDo, {done, title, kind}); + todo.user = req.user; + manager.save(todo).then((savedTodo) => { + res.send(savedTodo); + }); + }); + + export default router; \ No newline at end of file diff --git a/api/routes/login.js b/api/routes/login.js index 4737027c..c5a0f8dc 100644 --- a/api/routes/login.js +++ b/api/routes/login.js @@ -1,4 +1,5 @@ import { Router } from 'express'; +import isAuth from '../middleware/isAuthenticated'; export default (passport) => { const router = Router(); @@ -17,5 +18,8 @@ export default (passport) => { req.logout(); return res.send(); }); + router.get('/checkLogin', isAuth, (req, res) => { + res.send(req.user); + }); return router; }; diff --git a/api/routes/todo.js b/api/routes/todo.js new file mode 100644 index 00000000..d0633928 --- /dev/null +++ b/api/routes/todo.js @@ -0,0 +1,52 @@ +import { Router } from 'express'; +import { getRepository, getManager } from 'typeorm'; +import isAuthenticated from '../middleware/isAuthenticated'; +import ToDo from '../entities/todo'; + +const router = Router(); +router.route('/todos') + .all(isAuthenticated) + .get((req, res) => { + res.send(req.user.todos); + }) + .post((req, res) => { + const { done, title, kind} = req.body; + const manager = getManager(); + const todo = manager.create(ToDo, {done, title, kind}); + todo.user = req.user; + manager.save(todo).then((savedTodo) => { + res.send(savedTodo); + }); + }); +router.route('/todos/:id') + .all(isAuthenticated) + .all((req, res, next) => { + getRepository(ToDo).findOneOrFail( + { where: { userId: req.user.id, id: req.params.id } }, + ).then((_foundTodo) => { + req.todo = _foundTodo; + next(); + }, () => { + res.send(404); + }); + }) + .put((req, res) => { + const foundTodo = req.todo; + const { title, done, kind} = req.body; + foundTodo.title = title; + foundTodo.done = done; + foundTodo.kind = kind; + getManager().save(foundTodo).then((updatedTodo) => { + res.send(updatedTodo); + }); + }) + .get((req, res) => { + res.send(req.todo); + }) + .delete((req, res) => { + getManager().delete(ToDo, req.todo.id).then(() => { + res.send(200); + }); + }); + +export default router; diff --git a/api/server.js b/api/server.js index 4dcc3705..7bf148e5 100644 --- a/api/server.js +++ b/api/server.js @@ -6,6 +6,8 @@ import passport from 'passport'; import config from './config/passport'; import login from './routes/login'; +import todo from './routes/todo'; +import category from './routes/category'; // Setting up port const PORT = process.env.PORT || 3000; @@ -24,6 +26,8 @@ config(); // wire up all the routes app.use(login(passport)); +app.use(todo); +app.use(category); // respond with "hello world" when a GET request is made to the homepage app.get('/', (_req, res) => { @@ -31,5 +35,6 @@ app.get('/', (_req, res) => { }); createConnection().then(() => { + // eslint-disable-next-line no-console app.listen(PORT, () => console.log('Example app listening on port 3000!')); }); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..48e341a0 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3 @@ +{ + "lockfileVersion": 1 +} diff --git a/webapp/src/App.vue b/webapp/src/App.vue index be32b50b..3721c3d8 100644 --- a/webapp/src/App.vue +++ b/webapp/src/App.vue @@ -57,8 +57,13 @@ export default { }, methods: { logout: function() { - this.$store.dispatch("logout"); + this.$store.dispatch("logout").then(() => { + return this.$router.push("/"); + }); } + }, + mounted: function() { + this.$store.dispatch("checkLoggedIn"); } }; diff --git a/webapp/src/components/ToDo.vue b/webapp/src/components/ToDo.vue index feb5a372..91847b3b 100644 --- a/webapp/src/components/ToDo.vue +++ b/webapp/src/components/ToDo.vue @@ -1,9 +1,13 @@ @@ -17,6 +21,14 @@ export default { return {}; } } + }, + methods: { + handleCheck: function(value) { + this.$store.dispatch("updateTodo", { ...this.todo, done: value }); + }, + handleDelete: function() { + this.$store.dispatch("deleteTodo", this.todo); + } } }; diff --git a/webapp/src/store.js b/webapp/src/store.js index 351effa9..dfd3ee63 100644 --- a/webapp/src/store.js +++ b/webapp/src/store.js @@ -1,15 +1,9 @@ import Vue from "vue"; import Vuex from "vuex"; -import VuexPersist from "vuex-persist"; import axios from "axios"; Vue.use(Vuex); -const vuexPersist = new VuexPersist({ - key: "project-template", - storage: window.localStorage -}); - export const mutations = { login: function(state) { state.loginState = { ...state.loginState, loggedIn: true }; @@ -18,18 +12,29 @@ export const mutations = { state.loginState = { ...state.loginState, loggedIn: false }; }, addToDo(state, todo) { - state.todos = [ - ...state.todos, - { ...todo, done: false, id: state.todos.length + 1 } - ]; + state.todoIdx = state.todoIdx + 1; + state.todos = [...state.todos, { ...todo, done: false, id: state.todoIdx}]; + }, + updateToDo(state, todo) { + state.todos = state.todos.map(td => (td.id === todo.id ? todo : td)); + }, + deleteToDo(state, todo) { + state.todos = state.todos.filter(td => td.id !== todo.id); + }, + todosLoaded(state, todos) { + state.todos = todos; + }, + categoryLoaded(state, category) { + state.categorys = category; } }; export const actions = { - login: function({ commit }, payload) { + login: function({ commit, dispatch }, payload) { const { email, password } = payload; return axios.post("/api/login", { email, password }).then(() => { commit("login"); + return dispatch("loadTodos"); }); }, logout: function({ commit }) { @@ -38,18 +43,46 @@ export const actions = { }); }, addToDo({ commit }, toDo) { - debugger; - commit("addToDo", toDo); + return axios.post("/api/todos", toDo).then(response => { + commit("addToDo", response.data); + }); + }, + + updateTodo({ commit }, toDo) { + return axios.put(`/api/todos/${toDo.id}`, toDo).then(response => { + commit("updateToDo", response.data); + }); + }, + deleteTodo({ commit }, toDo) { + return axios.delete(`/api/todos/${toDo.id}`).then(() => { + commit("deleteToDo", toDo); + }); + }, + loadToDos({ commit }) { + return axios.get("/api/todos").then(response => { + commit("todosLoaded", response.data); + }); + }, + loadCategory({ commit }) { + return axios.get("/api/category").then(response => { + commit("categoryLoaded", response.data); + }); + }, + checkLoggedIn({ commit }) { + return axios.get("/api/checkLogin").then(() => { + commit("login"); + }); } }; export default new Vuex.Store({ - plugins: [vuexPersist.plugin], state: { todos: [], loginState: { loggedIn: false - } + }, + todoIdx: 0, + categorys:[], }, mutations, actions diff --git a/webapp/src/views/ToDos.vue b/webapp/src/views/ToDos.vue index b6c749ff..9d9feb72 100644 --- a/webapp/src/views/ToDos.vue +++ b/webapp/src/views/ToDos.vue @@ -2,13 +2,13 @@
-
My ToDos
+
My ToDos
@@ -17,14 +17,24 @@
New ToDo
- + + + + + + -
- -
+
+ +
-
+
@@ -37,26 +47,40 @@ export default { data: function() { return { newTodo: { - title: null - } + title: null, + kind: null, + }, }; }, computed: { todos() { return this.$store.state.todos; + }, + kind() { + return this.$store.state.categorys; } }, components: { ToDo }, methods: { - onSubmit () { - this.$store.dispatch('addToDo', this.newTodo).then(() => { + onSubmit() { + this.$store.dispatch("addToDo", this.newTodo).then(() => { this.newTodo.title = null; - }) - } + this.newTodo.kind = null; + }); + }, + }, + mounted: function() { + this.$store.dispatch("loadToDos").catch(() => { + // if we are not logged in redirect home + this.$router.push("/"); + }), + this.$store.dispatch("loadCategory").catch(() => { + // if we are not logged in redirect home + this.$router.push("/"); + }) } }; -> +