inner_plan 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|