inner_plan 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +28 -0
- data/Rakefile +8 -0
- data/app/assets/config/inner_plan_manifest.js +6 -0
- data/app/assets/stylesheets/inner_plan/application.css +24 -0
- data/app/components/application_view.rb +4 -0
- data/app/components/inner_plan/breadcrumbs_component.rb +26 -0
- data/app/components/inner_plan/groups/edit_view.rb +72 -0
- data/app/components/inner_plan/groups/new_view.rb +66 -0
- data/app/components/inner_plan/groups/row/handle_component.rb +30 -0
- data/app/components/inner_plan/groups/row_component.rb +31 -0
- data/app/components/inner_plan/groups/rows_component.rb +17 -0
- data/app/components/inner_plan/groups/show_menu_component.rb +30 -0
- data/app/components/inner_plan/groups/show_view.rb +77 -0
- data/app/components/inner_plan/lists/completed_tasks_component.rb +30 -0
- data/app/components/inner_plan/lists/edit_view.rb +62 -0
- data/app/components/inner_plan/lists/index_view.rb +57 -0
- data/app/components/inner_plan/lists/new_view.rb +49 -0
- data/app/components/inner_plan/lists/ongoing_tasks_component.rb +17 -0
- data/app/components/inner_plan/lists/row/handle_component.rb +37 -0
- data/app/components/inner_plan/lists/row_component.rb +55 -0
- data/app/components/inner_plan/lists/show_menu_component.rb +37 -0
- data/app/components/inner_plan/lists/show_view.rb +76 -0
- data/app/components/inner_plan/progress_bar_separator_component.rb +20 -0
- data/app/components/inner_plan/tasks/completed_toggler_component.rb +27 -0
- data/app/components/inner_plan/tasks/edit_view.rb +82 -0
- data/app/components/inner_plan/tasks/form/item_component.rb +21 -0
- data/app/components/inner_plan/tasks/form/row_component.rb +19 -0
- data/app/components/inner_plan/tasks/inline_form_view.rb +62 -0
- data/app/components/inner_plan/tasks/row/addons_component.rb +22 -0
- data/app/components/inner_plan/tasks/row/base_addon_component.rb +19 -0
- data/app/components/inner_plan/tasks/row/description_addon_component.rb +13 -0
- data/app/components/inner_plan/tasks/row/due_on_addon_component.rb +14 -0
- data/app/components/inner_plan/tasks/row/group_addon_component.rb +11 -0
- data/app/components/inner_plan/tasks/row/handle_component.rb +40 -0
- data/app/components/inner_plan/tasks/row_component.rb +35 -0
- data/app/components/inner_plan/tasks/rows_component.rb +22 -0
- data/app/components/inner_plan/tasks/show/item_component.rb +20 -0
- data/app/components/inner_plan/tasks/show/row_component.rb +19 -0
- data/app/components/inner_plan/tasks/show_menu_component.rb +30 -0
- data/app/components/inner_plan/tasks/show_view.rb +76 -0
- data/app/components/inner_plan/user_with_avatar_component.rb +17 -0
- data/app/concepts/inner_plan/list/operation/index.rb +11 -0
- data/app/concepts/inner_plan/task/operation/complete.rb +12 -0
- data/app/concepts/inner_plan/task/operation/create.rb +46 -0
- data/app/concepts/inner_plan/task/operation/reopen.rb +12 -0
- data/app/concepts/inner_plan/task/operation/update.rb +25 -0
- data/app/concepts/inner_plan/task/operation/update_position.rb +25 -0
- data/app/controllers/inner_plan/application_controller.rb +12 -0
- data/app/controllers/inner_plan/groups_controller.rb +59 -0
- data/app/controllers/inner_plan/lists_controller.rb +59 -0
- data/app/controllers/inner_plan/tasks_controller.rb +73 -0
- data/app/helpers/inner_plan/application_helper.rb +4 -0
- data/app/javascript/inner_plan/application.js +15 -0
- data/app/javascript/inner_plan/controllers/groups_controller.js +47 -0
- data/app/javascript/inner_plan/controllers/lists_controller.js +30 -0
- data/app/javascript/inner_plan/controllers/task_inline_form_controller.js +40 -0
- data/app/javascript/inner_plan/controllers/tasks_controller.js +47 -0
- data/app/jobs/inner_plan/application_job.rb +4 -0
- data/app/mailers/inner_plan/application_mailer.rb +6 -0
- data/app/models/inner_plan/application_record.rb +5 -0
- data/app/models/inner_plan/list.rb +29 -0
- data/app/models/inner_plan/task.rb +36 -0
- data/app/services/inner_plan/tasks/description_renderer.rb +18 -0
- data/app/services/inner_plan/tasks/title_renderer.rb +18 -0
- data/app/views/inner_plan/shared/_blankslate.svg.erb +1 -0
- data/app/views/inner_plan/tasks/complete.turbo_stream.erb +16 -0
- data/app/views/inner_plan/tasks/create.turbo_stream.erb +11 -0
- data/app/views/inner_plan/tasks/create_failure.turbo_stream.erb +5 -0
- data/app/views/layouts/action_text/contents/_content.html.erb +3 -0
- data/app/views/layouts/inner_plan/application.html.erb +38 -0
- data/config/importmap.rb +8 -0
- data/config/routes.rb +17 -0
- data/db/migrate/20241205203943_create_inner_plan_tables.rb +37 -0
- data/db/seeds.rb +14 -0
- data/lib/inner_plan/configuration.rb +116 -0
- data/lib/inner_plan/engine.rb +22 -0
- data/lib/inner_plan/smart_array.rb +66 -0
- data/lib/inner_plan/version.rb +3 -0
- data/lib/inner_plan.rb +28 -0
- data/lib/tasks/inner_plan_tasks.rake +4 -0
- metadata +265 -0
@@ -0,0 +1,30 @@
|
|
1
|
+
module InnerPlan::Tasks
|
2
|
+
class ShowMenuComponent < Phlex::HTML
|
3
|
+
include Phlex::Rails::Helpers::LinkTo
|
4
|
+
|
5
|
+
def initialize(task)
|
6
|
+
@task = task
|
7
|
+
end
|
8
|
+
|
9
|
+
def template
|
10
|
+
div(class: "dropdown") {
|
11
|
+
button(class: "btn btn-link text-reset p-0 opacity-50", data_bs_toggle: "dropdown") {
|
12
|
+
render(Phlex::Icons::Tabler::DotsCircleHorizontal.new(width: 35, height: 35))
|
13
|
+
}
|
14
|
+
|
15
|
+
ul(class: "dropdown-menu dropdown-menu-end") {
|
16
|
+
li {
|
17
|
+
link_to(helpers.edit_task_path(@task), class: 'dropdown-item d-flex align-items-center gap-2', data: { turbo_frame: :_top }) {
|
18
|
+
render(Phlex::Icons::Tabler::Pencil.new(width: 18, height: 18, class: 'text-secondary'))
|
19
|
+
span { "Edit task" }
|
20
|
+
}
|
21
|
+
}
|
22
|
+
}
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
attr_reader :task
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module InnerPlan::Tasks
|
2
|
+
class ShowView < ApplicationView
|
3
|
+
include Phlex::Rails::Helpers::ClassNames
|
4
|
+
include Phlex::Rails::Helpers::LinkTo
|
5
|
+
include Phlex::Rails::Helpers::Routes
|
6
|
+
|
7
|
+
attr_reader :task
|
8
|
+
|
9
|
+
def initialize(task:)
|
10
|
+
@task = task
|
11
|
+
end
|
12
|
+
|
13
|
+
def template
|
14
|
+
header(class: "mb-3") do
|
15
|
+
render(InnerPlan::BreadcrumbsComponent.new) do |c|
|
16
|
+
c.with_breadcrumb do
|
17
|
+
link_to "Home", helpers.root_path, class: "text-reset", data: { turbo_frame: :_top }
|
18
|
+
end
|
19
|
+
|
20
|
+
if @task.list.list.present?
|
21
|
+
c.with_breadcrumb do
|
22
|
+
link_to @task.list.list.title, @task.list.list, class: "text-reset"
|
23
|
+
end
|
24
|
+
c.with_breadcrumb do
|
25
|
+
link_to @task.list.title, helpers.group_path(@task.list), class: "text-reset"
|
26
|
+
end
|
27
|
+
else
|
28
|
+
c.with_breadcrumb do
|
29
|
+
link_to @task.list.title, @task.list, class: "text-reset"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
c.with_breadcrumb(active: true) { "Task ##{@task.id}" }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
div(class: "d-flex mb-2") do
|
38
|
+
div(class: "text-end me-2") do
|
39
|
+
div(style: "display:block;margin-top:-0.25rem;margin-left:-0.8rem") do
|
40
|
+
render(
|
41
|
+
InnerPlan::Tasks::CompletedTogglerComponent.new(
|
42
|
+
@task,
|
43
|
+
context: :tasks_show,
|
44
|
+
size: 39
|
45
|
+
)
|
46
|
+
)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
div(class: "w-100") do
|
50
|
+
h1(class: "h3") do
|
51
|
+
link_to @task.title,
|
52
|
+
helpers.edit_task_path(@task, focus: :title),
|
53
|
+
class: "text-reset text-decoration-none"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
div(class: "text-end") do
|
57
|
+
render(InnerPlan::Tasks::ShowMenuComponent.new(@task))
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
InnerPlan.configuration.task_show_view_rows.each do |row|
|
62
|
+
render InnerPlan::Tasks::Form::RowComponent.new do |component|
|
63
|
+
row.content.each do |item|
|
64
|
+
component.with_column(span: item.options[:span]) do
|
65
|
+
render(item.content.call(context: self))
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def description
|
73
|
+
InnerPlan::Tasks::DescriptionRenderer.call(task: @task)[:description]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module InnerPlan
|
2
|
+
class UserWithAvatarComponent < Phlex::HTML
|
3
|
+
include Phlex::Rails::Helpers::ImageTag
|
4
|
+
|
5
|
+
def initialize(user, avatar_size: 20)
|
6
|
+
@user = user
|
7
|
+
@avatar_size = avatar_size
|
8
|
+
end
|
9
|
+
|
10
|
+
def template
|
11
|
+
image_tag(@user.inner_plan_avatar_url, size: @avatar_size,
|
12
|
+
class: 'rounded-circle me-1',
|
13
|
+
style: 'margin-top:-0.1rem')
|
14
|
+
plain @user.inner_plan_to_s
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module InnerPlan::Task::Operation
|
2
|
+
class Create < Trailblazer::Operation
|
3
|
+
step :find_list
|
4
|
+
step :initialize_model
|
5
|
+
step :assign_list
|
6
|
+
step :assign_user
|
7
|
+
step :assign_position
|
8
|
+
left :validate_model
|
9
|
+
step :save_model
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def find_list(ctx, list_id:, **)
|
14
|
+
ctx[:list] = InnerPlan::List.find(list_id)
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize_model(ctx, params:, **)
|
18
|
+
model = InnerPlan::Task.new
|
19
|
+
model.title = params[:title]
|
20
|
+
model.description = params[:description]
|
21
|
+
model.due_on = params[:due_on]
|
22
|
+
|
23
|
+
ctx[:model] = model
|
24
|
+
end
|
25
|
+
|
26
|
+
def assign_list(ctx, list:, model:, **)
|
27
|
+
model.list = list
|
28
|
+
end
|
29
|
+
|
30
|
+
def assign_position(ctx, model:, **)
|
31
|
+
model.position = :last
|
32
|
+
end
|
33
|
+
|
34
|
+
def assign_user(ctx, current_user:, model:, **)
|
35
|
+
model.user = current_user
|
36
|
+
end
|
37
|
+
|
38
|
+
def validate_model(ctx, model:, **)
|
39
|
+
model.validate
|
40
|
+
end
|
41
|
+
|
42
|
+
def save_model(_ctx, model:, **)
|
43
|
+
model.save
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module InnerPlan::Task::Operation
|
2
|
+
class Update < Trailblazer::Operation
|
3
|
+
step Model::Find(InnerPlan::Task, find_by: :id)
|
4
|
+
step :assign_attributes
|
5
|
+
left :validate_model
|
6
|
+
step :update_model
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def assign_attributes(ctx, model:, params:, **)
|
11
|
+
model.title = params[:title]
|
12
|
+
model.description = params[:description]
|
13
|
+
model.due_on = params[:due_on]
|
14
|
+
true
|
15
|
+
end
|
16
|
+
|
17
|
+
def validate_model(ctx, model:, **)
|
18
|
+
model.validate
|
19
|
+
end
|
20
|
+
|
21
|
+
def update_model(ctx, model:, **)
|
22
|
+
model.save
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module InnerPlan::Task::Operation
|
2
|
+
class UpdatePosition < Trailblazer::Operation
|
3
|
+
step Model::Find(InnerPlan::Task, find_by: :id)
|
4
|
+
step :assign_position
|
5
|
+
step :assign_list
|
6
|
+
step :save
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def assign_position(ctx, params:, model:, **)
|
11
|
+
model.position = { before: params.dig(:task, :position, :before) }
|
12
|
+
end
|
13
|
+
|
14
|
+
def assign_list(ctx, params:, model:, **)
|
15
|
+
list_id = params.dig(:task, :list_id)
|
16
|
+
return true unless list_id
|
17
|
+
|
18
|
+
model.list = InnerPlan::List.find(list_id)
|
19
|
+
end
|
20
|
+
|
21
|
+
def save(ctx, model:, **)
|
22
|
+
model.save
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module InnerPlan
|
2
|
+
class ApplicationController < ActionController::Base
|
3
|
+
helper_method :current_inner_plan_user
|
4
|
+
|
5
|
+
private
|
6
|
+
|
7
|
+
def current_inner_plan_user
|
8
|
+
@current_inner_plan_user ||=
|
9
|
+
public_send(InnerPlan.configuration.current_user_method)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module InnerPlan
|
2
|
+
class GroupsController < ApplicationController
|
3
|
+
def show
|
4
|
+
@group = InnerPlan::List.sub.find(params[:id])
|
5
|
+
render InnerPlan::Groups::ShowView.new(group: @group)
|
6
|
+
end
|
7
|
+
|
8
|
+
def edit
|
9
|
+
@group = InnerPlan::List.sub.find(params[:id])
|
10
|
+
render InnerPlan::Groups::EditView.new(group: @group)
|
11
|
+
end
|
12
|
+
|
13
|
+
def update
|
14
|
+
@group = InnerPlan::List.sub.find(params[:id])
|
15
|
+
|
16
|
+
if @group.update(group_params)
|
17
|
+
redirect_to group_path(@group)
|
18
|
+
else
|
19
|
+
render :edit, status: :unprocessable_entity
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def new
|
24
|
+
@list = InnerPlan::List.root.find(params[:list_id])
|
25
|
+
@group = @list.lists.new(params[:id])
|
26
|
+
render InnerPlan::Groups::NewView.new(group: @group)
|
27
|
+
end
|
28
|
+
|
29
|
+
def create
|
30
|
+
@group = InnerPlan::List.new(group_params)
|
31
|
+
@group.list = InnerPlan::List.root.find(params[:list_id])
|
32
|
+
@group.user = current_inner_plan_user
|
33
|
+
@group.position = :last
|
34
|
+
|
35
|
+
if @group.save
|
36
|
+
redirect_to @group.list
|
37
|
+
else
|
38
|
+
render :new, status: :unprocessable_entity
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def update_position
|
43
|
+
@group = InnerPlan::List.sub.find(params[:id])
|
44
|
+
@group.position = { before: update_positions_params[:position][:before] }
|
45
|
+
@group.list = InnerPlan::List.root.find(update_positions_params[:list_id]) if update_positions_params[:list_id]
|
46
|
+
@group.save!
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def group_params
|
52
|
+
params.require(:list).permit(:title, :description)
|
53
|
+
end
|
54
|
+
|
55
|
+
def update_positions_params
|
56
|
+
params.require(:list).permit(:list_id, :position, position: :before)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module InnerPlan
|
2
|
+
class ListsController < ApplicationController
|
3
|
+
def index
|
4
|
+
@lists = InnerPlan::List::Operation::Index.call[:models]
|
5
|
+
render InnerPlan::Lists::IndexView.new(lists: @lists)
|
6
|
+
end
|
7
|
+
|
8
|
+
def show
|
9
|
+
@list = InnerPlan::List.root.find(params[:id])
|
10
|
+
render InnerPlan::Lists::ShowView.new(list: @list)
|
11
|
+
end
|
12
|
+
|
13
|
+
def new
|
14
|
+
@list = InnerPlan::List.new
|
15
|
+
render InnerPlan::Lists::NewView.new(list: @list)
|
16
|
+
end
|
17
|
+
|
18
|
+
def create
|
19
|
+
@list = InnerPlan::List.new(list_params)
|
20
|
+
@list.user = current_inner_plan_user
|
21
|
+
|
22
|
+
if @list.save
|
23
|
+
redirect_to lists_path
|
24
|
+
else
|
25
|
+
render InnerPlan::Lists::NewView.new(list: @list), status: :unprocessable_entity
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def edit
|
30
|
+
@list = InnerPlan::List.root.find(params[:id])
|
31
|
+
render InnerPlan::Lists::EditView.new(list: @list)
|
32
|
+
end
|
33
|
+
|
34
|
+
def update
|
35
|
+
@list = InnerPlan::List.root.find(params[:id])
|
36
|
+
if @list.update(list_params)
|
37
|
+
redirect_to list_path(@list)
|
38
|
+
else
|
39
|
+
render InnerPlan::Lists::EditView.new(list: @list)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def update_position
|
44
|
+
@list = InnerPlan::List.root.find(params[:id])
|
45
|
+
@list.position = { before: update_positions_params[:position][:before] }
|
46
|
+
@list.save!
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def list_params
|
52
|
+
params.require(:list).permit(:title, :description)
|
53
|
+
end
|
54
|
+
|
55
|
+
def update_positions_params
|
56
|
+
params.require(:list).permit(:position, position: :before)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module InnerPlan
|
2
|
+
class TasksController < ApplicationController
|
3
|
+
def index
|
4
|
+
@lists = InnerPlan::List::Operation::Index.call
|
5
|
+
end
|
6
|
+
|
7
|
+
def new
|
8
|
+
@task = InnerPlan::Task.new
|
9
|
+
@list = InnerPlan::List.find(params[:list_id])
|
10
|
+
end
|
11
|
+
|
12
|
+
def show
|
13
|
+
@task = InnerPlan::Task.find(params[:id])
|
14
|
+
render InnerPlan::Tasks::ShowView.new(task: @task)
|
15
|
+
end
|
16
|
+
|
17
|
+
def create
|
18
|
+
result = InnerPlan::Task::Operation::Create.call(
|
19
|
+
list_id: params[:list_id],
|
20
|
+
current_user: current_inner_plan_user,
|
21
|
+
params: params.fetch(:task, {})
|
22
|
+
)
|
23
|
+
@task, @list = result[:model], result[:list]
|
24
|
+
|
25
|
+
if result.success?
|
26
|
+
@new_task = InnerPlan::Task.new
|
27
|
+
else
|
28
|
+
render :create_failure, status: :unprocessable_entity
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def edit
|
33
|
+
@task = InnerPlan::Task.find(params[:id])
|
34
|
+
render InnerPlan::Tasks::EditView.new(task: @task, focus: params[:focus])
|
35
|
+
end
|
36
|
+
|
37
|
+
def update
|
38
|
+
result = InnerPlan::Task::Operation::Update.call(
|
39
|
+
params: params.fetch(:task, {}).merge(id: params[:id])
|
40
|
+
)
|
41
|
+
@task = result[:model]
|
42
|
+
|
43
|
+
if result.success?
|
44
|
+
redirect_to task_path(@task)
|
45
|
+
else
|
46
|
+
render :edit, status: :unprocessable_entity
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def update_position
|
51
|
+
result = InnerPlan::Task::Operation::UpdatePosition.call(params: params)
|
52
|
+
@task = result[:model]
|
53
|
+
end
|
54
|
+
|
55
|
+
def complete
|
56
|
+
result = InnerPlan::Task::Operation::Complete.call(params: params)
|
57
|
+
@task = result[:model]
|
58
|
+
|
59
|
+
respond_to do |format|
|
60
|
+
format.turbo_stream
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def reopen
|
65
|
+
result = InnerPlan::Task::Operation::Reopen.call(params: params)
|
66
|
+
@task = result[:model]
|
67
|
+
|
68
|
+
respond_to do |format|
|
69
|
+
format.turbo_stream { render :complete }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
import { Application } from "@hotwired/stimulus"
|
2
|
+
import * as bootstrap from 'bootstrap'
|
3
|
+
|
4
|
+
import ListsController from "controllers/lists_controller"
|
5
|
+
import GroupsController from "controllers/groups_controller"
|
6
|
+
import TasksController from "controllers/tasks_controller"
|
7
|
+
import TaskInlineFormController from "controllers/task_inline_form_controller"
|
8
|
+
|
9
|
+
// Stimulus
|
10
|
+
window.Stimulus = Application.start()
|
11
|
+
|
12
|
+
Stimulus.register("lists", ListsController)
|
13
|
+
Stimulus.register("groups", GroupsController)
|
14
|
+
Stimulus.register("tasks", TasksController)
|
15
|
+
Stimulus.register("task-inline-form", TaskInlineFormController)
|
@@ -0,0 +1,47 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
2
|
+
import Sortable from "sortablejs"
|
3
|
+
import { patch } from "@rails/request.js"
|
4
|
+
|
5
|
+
export default class extends Controller {
|
6
|
+
static values = {
|
7
|
+
listId: Number
|
8
|
+
};
|
9
|
+
|
10
|
+
connect() {
|
11
|
+
this.sortable = Sortable.create(this.element, {
|
12
|
+
handle: '.group-handle',
|
13
|
+
group: 'group',
|
14
|
+
onUpdate: this.onUpdate.bind(this),
|
15
|
+
onAdd: this.onAdd.bind(this),
|
16
|
+
})
|
17
|
+
}
|
18
|
+
|
19
|
+
destroy() {
|
20
|
+
this.sortable.destroy();
|
21
|
+
}
|
22
|
+
|
23
|
+
onAdd({ item }) {
|
24
|
+
const nextGroupEl = item.nextElementSibling;
|
25
|
+
const nextGroupId = nextGroupEl ? nextGroupEl.dataset.id : null
|
26
|
+
|
27
|
+
patch(item.dataset.updateUrl, {
|
28
|
+
body: {
|
29
|
+
list: {
|
30
|
+
list_id: this.listIdValue,
|
31
|
+
position: { before: nextGroupId }
|
32
|
+
}
|
33
|
+
}
|
34
|
+
})
|
35
|
+
}
|
36
|
+
|
37
|
+
onUpdate({ item }) {
|
38
|
+
const nextGroupEl = item.nextElementSibling;
|
39
|
+
const nextGroupId = nextGroupEl ? nextGroupEl.dataset.id : null
|
40
|
+
|
41
|
+
patch(item.dataset.updateUrl, {
|
42
|
+
body: {
|
43
|
+
list: { position: { before: nextGroupId } }
|
44
|
+
}
|
45
|
+
})
|
46
|
+
}
|
47
|
+
}
|
@@ -0,0 +1,30 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
2
|
+
import Sortable from "sortablejs"
|
3
|
+
import { patch } from "@rails/request.js"
|
4
|
+
|
5
|
+
export default class extends Controller {
|
6
|
+
static targets = ['list'];
|
7
|
+
|
8
|
+
connect() {
|
9
|
+
this.sortable = Sortable.create(this.element, {
|
10
|
+
handle: '.list-handle',
|
11
|
+
group: 'list',
|
12
|
+
onUpdate: this.onUpdate.bind(this)
|
13
|
+
})
|
14
|
+
}
|
15
|
+
|
16
|
+
destroy() {
|
17
|
+
this.sortable.destroy();
|
18
|
+
}
|
19
|
+
|
20
|
+
onUpdate({ item }) {
|
21
|
+
const nextListEl = item.nextElementSibling;
|
22
|
+
const nextListId = nextListEl ? nextListEl.dataset.id : null
|
23
|
+
|
24
|
+
patch(item.dataset.updateUrl, {
|
25
|
+
body: {
|
26
|
+
list: { position: { before: nextListId } }
|
27
|
+
}
|
28
|
+
})
|
29
|
+
}
|
30
|
+
}
|
@@ -0,0 +1,40 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static targets = ['form', 'toggler', 'defaultInput']
|
5
|
+
|
6
|
+
static values = {
|
7
|
+
formVisible: {
|
8
|
+
type: Boolean,
|
9
|
+
default: false
|
10
|
+
}
|
11
|
+
}
|
12
|
+
|
13
|
+
formVisibleValueChanged(value) {
|
14
|
+
if (value) {
|
15
|
+
this.showForm()
|
16
|
+
} else {
|
17
|
+
this.hideForm()
|
18
|
+
}
|
19
|
+
}
|
20
|
+
|
21
|
+
escPressed() {
|
22
|
+
this.formVisibleValue = false
|
23
|
+
}
|
24
|
+
|
25
|
+
togglerClicked(e) {
|
26
|
+
e.preventDefault()
|
27
|
+
this.formVisibleValue = true
|
28
|
+
}
|
29
|
+
|
30
|
+
showForm() {
|
31
|
+
this.formTarget.classList.remove('d-none')
|
32
|
+
this.togglerTarget.classList.add('d-none')
|
33
|
+
this.defaultInputTarget.focus()
|
34
|
+
}
|
35
|
+
|
36
|
+
hideForm() {
|
37
|
+
this.formTarget.classList.add('d-none')
|
38
|
+
this.togglerTarget.classList.remove('d-none')
|
39
|
+
}
|
40
|
+
}
|
@@ -0,0 +1,47 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
2
|
+
import Sortable from "sortablejs"
|
3
|
+
import { patch } from "@rails/request.js"
|
4
|
+
|
5
|
+
export default class extends Controller {
|
6
|
+
static values = {
|
7
|
+
listId: Number
|
8
|
+
};
|
9
|
+
|
10
|
+
connect() {
|
11
|
+
this.sortable = Sortable.create(this.element, {
|
12
|
+
handle: '.task-handle',
|
13
|
+
group: 'task',
|
14
|
+
onUpdate: this.onUpdate.bind(this),
|
15
|
+
onAdd: this.onAdd.bind(this),
|
16
|
+
})
|
17
|
+
}
|
18
|
+
|
19
|
+
destroy() {
|
20
|
+
this.sortable.destroy();
|
21
|
+
}
|
22
|
+
|
23
|
+
onAdd({ item }) {
|
24
|
+
const nextTaskEl = item.nextElementSibling;
|
25
|
+
const nextTaskId = nextTaskEl ? nextTaskEl.dataset.id : null
|
26
|
+
|
27
|
+
patch(item.dataset.updateUrl, {
|
28
|
+
body: {
|
29
|
+
task: {
|
30
|
+
list_id: this.listIdValue,
|
31
|
+
position: { before: nextTaskId }
|
32
|
+
}
|
33
|
+
}
|
34
|
+
})
|
35
|
+
}
|
36
|
+
|
37
|
+
onUpdate({ item }) {
|
38
|
+
const nextTaskEl = item.nextElementSibling;
|
39
|
+
const nextTaskId = nextTaskEl ? nextTaskEl.dataset.id : null
|
40
|
+
|
41
|
+
patch(item.dataset.updateUrl, {
|
42
|
+
body: {
|
43
|
+
task: { position: { before: nextTaskId } }
|
44
|
+
}
|
45
|
+
})
|
46
|
+
}
|
47
|
+
}
|