作業かんばんプロトタイプ

作業かんばんのプロトタイプを作成してみた。
作業かんばんのプロトタイプを RailsPlaygroundに移行。

「計画」Boxの createリンクで新しいタスクを作成。作成したタスクは、「計画」->「実行」->「振り返り」->「完了」の順にドラッグ&ドロップで移動できる。編集はダブりクリック。

Ajax周りの処理は くまくまーさんの render :update の解説を参考にさせて頂きました。Rails 1.0だと RJSテンプレートが使えないので、rake freeze_edge で開発版の Rails を利用。
それにしても、今まで使い慣れない JavaScriptを必死に書いていたのはなんだったのかってくらい便利。

ソースはこんな感じ。


#
#Contoller
#
class TaskBoardController < ApplicationController
model :task

def index
list
render :action => 'list'
end

def list
@plan_tasks = PlanTask.find(:all)
@do_tasks = DoTask.find(:all)
@look_back_tasks = LookBackTask.find(:all)
@done_tasks = DoneTask.find(:all)
end

def new
task = PlanTask.new
render :update do |page|
page.show_task_form(task)
end
end

def create
task_params = params[:task]
task = PlanTask.new(task_params)
task.deadline_on = task_params[:deadline_on]
task.save
render :update do |page|
task.errors.empty? ? page.save_task(task,true) : page.notify_errors(task)
end
end

def update
task_params = params[:task]
task = Task.find(params[:id])
task.deadline_on = task_params[:deadline_on]
task.update_attributes(params[:task])
render :update do |page|
task.errors.empty? ? page.save_task(task,false) : page.notify_errors(task)
end
end

def order
update_orders params["plan_tasks"], "PlanTask"
update_orders params["do_tasks"], "DoTask"
update_orders params["look_back_tasks"], "LookBackTask"
update_orders params["done_tasks"], "DoneTask"
render_nothing
end

def edit
task = Task.find(params[:id])
render :update do |page|
page.show_task_form(task)
end
end

def destroy
destroied_task_id = params[:id]
Task.find(destroied_task_id).destroy

render :update do |page|
page.remove "task_#{destroied_task_id}"
end
end

private
def update_orders(tasks, typename)
return if (tasks == nil)

tasks.each_with_index do |id,idx|
task = Task.find(id)
task.type = typename
task.save
end
end
end

#
# Helper
#
module TaskBoardHelper
def sortable_tasks(part, prev_part=nil, next_part=nil)
sortable_element part, :dropOnEmpty => true, :constraint => false,
:containment => [part, prev_part, next_part],
:handle => 'task',
:url => { :action => "order" }
end

def sortable_all_tasks
<<"EOF"
#{sortable_tasks "plan_tasks", nil, "do_tasks"}
#{sortable_tasks "do_tasks", "plan_tasks", "look_back_tasks"}
#{sortable_tasks "look_back_tasks", "do_tasks", "done_tasks"}
#{sortable_tasks "done_tasks", "look_back_tasks"}
EOF
end

def show_task_form(task)
part = task_part(task)
page.remove 'form'
page.insert_html :bottom, part, :partial => "form", :locals => {:task => task}
page.show_all_tasks
page.hide "#{part}_tasks"
end

def show_all_tasks
page.show 'plan_tasks', 'do_tasks', 'look_back_tasks', 'done_tasks'
end

def notify_errors(task)
page.replace_html 'form_error', :inline => "<%= @task=task; error_messages_for('task') %>", :locals => {:task => task}
end

def save_task(task, is_new)
part = task_part(task)
page.remove "task_#{task.id}" unless is_new
page.insert_html :bottom, "#{part}_tasks", :partial => "task", :locals => {:task => task}
page.insert_html :after, 'task_board', :inline => '<%= sortable_all_tasks %>'
page.hide 'form'
page.show_all_tasks
end

def notify_task(task)
return "completed_task" if (task_part(task) == 'done')
return "near_deadline" if Date.today > (task.deadline_on - 3)
return "over_deadline" if Date.today > task.deadline_on
""
end

private
def task_part(task)
to_part = {'PlanTask' => 'plan', 'DoTask' => 'do', 'LookBackTask' => 'look_back', 'DoneTask' => 'done'}
to_part[task.class.to_s]
end
end


#
# View: list.rhtml
#
<ul id="task_board">

<div id="form"></div> <!-- dummy -->

<li id="plan">
<h1>計画
<%= link_to_remote "create", :url => {:action => "new"} %>
</h1>
<ul id="plan_tasks">
<% for task in @plan_tasks do %> <%= render_partial "task", task %> <% end %>
</ul>
</li>
<li id="plan_to_do"/>

<li id="do">
<h1>実行</h1>
<ul id="do_tasks">
<% for task in @do_tasks do %> <%= render_partial "task", task %> <% end %>
</ul>
</li>
<li id="do_to_look_back"/>

<li id="look_back">
<h1>振り返り</h1>
<ul id="look_back_tasks">
<% for task in @look_back_tasks do %> <%= render_partial "task", task %> <% end %>
</ul>
</li>
<li id="look_back_to_done"/>

<li id="done">
<h1>完了</h1>
<ul id="done_tasks">
<% for task in @done_tasks do %> <%= render_partial "task", task %> <% end %>
</ul>
</li>
</ul>

<%= sortable_all_tasks %>

#
# View: task.rhtml
#
<% date = task.deadline_on
notify = notify_task(task)

  • %>

<li id="task_<%=h task.id %>" class="task <%=h notify %>"
title="<%=h task.description %>"
ondblclick="<%= remote_function :url => {:action => 'edit', :id => task.id} %>">
<div class="commands">
<%= link_to_remote "del", :url => {:action => "destroy", :id => task.id} %>
</div>
<div class="deadline_on">
<% unless date.year == Date.today.year %>
<%= date.year %>/
<% end -%>
<%= date.month %>/<%= date.day %>
までに</div>
<div class="title"><%= task.title %></div>
</li>

#
# View: form.rhtml
#
<% url = (task.new_record? ? {:action => "create"} : {:action => "update", :id => task.id}) -%>
<% submit_name = (task.new_record? ? "save" : "change") -%>

<% form_remote_for :task, task, :url => url, :html => {:id => "form"} do |t| %>
<%= submit_tag submit_name %>
<%= link_to_function "cancel", "Element.hide('form');Element.show('plan_tasks','do_tasks','look_back_tasks','done_tasks')" %>
<div id="form_error"></div>
<table>
<colgroup align="left" valign="top"/>
<colgroup align="left"/>
<tr>
<td>タイトル</td>
<td><%= t.text_field :title, :size => 40, :id => "initial_field" %></td>
</tr>
<tr>
<td>期日<%= task.deadline_on%></td>
<td><%= t.text_field :deadline_on, :size => 10 %>(yy-mm-dd)
</td>
</tr>
<tr>
<td>詳細</td>
<td><%= t.text_area :description, :cols => 30, :rows => 4 %></td>
</tr>
</table>
<%= javascript_tag "Field.focus('initial_field')" %>
<% end %>