oversee 0.2.0 → 0.3.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.
- checksums.yaml +4 -4
- data/app/assets/config/oversee_manifest.js +2 -0
- data/app/components/oversee/dashboard/filters.rb +23 -19
- 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 +19 -10
- data/app/components/oversee/field/form.rb +4 -23
- data/app/components/oversee/field/input/belongs_to.rb +22 -0
- data/app/components/oversee/field/input/boolean.rb +1 -11
- data/app/components/oversee/field/input/datetime.rb +1 -11
- data/app/components/oversee/field/input/integer.rb +1 -11
- data/app/components/oversee/field/input/rich_text.rb +18 -0
- data/app/components/oversee/field/input/string.rb +1 -11
- data/app/components/oversee/field/input.rb +13 -5
- data/app/components/oversee/field/label.rb +3 -2
- data/app/components/oversee/field/value/belongs_to.rb +21 -0
- data/app/components/oversee/field/value/datetime.rb +1 -1
- data/app/components/oversee/field/value/rich_text.rb +9 -0
- data/app/components/oversee/field/value.rb +11 -5
- data/app/components/oversee/layout/application.rb +46 -0
- data/app/components/oversee/resources/index.rb +21 -2
- data/app/components/oversee/resources/new.rb +7 -3
- data/app/components/oversee/resources/show.rb +150 -69
- data/app/components/oversee/resources/table.rb +7 -1
- data/app/controllers/oversee/dashboard_controller.rb +1 -1
- data/app/controllers/oversee/resources/fields_controller.rb +20 -36
- data/app/controllers/oversee/resources_controller.rb +22 -22
- data/app/javascript/oversee/application.js +3 -0
- data/app/javascript/oversee/controllers/index.js +8 -0
- data/app/javascript/oversee/controllers/reveal_controller.js +7 -0
- data/app/javascript/oversee/controllers/sidebar/state_controller.js +21 -0
- data/app/models/oversee/resource.rb +43 -17
- 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 +12 -0
- data/lib/tasks/oversee_tasks.rake +6 -4
- metadata +31 -8
- 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
@@ -1,13 +1,19 @@
|
|
1
1
|
class Oversee::Field::Value < Oversee::Base
|
2
2
|
MAP = {
|
3
3
|
string: Oversee::Field::Value::String,
|
4
|
+
belongs_to: Oversee::Field::Value::BelongsTo,
|
4
5
|
boolean: Oversee::Field::Value::Boolean,
|
5
|
-
integer: Oversee::Field::Value::Integer,
|
6
6
|
datetime: Oversee::Field::Value::Datetime,
|
7
|
+
enum: Oversee::Field::Value::Enum,
|
8
|
+
integer: Oversee::Field::Value::Integer,
|
9
|
+
rich_text: Oversee::Field::Value::RichText,
|
7
10
|
text: Oversee::Field::Value::Text,
|
8
|
-
enum: Oversee::Field::Value::Enum
|
9
11
|
}
|
10
12
|
|
13
|
+
attr_reader :key
|
14
|
+
attr_reader :value
|
15
|
+
attr_reader :datatype
|
16
|
+
|
11
17
|
def initialize(key: nil, value: nil, datatype: :string, **options)
|
12
18
|
@key = key
|
13
19
|
@value = value
|
@@ -18,14 +24,14 @@ class Oversee::Field::Value < Oversee::Base
|
|
18
24
|
end
|
19
25
|
|
20
26
|
def view_template
|
21
|
-
return p(class: "text-gray-400 text-xs"){ "—" } if
|
22
|
-
render component_class.new(key
|
27
|
+
return p(class: "text-gray-400 text-xs"){ "—" } if value.nil?
|
28
|
+
render component_class.new(key:, value:, **@options)
|
23
29
|
end
|
24
30
|
|
25
31
|
private
|
26
32
|
|
27
33
|
def component_class
|
28
|
-
MAP[
|
34
|
+
MAP[datatype.to_sym] || Oversee::Field::Value::String
|
29
35
|
end
|
30
36
|
|
31
37
|
def for_table?
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Oversee::Layout::Application < Oversee::Base
|
4
|
+
extend Phlex::Kit
|
5
|
+
|
6
|
+
include Phlex::Rails::Helpers::CSPMetaTag
|
7
|
+
include Phlex::Rails::Helpers::CSRFMetaTags
|
8
|
+
include Phlex::Rails::Helpers::StylesheetLinkTag
|
9
|
+
include Phlex::Rails::Helpers::JavascriptImportmapTags
|
10
|
+
|
11
|
+
def view_template
|
12
|
+
doctype
|
13
|
+
html do
|
14
|
+
# render Head
|
15
|
+
head do
|
16
|
+
csrf_meta_tags
|
17
|
+
csp_meta_tag
|
18
|
+
|
19
|
+
title { "Oversee | #{Oversee.application_name}" }
|
20
|
+
|
21
|
+
meta(name: "viewport", content: "width=device-width, initial-scale=1.0")
|
22
|
+
meta(name: "ROBOTS", content: "NOODP")
|
23
|
+
|
24
|
+
link(rel: "stylesheet", type: "text/css", href: "https://unpkg.com/trix@2.1.8/dist/trix.css")
|
25
|
+
|
26
|
+
render Oversee::Dashboard::Javascript.new
|
27
|
+
render Oversee::Dashboard::Tailwind.new
|
28
|
+
end
|
29
|
+
|
30
|
+
body(class: "min-h-screen bg-gray-100 p-4") do
|
31
|
+
# render Flash.new
|
32
|
+
|
33
|
+
div(class: "flex gap-4 w-full") do
|
34
|
+
div(class: "w-72 shrink-0") { render Oversee::Dashboard::Sidebar.new }
|
35
|
+
div(class: "w-full overflow-scroll") do
|
36
|
+
div(class: "bg-white rounded-lg overflow-clip p-4") do
|
37
|
+
yield
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -9,12 +9,31 @@ class Oversee::Resources::Index < Oversee::Base
|
|
9
9
|
end
|
10
10
|
|
11
11
|
|
12
|
+
def around_template
|
13
|
+
render Oversee::Layout::Application.new { super }
|
14
|
+
end
|
15
|
+
|
12
16
|
def view_template
|
13
|
-
render Oversee::Dashboard::Header.new(title: @resource_class.to_s
|
14
|
-
|
17
|
+
render Oversee::Dashboard::Header.new(title: @resource_class.to_s.pluralize) do |h|
|
18
|
+
h.left
|
19
|
+
h.right do
|
20
|
+
a(
|
21
|
+
href: helpers.new_resource_path(@params[:resource_class_name]),
|
22
|
+
target: "_top",
|
23
|
+
class: "inline-flex items-center justify-center gap-1.5 h-8 px-4 rounded-full bg-transparent text-gray-900 hover:bg-gray-100 text-sm font-medium transition group"
|
24
|
+
) do
|
25
|
+
render Phlex::Icons::Iconoir::Plus.new(class: "size-4 text-gray-500 group-hover:text-blue-500", stroke_width: 2.5)
|
26
|
+
plain "Add new"
|
27
|
+
end
|
28
|
+
end
|
15
29
|
end
|
30
|
+
hr(class: "my-4")
|
31
|
+
|
16
32
|
render Oversee::Dashboard::Filters.new(params: @params)
|
33
|
+
|
34
|
+
hr(class: "mt-4")
|
17
35
|
render Oversee::Resources::Table.new(resource_class: @resource_class, resources: @resources, params: @params)
|
36
|
+
hr()
|
18
37
|
render Oversee::Dashboard::Pagination.new(pagy: @pagy, params: @params)
|
19
38
|
end
|
20
39
|
|
@@ -7,11 +7,15 @@ class Oversee::Resources::New < Oversee::Base
|
|
7
7
|
@params = params
|
8
8
|
end
|
9
9
|
|
10
|
+
def around_template
|
11
|
+
render Oversee::Layout::Application.new { super }
|
12
|
+
end
|
13
|
+
|
10
14
|
def view_template
|
11
15
|
render Oversee::Dashboard::Header.new(title: @resource_class.to_s, subtitle: "Creating new record")
|
12
16
|
|
13
|
-
|
14
|
-
|
15
|
-
|
17
|
+
hr(class: "my-4")
|
18
|
+
|
19
|
+
render Oversee::Resources::Form.new(resource: @resource)
|
16
20
|
end
|
17
21
|
end
|
@@ -4,104 +4,166 @@ 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
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
58
|
+
hr(class: "my-4")
|
59
|
+
|
60
|
+
|
61
|
+
|
62
|
+
|
63
|
+
# COLUMNS
|
64
|
+
@resource_class.columns_hash.each do |key, metadata|
|
65
|
+
next if @oversee_resource.foreign_keys.include?(key.to_s)
|
66
|
+
|
67
|
+
div(class: "py-4") do
|
68
|
+
div(class: "space-y-2") do
|
69
|
+
render Oversee::Field::Label.new(key: key, datatype: metadata.sql_type_metadata.type)
|
70
|
+
div(id: dom_id(@resource, :"#{key}_row"), class: "flex items-center gap-2 mt-4") do
|
71
|
+
render Oversee::Field::Display.new(resource:, key:, value: @resource.send(key), datatype: metadata.sql_type_metadata.type)
|
72
|
+
# div(id: dom_id(@resource, :"#{key}_actions")) do
|
73
|
+
# button(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::Copy.new(class: "size-4") }
|
74
|
+
# end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
hr(class: "my-4")
|
81
|
+
|
82
|
+
# RICH TEXT Associations
|
83
|
+
if !!rich_text_associations.length
|
84
|
+
rich_text_associations.each do |association|
|
85
|
+
|
86
|
+
# Remove the "rich_text_" prefix from the association name
|
87
|
+
key = association[:name].to_s[10..].to_sym
|
88
|
+
|
89
|
+
div do
|
90
|
+
div(class: "space-y-4") do
|
91
|
+
div(class:"flex items-center gap-2") do
|
92
|
+
render Oversee::Field::Label.new(key: key.to_s.titleize, datatype: :rich_text)
|
93
|
+
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") }
|
94
|
+
end
|
95
|
+
|
96
|
+
div(id: dom_id(@resource, :"#{key.to_s}_row"), class: "flex items-center gap-2 mt-4") do
|
97
|
+
render Oversee::Field::Display.new(resource:, key:, value: @resource.send(key).to_plain_text[..196], datatype: :rich_text)
|
31
98
|
end
|
32
99
|
end
|
33
100
|
end
|
34
101
|
end
|
102
|
+
end
|
35
103
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
104
|
+
# BELONGS_TO Associations
|
105
|
+
if !!belongs_to_associations.length
|
106
|
+
belongs_to_associations.each do |association|
|
107
|
+
div(class: "py-6") do
|
108
|
+
div(class: "space-y-4") do
|
109
|
+
div(class:"flex items-center gap-2") do
|
110
|
+
render Oversee::Field::Label.new(key: association[:name].to_s.titleize, datatype: :data)
|
111
|
+
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") }
|
112
|
+
end
|
45
113
|
|
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
|
114
|
+
foreign_key = association[:foreign_key]
|
115
|
+
foreign_key_value = @resource[association[:foreign_key]]
|
116
|
+
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])
|
117
|
+
|
118
|
+
div(id: dom_id(@resource, :"#{foreign_key}_row"), class: "flex items-center gap-2 mt-4") do
|
119
|
+
render Oversee::Field::Display.new(resource:, key: foreign_key, value: foreign_key_value, datatype: :belongs_to, display_key: true)
|
120
|
+
div(id: dom_id(@resource, :"#{foreign_key}_actions")) do
|
121
|
+
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
122
|
end
|
59
123
|
end
|
60
124
|
end
|
61
125
|
end
|
62
126
|
end
|
127
|
+
end
|
128
|
+
|
129
|
+
hr(class: "my-4")
|
63
130
|
|
64
|
-
|
65
|
-
|
131
|
+
# HAS_MANY Associations
|
132
|
+
if !!has_many_associations.length
|
133
|
+
div(class: "flex flex-col gap-8") do
|
66
134
|
has_many_associations.each do |association|
|
67
135
|
associated_resources = @resource.send(association[:name])
|
68
136
|
associated_resource_class = association[:class_name].constantize
|
69
137
|
|
70
|
-
div(class: "
|
71
|
-
div(class:
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
end
|
138
|
+
div(class: "space-y-4") do
|
139
|
+
div(class:"flex items-center gap-2") do
|
140
|
+
render Oversee::Field::Label.new(key: association[:name].to_s.titleize, datatype: :data)
|
141
|
+
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") }
|
142
|
+
end
|
76
143
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
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
|
+
div(class: "bg-gray-50 p-2") do
|
145
|
+
# turbo_frame_tag(
|
146
|
+
# dom_id(associated_resource_class, :table),
|
147
|
+
# src: helpers.resources_table_path(resources_table_params(association)),
|
148
|
+
# loading: :lazy,
|
149
|
+
# data: { turbo_stream: true }
|
150
|
+
# ) do
|
151
|
+
# div(class: "h-20 flex items-center justify-center") { render Phlex::Icons::Iconoir::DatabaseSearch.new(class: "animate-pulse size-6 text-gray-600") }
|
152
|
+
# end
|
153
|
+
|
154
|
+
if associated_resources.present?
|
155
|
+
div(class: "bg-white") do
|
156
|
+
render Oversee::Resources::Table.new(
|
157
|
+
resources: associated_resources,
|
158
|
+
resource_class: associated_resource_class,
|
159
|
+
params: @params
|
160
|
+
)
|
104
161
|
end
|
162
|
+
else
|
163
|
+
p(class: "bg-gray-50 p-2 pr-4 flex gap-2 items-center text-xs") {
|
164
|
+
render Phlex::Icons::Iconoir::DatabaseSearch.new(class: "size-3")
|
165
|
+
plain "No #{association[:name].to_s.titleize.downcase} found"
|
166
|
+
}
|
105
167
|
end
|
106
168
|
end
|
107
169
|
end
|
@@ -112,6 +174,10 @@ class Oversee::Resources::Show < Oversee::Base
|
|
112
174
|
|
113
175
|
private
|
114
176
|
|
177
|
+
def rich_text_associations
|
178
|
+
@oversee_resource.rich_text_associations
|
179
|
+
end
|
180
|
+
|
115
181
|
def belongs_to_associations
|
116
182
|
@oversee_resource.associations[:belongs_to]
|
117
183
|
end
|
@@ -123,4 +189,19 @@ class Oversee::Resources::Show < Oversee::Base
|
|
123
189
|
def has_associations?
|
124
190
|
@resource_associations.present?
|
125
191
|
end
|
192
|
+
|
193
|
+
def resources_table_params(association)
|
194
|
+
# if association[:through].nil?
|
195
|
+
# return { association[:foreign_key] => { eq: [@resource.id] } }
|
196
|
+
# else
|
197
|
+
# keys = @resource.send(association[:through]).pluck(association[:foreign_key])
|
198
|
+
# return { @resource_class.primary_key => { eq: keys } }
|
199
|
+
# end
|
200
|
+
|
201
|
+
{
|
202
|
+
resource_class_name: association[:class_name],
|
203
|
+
association_name: association[:through],
|
204
|
+
filters: { association[:foreign_key] => { eq: [@resource.id] } }
|
205
|
+
}
|
206
|
+
end
|
126
207
|
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,6 +75,7 @@ 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
81
|
render Oversee::Field::Value.new(datatype: metadata.sql_type_metadata.type, value: resource.send(key), key: key)
|
@@ -1,44 +1,28 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
class Oversee::Resources::FieldsController < Oversee::ResourcesController
|
2
|
+
# Renders the display field for a resource
|
3
|
+
def show
|
4
|
+
end
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
# @datatype = @resource_class.columns_hash[@key.to_s].type
|
6
|
+
# Renders the input field for a resource
|
7
|
+
def input
|
8
|
+
set_resource
|
10
9
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
format.turbo_stream
|
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 "---"
|
10
|
+
key = params[:key].to_sym
|
11
|
+
value = params[:value] || @resource.send(key)
|
12
|
+
datatype = params[:datatype] || @resource.class.columns_hash[key.to_s].type
|
31
13
|
|
32
|
-
|
14
|
+
# puts "---" * 30
|
15
|
+
# puts "key: #{key}"
|
16
|
+
# puts "value: #{value}"
|
17
|
+
# puts "datatype: #{datatype}"
|
18
|
+
# puts "---" * 30
|
33
19
|
|
34
|
-
|
35
|
-
|
36
|
-
def set_resource_class
|
37
|
-
@resource_class = params[:resource_class].constantize
|
38
|
-
end
|
20
|
+
field_dom_id = dom_id(@resource, key)
|
21
|
+
field = Oversee::Field::Form.new(resource: @resource, datatype:, key:, value:)
|
39
22
|
|
40
|
-
|
41
|
-
|
23
|
+
respond_to do |format|
|
24
|
+
format.turbo_stream do
|
25
|
+
render turbo_stream: turbo_stream.replace(field_dom_id, field)
|
42
26
|
end
|
43
27
|
end
|
44
28
|
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
|
@@ -46,7 +46,7 @@ module Oversee
|
|
46
46
|
resource_class: @resource_class,
|
47
47
|
resource_associations: resource_associations,
|
48
48
|
params: params
|
49
|
-
)
|
49
|
+
), layout: false
|
50
50
|
end
|
51
51
|
|
52
52
|
def edit
|
@@ -73,25 +73,25 @@ module Oversee
|
|
73
73
|
redirect_to resources_path(resource: @resource_class)
|
74
74
|
end
|
75
75
|
|
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:)
|
76
|
+
def association
|
77
|
+
@resources = @resource_class.find(params[:id]).send(params[:association_name])
|
78
|
+
@pagy, @resources = pagy(@resources, limit: params[:per_page] || Oversee.configuration.per_page)
|
86
79
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
80
|
+
render Oversee::Resources::Index.new(
|
81
|
+
resources: @resources,
|
82
|
+
resource_class: @resource_class,
|
83
|
+
pagy: @pagy,
|
84
|
+
params: params
|
85
|
+
)
|
92
86
|
end
|
93
87
|
|
94
88
|
def table
|
89
|
+
if params[:association_name].present?
|
90
|
+
@resources = @resource_class.find(params[:id]).send(params[:association_name])
|
91
|
+
else
|
92
|
+
set_resources
|
93
|
+
end
|
94
|
+
|
95
95
|
component_id = dom_id(@resource_class, :table)
|
96
96
|
component = Oversee::Resources::Table.new(resource_class: @resource_class, resources: @resources, params: params)
|
97
97
|
|
@@ -119,8 +119,8 @@ module Oversee
|
|
119
119
|
set_sorting_rules
|
120
120
|
|
121
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
|
122
|
+
@resources = Oversee::Filter.new(collection: @resources, params:).apply
|
123
|
+
@resources = Oversee::Search.new(collection: @resources, resource_class: @resource_class, query: params[:query]).call
|
124
124
|
end
|
125
125
|
|
126
126
|
def resource_associations
|
@@ -0,0 +1,8 @@
|
|
1
|
+
import { Application } from "@hotwired/stimulus";
|
2
|
+
const application = Application.start();
|
3
|
+
|
4
|
+
application.debug = false;
|
5
|
+
window.Stimulus = application;
|
6
|
+
|
7
|
+
import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading";
|
8
|
+
eagerLoadControllersFrom("controllers", application);
|
@@ -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
|
+
}
|