UX redesign, some grooming.

dev
Luka Vandervelden 3 years ago
parent a928449c90
commit f096fcec4f
  1. 11
      Makefile
  2. 177
      client/authd.ls
  3. 136
      client/authws.ls
  4. 44
      client/column-edit-modal.ls
  5. 13
      client/font-awesome.ls
  6. 42
      client/index.ls
  7. 2
      client/modal.ls
  8. 55
      client/navbar.ls
  9. 289
      client/project-creation-modal.ls
  10. 81
      client/project.ls
  11. 17
      client/task.ls
  12. 3
      client/todowebsocket.ls
  13. 4
      index.html

@ -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/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
main.bundle.js: client/index.ls client/authws.ls client/bulma.ls client/card.ls client/column-edit-modal.ls client/font-awesome.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
@ -96,10 +96,11 @@ $(PACKAGE)-$(VERSION).tar.gz: distdir
$(Q)tar czf $(PACKAGE)-$(VERSION).tar.gz \
$(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/column-edit-modal.ls \
$(PACKAGE)-$(VERSION)/client/font-awesome.ls \
$(PACKAGE)-$(VERSION)/client/modal.ls \
$(PACKAGE)-$(VERSION)/client/navbar.ls \
$(PACKAGE)-$(VERSION)/client/project-creation-modal.ls \
@ -116,10 +117,11 @@ $(PACKAGE)-$(VERSION).tar.xz: distdir
$(Q)tar cJf $(PACKAGE)-$(VERSION).tar.xz \
$(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/column-edit-modal.ls \
$(PACKAGE)-$(VERSION)/client/font-awesome.ls \
$(PACKAGE)-$(VERSION)/client/modal.ls \
$(PACKAGE)-$(VERSION)/client/navbar.ls \
$(PACKAGE)-$(VERSION)/client/project-creation-modal.ls \
@ -136,10 +138,11 @@ $(PACKAGE)-$(VERSION).tar.bz2: distdir
$(Q)tar cjf $(PACKAGE)-$(VERSION).tar.bz2 \
$(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/column-edit-modal.ls \
$(PACKAGE)-$(VERSION)/client/font-awesome.ls \
$(PACKAGE)-$(VERSION)/client/modal.ls \
$(PACKAGE)-$(VERSION)/client/navbar.ls \
$(PACKAGE)-$(VERSION)/client/project-creation-modal.ls \

@ -1,177 +0,0 @@
bulma = require "./bulma.ls"
h = require 'maquette' .h
module.exports = {
create-socket: (socket-url) ->
self = {}
self.token = ""
request-types = {
"get-token": 0
"add-user": 1
"get-user": 2
"get-user-by-credentials": 3
"mod-user": 4
# TODO: code these messages
"register": 5
"get-extra": 6
"set-extra": 7
"update-password": 8
"list-users": 9
"set-permissions": 10
}
response-types = {
"error": 0
"token": 1
"user": 2
"user-added": 3
"user-edited": 4
"extra": 5
"extra-updated": 6
"users-list": 7
}
# TODO: naming convention
# users can record functions to run on events
self.user-on-socket-error = []
self.user-on-socket-close = []
self.callbacks = {}
for key, value of response-types
self.callbacks[value] = []
# self.user-on-message = []
self.add-event-listener = (type, callback) ->
type = response-types[type]
self.callbacks[type] ++= [callback]
self.open-socket = ->
console.log "Opening socket to #{socket-url}"
self.socket := new WebSocket socket-url
self.socket.onerror = (event) ->
for f in self.user-on-socket-error
f event
self.socket.close!
self.socket.onclose = (event) ->
for f in self.user-on-socket-close
f event
self.socket.onmessage = (event) ->
message = JSON.parse(event.data)
for f in self.callbacks[message.mtype]
f JSON.parse(message.payload)
self.reopen = ->
self.socket.close!
self.open-socket!
self.open-socket!
self.send = (type, opts) ->
self.socket.send JSON.stringify { mtype: type, payload: opts }
self.get-token = (login, password) ->
self.send request-types[\get-token], JSON.stringify {
login: login
password: password
}
self.get-user-by-credentials = (login, password) ->
self.send request-types[\get-user-by-credentials], JSON.stringify {
login: login
password: password
}
self.login = (login, password) ->
self.get-token login, password
self.get-user-by-credentials login, password
self.get-user = (uid) ->
self.send request-types[\get-user], JSON.stringify {
uid: uid
}
self.list-users = ->
self.send request-types[\list-users], JSON.stringify {
token: self.token
# FIXME: this will be removed once the authd program will accept
# any list-users requests from any user
# which is the only logical choice since any user can create projects and assign people to it
key: "nico-nico-nii"
}
self.set-permissions = (list-id, user-id, permission) ->
self.send request-types[\set-permissions], JSON.stringify {
token: self.token
list: list-id
uid: user-id
permission: permission
}
# TODO: authd overhaul
#self.add-user = (login, password) ->
# self.send request-types[\add-user], JSON.stringify {
# login: login
# password: password
# }
# TODO: authd overhaul
#self.mod-user = (uid) ->
# self.send request-types[\mod-user], JSON.stringify {
# uid: uid
# }
self
login-page: (model) ->
# XXX: container = marge
# XXX: box = dessiner un contour
h \div.container [
h \div.box [
if model.login-error
h \div.notification.is-danger [
model.login-error
]
h \form [
bulma.field [
bulma.label "Login"
bulma.input {
oninput: (e) ->
model.login = e.target.value
name: \login
id: \login-input
}
]
bulma.field [
bulma.label "Password"
bulma.input {
oninput: (e) ->
model.password = e.target.value
name: \password
type: \password
id: \password-input
}
]
h \button.button.is-fullwidth.is-primary {
onclick: (e) ->
# not to refresh the page since it's a form button
e.prevent-default!
model.authd-ws.get-token model.login, model.password
} [ "Connexion" ]
]
]
]
}

@ -0,0 +1,136 @@
bulma = require "./bulma.ls"
h = require 'maquette' .h
AuthWS = (socket-url) ->
self = {}
request-types = {
"get-token": 0
"add-user": 1
"get-user": 2
"get-user-by-credentials": 3
"mod-user": 4
"register": 5
"update-password": 6
"list-users": 7
"check-permission": 8
"set-permission": 9
}
response-types = {
"error": 0
"token": 1
"user": 2
"user-added": 3
"user-edited": 4
"users-list": 5
"permission-check": 6
"permission-set": 7
}
# TODO: naming convention
# users can record functions to run on events
self.user-on-socket-error = []
self.user-on-socket-close = []
self.callbacks = {}
for key, value of response-types
self.callbacks[value] = []
self.add-event-listener = (type, callback) ->
type = response-types[type]
self.callbacks[type] ++= [callback]
self.open-socket = ->
self.socket := new WebSocket socket-url
self.socket.onerror = (event) ->
for f in self.user-on-socket-error
f event
self.socket.close!
self.socket.onclose = (event) ->
for f in self.user-on-socket-close
f event
self.socket.onmessage = (event) ->
message = JSON.parse(event.data)
for f in self.callbacks[message.mtype]
f JSON.parse(message.payload)
self.reopen = ->
self.socket.close!
self.open-socket!
self.open-socket!
self.send = (type, opts) ->
self.socket.send JSON.stringify { mtype: type, payload: opts }
self.get-token = (login, password) ->
self.send request-types[\get-token], JSON.stringify {
login: login
password: password
}
self.get-user-by-credentials = (login, password) ->
self.send request-types[\get-user-by-credentials], JSON.stringify {
login: login
password: password
}
self.login = (login, password) ->
self.get-token login, password
self.get-user-by-credentials login, password
self.get-user = (uid) ->
self.send request-types[\get-user], JSON.stringify {
uid: uid
}
self.register = (login, password) ->
self.send request-types[\register], JSON.stringify {
login: login
password: password
}
self.get-extra = (token, name) ->
console.warn "get-extra: deprecated"
self.set-extra = (token, name, extra) ->
console.warn "set-extra: deprecated"
self.update-password = (login, old-password, new-password) ->
self.send request-types[\update-password], JSON.stringify {
login: login
old_password: old-password
new_password: new-password
}
self.list-users = (token) ->
console.warn "list-users: unstable API"
self.send request-types[\list-users], JSON.stringify {
token: token
}
# TODO: authd overhaul required
#self.add-user = (login, password) ->
# self.send request-types[\add-user], JSON.stringify {
# login: login
# password: password
# }
# TODO: authd overhaul required
#self.mod-user = (uid) ->
# self.send request-types[\mod-user], JSON.stringify {
# uid: uid
# }
self
module.exports = AuthWS

@ -0,0 +1,44 @@
{h} = require "maquette"
{field, control, input, label} = require "./bulma.ls"
Modal = require "./modal.ls"
deep-copy = (object) ->
JSON.parse JSON.stringify object
ColumnEditModal = (project, column, args) ->
self = {
column: deep-copy column
on-validation: args.on-validation || (column) ->
}
modal = Modal {
+visible
content: [
field [
label "Column title"
control [
input {
value: self.column.title
oninput: (e) ->
self.column.title = e.target.value
}
]
]
]
on-validation: ->
self.on-validation self.column
}
self.render = ->
modal.render!
self
module.exports = ColumnEditModal

@ -0,0 +1,13 @@
{h} = require "maquette"
module.exports = {
icon: (selector, icon) ->
unless icon
icon = selector
selector = ""
h (\span.icon + selector), [
h "span.fas.fa-#{icon}"
]
}

@ -1,16 +1,15 @@
maquette = require "maquette"
nmd = require "nano-markdown"
authd = require "./authd.ls"
LoginForm = require "../lib/authd/client/login-form.ls"
todows = require "./todowebsocket.ls"
AuthWS = require "./authws.ls"
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"
{create-projector, h} = maquette
projector = create-projector!
@ -58,7 +57,7 @@ console.log "todod url: " + model.todod-url
# network configuration
#
model.authd-ws = authd.create-socket model.authd-url
model.authd-ws = AuthWS model.authd-url
model.todod-ws = todows.create-socket model.todod-url
@ -71,9 +70,13 @@ model.todod-ws = todows.create-socket model.todod-url
authd-on-websocket-error = (event) ->
console.log "WebSocket error.", event
model.current-view := "network-error"
model.last-network-error := event
projector.schedule-render!
authd-on-websocket-close = (event) ->
if model.current-view == "network-error"
return
model.current-view := "login"
console.log "WebSocket has been closed.", event
# model.todod-ws.reopen!
@ -122,9 +125,13 @@ model.authd-ws.add-event-listener \users-list, (message) ->
on-websocket-error = (event) ->
console.log "WebSocket error.", event
model.current-view := "network-error"
model.last-network-error := event
projector.schedule-render!
on-websocket-close = (event) ->
if model.current-view == "network-error"
return
model.current-view := "login"
console.log "WebSocket has been closed.", event
# model.todod-ws.reopen!
@ -136,7 +143,7 @@ model.todod-ws.user-on-socket-error ++= [ on-websocket-error ]
model.todod-ws.user-on-socket-close ++= [ on-websocket-close ]
model.todod-ws.add-event-listener \lists-list, (message) ->
console.log "Project list received", message
console.log "Projects list received", message
model.project-list := message.lists.map (x) ->
old-project = model.project-list.find((.id == x.id))
@ -192,7 +199,7 @@ model.todod-ws.add-event-listener \tasks, (message) ->
project = model.project-list.find((.id == message.list))
project.tasks := message.tasks.map (e) -> Task e, project, model.todod-ws
if model.viewed-project.id == project.id
if model.viewed-project && model.viewed-project.id == project.id
model.viewed-project := project
projector.schedule-render!
@ -202,7 +209,7 @@ model.todod-ws.add-event-listener \task-created, (message) ->
task = message.task
list = model.project-list.find((.id == task.list))
if list.id == model.viewed-project.id
if model.viewed-project && list.id == model.viewed-project.id
model.viewed-project := list
if list
@ -249,12 +256,6 @@ render-project-list = ->
]
]
Column = (title) ->
{
title: title
id: UUID! # TODO FIXME XXX
}
render-project = (project) ->
if project
project.render!
@ -266,6 +267,10 @@ render-project = (project) ->
model.login-form = LoginForm {
authws-url: model.authd-url
on-login: (user, token) ->
# FIXME: May double-login if the user clicks the “login” button too much.
if model.current-view != "login"
return
model.current-view := "project-list"
model.authd-ws.token := token
model.todod-ws.token := token
@ -295,6 +300,19 @@ render-body = ->
render-project-list!
when "project"
render-project model.viewed-project
when "network-error"
h \div.container [
h \div.notification.is-danger [
h \div.title.is-2 [ "Socket error!" ]
h \div.content [
h \p [ "We’re very sorry, but something got very wrong between here and the backend." ]
h \p [ "Please check your network connection and refresh the page." ]
h \p.small [ "The error happened on " + model.last-network-error.explicit-original-target.url ]
]
]
]
else
h \div.notification.is-error [

@ -53,7 +53,7 @@ Modal = (args, caller) ->
]
]
h \button.button.modal-close {
h \a.modal-close {
aria-label: "close"
onclick: ->
self.visible := false

@ -1,23 +1,18 @@
{h} = require "maquette"
{button, field, control} = require "./bulma.ls"
{icon} = require "./font-awesome.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!" ]
Modal = require "./modal.ls"
UUID = require "uuid/v4"
ProjectCreationModal = require "./project-creation-modal.ls"
Column = (title) ->
{
title: title
id: UUID! # TODO FIXME XXX
}
Navbar = ->
self = {}
@ -45,19 +40,31 @@ Navbar = ->
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
h \a.navbar-item.has-text-success {
key: \new-project
onclick: ->
model.modal := ProjectCreationModal {
on-validation: (project) ->
model.todod-ws.add-list project.title, project
}
} [
h \span [ "New project" ]
icon \plus
]
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" ]
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!
} [
h \span [ "Logout" ]
icon \sign-out-alt
]
]
]

