New kanban application built using liveview

Hello all,

I’ve finally published my kanban application built using Phoenix liveview. It is available here

It’s a simple trello like app that I built for my own needs and to learn elixir & liveview (i’m coming from erlang). It only has the features that I needed and I’m currently using it everyday (I’m hosting it on a private raspberry pi3)

Hope some of you may find it useful for their own needs also :slight_smile:

Cheers,

Sébastien.

28 Likes

wooow, very nice! thank you!

1 Like

How do i create a user? :smiley:

1 Like

Running into this problem when running mix ecto.setup

** (Postgrex.Error) ERROR 42883 (undefined_function) function public.sp_delete_board(bigint) does not exist

And i’m still not sure how to enable that first user, i added a password hash. and changed some credentials around. but not sure how to actually seed?

** (Postgrex.Error) ERROR 42883:

see https://github.com/seb3s/kandesk/pull/2 for fix

Isn’t it easier just to use custom seeds.exs script file placed somewhere on your target device? It’s well known way especially in newly generated phoenix projects and should not be hard to make it work with deployment libraries like distillery.

Yes I think I’m going to provide an admin first user via seeds.exs to ease setup.
I didn’t know the usage of this file when I started the project so this is part of the history :slight_smile:

2 Likes

I’ve updated the first admin user creation procedure.
It is also now explained in the new readme file.

2 Likes

Hello,

To give potential users a better overview, here is the current features set:

Boards:

  • create / update / delete
  • sharable among users with fine grain access control (edit_board?, delete_board?, create_column?, edit_column?, delete_column?, move_column?, create_task?, edit_task?, delete_task?, move_task?, admin_tags?, assoc_tags?)
  • export board content to pseudo xml data

Columns:

  • create / update / delete
  • visibility: a column can be made visible to its creator only
  • columns are sortable via drag & drop
  • duplicate column
  • ability to move a column to another board
  • export column content to pseudo xml data
  • realtime updating among users viewing the same board

Cards:

  • create / update / delete
  • cards are sortable via drag & drop within and between columns
  • tags association
  • realtime updating among users viewing the same board

Tags:

  • create / update
  • two display modes: large with text or small without
  • new tags can be added
  • tags are sortable to control the order in which they appear on cards
  • realtime updating among users viewing the same board

Account page:

  • basic page to maintain your personal data (currently names & password)

Admin area:

  • manage users: basic admin panel to be able to create / update / delete users

Cheers,

Sébastien.

1 Like

It already looks really nice! The instant updates across board views is really nice.

Locally, I’ve wrapped strings in gettext for translation (noticed when some of the buttons showed up in French :slight_smile: ) … when I’m back to my home dev machine, I’ll pop in a CLDR impl for switching language and push a PR.

I live and die by my kanban boards (ok, maybe not quite that dramatic … but close!) and the current board I use in a well-known, long-lived OSS project that is written in PHP … it has tons of features, but also has some drawbacks, and I don’t really need many of the more advanced features. So, I’ll be watching this project of yours with interest, and perhaps contribute some PRs here and there.

Thanks for making and publishing this!

p.s. When saving files such as lib/kandesk_web/live/index_live.ex I get massive diffs as they are not formatted with the Elixir formatter … it might be nice to enable the formatter in your editor so that it comes out in more-or-less standard form.

1 Like

Hi,

Locally, I’ve wrapped strings in gettext for translation (noticed when some of the buttons showed up in French) … when I’m back to my home dev machine, I’ll pop in a CLDR impl for switching language and push a PR.

Yes I’m french and I have missed some strings !! :slight_smile:
I had internationalization in my todo so that’s really good to read that…

Thanks a lot for your positive feedback !

p.s. I’m a veteran coder (50 years old and still loving it yeah !) and so probably highly opinionated regarding indentation… but I will take a look at what normal elixir formatting is. I’m using sublimeText and if you know a good module for that, that would be great !

No worries, you’ve only got a few years on me … lots of us old dogs around! :wink:

I hear you… however, the Elixir community has really done a nice job of creating a consistent experience between libraries and apps with the formatter. Makes it that little bit easier to read / debug / contribute across projects.

You appear to be using 2-space-indent already, so that won’t change as that’s also the default Elixir style. It’s mostly just things like:

