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.
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