oversee 0.1.1 → 0.2.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 (81) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +53 -14
  3. data/app/components/oversee/{application_component.rb → base.rb} +1 -1
  4. data/app/components/oversee/card.rb +13 -0
  5. data/app/components/oversee/dashboard/filters.rb +54 -0
  6. data/app/components/oversee/dashboard/header.rb +41 -0
  7. data/app/components/oversee/dashboard/index.rb +24 -0
  8. data/app/components/oversee/dashboard/javascript.rb +9 -0
  9. data/app/components/oversee/dashboard/pagination.rb +23 -0
  10. data/app/components/oversee/dashboard/sidebar.rb +53 -0
  11. data/app/components/oversee/dashboard/tailwind.rb +33 -0
  12. data/app/components/oversee/field/display.rb +29 -0
  13. data/app/components/oversee/field/form.rb +49 -0
  14. data/app/components/oversee/field/input/boolean.rb +23 -0
  15. data/app/components/oversee/field/input/datetime.rb +20 -0
  16. data/app/components/oversee/field/input/integer.rb +20 -0
  17. data/app/components/oversee/field/input/string.rb +20 -0
  18. data/app/components/oversee/field/input.rb +26 -0
  19. data/app/components/oversee/field/label.rb +24 -0
  20. data/app/components/oversee/field/value/boolean.rb +50 -0
  21. data/app/components/oversee/field/value/datetime.rb +9 -0
  22. data/app/components/oversee/field/value/enum.rb +9 -0
  23. data/app/components/oversee/field/value/integer.rb +9 -0
  24. data/app/components/oversee/field/value/string.rb +20 -0
  25. data/app/components/oversee/field/value/text.rb +9 -0
  26. data/app/components/oversee/field/value.rb +34 -0
  27. data/app/components/oversee/resources/base.rb +4 -0
  28. data/app/components/oversee/resources/errors.rb +41 -0
  29. data/app/components/oversee/resources/form.rb +49 -0
  30. data/app/components/oversee/resources/index.rb +26 -0
  31. data/app/components/oversee/resources/new.rb +17 -0
  32. data/app/components/oversee/resources/show.rb +126 -0
  33. data/app/components/oversee/resources/table.rb +109 -0
  34. data/app/components/oversee/table/body.rb +13 -0
  35. data/app/components/oversee/table/data.rb +5 -0
  36. data/app/components/oversee/table/head.rb +9 -0
  37. data/app/components/oversee/table/row.rb +9 -0
  38. data/app/components/oversee/table.rb +21 -0
  39. data/app/controllers/oversee/dashboard_controller.rb +4 -0
  40. data/app/controllers/oversee/resources_controller.rb +62 -23
  41. data/app/models/oversee/resource.rb +36 -0
  42. data/app/oversee/filter.rb +39 -0
  43. data/app/oversee/search.rb +34 -0
  44. data/app/views/layouts/oversee/application.html.erb +5 -5
  45. data/config/locales/en.yml +4 -0
  46. data/config/locales/oversee.en.yml +4 -0
  47. data/config/routes.rb +10 -10
  48. data/lib/oversee/configuration.rb +3 -0
  49. data/lib/oversee/version.rb +1 -1
  50. data/lib/oversee.rb +14 -2
  51. metadata +71 -47
  52. data/app/assets/config/oversee_manifest.js +0 -1
  53. data/app/assets/stylesheets/oversee/application.css +0 -15
  54. data/app/components/oversee/card_component.rb +0 -15
  55. data/app/components/oversee/dashboard/sidebar_component.rb +0 -127
  56. data/app/components/oversee/field_component.rb +0 -39
  57. data/app/components/oversee/field_label_component.rb +0 -155
  58. data/app/components/oversee/fields/display_row_component.rb +0 -58
  59. data/app/components/oversee/fields/input/boolean_component.rb +0 -26
  60. data/app/components/oversee/fields/input/datetime_component.rb +0 -27
  61. data/app/components/oversee/fields/input/integer_component.rb +0 -26
  62. data/app/components/oversee/fields/input/string_component.rb +0 -26
  63. data/app/components/oversee/fields/value/boolean_component.rb +0 -44
  64. data/app/components/oversee/fields/value/datetime_component.rb +0 -16
  65. data/app/components/oversee/fields/value/enum_component.rb +0 -16
  66. data/app/components/oversee/fields/value/integer_component.rb +0 -16
  67. data/app/components/oversee/fields/value/string_component.rb +0 -23
  68. data/app/components/oversee/fields/value/text_component.rb +0 -16
  69. data/app/helpers/oversee/application_helper.rb +0 -5
  70. data/app/mailers/oversee/application_mailer.rb +0 -6
  71. data/app/views/oversee/application/_javascript.html.erb +0 -17
  72. data/app/views/oversee/dashboard/show.html.erb +0 -18
  73. data/app/views/oversee/resources/_form.html.erb +0 -10
  74. data/app/views/oversee/resources/_input_field.html.erb +0 -20
  75. data/app/views/oversee/resources/edit.html.erb +0 -30
  76. data/app/views/oversee/resources/index.html.erb +0 -59
  77. data/app/views/oversee/resources/input_field.html.erb +0 -1
  78. data/app/views/oversee/resources/new.html.erb +0 -28
  79. data/app/views/oversee/resources/show.html.erb +0 -51
  80. data/app/views/oversee/resources/update.turbo_stream.erb +0 -3
  81. data/app/views/shared/_sidebar.html.erb +0 -32
