terrazzo 0.2.2 → 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/controllers/terrazzo/application_controller.rb +7 -1
- data/app/views/terrazzo/application/_navigation.json.props +12 -0
- data/app/views/terrazzo/application/edit.json.props +0 -8
- data/app/views/terrazzo/application/index.json.props +0 -9
- data/app/views/terrazzo/application/new.json.props +0 -8
- data/app/views/terrazzo/application/show.json.props +0 -9
- data/lib/generators/terrazzo/eject/eject_generator.rb +232 -0
- data/lib/generators/terrazzo/field/field_generator.rb +16 -9
- data/lib/generators/terrazzo/field/templates/FormField.jsx.erb +1 -2
- data/lib/generators/terrazzo/install/install_generator.rb +1 -1
- data/lib/generators/terrazzo/install/templates/admin.css +1 -0
- data/lib/generators/terrazzo/install/templates/{application.json.props → application.json.props.tt} +1 -0
- data/lib/generators/terrazzo/install/templates/page_to_page_mapping.js.erb +4 -15
- data/lib/generators/terrazzo/views/templates/components/Layout.jsx +1 -1
- data/lib/generators/terrazzo/views/templates/components/Pagination.jsx +3 -3
- data/lib/generators/terrazzo/views/templates/components/SearchBar.jsx +1 -1
- data/lib/generators/terrazzo/views/templates/components/SortableHeader.jsx +1 -1
- data/lib/generators/terrazzo/views/templates/components/app-sidebar.jsx +23 -22
- data/lib/generators/terrazzo/views/templates/components/site-header.jsx +2 -2
- data/lib/generators/terrazzo/views/templates/components/ui/index.js +1 -1
- data/lib/generators/terrazzo/views/templates/fields/belongs_to/FormField.jsx +1 -1
- data/lib/generators/terrazzo/views/templates/fields/boolean/FormField.jsx +1 -1
- data/lib/generators/terrazzo/views/templates/fields/boolean/IndexField.jsx +1 -1
- data/lib/generators/terrazzo/views/templates/fields/has_many/FormField.jsx +3 -3
- data/lib/generators/terrazzo/views/templates/fields/has_many/IndexField.jsx +1 -1
- data/lib/generators/terrazzo/views/templates/fields/has_many/ShowField.jsx +17 -5
- data/lib/generators/terrazzo/views/templates/fields/has_one/FormField.jsx +1 -1
- data/lib/generators/terrazzo/views/templates/fields/hstore/FormField.jsx +3 -3
- data/lib/generators/terrazzo/views/templates/fields/hstore/IndexField.jsx +1 -1
- data/lib/generators/terrazzo/views/templates/fields/hstore/ShowField.jsx +1 -1
- data/lib/generators/terrazzo/views/templates/fields/polymorphic/FormField.jsx +1 -1
- data/lib/generators/terrazzo/views/templates/fields/rich_text/FormField.jsx +2 -2
- data/lib/generators/terrazzo/views/templates/fields/select/FormField.jsx +1 -1
- data/lib/generators/terrazzo/views/templates/fields/select/IndexField.jsx +1 -1
- data/lib/generators/terrazzo/views/templates/fields/shared/TextInputFormField.jsx +2 -2
- data/lib/generators/terrazzo/views/templates/fields/text/FormField.jsx +2 -2
- data/lib/generators/terrazzo/views/templates/pages/_form.jsx +2 -2
- data/lib/generators/terrazzo/views/templates/pages/_navigation.json.props +5 -0
- data/lib/generators/terrazzo/views/templates/pages/edit.jsx +2 -3
- data/lib/generators/terrazzo/views/templates/pages/index.jsx +3 -14
- data/lib/generators/terrazzo/views/templates/pages/new.jsx +2 -3
- data/lib/generators/terrazzo/views/templates/pages/show.jsx +3 -4
- data/lib/generators/terrazzo/views/views_generator.rb +45 -26
- data/lib/terrazzo/field/associative.rb +1 -1
- data/lib/terrazzo/field/belongs_to.rb +2 -2
- data/lib/terrazzo/field/has_many.rb +12 -5
- data/lib/terrazzo/field/has_one.rb +2 -2
- data/lib/terrazzo/field/polymorphic.rb +3 -3
- data/lib/terrazzo/version.rb +1 -1
- data/terrazzo.gemspec +1 -1
- metadata +6 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a9ba7563c1db759831717790db2c022eb3f2d343c3f393f6679e164c250f8fc5
|
|
4
|
+
data.tar.gz: a985e9f57b97f224b26a58cd72d9e580666d0aae1b16efd8e722ce14460ad860
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 877fed40c106cc9ed5443197322033abd8c4d51c5fa69cb596d63ca77b355ffeb4c3235a13125a6bb6c7f1f28a747cab2c2674ebf08fee6bb4a2c7f225fc1eed
|
|
7
|
+
data.tar.gz: 24cdb50150d40149fda75e5fe376978222ab3b200247a006989cb52e98ea2e9482747ab49b3880f7557cb072ca089691f7468f3573a778eaec764bcf45b6cf3c
|
|
@@ -196,9 +196,15 @@ module Terrazzo
|
|
|
196
196
|
# Build a page identifier that matches the user's namespace, not the
|
|
197
197
|
# engine's internal template path. The React page-to-component mapping
|
|
198
198
|
# keys off this identifier (e.g. "admin/application/index").
|
|
199
|
+
#
|
|
200
|
+
# Map create → new and update → edit so that failed validations
|
|
201
|
+
# (which render :new / :edit) resolve to the correct React component.
|
|
202
|
+
TERRAZZO_ACTION_MAP = { "create" => "new", "update" => "edit" }.freeze
|
|
203
|
+
|
|
199
204
|
def terrazzo_page_identifier
|
|
200
205
|
ns = controller_path.split("/").first
|
|
201
|
-
|
|
206
|
+
mapped_action = TERRAZZO_ACTION_MAP[action_name] || action_name
|
|
207
|
+
"#{ns}/application/#{mapped_action}"
|
|
202
208
|
end
|
|
203
209
|
|
|
204
210
|
private
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
resources = Terrazzo::Namespace.new(namespace).resources_with_index_route
|
|
2
|
+
|
|
3
|
+
json.array! [{ label: "Resources", resources: resources }] do |group|
|
|
4
|
+
json.label group[:label]
|
|
5
|
+
json.items do
|
|
6
|
+
json.array! group[:resources] do |r|
|
|
7
|
+
json.label r.resource_name.humanize.pluralize
|
|
8
|
+
json.path url_for(controller: "/#{r.controller_path}", action: :index, only_path: true)
|
|
9
|
+
json.active r.controller_path == controller_path
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -60,11 +60,3 @@ rescue ActionController::UrlGenerationError
|
|
|
60
60
|
nil
|
|
61
61
|
end
|
|
62
62
|
json.resourceName resource_name
|
|
63
|
-
|
|
64
|
-
json.navigation do
|
|
65
|
-
json.array! Terrazzo::Namespace.new(namespace).resources_with_index_route do |nav_resource|
|
|
66
|
-
json.label nav_resource.resource_name.humanize.pluralize
|
|
67
|
-
json.path url_for(controller: "/#{nav_resource.controller_path}", action: :index, only_path: true)
|
|
68
|
-
json.active nav_resource.controller_path == controller_path
|
|
69
|
-
end
|
|
70
|
-
end
|
|
@@ -42,7 +42,6 @@ json.table do
|
|
|
42
42
|
json.attribute attr.to_s
|
|
43
43
|
json.fieldType field.field_type
|
|
44
44
|
json.value field.serialize_value(:index)
|
|
45
|
-
json.options field.serializable_options
|
|
46
45
|
|
|
47
46
|
if field.class.associative? && field.data.present?
|
|
48
47
|
json.showPath polymorphic_path([namespace, field.data]) rescue nil
|
|
@@ -91,11 +90,3 @@ end
|
|
|
91
90
|
json.resourceName resource_name.pluralize
|
|
92
91
|
json.singularResourceName resource_name
|
|
93
92
|
json.newResourcePath new_polymorphic_path([namespace, resource_class]) rescue nil
|
|
94
|
-
|
|
95
|
-
json.navigation do
|
|
96
|
-
json.array! Terrazzo::Namespace.new(namespace).resources_with_index_route do |nav_resource|
|
|
97
|
-
json.label nav_resource.resource_name.humanize.pluralize
|
|
98
|
-
json.path url_for(controller: "/#{nav_resource.controller_path}", action: :index, only_path: true)
|
|
99
|
-
json.active nav_resource.controller_path == controller_path
|
|
100
|
-
end
|
|
101
|
-
end
|
|
@@ -55,11 +55,3 @@ rescue ActionController::UrlGenerationError
|
|
|
55
55
|
nil
|
|
56
56
|
end
|
|
57
57
|
json.resourceName resource_name
|
|
58
|
-
|
|
59
|
-
json.navigation do
|
|
60
|
-
json.array! Terrazzo::Namespace.new(namespace).resources_with_index_route do |nav_resource|
|
|
61
|
-
json.label nav_resource.resource_name.humanize.pluralize
|
|
62
|
-
json.path url_for(controller: "/#{nav_resource.controller_path}", action: :index, only_path: true)
|
|
63
|
-
json.active nav_resource.controller_path == controller_path
|
|
64
|
-
end
|
|
65
|
-
end
|
|
@@ -5,7 +5,6 @@ show_field_json = ->(json, field) do
|
|
|
5
5
|
json.label field.attribute.to_s.humanize
|
|
6
6
|
json.fieldType field.field_type
|
|
7
7
|
json.value field.serialize_value(:show)
|
|
8
|
-
json.options field.serializable_options
|
|
9
8
|
|
|
10
9
|
if field.class.associative? && field.data.present?
|
|
11
10
|
if field.is_a?(Terrazzo::Field::HasMany)
|
|
@@ -44,11 +43,3 @@ rescue ActionController::UrlGenerationError
|
|
|
44
43
|
end
|
|
45
44
|
json.resourceName resource_name
|
|
46
45
|
json.pluralResourceName resource_name.pluralize
|
|
47
|
-
|
|
48
|
-
json.navigation do
|
|
49
|
-
json.array! Terrazzo::Namespace.new(namespace).resources_with_index_route do |nav_resource|
|
|
50
|
-
json.label nav_resource.resource_name.humanize.pluralize
|
|
51
|
-
json.path url_for(controller: "/#{nav_resource.controller_path}", action: :index, only_path: true)
|
|
52
|
-
json.active nav_resource.controller_path == controller_path
|
|
53
|
-
end
|
|
54
|
-
end
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
require "rails/generators"
|
|
2
|
+
|
|
3
|
+
module Terrazzo
|
|
4
|
+
module Generators
|
|
5
|
+
class EjectGenerator < Rails::Generators::Base
|
|
6
|
+
source_root File.expand_path("../views/templates", __dir__)
|
|
7
|
+
|
|
8
|
+
argument :target, type: :string,
|
|
9
|
+
desc: "What to eject (e.g., fields/string, components/Layout, ui/button, pages/index)"
|
|
10
|
+
|
|
11
|
+
class_option :namespace, type: :string, default: "admin",
|
|
12
|
+
desc: "Admin namespace"
|
|
13
|
+
|
|
14
|
+
def eject
|
|
15
|
+
case category
|
|
16
|
+
when "fields"
|
|
17
|
+
eject_field
|
|
18
|
+
when "components"
|
|
19
|
+
eject_component
|
|
20
|
+
when "ui"
|
|
21
|
+
eject_ui
|
|
22
|
+
when "pages"
|
|
23
|
+
eject_page
|
|
24
|
+
when "navigation"
|
|
25
|
+
eject_navigation
|
|
26
|
+
else
|
|
27
|
+
say_status :error, "Unknown category '#{category}'. Use fields/, components/, ui/, pages/, or navigation", :red
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def category
|
|
34
|
+
target.split("/").first
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def component_name
|
|
38
|
+
target.split("/", 2).last
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def namespace_name
|
|
42
|
+
options[:namespace]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def eject_field
|
|
46
|
+
field_type = component_name
|
|
47
|
+
source_dir = "fields/#{field_type}"
|
|
48
|
+
|
|
49
|
+
unless File.directory?(File.join(self.class.source_root, source_dir))
|
|
50
|
+
say_status :error, "Unknown field type '#{field_type}'", :red
|
|
51
|
+
return
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
%w[IndexField.jsx ShowField.jsx FormField.jsx].each do |file|
|
|
55
|
+
source = File.join(source_dir, file)
|
|
56
|
+
next unless File.exist?(File.join(self.class.source_root, source))
|
|
57
|
+
copy_file source, "app/views/#{namespace_name}/fields/#{field_type}/#{file}"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Copy shared dependencies if needed
|
|
61
|
+
if field_uses_shared?(field_type)
|
|
62
|
+
copy_file "fields/shared/TextInputFormField.jsx",
|
|
63
|
+
"app/views/#{namespace_name}/fields/shared/TextInputFormField.jsx"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
update_fields_barrel(field_type)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def eject_component
|
|
70
|
+
name = component_name
|
|
71
|
+
source = "components/#{name}.jsx"
|
|
72
|
+
|
|
73
|
+
unless File.exist?(File.join(self.class.source_root, source))
|
|
74
|
+
say_status :error, "Unknown component '#{name}'", :red
|
|
75
|
+
return
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
copy_file source, "app/views/#{namespace_name}/components/#{name}.jsx"
|
|
79
|
+
update_components_barrel(name)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def eject_ui
|
|
83
|
+
name = component_name
|
|
84
|
+
source = "components/ui/#{name}.jsx"
|
|
85
|
+
|
|
86
|
+
unless File.exist?(File.join(self.class.source_root, source))
|
|
87
|
+
say_status :error, "Unknown UI component '#{name}'", :red
|
|
88
|
+
return
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
copy_file source, "app/views/#{namespace_name}/components/ui/#{name}.jsx"
|
|
92
|
+
update_ui_barrel(name)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def eject_page
|
|
96
|
+
name = component_name
|
|
97
|
+
source = "pages/#{name}.jsx"
|
|
98
|
+
|
|
99
|
+
unless File.exist?(File.join(self.class.source_root, source))
|
|
100
|
+
say_status :error, "Unknown page template '#{name}'", :red
|
|
101
|
+
return
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
copy_file source, "app/views/#{namespace_name}/application/#{name}.jsx"
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def eject_navigation
|
|
108
|
+
source = File.join(Terrazzo::Engine.root, "app/views/terrazzo/application/_navigation.json.props")
|
|
109
|
+
dest = "app/views/#{namespace_name}/application/_navigation.json.props"
|
|
110
|
+
copy_file source, dest
|
|
111
|
+
say "\nNavigation partial ejected to #{dest}.", :green
|
|
112
|
+
say "Edit it to customize your admin navigation."
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def field_uses_shared?(field_type)
|
|
116
|
+
%w[string number email url password date date_time time].include?(field_type)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def update_fields_barrel(field_type)
|
|
120
|
+
barrel_path = "app/views/#{namespace_name}/fields/index.js"
|
|
121
|
+
barrel_file = File.join(destination_root, barrel_path)
|
|
122
|
+
|
|
123
|
+
type_label = field_type.split("_").map(&:capitalize).join("")
|
|
124
|
+
local_exports = <<~JS.strip
|
|
125
|
+
// #{type_label} - ejected
|
|
126
|
+
export { IndexField as #{type_label}IndexField } from "./#{field_type}/IndexField";
|
|
127
|
+
export { ShowField as #{type_label}ShowField } from "./#{field_type}/ShowField";
|
|
128
|
+
export { FormField as #{type_label}FormField } from "./#{field_type}/FormField";
|
|
129
|
+
JS
|
|
130
|
+
|
|
131
|
+
if File.exist?(barrel_file)
|
|
132
|
+
content = File.read(barrel_file)
|
|
133
|
+
|
|
134
|
+
# If barrel is still the default re-export-all, replace with explicit exports
|
|
135
|
+
if content.include?('export * from "terrazzo/fields"')
|
|
136
|
+
new_content = build_fields_barrel_with_ejection(field_type)
|
|
137
|
+
create_file barrel_path, new_content, force: true
|
|
138
|
+
else
|
|
139
|
+
# Barrel already has explicit exports; replace the line for this field type
|
|
140
|
+
# by inserting local exports before the terrazzo re-exports
|
|
141
|
+
unless content.include?("./#{field_type}/")
|
|
142
|
+
append_to_file barrel_path, "\n#{local_exports}\n"
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def build_fields_barrel_with_ejection(ejected_field_type)
|
|
149
|
+
all_field_types = %w[
|
|
150
|
+
string text number boolean date date_time time email url password
|
|
151
|
+
select rich_text belongs_to has_many has_one polymorphic hstore
|
|
152
|
+
]
|
|
153
|
+
|
|
154
|
+
lines = ['export { FieldRenderer, registerFieldType } from "terrazzo/fields";', ""]
|
|
155
|
+
|
|
156
|
+
all_field_types.each do |ft|
|
|
157
|
+
label = ft.split("_").map(&:capitalize).join("")
|
|
158
|
+
if ft == ejected_field_type
|
|
159
|
+
lines << "// #{label} - ejected"
|
|
160
|
+
lines << "export { IndexField as #{label}IndexField } from \"./#{ft}/IndexField\";"
|
|
161
|
+
lines << "export { ShowField as #{label}ShowField } from \"./#{ft}/ShowField\";"
|
|
162
|
+
lines << "export { FormField as #{label}FormField } from \"./#{ft}/FormField\";"
|
|
163
|
+
else
|
|
164
|
+
lines << "export { #{label}IndexField, #{label}ShowField, #{label}FormField } from \"terrazzo/fields\";"
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
lines.join("\n") + "\n"
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def update_components_barrel(name)
|
|
172
|
+
barrel_path = "app/views/#{namespace_name}/components/index.js"
|
|
173
|
+
barrel_file = File.join(destination_root, barrel_path)
|
|
174
|
+
|
|
175
|
+
return unless File.exist?(barrel_file)
|
|
176
|
+
|
|
177
|
+
content = File.read(barrel_file)
|
|
178
|
+
if content.include?('export * from "terrazzo/components"')
|
|
179
|
+
export_name = component_export_name(name)
|
|
180
|
+
new_content = build_components_barrel_with_ejection(name, export_name)
|
|
181
|
+
create_file barrel_path, new_content, force: true
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def build_components_barrel_with_ejection(ejected_name, export_name)
|
|
186
|
+
all_components = {
|
|
187
|
+
"Layout" => "Layout",
|
|
188
|
+
"app-sidebar" => "AppSidebar",
|
|
189
|
+
"site-header" => "SiteHeader",
|
|
190
|
+
"FlashMessages" => "FlashMessages",
|
|
191
|
+
"SearchBar" => "SearchBar",
|
|
192
|
+
"Pagination" => "Pagination",
|
|
193
|
+
"SortableHeader" => "SortableHeader",
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
lines = []
|
|
197
|
+
all_components.each do |file_name, export|
|
|
198
|
+
if file_name == ejected_name
|
|
199
|
+
lines << "export { #{export} } from \"./#{file_name}\"; // ejected"
|
|
200
|
+
else
|
|
201
|
+
lines << "export { #{export} } from \"terrazzo/components\";"
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
lines.join("\n") + "\n"
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def update_ui_barrel(name)
|
|
209
|
+
barrel_path = "app/views/#{namespace_name}/components/ui/index.js"
|
|
210
|
+
barrel_file = File.join(destination_root, barrel_path)
|
|
211
|
+
|
|
212
|
+
return unless File.exist?(barrel_file)
|
|
213
|
+
|
|
214
|
+
content = File.read(barrel_file)
|
|
215
|
+
if content.include?('export * from "terrazzo/ui"')
|
|
216
|
+
new_content = "export * from \"terrazzo/ui\";\n"
|
|
217
|
+
new_content += "// Override ejected UI component:\n"
|
|
218
|
+
new_content += "export * from \"./#{name}\";\n"
|
|
219
|
+
create_file barrel_path, new_content, force: true
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def component_export_name(file_name)
|
|
224
|
+
case file_name
|
|
225
|
+
when "app-sidebar" then "AppSidebar"
|
|
226
|
+
when "site-header" then "SiteHeader"
|
|
227
|
+
else file_name
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
end
|
|
@@ -20,15 +20,22 @@ module Terrazzo
|
|
|
20
20
|
end
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
def
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
23
|
+
def register_in_barrel
|
|
24
|
+
barrel_path = File.join(destination_root, "app/views/#{namespace_name}/fields/index.js")
|
|
25
|
+
return unless File.exist?(barrel_path)
|
|
26
|
+
|
|
27
|
+
registration = <<~JS
|
|
28
|
+
|
|
29
|
+
// #{class_name} - custom field
|
|
30
|
+
export { IndexField as #{class_name}IndexField } from "./#{file_name}/IndexField";
|
|
31
|
+
export { ShowField as #{class_name}ShowField } from "./#{file_name}/ShowField";
|
|
32
|
+
export { FormField as #{class_name}FormField } from "./#{file_name}/FormField";
|
|
33
|
+
JS
|
|
34
|
+
|
|
35
|
+
append_to_file "app/views/#{namespace_name}/fields/index.js", registration
|
|
36
|
+
|
|
37
|
+
say "\nCustom field '#{file_name}' registered in fields/index.js.", :green
|
|
38
|
+
say "Use it in your dashboard:"
|
|
32
39
|
say " #{file_name}: Terrazzo::Field::#{class_name},"
|
|
33
40
|
end
|
|
34
41
|
|
|
@@ -1,17 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
for (const key in pages) {
|
|
7
|
-
const identifier = key.replace("../../views/", "").replace(/\.jsx$/, "")
|
|
8
|
-
pageToPageMapping[identifier] = pages[key].default
|
|
9
|
-
}
|
|
10
|
-
<% else -%>
|
|
11
|
-
import AdminIndex from "../../views/<%= namespace_name %>/application/index"
|
|
12
|
-
import AdminShow from "../../views/<%= namespace_name %>/application/show"
|
|
13
|
-
import AdminNew from "../../views/<%= namespace_name %>/application/new"
|
|
14
|
-
import AdminEdit from "../../views/<%= namespace_name %>/application/edit"
|
|
1
|
+
import AdminIndex from "../../views/<%= namespace_name %>/application/index";
|
|
2
|
+
import AdminShow from "../../views/<%= namespace_name %>/application/show";
|
|
3
|
+
import AdminNew from "../../views/<%= namespace_name %>/application/new";
|
|
4
|
+
import AdminEdit from "../../views/<%= namespace_name %>/application/edit";
|
|
15
5
|
|
|
16
6
|
export const pageToPageMapping = {
|
|
17
7
|
'<%= namespace_name %>/application/index': AdminIndex,
|
|
@@ -19,4 +9,3 @@ export const pageToPageMapping = {
|
|
|
19
9
|
'<%= namespace_name %>/application/new': AdminNew,
|
|
20
10
|
'<%= namespace_name %>/application/edit': AdminEdit,
|
|
21
11
|
}
|
|
22
|
-
<% end -%>
|
|
@@ -3,7 +3,7 @@ import React from "react";
|
|
|
3
3
|
import { AppSidebar } from "./app-sidebar";
|
|
4
4
|
import { SiteHeader } from "./site-header";
|
|
5
5
|
import { FlashMessages } from "./FlashMessages";
|
|
6
|
-
import { SidebarProvider, SidebarInset } from "
|
|
6
|
+
import { SidebarProvider, SidebarInset } from "terrazzo/ui";
|
|
7
7
|
|
|
8
8
|
export function Layout({ navigation, title, actions, children }) {
|
|
9
9
|
return (
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import React, { useContext } from "react";
|
|
2
2
|
import { NavigationContext } from "@thoughtbot/superglue";
|
|
3
3
|
|
|
4
|
-
import { Field, FieldLabel } from "
|
|
4
|
+
import { Field, FieldLabel } from "terrazzo/ui";
|
|
5
5
|
import {
|
|
6
6
|
Pagination as PaginationRoot,
|
|
7
7
|
PaginationContent,
|
|
8
8
|
PaginationItem,
|
|
9
9
|
PaginationNext,
|
|
10
10
|
PaginationPrevious,
|
|
11
|
-
} from "
|
|
11
|
+
} from "terrazzo/ui";
|
|
12
12
|
import {
|
|
13
13
|
Select,
|
|
14
14
|
SelectContent,
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
SelectItem,
|
|
17
17
|
SelectTrigger,
|
|
18
18
|
SelectValue,
|
|
19
|
-
} from "
|
|
19
|
+
} from "terrazzo/ui";
|
|
20
20
|
|
|
21
21
|
export function Pagination({ currentPage, totalPages, totalCount, perPage, nextPagePath, prevPagePath }) {
|
|
22
22
|
const { remote, pageKey } = useContext(NavigationContext);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useRef, useContext } from "react";
|
|
2
2
|
import { NavigationContext } from "@thoughtbot/superglue";
|
|
3
3
|
|
|
4
|
-
import { Input } from "
|
|
4
|
+
import { Input } from "terrazzo/ui";
|
|
5
5
|
import { Search } from "lucide-react";
|
|
6
6
|
|
|
7
7
|
export function SearchBar({ searchTerm, searchPath }) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { ChevronUp, ChevronDown, ChevronsUpDown } from "lucide-react";
|
|
3
3
|
|
|
4
|
-
import { TableHead } from "
|
|
4
|
+
import { TableHead } from "terrazzo/ui";
|
|
5
5
|
|
|
6
6
|
export function SortableHeader({ label, sortable, sortUrl, sortDirection }) {
|
|
7
7
|
if (!sortable) {
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
SidebarMenu,
|
|
12
12
|
SidebarMenuButton,
|
|
13
13
|
SidebarMenuItem,
|
|
14
|
-
} from "
|
|
14
|
+
} from "terrazzo/ui";
|
|
15
15
|
|
|
16
16
|
export function AppSidebar({
|
|
17
17
|
navigation,
|
|
@@ -27,7 +27,7 @@ export function AppSidebar({
|
|
|
27
27
|
asChild
|
|
28
28
|
className="data-[slot=sidebar-menu-button]:!p-1.5">
|
|
29
29
|
|
|
30
|
-
<a href={navigation[0]?.path ?? "#"} data-sg-visit>
|
|
30
|
+
<a href={navigation[0]?.items?.[0]?.path ?? navigation[0]?.path ?? "#"} data-sg-visit>
|
|
31
31
|
<LayoutDashboardIcon className="h-5 w-5" />
|
|
32
32
|
<span className="text-base font-semibold">{title}</span>
|
|
33
33
|
</a>
|
|
@@ -36,26 +36,27 @@ export function AppSidebar({
|
|
|
36
36
|
</SidebarMenu>
|
|
37
37
|
</SidebarHeader>
|
|
38
38
|
<SidebarContent>
|
|
39
|
-
|
|
40
|
-
<
|
|
41
|
-
|
|
42
|
-
<
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
<
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
39
|
+
{navigation.map((group) =>
|
|
40
|
+
<SidebarGroup key={group.label}>
|
|
41
|
+
<SidebarGroupLabel>{group.label}</SidebarGroupLabel>
|
|
42
|
+
<SidebarGroupContent>
|
|
43
|
+
<SidebarMenu>
|
|
44
|
+
{group.items.map((item) =>
|
|
45
|
+
<SidebarMenuItem key={item.path}>
|
|
46
|
+
<SidebarMenuButton
|
|
47
|
+
asChild
|
|
48
|
+
isActive={item.active}
|
|
49
|
+
tooltip={item.label}>
|
|
50
|
+
<a href={item.path} data-sg-visit>
|
|
51
|
+
<span>{item.label}</span>
|
|
52
|
+
</a>
|
|
53
|
+
</SidebarMenuButton>
|
|
54
|
+
</SidebarMenuItem>
|
|
55
|
+
)}
|
|
56
|
+
</SidebarMenu>
|
|
57
|
+
</SidebarGroupContent>
|
|
58
|
+
</SidebarGroup>
|
|
59
|
+
)}
|
|
59
60
|
</SidebarContent>
|
|
60
61
|
</Sidebar>);
|
|
61
62
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { Separator } from "
|
|
3
|
-
import { SidebarTrigger } from "
|
|
2
|
+
import { Separator } from "terrazzo/ui";
|
|
3
|
+
import { SidebarTrigger } from "terrazzo/ui";
|
|
4
4
|
|
|
5
5
|
export function SiteHeader({ title, actions }) {
|
|
6
6
|
return (
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import React, { useState, useRef, useCallback } from "react";
|
|
2
2
|
import { X, Check } from "lucide-react";
|
|
3
3
|
|
|
4
|
-
import { Label } from "
|
|
5
|
-
import { Badge } from "
|
|
6
|
-
import { Popover, PopoverTrigger, PopoverContent } from "
|
|
4
|
+
import { Label } from "terrazzo/ui";
|
|
5
|
+
import { Badge } from "terrazzo/ui";
|
|
6
|
+
import { Popover, PopoverTrigger, PopoverContent } from "terrazzo/ui";
|
|
7
7
|
import { cn } from "terrazzo";
|
|
8
8
|
|
|
9
9
|
export function FormField({ value, label, input, options, required }) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import React, { useState } from "react";
|
|
1
|
+
import React, { useState, useContext } from "react";
|
|
2
|
+
import { NavigationContext } from "@thoughtbot/superglue";
|
|
2
3
|
|
|
3
4
|
import {
|
|
4
5
|
Table,
|
|
@@ -7,9 +8,9 @@ import {
|
|
|
7
8
|
TableRow,
|
|
8
9
|
TableHead,
|
|
9
10
|
TableCell,
|
|
10
|
-
} from "
|
|
11
|
-
import { Badge } from "
|
|
12
|
-
import { Button } from "
|
|
11
|
+
} from "terrazzo/ui";
|
|
12
|
+
import { Badge } from "terrazzo/ui";
|
|
13
|
+
import { Button } from "terrazzo/ui";
|
|
13
14
|
import { FieldRenderer } from "../FieldRenderer";
|
|
14
15
|
|
|
15
16
|
export function ShowField({ value, itemShowPaths }) {
|
|
@@ -17,6 +18,7 @@ export function ShowField({ value, itemShowPaths }) {
|
|
|
17
18
|
|
|
18
19
|
const { items, headers, total, initialLimit } = value;
|
|
19
20
|
const [expanded, setExpanded] = useState(false);
|
|
21
|
+
const { visit } = useContext(NavigationContext);
|
|
20
22
|
|
|
21
23
|
if (!items || items.length === 0) {
|
|
22
24
|
return <span className="text-muted-foreground">None</span>;
|
|
@@ -26,6 +28,13 @@ export function ShowField({ value, itemShowPaths }) {
|
|
|
26
28
|
const hasMore = initialLimit && initialLimit > 0 && total > initialLimit;
|
|
27
29
|
const visibleItems = expanded || !hasMore ? items : items.slice(0, initialLimit);
|
|
28
30
|
|
|
31
|
+
const handleRowClick = (e, showPath) => {
|
|
32
|
+
if (!showPath) return;
|
|
33
|
+
if (e.target.closest("a, button, form")) return;
|
|
34
|
+
if (window.getSelection().toString()) return;
|
|
35
|
+
visit(showPath, {});
|
|
36
|
+
};
|
|
37
|
+
|
|
29
38
|
// Table mode: collection_attributes specified
|
|
30
39
|
if (headers) {
|
|
31
40
|
return (
|
|
@@ -43,7 +52,10 @@ export function ShowField({ value, itemShowPaths }) {
|
|
|
43
52
|
{visibleItems.map((item) => {
|
|
44
53
|
const showPath = pathFor(item.id);
|
|
45
54
|
return (
|
|
46
|
-
<TableRow
|
|
55
|
+
<TableRow
|
|
56
|
+
key={item.id}
|
|
57
|
+
className={showPath ? "cursor-pointer" : ""}
|
|
58
|
+
onClick={(e) => handleRowClick(e, showPath)}>
|
|
47
59
|
{item.columns.map((col, colIndex) =>
|
|
48
60
|
<TableCell key={col.attribute}>
|
|
49
61
|
{showPath && colIndex === 0 ? (
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React, { useState, useRef, useEffect } from "react";
|
|
2
2
|
|
|
3
|
-
import { Input } from "
|
|
4
|
-
import { Button } from "
|
|
5
|
-
import { Label } from "
|
|
3
|
+
import { Input } from "terrazzo/ui";
|
|
4
|
+
import { Button } from "terrazzo/ui";
|
|
5
|
+
import { Label } from "terrazzo/ui";
|
|
6
6
|
|
|
7
7
|
export function FormField({ value, label, input, options, required }) {
|
|
8
8
|
const initialPairs = () => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
|
|
3
|
-
import { Textarea } from "
|
|
4
|
-
import { Label } from "
|
|
3
|
+
import { Textarea } from "terrazzo/ui";
|
|
4
|
+
import { Label } from "terrazzo/ui";
|
|
5
5
|
|
|
6
6
|
export function FormField({ value, label, input, required }) {
|
|
7
7
|
return (
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
|
|
3
|
-
import { Input } from "
|
|
4
|
-
import { Label } from "
|
|
3
|
+
import { Input } from "terrazzo/ui";
|
|
4
|
+
import { Label } from "terrazzo/ui";
|
|
5
5
|
|
|
6
6
|
export function TextInputFormField({ type = "text", value, label, input, required, defaultValue, ...inputProps }) {
|
|
7
7
|
const resolvedDefault = defaultValue !== undefined ? defaultValue : String(value ?? "");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
|
|
3
|
-
import { Textarea } from "
|
|
4
|
-
import { Label } from "
|
|
3
|
+
import { Textarea } from "terrazzo/ui";
|
|
4
|
+
import { Label } from "terrazzo/ui";
|
|
5
5
|
|
|
6
6
|
export function FormField({ value, label, input, required }) {
|
|
7
7
|
return (
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
|
|
3
|
-
import { FieldRenderer } from "
|
|
4
|
-
import { Button } from "
|
|
3
|
+
import { FieldRenderer } from "terrazzo/fields";
|
|
4
|
+
import { Button } from "terrazzo/ui";
|
|
5
5
|
|
|
6
6
|
export function AdminForm({ form, errors }) {
|
|
7
7
|
const { props: formProps, extras, fieldGroups, fields } = form;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
json.array! Terrazzo::Namespace.new(namespace).resources_with_index_route do |nav_resource|
|
|
2
|
+
json.label nav_resource.resource_name.humanize.pluralize
|
|
3
|
+
json.path url_for(controller: "/#{nav_resource.controller_path}", action: :index, only_path: true)
|
|
4
|
+
json.active nav_resource.controller_path == controller_path
|
|
5
|
+
end
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { useContent } from "@thoughtbot/superglue";
|
|
3
3
|
|
|
4
|
-
import { Layout } from "
|
|
4
|
+
import { Layout } from "terrazzo/components";
|
|
5
5
|
import { AdminForm } from "./_form";
|
|
6
|
-
import { Button } from "
|
|
7
|
-
import { Card, CardContent } from "../components/ui/card";
|
|
6
|
+
import { Button, Card, CardContent } from "terrazzo/ui";
|
|
8
7
|
|
|
9
8
|
export default function AdminEdit() {
|
|
10
9
|
const {
|
|
@@ -1,20 +1,9 @@
|
|
|
1
1
|
import React, { useContext } from "react";
|
|
2
2
|
import { useContent, NavigationContext } from "@thoughtbot/superglue";
|
|
3
3
|
|
|
4
|
-
import { Layout } from "
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { SortableHeader } from "../components/SortableHeader";
|
|
8
|
-
import { FieldRenderer } from "../fields/FieldRenderer";
|
|
9
|
-
import { Button } from "../components/ui/button";
|
|
10
|
-
import {
|
|
11
|
-
Table,
|
|
12
|
-
TableHeader,
|
|
13
|
-
TableBody,
|
|
14
|
-
TableRow,
|
|
15
|
-
TableHead,
|
|
16
|
-
TableCell,
|
|
17
|
-
} from "../components/ui/table";
|
|
4
|
+
import { Layout, SearchBar, Pagination, SortableHeader } from "terrazzo/components";
|
|
5
|
+
import { FieldRenderer } from "terrazzo/fields";
|
|
6
|
+
import { Button, Table, TableHeader, TableBody, TableRow, TableHead, TableCell } from "terrazzo/ui";
|
|
18
7
|
|
|
19
8
|
export default function AdminIndex() {
|
|
20
9
|
const { visit } = useContext(NavigationContext);
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { useContent } from "@thoughtbot/superglue";
|
|
3
3
|
|
|
4
|
-
import { Layout } from "
|
|
4
|
+
import { Layout } from "terrazzo/components";
|
|
5
5
|
import { AdminForm } from "./_form";
|
|
6
|
-
import { Button } from "
|
|
7
|
-
import { Card, CardContent } from "../components/ui/card";
|
|
6
|
+
import { Button, Card, CardContent } from "terrazzo/ui";
|
|
8
7
|
|
|
9
8
|
export default function AdminNew() {
|
|
10
9
|
const {
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { useContent } from "@thoughtbot/superglue";
|
|
3
3
|
|
|
4
|
-
import { Layout } from "
|
|
5
|
-
import { FieldRenderer } from "
|
|
6
|
-
import { Button } from "
|
|
7
|
-
import { Card, CardContent, CardHeader, CardTitle } from "../components/ui/card";
|
|
4
|
+
import { Layout } from "terrazzo/components";
|
|
5
|
+
import { FieldRenderer } from "terrazzo/fields";
|
|
6
|
+
import { Button, Card, CardContent, CardHeader, CardTitle } from "terrazzo/ui";
|
|
8
7
|
|
|
9
8
|
export default function AdminShow() {
|
|
10
9
|
const {
|
|
@@ -8,38 +8,57 @@ module Terrazzo
|
|
|
8
8
|
class_option :namespace, type: :string, default: "admin",
|
|
9
9
|
desc: "Admin namespace"
|
|
10
10
|
|
|
11
|
-
def
|
|
12
|
-
|
|
11
|
+
def create_fields_barrel
|
|
12
|
+
create_file "app/views/#{namespace_name}/fields/index.js", <<~JS
|
|
13
|
+
// Re-export all fields from the terrazzo package.
|
|
14
|
+
// To customize a field, run: rails g terrazzo:eject fields/<field_type>
|
|
15
|
+
export * from "terrazzo/fields";
|
|
16
|
+
JS
|
|
13
17
|
end
|
|
14
18
|
|
|
15
|
-
def
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
SearchBar.jsx
|
|
22
|
-
Pagination.jsx
|
|
23
|
-
SortableHeader.jsx
|
|
24
|
-
].each do |file|
|
|
25
|
-
copy_file "components/#{file}", "app/views/#{namespace_name}/components/#{file}"
|
|
26
|
-
end
|
|
19
|
+
def create_components_barrel
|
|
20
|
+
create_file "app/views/#{namespace_name}/components/index.js", <<~JS
|
|
21
|
+
// Re-export all components from the terrazzo package.
|
|
22
|
+
// To customize, run: rails g terrazzo:eject components/<component_name>
|
|
23
|
+
export * from "terrazzo/components";
|
|
24
|
+
JS
|
|
27
25
|
end
|
|
28
26
|
|
|
29
|
-
def
|
|
30
|
-
{
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
27
|
+
def create_ui_barrel
|
|
28
|
+
create_file "app/views/#{namespace_name}/components/ui/index.js", <<~JS
|
|
29
|
+
// Re-export all UI primitives from the terrazzo package.
|
|
30
|
+
// To customize, run: rails g terrazzo:eject ui/<component_name>
|
|
31
|
+
export * from "terrazzo/ui";
|
|
32
|
+
JS
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def create_navigation_partial
|
|
36
|
+
create_file "app/views/#{namespace_name}/application/_navigation.json.props", <<~RUBY
|
|
37
|
+
resources = Terrazzo::Namespace.new(namespace).resources_with_index_route
|
|
38
|
+
|
|
39
|
+
json.array! [{ label: "Resources", resources: resources }] do |group|
|
|
40
|
+
json.label group[:label]
|
|
41
|
+
json.items do
|
|
42
|
+
json.array! group[:resources] do |r|
|
|
43
|
+
json.label r.resource_name.humanize.pluralize
|
|
44
|
+
json.path url_for(controller: "/\#{r.controller_path}", action: :index, only_path: true)
|
|
45
|
+
json.active r.controller_path == controller_path
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
RUBY
|
|
39
50
|
end
|
|
40
51
|
|
|
41
|
-
def
|
|
42
|
-
|
|
52
|
+
def create_page_stubs
|
|
53
|
+
{
|
|
54
|
+
"index" => "AdminIndex",
|
|
55
|
+
"show" => "AdminShow",
|
|
56
|
+
"new" => "AdminNew",
|
|
57
|
+
"edit" => "AdminEdit",
|
|
58
|
+
}.each do |page, component|
|
|
59
|
+
create_file "app/views/#{namespace_name}/application/#{page}.jsx",
|
|
60
|
+
"export { #{component} as default } from \"terrazzo/pages\";\n"
|
|
61
|
+
end
|
|
43
62
|
end
|
|
44
63
|
|
|
45
64
|
private
|
|
@@ -4,10 +4,10 @@ module Terrazzo
|
|
|
4
4
|
def serialize_value(mode)
|
|
5
5
|
case mode
|
|
6
6
|
when :form
|
|
7
|
-
foreign_key_value
|
|
7
|
+
foreign_key_value&.to_s
|
|
8
8
|
else
|
|
9
9
|
return nil if data.nil?
|
|
10
|
-
{ id: data.id, display: display_name(data) }
|
|
10
|
+
{ id: data.id.to_s, display: display_name(data) }
|
|
11
11
|
end
|
|
12
12
|
end
|
|
13
13
|
|
|
@@ -8,11 +8,11 @@ module Terrazzo
|
|
|
8
8
|
when :index
|
|
9
9
|
data.size
|
|
10
10
|
when :form
|
|
11
|
-
data.map
|
|
11
|
+
data.map { |r| r.id.to_s }
|
|
12
12
|
when :show
|
|
13
13
|
serialize_show_value
|
|
14
14
|
else
|
|
15
|
-
data.map { |r| { id: r.id, display: display_name(r) } }
|
|
15
|
+
data.map { |r| { id: r.id.to_s, display: display_name(r) } }
|
|
16
16
|
end
|
|
17
17
|
end
|
|
18
18
|
|
|
@@ -44,19 +44,26 @@ module Terrazzo
|
|
|
44
44
|
limit = options.fetch(:limit, 5)
|
|
45
45
|
all_records = data.to_a
|
|
46
46
|
total = all_records.size
|
|
47
|
-
col_attrs = options[:collection_attributes]
|
|
47
|
+
col_attrs = options[:collection_attributes] || resolve_default_collection_attributes
|
|
48
48
|
|
|
49
49
|
if col_attrs
|
|
50
50
|
serialize_with_collection_attributes(all_records, col_attrs, total, limit)
|
|
51
51
|
else
|
|
52
52
|
{
|
|
53
|
-
items: all_records.map { |r| { id: r.id, display: display_name(r) } },
|
|
53
|
+
items: all_records.map { |r| { id: r.id.to_s, display: display_name(r) } },
|
|
54
54
|
total: total,
|
|
55
55
|
initialLimit: limit
|
|
56
56
|
}
|
|
57
57
|
end
|
|
58
58
|
end
|
|
59
59
|
|
|
60
|
+
def resolve_default_collection_attributes
|
|
61
|
+
dashboard_class = find_associated_dashboard
|
|
62
|
+
dashboard_class.new.collection_attributes
|
|
63
|
+
rescue NameError
|
|
64
|
+
nil
|
|
65
|
+
end
|
|
66
|
+
|
|
60
67
|
def serialize_with_collection_attributes(records, col_attrs, total, limit)
|
|
61
68
|
dashboard_class = find_associated_dashboard
|
|
62
69
|
|
|
@@ -73,7 +80,7 @@ module Terrazzo
|
|
|
73
80
|
value: field.serialize_value(:index)
|
|
74
81
|
}
|
|
75
82
|
end
|
|
76
|
-
{ id: record.id, columns: columns }
|
|
83
|
+
{ id: record.id.to_s, columns: columns }
|
|
77
84
|
end
|
|
78
85
|
|
|
79
86
|
{
|
|
@@ -5,10 +5,10 @@ module Terrazzo
|
|
|
5
5
|
case mode
|
|
6
6
|
when :form
|
|
7
7
|
return nil if data.nil?
|
|
8
|
-
{ type: data.class.name, id: data.id }
|
|
8
|
+
{ type: data.class.name, id: data.id.to_s }
|
|
9
9
|
else
|
|
10
10
|
return nil if data.nil?
|
|
11
|
-
{ id: data.id, display: display_name(data), type: data.class.name }
|
|
11
|
+
{ id: data.id.to_s, display: display_name(data), type: data.class.name }
|
|
12
12
|
end
|
|
13
13
|
end
|
|
14
14
|
|
|
@@ -17,7 +17,7 @@ module Terrazzo
|
|
|
17
17
|
classes = options[:classes] || []
|
|
18
18
|
opts[:groupedOptions] = classes.each_with_object({}) do |klass, hash|
|
|
19
19
|
klass = klass.constantize if klass.is_a?(::String)
|
|
20
|
-
hash[klass.name] = klass.all.map { |r| [display_name(r), r.id] }
|
|
20
|
+
hash[klass.name] = klass.all.map { |r| [display_name(r), r.id.to_s] }
|
|
21
21
|
end
|
|
22
22
|
opts
|
|
23
23
|
end
|
data/lib/terrazzo/version.rb
CHANGED
data/terrazzo.gemspec
CHANGED
|
@@ -7,7 +7,7 @@ Gem::Specification.new do |spec|
|
|
|
7
7
|
spec.email = []
|
|
8
8
|
spec.summary = "A Rails admin framework powered by Superglue and React"
|
|
9
9
|
spec.description = "Drop-in admin panel for Rails apps using the Administrate dashboard DSL with a React SPA frontend powered by Superglue."
|
|
10
|
-
spec.homepage = "https://github.com/
|
|
10
|
+
spec.homepage = "https://github.com/gohypelab/terrazzo"
|
|
11
11
|
spec.license = "MIT"
|
|
12
12
|
spec.required_ruby_version = ">= 3.1"
|
|
13
13
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: terrazzo
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Terrazzo Contributors
|
|
@@ -117,6 +117,7 @@ files:
|
|
|
117
117
|
- LICENSE
|
|
118
118
|
- Rakefile
|
|
119
119
|
- app/controllers/terrazzo/application_controller.rb
|
|
120
|
+
- app/views/terrazzo/application/_navigation.json.props
|
|
120
121
|
- app/views/terrazzo/application/edit.json.props
|
|
121
122
|
- app/views/terrazzo/application/index.json.props
|
|
122
123
|
- app/views/terrazzo/application/new.json.props
|
|
@@ -126,6 +127,7 @@ files:
|
|
|
126
127
|
- lib/generators/terrazzo/dashboard/dashboard_generator.rb
|
|
127
128
|
- lib/generators/terrazzo/dashboard/templates/controller.rb.erb
|
|
128
129
|
- lib/generators/terrazzo/dashboard/templates/dashboard.rb.erb
|
|
130
|
+
- lib/generators/terrazzo/eject/eject_generator.rb
|
|
129
131
|
- lib/generators/terrazzo/field/field_generator.rb
|
|
130
132
|
- lib/generators/terrazzo/field/templates/FormField.jsx.erb
|
|
131
133
|
- lib/generators/terrazzo/field/templates/IndexField.jsx.erb
|
|
@@ -134,8 +136,8 @@ files:
|
|
|
134
136
|
- lib/generators/terrazzo/install/install_generator.rb
|
|
135
137
|
- lib/generators/terrazzo/install/templates/admin.css
|
|
136
138
|
- lib/generators/terrazzo/install/templates/application.js.erb
|
|
137
|
-
- lib/generators/terrazzo/install/templates/application.json.props
|
|
138
139
|
- lib/generators/terrazzo/install/templates/application.json.props.erb
|
|
140
|
+
- lib/generators/terrazzo/install/templates/application.json.props.tt
|
|
139
141
|
- lib/generators/terrazzo/install/templates/application_controller.rb.erb
|
|
140
142
|
- lib/generators/terrazzo/install/templates/application_visit.js.erb
|
|
141
143
|
- lib/generators/terrazzo/install/templates/flash_slice.js.erb
|
|
@@ -231,6 +233,7 @@ files:
|
|
|
231
233
|
- lib/generators/terrazzo/views/templates/fields/url/IndexField.jsx
|
|
232
234
|
- lib/generators/terrazzo/views/templates/fields/url/ShowField.jsx
|
|
233
235
|
- lib/generators/terrazzo/views/templates/pages/_form.jsx
|
|
236
|
+
- lib/generators/terrazzo/views/templates/pages/_navigation.json.props
|
|
234
237
|
- lib/generators/terrazzo/views/templates/pages/edit.jsx
|
|
235
238
|
- lib/generators/terrazzo/views/templates/pages/index.jsx
|
|
236
239
|
- lib/generators/terrazzo/views/templates/pages/new.jsx
|
|
@@ -275,7 +278,7 @@ files:
|
|
|
275
278
|
- lib/terrazzo/uses_superglue.rb
|
|
276
279
|
- lib/terrazzo/version.rb
|
|
277
280
|
- terrazzo.gemspec
|
|
278
|
-
homepage: https://github.com/
|
|
281
|
+
homepage: https://github.com/gohypelab/terrazzo
|
|
279
282
|
licenses:
|
|
280
283
|
- MIT
|
|
281
284
|
metadata: {}
|