diff --git a/Makefile b/Makefile index a611bcd..6a475e7 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ main.js: main.bundle.js $(Q)npx babel --minified main.bundle.js -o main.js -main.bundle.js: client/index.ls client/authd.ls client/bulma.ls client/card.ls client/modal.ls client/project-creation-modal.ls client/project.ls client/task-creation-modal.ls client/task.ls client/task-removal-modal.ls client/todowebsocket.ls client/validation-modal.ls +main.bundle.js: client/index.ls client/authd.ls client/authws.ls client/bulma.ls client/card.ls client/modal.ls client/navbar.ls client/project-creation-modal.ls client/project.ls client/task-creation-modal.ls client/task.ls client/task-removal-modal.ls client/todowebsocket.ls client/validation-modal.ls @echo ' BUN > main.bundle.js' $(Q)npx browserify -t browserify-livescript client/index.ls -o main.bundle.js @@ -97,9 +97,11 @@ $(PACKAGE)-$(VERSION).tar.gz: distdir $(PACKAGE)-$(VERSION)/client/index.ls \ $(PACKAGE)-$(VERSION)/client/style.sass \ $(PACKAGE)-$(VERSION)/client/authd.ls \ + $(PACKAGE)-$(VERSION)/client/authws.ls \ $(PACKAGE)-$(VERSION)/client/bulma.ls \ $(PACKAGE)-$(VERSION)/client/card.ls \ $(PACKAGE)-$(VERSION)/client/modal.ls \ + $(PACKAGE)-$(VERSION)/client/navbar.ls \ $(PACKAGE)-$(VERSION)/client/project-creation-modal.ls \ $(PACKAGE)-$(VERSION)/client/project.ls \ $(PACKAGE)-$(VERSION)/client/task-creation-modal.ls \ @@ -115,9 +117,11 @@ $(PACKAGE)-$(VERSION).tar.xz: distdir $(PACKAGE)-$(VERSION)/client/index.ls \ $(PACKAGE)-$(VERSION)/client/style.sass \ $(PACKAGE)-$(VERSION)/client/authd.ls \ + $(PACKAGE)-$(VERSION)/client/authws.ls \ $(PACKAGE)-$(VERSION)/client/bulma.ls \ $(PACKAGE)-$(VERSION)/client/card.ls \ $(PACKAGE)-$(VERSION)/client/modal.ls \ + $(PACKAGE)-$(VERSION)/client/navbar.ls \ $(PACKAGE)-$(VERSION)/client/project-creation-modal.ls \ $(PACKAGE)-$(VERSION)/client/project.ls \ $(PACKAGE)-$(VERSION)/client/task-creation-modal.ls \ @@ -133,9 +137,11 @@ $(PACKAGE)-$(VERSION).tar.bz2: distdir $(PACKAGE)-$(VERSION)/client/index.ls \ $(PACKAGE)-$(VERSION)/client/style.sass \ $(PACKAGE)-$(VERSION)/client/authd.ls \ + $(PACKAGE)-$(VERSION)/client/authws.ls \ $(PACKAGE)-$(VERSION)/client/bulma.ls \ $(PACKAGE)-$(VERSION)/client/card.ls \ $(PACKAGE)-$(VERSION)/client/modal.ls \ + $(PACKAGE)-$(VERSION)/client/navbar.ls \ $(PACKAGE)-$(VERSION)/client/project-creation-modal.ls \ $(PACKAGE)-$(VERSION)/client/project.ls \ $(PACKAGE)-$(VERSION)/client/task-creation-modal.ls \ diff --git a/client/authd.ls b/client/authd.ls index b4afbc3..4c64211 100644 --- a/client/authd.ls +++ b/client/authd.ls @@ -48,8 +48,6 @@ module.exports = { # self.user-on-message = [] self.add-event-listener = (type, callback) -> - # console.log "authd: add event listener", type, callback, response-types[type] - console.log response-types type = response-types[type] self.callbacks[type] ++= [callback] @@ -69,7 +67,6 @@ module.exports = { self.socket.onmessage = (event) -> message = JSON.parse(event.data) - console.log "authd message received: ", message for f in self.callbacks[message.mtype] f JSON.parse(message.payload) @@ -81,7 +78,6 @@ module.exports = { self.open-socket! self.send = (type, opts) -> - console.log JSON.stringify { mtype: type, payload: opts } self.socket.send JSON.stringify { mtype: type, payload: opts } self.get-token = (login, password) -> diff --git a/client/index.ls b/client/index.ls index bbfcaea..5c5bcc0 100644 --- a/client/index.ls +++ b/client/index.ls @@ -8,6 +8,7 @@ bulma = require "./bulma.ls" Task = require "./task.ls" Project = require "./project.ls" Modal = require "./modal.ls" +Navbar = require "./navbar.ls" # ValidationModal = require "./validation-modal.ls" UUID = require "uuid/v4" @@ -232,43 +233,9 @@ model.todod-ws.add-event-listener \task-removed, (message) -> project.tasks := project.tasks.filter((.id != task)) projector.schedule-render! - - -render-navbar = -> - - h \div.navbar [ - h \div.navbar-start [ - h \a.navbar-item.is-size-2 { - onclick: -> - if model.viewed-project - model.todod-ws.unsubscribe model.viewed-project.id - model.todod-ws.list-lists! - model.viewed-project := void - model.current-view := "project-list" - } [ "⌂" ] - ] - - if model.current-view == "project" && model.viewed-project - model.viewed-project.inner-nav-render! - - h \div.navbar-end [ - - if model.current-view == "project" && model.viewed-project - model.viewed-project.right-nav-render! - - h \a.navbar-item { - key: "logout" - onclick: -> - model.current-view := "login" - # TODO: remove anything related to the old session on the client - model.todod-ws.reopen! - } [ "Logout" ] - ] - ] - render-project-list = -> - h \div.section model.project-list.map (project) -> - h \div.box { + h \div.cards-list model.project-list.map (project) -> + h \div.card.is-grey { key: project.id onclick: -> model.current-view := "project" @@ -277,7 +244,9 @@ render-project-list = -> model.todod-ws.get-list project.id model.todod-ws.get-tasks project.id } [ - bulma.title 4 project.title + h \div.card-content [ + bulma.title 3 project.title + ] ] Column = (title) -> @@ -286,22 +255,6 @@ Column = (title) -> id: UUID! # TODO FIXME XXX } -render-new-project-button = -> - h \div.button.is-primary.is-large.is-fullwidth { - onclick: -> - model.todod-ws.add-list "New project", { - extra_properties: { - columns: [ - Column "Unassigned" - Column "Work in progress" - Column "To be checked" - Column "Being checked" - Column "Done" - ] - } - } - } [ "New project!" ] - render-project = (project) -> if project project.render! @@ -321,44 +274,38 @@ model.login-form = LoginForm { projector.schedule-render! } +navbar = Navbar! + render-body = -> - h \div.section [ - switch model.current-view - when "login" - # authd.login-page model - model.login-form.render! + h \div#body [ + navbar.render model - when "project-list" - h \div#project-list [ - render-navbar! + h \div#main-section [ + switch model.current-view + when "login" + h \div.columns [ + h \div.column + h \div.column [ + model.login-form.render! + ] + h \div.column + ] + when "project-list" render-project-list! - - render-new-project-button! - ] - - when "project" - h \div [ - render-navbar! - + when "project" render-project model.viewed-project - ] - else - h \div.notification.is-error [ - "Wait, what? Internal error!" - ] + else + h \div.notification.is-error [ + "Wait, what? Internal error!" + ] + ] if model.modal model.modal.render! ] - -renderer = -> - render-navbar! - - render-body! - document.add-event-listener 'DOMContentLoaded' -> - projector.append document.body, renderer + projector.append document.body, render-body diff --git a/client/navbar.ls b/client/navbar.ls new file mode 100644 index 0000000..8fa412b --- /dev/null +++ b/client/navbar.ls @@ -0,0 +1,68 @@ +{h} = require "maquette" + +{button, field, control} = require "./bulma.ls" + +render-new-project-button = (model) -> + h \div.button.is-success.is-medium.is-outlined { + onclick: -> + model.todod-ws.add-list "New project", { + extra_properties: { + columns: [ + Column "Unassigned" + Column "Work in progress" + Column "To be checked" + Column "Being checked" + Column "Done" + ] + } + } + } [ "New project!" ] + + +Navbar = -> + self = {} + + self.render = (model) -> + h \div.navbar [ + h \div.navbar-start [ + h \div.navbar-brand [ + h \a.navbar-item { + onclick: -> + if model.viewed-project + model.todod-ws.unsubscribe model.viewed-project.id + model.todod-ws.list-lists! + model.viewed-project := void + model.current-view := "project-list" + } [ + "todod - kanbanc" + ] + ] + ] + + if model.current-view == "project" && model.viewed-project + model.viewed-project.inner-nav-render! + + if model.current-view != "login" + h \div.navbar-end [ + if model.current-view == "project-list" + h \div.navbar-item {key: "new project"} [ + render-new-project-button model + ] + else if model.current-view == "project" && model.viewed-project + model.viewed-project.right-nav-render! + + h \div.navbar-item {key: "logout"} [ + h \a.button.is-medium.is-outlined { + onclick: -> + model.current-view := "login" + # TODO: remove anything related to the old session on the client + model.todod-ws.reopen! + } [ "Logout" ] + ] + ] + ] + + self + +module.exports = Navbar + diff --git a/client/project-creation-modal.ls b/client/project-creation-modal.ls index 9192751..0669e36 100644 --- a/client/project-creation-modal.ls +++ b/client/project-creation-modal.ls @@ -191,15 +191,17 @@ ProjectCreationModal = (project, todod-ws, users) -> } ] - button \.is-success.is-outlined { - onclick: -> - new-col = { - id: UUID! - title: self.tmp.new-column-input.title - } - self.extra_properties.columns ++= [ new-col ] - self.tmp.new-column-input.title := "New column !" - } [ "+" ] + control [ + button \.is-success.is-outlined { + onclick: -> + new-col = { + id: UUID! + title: self.tmp.new-column-input.title + } + self.extra_properties.columns ++= [ new-col ] + self.tmp.new-column-input.title := "New column !" + } [ "+" ] + ] ] ] diff --git a/client/project.ls b/client/project.ls index 2f1cc7f..91ea075 100644 --- a/client/project.ls +++ b/client/project.ls @@ -25,6 +25,8 @@ Project = (self, todod-ws, users) -> self.tasks = [] self.users = users || [] + self.selected-task-id = void + modal = void self.render-column = (column, first) -> @@ -38,14 +40,20 @@ Project = (self, todod-ws, users) -> # COLUMNS # - h \div.column { + h \div.column.cards-list { key: column.id } [ - h \div.box { key: self.id } [ - h \p.title.is-4 [ column.title ] - for task in tasks-to-display - task.render! - ] + h \p.title.is-4 [ column.title ] + + tasks-to-display.map (task) -> + task.render { + is-selected: (task.id == self.selected-task-id) + onclick: -> + if self.selected-task-id == task.id + self.selected-task-id := void + else + self.selected-task-id := task.id + } ] self.inner-nav-render = -> @@ -56,7 +64,7 @@ Project = (self, todod-ws, users) -> h \div.navbar-item { key: "navbar-new-task" } [ - h \div.button.is-success.is-outlined { + h \div.button.is-success.is-outlined.is-medium { onclick: -> modal := TaskCreationModal self, self.todod-ws, void, self.users } [ "New task" ] @@ -65,7 +73,7 @@ Project = (self, todod-ws, users) -> h \div.navbar-item { key: "navbar-edit-project" } [ - h \div.button.is-outlined { + h \div.button.is-dark.is-outlined.is-medium { onclick: -> modal := ProjectCreationModal self, self.todod-ws, self.users } [ "Edit this project" ] @@ -74,7 +82,7 @@ Project = (self, todod-ws, users) -> h \div.navbar-item { key: "navbar-delete-project" } [ - h \div.button.is-danger.is-outlined { + h \div.button.is-danger.is-outlined.is-medium { onclick: -> modal := Modal { +visible @@ -93,8 +101,16 @@ Project = (self, todod-ws, users) -> h \div.columns [ if columns = self.extra_properties.columns - for dom in columns.map((column, index) -> self.render-column(column, index == 0)) - dom + for column in columns + is-last = column == columns[columns.length-1] + + if is-last + self.render-column column, is-last + else + [ + self.render-column column, is-last + h \div.is-divider-vertical + ] ] if modal diff --git a/client/style.sass b/client/style.sass index a61683f..53bc983 100644 --- a/client/style.sass +++ b/client/style.sass @@ -7,14 +7,54 @@ // @import "../node_modules/bulmaswatch/superhero/_overrides.scss" -.project>.columns +#body + position: absolute + top: 0 + left: 0 + bottom: 0 + right: 0 + display: flex + flex-flow: column nowrap + +.project, .project .columns + height: 100% + +.project .columns overflow-x: auto .avatar border-radius: 4px -.card.is-selected +#main-section + margin: 0.75rem 1.5rem + flex: 1 1 auto + +.project .columns .column + padding: 0.25rem + +.cards-list .card + border-top: 4px solid $grey-lighter +.cards-list .card .card-content + padding: 0.75rem +.cards-list .card .card-footer + margin: -0.75rem +.cards-list .card.is-selected border-width: 4px +.cards-list .card .title.is-5 + margin-bottom: 0.25rem + +.cards-list > .title + margin: 0.5rem + margin-bottom: 1rem + +.card .title.is-5, .card .title.is-6 + font-weight: normal + +.cards-list .card + margin-bottom: 1rem + +.cards-list .card:last-child + margin-bottom: 0 .is-column-header + .card, .card + .button margin-top: 12px @@ -25,10 +65,22 @@ .project margin-top: 12px +.navbar + border-bottom: 2px solid $grey-lighter + background-color: $white + @each $name, $pair in $colors $color: nth($pair, 1) $color-invert: nth($pair, 2) .card.is-#{$name} - background-color: darken($color, 30) + border-top: 4px solid $color + +@import "../node_modules/bulma-divider/src/sass/index.sass" + +.is-divider-vertical + padding: 0 0.5rem +.is-divider-vertical::before + top: 0 + bottom: 0 diff --git a/client/task-creation-modal.ls b/client/task-creation-modal.ls index 8ff55e9..44b7ec8 100644 --- a/client/task-creation-modal.ls +++ b/client/task-creation-modal.ls @@ -101,7 +101,7 @@ TaskCreationModal = (project, todod-ws, task, users) -> field [ - label "Initial column" + label "Column" control [ select \.is-fullwidth { diff --git a/client/task.ls b/client/task.ls index 53df5b7..1c698a9 100644 --- a/client/task.ls +++ b/client/task.ls @@ -14,41 +14,54 @@ display-login = (task, users) -> Task = (self, project, todod-ws) -> modal = void - self.render = -> + self.render = (args) -> + args or= {} + background-color = "grey" if self.extra_properties && self.extra_properties.background-color background-color = self.extra_properties.background-color - h "div.card.has-background-#{background-color}" { + h "div.card.is-#{background-color}" { + classes: { + "is-selected": args.is-selected + } key: self.id + onclick: (e) -> + if args.onclick + args.onclick(e) } [ h \div.card-content [ + h \div.title.is-5 [ self.title ] h \div.media [ h \div.media-left [ # FIXME: assignee card image - "LEFT" + ] + h \div.media-content [ display-login self, project.users ] - - h \div.media-content [ self.title ] - - h \div.button { - onclick: -> - modal := TaskCreationModal project, todod-ws, self, project.users - } [ "Edit" ] - - h \div.button.is-danger { - onclick: -> - modal := TaskRemovalModal project.id, todod-ws, self - } [ "X" ] ] - h \div.content { - key: "task-description-#{self.id}" - after-create: (dom) -> - dom.innerHTML = nmd self.description - } [ ] + if args.is-selected + h \div.content { + key: "task-description-#{self.id}" + after-create: (dom) -> + dom.innerHTML = nmd self.description + } [ ] + + if args.is-selected + h \div.card-footer [ + h \a.card-footer-item { + onclick: -> + modal := TaskCreationModal project, todod-ws, self, project.users + } [ "Edit" ] + + h \a.card-footer-item.has-text-danger { + onclick: -> + modal := TaskRemovalModal project.id, todod-ws, self + } [ "Destroy" ] + ] ] + if modal modal.render! ] diff --git a/client/todowebsocket.ls b/client/todowebsocket.ls index c4354a4..a3ba7e1 100644 --- a/client/todowebsocket.ls +++ b/client/todowebsocket.ls @@ -69,7 +69,6 @@ module.exports = { self.socket.onmessage = (event) -> message = JSON.parse(event.data) parsed-message = JSON.parse(message.payload) - console.log "todod message #{message.mtype} received: ", parsed-message for f in self.callbacks[message.mtype] f parsed-message @@ -81,8 +80,6 @@ module.exports = { self.open-socket! self.send = (type, opts) -> - console.log "sending message #{type} to todod: ", opts - # console.log JSON.stringify { mtype: type, payload: opts } self.socket.send JSON.stringify { mtype: type, payload: opts }