@@ -0,0 +1,20 @@
1
+ class Oversee::Field::Value::String < Phlex::HTML
2
+ def initialize(key: nil, value: nil, kind: :value)
3
+ @key = key
4
+ @value = value
5
+ @kind = kind
6
+ end
7
+
8
+ def view_template
9
+ return p(class: "text-gray-400 text-xs uppercase") { "Empty" } if @value == ""
10
+ return p(class: "text-gray-400 text-xs uppercase") { "Redacted" } if sensitive?
11
+
12
+ p(class: "truncate") { @value }
13
+ end
14
+
15
+ private
16
+
17
+ def sensitive?
18
+ @key&.downcase&.include?("password") || @key&.downcase&.include?("token")
19
+ end
20
+ end
@@ -0,0 +1,9 @@
1
+ class Oversee::Field::Value::Text < Phlex::HTML
2
+ def initialize(datatype: :string, key: nil, value: nil, kind: :value)
3
+ @value = value
4
+ end
5
+
6
+ def view_template
7
+ p(class: "truncate") { @value }
8
+ end
9
+ end
@@ -0,0 +1,34 @@
1
+ class Oversee::Field::Value < Oversee::Base
2
+ MAP = {
3
+ string: Oversee::Field::Value::String,
4
+ boolean: Oversee::Field::Value::Boolean,
5
+ integer: Oversee::Field::Value::Integer,
6
+ datetime: Oversee::Field::Value::Datetime,
7
+ text: Oversee::Field::Value::Text,
8
+ enum: Oversee::Field::Value::Enum
9
+ }
10
+
11
+ def initialize(key: nil, value: nil, datatype: :string, **options)
12
+ @key = key
13
+ @value = value
14
+ @datatype = datatype
15
+ @options = options
16
+
17
+ @class = @options[:class]
18
+ end
19
+
20
+ def view_template
21
+ return p(class: "text-gray-400 text-xs"){ "—" } if @value.nil?
22
+ render component_class.new(key: @key, value: @value)
23
+ end
24
+
25
+ private
26
+
27
+ def component_class
28
+ MAP[@datatype.to_sym] || Oversee::Field::Value::String
29
+ end
30
+
31
+ def for_table?
32
+ @options[:for_table] || false
33
+ end
34
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Oversee::Resources::Base < Oversee::Base
4
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Oversee::Resources::Errors < Oversee::Base
4
+ def initialize(resource: nil, errors: nil, content: nil, **options)
5
+ @resource = resource
6
+ @errors = errors
7
+ @content = content
8
+ @options = options
9
+ end
10
+
11
+ def view_template
12
+ if @content || @resource&.errors&.any?
13
+ div(class: container_class) do
14
+ div(class: "inline-flex items-center gap-1.5 bg-red-600 py-1 px-2 text-xs text-white") do
15
+ # render Phlex::TablerIcons::ExclamationCircle.new(class: "size-5", stroke_width: 1.75)
16
+ h3(class: "text-sm font-medium") { plain @content || "Something went wrong!" }
17
+ end
18
+ if displayed_errors.present?
19
+ div(class: "mt-4 border-l-2 border-red-600 p-2") do
20
+ ul(class: "space-y-1 text-sm text-gray-700") do
21
+ displayed_errors.each do |message|
22
+ li { "— #{message}" }
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def container_class
34
+ @options[:container_class] || "bg-white mb-4"
35
+ end
36
+
37
+ def displayed_errors
38
+ @errors || @resource&.errors&.full_messages
39
+ end
40
+
41
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Oversee::Resources::Form < Oversee::Base
4
+ include Phlex::Rails::Helpers::FormWith
5
+ def initialize(resource:)
6
+ @resource = resource
7
+ end
8
+
9
+ def view_template
10
+ div(id: dom_id(@resource)) do
11
+ render Oversee::Resources::Errors.new(resource: @resource)
12
+ form_with model: @resource,
13
+ scope: :resource,
14
+ url: helpers.create_resource_path(resource_class_name:),
15
+ scope: :resource do |f|
16
+ @resource.class.columns_hash.each do |key, metadata|
17
+ if [@resource.class.primary_key, "created_at", "updated_at"].include?(key)
18
+ next
19
+ end
20
+ div(class: "py-2") do
21
+ render Oversee::Field::Label.new(
22
+ key: key,
23
+ datatype: metadata.sql_type_metadata.type
24
+ )
25
+ div(class: "mt-2") do
26
+ render Oversee::Field::Input.new(
27
+ datatype: metadata.sql_type_metadata.type,
28
+ key: key
29
+ )
30
+ end
31
+ end
32
+ end
33
+ hr(class: "-mx-8 my-8")
34
+ div(class: "flex justify-end mt-8") do
35
+ plain f.submit "Save",
36
+ class:
37
+ "bg-gray-900 px-6 py-2 rounded-full text-white font-medium text-sm hover:bg-gray-700 cursor-pointer"
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+
44
+ private
45
+
46
+ def resource_class_name
47
+ @resource.class.to_s
48
+ end
49
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Oversee::Resources::Index < Oversee::Base
4
+ def initialize(resources:, resource_class:, pagy:, params:)
5
+ @resources = resources
6
+ @resource_class = resource_class
7
+ @pagy = pagy
8
+ @params = params
9
+ end
10
+
11
+
12
+ def view_template
13
+ render Oversee::Dashboard::Header.new(title: @resource_class.to_s, subtitle: "Index") do
14
+ a(href: helpers.new_resource_path(@params[:resource_class_name]), class: "inline-flex items-center justify-center size-8 rounded-full bg-emerald-100 text-emerald-500 hover:bg-emerald-200 text-sm font-medium") { render Phlex::Icons::Iconoir::Plus.new(class: "size-4 text-emerald-500") }
15
+ end
16
+ render Oversee::Dashboard::Filters.new(params: @params)
17
+ render Oversee::Resources::Table.new(resource_class: @resource_class, resources: @resources, params: @params)
18
+ render Oversee::Dashboard::Pagination.new(pagy: @pagy, params: @params)
19
+ end
20
+
21
+ private
22
+
23
+ def resource_associations
24
+ @resource_class.reflect_on_all_associations.select { |association| association.macro == :belongs_to }
25
+ end
26
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Oversee::Resources::New < Oversee::Base
4
+ def initialize(resource:, resource_class:, params:)
5
+ @resource = resource
6
+ @resource_class = resource_class
7
+ @params = params
8
+ end
9
+
10
+ def view_template
11
+ render Oversee::Dashboard::Header.new(title: @resource_class.to_s, subtitle: "Creating new record")
12
+
13
+ div(class: "p-8") do
14
+ render Oversee::Resources::Form.new(resource: @resource)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Oversee::Resources::Show < Oversee::Base
4
+ include Phlex::Rails::Helpers::ButtonTo
5
+ include Phlex::Rails::Helpers::TurboFrameTag
6
+
7
+ def initialize(resource:, resource_class:, resource_associations:, params:)
8
+ @resource = resource
9
+ @resource_class = resource_class
10
+ @resource_associations = resource_associations
11
+ @params = params
12
+ @oversee_resource = Oversee::Resource.new(resource_class: @resource_class, resource: @resource)
13
+ end
14
+
15
+
16
+ def view_template
17
+ render Oversee::Dashboard::Header.new(title: @resource_class.to_s, subtitle: "##{@resource.id}", return_path: helpers.resources_path(resource_class_name: @params[:resource_class_name])) do
18
+ button_to(helpers.resource_path(resource_class_name: @params[:resource_class_name]), method: :delete, data: { turbo_confirm: "Are you sure?" }, class: "size-8 inline-flex items-center justify-center rounded-full text-rose-500 bg-rose-50 hover:bg-rose-100 text-sm font-medium transition-colors") { render Phlex::Icons::Iconoir::Trash.new(class: "size-4") }
19
+ end
20
+
21
+ div(class: "p-8") do
22
+ @resource_class.columns_hash.each do |key, metadata|
23
+ div(class: "py-4") do
24
+ div(class: "space-y-2") do
25
+ render Oversee::Field::Label.new(key: key, datatype: metadata.sql_type_metadata.type)
26
+ div(id: dom_id(@resource, :"#{key}_row"), class: "flex items-center gap-2 mt-4") do
27
+ render Oversee::Field::Display.new(resource: @resource, key: key, datatype: metadata.sql_type_metadata.type)
28
+ # div(id: dom_id(@resource, :"#{key}_actions")) do
29
+ # div(class: "bg-white text-gray-400 size-9 aspect-square inline-flex items-center justify-center"){ render Phlex::Icons::Iconoir::Copy.new(class: "size-5", stroke_width: 1.75) }
30
+ # end
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ # BELONGS_TO Associations
37
+ if !!belongs_to_associations.length
38
+ belongs_to_associations.each do |association|
39
+ div(class: "py-6") do
40
+ div(class: "space-y-4") do
41
+ div(class:"flex items-center gap-2") do
42
+ render Oversee::Field::Label.new(key: association[:name].to_s.titleize, datatype: :data)
43
+ a(href: helpers.resources_path(resource_class_name: association[:class_name].to_s), class: "hover:text-blue-500") { render Phlex::Icons::Iconoir::ArrowUpRight.new(class: "size-3") }
44
+ end
45
+
46
+ div(class: "flex items-center gap-2 flex-wrap") do
47
+ foreign_key_value = @resource[association[:foreign_key]]
48
+ path = !!foreign_key_value ? helpers.resource_path(id: foreign_key_value, resource_class_name: association[:class_name]) : helpers.resources_path(resource_class_name: association[:class_name])
49
+
50
+ a(
51
+ href: path,
52
+ class:
53
+ "inline-flex items-center gap-2 text-xs border border-transparent bg-gray-100 hover:bg-gray-200 px-3 py-1.5"
54
+ ) do
55
+ plain "#{association[:class_name]} | #{foreign_key_value}"
56
+ span { render Phlex::Icons::Iconoir::ArrowUpRight.new(class: "size-3") }
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ # HAS_MANY Associations
65
+ if !!has_many_associations.length
66
+ has_many_associations.each do |association|
67
+ associated_resources = @resource.send(association[:name])
68
+ associated_resource_class = association[:class_name].constantize
69
+
70
+ div(class: "py-6") do
71
+ div(class: "space-y-4") do
72
+ div(class:"flex items-center gap-2") do
73
+ render Oversee::Field::Label.new(key: association[:name].to_s.titleize, datatype: :data)
74
+ a(href: helpers.resources_path(resource_class_name: association[:class_name]), class: "hover:text-blue-500") { render Phlex::Icons::Iconoir::ArrowUpRight.new(class: "size-3") }
75
+ end
76
+
77
+ div(class: "bg-gray-50 p-2") do
78
+ if associated_resources.present?
79
+ div(class: "bg-white") do
80
+ # turbo_frame_tag(
81
+ # dom_id(associated_resource_class, :table),
82
+ # src: helpers.resources_table_path(
83
+ # resource_class_name: association[:class_name],
84
+ # filters: { eq: { association[:foreign_key] => [@resource.id] } }
85
+ # ),
86
+ # loading: :lazy,
87
+ # data: { turbo_stream: true }
88
+ # ) do
89
+ # div(class: "h-20 flex items-center justify-center") { render Phlex::Icons::Iconoir::DatabaseSearch.new(class: "animate-pulse size-6 text-gray-600") }
90
+ # end
91
+
92
+ #
93
+ render Oversee::Resources::Table.new(
94
+ resources: associated_resources,
95
+ resource_class: associated_resource_class,
96
+ params: @params
97
+ )
98
+ end
99
+ else
100
+ p(class: "bg-gray-50 p-2 pr-4 flex gap-2 items-center text-xs") {
101
+ render Phlex::Icons::Iconoir::DatabaseSearch.new(class: "size-3")
102
+ plain "No #{association[:name].to_s.titleize.downcase} found"
103
+ }
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
112
+
113
+ private
114
+
115
+ def belongs_to_associations
116
+ @oversee_resource.associations[:belongs_to]
117
+ end
118
+
119
+ def has_many_associations
120
+ @oversee_resource.associations[:has_many]
121
+ end
122
+
123
+ def has_associations?
124
+ @resource_associations.present?
125
+ end
126
+ end
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Oversee::Resources::Table < Oversee::Base
4
+ include Phlex::Rails::Helpers::TurboFrameTag
5
+
6
+ def initialize(resources:, resource_class:, params:, **options)
7
+ @resources = resources
8
+ @resource_class = resource_class || resources.first.class
9
+ @params = params
10
+ @options = options
11
+ end
12
+
13
+ def view_template
14
+ turbo_frame_tag dom_id(@resource_class, :table), target: :_top do
15
+ render Oversee::Table.new do |table|
16
+ table.head do |head|
17
+ head.row do
18
+ th(scope: "col", class: "px-4 py-3 text-left text-xs text-gray-900 uppercase")
19
+ # Attributes
20
+ @resource_class.columns_hash.each do |key, metadata|
21
+ th(scope: "col", class: "text-left text-xs text-gray-900 uppercase whitespace-nowrap hover:bg-gray-50 transition relative") do
22
+ a(
23
+ href:
24
+ (
25
+ helpers.resources_path(
26
+ resource: @params[:resource],
27
+ sort_attribute: key,
28
+ sort_direction:
29
+ @params[:sort_direction] == "asc" ? :desc : :asc
30
+ )
31
+ ),
32
+ class: "px-4 py-3 flex items-center justify-between gap-2 w-full h-full hover:text-gray-900 transition-colors"
33
+ ) do
34
+ render Oversee::Field::Label.new(key: key, datatype: metadata.sql_type_metadata.type)
35
+ render Phlex::Icons::Iconoir::ArrowSeparateVertical.new(class: "size-3 text-gray-500", stroke_width: 2.5)
36
+ end
37
+ end
38
+ end
39
+
40
+ # Associations
41
+ resource_associations.each do |association|
42
+ th(scope: "col", class: "text-left text-xs text-gray-900 uppercase whitespace-nowrap hover:bg-gray-50 transition relative") do
43
+ span(
44
+ class: "px-4 py-3 flex items-center justify-between gap-2 w-full h-full hover:text-gray-900 transition-colors"
45
+ ) do
46
+ render Oversee::Field::Label.new(key: association.name, datatype: nil)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ table.body do |body|
54
+ @resources.each do |resource|
55
+ body.row do |row|
56
+ td do
57
+ div(class: "flex space-x-2 mx-4") do
58
+ a(
59
+ href:
60
+ (
61
+ helpers.resource_path(
62
+ resource.id,
63
+ resource_class_name: @params[:resource_class_name]
64
+ )
65
+ ),
66
+ data: { turbo_stream: true },
67
+ class: "size-6 bg-gray-100 inline-flex items-center justify-center hover:bg-gray-200 group"
68
+ ) { render Phlex::Icons::Iconoir::Eye.new(class: "size-4 text-gray-500") }
69
+ end
70
+ end
71
+
72
+ @resource_class.columns_hash.each do |key, metadata|
73
+ row.data do
74
+ div(class: "max-w-96") do
75
+ render Oversee::Field::Value.new(datatype: metadata.sql_type_metadata.type, value: resource.send(key), key: key)
76
+ end
77
+ end
78
+ end
79
+
80
+ resource_associations.each do |association|
81
+ foreign_id = resource.send(association.foreign_key)
82
+ resource_class_name = association.class_name
83
+
84
+ path = !!foreign_id ? helpers.resource_path(id: foreign_id, resource_class_name:) : helpers.resources_path(resource_class_name: resource_class_name)
85
+
86
+ row.data do
87
+ div(class: "max-w-96") do
88
+ a(
89
+ href: path,
90
+ class:"px-2 py-1 bg-gray-100 text-gray-500 text-sm flex items-center justify-between gap-2 hover:bg-gray-200 hover:text-gray-900 min-w-20") do
91
+ span { foreign_id.presence || "N/A" }
92
+ render Phlex::Icons::Iconoir::ArrowRight.new(class: "size-3")
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
103
+
104
+ private
105
+
106
+ def resource_associations
107
+ @resource_class.reflect_on_all_associations.select { |association| association.macro == :belongs_to }
108
+ end
109
+ end
@@ -0,0 +1,13 @@
1
+ class Oversee::Table::Body < Phlex::HTML
2
+ def view_template(&)
3
+ tbody(class: "divide-y divide-gray-200", &)
4
+ end
5
+
6
+ def row(&)
7
+ render Oversee::Table::Row.new(&)
8
+ end
9
+
10
+ def data(&)
11
+ render Oversee::Table::Data.new(&)
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ class Oversee::Table::Data < Phlex::HTML
2
+ def view_template(&)
3
+ td(class: "whitespace-nowrap p-4 text-sm text-gray-500", &)
4
+ end
5
+ end
@@ -0,0 +1,9 @@
1
+ class Oversee::Table::Head < Phlex::HTML
2
+ def view_template(&)
3
+ thead(class: "bg-white", &)
4
+ end
5
+
6
+ def row(&)
7
+ render Oversee::Table::Row.new(&)
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ class Oversee::Table::Row < Phlex::HTML
2
+ def view_template(&)
3
+ tr(class: "divide-x divide-gray-200") { yield }
4
+ end
5
+
6
+ def data(&)
7
+ render Oversee::Table::Data.new(&)
8
+ end
9
+ end
@@ -0,0 +1,21 @@
1
+ class Oversee::Table < Phlex::HTML
2
+ def view_template(&)
3
+ div(class: "overflow-x-hidden") do
4
+ div(class: "-mx-4 overflow-x-auto sm:-mx-6 lg:-mx-8") do
5
+ div(class: "inline-block min-w-full align-middle sm:px-6 lg:px-8") do
6
+ table(class: "min-w-full divide-y divide-gray-200") do
7
+ yield
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
13
+
14
+ def head(&)
15
+ render Oversee::Table::Head.new(&)
16
+ end
17
+
18
+ def body(&)
19
+ render Oversee::Table::Body.new(&)
20
+ end
21
+ end
@@ -1,5 +1,9 @@
1
1
  module Oversee
