inner_plan 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +28 -0
  3. data/Rakefile +8 -0
  4. data/app/assets/config/inner_plan_manifest.js +6 -0
  5. data/app/assets/stylesheets/inner_plan/application.css +24 -0
  6. data/app/components/application_view.rb +4 -0
  7. data/app/components/inner_plan/breadcrumbs_component.rb +26 -0
  8. data/app/components/inner_plan/groups/edit_view.rb +72 -0
  9. data/app/components/inner_plan/groups/new_view.rb +66 -0
  10. data/app/components/inner_plan/groups/row/handle_component.rb +30 -0
  11. data/app/components/inner_plan/groups/row_component.rb +31 -0
  12. data/app/components/inner_plan/groups/rows_component.rb +17 -0
  13. data/app/components/inner_plan/groups/show_menu_component.rb +30 -0
  14. data/app/components/inner_plan/groups/show_view.rb +77 -0
  15. data/app/components/inner_plan/lists/completed_tasks_component.rb +30 -0
  16. data/app/components/inner_plan/lists/edit_view.rb +62 -0
  17. data/app/components/inner_plan/lists/index_view.rb +57 -0
  18. data/app/components/inner_plan/lists/new_view.rb +49 -0
  19. data/app/components/inner_plan/lists/ongoing_tasks_component.rb +17 -0
  20. data/app/components/inner_plan/lists/row/handle_component.rb +37 -0
  21. data/app/components/inner_plan/lists/row_component.rb +55 -0
  22. data/app/components/inner_plan/lists/show_menu_component.rb +37 -0
  23. data/app/components/inner_plan/lists/show_view.rb +76 -0
  24. data/app/components/inner_plan/progress_bar_separator_component.rb +20 -0
  25. data/app/components/inner_plan/tasks/completed_toggler_component.rb +27 -0
  26. data/app/components/inner_plan/tasks/edit_view.rb +82 -0
  27. data/app/components/inner_plan/tasks/form/item_component.rb +21 -0
  28. data/app/components/inner_plan/tasks/form/row_component.rb +19 -0
  29. data/app/components/inner_plan/tasks/inline_form_view.rb +62 -0
  30. data/app/components/inner_plan/tasks/row/addons_component.rb +22 -0
  31. data/app/components/inner_plan/tasks/row/base_addon_component.rb +19 -0
  32. data/app/components/inner_plan/tasks/row/description_addon_component.rb +13 -0
  33. data/app/components/inner_plan/tasks/row/due_on_addon_component.rb +14 -0
  34. data/app/components/inner_plan/tasks/row/group_addon_component.rb +11 -0
  35. data/app/components/inner_plan/tasks/row/handle_component.rb +40 -0
  36. data/app/components/inner_plan/tasks/row_component.rb +35 -0
  37. data/app/components/inner_plan/tasks/rows_component.rb +22 -0
  38. data/app/components/inner_plan/tasks/show/item_component.rb +20 -0
  39. data/app/components/inner_plan/tasks/show/row_component.rb +19 -0
  40. data/app/components/inner_plan/tasks/show_menu_component.rb +30 -0
  41. data/app/components/inner_plan/tasks/show_view.rb +76 -0
  42. data/app/components/inner_plan/user_with_avatar_component.rb +17 -0
  43. data/app/concepts/inner_plan/list/operation/index.rb +11 -0
  44. data/app/concepts/inner_plan/task/operation/complete.rb +12 -0
  45. data/app/concepts/inner_plan/task/operation/create.rb +46 -0
  46. data/app/concepts/inner_plan/task/operation/reopen.rb +12 -0
  47. data/app/concepts/inner_plan/task/operation/update.rb +25 -0
  48. data/app/concepts/inner_plan/task/operation/update_position.rb +25 -0
  49. data/app/controllers/inner_plan/application_controller.rb +12 -0
  50. data/app/controllers/inner_plan/groups_controller.rb +59 -0
  51. data/app/controllers/inner_plan/lists_controller.rb +59 -0
  52. data/app/controllers/inner_plan/tasks_controller.rb +73 -0
  53. data/app/helpers/inner_plan/application_helper.rb +4 -0
  54. data/app/javascript/inner_plan/application.js +15 -0
  55. data/app/javascript/inner_plan/controllers/groups_controller.js +47 -0
  56. data/app/javascript/inner_plan/controllers/lists_controller.js +30 -0
  57. data/app/javascript/inner_plan/controllers/task_inline_form_controller.js +40 -0
  58. data/app/javascript/inner_plan/controllers/tasks_controller.js +47 -0
  59. data/app/jobs/inner_plan/application_job.rb +4 -0
  60. data/app/mailers/inner_plan/application_mailer.rb +6 -0
  61. data/app/models/inner_plan/application_record.rb +5 -0
  62. data/app/models/inner_plan/list.rb +29 -0
  63. data/app/models/inner_plan/task.rb +36 -0
  64. data/app/services/inner_plan/tasks/description_renderer.rb +18 -0
  65. data/app/services/inner_plan/tasks/title_renderer.rb +18 -0
  66. data/app/views/inner_plan/shared/_blankslate.svg.erb +1 -0
  67. data/app/views/inner_plan/tasks/complete.turbo_stream.erb +16 -0
  68. data/app/views/inner_plan/tasks/create.turbo_stream.erb +11 -0
  69. data/app/views/inner_plan/tasks/create_failure.turbo_stream.erb +5 -0
  70. data/app/views/layouts/action_text/contents/_content.html.erb +3 -0
  71. data/app/views/layouts/inner_plan/application.html.erb +38 -0
  72. data/config/importmap.rb +8 -0
  73. data/config/routes.rb +17 -0
  74. data/db/migrate/20241205203943_create_inner_plan_tables.rb +37 -0
  75. data/db/seeds.rb +14 -0
  76. data/lib/inner_plan/configuration.rb +116 -0
  77. data/lib/inner_plan/engine.rb +22 -0
  78. data/lib/inner_plan/smart_array.rb +66 -0
  79. data/lib/inner_plan/version.rb +3 -0
  80. data/lib/inner_plan.rb +28 -0
  81. data/lib/tasks/inner_plan_tasks.rake +4 -0
  82. metadata +265 -0
