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,29 @@
1
+ module InnerPlan
2
+ class List < ApplicationRecord
3
+ has_many :tasks, class_name: 'InnerPlan::Task'
4
+ has_many :tasks_including_groups, ->(list){ rewhere(list_id: ([list.id] + list.lists.pluck(:id))) }, class_name: 'InnerPlan::Task'
5
+ has_many :lists, foreign_key: :parent_id
6
+ belongs_to :list, foreign_key: :parent_id, optional: true
7
+ belongs_to :user, class_name: InnerPlan.configuration.user_class_name
8
+
9
+ positioned
10
+
11
+ scope :ordered_by_position, ->{ order(position: :asc) }
12
+ scope :root, ->{ where(parent_id: nil) }
13
+ scope :sub, ->{ where.not(parent_id: nil) }
14
+
15
+ validates :title, presence: true
16
+
17
+ def root?
18
+ parent_id.blank?
19
+ end
20
+
21
+ def sub?
22
+ parent_id.present?
23
+ end
24
+
25
+ def to_param
26
+ [id.to_s, title.to_url(limit: 50, truncate_words: false)].join('-')
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,36 @@
1
+ module InnerPlan
2
+ class Task < ApplicationRecord
3
+ belongs_to :list
4
+ belongs_to :user, class_name: InnerPlan.configuration.user_class_name,
5
+ optional: true
6
+
7
+ positioned on: :list
8
+
9
+ scope :ongoing, ->{ where(completed_at: nil) }
10
+ scope :completed, ->{ where.not(completed_at: nil).order(completed_at: :desc) }
11
+ scope :ordered_by_position, ->{ order(position: :asc) }
12
+ scope :recently_completed_first, -> { order(completed_at: :desc) }
13
+
14
+ validates :title, presence: true
15
+
16
+ def ongoing?
17
+ !completed?
18
+ end
19
+
20
+ def completed?
21
+ completed_at.present?
22
+ end
23
+
24
+ def complete!
25
+ touch(:completed_at)
26
+ end
27
+
28
+ def reopen!
29
+ update(completed_at: nil)
30
+ end
31
+
32
+ def to_param
33
+ [id.to_s, title.to_url(limit: 50, truncate_words: false)].join('-')
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,18 @@
1
+ module InnerPlan::Tasks
2
+ class DescriptionRenderer < Trailblazer::Operation
3
+ include ActionView::Helpers::TextHelper
4
+
5
+ step :extract_description
6
+ step :apply_simple_format
7
+
8
+ private
9
+
10
+ def extract_description(ctx, task:, **)
11
+ ctx[:description] = task.description
12
+ end
13
+
14
+ def apply_simple_format(ctx, description:, **)
15
+ ctx[:description] = simple_format(description)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ module InnerPlan::Tasks
2
+ class TitleRenderer < Trailblazer::Operation
3
+ include ActionView::Helpers::SanitizeHelper
4
+
5
+ step :extract_title
6
+ step :sanitize_title
7
+
8
+ private
9
+
10
+ def extract_title(ctx, task:, **)
11
+ ctx[:title] = task.title
12
+ end
13
+
14
+ def sanitize_title(ctx, title:, **)
15
+ ctx[:title] = sanitize(title)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" class="opacity-75" data-name="Layer 1" width="300" viewBox="0 0 647.63626 632.17383" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M687.3279,276.08691H512.81813a15.01828,15.01828,0,0,0-15,15v387.85l-2,.61005-42.81006,13.11a8.00676,8.00676,0,0,1-9.98974-5.31L315.678,271.39691a8.00313,8.00313,0,0,1,5.31006-9.99l65.97022-20.2,191.25-58.54,65.96972-20.2a7.98927,7.98927,0,0,1,9.99024,5.3l32.5498,106.32Z" transform="translate(-276.18187 -133.91309)" fill="#f2f2f2"/><path d="M725.408,274.08691l-39.23-128.14a16.99368,16.99368,0,0,0-21.23-11.28l-92.75,28.39L380.95827,221.60693l-92.75,28.4a17.0152,17.0152,0,0,0-11.28028,21.23l134.08008,437.93a17.02661,17.02661,0,0,0,16.26026,12.03,16.78926,16.78926,0,0,0,4.96972-.75l63.58008-19.46,2-.62v-2.09l-2,.61-64.16992,19.65a15.01489,15.01489,0,0,1-18.73-9.95l-134.06983-437.94a14.97935,14.97935,0,0,1,9.94971-18.73l92.75-28.4,191.24024-58.54,92.75-28.4a15.15551,15.15551,0,0,1,4.40966-.66,15.01461,15.01461,0,0,1,14.32032,10.61l39.0498,127.56.62012,2h2.08008Z" transform="translate(-276.18187 -133.91309)" fill="#3f3d56"/><path d="M398.86279,261.73389a9.0157,9.0157,0,0,1-8.61133-6.3667l-12.88037-42.07178a8.99884,8.99884,0,0,1,5.9712-11.24023l175.939-53.86377a9.00867,9.00867,0,0,1,11.24072,5.9707l12.88037,42.07227a9.01029,9.01029,0,0,1-5.9707,11.24072L401.49219,261.33887A8.976,8.976,0,0,1,398.86279,261.73389Z" transform="translate(-276.18187 -133.91309)" fill="#6c63ff"/><circle cx="190.15351" cy="24.95465" r="20" fill="#6c63ff"/><circle cx="190.15351" cy="24.95465" r="12.66462" fill="#fff"/><path d="M878.81836,716.08691h-338a8.50981,8.50981,0,0,1-8.5-8.5v-405a8.50951,8.50951,0,0,1,8.5-8.5h338a8.50982,8.50982,0,0,1,8.5,8.5v405A8.51013,8.51013,0,0,1,878.81836,716.08691Z" transform="translate(-276.18187 -133.91309)" fill="#e6e6e6"/><path d="M723.31813,274.08691h-210.5a17.02411,17.02411,0,0,0-17,17v407.8l2-.61v-407.19a15.01828,15.01828,0,0,1,15-15H723.93825Zm183.5,0h-394a17.02411,17.02411,0,0,0-17,17v458a17.0241,17.0241,0,0,0,17,17h394a17.0241,17.0241,0,0,0,17-17v-458A17.02411,17.02411,0,0,0,906.81813,274.08691Zm15,475a15.01828,15.01828,0,0,1-15,15h-394a15.01828,15.01828,0,0,1-15-15v-458a15.01828,15.01828,0,0,1,15-15h394a15.01828,15.01828,0,0,1,15,15Z" transform="translate(-276.18187 -133.91309)" fill="#3f3d56"/><path d="M801.81836,318.08691h-184a9.01015,9.01015,0,0,1-9-9v-44a9.01016,9.01016,0,0,1,9-9h184a9.01016,9.01016,0,0,1,9,9v44A9.01015,9.01015,0,0,1,801.81836,318.08691Z" transform="translate(-276.18187 -133.91309)" fill="#6c63ff"/><circle cx="433.63626" cy="105.17383" r="20" fill="#6c63ff"/><circle cx="433.63626" cy="105.17383" r="12.18187" fill="#fff"/></svg>
@@ -0,0 +1,16 @@
1
+ <% if params[:context] == 'lists_index' %>
2
+ <turbo-stream action="update" target="<%= dom_id(@task.list, :ongoing_tasks) %>">
3
+ <template>
4
+ <%= render(InnerPlan::Lists::OngoingTasksComponent.new(@task.list, context: params[:context])) %>
5
+ </template>
6
+ </turbo-stream>
7
+
8
+ <% list = @task.list.root? ? @task.list : @task.list.list %>
9
+ <turbo-stream action="replace" target="<%= dom_id(list, :completed_tasks) %>">
10
+ <template>
11
+ <%= render(InnerPlan::Lists::CompletedTasksComponent.new(list, context: params[:context])) %>
12
+ </template>
13
+ </turbo-stream>
14
+ <% else %>
15
+ <turbo-stream action="refresh"></turbo-stream>
16
+ <% end %>
@@ -0,0 +1,11 @@
1
+ <turbo-stream action="append" target="<%= dom_id(@list, :ongoing_tasks) %>">
2
+ <template>
3
+ <%= render(InnerPlan::Tasks::RowComponent.new(@task)) %>
4
+ </template>
5
+ </turbo-stream>
6
+
7
+ <turbo-stream action="update" target="<%= dom_id(@list, :add_task) %>">
8
+ <template>
9
+ <%= render InnerPlan::Tasks::InlineFormView.new(list: @list, form_visible: true) %>
10
+ </template>
11
+ </turbo-stream>
@@ -0,0 +1,5 @@
1
+ <turbo-stream action="update" target="<%= dom_id(@list, :add_task) %>">
2
+ <template>
3
+ <%= render InnerPlan::Tasks::InlineFormView.new(list: @list, task: @task, form_visible: true) %>
4
+ </template>
5
+ </turbo-stream>
@@ -0,0 +1,3 @@
1
+ <div class="trix-content">
2
+ <%= yield -%>
3
+ </div>
@@ -0,0 +1,38 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Inner plan</title>
5
+
6
+ <meta name="turbo-refresh-scroll" content="preserve">
7
+
8
+ <%= csrf_meta_tags %>
9
+ <%= csp_meta_tag %>
10
+
11
+ <script src="https://cdn.jsdelivr.net/npm/@hotwired/turbo@8.0.12/dist/turbo.es2017-umd.min.js"></script>
12
+
13
+ <%= stylesheet_link_tag "inner_plan/application", media: "all" %>
14
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
15
+ <link href="https://cdn.jsdelivr.net/npm/trix@2.1.8/dist/trix.min.css" rel="stylesheet">
16
+
17
+ <%= javascript_importmap_tags "application", importmap: InnerPlan.importmap %>
18
+
19
+ <% InnerPlan.configuration.additional_importmap_modules.each do |importmap_module| %>
20
+ <script type="module">import "<%= importmap_module %>"</script>
21
+ <% end %>
22
+ </head>
23
+ <body>
24
+
25
+ <div class="container">
26
+ <div class="row justify-content-center my-5">
27
+ <div class="col-lg-8 col-md-10 col-12">
28
+ <div class="card" style="--bs-card-border-radius: 1rem;">
29
+ <div class="card-body p-5">
30
+ <%= yield %>
31
+ </div>
32
+ </div>
33
+ </div>
34
+ </div>
35
+ </div>
36
+
37
+ </body>
38
+ </html>
@@ -0,0 +1,8 @@
1
+ pin "application", to: "inner_plan/application.js", preload: true
2
+ pin "@hotwired/stimulus", to: "https://unpkg.com/@hotwired/stimulus/dist/stimulus.js"
3
+ pin "sortablejs", to: "https://cdn.jsdelivr.net/npm/sortablejs@1.15.4/+esm"
4
+ pin "@rails/request.js", to: "https://cdn.jsdelivr.net/npm/@rails/request.js@0.0.11/dist/requestjs.min.js"
5
+ pin "@popperjs/core", to: "https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/esm/popper.min.js"
6
+ pin "bootstrap", to: "https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.esm.min.js"
7
+
8
+ pin_all_from InnerPlan::Engine.root.join("app/javascript/inner_plan/controllers"), under: "controllers", to: "inner_plan/controllers"
data/config/routes.rb ADDED
@@ -0,0 +1,17 @@
1
+ InnerPlan::Engine.routes.draw do
2
+ resources :lists, except: [:destroy] do
3
+ patch :update_position, on: :member
4
+
5
+ resources :tasks, shallow: true, except: [:destroy] do
6
+ patch :update_position, on: :member
7
+ post :complete, on: :member
8
+ post :reopen, on: :member
9
+ end
10
+
11
+ resources :groups, shallow: true, except: [:destroy] do
12
+ patch :update_position, on: :member
13
+ end
14
+ end
15
+
16
+ root to: 'lists#index'
17
+ end
@@ -0,0 +1,37 @@
1
+ class CreateInnerPlanTables < ActiveRecord::Migration[7.1]
2
+ def change
3
+ create_table "inner_plan_lists", force: :cascade do |t|
4
+ t.string "title"
5
+ t.integer "position", default: 0
6
+ t.datetime "created_at", null: false
7
+ t.datetime "updated_at", null: false
8
+ t.integer "completed_tasks_count", default: 0
9
+ t.integer "tasks_count", default: 0
10
+ t.integer "ongoing_tasks_count", default: 0
11
+ t.integer "user_id"
12
+ t.integer "parent_id"
13
+ t.text "description"
14
+ t.index ["parent_id"], name: "index_inner_plan_lists_on_parent_id"
15
+ t.index ["position"], name: "index_inner_plan_lists_on_position", unique: true
16
+ t.index ["user_id"], name: "index_inner_plan_lists_on_user_id"
17
+ end
18
+
19
+ create_table "inner_plan_tasks", force: :cascade do |t|
20
+ t.string "title"
21
+ t.text "description"
22
+ t.datetime "completed_at"
23
+ t.datetime "created_at", null: false
24
+ t.datetime "updated_at", null: false
25
+ t.integer "position", default: 0
26
+ t.date "due_on"
27
+ t.integer "user_id"
28
+ t.integer "list_id"
29
+ t.index ["list_id", "position"], name: "index_inner_plan_tasks_on_list_id_and_position", unique: true
30
+ t.index ["list_id"], name: "index_inner_plan_tasks_on_list_id"
31
+ t.index ["user_id"], name: "index_inner_plan_tasks_on_user_id"
32
+ end
33
+
34
+ add_foreign_key "inner_plan_lists", "inner_plan_lists", column: "parent_id"
35
+ add_foreign_key "inner_plan_tasks", "inner_plan_lists", column: "list_id"
36
+ end
37
+ end
data/db/seeds.rb ADDED
@@ -0,0 +1,14 @@
1
+ user = User.create! email: 'email@example.com', password: 'ChangeMe666'
2
+ InnerPlan::Profile.create! user: user
3
+
4
+ list = InnerPlan::List.create! title: 'Work and such', user: user
5
+ sub = InnerPlan::sub.create! list: list, title: 'Default', default: true, user: user
6
+
7
+ InnerPlan::Task.create! sub: sub, title: 'Duplikowanie produktów nie kopiuje cen', user: user
8
+ InnerPlan::Task.create! sub: sub, title: 'na android rozwala fotki - maja zły wymiar', user: user
9
+ InnerPlan::Task.create! sub: sub, title: 'wymuszenie w formularzu adresu wpisania numeru ulicy osobno', user: user
10
+ InnerPlan::Task.create! sub: sub, title: 'Masowe promocje nie odejmują tylko dodają do cen', user: user
11
+ InnerPlan::Task.create! sub: sub, title: 'Duplikowanie produktów nie kopiuje cen', completed_at: Time.current, user: user
12
+ InnerPlan::Task.create! sub: sub, title: 'Kiedy stan na minusie a desired na zero, nie dodawać produktu do raportu dla szwalni', completed_at: Time.current, user: user
13
+ InnerPlan::Task.create! sub: sub, title: 'Dodatki do koszyka', completed_at: Time.current
14
+ InnerPlan::Task.create! sub: sub, title: 'zliczanie na home tylko zamówień opłaconych i odejmowanie anulowanych', completed_at: Time.current, user: user
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ module InnerPlan
4
+ class Configuration
5
+ attr_accessor :user_class_name,
6
+ :current_user_method,
7
+ :task_edit_view_rows,
8
+ :task_show_view_rows,
9
+ :task_row_addons,
10
+ :additional_importmap_modules
11
+
12
+ def initialize
13
+ @user_class_name = 'User'
14
+ @current_user_method = :current_user
15
+ @additional_importmap_modules = []
16
+
17
+ # Tasks::EditView rows
18
+ due_on_item = proc { |context:, form:|
19
+ InnerPlan::Tasks::Form::ItemComponent.new(icon: Phlex::Icons::Tabler::CalendarDue, title: 'Due on') do
20
+ context.plain form.date_field :due_on,
21
+ class: "form-control",
22
+ autofocus: (context.focus == :due_on)
23
+ end
24
+ }
25
+
26
+ description_item = proc { |context:, form:|
27
+ InnerPlan::Tasks::Form::ItemComponent.new(icon: Phlex::Icons::Tabler::FileText, title: 'Description') do
28
+ context.plain form.text_area(:description, class: 'form-control', autofocus: (context.focus == :description))
29
+ end
30
+ }
31
+
32
+ @task_edit_view_rows = InnerPlan::SmartArray.new([
33
+ InnerPlan::SmartArray::Item.new(:primary_row, {}, InnerPlan::SmartArray.new([
34
+ InnerPlan::SmartArray::Item.new(:due_on, { span: 6 }, due_on_item),
35
+ ])),
36
+
37
+ InnerPlan::SmartArray::Item.new(:secondary_row, {}, InnerPlan::SmartArray.new([
38
+ InnerPlan::SmartArray::Item.new(:description, { span: 12 }, description_item)
39
+ ]))
40
+ ])
41
+
42
+ # Tasks::ShowView rows
43
+ due_on_item = proc { |context:|
44
+ InnerPlan::Tasks::Show::ItemComponent.new(icon: Phlex::Icons::Tabler::CalendarDue, title: 'Due on') do
45
+ context.a(
46
+ href: (context.helpers.edit_task_path(context.task, focus: :due_on)),
47
+ class:
48
+ (
49
+ context.class_names(
50
+ "text-decoration-none",
51
+ "text-body-tertiary" => context.task.due_on.blank?,
52
+ "text-reset" => context.task.due_on.present?
53
+ )
54
+ )
55
+ ) do
56
+ if context.task.due_on
57
+ context.plain context.task.due_on.strftime("%a, %b %e, %Y")
58
+ else
59
+ context.plain " Select a date... "
60
+ end
61
+ end
62
+ end
63
+ }
64
+
65
+ description_item = proc { |context:|
66
+ InnerPlan::Tasks::Show::ItemComponent.new(icon: Phlex::Icons::Tabler::FileText, title: 'Description') do
67
+ if context.task.description.present?
68
+ context.plain context.description.to_s
69
+ else
70
+ context.a(
71
+ href: (context.helpers.edit_task_path(context.task, focus: :description)),
72
+ class:
73
+ (
74
+ context.class_names(
75
+ "text-decoration-none",
76
+ "text-body-tertiary" => context.task.description.blank?,
77
+ "text-reset" => context.task.description.present?
78
+ )
79
+ )
80
+ ) { " Click to add description... " }
81
+ end
82
+ end
83
+ }
84
+
85
+ created_by_item = proc { |context:|
86
+ InnerPlan::Tasks::Show::ItemComponent.new(icon: Phlex::Icons::Tabler::CirclePlus, title: 'Created By') do
87
+ context.div(class: "text-body-tertiary") do
88
+ context.render(InnerPlan::UserWithAvatarComponent.new(context.task.user))
89
+ context.plain " on "
90
+ context.plain context.task.created_at.strftime("%a, %b %e, %Y")
91
+ end
92
+ end
93
+ }
94
+
95
+ @task_show_view_rows = InnerPlan::SmartArray.new([
96
+ InnerPlan::SmartArray::Item.new(:primary_row, {}, InnerPlan::SmartArray.new([
97
+ InnerPlan::SmartArray::Item.new(:due_on, { span: 6 }, due_on_item),
98
+ ])),
99
+
100
+ InnerPlan::SmartArray::Item.new(:secondary_row, {}, InnerPlan::SmartArray.new([
101
+ InnerPlan::SmartArray::Item.new(:description, { span: 12 }, description_item)
102
+ ])),
103
+
104
+ InnerPlan::SmartArray::Item.new(:tertiary_row, {}, InnerPlan::SmartArray.new([
105
+ InnerPlan::SmartArray::Item.new(:created_by, { span: 12 }, created_by_item)
106
+ ]))
107
+ ])
108
+
109
+ @task_row_addons = InnerPlan::SmartArray.new([
110
+ ::InnerPlan::SmartArray::Item.new(:group, {}, 'InnerPlan::Tasks::Row::GroupAddonComponent'),
111
+ ::InnerPlan::SmartArray::Item.new(:description, {}, 'InnerPlan::Tasks::Row::DescriptionAddonComponent'),
112
+ ::InnerPlan::SmartArray::Item.new(:due_on, {}, 'InnerPlan::Tasks::Row::DueOnAddonComponent'),
113
+ ])
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,22 @@
1
+ module InnerPlan
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace InnerPlan
4
+
5
+ initializer "inner_plan.importmap", before: "importmap" do |app|
6
+ # https://github.com/rails/importmap-rails#composing-import-maps
7
+ # app.config.importmap.paths << root.join("config/importmap.rb")
8
+ InnerPlan.importmap.draw(root.join("config/importmap.rb"))
9
+
10
+ # https://github.com/rails/importmap-rails#sweeping-the-cache-in-development-and-test
11
+ app.config.importmap.cache_sweepers << root.join("app/javascript")
12
+ end
13
+
14
+ initializer "inner_plan.assets" do |app|
15
+ # my_engine/app/javascript needs to be in the asset path
16
+ app.config.assets.paths << root.join("app/javascript")
17
+
18
+ # manifest has to be precompiled
19
+ app.config.assets.precompile += %w[inner_plan/manifest.js]
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,66 @@
1
+ module InnerPlan
2
+ class SmartArray < Array
3
+ class Item < Struct.new(:key, :options, :content); end
4
+
5
+ class KeyNotFoundError < StandardError; end
6
+ class WrongItemTypeError < StandardError; end
7
+ class ItemKeyNotUniqueError < StandardError; end
8
+
9
+ def add(item)
10
+ raise WrongItemTypeError unless item.is_a?(Item)
11
+ raise ItemKeyNotUniqueError if has_item_key?(item.key)
12
+
13
+ self.push(item)
14
+ end
15
+
16
+ def add_before(before_key, item)
17
+ raise WrongItemTypeError unless item.is_a?(Item)
18
+ raise ItemKeyNotUniqueError if has_item_key?(item.key)
19
+
20
+ index = find_item_index(before_key)
21
+ raise KeyNotFoundError unless index
22
+
23
+ self.insert(index, item)
24
+ end
25
+
26
+ def add_after(after_key, item)
27
+ raise WrongItemTypeError unless item.is_a?(Item)
28
+ raise ItemKeyNotUniqueError if has_item_key?(item.key)
29
+
30
+ index = find_item_index(after_key)
31
+ raise KeyNotFoundError unless index
32
+
33
+ self.insert(index + 1, item)
34
+ end
35
+
36
+ def replace(key, item)
37
+ raise WrongItemTypeError unless item.is_a?(Item)
38
+
39
+ index = find_item_index(key)
40
+ raise KeyNotFoundError unless index
41
+
42
+ self[index] = item
43
+
44
+ self
45
+ end
46
+
47
+ def get(key)
48
+ index = find_item_index(key)
49
+ raise KeyNotFoundError unless index
50
+
51
+ self[index]
52
+ end
53
+
54
+ private
55
+
56
+ def find_item_index(key)
57
+ self.find_index do |item|
58
+ item.key == key
59
+ end
60
+ end
61
+
62
+ def has_item_key?(key)
63
+ find_item_index(key) != nil
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,3 @@
1
+ module InnerPlan
2
+ VERSION = "0.1.0"
3
+ end
data/lib/inner_plan.rb ADDED
@@ -0,0 +1,28 @@
1
+ require "inner_plan/version"
2
+ require "inner_plan/engine"
3
+ require "inner_plan/configuration"
4
+ require "inner_plan/smart_array"
5
+
6
+ require "turbo-rails"
7
+ require "positioning"
8
+ require "importmap-rails"
9
+ require "image_processing"
10
+ require "phlex"
11
+ require "phlex-rails"
12
+ require "phlex-icons-tabler"
13
+ require "trailblazer-rails"
14
+ require "stringex"
15
+
16
+ module InnerPlan
17
+ class << self
18
+ def configuration
19
+ @configuration ||= Configuration.new
20
+ end
21
+
22
+ def configure
23
+ yield(configuration)
24
+ end
25
+ end
26
+
27
+ mattr_accessor :importmap, default: Importmap::Map.new
28
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :inner_plan do
3
+ # # Task goes here
4
+ # end