2
2
  class DashboardController < BaseController
3
+ def index
4
+ render Oversee::Dashboard::Index.new
5
+ end
6
+
3
7
  def show
4
8
  fetch_metrics
5
9
 
@@ -2,51 +2,67 @@ module Oversee
2
2
  class ResourcesController < BaseController
3
3
  include ActionView::RecordIdentifier
4
4
 
5
- before_action :set_resource_class, except: [:create, :update]
6
- before_action :set_resource, only: %i[show edit destroy]
5
+ before_action :set_resource_class
6
+ before_action :set_resource, only: %i[show edit update destroy input_field]
7
+ before_action :set_resources, only: %i[index table]
7
8
 
8
9
  def index
9
- set_sorting_rules
10
-
11
- @resources = @resource_class.order(@sort_attribute.to_sym => sort_direction)
12
- @pagy, @resources = pagy(@resources)
10
+ @pagy, @resources = pagy(@resources, limit: params[:per_page] || Oversee.configuration.per_page)
11
+
12
+ render Oversee::Resources::Index.new(
13
+ resources: @resources,
14
+ resource_class: @resource_class,
15
+ pagy: @pagy,
16
+ params: params
17
+ )
13
18
  end
14
19
 
15
20
  def new
16
21
  @resource = @resource_class.new
