From 5b8dad103d6a661e35ee5ddc07e1ea2c2214f812 Mon Sep 17 00:00:00 2001 From: sdweber422 Date: Mon, 19 Dec 2016 18:09:58 -0800 Subject: [PATCH 01/17] Changes to models and migrations to take extra column isDeleted --- migrations/20161018174824-create-item.js | 7 ++++++- models/item.js | 3 ++- routes/items.js | 1 - 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/migrations/20161018174824-create-item.js b/migrations/20161018174824-create-item.js index f08d6da..c420d43 100644 --- a/migrations/20161018174824-create-item.js +++ b/migrations/20161018174824-create-item.js @@ -35,10 +35,15 @@ module.exports = { updatedAt: { allowNull: false, type: Sequelize.DATE + }, + is_deleted: { + type: Sequelize.BOOLEAN, + allowNull: false, + defaultValue: false } }); }, down: function(queryInterface, Sequelize) { return queryInterface.dropTable('Items'); } -}; \ No newline at end of file +}; diff --git a/models/item.js b/models/item.js index 38a86e1..e416749 100644 --- a/models/item.js +++ b/models/item.js @@ -10,7 +10,8 @@ module.exports = function(sequelize, DataTypes) { is_root: DataTypes.BOOLEAN, starred: DataTypes.BOOLEAN, parent_id: DataTypes.INTEGER, - user_id: DataTypes.INTEGER + user_id: DataTypes.INTEGER, + is_deleted: DataTypes.BOOLEAN }, { classMethods: { associate: function(models) { diff --git a/routes/items.js b/routes/items.js index fbdbdea..ac15e83 100644 --- a/routes/items.js +++ b/routes/items.js @@ -9,7 +9,6 @@ const buildStarredItemArray = require( './items/build_starred_item_array' ) router.get( '/', ( request, response ) => { const { Item } = request.app.get( 'models' ) - const { user, query } = request buildFilteredItemTree( Item, user, query ) From c50b89c723dc1f4069f58de495378d5bb721961b Mon Sep 17 00:00:00 2001 From: generalmeow Date: Tue, 20 Dec 2016 11:31:36 -0800 Subject: [PATCH 02/17] created migration for is_deleted --- migrations/20161220192127-add-is-deleted-to-items.js | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 migrations/20161220192127-add-is-deleted-to-items.js diff --git a/migrations/20161220192127-add-is-deleted-to-items.js b/migrations/20161220192127-add-is-deleted-to-items.js new file mode 100644 index 0000000..a21e088 --- /dev/null +++ b/migrations/20161220192127-add-is-deleted-to-items.js @@ -0,0 +1,11 @@ +'use strict'; + + +module.exports = { + up: function (queryInterface, Sequelize) { + queryInterface.addColumn( 'Items', 'is_deleted', { type: Sequelize.BOOLEAN, defaultValue: false, allowNull: false } ) + }, + down: function (queryInterface, Sequelize) { + queryInterface.removeColumn( 'Items', 'is_deleted') + } +}; From 277e9f10e4574b89528431699fcb3cdd3210463f Mon Sep 17 00:00:00 2001 From: sdweber422 Date: Tue, 20 Dec 2016 13:59:56 -0800 Subject: [PATCH 03/17] Changes model to reflect is_deleted column --- models/item.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/models/item.js b/models/item.js index 38a86e1..93b9be9 100644 --- a/models/item.js +++ b/models/item.js @@ -10,7 +10,8 @@ module.exports = function(sequelize, DataTypes) { is_root: DataTypes.BOOLEAN, starred: DataTypes.BOOLEAN, parent_id: DataTypes.INTEGER, - user_id: DataTypes.INTEGER + user_id: DataTypes.INTEGER, + is_deleted: DataTypes.BOOLEAN }, { classMethods: { associate: function(models) { From 8adeed6e58072ab6a774f2413c33a7a9afedeb32 Mon Sep 17 00:00:00 2001 From: Shaka Lee Date: Wed, 21 Dec 2016 16:05:22 -0800 Subject: [PATCH 04/17] Export all [Fixes #17] * Added export all option to navigation * Preview plain text and html exports * Download plain text and styled exports --- public/javascripts/client.js | 29 +++++++ public/stylesheets/exportmodal.css | 113 +++++++++++++++++++++++++ public/stylesheets/navbar.css | 7 +- public/stylesheets/style.css | 2 +- routes/items.js | 12 +++ views/items/html.pug | 3 + views/items/index.pug | 18 +--- views/items/mixins.pug | 24 ++++++ views/items/plain_text.pug | 3 + views/items/preview-export.pug | 26 ++++++ views/layouts/authenticated-layout.pug | 6 +- views/layouts/layout.pug | 1 - 12 files changed, 221 insertions(+), 23 deletions(-) create mode 100644 public/javascripts/client.js create mode 100644 public/stylesheets/exportmodal.css create mode 100644 views/items/html.pug create mode 100644 views/items/mixins.pug create mode 100644 views/items/plain_text.pug create mode 100644 views/items/preview-export.pug diff --git a/public/javascripts/client.js b/public/javascripts/client.js new file mode 100644 index 0000000..637660a --- /dev/null +++ b/public/javascripts/client.js @@ -0,0 +1,29 @@ +$( document ).ready( () => { + const modal = document.querySelector( '.modal' ) + + document.querySelector( '.export-link' ).onclick = event => { + event.preventDefault() + + modal.style.display = 'block' + } + + document.querySelector( '.preview-window--footer__close-link' ).onclick = event => { + event.preventDefault() + + modal.style.display = 'none' + } + + document.getElementById( 'plain-text-preview' ).onclick = event => { + $( '#preview-text-text.item-page.text__tree.hidden' ).removeClass( 'hidden' ).addClass( 'show' ) + $( '#preview-text.item-page.html__tree.show' ).removeClass( 'show' ).addClass( 'hidden' ) + $( '.plain-text-download' ).removeClass( 'hidden' ).addClass( 'show' ) + $( '.html-text-download' ).removeClass( 'show' ).addClass( 'hidden' ) + } + + document.getElementById( 'formatted-text-preview' ).onclick = event => { + $( '#preview-text.item-page.html__tree.hidden' ).removeClass( 'hidden' ).addClass( 'show' ) + $( '#preview-text-text.item-page.text__tree.show' ).removeClass( 'show' ).addClass( 'hidden' ) + $( '.html-text-download' ).removeClass( 'hidden' ).addClass( 'show' ) + $( '.plain-text-download' ).removeClass( 'show' ).addClass( 'hidden' ) + } +}) diff --git a/public/stylesheets/exportmodal.css b/public/stylesheets/exportmodal.css new file mode 100644 index 0000000..6fd1837 --- /dev/null +++ b/public/stylesheets/exportmodal.css @@ -0,0 +1,113 @@ +.modal { + display: none; + font-family: 'Open Sans', Arial; + background-color: white; + width: 65%; + font-size: 15px; + height: 500px; + position: absolute; + top: 20%; + left: 20%; + bottom: 20%; + right: 20%; + overflow: auto; + z-index: 101; + box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); + outline: .5px #888 solid; +} + +a.preview-window--footer__download-link, a.preview-window--footer__download-link:visited { + font-size: 15px; + color: blue; +} + +.hidden { + display: none; +} + +.show { + display: block; + font-size: 14px; +} + +.preview-window--header { + display: flex; + padding-left: 15px; + padding-right: 15px; + width: 100%; + color: #4F4F4F; + text-align: left; + text-shadow: 0 1px 0 rgba(255,255,255,0.5); + flex-direction: row; + background: rgba(226,226,226,1); + background: -moz-linear-gradient(left, rgba(226,226,226,1) 0%, rgba(221,221,221,1) 41%, rgba(219,219,219,1) 58%, rgba(244,244,244,1) 87%, rgba(254,254,254,1) 98%); + background: -webkit-gradient(left top, right top, color-stop(0%, rgba(226,226,226,1)), color-stop(41%, rgba(221,221,221,1)), color-stop(58%, rgba(219,219,219,1)), color-stop(87%, rgba(244,244,244,1)), color-stop(98%, rgba(254,254,254,1))); + background: -webkit-linear-gradient(left, rgba(226,226,226,1) 0%, rgba(221,221,221,1) 41%, rgba(219,219,219,1) 58%, rgba(244,244,244,1) 87%, rgba(254,254,254,1) 98%); + background: -o-linear-gradient(left, rgba(226,226,226,1) 0%, rgba(221,221,221,1) 41%, rgba(219,219,219,1) 58%, rgba(244,244,244,1) 87%, rgba(254,254,254,1) 98%); + background: -ms-linear-gradient(left, rgba(226,226,226,1) 0%, rgba(221,221,221,1) 41%, rgba(219,219,219,1) 58%, rgba(244,244,244,1) 87%, rgba(254,254,254,1) 98%); + background: linear-gradient(to right, rgba(226,226,226,1) 0%, rgba(221,221,221,1) 41%, rgba(219,219,219,1) 58%, rgba(244,244,244,1) 87%, rgba(254,254,254,1) 98%); +} + +.preview-window--body { + outline: .5px #888 solid; + margin-left: 15px; + margin-right: 15px; + margin-top: 15px; + padding-top: 15px; + padding-left: 15px; + padding-right: 15px; + overflow-y: auto; + justify-content: center; + overflow: auto; + height: 200px; + width: auto; +} + +.preview-window--body__item { + margin-right: 14px; + margin-left: 14px; + padding: 15px; + background-color: #EEE; + align-content: center; + justify-content: space-between; + border: .25px #B6B6B6 solid; +} + +.preview-window--footer { + color: #666; + font-size: 15px; + display: flex; + flex-direction: column; + align-content: center; + align-items: center; + justify-content: center; +} + +p.preview-window--footer__item { + margin: 5px; +} + +.preview-window--footer__separator { + border-top: .5px #888 solid; + margin-top: 10px; + display: flex; + flex-direction: column; + align-content: center; + align-items: center; + justify-content: center; +} + +.preview-window--footer__close-link { + position: relative; + margin-top: 10px; + margin-bottom: 5px; + padding: 5px; + font-size: 13px; + color: white; + width: 25%; + border: 1px solid #111; +} + +.preview-window--footer__close-link:hover { + background-color: #888; +} diff --git a/public/stylesheets/navbar.css b/public/stylesheets/navbar.css index c94fdec..fa16c9b 100644 --- a/public/stylesheets/navbar.css +++ b/public/stylesheets/navbar.css @@ -148,7 +148,8 @@ text-decoration: none; color: #fff; width: 100%; - padding: 6px 0; + padding-top: 10px; + padding-bottom: 10px; display: block; } @@ -157,10 +158,6 @@ cursor: pointer; } -.dropdown__item--label { - margin-left: 10px; -} - .dropdown__toggle--open { border-bottom: none; background-color: #666; diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css index 992048e..5b72fa5 100644 --- a/public/stylesheets/style.css +++ b/public/stylesheets/style.css @@ -1,4 +1,4 @@ -body{ +body { min-width: 100%; top: 0; left: 0; diff --git a/routes/items.js b/routes/items.js index cb4eab8..e088037 100644 --- a/routes/items.js +++ b/routes/items.js @@ -25,6 +25,17 @@ router.get( '/starred', ( request, response ) => { .then( data => response.json( { data } ) ) }) +router.get( '/download/:type', ( request, response ) => { + const { Item } = request.app.get( 'models' ) + + const { user, query } = request + + response.set( 'Content-Type', 'text/plain' ) + buildFilteredItemTree( Item, user, query ) + .then( generateBreadcrumbs ) + .then( respondWithItems( user, data => response.render( `items/${request.params.type}`, data ))) +}) + router.get( '/:item_id', ( request, response ) => { const { Item } = request.app.get( 'models' ) @@ -64,4 +75,5 @@ router.post( '/:id', ( request, response ) => { ) }) + module.exports = router diff --git a/views/items/html.pug b/views/items/html.pug new file mode 100644 index 0000000..8b46a67 --- /dev/null +++ b/views/items/html.pug @@ -0,0 +1,3 @@ +include mixins.pug + ++display( tree, 0 ) \ No newline at end of file diff --git a/views/items/index.pug b/views/items/index.pug index a8b531e..4c10055 100644 --- a/views/items/index.pug +++ b/views/items/index.pug @@ -1,22 +1,10 @@ extends ../layouts/authenticated-layout -mixin display( nodes, depth ) - each entry in nodes - .item(data-id=entry.id) - .item__heading(data-id=entry.id, data-completed=entry.completed) - .item__toggle(aria-hidden='true', data-id=entry.id, data-completed=entry.completed) - |● - .item__title(data-id=entry.id, data-completed=entry.completed, class={completed: entry.completed}) - span(data-id=entry.id)=entry.title - input.item__edit-title.item--hidden(data-id=entry.id, type='text', name='edit-title', value=entry.title) - .item__description(data-id=entry.id) - span(data-id=entry.id)=entry.description - input.item__edit-description.item--hidden(data-id=entry.id, type='text', name='edit-description', value=entry.description) - .item__children - +display( entry.children, depth + 1 ) +include mixins.pug block content - + .modal + include preview-export.pug div.main-page .work-page .work-page__main diff --git a/views/items/mixins.pug b/views/items/mixins.pug new file mode 100644 index 0000000..bf18523 --- /dev/null +++ b/views/items/mixins.pug @@ -0,0 +1,24 @@ +mixin textEntry( entry, depth ) + | !{`${" ".repeat( depth * 2 )}- ${entry.completed ? '[COMPLETED] ' : ''}${entry.title}\n`} + if( entry.description ) + | !{`${" ".repeat( depth * 2 + 2 )}"${entry.description}"`} + +mixin displayText( nodes, depth ) + each entry in nodes + +textEntry( entry, depth ) + +displayText( entry.children, depth + 1 ) + +mixin display( nodes, depth ) + each entry in nodes + .item(data-id=entry.id) + .item__heading(data-id=entry.id, data-completed=entry.completed) + .item__toggle(aria-hidden='true', data-id=entry.id, data-completed=entry.completed) + |● + .item__title(data-id=entry.id, data-completed=entry.completed) + span(data-id=entry.id)=entry.title + input.item__edit-title.item--hidden(data-id=entry.id, type='text', name='edit-title', value=entry.title) + .item__description(data-id=entry.id) + span(data-id=entry.id)=entry.description + input.item__edit-description.item--hidden(data-id=entry.id, type='text', name='edit-description', value=entry.description) + .item__children + +display( entry.children, depth + 1 ) \ No newline at end of file diff --git a/views/items/plain_text.pug b/views/items/plain_text.pug new file mode 100644 index 0000000..85432ef --- /dev/null +++ b/views/items/plain_text.pug @@ -0,0 +1,3 @@ +include mixins.pug + ++displayText( tree, 0 ) \ No newline at end of file diff --git a/views/items/preview-export.pug b/views/items/preview-export.pug new file mode 100644 index 0000000..3b8a182 --- /dev/null +++ b/views/items/preview-export.pug @@ -0,0 +1,26 @@ +modal + .preview-window + .preview-window--header + h3.preview-window--header__item Export List + + .preview-window--body + #preview-text.item-page.html__tree.show + +display( tree, 0 ) + #preview-text-text.item-page.text__tree.hidden + pre + +displayText( tree, 0 ) + .preview-window--body__item + form + label Formatted + input#formatted-text-preview.preview-window--body__item( type='radio' checked name="selected-preview") + label Plain Text + input#plain-text-preview.preview-window--body__item( type='radio' name="selected-preview") + + .preview-window--footer + p.preview-window--footer__item Press ⌘ + C to copy your list. You can paste it anywhere. + .plain-text-download.show + a.preview-window--footer__download-link(href='/items/download/plain_text' rel='external' download='floworky-export.txt') (Or click to download) + .html-text-download.hidden + a.preview-window--footer__download-link(href='/items/download/html' rel='external' download='floworky-export.html') (Or click to download) + .preview-window--footer__separator + button.preview-window--footer__close-link Close \ No newline at end of file diff --git a/views/layouts/authenticated-layout.pug b/views/layouts/authenticated-layout.pug index 967fcf0..ff7a8d6 100644 --- a/views/layouts/authenticated-layout.pug +++ b/views/layouts/authenticated-layout.pug @@ -13,6 +13,7 @@ html link(rel='stylesheet', href='/stylesheets/navbar.css') link(rel='stylesheet', href='/stylesheets/authenticated.css') link(rel='stylesheet', href='/stylesheets/weekly-review.css') + link(rel='stylesheet', href='/stylesheets/exportmodal.css') body nav.navbar @@ -39,6 +40,8 @@ html li.dropdown__item a.dropdown__item--link .dropdown__item--label#weekly-summary Weekly Summary + li.dropdown__item + a.dropdown__item--link.export-link Export All li.dropdown__item a.dropdown__item--link(href='/accounts/logout') .dropdown__item--label Log Out @@ -46,7 +49,7 @@ html .container block content - + include ../accounts/weekly-report.pug script(src='https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js') @@ -54,3 +57,4 @@ html script(src='/javascripts/items.js') script(src='/javascripts/starred.js') script(src='/javascripts/weekly-review.js') + script(src='/javascripts/client.js') diff --git a/views/layouts/layout.pug b/views/layouts/layout.pug index ef7f221..6c4d3cc 100644 --- a/views/layouts/layout.pug +++ b/views/layouts/layout.pug @@ -12,7 +12,6 @@ html link(href='https://fonts.googleapis.com/css?family=Roboto:300', rel='stylesheet') link(rel='stylesheet', href='/stylesheets/style.css') link(rel='stylesheet', href='/stylesheets/login.css') - body .login-page nav From e545d1125bf3d4a2574b94dfa95d73aecb72a594 Mon Sep 17 00:00:00 2001 From: jrob8577 Date: Wed, 21 Dec 2016 16:22:43 -0800 Subject: [PATCH 05/17] travis bump --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 954b46c..f49f0c8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: node_js sudo: false node_js: -- 6.6.0 +- 7.2.0 deploy: provider: heroku api_key: From a7385a3ae4e0b5cbea88366535738f206691f89d Mon Sep 17 00:00:00 2001 From: generalmeow Date: Tue, 20 Dec 2016 11:31:36 -0800 Subject: [PATCH 06/17] created migration for is_deleted --- migrations/20161220192127-add-is-deleted-to-items.js | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 migrations/20161220192127-add-is-deleted-to-items.js diff --git a/migrations/20161220192127-add-is-deleted-to-items.js b/migrations/20161220192127-add-is-deleted-to-items.js new file mode 100644 index 0000000..a21e088 --- /dev/null +++ b/migrations/20161220192127-add-is-deleted-to-items.js @@ -0,0 +1,11 @@ +'use strict'; + + +module.exports = { + up: function (queryInterface, Sequelize) { + queryInterface.addColumn( 'Items', 'is_deleted', { type: Sequelize.BOOLEAN, defaultValue: false, allowNull: false } ) + }, + down: function (queryInterface, Sequelize) { + queryInterface.removeColumn( 'Items', 'is_deleted') + } +}; From 6bc58e5175192c52b2cf7aca2d022e505f5fc11e Mon Sep 17 00:00:00 2001 From: generalmeow Date: Wed, 21 Dec 2016 16:40:30 -0800 Subject: [PATCH 07/17] WIP - added delete link to items/index.pug, click function to jQuery to mimic dropdown delete, and delete functionality to route to change is_delete flag to true --- models/item.js | 2 +- public/javascripts/items.js | 17 +++++++++++++++++ routes/items.js | 19 +++++++++++++++++++ views/items/index.pug | 1 + 4 files changed, 38 insertions(+), 1 deletion(-) diff --git a/models/item.js b/models/item.js index e416749..dda907e 100644 --- a/models/item.js +++ b/models/item.js @@ -1,6 +1,6 @@ 'use strict'; -const VALID_PARAMETERS = [ 'completed', 'title', 'description', 'starred' ] +const VALID_PARAMETERS = [ 'completed', 'title', 'description', 'starred', 'is_deleted' ] module.exports = function(sequelize, DataTypes) { var Item = sequelize.define('Item', { diff --git a/public/javascripts/items.js b/public/javascripts/items.js index 2c69885..a73bfe9 100644 --- a/public/javascripts/items.js +++ b/public/javascripts/items.js @@ -18,6 +18,8 @@ const titleEdited = event => { const elementToHide = $( event.target ) const id = elementToHide.data( 'id' ) const elementToShow = $( selector( 'item__title', id, 'span' ) ) + console.log('element', elementToHide); + if( event.keyCode === RETURN_KEY ) { let updatedTitle = elementToHide[0].value @@ -128,6 +130,20 @@ const completedClicked = event => { ) } + const deleteItem = (event) => { + const elementToHide = $( event.target ) + const id = elementToHide.data( 'id' ) + const elementToShow = $( selector( 'item__title', id, 'span' ) ) + + let updatedTitle = elementToHide[0].value + fetch( `/items/delete/${id}`, params( { title: updatedTitle } ) ) + .then( result => result.json() ) + .then ( checkJsonForSuccessField ) + .then( json => { + // toggle( elementToShow, elementToHide ) + $( elementToShow[0] ).html(updatedTitle) + }) + } $(document).ready( () => { $( '.item__edit-title' ).keypress( titleEdited ) $( '.item__title > span' ).click( clickToUpdate( 'item__title' )) @@ -136,6 +152,7 @@ $(document).ready( () => { $( '.item__toggle' ).click( completedClicked ) $( '.dropdown__toggle' ).click( dropdownToggle ) $( '.star' ).click( starredToggle ) + $( '.deleted').click( deleteItem ) getFilterStatus() getCheckedStatus() }) diff --git a/routes/items.js b/routes/items.js index ac15e83..0c08ad8 100644 --- a/routes/items.js +++ b/routes/items.js @@ -63,4 +63,23 @@ router.post( '/:id', ( request, response ) => { ) }) +router.post( '/delete/:id', ( request, response ) => { + const { Item, Audit } = request.app.get( 'models' ) + const { id } = request.params + const user_id = parseInt( request.user.id ) + + Item.findOne({ where: { id, user_id } }) + .then( item => { + const softDeletedItem = item + softDeletedItem.update( Item.filterParameters( { is_deleted: true } ) ) + return softDeletedItem + }) + .then( createAuditEntries( Item, Audit, user_id ) ) + .then( result => response.json({ success: true, id })) + .catch( error => + response.json({ success: false, id, message: error.message }) + ) +}) + + module.exports = router diff --git a/views/items/index.pug b/views/items/index.pug index a8b531e..522dd05 100644 --- a/views/items/index.pug +++ b/views/items/index.pug @@ -8,6 +8,7 @@ mixin display( nodes, depth ) |● .item__title(data-id=entry.id, data-completed=entry.completed, class={completed: entry.completed}) span(data-id=entry.id)=entry.title + a.deleted(href='#', data-id=entry.id, data-completed=entry.completed) Delete input.item__edit-title.item--hidden(data-id=entry.id, type='text', name='edit-title', value=entry.title) .item__description(data-id=entry.id) span(data-id=entry.id)=entry.description From 9158ba4186e4f9a26d074ef5f878c72d0d1fb45d Mon Sep 17 00:00:00 2001 From: sdweber422 Date: Thu, 22 Dec 2016 09:56:45 -0800 Subject: [PATCH 08/17] Added is_deleted to FETCH_ATTRTIBUTES, altered ItemNode constructor to include is_deleted, added deleteItem to jQuery to soft delete from database and hide associated elements on page --- public/javascripts/items.js | 16 ++++++++-------- routes/items/item_response.js | 5 +++-- src/item_node.js | 4 ++-- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/public/javascripts/items.js b/public/javascripts/items.js index a73bfe9..68e5616 100644 --- a/public/javascripts/items.js +++ b/public/javascripts/items.js @@ -130,18 +130,18 @@ const completedClicked = event => { ) } - const deleteItem = (event) => { - const elementToHide = $( event.target ) - const id = elementToHide.data( 'id' ) - const elementToShow = $( selector( 'item__title', id, 'span' ) ) + const deleteItem = ( event ) => { + const elementToDelete = $( event.target ).parent().children() + const id = elementToDelete.data( 'id' ) + const elementToHide = $( `.item__toggle[data-id='${id}']` ) - let updatedTitle = elementToHide[0].value - fetch( `/items/delete/${id}`, params( { title: updatedTitle } ) ) + let deletedTitle = elementToDelete[0].value + fetch( `/items/delete/${id}`, params( { title: deletedTitle } ) ) .then( result => result.json() ) .then ( checkJsonForSuccessField ) .then( json => { - // toggle( elementToShow, elementToHide ) - $( elementToShow[0] ).html(updatedTitle) + $( elementToDelete[0] ).addClass( 'item--hidden' ) + $( elementToHide[0] ).addClass( 'item--hidden' ) }) } $(document).ready( () => { diff --git a/routes/items/item_response.js b/routes/items/item_response.js index 5655061..9e58d59 100644 --- a/routes/items/item_response.js +++ b/routes/items/item_response.js @@ -1,6 +1,6 @@ const { pruneTree } = require( './tree_creation' ) -const FETCH_ATTRIBUTES = [ 'title', 'description', 'completed', 'starred', 'is_root', 'parent_id', 'id' ] +const FETCH_ATTRIBUTES = [ 'title', 'description', 'completed', 'starred', 'is_root', 'parent_id', 'id', 'is_deleted' ] const createRootItem = Item => user => { return Item.create({ @@ -8,7 +8,8 @@ const createRootItem = Item => user => { parent_id: 0, title: 'Home', description: 'Welcome to Floworky', - user_id: user.id + user_id: user.id, + is_deleted: false }).then( result => user ) } diff --git a/src/item_node.js b/src/item_node.js index 94fc1c9..f733786 100644 --- a/src/item_node.js +++ b/src/item_node.js @@ -1,6 +1,6 @@ class ItemNode { constructor( item ) { - const { id, parent_id, title, description, completed, starred, is_root } = item + const { id, parent_id, title, description, completed, starred, is_root, is_deleted } = item this.id = id this.parent_id = parent_id @@ -9,7 +9,7 @@ class ItemNode { this.completed = completed this.starred = starred this.is_root = is_root - + this.is_deleted = is_deleted this.children = [] } From aa4d198102f821ab9593d5ea41767363d44d956b Mon Sep 17 00:00:00 2001 From: TJ Walker Date: Thu, 22 Dec 2016 11:24:07 -0800 Subject: [PATCH 09/17] Task Dropdown Menu [Fixes #124] * Reveal item Menu On Bullet Hover --- public/javascripts/task-dropdown.js | 12 +++++++ public/stylesheets/authenticated.css | 1 + public/stylesheets/item-menu.css | 50 ++++++++++++++++++++++++++ views/items/item-menu.pug | 7 ++++ views/items/mixins.pug | 6 ++-- views/layouts/authenticated-layout.pug | 4 ++- 6 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 public/javascripts/task-dropdown.js create mode 100644 public/stylesheets/item-menu.css create mode 100644 views/items/item-menu.pug diff --git a/public/javascripts/task-dropdown.js b/public/javascripts/task-dropdown.js new file mode 100644 index 0000000..ee3f115 --- /dev/null +++ b/public/javascripts/task-dropdown.js @@ -0,0 +1,12 @@ +const taskMenuTriggers = document.querySelectorAll( '.item__toggle' ) + +taskMenuTriggers.forEach( trigger => { + trigger.addEventListener( 'mouseenter', event => { + event.target.children[ 0 ].style.display = 'block' + }) + + trigger.addEventListener( 'mouseleave', event => { + event.target.children[ 0 ].style.display = 'none' + }) +}) + diff --git a/public/stylesheets/authenticated.css b/public/stylesheets/authenticated.css index 0f1d3a4..dda89b4 100644 --- a/public/stylesheets/authenticated.css +++ b/public/stylesheets/authenticated.css @@ -69,6 +69,7 @@ } .item__toggle { + position: relative; display: inline; font-size: 14px; padding: 0 6px; diff --git a/public/stylesheets/item-menu.css b/public/stylesheets/item-menu.css new file mode 100644 index 0000000..e4e0488 --- /dev/null +++ b/public/stylesheets/item-menu.css @@ -0,0 +1,50 @@ +.item__menu { + display: none; + position: absolute; + left: 50%; + transform: translateX(-50%); + margin-top: 14px; + background-color: #e8e8e8; + padding: 10px 5px; + width: 80px; + margin-bottom: 10px; + border-radius: 4px; + border: 1px solid #bbb ; + z-index: 8; +} + +.item__menu::before { + content: ''; + position: absolute; + top: -15px; + left: 50%; + transform: translateX(-50%); + border-bottom: 15px solid hsl(0, 0%, 91%); + border-left: 15px solid transparent; + border-right: 15px solid transparent; +} + +.item__menu ul { + padding: 0; + margin: 0; + list-style: none; +} + +.item__menu li { + border-top: 1px solid #bbb; + padding: 5px 0; +} + +.item__menu li:hover { + text-decoration: underline; + cursor: pointer; +} + +.item__menu li:first-child { + border-top: none; + padding-top: 0; +} + +.item__menu li:last-child { + padding-bottom: 0; +} diff --git a/views/items/item-menu.pug b/views/items/item-menu.pug new file mode 100644 index 0000000..dd54579 --- /dev/null +++ b/views/items/item-menu.pug @@ -0,0 +1,7 @@ +ul + li Complete + li Add Note + li Share + li Export + li Duplicate + li Delete diff --git a/views/items/mixins.pug b/views/items/mixins.pug index bf18523..ab9f5f2 100644 --- a/views/items/mixins.pug +++ b/views/items/mixins.pug @@ -14,11 +14,13 @@ mixin display( nodes, depth ) .item__heading(data-id=entry.id, data-completed=entry.completed) .item__toggle(aria-hidden='true', data-id=entry.id, data-completed=entry.completed) |● - .item__title(data-id=entry.id, data-completed=entry.completed) + .item__menu + include item-menu.pug + .item__title(data-id=entry.id, data-completed=entry.completed, class={completed: entry.completed}) span(data-id=entry.id)=entry.title input.item__edit-title.item--hidden(data-id=entry.id, type='text', name='edit-title', value=entry.title) .item__description(data-id=entry.id) span(data-id=entry.id)=entry.description input.item__edit-description.item--hidden(data-id=entry.id, type='text', name='edit-description', value=entry.description) .item__children - +display( entry.children, depth + 1 ) \ No newline at end of file + +display( entry.children, depth + 1 ) diff --git a/views/layouts/authenticated-layout.pug b/views/layouts/authenticated-layout.pug index ff7a8d6..9d83e5e 100644 --- a/views/layouts/authenticated-layout.pug +++ b/views/layouts/authenticated-layout.pug @@ -14,7 +14,8 @@ html link(rel='stylesheet', href='/stylesheets/authenticated.css') link(rel='stylesheet', href='/stylesheets/weekly-review.css') link(rel='stylesheet', href='/stylesheets/exportmodal.css') - + link(rel='stylesheet', href='/stylesheets/item-menu.css') + body nav.navbar .navbar__header @@ -58,3 +59,4 @@ html script(src='/javascripts/starred.js') script(src='/javascripts/weekly-review.js') script(src='/javascripts/client.js') + script(src='/javascripts/task-dropdown.js') From ea68792354321cd34f12c3826bd68ac1e1d3cddb Mon Sep 17 00:00:00 2001 From: sdweber422 Date: Thu, 22 Dec 2016 12:17:56 -0800 Subject: [PATCH 10/17] Reverted models/item.js to original and migrations/...create-item.js to original, changed declarations model to refelct is_deleted, and added functionality to not show soft deleted items --- declarations/database/models.js | 1 + migrations/20161018174824-create-item.js | 7 +------ models/item.js | 5 ++--- routes/items/item_response.js | 5 +++-- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/declarations/database/models.js b/declarations/database/models.js index b57b198..3f6e1d5 100644 --- a/declarations/database/models.js +++ b/declarations/database/models.js @@ -6,6 +6,7 @@ declare type Item = { starred: boolean; parent_id: number; user_id: number; + is_deleted: boolean; }; declare type User = { diff --git a/migrations/20161018174824-create-item.js b/migrations/20161018174824-create-item.js index c420d43..f08d6da 100644 --- a/migrations/20161018174824-create-item.js +++ b/migrations/20161018174824-create-item.js @@ -35,15 +35,10 @@ module.exports = { updatedAt: { allowNull: false, type: Sequelize.DATE - }, - is_deleted: { - type: Sequelize.BOOLEAN, - allowNull: false, - defaultValue: false } }); }, down: function(queryInterface, Sequelize) { return queryInterface.dropTable('Items'); } -}; +}; \ No newline at end of file diff --git a/models/item.js b/models/item.js index dda907e..38a86e1 100644 --- a/models/item.js +++ b/models/item.js @@ -1,6 +1,6 @@ 'use strict'; -const VALID_PARAMETERS = [ 'completed', 'title', 'description', 'starred', 'is_deleted' ] +const VALID_PARAMETERS = [ 'completed', 'title', 'description', 'starred' ] module.exports = function(sequelize, DataTypes) { var Item = sequelize.define('Item', { @@ -10,8 +10,7 @@ module.exports = function(sequelize, DataTypes) { is_root: DataTypes.BOOLEAN, starred: DataTypes.BOOLEAN, parent_id: DataTypes.INTEGER, - user_id: DataTypes.INTEGER, - is_deleted: DataTypes.BOOLEAN + user_id: DataTypes.INTEGER }, { classMethods: { associate: function(models) { diff --git a/routes/items/item_response.js b/routes/items/item_response.js index 9e58d59..62df320 100644 --- a/routes/items/item_response.js +++ b/routes/items/item_response.js @@ -12,9 +12,10 @@ const createRootItem = Item => user => { is_deleted: false }).then( result => user ) } +const filterStatus = user_id => ( { user_id, is_deleted: false } ) const allItemsQuery = user_id => ( - { order: [['createdAt', 'ASC']], where: { user_id }, FETCH_ATTRIBUTES } + { order: [['createdAt', 'ASC']], where: filterStatus( user_id ), FETCH_ATTRIBUTES } ) const filterClause = ( query, user_id ) => @@ -24,7 +25,7 @@ const filterClause = ( query, user_id ) => whereSearch( query ), whereCompleted( query ), whereStarred( query ), - { user_id } + filterStatus( user_id ) ) }) From 9aad6c28e7da2be3dad2b8d07a23275440cdad57 Mon Sep 17 00:00:00 2001 From: sdweber422 Date: Mon, 19 Dec 2016 18:09:58 -0800 Subject: [PATCH 11/17] Changes to models and migrations to take extra column isDeleted --- migrations/20161018174824-create-item.js | 7 ++++++- models/item.js | 3 ++- routes/items.js | 1 - 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/migrations/20161018174824-create-item.js b/migrations/20161018174824-create-item.js index f08d6da..c420d43 100644 --- a/migrations/20161018174824-create-item.js +++ b/migrations/20161018174824-create-item.js @@ -35,10 +35,15 @@ module.exports = { updatedAt: { allowNull: false, type: Sequelize.DATE + }, + is_deleted: { + type: Sequelize.BOOLEAN, + allowNull: false, + defaultValue: false } }); }, down: function(queryInterface, Sequelize) { return queryInterface.dropTable('Items'); } -}; \ No newline at end of file +}; diff --git a/models/item.js b/models/item.js index 38a86e1..e416749 100644 --- a/models/item.js +++ b/models/item.js @@ -10,7 +10,8 @@ module.exports = function(sequelize, DataTypes) { is_root: DataTypes.BOOLEAN, starred: DataTypes.BOOLEAN, parent_id: DataTypes.INTEGER, - user_id: DataTypes.INTEGER + user_id: DataTypes.INTEGER, + is_deleted: DataTypes.BOOLEAN }, { classMethods: { associate: function(models) { diff --git a/routes/items.js b/routes/items.js index e088037..1d08882 100644 --- a/routes/items.js +++ b/routes/items.js @@ -9,7 +9,6 @@ const buildStarredItemArray = require( './items/build_starred_item_array' ) router.get( '/', ( request, response ) => { const { Item } = request.app.get( 'models' ) - const { user, query } = request buildFilteredItemTree( Item, user, query ) From db22c5466a9fd011f1a637bcc0c555de1b93dd38 Mon Sep 17 00:00:00 2001 From: generalmeow Date: Tue, 20 Dec 2016 11:31:36 -0800 Subject: [PATCH 12/17] created migration for is_deleted --- migrations/20161220192127-add-is-deleted-to-items.js | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 migrations/20161220192127-add-is-deleted-to-items.js diff --git a/migrations/20161220192127-add-is-deleted-to-items.js b/migrations/20161220192127-add-is-deleted-to-items.js new file mode 100644 index 0000000..a21e088 --- /dev/null +++ b/migrations/20161220192127-add-is-deleted-to-items.js @@ -0,0 +1,11 @@ +'use strict'; + + +module.exports = { + up: function (queryInterface, Sequelize) { + queryInterface.addColumn( 'Items', 'is_deleted', { type: Sequelize.BOOLEAN, defaultValue: false, allowNull: false } ) + }, + down: function (queryInterface, Sequelize) { + queryInterface.removeColumn( 'Items', 'is_deleted') + } +}; From 2819b615f6419bc37c6d95a6b9fcc1fb574b8f34 Mon Sep 17 00:00:00 2001 From: sdweber422 Date: Tue, 20 Dec 2016 13:59:56 -0800 Subject: [PATCH 13/17] Changes model to reflect is_deleted column --- models/item.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/models/item.js b/models/item.js index e416749..38a86e1 100644 --- a/models/item.js +++ b/models/item.js @@ -10,8 +10,7 @@ module.exports = function(sequelize, DataTypes) { is_root: DataTypes.BOOLEAN, starred: DataTypes.BOOLEAN, parent_id: DataTypes.INTEGER, - user_id: DataTypes.INTEGER, - is_deleted: DataTypes.BOOLEAN + user_id: DataTypes.INTEGER }, { classMethods: { associate: function(models) { From 1da55bea579d271353b1e45d4d4b0ae8075bf2da Mon Sep 17 00:00:00 2001 From: generalmeow Date: Wed, 21 Dec 2016 16:40:30 -0800 Subject: [PATCH 14/17] WIP - added delete link to items/index.pug, click function to jQuery to mimic dropdown delete, and delete functionality to route to change is_delete flag to true --- models/item.js | 2 +- public/javascripts/items.js | 17 +++++++++++++++++ routes/items.js | 17 +++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/models/item.js b/models/item.js index 38a86e1..877d955 100644 --- a/models/item.js +++ b/models/item.js @@ -1,6 +1,6 @@ 'use strict'; -const VALID_PARAMETERS = [ 'completed', 'title', 'description', 'starred' ] +const VALID_PARAMETERS = [ 'completed', 'title', 'description', 'starred', 'is_deleted' ] module.exports = function(sequelize, DataTypes) { var Item = sequelize.define('Item', { diff --git a/public/javascripts/items.js b/public/javascripts/items.js index 2c69885..a73bfe9 100644 --- a/public/javascripts/items.js +++ b/public/javascripts/items.js @@ -18,6 +18,8 @@ const titleEdited = event => { const elementToHide = $( event.target ) const id = elementToHide.data( 'id' ) const elementToShow = $( selector( 'item__title', id, 'span' ) ) + console.log('element', elementToHide); + if( event.keyCode === RETURN_KEY ) { let updatedTitle = elementToHide[0].value @@ -128,6 +130,20 @@ const completedClicked = event => { ) } + const deleteItem = (event) => { + const elementToHide = $( event.target ) + const id = elementToHide.data( 'id' ) + const elementToShow = $( selector( 'item__title', id, 'span' ) ) + + let updatedTitle = elementToHide[0].value + fetch( `/items/delete/${id}`, params( { title: updatedTitle } ) ) + .then( result => result.json() ) + .then ( checkJsonForSuccessField ) + .then( json => { + // toggle( elementToShow, elementToHide ) + $( elementToShow[0] ).html(updatedTitle) + }) + } $(document).ready( () => { $( '.item__edit-title' ).keypress( titleEdited ) $( '.item__title > span' ).click( clickToUpdate( 'item__title' )) @@ -136,6 +152,7 @@ $(document).ready( () => { $( '.item__toggle' ).click( completedClicked ) $( '.dropdown__toggle' ).click( dropdownToggle ) $( '.star' ).click( starredToggle ) + $( '.deleted').click( deleteItem ) getFilterStatus() getCheckedStatus() }) diff --git a/routes/items.js b/routes/items.js index 1d08882..1d0a9ba 100644 --- a/routes/items.js +++ b/routes/items.js @@ -74,5 +74,22 @@ router.post( '/:id', ( request, response ) => { ) }) +router.post( '/delete/:id', ( request, response ) => { + const { Item, Audit } = request.app.get( 'models' ) + const { id } = request.params + const user_id = parseInt( request.user.id ) + + Item.findOne({ where: { id, user_id } }) + .then( item => { + const softDeletedItem = item + softDeletedItem.update( Item.filterParameters( { is_deleted: true } ) ) + return softDeletedItem + }) + .then( createAuditEntries( Item, Audit, user_id ) ) + .then( result => response.json({ success: true, id })) + .catch( error => + response.json({ success: false, id, message: error.message }) + ) +}) module.exports = router From 5166c0b734e814f594c2784bcfca8e5a523fa3ed Mon Sep 17 00:00:00 2001 From: sdweber422 Date: Thu, 22 Dec 2016 09:56:45 -0800 Subject: [PATCH 15/17] Added is_deleted to FETCH_ATTRTIBUTES, altered ItemNode constructor to include is_deleted, added deleteItem to jQuery to soft delete from database and hide associated elements on page --- public/javascripts/items.js | 16 ++++++++-------- routes/items/item_response.js | 5 +++-- src/item_node.js | 4 ++-- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/public/javascripts/items.js b/public/javascripts/items.js index a73bfe9..68e5616 100644 --- a/public/javascripts/items.js +++ b/public/javascripts/items.js @@ -130,18 +130,18 @@ const completedClicked = event => { ) } - const deleteItem = (event) => { - const elementToHide = $( event.target ) - const id = elementToHide.data( 'id' ) - const elementToShow = $( selector( 'item__title', id, 'span' ) ) + const deleteItem = ( event ) => { + const elementToDelete = $( event.target ).parent().children() + const id = elementToDelete.data( 'id' ) + const elementToHide = $( `.item__toggle[data-id='${id}']` ) - let updatedTitle = elementToHide[0].value - fetch( `/items/delete/${id}`, params( { title: updatedTitle } ) ) + let deletedTitle = elementToDelete[0].value + fetch( `/items/delete/${id}`, params( { title: deletedTitle } ) ) .then( result => result.json() ) .then ( checkJsonForSuccessField ) .then( json => { - // toggle( elementToShow, elementToHide ) - $( elementToShow[0] ).html(updatedTitle) + $( elementToDelete[0] ).addClass( 'item--hidden' ) + $( elementToHide[0] ).addClass( 'item--hidden' ) }) } $(document).ready( () => { diff --git a/routes/items/item_response.js b/routes/items/item_response.js index 5655061..9e58d59 100644 --- a/routes/items/item_response.js +++ b/routes/items/item_response.js @@ -1,6 +1,6 @@ const { pruneTree } = require( './tree_creation' ) -const FETCH_ATTRIBUTES = [ 'title', 'description', 'completed', 'starred', 'is_root', 'parent_id', 'id' ] +const FETCH_ATTRIBUTES = [ 'title', 'description', 'completed', 'starred', 'is_root', 'parent_id', 'id', 'is_deleted' ] const createRootItem = Item => user => { return Item.create({ @@ -8,7 +8,8 @@ const createRootItem = Item => user => { parent_id: 0, title: 'Home', description: 'Welcome to Floworky', - user_id: user.id + user_id: user.id, + is_deleted: false }).then( result => user ) } diff --git a/src/item_node.js b/src/item_node.js index 94fc1c9..f733786 100644 --- a/src/item_node.js +++ b/src/item_node.js @@ -1,6 +1,6 @@ class ItemNode { constructor( item ) { - const { id, parent_id, title, description, completed, starred, is_root } = item + const { id, parent_id, title, description, completed, starred, is_root, is_deleted } = item this.id = id this.parent_id = parent_id @@ -9,7 +9,7 @@ class ItemNode { this.completed = completed this.starred = starred this.is_root = is_root - + this.is_deleted = is_deleted this.children = [] } From 5d27717097bd1dcf1087c09f8a3219c91934811c Mon Sep 17 00:00:00 2001 From: sdweber422 Date: Thu, 22 Dec 2016 12:17:56 -0800 Subject: [PATCH 16/17] Reverted models/item.js to original and migrations/...create-item.js to original, changed declarations model to refelct is_deleted, and added functionality to not show soft deleted items --- declarations/database/models.js | 1 + migrations/20161018174824-create-item.js | 7 +------ models/item.js | 2 +- routes/items/item_response.js | 5 +++-- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/declarations/database/models.js b/declarations/database/models.js index b57b198..3f6e1d5 100644 --- a/declarations/database/models.js +++ b/declarations/database/models.js @@ -6,6 +6,7 @@ declare type Item = { starred: boolean; parent_id: number; user_id: number; + is_deleted: boolean; }; declare type User = { diff --git a/migrations/20161018174824-create-item.js b/migrations/20161018174824-create-item.js index c420d43..f08d6da 100644 --- a/migrations/20161018174824-create-item.js +++ b/migrations/20161018174824-create-item.js @@ -35,15 +35,10 @@ module.exports = { updatedAt: { allowNull: false, type: Sequelize.DATE - }, - is_deleted: { - type: Sequelize.BOOLEAN, - allowNull: false, - defaultValue: false } }); }, down: function(queryInterface, Sequelize) { return queryInterface.dropTable('Items'); } -}; +}; \ No newline at end of file diff --git a/models/item.js b/models/item.js index 877d955..38a86e1 100644 --- a/models/item.js +++ b/models/item.js @@ -1,6 +1,6 @@ 'use strict'; -const VALID_PARAMETERS = [ 'completed', 'title', 'description', 'starred', 'is_deleted' ] +const VALID_PARAMETERS = [ 'completed', 'title', 'description', 'starred' ] module.exports = function(sequelize, DataTypes) { var Item = sequelize.define('Item', { diff --git a/routes/items/item_response.js b/routes/items/item_response.js index 9e58d59..62df320 100644 --- a/routes/items/item_response.js +++ b/routes/items/item_response.js @@ -12,9 +12,10 @@ const createRootItem = Item => user => { is_deleted: false }).then( result => user ) } +const filterStatus = user_id => ( { user_id, is_deleted: false } ) const allItemsQuery = user_id => ( - { order: [['createdAt', 'ASC']], where: { user_id }, FETCH_ATTRIBUTES } + { order: [['createdAt', 'ASC']], where: filterStatus( user_id ), FETCH_ATTRIBUTES } ) const filterClause = ( query, user_id ) => @@ -24,7 +25,7 @@ const filterClause = ( query, user_id ) => whereSearch( query ), whereCompleted( query ), whereStarred( query ), - { user_id } + filterStatus( user_id ) ) }) From 61a72d1421a1bb69af3beeeaba6857a0541ebb4c Mon Sep 17 00:00:00 2001 From: sdweber422 Date: Thu, 22 Dec 2016 16:25:20 -0800 Subject: [PATCH 17/17] fixed functions --- models/item.js | 3 ++- public/javascripts/items.js | 26 ++++++++++---------------- routes/items.js | 15 ++++++--------- views/items/item-menu.pug | 4 ++-- views/items/mixins.pug | 2 +- 5 files changed, 21 insertions(+), 29 deletions(-) diff --git a/models/item.js b/models/item.js index 38a86e1..e416749 100644 --- a/models/item.js +++ b/models/item.js @@ -10,7 +10,8 @@ module.exports = function(sequelize, DataTypes) { is_root: DataTypes.BOOLEAN, starred: DataTypes.BOOLEAN, parent_id: DataTypes.INTEGER, - user_id: DataTypes.INTEGER + user_id: DataTypes.INTEGER, + is_deleted: DataTypes.BOOLEAN }, { classMethods: { associate: function(models) { diff --git a/public/javascripts/items.js b/public/javascripts/items.js index 68e5616..e22dfb9 100644 --- a/public/javascripts/items.js +++ b/public/javascripts/items.js @@ -52,7 +52,7 @@ const descriptionEdited = event => { const completedClicked = event => { const element = $( event.target ) - const id = element.data( 'id' ) + const id = element.parent().data( 'id' ) const completed = ! element.data( 'completed' ) fetch( `/items/${id}`, params({ completed: completed } ) ) @@ -130,29 +130,23 @@ const completedClicked = event => { ) } - const deleteItem = ( event ) => { - const elementToDelete = $( event.target ).parent().children() - const id = elementToDelete.data( 'id' ) - const elementToHide = $( `.item__toggle[data-id='${id}']` ) - - let deletedTitle = elementToDelete[0].value - fetch( `/items/delete/${id}`, params( { title: deletedTitle } ) ) - .then( result => result.json() ) - .then ( checkJsonForSuccessField ) - .then( json => { - $( elementToDelete[0] ).addClass( 'item--hidden' ) - $( elementToHide[0] ).addClass( 'item--hidden' ) - }) + const deleteItem = event => { + const elementToHide = $( event.target ).closest( '.item' ) + const id = elementToHide.data( 'id' ) + const elementToDelete = $( `.title__data[data-id='${id}']` ) + + fetch( `/items/${id}`, fetchParams( 'DELETE' )) + .then( result => $( elementToHide[0] ).addClass( 'item--hidden' )) } $(document).ready( () => { $( '.item__edit-title' ).keypress( titleEdited ) $( '.item__title > span' ).click( clickToUpdate( 'item__title' )) $( '.item__edit-description' ).keypress( descriptionEdited ) $( '.item__description > span' ).click( clickToUpdate( 'item__description' )) - $( '.item__toggle' ).click( completedClicked ) + $( '.item__menu > ul > li:first-child' ).click( completedClicked ) $( '.dropdown__toggle' ).click( dropdownToggle ) $( '.star' ).click( starredToggle ) - $( '.deleted').click( deleteItem ) + $( '.delete' ).click( deleteItem ) getFilterStatus() getCheckedStatus() }) diff --git a/routes/items.js b/routes/items.js index 1d0a9ba..e7714e0 100644 --- a/routes/items.js +++ b/routes/items.js @@ -74,22 +74,19 @@ router.post( '/:id', ( request, response ) => { ) }) -router.post( '/delete/:id', ( request, response ) => { +router.delete( '/:id', ( request, response ) => { const { Item, Audit } = request.app.get( 'models' ) - const { id } = request.params + const id = parseInt( request.params.id ) const user_id = parseInt( request.user.id ) Item.findOne({ where: { id, user_id } }) .then( item => { - const softDeletedItem = item - softDeletedItem.update( Item.filterParameters( { is_deleted: true } ) ) - return softDeletedItem + item.update({ is_deleted: true }) + + return item }) .then( createAuditEntries( Item, Audit, user_id ) ) - .then( result => response.json({ success: true, id })) - .catch( error => - response.json({ success: false, id, message: error.message }) - ) + .then( result => response.status( 200 ).send({}) ) }) module.exports = router diff --git a/views/items/item-menu.pug b/views/items/item-menu.pug index dd54579..adddf6a 100644 --- a/views/items/item-menu.pug +++ b/views/items/item-menu.pug @@ -1,7 +1,7 @@ -ul +ul(data-id=entry.id) li Complete li Add Note li Share li Export li Duplicate - li Delete + li.delete Delete diff --git a/views/items/mixins.pug b/views/items/mixins.pug index ab9f5f2..5ed856d 100644 --- a/views/items/mixins.pug +++ b/views/items/mixins.pug @@ -17,7 +17,7 @@ mixin display( nodes, depth ) .item__menu include item-menu.pug .item__title(data-id=entry.id, data-completed=entry.completed, class={completed: entry.completed}) - span(data-id=entry.id)=entry.title + span.title__data(data-id=entry.id)=entry.title input.item__edit-title.item--hidden(data-id=entry.id, type='text', name='edit-title', value=entry.title) .item__description(data-id=entry.id) span(data-id=entry.id)=entry.description