@ -6,216 +6,155 @@ bulma = require "./bulma.ls"
{field, control, label, button, tag, input, select} = bulma
col-to-lines = (column, self) ->
field \.has-addons {
key: "column.#{column.id}"
} [
control \.is-expanded [
input {
key: "input" + column.id
value: column.title
oninput: (e) ->
self.extra_properties.columns.find((.id == column.id)).title := e.target.value
}
]
deep-copy = (object) ->
JSON.parse JSON.stringify object
control [
button \.is-danger.is-outlined {
key: "button" + column.id
onclick: ->
self.extra_properties.columns := self.extra_properties.columns.filter((.id != column.id))
} [ "DELETE" ]
]
]
user-form-selection = (self, user) ->
h \option {
value: user.uid
} [ user.login ]
permission-groups =
"read"
"post"
"edit"
"admin"
permissions-add = (self, permission, user-id) ->
perm-list = self.permissions[permission]
if perm-list
is-already-there = perm-list.find (e) -> (""+ e) == ("" + user-id)
if is-already-there
console.log "user #{user-id} already in #{perm-list}"
else
perm-list ++= [ parseInt(user-id) ]
self.permissions[permission] := perm-list
# console.log "adding user #{user-id} to #{perm-list[0]}: #{perm-list}"
else
console.log "Cannot find #{permission} permissions, creating it"
self.permissions[permission] := user-id
permission-to-form-selection = (self, permission) ->
h \option {
value: permission
} [ permission ]
ProjectCreationModal = (project, todod-ws, users) ->
# work on a copy of the columns
# in case of cancelled modifications, only the copies are changed
columns-copy = []
for col in project.extra_properties.columns
new-col = {}
for k,v of col
new-col[k] = v
columns-copy ++= [ new-col ]
const PERMISSION_LEVELS = ["admin", "edit", "read"]
remove-permission = (project, uid) ->
for key, value of project.permissions
project.permissions[key] := value.filter (!= uid)
add-permission = (project, uid, perm) ->
project.permissions[perm].push uid
ProjectCreationModal = (args) ->
self = {
title: project.title || ""
permissions: project.permissions || {admin: [], edit: [], post: [], read: []}
tmp:
new-user-permission:
id: void
permission: permission-groups[0]
new-column-input:
title: "New column !"
users: users || []
extra_properties:
columns: columns-copy
project: if args.project
deep-copy args.project
else
{}
visible: args.visible || true
on-validation: args.on-validation || (project) ->
input : {
# Used when giving permissions to users not currently in the
# list of permissions.
new-user: ""
new-user-permission: \read
}
}
modal = Modal {
+visible
content-render: (self) ->
h \div.form [
self.project.tasks := void
unless self.project.extra_properties
Column = (title, args) ->
self = {
title: title
id: UUID!
}
field [
label "Project title"
for key, value of (args || {})
self[key] = value
input {
value: self.title
oninput: (e) ->
self.title := e.target.value
}
]
self
self.project.extra_properties = {
columns: [
Column "Unassigned" {color: "red"}
Column "Work in Progress"
Column "To be Checked"
Column "Being Checked"
Column "Done" {color: "green"}
]
}
self.modal = Modal {
on-validation: ->
if true # FIXME: Validate project
self.visible := false
# h \hr []
args.on-validation self.project
# bulma.field [
# bulma.label "Adding a user"
# bulma.input {
# value: self.new-user
# oninput: (e) ->
# self.new-user := e.target.value
# name: \new-user
# id: \user-add
# }
# ]
on-cancellation: ->
self.visible := false
h \hr []
content-render: -> [
field {key: \title} [
label "Title"
field [
input {
value: self.project.title
oninput: (e) ->
console.log "oninput => ", e.target.value
self.project.title := e.target.value
}
]
if self.project.permissions
field {key: \permissions} [
label "Permissions"
h \table.table.is-fullwidth.is-striped [
h \table.table.is-fullwidth [
h \thead [
h \th [ "User" ]
h \th [ "Permission" ]
]
h \tbody [
for permission, uids of self.permissions
for uid in uids
h \tr {key: uid.to-string!} [
h \td [
uid.to-string!
for permission in PERMISSION_LEVELS
[
for uid in self.project.permissions[permission]
h \tr {key: uid} [
# FIXME: show full name or login
h \td [ uid.to-string! ]
h \td [ permission ]
]
h \td.is-narrow [
permission
]
]
]
]
]
]
h \hr []
# FIXME: This is supposed to go in a .field, right?
label "Adding new user"
if self.project.permissions
field \.has-addons [
control \.is-expanded [
# FIXME: Replace by a “user search input” as soon
# as one is available.
input {
value: self.input.new-user
oninput: (e) ->
self.input.new-user := e.target.value
}
]
field \.has-addons { key: \new-user } [
control [
select {
onchange: (e) ->
self.tmp.new-user-permission.permission := e.target.value
} permission-groups.map (permission) -> permission-to-form-selection self, permission
]
control \.is-expanded [
select \.is-fullwidth {
onchange: (e) ->
self.tmp.new-user-permission.uid := e.target.value
} [
user-form-selection self, { login: "Choose a user", uid: "-" }
for user-id, user of self.tmp.users
user-form-selection self, user
]
self.input.new-user-permission := e.target.value
} PERMISSION_LEVELS.map (perm) ->
h \option {
selected: perm == \read
} [ perm ]
]
control [
button \.is-success.is-outlined {
h \div.button.is-success.is-outlined {
onclick: ->
if self.tmp.new-user-permission.uid == void || self.tmp.new-user-permission.uid == "-"
console.log "adding an user permission on the kanban: failed, no user selected"
else
# TODO:
# adding the permissions in self.permissions
# then editing the project in the db via todod-ws
permissions-add self, self.tmp.new-user-permission.permission, self.tmp.new-user-permission.uid
} [ "+" ]
]
]
h \hr []
uid = parse-int self.input.new-user
perm = self.input.new-user-permission
label [ "Columns" ]
self.input.new-user := ""
# FIXME: Really? :thonk:
#self.input.new-user-permission := \read
for dom in (self.extra_properties.columns.map (column) -> col-to-lines column, self)
dom
console.log "Adding new perm? :/", uid, perm
h \hr []
field \.has-addons { key: \new-column } [
control \.is-expanded [
input {
value: self.tmp.new-column-input.title
oninput: (e) ->
self.tmp.new-column-input.title := e.target.value
}
]
remove-permission self.project, uid
add-permission self.project, uid, perm
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 !"
} [ "+" ]
console.log self.project.permissions
} [
"+"
]
]
]
]
on-validation: ->
tmp = delete self.tmp
if project.id
todod-ws.edit-list project.id, self
else
todod-ws.add-list self.title, self
self.tmp := tmp
}, self
]
}
self.render = ->
modal.render!
self.modal.visible = self.visible
self.modal.render!
self

@ -5,6 +5,12 @@ Task = require "./task.ls"
Modal = require './modal.ls'
TaskCreationModal = require './task-creation-modal.ls'
ProjectCreationModal = require './project-creation-modal.ls'
ColumnEditModal = require './column-edit-modal.ls'
{icon} = require './font-awesome.ls'
deep-copy = (object) ->
JSON.parse JSON.stringify object
is-right-column = (task, column-id) ->
task.extra_properties && task.extra_properties.column && task.extra_properties.column == column-id
@ -43,7 +49,31 @@ Project = (self, todod-ws, users) ->
h \div.column.cards-list {
key: column.id
} [
h \p.title.is-4 [ column.title ]
h \p.title.is-4 [
column.title
h \a.is-pulled-right.icon.has-text-grey {
onclick: ->
modal := ColumnEditModal self, column, {
on-validation: (column) ->
console.log "column update:", column
modal.visible := false
extra_properties = deep-copy(self.extra_properties)
extra_properties.columns = extra_properties.columns.map (old-column) ->
if old-column.id == column.id
column
else
old-column
todod-ws.edit-list self.id, {
extra_properties: extra_properties
}
}
} [
icon \cog
]
]
tasks-to-display.map (task) ->
task.render {
@ -61,37 +91,44 @@ Project = (self, todod-ws, users) ->
self.right-nav-render = ->
[
h \div.navbar-item {
h \a.navbar-item.has-text-success {
key: "navbar-new-task"
onclick: ->
modal := TaskCreationModal self, self.todod-ws, void, self.users
} [
h \div.button.is-success.is-outlined.is-medium {
onclick: ->
modal := TaskCreationModal self, self.todod-ws, void, self.users
} [ "New task" ]
h \span [ "New task" ]
icon \plus
]
h \div.navbar-item {
h \a.navbar-item {
key: "navbar-edit-project"
onclick: ->
modal := ProjectCreationModal {
project: self
on-validation: (project) ->
console.log "Requesting edit for", project
self.todod-ws.edit-list project.id, project
}
} [
h \div.button.is-dark.is-outlined.is-medium {
onclick: ->
modal := ProjectCreationModal self, self.todod-ws, self.users
} [ "Edit this project" ]
icon \cog
# "Edit this project"
]
h \div.navbar-item {
h \a.navbar-item.has-text-danger {
key: "navbar-delete-project"
onclick: ->
modal := Modal {
+visible
content:
h \p [ "Are you sure you want to remove board #{self.title}?" ]
on-validation: ->
self.todod-ws.remove-list self.id
}
} [
h \div.button.is-danger.is-outlined.is-medium {
onclick: ->
modal := Modal {
+visible
content:
h \p [ "Are you sure you want to remove board #{self.title}?" ]
on-validation: ->
self.todod-ws.remove-list self.id
}
} [ "Delete this project" ]
icon \skull-crossbones
# "Delete this project"
]
]

@ -4,6 +4,8 @@ nmd = require "nano-markdown"
TaskCreationModal = require './task-creation-modal.ls'
TaskRemovalModal = require './task-removal-modal.ls'
{icon} = require "./font-awesome.ls"
display-login = (task, users) ->
if task.extra_properties && task.extra_properties.assignee-id && users && users[task.extra_properties.assignee-id] && users[task.extra_properties.assignee-id].login
h \p [ '@' + users[task.extra_properties.assignee-id].login ]
@ -31,7 +33,15 @@ Task = (self, project, todod-ws) ->
args.onclick(e)
} [
h \div.card-content [
h \div.title.is-5 [ self.title ]
h \div.title.is-5 [
h \a.has-text-grey.is-pulled-right {
onclick: ->
modal := TaskCreationModal project, todod-ws, self, project.users
} [
icon \cog
]
self.title
]
h \div.media [
h \div.media-left [
# FIXME: assignee card image
@ -50,11 +60,6 @@ Task = (self, project, todod-ws) ->
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

@ -21,6 +21,7 @@ module.exports = {
"edit-task": 9
"subscribe": 10
"unsubscribe": 11
"set-permission": 12
}
response-types = {
@ -70,6 +71,8 @@ module.exports = {
message = JSON.parse(event.data)
parsed-message = JSON.parse(message.payload)
console.log "received todod message:", parsed-message
for f in self.callbacks[message.mtype]
f parsed-message

@ -4,8 +4,8 @@
<title>Kanban</title>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta charset="utf-8"/>
<link rel="stylesheet" href="/font-awesome/fontawesome.min.css"/>
<link rel="stylesheet" href="/font-awesome/solid.min.css"/>
<link rel="stylesheet" href="/font-awesome/css/fontawesome.min.css"/>
<link rel="stylesheet" href="/font-awesome/css/solid.min.css"/>
<link rel="stylesheet" href="/style.css"/>
</head>
<body>

Loading…
Cancel
Save