22
+ render Oversee::Resources::New.new(
23
+ resource: @resource,
24
+ resource_class: @resource_class,
25
+ params: params
26
+ )
17
27
  end
18
28
 
19
29
  def create
20
- @resource_class = params[:resource_class].constantize
21
30
  @resource = @resource_class.new(resource_params)
22
31
 
23
32
  respond_to do |format|
24
33
  if @resource.update(resource_params)
25
- format.html { redirect_to resource_path(@resource.id, resource: @resource_class) }
26
- format.turbo_stream
34
+ format.html { redirect_to resource_path(@resource.id, resource_class_name: @resource_class) }
35
+ format.turbo_stream { redirect_to resource_path(@resource.id, resource_class_name: @resource_class), status: :see_other }
27
36
  else
37
+ format.html { render :new }
38
+ format.turbo_stream { render turbo_stream: turbo_stream.replace(dom_id(@resource), Oversee::Resources::Form.new(resource: @resource)) }
28
39
  end
29
40
  end
30
41
  end
31
42
 
32
43
  def show
33
- resource_associations
44
+ render Oversee::Resources::Show.new(
45
+ resource: @resource,
46
+ resource_class: @resource_class,
47
+ resource_associations: resource_associations,
48
+ params: params
49
+ )
34
50
  end
