oversee 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +8 -3
- data/app/assets/config/oversee_manifest.js +2 -0
- data/app/components/oversee/dashboard/actions.rb +58 -0
- data/app/components/oversee/dashboard/filter.rb +12 -0
- data/app/components/oversee/dashboard/filters.rb +5 -51
- data/app/components/oversee/dashboard/header.rb +34 -18
- data/app/components/oversee/dashboard/index.rb +23 -11
- data/app/components/oversee/dashboard/javascript.rb +4 -4
- data/app/components/oversee/dashboard/pagination.rb +1 -1
- data/app/components/oversee/dashboard/sidebar.rb +38 -1
- data/app/components/oversee/dashboard/tailwind.rb +30 -1
- data/app/components/oversee/field/display.rb +12 -21
- data/app/components/oversee/field/form.rb +5 -37
- data/app/components/oversee/field/input/belongs_to.rb +5 -0
- data/app/components/oversee/field/input/boolean.rb +3 -18
- data/app/components/oversee/field/input/date.rb +5 -0
- data/app/components/oversee/field/input/datetime.rb +2 -17
- data/app/components/oversee/field/input/integer.rb +2 -17
- data/app/components/oversee/field/input/json.rb +5 -0
- data/app/components/oversee/field/input/rich_text.rb +10 -0
- data/app/components/oversee/field/input/string.rb +2 -17
- data/app/components/oversee/field/input.rb +13 -11
- data/app/components/oversee/field/label.rb +16 -15
- data/app/components/oversee/field/set.rb +36 -0
- data/app/components/oversee/field/value/belongs_to.rb +15 -0
- data/app/components/oversee/field/value/boolean.rb +2 -47
- data/app/components/oversee/field/value/date.rb +5 -0
- data/app/components/oversee/field/value/datetime.rb +2 -6
- data/app/components/oversee/field/value/enum.rb +2 -6
- data/app/components/oversee/field/value/integer.rb +2 -6
- data/app/components/oversee/field/value/json.rb +6 -0
- data/app/components/oversee/field/value/rich_text.rb +5 -0
- data/app/components/oversee/field/value/string.rb +3 -10
- data/app/components/oversee/field/value/text.rb +2 -6
- data/app/components/oversee/field/value.rb +14 -5
- data/app/components/oversee/field.rb +42 -0
- data/app/components/oversee/flash.rb +39 -0
- data/app/components/oversee/layout/application.rb +46 -0
- data/app/components/oversee/resources/index.rb +22 -3
- data/app/components/oversee/resources/new.rb +7 -3
- data/app/components/oversee/resources/show.rb +139 -68
- data/app/components/oversee/resources/table.rb +8 -2
- data/app/controllers/oversee/dashboard_controller.rb +1 -1
- data/app/controllers/oversee/resources/fields_controller.rb +24 -36
- data/app/controllers/oversee/resources_controller.rb +29 -24
- data/app/javascript/oversee/application.js +3 -0
- data/app/javascript/oversee/controllers/clipboard_controller.js +21 -0
- data/app/javascript/oversee/controllers/index.js +9 -0
- data/app/javascript/oversee/controllers/notification_controller.js +17 -0
- data/app/javascript/oversee/controllers/reveal_controller.js +19 -0
- data/app/javascript/oversee/controllers/sidebar/state_controller.js +21 -0
- data/app/models/oversee/resource.rb +49 -20
- data/app/{oversee → service/oversee}/filter.rb +2 -2
- data/app/{oversee → service/oversee}/search.rb +1 -1
- data/app/views/oversee/base.rb +5 -0
- data/config/importmap.rb +14 -0
- data/config/routes.rb +16 -10
- data/lib/generators/oversee/install_generator.rb +7 -0
- data/lib/oversee/engine.rb +19 -0
- data/lib/oversee/version.rb +1 -1
- data/lib/oversee.rb +13 -0
- data/lib/tasks/oversee_tasks.rake +6 -4
- metadata +71 -9
- data/app/jobs/oversee/application_job.rb +0 -4
- data/app/oversee/cards.rb +0 -2
- data/app/oversee/resource.rb +0 -10
- data/app/views/layouts/oversee/application.html.erb +0 -26
@@ -4,104 +4,156 @@ class Oversee::Resources::Show < Oversee::Base
|
|
4
4
|
include Phlex::Rails::Helpers::ButtonTo
|
5
5
|
include Phlex::Rails::Helpers::TurboFrameTag
|
6
6
|
|
7
|
+
|
8
|
+
attr_reader :resource
|
9
|
+
attr_reader :resource_class
|
10
|
+
attr_reader :resource_associations
|
11
|
+
|
7
12
|
def initialize(resource:, resource_class:, resource_associations:, params:)
|
8
13
|
@resource = resource
|
9
14
|
@resource_class = resource_class
|
10
15
|
@resource_associations = resource_associations
|
11
16
|
@params = params
|
12
|
-
|
17
|
+
|
18
|
+
@oversee_resource = Oversee::Resource.new(resource_class: @resource_class, instance: @resource)
|
13
19
|
end
|
14
20
|
|
21
|
+
def around_template
|
22
|
+
render Oversee::Layout::Application.new { super }
|
23
|
+
end
|
15
24
|
|
16
25
|
def view_template
|
17
|
-
render Oversee::Dashboard::Header.new(title: @resource_class.to_s
|
18
|
-
|
26
|
+
render Oversee::Dashboard::Header.new(title: @resource_class.to_s.pluralize) do |h|
|
27
|
+
h.left do
|
28
|
+
h.separator
|
29
|
+
div(class: "inline-flex items-center gap-1 text-sm bg-gray-100 text-gray-600 h-6 px-2") do
|
30
|
+
render Phlex::Icons::Iconoir::Hashtag.new(class: "size-2.5 text-gray-500", stroke_width: 2)
|
31
|
+
span { @resource.to_param }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
h.right do
|
35
|
+
details(class: "relative inline-block") do
|
36
|
+
summary(class: "cursor-pointer list-none") do
|
37
|
+
div(class: "inline-flex items-center justify-center size-8 hover:bg-indigo-50 transition") do
|
38
|
+
render Phlex::Icons::Iconoir::MoreHorizCircle.new(class: "size-4 text-gray-500")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
ul(
|
42
|
+
class:
|
43
|
+
"absolute p-2 mt-2 right-0 top-full min-w-40 overflow-hidden rounded-lg bg-white border border-b-2 border-gray-200/75 divide-y text-xs text-gray-500 font-medium"
|
44
|
+
) do
|
45
|
+
li(class: "w-full") do
|
46
|
+
button_to(helpers.resource_path(resource_class_name: @params[:resource_class_name]), method: :delete, data: { turbo_confirm: "Are you sure?" }, class: "p-1 hover:bg-gray-50 w-full flex items-center gap-2 transition duration-100") do
|
47
|
+
div(class: "inline-flex items-center justify-center size-6 bg-gray-100") do
|
48
|
+
render Phlex::Icons::Iconoir::Trash.new(class: "size-3 text-gray-500")
|
49
|
+
end
|
50
|
+
plain "Delete"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
19
56
|
end
|
20
57
|
|
21
|
-
|
58
|
+
hr(class: "my-4")
|
59
|
+
|
60
|
+
# COLUMNS
|
61
|
+
div(class: "flex flex-col gap-4") do
|
22
62
|
@resource_class.columns_hash.each do |key, metadata|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
63
|
+
next if @oversee_resource.foreign_keys.include?(key.to_s)
|
64
|
+
value = @resource.send(key)
|
65
|
+
datatype = metadata.sql_type_metadata.type
|
66
|
+
render Oversee::Field::Set.new(resource:, key:, value:, datatype:)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
# RICH TEXT Associations
|
72
|
+
if !!rich_text_associations.length
|
73
|
+
hr(class: "my-4")
|
74
|
+
rich_text_associations.each do |association|
|
75
|
+
|
76
|
+
# Remove the "rich_text_" prefix from the association name
|
77
|
+
key = association[:name].to_s[10..].to_sym
|
78
|
+
|
79
|
+
div do
|
80
|
+
div(class: "space-y-4") do
|
81
|
+
div(class:"flex items-center gap-2") do
|
82
|
+
render Oversee::Field::Label.new(key: key.to_s.titleize, datatype: :rich_text)
|
83
|
+
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") }
|
84
|
+
end
|
85
|
+
|
86
|
+
div(id: dom_id(@resource, :"#{key.to_s}_row"), class: "flex items-center gap-2 mt-4") do
|
87
|
+
render Oversee::Field::Display.new(resource:, key:, value: @resource.send(key).to_plain_text[..196], datatype: :rich_text)
|
31
88
|
end
|
32
89
|
end
|
33
90
|
end
|
34
91
|
end
|
92
|
+
end
|
35
93
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
94
|
+
# BELONGS_TO Associations
|
95
|
+
if !!belongs_to_associations.length
|
96
|
+
belongs_to_associations.each do |association|
|
97
|
+
div(class: "py-6") do
|
98
|
+
div(class: "space-y-4") do
|
99
|
+
div(class:"flex items-center gap-2") do
|
100
|
+
render Oversee::Field::Label.new(key: association[:name].to_s.titleize, datatype: :data)
|
101
|
+
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") }
|
102
|
+
end
|
45
103
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
) do
|
55
|
-
plain "#{association[:class_name]} | #{foreign_key_value}"
|
56
|
-
span { render Phlex::Icons::Iconoir::ArrowUpRight.new(class: "size-3") }
|
57
|
-
end
|
104
|
+
foreign_key = association[:foreign_key]
|
105
|
+
foreign_key_value = @resource[association[:foreign_key]]
|
106
|
+
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])
|
107
|
+
|
108
|
+
div(id: dom_id(@resource, :"#{foreign_key}_row"), class: "flex items-center gap-2 mt-4") do
|
109
|
+
render Oversee::Field::Display.new(resource:, key: foreign_key, value: foreign_key_value, datatype: :belongs_to, display_key: true)
|
110
|
+
div(id: dom_id(@resource, :"#{foreign_key}_actions")) do
|
111
|
+
a(href: path, class: "bg-gray-100 hover:bg-gray-200 text-gray-400 hover:text-blue-500 size-10 aspect-square inline-flex items-center justify-center transition-colors"){ render Phlex::Icons::Iconoir::ArrowUpRight.new(class: "size-4") }
|
58
112
|
end
|
59
113
|
end
|
60
114
|
end
|
61
115
|
end
|
62
116
|
end
|
117
|
+
end
|
63
118
|
|
64
|
-
|
65
|
-
|
119
|
+
hr(class: "my-4")
|
120
|
+
|
121
|
+
# HAS_MANY Associations
|
122
|
+
if !!has_many_associations.length
|
123
|
+
div(class: "flex flex-col gap-8") do
|
66
124
|
has_many_associations.each do |association|
|
67
125
|
associated_resources = @resource.send(association[:name])
|
68
126
|
associated_resource_class = association[:class_name].constantize
|
69
127
|
|
70
|
-
div(class: "
|
71
|
-
div(class:
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
128
|
+
div(class: "space-y-4") do
|
129
|
+
div(class:"flex items-center gap-2") do
|
130
|
+
render Oversee::Field::Label.new(key: association[:name].to_s.titleize, datatype: :data)
|
131
|
+
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") }
|
132
|
+
end
|
133
|
+
|
134
|
+
div(class: "bg-gray-50 p-2") do
|
135
|
+
# turbo_frame_tag(
|
136
|
+
# dom_id(associated_resource_class, :table),
|
137
|
+
# src: helpers.resources_table_path(resources_table_params(association)),
|
138
|
+
# loading: :lazy,
|
139
|
+
# data: { turbo_stream: true }
|
140
|
+
# ) do
|
141
|
+
# div(class: "h-20 flex items-center justify-center") { render Phlex::Icons::Iconoir::DatabaseSearch.new(class: "animate-pulse size-6 text-gray-600") }
|
142
|
+
# end
|
76
143
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
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
|
-
}
|
144
|
+
if associated_resources.present?
|
145
|
+
div(class: "bg-white") do
|
146
|
+
render Oversee::Resources::Table.new(
|
147
|
+
resources: associated_resources,
|
148
|
+
resource_class: associated_resource_class,
|
149
|
+
params: @params
|
150
|
+
)
|
104
151
|
end
|
152
|
+
else
|
153
|
+
p(class: "bg-gray-50 p-2 pr-4 flex gap-2 items-center text-xs") {
|
154
|
+
render Phlex::Icons::Iconoir::DatabaseSearch.new(class: "size-3")
|
155
|
+
plain "No #{association[:name].to_s.titleize.downcase} found"
|
156
|
+
}
|
105
157
|
end
|
106
158
|
end
|
107
159
|
end
|
@@ -112,6 +164,10 @@ class Oversee::Resources::Show < Oversee::Base
|
|
112
164
|
|
113
165
|
private
|
114
166
|
|
167
|
+
def rich_text_associations
|
168
|
+
@oversee_resource.rich_text_associations
|
169
|
+
end
|
170
|
+
|
115
171
|
def belongs_to_associations
|
116
172
|
@oversee_resource.associations[:belongs_to]
|
117
173
|
end
|
@@ -123,4 +179,19 @@ class Oversee::Resources::Show < Oversee::Base
|
|
123
179
|
def has_associations?
|
124
180
|
@resource_associations.present?
|
125
181
|
end
|
182
|
+
|
183
|
+
def resources_table_params(association)
|
184
|
+
# if association[:through].nil?
|
185
|
+
# return { association[:foreign_key] => { eq: [@resource.id] } }
|
186
|
+
# else
|
187
|
+
# keys = @resource.send(association[:through]).pluck(association[:foreign_key])
|
188
|
+
# return { @resource_class.primary_key => { eq: keys } }
|
189
|
+
# end
|
190
|
+
|
191
|
+
{
|
192
|
+
resource_class_name: association[:class_name],
|
193
|
+
association_name: association[:through],
|
194
|
+
filters: { association[:foreign_key] => { eq: [@resource.id] } }
|
195
|
+
}
|
196
|
+
end
|
126
197
|
end
|
@@ -8,16 +8,19 @@ class Oversee::Resources::Table < Oversee::Base
|
|
8
8
|
@resource_class = resource_class || resources.first.class
|
9
9
|
@params = params
|
10
10
|
@options = options
|
11
|
+
|
12
|
+
@oversee_resource = Oversee::Resource.new(resource_class: @resource_class)
|
11
13
|
end
|
12
14
|
|
13
15
|
def view_template
|
14
|
-
turbo_frame_tag dom_id(@resource_class, :table), target:
|
16
|
+
turbo_frame_tag dom_id(@resource_class, :table), target: "_top" do
|
15
17
|
render Oversee::Table.new do |table|
|
16
18
|
table.head do |head|
|
17
19
|
head.row do
|
18
20
|
th(scope: "col", class: "px-4 py-3 text-left text-xs text-gray-900 uppercase")
|
19
21
|
# Attributes
|
20
22
|
@resource_class.columns_hash.each do |key, metadata|
|
23
|
+
next if @oversee_resource.foreign_keys.include?(key.to_s)
|
21
24
|
th(scope: "col", class: "text-left text-xs text-gray-900 uppercase whitespace-nowrap hover:bg-gray-50 transition relative") do
|
22
25
|
a(
|
23
26
|
href:
|
@@ -29,6 +32,7 @@ class Oversee::Resources::Table < Oversee::Base
|
|
29
32
|
@params[:sort_direction] == "asc" ? :desc : :asc
|
30
33
|
)
|
31
34
|
),
|
35
|
+
target: "_top",
|
32
36
|
class: "px-4 py-3 flex items-center justify-between gap-2 w-full h-full hover:text-gray-900 transition-colors"
|
33
37
|
) do
|
34
38
|
render Oversee::Field::Label.new(key: key, datatype: metadata.sql_type_metadata.type)
|
@@ -52,6 +56,7 @@ class Oversee::Resources::Table < Oversee::Base
|
|
52
56
|
|
53
57
|
table.body do |body|
|
54
58
|
@resources.each do |resource|
|
59
|
+
|
55
60
|
body.row do |row|
|
56
61
|
td do
|
57
62
|
div(class: "flex space-x-2 mx-4") do
|
@@ -70,9 +75,10 @@ class Oversee::Resources::Table < Oversee::Base
|
|
70
75
|
end
|
71
76
|
|
72
77
|
@resource_class.columns_hash.each do |key, metadata|
|
78
|
+
next if @oversee_resource.foreign_keys.include?(key.to_s)
|
73
79
|
row.data do
|
74
80
|
div(class: "max-w-96") do
|
75
|
-
render Oversee::Field::Value.new(datatype: metadata.sql_type_metadata.type, value: resource.send(key),
|
81
|
+
render Oversee::Field::Value.new(key:, datatype: metadata.sql_type_metadata.type, value: resource.send(key), for_table: true)
|
76
82
|
end
|
77
83
|
end
|
78
84
|
end
|
@@ -1,45 +1,33 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
class FieldsController < BaseController
|
4
|
-
before_action :set_resource_class
|
1
|
+
class Oversee::Resources::FieldsController < Oversee::ResourcesController
|
2
|
+
before_action :set_resource, only: %i[input]
|
5
3
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
# @datatype = @resource_class.columns_hash[@key.to_s].type
|
4
|
+
# Renders the display field for a resource
|
5
|
+
def show
|
6
|
+
end
|
10
7
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
else
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
# Non-standard actions
|
21
|
-
def edit
|
22
|
-
@resource = @resource_class.find(params[:id])
|
23
|
-
@key = params[:key].to_sym
|
24
|
-
@value = @resource.send(@key)
|
25
|
-
@datatype = @resource.class.columns_hash[@key.to_s].type
|
26
|
-
puts "---"
|
27
|
-
puts "key: #{@key}"
|
28
|
-
puts "value: #{@value}"
|
29
|
-
puts "datatype: #{@datatype}"
|
30
|
-
puts "---"
|
8
|
+
# Renders the input field for a resource
|
9
|
+
def input
|
10
|
+
component_dom_id = dom_id(@resource, key)
|
11
|
+
component = Oversee::Field::Set.new(resource: @resource, datatype:, key:, value:, state: :input)
|
31
12
|
|
13
|
+
respond_to do |format|
|
14
|
+
format.turbo_stream do
|
15
|
+
render turbo_stream: turbo_stream.replace(component_dom_id, component)
|
32
16
|
end
|
17
|
+
end
|
18
|
+
end
|
33
19
|
|
34
|
-
|
20
|
+
private
|
35
21
|
|
36
|
-
|
37
|
-
|
38
|
-
|
22
|
+
def key
|
23
|
+
params[:key].to_sym
|
24
|
+
end
|
39
25
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
26
|
+
def value
|
27
|
+
params[:value] || @resource.send(key)
|
28
|
+
end
|
29
|
+
|
30
|
+
def datatype
|
31
|
+
params[:datatype] || @resource.class.columns_hash[key.to_s].type
|
44
32
|
end
|
45
33
|
end
|
@@ -3,8 +3,8 @@ module Oversee
|
|
3
3
|
include ActionView::RecordIdentifier
|
4
4
|
|
5
5
|
before_action :set_resource_class
|
6
|
-
before_action :set_resource, only: %i[show edit update destroy
|
7
|
-
before_action :set_resources, only: %i[index
|
6
|
+
before_action :set_resource, only: %i[show edit update destroy]
|
7
|
+
before_action :set_resources, only: %i[index]
|
8
8
|
|
9
9
|
def index
|
10
10
|
@pagy, @resources = pagy(@resources, limit: params[:per_page] || Oversee.configuration.per_page)
|
@@ -14,7 +14,7 @@ module Oversee
|
|
14
14
|
resource_class: @resource_class,
|
15
15
|
pagy: @pagy,
|
16
16
|
params: params
|
17
|
-
)
|
17
|
+
), layout: false
|
18
18
|
end
|
19
19
|
|
20
20
|
def new
|
@@ -23,7 +23,7 @@ module Oversee
|
|
23
23
|
resource: @resource,
|
24
24
|
resource_class: @resource_class,
|
25
25
|
params: params
|
26
|
-
)
|
26
|
+
), layout: false
|
27
27
|
end
|
28
28
|
|
29
29
|
def create
|
@@ -31,6 +31,7 @@ module Oversee
|
|
31
31
|
|
32
32
|
respond_to do |format|
|
33
33
|
if @resource.update(resource_params)
|
34
|
+
flash.now[:notice] = "Resource was successfully created."
|
34
35
|
format.html { redirect_to resource_path(@resource.id, resource_class_name: @resource_class) }
|
35
36
|
format.turbo_stream { redirect_to resource_path(@resource.id, resource_class_name: @resource_class), status: :see_other }
|
36
37
|
else
|
@@ -46,7 +47,7 @@ module Oversee
|
|
46
47
|
resource_class: @resource_class,
|
47
48
|
resource_associations: resource_associations,
|
48
49
|
params: params
|
49
|
-
)
|
50
|
+
), layout: false
|
50
51
|
end
|
51
52
|
|
52
53
|
def edit
|
@@ -58,10 +59,14 @@ module Oversee
|
|
58
59
|
|
59
60
|
respond_to do |format|
|
60
61
|
if @resource.update(resource_params)
|
62
|
+
flash.now[:notice] = "#{@resource.class.to_s.titleize.capitalize.gsub("::"," ")} was successfully updated!"
|
61
63
|
format.html { redirect_to resource_path(@resource.id, resource: @resource_class) }
|
62
64
|
format.turbo_stream do
|
63
|
-
component = Oversee::Field::
|
64
|
-
render turbo_stream:
|
65
|
+
component = Oversee::Field::Set.new(resource: @resource, datatype:, key:, value: @resource.send(key))
|
66
|
+
render turbo_stream: [
|
67
|
+
turbo_stream.replace(dom_id(@resource, key), component),
|
68
|
+
turbo_stream.replace(:flash, Oversee::Flash)
|
69
|
+
]
|
65
70
|
end
|
66
71
|
else
|
67
72
|
end
|
@@ -73,25 +78,25 @@ module Oversee
|
|
73
78
|
redirect_to resources_path(resource: @resource_class)
|
74
79
|
end
|
75
80
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
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:)
|
81
|
+
def association
|
82
|
+
@resources = @resource_class.find(params[:id]).send(params[:association_name])
|
83
|
+
@pagy, @resources = pagy(@resources, limit: params[:per_page] || Oversee.configuration.per_page)
|
86
84
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
85
|
+
render Oversee::Resources::Index.new(
|
86
|
+
resources: @resources,
|
87
|
+
resource_class: @resource_class,
|
88
|
+
pagy: @pagy,
|
89
|
+
params: params
|
90
|
+
)
|
92
91
|
end
|
93
92
|
|
94
93
|
def table
|
94
|
+
if params[:association_name].present?
|
95
|
+
@resources = @resource_class.find(params[:id]).send(params[:association_name])
|
96
|
+
else
|
97
|
+
set_resources
|
98
|
+
end
|
99
|
+
|
95
100
|
component_id = dom_id(@resource_class, :table)
|
96
101
|
component = Oversee::Resources::Table.new(resource_class: @resource_class, resources: @resources, params: params)
|
97
102
|
|
@@ -119,8 +124,8 @@ module Oversee
|
|
119
124
|
set_sorting_rules
|
120
125
|
|
121
126
|
@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
|
127
|
+
@resources = Oversee::Filter.new(collection: @resources, params:).apply
|
128
|
+
@resources = Oversee::Search.new(collection: @resources, resource_class: @resource_class, query: params[:query]).call
|
124
129
|
end
|
125
130
|
|
126
131
|
def resource_associations
|
@@ -0,0 +1,21 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static targets = ["copyIcon", "successIcon"];
|
5
|
+
static values = { content: String };
|
6
|
+
|
7
|
+
async copy() {
|
8
|
+
try {
|
9
|
+
await navigator.clipboard.writeText(this.contentValue);
|
10
|
+
this.toggleIcons(true);
|
11
|
+
setTimeout(() => this.toggleIcons(false), 1500);
|
12
|
+
} catch (error) {
|
13
|
+
console.error(error.message);
|
14
|
+
}
|
15
|
+
}
|
16
|
+
|
17
|
+
toggleIcons(isCopied) {
|
18
|
+
this.copyIconTarget.classList.toggle("hidden", isCopied);
|
19
|
+
this.successIconTarget.classList.toggle("hidden", !isCopied);
|
20
|
+
}
|
21
|
+
}
|
@@ -0,0 +1,9 @@
|
|
1
|
+
import { Application } from "@hotwired/stimulus";
|
2
|
+
|
3
|
+
const application = Application.start();
|
4
|
+
|
5
|
+
application.debug = false;
|
6
|
+
window.Stimulus = application;
|
7
|
+
|
8
|
+
import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading";
|
9
|
+
eagerLoadControllersFrom("controllers", application);
|
@@ -0,0 +1,17 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
connect() {
|
5
|
+
setTimeout(() => {
|
6
|
+
this.element.classList.remove("opacity-0");
|
7
|
+
}, 100);
|
8
|
+
|
9
|
+
setTimeout(() => {
|
10
|
+
this.element.classList.remove("opacity-100");
|
11
|
+
this.element.classList.add("opacity-0");
|
12
|
+
}, 2500);
|
13
|
+
setTimeout(() => {
|
14
|
+
this.element.outerHTML = "";
|
15
|
+
}, 3000);
|
16
|
+
}
|
17
|
+
}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static targets = ["revealable"];
|
5
|
+
static values = { revealableId: String };
|
6
|
+
|
7
|
+
connect() {
|
8
|
+
if (this.hasRevealableIdValue)
|
9
|
+
this.target = document.getElementById(this.revealableIdValue);
|
10
|
+
|
11
|
+
if (!this.target && this.hasRevealableTarget)
|
12
|
+
this.target = this.revealableTarget;
|
13
|
+
}
|
14
|
+
|
15
|
+
toggle() {
|
16
|
+
if (!this.target) return;
|
17
|
+
this.target.classList.toggle("hidden");
|
18
|
+
}
|
19
|
+
}
|
@@ -0,0 +1,21 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
2
|
+
|
3
|
+
const KEY_BASE = "oversee-sidebar";
|
4
|
+
|
5
|
+
export default class extends Controller {
|
6
|
+
initialize() {
|
7
|
+
this.key = `${KEY_BASE}--${this.element.id}`;
|
8
|
+
this.expanded = localStorage.getItem(this.key) === "true";
|
9
|
+
this.element.open = this.expanded;
|
10
|
+
}
|
11
|
+
|
12
|
+
toggle() {
|
13
|
+
this.expanded = !this.expanded;
|
14
|
+
this.element.open = this.expanded;
|
15
|
+
}
|
16
|
+
|
17
|
+
persist(event) {
|
18
|
+
this.expanded = event.newState === "open";
|
19
|
+
localStorage.setItem(this.key, this.expanded);
|
20
|
+
}
|
21
|
+
}
|