@@ -0,0 +1,37 @@
1
+ module InnerPlan::Lists::Row
2
+ class HandleComponent < Phlex::HTML
3
+ include Phlex::Rails::Helpers::LinkTo
4
+
5
+ def initialize(list)
6
+ @list = list
7
+ end
8
+
9
+ def template
10
+ div(class: "dropstart", style: "padding-top:1.5rem") {
11
+ a(class: "align-top me-2 text-body-tertiary list-handle cm", data: { bs_toggle: :dropdown }) {
12
+ render(Phlex::Icons::Tabler::GripVertical.new(width: 20, height: 20))
13
+ }
14
+
15
+ ul(class: "dropdown-menu") {
16
+ li {
17
+ link_to(helpers.edit_list_path(list), class: "dropdown-item d-flex gap-2 align-items-center") {
18
+ render(Phlex::Icons::Tabler::Pencil.new(width: 18, height: 18, class: 'text-secondary'))
19
+ span { 'Edit list' }
20
+ }
21
+ }
22
+
23
+ li {
24
+ link_to(helpers.new_list_group_path(list), class: "dropdown-item d-flex gap-2 align-items-center") {
25
+ render(Phlex::Icons::Tabler::Apps.new(width: 18, height: 18, class: 'text-secondary'))
26
+ span { 'Add group' }
27
+ }
28
+ }
29
+ }
30
+ }
31
+ end
32
+
33
+ private
34
+
35
+ attr_reader :list
36
+ end
37
+ end
@@ -0,0 +1,55 @@
1
+ module InnerPlan::Lists
2
+ class RowComponent < Phlex::HTML
3
+ include Phlex::Rails::Helpers::LinkTo
4
+ include Phlex::Rails::Helpers::DOMID
5
+
6
+ def initialize(list, context: nil)
7
+ @list = list
8
+ @context = context
9
+ end
10
+
11
+ def template
12
+ lists = list.lists.ordered_by_position
13
+
14
+ div(data: { update_url: helpers.update_position_list_path(list), id: list.id }, class: "mb-5") {
15
+ div(class: "d-flex w-100 mb-1") {
16
+ render(InnerPlan::Lists::Row::HandleComponent.new(list))
17
+
18
+ div(class: "ms-1 w-100") {
19
+ small(class: "text-body-tertiary") {
20
+ plain("#{list.tasks.completed.count}/#{list.tasks.count} completed")
21
+ }
22
+
23
+ h3(class: "h5") {
24
+ link_to list.title, list, class: 'text-reset'
25
+ }
26
+
27
+ if list.description.present?
28
+ div(class: "mb-2") {
29
+ list.description
30
+ }
31
+ end
32
+ }
33
+ }
34
+
35
+ # Ongoing tasks
36
+ render(InnerPlan::Lists::OngoingTasksComponent.new(list, context: context))
37
+
38
+ # Groups
39
+ render(InnerPlan::Groups::RowsComponent.new(lists, list: list, context: context))
40
+
41
+ # Add new task form
42
+ last_group = lists.to_a.last || list
43
+ div(class: "mt-2 mb-4", id: dom_id(last_group, :add_task)) {
44
+ render InnerPlan::Tasks::InlineFormView.new(list: last_group)
45
+ }
46
+
47
+ render(InnerPlan::Lists::CompletedTasksComponent.new(list, context: context))
48
+ }
49
+ end
50
+
51
+ private
52
+
53
+ attr_reader :list, :context
54
+ end
55
+ end
@@ -0,0 +1,37 @@
1
+ module InnerPlan::Lists
2
+ class ShowMenuComponent < Phlex::HTML
3
+ include Phlex::Rails::Helpers::LinkTo
4
+
5
+ def initialize(list)
6
+ @list = list
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_list_path(list), 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 list" }
20
+ }
21
+ }
22
+
23
+ li {
24
+ link_to(helpers.new_list_group_path(list), class: 'dropdown-item d-flex align-items-center gap-2', data: { turbo_frame: :_top }) {
25
+ render(Phlex::Icons::Tabler::Apps.new(width: 18, height: 18, class: 'text-secondary'))
26
+ span { "Add group" }
27
+ }
28
+ }
29
+ }
30
+ }
31
+ end
32
+
33
+ private
34
+
35
+ attr_reader :list
36
+ end
37
+ end
@@ -0,0 +1,76 @@
1
+ module InnerPlan::Lists
2
+ class ShowView < ApplicationView
3
+ include Phlex::Rails::Helpers::DOMID
4
+ include Phlex::Rails::Helpers::LinkTo
5
+ include Phlex::Rails::Helpers::Routes
6
+ include Phlex::Rails::Helpers::ContentTag
7
+
8
+ def initialize(list:)
9
+ @list = list
10
+ end
11
+
12
+ def template
13
+ lists = @list.lists.ordered_by_position
14
+
15
+ content_tag(:turbo_frame, id: (dom_id(@list, :header))) do
16
+ header do
17
+ render(InnerPlan::BreadcrumbsComponent.new) do |c|
18
+ c.with_breadcrumb do
19
+ link_to "Home", helpers.root_path, class: "text-reset", data: { turbo_frame: :_top }
20
+ end
21
+ c.with_breadcrumb(active: true) { "List ##{@list.id}" }
22
+ end
23
+ div(class: "row") do
24
+ div(class: "col-10") do
25
+ h1(class: "h2") do
26
+ link_to @list.title, helpers.edit_list_path(@list), class: "text-reset text-decoration-none"
27
+ small(class: "fs-6 text-body-tertiary ms-2") do
28
+ plain @list.tasks.completed.count
29
+ plain "/"
30
+ plain @list.tasks.count
31
+ end
32
+ end
33
+ end
34
+ div(class: "col-2 text-end") do
35
+ render(InnerPlan::Lists::ShowMenuComponent.new(@list))
36
+ end
37
+ end
38
+ render(
39
+ InnerPlan::ProgressBarSeparatorComponent.new(
40
+ completed: @list.tasks.completed.count,
41
+ total: @list.tasks.count
42
+ )
43
+ )
44
+
45
+ div(class: "mb-4 mt-3") { @list.description }
46
+ end
47
+ end
48
+
49
+ render(
50
+ InnerPlan::Lists::OngoingTasksComponent.new(@list, context: :lists_show)
51
+ )
52
+
53
+ render(
54
+ InnerPlan::Groups::RowsComponent.new(
55
+ lists.ordered_by_position,
56
+ list: @list,
57
+ context: :lists_show
58
+ )
59
+ )
60
+
61
+ last_group = lists.to_a.last || @list
62
+
63
+ content_tag(:div, class: "mt-2 mb-4", id: dom_id(last_group, :add_task)) do
64
+ render InnerPlan::Tasks::InlineFormView.new(list: last_group)
65
+ end
66
+
67
+ render(
68
+ InnerPlan::Lists::CompletedTasksComponent.new(
69
+ @list,
70
+ context: :lists_show,
71
+ limit: 0
72
+ )
73
+ )
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,20 @@
1
+ module InnerPlan
2
+ class ProgressBarSeparatorComponent < Phlex::HTML
3
+ def initialize(completed: 0, total: 100)
4
+ @completed = completed
5
+ @total = total
6
+ end
7
+
8
+ def template
9
+ div(class: 'progress mt-2', style: 'height: 2px;') {
10
+ div(class: 'progress-bar bg-success', style: "width: #{completion_percent}%")
11
+ }
12
+ end
13
+
14
+ private
15
+
16
+ def completion_percent
17
+ @completed.to_f / @total.to_f * 100.0
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,27 @@
1
+ module InnerPlan::Tasks
2
+ class CompletedTogglerComponent < Phlex::HTML
3
+ include Phlex::Rails::Helpers::ButtonTo
4
+
5
+ def initialize(task, context: nil, size: 24)
6
+ @task = task
7
+ @context = context
8
+ @size = size
9
+ end
10
+
11
+ def template
12
+ if task.ongoing?
13
+ button_to(helpers.complete_task_path(task, context: context), method: :post, class: 'btn p-0 opacity-50', style: 'height:24px;display:block;margin-top:-1px', form: { style: 'height:24px;display:block;' }) {
14
+ render(Phlex::Icons::Tabler::SquareRounded.new(width: size, height: size))
15
+ }
16
+ else
17
+ button_to(helpers.reopen_task_path(task, context: context), method: :post, class: 'btn p-0 text-success opacity-75') {
18
+ render(Phlex::Icons::Tabler::SquareRoundedCheck.new(width: size, height: size))
19
+ }
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ attr_reader :task, :context, :size
26
+ end
27
+ end
@@ -0,0 +1,82 @@
1
+ module InnerPlan::Tasks
2
+ class EditView < ApplicationView
3
+ include Phlex::Rails::Helpers::DOMID
4
+ include Phlex::Rails::Helpers::LinkTo
5
+ include Phlex::Rails::Helpers::Routes
6
+ include Phlex::Rails::Helpers::ContentTag
7
+ include Phlex::Rails::Helpers::FormWith
8
+ include Phlex::Rails::Helpers::Debug
9
+
10
+ attr_reader :focus
11
+
12
+ def initialize(task:, focus: nil)
13
+ @task = task
14
+ @focus = focus&.to_sym
15
+ end
16
+
17
+ def template
18
+ header(class: "mb-3") do
19
+ render(InnerPlan::BreadcrumbsComponent.new) do |c|
20
+ c.with_breadcrumb do
21
+ link_to "Home",
22
+ helpers.root_path,
23
+ class: "text-reset",
24
+ data: {
25
+ turbo_frame: :_top
26
+ }
27
+ end
28
+ if @task.list.sub?
29
+ c.with_breadcrumb do
30
+ link_to @task.list.list.title, @task.list.list, class: "text-reset"
31
+ end
32
+ c.with_breadcrumb do
33
+ link_to @task.list.title, helpers.group_path(@task.list), class: "text-reset"
34
+ end
35
+ else
36
+ c.with_breadcrumb do
37
+ link_to @task.list.title, @task.list, class: "text-reset"
38
+ end
39
+ end
40
+ c.with_breadcrumb do
41
+ link_to "Task ##{@task.id}", @task, class: "text-reset"
42
+ end
43
+ c.with_breadcrumb(active: true) { "Edit" }
44
+ end
45
+ end
46
+
47
+ form_with model: @task do |f|
48
+ div(class: "d-flex mb-4") do
49
+ div(class: "text-end me-2 opacity-50") do
50
+ content_tag :a,
51
+ style:
52
+ "display:block; margin-top: -0.1rem;margin-left:-0.6rem",
53
+ data: {
54
+ turbo: true,
55
+ turbo_stream: true
56
+ } do
57
+ render(Phlex::Icons::Tabler::SquareRounded.new(width: 39, height: 39))
58
+ end
59
+ end
60
+ div(class: "w-100") do
61
+ plain f.text_field :title,
62
+ class: "form-control form-control-lg",
63
+ autofocus: (@focus == "title")
64
+ end
65
+ end
66
+
67
+ InnerPlan.configuration.task_edit_view_rows.each do |row|
68
+ render InnerPlan::Tasks::Form::RowComponent.new do |component|
69
+ row.content.each do |item|
70
+ component.with_column(span: item.options[:span]) do
71
+ render(item.content.call(context: self, form: f))
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+ plain f.submit "Save changes", class: "btn btn-success btn-sm me-2"
78
+ link_to "Cancel", @task, class: "btn btn-outline-secondary btn-sm"
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,21 @@
1
+ module InnerPlan::Tasks::Form
2
+ class ItemComponent < Phlex::HTML
3
+ def initialize(icon:, title: nil)
4
+ @icon = icon
5
+ @title = title
6
+ end
7
+
8
+ def template(&content)
9
+ div(class: "d-flex mb-4") do
10
+ div(class: "me-3 text-body-tertiary", style: "margin-top:-.2 rem") do
11
+ render(@icon.new(width: 24, height: 24))
12
+ end
13
+
14
+ div(class: "pe-4 w-100") do
15
+ h6 { @title }
16
+ render content
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,19 @@
1
+ module InnerPlan::Tasks::Form
2
+ class RowComponent < Phlex::HTML
3
+ def initialize
4
+ @columns = []
5
+ end
6
+
7
+ def template(&content)
8
+ div(class: "row") {
9
+ render content
10
+ }
11
+ end
12
+
13
+ def with_column(span: 6, &content)
14
+ div(class: "col-#{span}") {
15
+ render content
16
+ }
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,62 @@
1
+ module InnerPlan::Tasks
2
+ class InlineFormView < ApplicationView
3
+ include Phlex::Rails::Helpers::DOMID
4
+ include Phlex::Rails::Helpers::LinkTo
5
+ include Phlex::Rails::Helpers::Routes
6
+ include Phlex::Rails::Helpers::ContentTag
7
+ include Phlex::Rails::Helpers::FormWith
8
+
9
+ def initialize(task: InnerPlan::Task.new, list:, form_visible: false)
10
+ @task = task
11
+ @list = list
12
+ @form_visible = form_visible
13
+ end
14
+
15
+ def template
16
+ div(
17
+ data_controller: "task-inline-form",
18
+ data_task_inline_form_form_visible_value:
19
+ (@form_visible == true)
20
+ ) do
21
+ form_with model: @task,
22
+ url: helpers.list_tasks_path(@list),
23
+ class: ("d-none" if @form_visible != true),
24
+ data: {
25
+ task_inline_form_target: :form
26
+ } do |f|
27
+ div(class: "d-flex w-100 mb-1") do
28
+ div(
29
+ class: "align-top me-2 text-muted invisible",
30
+ style: "margin-top:-.05 rem"
31
+ ) do
32
+ render(Phlex::Icons::Tabler::GripVertical.new(width: 20, height: 20))
33
+ end
34
+ div(
35
+ class: "align-top me-2",
36
+ style: "width:2 rem;height:2 rem;margin-top:-.12 rem"
37
+ ) do
38
+ button(class: "btn p-0 opacity-50") do
39
+ render(Phlex::Icons::Tabler::SquareRounded.new(width: 24, height: 24))
40
+ end
41
+ end
42
+ div(class: "form-check-label ms-1 w-100") do
43
+ plain f.text_field :title,
44
+ autocomplete: "off",
45
+ class: "form-control w-100",
46
+ data: {
47
+ task_inline_form_target: :defaultInput,
48
+ action: "keydown.esc->task-inline-form#escPressed"
49
+ }
50
+ end
51
+ end
52
+ end
53
+ a(
54
+ href: "#",
55
+ data_action: "task-inline-form#togglerClicked",
56
+ data_task_inline_form_target: "toggler",
57
+ class: "btn btn-outline-secondary btn-sm ms-tasks-element"
58
+ ) { " Add a task " }
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,22 @@
1
+ module InnerPlan::Tasks::Row
2
+ class AddonsComponent < Phlex::HTML
3
+ def initialize(task)
4
+ @task = task
5
+ end
6
+
7
+ def template
8
+ div(class: 'd-inline-flex align-items-center gap-1') {
9
+ InnerPlan.configuration.task_row_addons.each do |item|
10
+ addon_instance = item.content.constantize.new(task)
11
+ next unless addon_instance.render?
12
+
13
+ render(addon_instance)
14
+ end
15
+ }
16
+ end
17
+
18
+ private
19
+
20
+ attr_reader :task
21
+ end
22
+ end
@@ -0,0 +1,19 @@
1
+ module InnerPlan::Tasks::Row
2
+ class BaseAddonComponent < Phlex::HTML
3
+ def initialize(task)
4
+ @task = task
5
+ end
6
+
7
+ def render?
8
+ true
9
+ end
10
+
11
+ def template
12
+ raise NotImplementedError
13
+ end
14
+
15
+ private
16
+
17
+ attr_reader :task
18
+ end
19
+ end
@@ -0,0 +1,13 @@
1
+ module InnerPlan::Tasks::Row
2
+ class DescriptionAddonComponent < BaseAddonComponent
3
+ def render?
4
+ !task.completed? && task.description.present?
5
+ end
6
+
7
+ def template
8
+ span(class: "text-body-tertiary") {
9
+ render(Phlex::Icons::Tabler::FileText.new(width: 15, height: 15, style: 'margin-top:-3px'))
10
+ }
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ module InnerPlan::Tasks::Row
2
+ class DueOnAddonComponent < BaseAddonComponent
3
+ def render?
4
+ !task.completed? && task.due_on.present?
5
+ end
6
+
7
+ def template
8
+ small(class: "text-body-tertiary text-nowrap") {
9
+ render(Phlex::Icons::Tabler::CalendarDue.new(width: 15, height: 15, style: 'margin-top:-3px'))
10
+ span(class: 'ms-1') { task.due_on.strftime('%a, %b %e') }
11
+ }
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ module InnerPlan::Tasks::Row
2
+ class GroupAddonComponent < BaseAddonComponent
3
+ def render?
4
+ @task.completed? && @task.list.sub?
5
+ end
6
+
7
+ def template
8
+ plain " &mdash; ".html_safe + @task.list.title
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,40 @@
1
+ module InnerPlan::Tasks::Row
2
+ class HandleComponent < Phlex::HTML
3
+ include Phlex::Rails::Helpers::ClassNames
4
+ include Phlex::Rails::Helpers::LinkTo
5
+
6
+ def initialize(task)
7
+ @task = task
8
+ end
9
+
10
+ def template
11
+ div(
12
+ class: class_names('align-top me-2 text-body-tertiary task-handle cm', invisible: task.completed?),
13
+ style: 'line-height: 1.6rem'
14
+ ) {
15
+ div(class: "dropstart") {
16
+ a(class: "align-top text-body-tertiary task-handle cm", data_bs_toggle: "dropdown") {
17
+ render(Phlex::Icons::Tabler::GripVertical.new(width: 20, height: 20))
18
+ }
19
+
20
+ ul(class: "dropdown-menu dropdown-menu-end") {
21
+ li {
22
+ link_to(
23
+ helpers.edit_task_path(task),
24
+ class: 'dropdown-item d-flex align-items-center gap-2',
25
+ data: { turbo_frame: :_top }
26
+ ) {
27
+ render(Phlex::Icons::Tabler::Pencil.new(width: 18, height: 18, class: 'text-secondary'))
28
+ span { "Edit task" }
29
+ }
30
+ }
31
+ }
32
+ }
33
+ }
34
+ end
35
+
36
+ private
37
+
38
+ attr_reader :task
39
+ end
40
+ end
@@ -0,0 +1,35 @@
1
+ module InnerPlan::Tasks
2
+ class RowComponent < Phlex::HTML
3
+ include Phlex::Rails::Helpers::ClassNames
4
+ include Phlex::Rails::Helpers::LinkTo
5
+ include Phlex::Rails::Helpers::CheckBox
6
+
7
+ def initialize(task, context: nil)
8
+ @task = task
9
+ @context = context
10
+ end
11
+
12
+ def template
13
+ div(class: "d-flex w-100 align-items-start", data: { id: task.id, update_url: helpers.update_position_task_path(task) }) {
14
+ render(InnerPlan::Tasks::Row::HandleComponent.new(task))
15
+
16
+ div(class: 'align-top', style: 'width:2rem; height: 2rem; line-height: 1.6rem;') {
17
+ render(InnerPlan::Tasks::CompletedTogglerComponent.new(task, context: context))
18
+ }
19
+
20
+ div(class: class_names('form-check-label ms-1 w-100', 'text-body-tertiary' => task.completed?), style: 'line-height: 1.6rem; padding-top: 1px;') {
21
+ link_to(title, task, class: 'text-reset text-decoration-none me-1')
22
+ render(InnerPlan::Tasks::Row::AddonsComponent.new(task))
23
+ }
24
+ }
25
+ end
26
+
27
+ private
28
+
29
+ attr_reader :task, :context
30
+
31
+ def title
32
+ InnerPlan::Tasks::TitleRenderer.call(task: task)[:title]
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,22 @@
1
+ module InnerPlan::Tasks
2
+ class RowsComponent < Phlex::HTML
3
+ include Phlex::Rails::Helpers::DOMID
4
+
5
+ def initialize(tasks, list:, context: nil, id_key: :ongoing_tasks)
6
+ @tasks = tasks
7
+ @list = list
8
+ @context = context
9
+ @id_key = id_key
10
+ end
11
+
12
+ def template(&content)
13
+ div(id: dom_id(@list, @id_key), data: { controller: :tasks, tasks_list_id_value: @list.id }) {
14
+ @tasks.each do |task|
15
+ render(InnerPlan::Tasks::RowComponent.new(task, context: @context))
16
+ end
17
+
18
+ render(content) if content
19
+ }
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,20 @@
1
+ module InnerPlan::Tasks::Show
2
+ class ItemComponent < Phlex::HTML
3
+ def initialize(icon:, title: nil)
4
+ @icon = icon
5
+ @title = title
6
+ end
7
+
8
+ def template(&content)
9
+ div(class: "d-flex mt-4") do
10
+ div(class: "me-3 text-body-tertiary", style: "margin-top:-.2rem") do
11
+ render(@icon.new(width: 24, height: 24))
12
+ end
13
+ div do
14
+ h6 { @title }
15
+ render content
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,19 @@
1
+ module InnerPlan::Tasks::Show
2
+ class RowComponent < Phlex::HTML
3
+ def initialize
4
+ @columns = []
5
+ end
6
+
7
+ def template(&content)
8
+ div(class: "row") {
9
+ render content
10
+ }
11
+ end
12
+
13
+ def with_column(span: 6, &content)
14
+ div(class: "col-#{span}") {
15
+ render content
16
+ }
17
+ end
18
+ end
19
+ end