35
51
 
36
52
  def edit
37
53
  end
38
54
 
39
55
  def update
40
- @resource_class = params[:resource_class].constantize
41
- set_resource
42
-
43
- @key = params[:resource][:oversee_key]
44
- @datatype = params[:resource][:oversee_datatype]
56
+ key = params[:oversee_key]
57
+ datatype = params[:oversee_datatype]
45
58
 
46
59
  respond_to do |format|
47
60
  if @resource.update(resource_params)
48
61
  format.html { redirect_to resource_path(@resource.id, resource: @resource_class) }
49
- format.turbo_stream
62
+ format.turbo_stream do
63
+ component = Oversee::Field::Display.new(resource: @resource, datatype:, key:, value: @resource.send(key))
64
+ render turbo_stream: turbo_stream.replace(dom_id(@resource, key), component)
65
+ end
50
66
  else
51
67
  end
52
68
  end
@@ -61,15 +77,30 @@ module Oversee
61
77
  def input_field
62
78
  set_resource
63
79
 
64
- @key = params[:key].to_sym
65
- @value = @resource.send(@key)
66
- @datatype = @resource.class.columns_hash[@key.to_s].type
80
+ key = params[:key].to_sym
81
+ value = @resource.send(key)
82
+ datatype = @resource.class.columns_hash[key.to_s].type
83
+
84
+ field_dom_id = dom_id(@resource, key)
85
+ field = Oversee::Field::Form.new(resource: @resource, datatype:, key:, value:)
86
+
87
+ respond_to do |format|
88
+ format.turbo_stream do
89
+ render turbo_stream: turbo_stream.replace(field_dom_id, field)
90
+ end
91
+ end
92
+ end
93
+
94
+ def table
95
+ component_id = dom_id(@resource_class, :table)
96
+ component = Oversee::Resources::Table.new(resource_class: @resource_class, resources: @resources, params: params)
67
97
 