-    q1 = from b in Board,
-      where: [creator_id: ^user_id],
-      select: b.id
+    q1 =
+      from b in Board,
+        where: [creator_id: ^user_id],
+        select: b.id
1 Like

Ok, thanks for the link.

I’ve tried the elixir default formatting but I have to say that I already hate it to a certain degree.
Is it configurable somewhere ?

For example It doesn’t keep the line spaces I put in my code (or absence of lines) so I have no control of the readibility of my code (to my eyes I mean, and that could be subjective).

Another one

with "admin" <- assigns.user.role do :ok else _ -> raise(@access_error) end

is transformed into

with "admin" <- assigns.user.role do
  :ok
else
  _ -> raise(@access_error)
end

Six lines instead of one just to check the access rights to the function call. So If I can’t configure it at some level, I’m probably not going to use it…

Sébastien.

1 Like
unless assigns.user.role == "admin", do: raise(@access_error)

This will stay single line and is imo simpler to read

1 Like

Yes, but that an example where I don’t want a line to be transformed into 6 lines automatically

Another one I really dislike

def handle_event("show_modal", %{"modal" => "create_column" = modal}, socket) do
  {:noreply, assign(socket, show_modal: modal, changeset: Column.changeset(%Column{}, %{}))}
end

def handle_event("show_modal", %{"modal" => "create_task" = modal, "column_id" => column_id, "top_bottom" => top_bottom}, socket) do
  {:noreply, assign(socket, show_modal: modal, changeset: Task.changeset(%Task{}, %{}),
    column_id: to_integer(column_id), top_bottom: top_bottom)}
end

gets transformed into

def handle_event("show_modal", %{"modal" => "create_column" = modal}, socket) do
  {:noreply, assign(socket, show_modal: modal, changeset: Column.changeset(%Column{}, %{}))}
end

def handle_event(
      "show_modal",
      %{"modal" => "create_task" = modal, "column_id" => column_id, "top_bottom" => top_bottom},
      socket
    ) do
  {:noreply,
   assign(socket,
     show_modal: modal,
     changeset: Task.changeset(%Task{}, %{}),
     column_id: to_integer(column_id),
     top_bottom: top_bottom
   )}
end

Now my eyes have to move a lot to find which clause will be called. That’s typically a place where I want to keep control of the line breaks.

But maybe I’m too old :slight_smile: (at least my eyes are…)

It isn’t configurable, and this on purpose: the idea is to have a consistent style across the community. We see it in a number of languages these days … and it does have it’s benefits.

It isn’t 100% my personal style either, but I have adapted to the community styles for the languages I use which have one, and I no longer really notice it at all. Having the editor fix up changes on save is really what makes it bearable. :wink:

And the small differences to what I would naturally write myself are outweighed by the consistency between libraries… being able to open random Elixir libs from hex.pm and not be dazzled by someone’s genius idea of formatting code is pretty great … :slight_smile:

Back on topic! :slight_smile:

What would you think about splitting the rather large and bound to grow further index_live.ex into three: one for listing boards, one for showing a board, and one for looking at a specific task (either as a component, or as a whole-page view)?

Benefits:

  • Different routes for resources so one could easily bookmark a board or link to a task by URL
  • Smaller LiveViews should make for more maintainable code

I’ve voluntarily taken a SPA route as I wanted to test if it was feasible with liveview.
I’ve also got in mind a PRO project I’m maybe going to convert to elixir (from erlang / N2O) and this kandesk project serves me as a prototype (I also use it everyday of course instead of trello which I’ve moved away).

So regarding the splitting, I’ve already started an approach you can see with pages. The splitting of handle_event callbacks is done here:

  ## page delegation
  ## ---------------
  def handle_event(event, %{"delegate" => delegate} = params, socket) do
    valid_delegates = ["Page.Account", "Page.Admin_users"]
    if !Enum.any?(valid_delegates, & &1 == delegate), do: raise(@access_error)
    apply(String.to_existing_atom("Elixir.KandeskWeb." <> delegate), :handle_event, [event, params, socket])
  end

I’m not sure if it is the best way of doing it but it seems to function quite well. Having everything in one big liveview also simplifies a few things as long as I don’t care about url right now.

But certainly a different route has to be taken if some boards are to be made public outside of the login area.

I’ve not already done the splitting for the board page itself (the big one) but the same approach can be made to have small modules (columns, cards, tags …)