oversee 0.1.0 → 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 +79 -21
  41. data/app/models/oversee/resource.rb +36 -0
  42. data/app/oversee/filter.rb +39 -0
  43. data/app/oversee/resource.rb +10 -0
  44. data/app/oversee/search.rb +34 -0
  45. data/app/views/layouts/oversee/application.html.erb +6 -6
  46. data/config/locales/en.yml +4 -0
  47. data/config/locales/oversee.en.yml +4 -0
  48. data/config/routes.rb +12 -6
  49. data/lib/oversee/configuration.rb +3 -0
  50. data/lib/oversee/version.rb +1 -1
  51. data/lib/oversee.rb +19 -2
  52. metadata +73 -47
  53. data/app/assets/config/oversee_manifest.js +0 -1
  54. data/app/assets/stylesheets/oversee/application.css +0 -15
  55. data/app/components/oversee/card_component.rb +0 -15
  56. data/app/components/oversee/dashboard/sidebar_component.rb +0 -127
  57. data/app/components/oversee/field_component.rb +0 -36
  58. data/app/components/oversee/field_label_component.rb +0 -155
  59. data/app/components/oversee/fields/display_row_component.rb +0 -58
  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 -31
  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/input_field.turbo_stream.erb +0 -3
  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,34 +2,67 @@ module Oversee
2
2
  class ResourcesController < BaseController
3
3
  include ActionView::RecordIdentifier
4
4
 
5
- before_action :set_resource_class, except: [: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
+ @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
+ )
18
+ end
10
19
 
11
- @resources = @resource_class.order(@sort_attribute.to_sym => sort_direction)
12
- @pagy, @resources = pagy(@resources)
20
+ def new
21
+ @resource = @resource_class.new
22
+ render Oversee::Resources::New.new(
23
+ resource: @resource,
24
+ resource_class: @resource_class,
25
+ params: params
26
+ )
27
+ end
28
+
29
+ def create
30
+ @resource = @resource_class.new(resource_params)
31
+
32
+ respond_to do |format|
33
+ if @resource.update(resource_params)
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 }
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)) }
39
+ end
40
+ end
13
41
  end
14
42
 
15
43
  def show
16
- 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
+ )
17
50
  end
18
51
 
19
52
  def edit
20
53
  end
21
54
 
22
55
  def update
23
- @resource_class = params[:resource_class].constantize
24
- set_resource
25
-
26
- @key = params[:resource][:oversee_key]
27
- @datatype = params[:resource][:oversee_datatype]
56
+ key = params[:oversee_key]
57
+ datatype = params[:oversee_datatype]
28
58
 
29
59
  respond_to do |format|
30
60
  if @resource.update(resource_params)
31
61
  format.html { redirect_to resource_path(@resource.id, resource: @resource_class) }
32
- 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
33
66
  else
34
67
  end
35
68
  end
@@ -44,27 +77,52 @@ module Oversee
44
77
  def input_field
45
78
  set_resource
46
79
 
47
- @key = params[:key].to_sym
48
- @value = @resource.send(@key)
49
- @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:)
50
86
 
51
- puts "---"
52
- puts "key: #{@key}"
53
- puts "value: #{@value}"
54
- puts "datatype: #{@datatype}"
55
- puts "---"
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)
97
+
98
+ respond_to do |format|
99
+ format.turbo_stream do
100
+ render turbo_stream: turbo_stream.replace(component_id, component)
101
+ end
102
+ format.html do
103
+ render component, layout: false
104
+ end
105
+ end
56
106
  end
57
107
 
58
108
  private
59
109
 
60
110
  def set_resource_class
61
- @resource_class = params[:resource].constantize
111
+ @resource_class = params[:resource_class_name].constantize
62
112
  end
63
113
 
64
114
  def set_resource
65
115
  @resource = @resource_class.find(params[:id])
66
116
  end
67
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
+
68
126
  def resource_associations
69
127
  @resource_associations ||= @resource_class.reflect_on_all_associations
70
128
  end