diff --git a/client/authd.ls b/client/authd.ls
index 368353b..b4afbc3 100644
--- a/client/authd.ls
+++ b/client/authd.ls
@@ -6,12 +6,23 @@ 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 = {
@@ -20,6 +31,9 @@ module.exports = {
"user": 2
"user-added": 3
"user-edited": 4
+ "extra": 5
+ "extra-updated": 6
+ "users-list": 7
}
# TODO: naming convention
@@ -92,6 +106,23 @@ module.exports = {
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 {
diff --git a/client/index.ls b/client/index.ls
index 664a2c7..bbfcaea 100644
--- a/client/index.ls
+++ b/client/index.ls
@@ -1,41 +1,8 @@
-# first:
-# connection to authd
-# display the authd widget
-#
-# second:
-# connection to todod
-# rewrite the whole body component
-# page 1: (TODO) list of lists (or todos)
-# display:
-# navbar
-#
-# "element 1" button
-# "element 2" button
-# ...
-# "element x" button
-#
-# "new list" button
-# widgets:
-# navbar: [return to list button] [logout]
-# element button:
-# [project list name]
-# or
-# [project list name] [RIGTHS] [DELETE]
-#
-# page 2:
-# display:
-# (TODO) list of tasks (this client has a kanban display of tasks, with columns)
-# (TODO) task widget
-# widgets:
-# task widget:
-# TODO
-#
-# page 3: (TODO) pages about
-
maquette = require "maquette"
nmd = require "nano-markdown"
authd = require "./authd.ls"
+LoginForm = require "../lib/authd/client/login-form.ls"
todows = require "./todowebsocket.ls"
bulma = require "./bulma.ls"
Task = require "./task.ls"
@@ -49,16 +16,10 @@ projector = create-projector!
model = {
# view: login, project-list, project, network-error (TODO: other views, such as rights)
- # XXX FIXME TODO: TESTING THINGS
current-view: "login"
viewed-project: void
- # current-view: "testing-modals"
-
- # { uid => user data }
- users: {}
-
# list of Project objects
project-list: []
@@ -71,6 +32,9 @@ model = {
previous-error: undefined
error: undefined
+
+ # { uid => user data }
+ users: {}
}
model.authd-url =
@@ -122,22 +86,30 @@ model.authd-ws.user-on-socket-close ++= [ authd-on-websocket-close ]
# authd message handlers
-model.authd-ws.add-event-listener \token, (message) ->
- model.current-view := "project-list"
- model.todod-ws.token := message.token
- model.todod-ws.list-lists!
- projector.schedule-render!
+# HANDLED with the "on-login" callback in the LoginForm component
+# model.authd-ws.add-event-listener \token, (message) ->
+# model.current-view := "project-list"
+# model.authd-ws.token := message.token
+# model.todod-ws.token := message.token
+# model.todod-ws.list-lists!
+# model.authd-ws.list-users!
+# projector.schedule-render!
model.authd-ws.add-event-listener \error, (message) ->
- # console.log "authd error", message
+ console.log "authd error", message
projector.schedule-render!
model.authd-ws.add-event-listener \user, (message) ->
model.users[message.user.uid] := message.user
projector.schedule-render!
-# TODO: user-added, user-edited
+model.authd-ws.add-event-listener \users-list, (message) ->
+ console.log "Received users: ", message
+ message.users.map (user) ->
+ model.users[user.uid] := user
+ projector.schedule-render!
+# TODO: user-added, user-edited
#
@@ -167,7 +139,7 @@ model.todod-ws.add-event-listener \lists-list, (message) ->
model.project-list := message.lists.map (x) ->
old-project = model.project-list.find((.id == x.id))
- new-project = Project x, model.todod-ws
+ new-project = Project x, model.todod-ws, model.users
if old-project && new-project.id == old-project.id
new-project.tasks = old-project.tasks
@@ -179,13 +151,13 @@ model.todod-ws.add-event-listener \lists-list, (message) ->
model.todod-ws.add-event-listener \new-list, (message) ->
console.log "New project", message
- model.project-list := model.project-list ++ [ (Project message.list, model.todod-ws) ]
+ model.project-list := model.project-list ++ [ (Project message.list, model.todod-ws, model.users) ]
projector.schedule-render!
model.todod-ws.add-event-listener \list-updated, (message) ->
console.log "Project updated", message
- new-project = Project message.list, model.todod-ws
+ new-project = Project message.list, model.todod-ws, model.users
model.project-list := model.project-list.map (project) ->
if project.id == message.list.id
@@ -268,22 +240,24 @@ render-navbar = ->
h \div.navbar-start [
h \a.navbar-item.is-size-2 {
onclick: ->
- model.todod-ws.unsubscribe model.viewed-project.id
+ 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.viewed-project
+ if model.current-view == "project" && model.viewed-project
model.viewed-project.inner-nav-render!
h \div.navbar-end [
- if model.viewed-project
+ 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
@@ -336,11 +310,23 @@ render-project = (project) ->
bulma.title 3 "Error, we did not get the project id " + project.id
]
+model.login-form = LoginForm {
+ authws-url: model.authd-url
+ on-login: (user, token) ->
+ model.current-view := "project-list"
+ model.authd-ws.token := token
+ model.todod-ws.token := token
+ model.todod-ws.list-lists!
+ model.authd-ws.list-users!
+ projector.schedule-render!
+}
+
render-body = ->
h \div.section [
switch model.current-view
when "login"
- authd.login-page model
+ # authd.login-page model
+ model.login-form.render!
when "project-list"
h \div#project-list [
diff --git a/client/project-creation-modal.ls b/client/project-creation-modal.ls
index da13103..e21b28d 100644
--- a/client/project-creation-modal.ls
+++ b/client/project-creation-modal.ls
@@ -24,18 +24,37 @@ col-to-lines = (column, self) ->
} [ "DELETE" ]
]
-#
+user-form-selection = (self, user) ->
+ h \option {
+ value: user.uid
+ } [ user.login ]
-ProjectCreationModal = (project, todod-ws) ->
+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
@@ -48,11 +67,15 @@ ProjectCreationModal = (project, todod-ws) ->
self = {
title: project.title || ""
- permissions: project.permissions || [[]]
- new-user: "New user"
- new-column-input: {
- title: "New column !"
- }
+ 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
@@ -73,29 +96,30 @@ ProjectCreationModal = (project, todod-ws) ->
}
]
- h \hr []
+ # h \hr []
- 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
- }
- ]
+ # 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
+ # }
+ # ]
h \hr []
h \aside.menu [
h \p.menu-label [ "Permissions" ]
- h \ul.menu-list self.permissions.map (permission) ->
- h \li [
- h \p permission.map (e, index) ->
+ h \ul.menu-list Object.keys(self.permissions).map (permission) ->
+ h \li {
+ key: "permission" + permission
+ } [
+ h \p self.permissions[permission].map (e, index) ->
if index == 0
- "Permissions '" + e + "': "
+ "Permissions '" + permission + "': " + e + ", "
else
"" + e + ", "
]
@@ -103,6 +127,43 @@ ProjectCreationModal = (project, todod-ws) ->
h \hr []
+ h \p [ "Adding new user" ]
+
+ h \div.field.has-addons {
+ key: "adding-user"
+ } [
+ h \div.select.control [
+ h \select {
+ onchange: (e) ->
+ self.tmp.new-user-permission.permission := e.target.value
+ } permission-groups.map (permission) -> permission-to-form-selection self, permission
+ ]
+
+ h \div.select.control.is-expanded [
+ h \select {
+ 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
+ ]
+ ]
+
+ h \div.control.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 []
+
h \p [ "Choose the columns" ]
for dom in (self.extra_properties.columns.map (column) -> col-to-lines column, self)
@@ -115,9 +176,9 @@ ProjectCreationModal = (project, todod-ws) ->
} [
h \p.control.is-expanded [
h \input.input {
- value: self.new-column-input.title
+ value: self.tmp.new-column-input.title
oninput: (e) ->
- self.new-column-input.title := e.target.value
+ self.tmp.new-column-input.title := e.target.value
} [ ]
]
@@ -125,20 +186,21 @@ ProjectCreationModal = (project, todod-ws) ->
onclick: ->
new-col = {
id: UUID!
- title: self.new-column-input.title
+ title: self.tmp.new-column-input.title
}
self.extra_properties.columns ++= [ new-col ]
- self.new-column-input.title := "New column !"
+ self.tmp.new-column-input.title := "New column !"
} [ "+" ]
]
-
]
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 = ->
diff --git a/client/project.ls b/client/project.ls
index 8d87b00..0ad731a 100644
--- a/client/project.ls
+++ b/client/project.ls
@@ -4,7 +4,7 @@ bulma = require "./bulma.ls"
Task = require "./task.ls"
Modal = require './modal.ls'
TaskCreationModal = require './task-creation-modal.ls'
-ProjectCreationModal = require "./project-creation-modal.ls"
+ProjectCreationModal = require './project-creation-modal.ls'
is-right-column = (task, column-id) ->
task.extra_properties && task.extra_properties.column && task.extra_properties.column == column-id
@@ -20,9 +20,10 @@ orphan-tasks = (tasks, columns) ->
tasks.filter (task) -> (! has-column) || inexistant-column task, columns
-Project = (self, todod-ws) ->
+Project = (self, todod-ws, users) ->
self.todod-ws = todod-ws
self.tasks = []
+ self.users = users || []
modal = void
@@ -33,6 +34,10 @@ Project = (self, todod-ws) ->
if first
tasks-to-display ++= orphan-tasks self.tasks, self.extra_properties.columns
+ #
+ # COLUMNS
+ #
+
h \div.column {
key: column.id
} [
@@ -52,21 +57,27 @@ Project = (self, todod-ws) ->
self.right-nav-render = ->
[
- h \div.navbar-item [
+ h \div.navbar-item {
+ key: "navbar-new-task"
+ } [
h \div.button.is-success.is-outlined {
onclick: ->
- modal := TaskCreationModal self, self.todod-ws
+ modal := TaskCreationModal self, self.todod-ws, void, self.users
} [ "New task" ]
]
- h \div.navbar-item [
+ h \div.navbar-item {
+ key: "navbar-edit-project"
+ } [
h \div.button.is-outlined {
onclick: ->
- modal := ProjectCreationModal self, self.todod-ws
+ modal := ProjectCreationModal self, self.todod-ws, self.users
} [ "Edit this project" ]
]
- h \div.navbar-item [
+ h \div.navbar-item {
+ key: "navbar-delete-project"
+ } [
h \div.button.is-danger.is-outlined {
onclick: ->
modal := Modal {
diff --git a/client/style.sass b/client/style.sass
index 7183fa3..a61683f 100644
--- a/client/style.sass
+++ b/client/style.sass
@@ -1,14 +1,14 @@
@charset "utf-8"
-@import "../node_modules/bulmaswatch/superhero/_variables.scss"
+// @import "../node_modules/bulmaswatch/superhero/_variables.scss"
// Import Bulma core
@import "../node_modules/bulma/bulma"
-@import "../node_modules/bulmaswatch/superhero/_overrides.scss"
+// @import "../node_modules/bulmaswatch/superhero/_overrides.scss"
.project>.columns
- overflow-x: scroll
+ overflow-x: auto
.avatar
border-radius: 4px
diff --git a/client/task-creation-modal.ls b/client/task-creation-modal.ls
index 30f93cf..6fcd757 100644
--- a/client/task-creation-modal.ls
+++ b/client/task-creation-modal.ls
@@ -1,7 +1,30 @@
+# TODO: on modification, the description isn't updated on the client
+
h = require 'maquette' .h
Modal = require './modal.ls'
+colors = [
+ "white"
+ "black"
+ "light"
+ "dark"
+ "primary"
+ "info"
+ "link"
+ "success"
+ "warning"
+ "danger"
+ "black-bis"
+ "black-ter"
+ "grey-darker"
+ "grey-dark"
+ "grey"
+ "grey-light"
+ "grey-lighter"
+ "white-ter"
+ "white-bis"
+]
column-form-selection = (self, column) ->
h \option {
@@ -9,32 +32,57 @@ column-form-selection = (self, column) ->
selected: self.extra_properties && self.extra_properties.column == column.id
} [ column.title ]
+color-to-form-selection = (self, color, current-color) ->
+ h \option {
+ value: color
+ selected: current-color && current-color == color
+ } [ color ]
-TaskCreationModal = (project, todod-ws, task) ->
+user-form-selection = (self, user) ->
+ h \option {
+ value: user.uid
+ } [ user.login ]
+
+TaskCreationModal = (project, todod-ws, task, users) ->
task ||= {}
# copy not to override anything on cancel
self = {
title: task.title || ""
description: task.description || ""
- extra_properties: {
+ extra_properties:
column: ""
- }
+ background-color: ""
+ assignee-id: task.assignee-id || void
+ tmp:
+ users: users || []
}
- if task.extra_properties && task.extra_properties.column
- self.extra_properties.column = task.extra_properties.column
+ # copy extra properties
+ # currently: column + background-color + assignee + expected duration time
+ for k,v of task.extra_properties
+ self.extra_properties[k] = v
modal = Modal {
+visible
content-render: (self) ->
h \div.form [
+
+ #
+ # TITLE
+ #
+
h \input.input {
value: self.title
oninput: (e) ->
self.title := e.target.value
}
+
+ #
+ # DESCRIPTION
+ #
+
h \textarea {
value: self.description
oninput: (e) ->
@@ -51,13 +99,52 @@ TaskCreationModal = (project, todod-ws, task) ->
} project.extra_properties.columns.map (column) -> column-form-selection self, column
]
+
+ #
+ # USER MANAGEMENT
+ #
+
+ h \hr []
+
+ h \p [ "Assign someone to the task" ]
+
+ h \div.field.has-addons {
+ key: "assign-someone-to-task"
+ } [
+ h \div.select.control [
+ h \select {
+ onchange: (e) ->
+ self.extra_properties.assignee-id := 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
+ ]
+ ]
+ ]
+
+
+ #
+ # BACKGROUND COLOR
+ #
+
+ h \p [ "Choose the background color" ]
+
+ h \div.select [
+ h \select {
+ onchange: (e) ->
+ self.extra_properties.background-color := e.target.value
+ } colors.map (color) -> color-to-form-selection self, color, self.extra_properties.background-color
+ ]
]
on-validation: ->
+ tmp = delete self.tmp
if task.id
todod-ws.edit-task task.id, self
else
todod-ws.add-task project.id, self.title, self
+ self.tmp = tmp
}, self
self.render = ->
diff --git a/client/task.ls b/client/task.ls
index 73535b3..53df5b7 100644
--- a/client/task.ls
+++ b/client/task.ls
@@ -1,62 +1,53 @@
-#
-# Tasks, previous version of todos
-#
-
h = require 'maquette' .h
bulma = require "./bulma.ls"
nmd = require "nano-markdown"
TaskCreationModal = require './task-creation-modal.ls'
TaskRemovalModal = require './task-removal-modal.ls'
-#
-# generic functions
-#
-
-get-previous = (collection, element) ->
- var previous
-
- for item in collection
- if item == element
- return previous
-
- previous = item
-
-get-next = (collection, element) ->
- var found-element
-
- for item in collection
- if found-element
- return item
-
- if item == element
- found-element := true
+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 ]
+ else
+ h \p [ '-' ]
Task = (self, project, todod-ws) ->
modal = void
self.render = ->
- h \div.card { key: self.id } [
+ 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}" {
+ key: self.id
+ } [
h \div.card-content [
h \div.media [
h \div.media-left [
+ # FIXME: assignee card image
"LEFT"
+ display-login self, project.users
]
- h \div.media-content [
- self.title
- ]
+
+ h \div.media-content [ self.title ]
+
h \div.button {
onclick: ->
- modal := TaskCreationModal project, todod-ws, self
+ modal := TaskCreationModal project, todod-ws, self, project.users
} [ "Edit" ]
- h \div.button {
+
+ h \div.button.is-danger {
onclick: ->
modal := TaskRemovalModal project.id, todod-ws, self
} [ "X" ]
]
- h \div.content [
- self.description
- ]
+
+ h \div.content {
+ key: "task-description-#{self.id}"
+ after-create: (dom) ->
+ dom.innerHTML = nmd self.description
+ } [ ]
]
if modal
modal.render!
diff --git a/client/todowebsocket.ls b/client/todowebsocket.ls
index 64e46f3..c4354a4 100644
--- a/client/todowebsocket.ls
+++ b/client/todowebsocket.ls
@@ -68,10 +68,11 @@ module.exports = {
self.socket.onmessage = (event) ->
message = JSON.parse(event.data)
- console.log "todod message received: ", message
+ parsed-message = JSON.parse(message.payload)
+ console.log "todod message #{message.mtype} received: ", parsed-message
for f in self.callbacks[message.mtype]
- f JSON.parse(message.payload)
+ f parsed-message
self.reopen = ->
self.socket.close!
@@ -80,37 +81,43 @@ module.exports = {
self.open-socket!
self.send = (type, opts) ->
- console.log JSON.stringify { mtype: type, payload: 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 }
self.list-lists = ->
self.send request-types[\list-lists], JSON.stringify {
token: self.token
+ id: "list-lists"
}
self.get-list = (list-id) ->
self.send request-types[\get-list], JSON.stringify {
token: self.token
list: list-id
+ id: "get-list"
}
self.get-tasks = (list-id) ->
self.send request-types[\get-tasks], JSON.stringify {
token: self.token
list: list-id
+ id: "get-tasks"
}
self.get-task = (task-id) ->
self.send request-types[\get-task], JSON.stringify {
token: self.token
task: task-id
+ id: "get-task"
}
self.remove-list = (list-id) ->
self.send request-types[\remove-list], JSON.stringify {
token: self.token
list: list-id
+ id: "remove-list"
}
# TODO: extra properties
@@ -118,6 +125,7 @@ module.exports = {
payload = {
token: self.token
title: title
+ id: "add-list"
}
for key, value of opts
@@ -129,6 +137,7 @@ module.exports = {
payload = {
token: self.token
list: list-id
+ id: "edit-list"
}
for key, value of opts
@@ -140,6 +149,7 @@ module.exports = {
self.send request-types[\remove-task], JSON.stringify {
token: self.token
task: task-id
+ id: "remove-task"
}
# TODO: extra properties
@@ -148,6 +158,7 @@ module.exports = {
token: self.token
list: list-id
title: title
+ id: "add-task"
}
for key, value of opts
@@ -159,6 +170,7 @@ module.exports = {
payload = {
token: self.token
task: task-id
+ id: "edit-task"
}
for key, value of opts
@@ -170,12 +182,14 @@ module.exports = {
self.send request-types[\subscribe], JSON.stringify {
token: self.token
list: list-id
+ id: "subscribe"
}
self.unsubscribe = (list-id) ->
self.send request-types[\unsubscribe], JSON.stringify {
token: self.token
list: list-id
+ id: "unsubscribe"
}
self