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