68
98
  respond_to do |format|
69
99
  format.turbo_stream do
70
- render turbo_stream: [
71
- turbo_stream.replace(dom_id(@resource, @key), partial: "oversee/resources/input_field", locals: { datatype: @datatype, key: @key, value: @value })
72
- ]
100
+ render turbo_stream: turbo_stream.replace(component_id, component)
101
+ end
102
+ format.html do
103
+ render component, layout: false
73
104
  end
74
105
  end
75
106
  end
@@ -77,13 +108,21 @@ module Oversee
77
108
  private
78
109
 
79
110
  def set_resource_class
80
- @resource_class = params[:resource].constantize
111
+ @resource_class = params[:resource_class_name].constantize
81
112
  end
82
113
 
83
114
  def set_resource
84
115
  @resource = @resource_class.find(params[:id])
85
116
  end
86
117
 
118
+ def set_resources
119
+ set_sorting_rules
120
+
121
+ @resources = @resource_class.order(@sort_attribute.to_sym => sort_direction)
122
+ @resources = Filter.new(collection: @resources, params:).apply
123
+ @resources = Search.new(collection: @resources, resource_class: @resource_class, query: params[:query]).call
124
+ end
125
+
87
126
  def resource_associations
88
127
  @resource_associations ||= @resource_class.reflect_on_all_associations
89
128
  end