terrazzo 0.1.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 +7 -0
- data/LICENSE +21 -0
- data/Rakefile +11 -0
- data/app/controllers/terrazzo/application_controller.rb +208 -0
- data/app/views/terrazzo/application/edit.json.props +70 -0
- data/app/views/terrazzo/application/index.json.props +97 -0
- data/app/views/terrazzo/application/new.json.props +65 -0
- data/app/views/terrazzo/application/show.json.props +44 -0
- data/app/views/terrazzo/application/superglue.html.erb +5 -0
- data/config/locales/en.yml +28 -0
- data/lib/generators/terrazzo/dashboard/dashboard_generator.rb +118 -0
- data/lib/generators/terrazzo/dashboard/templates/controller.rb.erb +4 -0
- data/lib/generators/terrazzo/dashboard/templates/dashboard.rb.erb +36 -0
- data/lib/generators/terrazzo/field/field_generator.rb +42 -0
- data/lib/generators/terrazzo/field/templates/FormField.jsx.erb +19 -0
- data/lib/generators/terrazzo/field/templates/IndexField.jsx.erb +5 -0
- data/lib/generators/terrazzo/field/templates/ShowField.jsx.erb +5 -0
- data/lib/generators/terrazzo/field/templates/field.rb.erb +23 -0
- data/lib/generators/terrazzo/install/install_generator.rb +85 -0
- data/lib/generators/terrazzo/install/templates/application.js.erb +27 -0
- data/lib/generators/terrazzo/install/templates/application.json.props +27 -0
- data/lib/generators/terrazzo/install/templates/application.json.props.erb +17 -0
- data/lib/generators/terrazzo/install/templates/application_controller.rb.erb +24 -0
- data/lib/generators/terrazzo/install/templates/application_visit.js.erb +8 -0
- data/lib/generators/terrazzo/install/templates/flash_slice.js.erb +42 -0
- data/lib/generators/terrazzo/install/templates/page_to_page_mapping.js.erb +22 -0
- data/lib/generators/terrazzo/install/templates/store.js.erb +24 -0
- data/lib/generators/terrazzo/install/templates/superglue.html.erb.erb +20 -0
- data/lib/generators/terrazzo/routes/routes_generator.rb +38 -0
- data/lib/generators/terrazzo/views/edit_generator.rb +28 -0
- data/lib/generators/terrazzo/views/field_generator.rb +32 -0
- data/lib/generators/terrazzo/views/index_generator.rb +24 -0
- data/lib/generators/terrazzo/views/layout_generator.rb +26 -0
- data/lib/generators/terrazzo/views/navigation_generator.rb +24 -0
- data/lib/generators/terrazzo/views/new_generator.rb +28 -0
- data/lib/generators/terrazzo/views/show_generator.rb +24 -0
- data/lib/generators/terrazzo/views/templates/components/FlashMessages.jsx +26 -0
- data/lib/generators/terrazzo/views/templates/components/Layout.jsx +23 -0
- data/lib/generators/terrazzo/views/templates/components/Pagination.jsx +69 -0
- data/lib/generators/terrazzo/views/templates/components/SearchBar.jsx +35 -0
- data/lib/generators/terrazzo/views/templates/components/SortableHeader.jsx +29 -0
- data/lib/generators/terrazzo/views/templates/components/app-sidebar.jsx +62 -0
- data/lib/generators/terrazzo/views/templates/components/site-header.jsx +19 -0
- data/lib/generators/terrazzo/views/templates/components/ui/avatar.jsx +35 -0
- data/lib/generators/terrazzo/views/templates/components/ui/badge.jsx +34 -0
- data/lib/generators/terrazzo/views/templates/components/ui/button.jsx +47 -0
- data/lib/generators/terrazzo/views/templates/components/ui/card.jsx +50 -0
- data/lib/generators/terrazzo/views/templates/components/ui/dropdown-menu.jsx +155 -0
- data/lib/generators/terrazzo/views/templates/components/ui/field.jsx +28 -0
- data/lib/generators/terrazzo/views/templates/components/ui/index.js +106 -0
- data/lib/generators/terrazzo/views/templates/components/ui/input.jsx +19 -0
- data/lib/generators/terrazzo/views/templates/components/ui/label.jsx +16 -0
- data/lib/generators/terrazzo/views/templates/components/ui/pagination.jsx +85 -0
- data/lib/generators/terrazzo/views/templates/components/ui/popover.jsx +27 -0
- data/lib/generators/terrazzo/views/templates/components/ui/select.jsx +127 -0
- data/lib/generators/terrazzo/views/templates/components/ui/separator.jsx +23 -0
- data/lib/generators/terrazzo/views/templates/components/ui/sheet.jsx +109 -0
- data/lib/generators/terrazzo/views/templates/components/ui/sidebar.jsx +629 -0
- data/lib/generators/terrazzo/views/templates/components/ui/skeleton.jsx +10 -0
- data/lib/generators/terrazzo/views/templates/components/ui/table.jsx +83 -0
- data/lib/generators/terrazzo/views/templates/components/ui/textarea.jsx +18 -0
- data/lib/generators/terrazzo/views/templates/components/ui/tooltip.jsx +24 -0
- data/lib/generators/terrazzo/views/templates/fields/FieldRenderer.jsx +103 -0
- data/lib/generators/terrazzo/views/templates/fields/belongs_to/FormField.jsx +29 -0
- data/lib/generators/terrazzo/views/templates/fields/belongs_to/IndexField.jsx +7 -0
- data/lib/generators/terrazzo/views/templates/fields/belongs_to/ShowField.jsx +7 -0
- data/lib/generators/terrazzo/views/templates/fields/boolean/FormField.jsx +21 -0
- data/lib/generators/terrazzo/views/templates/fields/boolean/IndexField.jsx +12 -0
- data/lib/generators/terrazzo/views/templates/fields/boolean/ShowField.jsx +6 -0
- data/lib/generators/terrazzo/views/templates/fields/date/FormField.jsx +8 -0
- data/lib/generators/terrazzo/views/templates/fields/date/IndexField.jsx +7 -0
- data/lib/generators/terrazzo/views/templates/fields/date/ShowField.jsx +7 -0
- data/lib/generators/terrazzo/views/templates/fields/date_time/FormField.jsx +8 -0
- data/lib/generators/terrazzo/views/templates/fields/date_time/IndexField.jsx +7 -0
- data/lib/generators/terrazzo/views/templates/fields/date_time/ShowField.jsx +7 -0
- data/lib/generators/terrazzo/views/templates/fields/email/FormField.jsx +7 -0
- data/lib/generators/terrazzo/views/templates/fields/email/IndexField.jsx +10 -0
- data/lib/generators/terrazzo/views/templates/fields/email/ShowField.jsx +10 -0
- data/lib/generators/terrazzo/views/templates/fields/has_many/FormField.jsx +151 -0
- data/lib/generators/terrazzo/views/templates/fields/has_many/IndexField.jsx +8 -0
- data/lib/generators/terrazzo/views/templates/fields/has_many/ShowField.jsx +72 -0
- data/lib/generators/terrazzo/views/templates/fields/has_one/FormField.jsx +28 -0
- data/lib/generators/terrazzo/views/templates/fields/has_one/IndexField.jsx +7 -0
- data/lib/generators/terrazzo/views/templates/fields/has_one/ShowField.jsx +7 -0
- data/lib/generators/terrazzo/views/templates/fields/hstore/FormField.jsx +120 -0
- data/lib/generators/terrazzo/views/templates/fields/hstore/IndexField.jsx +15 -0
- data/lib/generators/terrazzo/views/templates/fields/hstore/ShowField.jsx +24 -0
- data/lib/generators/terrazzo/views/templates/fields/index.js +81 -0
- data/lib/generators/terrazzo/views/templates/fields/number/FormField.jsx +9 -0
- data/lib/generators/terrazzo/views/templates/fields/number/IndexField.jsx +9 -0
- data/lib/generators/terrazzo/views/templates/fields/number/ShowField.jsx +9 -0
- data/lib/generators/terrazzo/views/templates/fields/password/FormField.jsx +7 -0
- data/lib/generators/terrazzo/views/templates/fields/password/IndexField.jsx +6 -0
- data/lib/generators/terrazzo/views/templates/fields/password/ShowField.jsx +6 -0
- data/lib/generators/terrazzo/views/templates/fields/polymorphic/FormField.jsx +58 -0
- data/lib/generators/terrazzo/views/templates/fields/polymorphic/IndexField.jsx +7 -0
- data/lib/generators/terrazzo/views/templates/fields/polymorphic/ShowField.jsx +7 -0
- data/lib/generators/terrazzo/views/templates/fields/rich_text/FormField.jsx +21 -0
- data/lib/generators/terrazzo/views/templates/fields/rich_text/IndexField.jsx +8 -0
- data/lib/generators/terrazzo/views/templates/fields/rich_text/ShowField.jsx +6 -0
- data/lib/generators/terrazzo/views/templates/fields/select/FormField.jsx +29 -0
- data/lib/generators/terrazzo/views/templates/fields/select/IndexField.jsx +8 -0
- data/lib/generators/terrazzo/views/templates/fields/select/ShowField.jsx +5 -0
- data/lib/generators/terrazzo/views/templates/fields/shared/TextInputFormField.jsx +24 -0
- data/lib/generators/terrazzo/views/templates/fields/string/FormField.jsx +7 -0
- data/lib/generators/terrazzo/views/templates/fields/string/IndexField.jsx +5 -0
- data/lib/generators/terrazzo/views/templates/fields/string/ShowField.jsx +5 -0
- data/lib/generators/terrazzo/views/templates/fields/text/FormField.jsx +20 -0
- data/lib/generators/terrazzo/views/templates/fields/text/IndexField.jsx +5 -0
- data/lib/generators/terrazzo/views/templates/fields/text/ShowField.jsx +5 -0
- data/lib/generators/terrazzo/views/templates/fields/time/FormField.jsx +8 -0
- data/lib/generators/terrazzo/views/templates/fields/time/IndexField.jsx +7 -0
- data/lib/generators/terrazzo/views/templates/fields/time/ShowField.jsx +7 -0
- data/lib/generators/terrazzo/views/templates/fields/url/FormField.jsx +7 -0
- data/lib/generators/terrazzo/views/templates/fields/url/IndexField.jsx +10 -0
- data/lib/generators/terrazzo/views/templates/fields/url/ShowField.jsx +10 -0
- data/lib/generators/terrazzo/views/templates/pages/_form.jsx +76 -0
- data/lib/generators/terrazzo/views/templates/pages/edit.jsx +44 -0
- data/lib/generators/terrazzo/views/templates/pages/index.jsx +106 -0
- data/lib/generators/terrazzo/views/templates/pages/new.jsx +36 -0
- data/lib/generators/terrazzo/views/templates/pages/show.jsx +82 -0
- data/lib/generators/terrazzo/views/views_generator.rb +52 -0
- data/lib/terrazzo/base_dashboard.rb +88 -0
- data/lib/terrazzo/engine.rb +21 -0
- data/lib/terrazzo/field/associative.rb +56 -0
- data/lib/terrazzo/field/base.rb +114 -0
- data/lib/terrazzo/field/belongs_to.rb +53 -0
- data/lib/terrazzo/field/boolean.rb +9 -0
- data/lib/terrazzo/field/date.rb +10 -0
- data/lib/terrazzo/field/date_time.rb +10 -0
- data/lib/terrazzo/field/deferred.rb +50 -0
- data/lib/terrazzo/field/email.rb +15 -0
- data/lib/terrazzo/field/has_many.rb +98 -0
- data/lib/terrazzo/field/has_one.rb +33 -0
- data/lib/terrazzo/field/hstore.rb +37 -0
- data/lib/terrazzo/field/money.rb +33 -0
- data/lib/terrazzo/field/number.rb +17 -0
- data/lib/terrazzo/field/password.rb +16 -0
- data/lib/terrazzo/field/polymorphic.rb +36 -0
- data/lib/terrazzo/field/rich_text.rb +30 -0
- data/lib/terrazzo/field/select.rb +33 -0
- data/lib/terrazzo/field/string.rb +27 -0
- data/lib/terrazzo/field/text.rb +27 -0
- data/lib/terrazzo/field/time.rb +10 -0
- data/lib/terrazzo/field/url.rb +9 -0
- data/lib/terrazzo/filter.rb +26 -0
- data/lib/terrazzo/generator_helpers.rb +36 -0
- data/lib/terrazzo/namespace/resource.rb +39 -0
- data/lib/terrazzo/namespace.rb +34 -0
- data/lib/terrazzo/not_authorized_error.rb +4 -0
- data/lib/terrazzo/order.rb +71 -0
- data/lib/terrazzo/page/base.rb +12 -0
- data/lib/terrazzo/page/collection.rb +28 -0
- data/lib/terrazzo/page/form.rb +43 -0
- data/lib/terrazzo/page/show.rb +46 -0
- data/lib/terrazzo/resource_resolver.rb +40 -0
- data/lib/terrazzo/search.rb +56 -0
- data/lib/terrazzo/version.rb +3 -0
- data/lib/terrazzo.rb +47 -0
- data/terrazzo.gemspec +32 -0
- metadata +297 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
require "terrazzo/base_dashboard"
|
|
2
|
+
|
|
3
|
+
class <%= class_name %>Dashboard < Terrazzo::BaseDashboard
|
|
4
|
+
ATTRIBUTE_TYPES = {
|
|
5
|
+
<% attribute_types.each do |attr, type| -%>
|
|
6
|
+
<% if has_enum?(attr.to_s) -%>
|
|
7
|
+
<%= attr %>: Terrazzo::<%= type %>.with_options(collection: <%= enum_collection(attr.to_s).inspect %>),
|
|
8
|
+
<% else -%>
|
|
9
|
+
<%= attr %>: Terrazzo::<%= type %>,
|
|
10
|
+
<% end -%>
|
|
11
|
+
<% end -%>
|
|
12
|
+
}.freeze
|
|
13
|
+
|
|
14
|
+
COLLECTION_ATTRIBUTES = %i[
|
|
15
|
+
<% collection_attributes.each do |attr| -%>
|
|
16
|
+
<%= attr %>
|
|
17
|
+
<% end -%>
|
|
18
|
+
].freeze
|
|
19
|
+
|
|
20
|
+
SHOW_PAGE_ATTRIBUTES = %i[
|
|
21
|
+
<% show_page_attributes.each do |attr| -%>
|
|
22
|
+
<%= attr %>
|
|
23
|
+
<% end -%>
|
|
24
|
+
].freeze
|
|
25
|
+
|
|
26
|
+
FORM_ATTRIBUTES = %i[
|
|
27
|
+
<% form_attributes.each do |attr| -%>
|
|
28
|
+
<%= attr %>
|
|
29
|
+
<% end -%>
|
|
30
|
+
].freeze
|
|
31
|
+
|
|
32
|
+
# COLLECTION_FILTERS = {
|
|
33
|
+
# active: ->(resources) { resources.where(active: true) },
|
|
34
|
+
# recent: ->(resources) { resources.where("created_at > ?", 30.days.ago) },
|
|
35
|
+
# }.freeze
|
|
36
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
require "rails/generators"
|
|
2
|
+
|
|
3
|
+
module Terrazzo
|
|
4
|
+
module Generators
|
|
5
|
+
class FieldGenerator < Rails::Generators::NamedBase
|
|
6
|
+
source_root File.expand_path("templates", __dir__)
|
|
7
|
+
|
|
8
|
+
class_option :namespace, type: :string, default: "admin",
|
|
9
|
+
desc: "Admin namespace"
|
|
10
|
+
|
|
11
|
+
def create_field_class
|
|
12
|
+
template "field.rb.erb",
|
|
13
|
+
"app/fields/#{file_name}_field.rb"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def create_jsx_components
|
|
17
|
+
%w[IndexField ShowField FormField].each do |component|
|
|
18
|
+
template "#{component}.jsx.erb",
|
|
19
|
+
"app/views/#{namespace_name}/fields/#{file_name}/#{component}.jsx"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def show_registration_instructions
|
|
24
|
+
say "\nTo use your custom field, register it in your FieldRenderer.jsx:", :green
|
|
25
|
+
say " import { IndexField as #{class_name}Index } from \"./#{file_name}/IndexField\";"
|
|
26
|
+
say " import { ShowField as #{class_name}Show } from \"./#{file_name}/ShowField\";"
|
|
27
|
+
say " import { FormField as #{class_name}Form } from \"./#{file_name}/FormField\";"
|
|
28
|
+
say ""
|
|
29
|
+
say " registerFieldType(\"#{file_name}\", { index: #{class_name}Index, show: #{class_name}Show, form: #{class_name}Form });"
|
|
30
|
+
say ""
|
|
31
|
+
say "Then use it in your dashboard:"
|
|
32
|
+
say " #{file_name}: Terrazzo::Field::#{class_name},"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def namespace_name
|
|
38
|
+
options[:namespace]
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Input } from "../../components/ui/input";
|
|
3
|
+
import { Label } from "../../components/ui/label";
|
|
4
|
+
|
|
5
|
+
export function FormField({ attribute, label, value, input, required }) {
|
|
6
|
+
return (
|
|
7
|
+
<div className="space-y-2">
|
|
8
|
+
<Label htmlFor={input.id}>
|
|
9
|
+
{label}{required && <span className="text-red-500 ml-1">*</span>}
|
|
10
|
+
</Label>
|
|
11
|
+
<Input
|
|
12
|
+
type="text"
|
|
13
|
+
id={input.id}
|
|
14
|
+
name={input.name}
|
|
15
|
+
defaultValue={value ?? ""}
|
|
16
|
+
/>
|
|
17
|
+
</div>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Terrazzo
|
|
2
|
+
module Field
|
|
3
|
+
class <%= class_name %> < Base
|
|
4
|
+
def serialize_value(mode)
|
|
5
|
+
data
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def serializable_options
|
|
9
|
+
{}
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class << self
|
|
13
|
+
def searchable?
|
|
14
|
+
false
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def sortable?
|
|
18
|
+
true
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
require "rails/generators"
|
|
2
|
+
|
|
3
|
+
module Terrazzo
|
|
4
|
+
module Generators
|
|
5
|
+
class InstallGenerator < Rails::Generators::Base
|
|
6
|
+
source_root File.expand_path("templates", __dir__)
|
|
7
|
+
|
|
8
|
+
class_option :namespace, type: :string, default: "admin",
|
|
9
|
+
desc: "Admin namespace"
|
|
10
|
+
class_option :bundler, type: :string, default: "vite",
|
|
11
|
+
desc: "JavaScript bundler (vite or sprockets)"
|
|
12
|
+
|
|
13
|
+
def create_application_controller
|
|
14
|
+
template "application_controller.rb.erb",
|
|
15
|
+
"app/controllers/#{namespace_name}/application_controller.rb"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def create_layout
|
|
19
|
+
template "superglue.html.erb.erb",
|
|
20
|
+
"app/views/layouts/#{namespace_name}/application.html.erb"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def create_json_props_layout
|
|
24
|
+
copy_file "application.json.props",
|
|
25
|
+
"app/views/layouts/#{namespace_name}/application.json.props"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def create_js_entry_point
|
|
29
|
+
template "application.js.erb",
|
|
30
|
+
"app/javascript/#{namespace_name}/application.jsx"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def create_store
|
|
34
|
+
template "store.js.erb",
|
|
35
|
+
"app/javascript/#{namespace_name}/store.js"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def create_page_to_page_mapping
|
|
39
|
+
template "page_to_page_mapping.js.erb",
|
|
40
|
+
"app/javascript/#{namespace_name}/page_to_page_mapping.js"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def create_application_visit
|
|
44
|
+
template "application_visit.js.erb",
|
|
45
|
+
"app/javascript/#{namespace_name}/application_visit.js"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def create_flash_slice
|
|
49
|
+
template "flash_slice.js.erb",
|
|
50
|
+
"app/javascript/#{namespace_name}/slices/flash.js"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def run_views_generator
|
|
54
|
+
generate "terrazzo:views", "--namespace=#{namespace_name}"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def run_routes_generator
|
|
58
|
+
generate "terrazzo:routes", "--namespace=#{namespace_name}"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def run_dashboard_generators
|
|
62
|
+
application_models.each do |model|
|
|
63
|
+
generate "terrazzo:dashboard", model.name, "--namespace=#{namespace_name}", "--bundler=#{options[:bundler]}"
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
def namespace_name
|
|
70
|
+
options[:namespace]
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def vite?
|
|
74
|
+
options[:bundler] == "vite"
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def application_models
|
|
78
|
+
Rails.application.eager_load! if defined?(Rails)
|
|
79
|
+
ApplicationRecord.descendants.reject(&:abstract_class?)
|
|
80
|
+
rescue
|
|
81
|
+
[]
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import { createRoot } from "react-dom/client"
|
|
3
|
+
import { Application } from "@thoughtbot/superglue"
|
|
4
|
+
import { buildVisitAndRemote } from "./application_visit"
|
|
5
|
+
import { pageToPageMapping } from "./page_to_page_mapping"
|
|
6
|
+
import { store } from "./store"
|
|
7
|
+
|
|
8
|
+
if (typeof window !== "undefined") {
|
|
9
|
+
document.addEventListener("DOMContentLoaded", function () {
|
|
10
|
+
const appEl = document.getElementById("superglue-app")
|
|
11
|
+
const location = window.location
|
|
12
|
+
|
|
13
|
+
if (appEl) {
|
|
14
|
+
const root = createRoot(appEl)
|
|
15
|
+
root.render(
|
|
16
|
+
<Application
|
|
17
|
+
initialPage={window.SUPERGLUE_INITIAL_PAGE_STATE}
|
|
18
|
+
baseUrl={location.origin}
|
|
19
|
+
path={location.pathname + location.search + location.hash}
|
|
20
|
+
store={store}
|
|
21
|
+
mapping={pageToPageMapping}
|
|
22
|
+
buildVisitAndRemote={buildVisitAndRemote}
|
|
23
|
+
/>
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
path = request.format.json? ? param_to_dig_path(params[:props_at]) : nil
|
|
2
|
+
|
|
3
|
+
json.data(dig: path) do
|
|
4
|
+
yield
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
json.componentIdentifier terrazzo_page_identifier
|
|
8
|
+
json.defers json.deferred!
|
|
9
|
+
json.fragments json.fragments!
|
|
10
|
+
json.assets []
|
|
11
|
+
|
|
12
|
+
if protect_against_forgery?
|
|
13
|
+
json.csrfToken form_authenticity_token
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
if path
|
|
17
|
+
json.action "graft"
|
|
18
|
+
json.path params[:props_at]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
json.restoreStrategy "fromCacheAndRevisitInBackground"
|
|
22
|
+
|
|
23
|
+
json.renderedAt Time.now.to_i
|
|
24
|
+
|
|
25
|
+
json.slices do
|
|
26
|
+
json.flash flash.to_h
|
|
27
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
json.data(search: request.path) do
|
|
2
|
+
yield
|
|
3
|
+
end
|
|
4
|
+
|
|
5
|
+
json.componentIdentifier local_assigns[:virtual_path_of_template]
|
|
6
|
+
json.assets do
|
|
7
|
+
json.array! []
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
json.csrfToken form_authenticity_token
|
|
11
|
+
json.restoreStrategy "fromCacheAndRevisitInBackground"
|
|
12
|
+
|
|
13
|
+
json.flash do
|
|
14
|
+
flash.each do |type, message|
|
|
15
|
+
json.set!(type, message)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module <%= namespace_name.camelize %>
|
|
2
|
+
class ApplicationController < Terrazzo::ApplicationController
|
|
3
|
+
layout "<%= namespace_name %>/application"
|
|
4
|
+
superglue_template "terrazzo/application/superglue"
|
|
5
|
+
|
|
6
|
+
# before_action :authenticate_admin
|
|
7
|
+
|
|
8
|
+
# Override to limit which records are visible
|
|
9
|
+
# def scoped_resource
|
|
10
|
+
# resource_class.all
|
|
11
|
+
# end
|
|
12
|
+
|
|
13
|
+
# Override to control access to actions
|
|
14
|
+
# def authorized_action?(resource, action)
|
|
15
|
+
# true
|
|
16
|
+
# end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
# def authenticate_admin
|
|
21
|
+
# redirect_to root_path unless current_user&.admin?
|
|
22
|
+
# end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { visit, remote } from "@thoughtbot/superglue/action_creators"
|
|
2
|
+
|
|
3
|
+
export function buildVisitAndRemote(ref, store, pageToPageMapping) {
|
|
4
|
+
const appVisit = visit(ref, store, pageToPageMapping)
|
|
5
|
+
const appRemote = remote(ref, store, pageToPageMapping)
|
|
6
|
+
|
|
7
|
+
return { visit: appVisit, remote: appRemote }
|
|
8
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { createSlice } from "@reduxjs/toolkit"
|
|
2
|
+
import { saveResponse, beforeVisit } from "@thoughtbot/superglue"
|
|
3
|
+
|
|
4
|
+
export const flashSlice = createSlice({
|
|
5
|
+
name: "flash",
|
|
6
|
+
initialState: {},
|
|
7
|
+
reducers: {
|
|
8
|
+
clearFlash(state, { payload }) {
|
|
9
|
+
const key = payload
|
|
10
|
+
if (!key) {
|
|
11
|
+
return {}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
delete state[key]
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
...state
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
flash(state, { payload }) {
|
|
21
|
+
return {
|
|
22
|
+
...state,
|
|
23
|
+
...payload
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
extraReducers: builder => {
|
|
28
|
+
builder.addCase(beforeVisit, (_state, _action) => {
|
|
29
|
+
return {}
|
|
30
|
+
})
|
|
31
|
+
builder.addCase(saveResponse, (state, action) => {
|
|
32
|
+
const { page } = action.payload
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
...state,
|
|
36
|
+
...page.slices.flash
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
export const { clearFlash, flash } = flashSlice.actions
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<% if vite? -%>
|
|
2
|
+
const pages = import.meta.glob("../../views/<%= namespace_name %>/**/*.jsx", { eager: true })
|
|
3
|
+
|
|
4
|
+
export const pageToPageMapping = {}
|
|
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"
|
|
15
|
+
|
|
16
|
+
export const pageToPageMapping = {
|
|
17
|
+
'<%= namespace_name %>/application/index': AdminIndex,
|
|
18
|
+
'<%= namespace_name %>/application/show': AdminShow,
|
|
19
|
+
'<%= namespace_name %>/application/new': AdminNew,
|
|
20
|
+
'<%= namespace_name %>/application/edit': AdminEdit,
|
|
21
|
+
}
|
|
22
|
+
<% end -%>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { configureStore } from "@reduxjs/toolkit"
|
|
2
|
+
import { flashSlice } from "./slices/flash"
|
|
3
|
+
import {
|
|
4
|
+
beforeVisit,
|
|
5
|
+
beforeFetch,
|
|
6
|
+
beforeRemote,
|
|
7
|
+
rootReducer,
|
|
8
|
+
} from "@thoughtbot/superglue"
|
|
9
|
+
|
|
10
|
+
const { pages, superglue } = rootReducer
|
|
11
|
+
|
|
12
|
+
export const store = configureStore({
|
|
13
|
+
middleware: (getDefaultMiddleware) =>
|
|
14
|
+
getDefaultMiddleware({
|
|
15
|
+
serializableCheck: {
|
|
16
|
+
ignoredActions: [beforeFetch.type, beforeVisit.type, beforeRemote.type],
|
|
17
|
+
},
|
|
18
|
+
}),
|
|
19
|
+
reducer: {
|
|
20
|
+
superglue,
|
|
21
|
+
pages,
|
|
22
|
+
flash: flashSlice.reducer,
|
|
23
|
+
},
|
|
24
|
+
})
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<%%= csrf_meta_tags %>
|
|
7
|
+
<%%= csp_meta_tag %>
|
|
8
|
+
<%%= yield :head %>
|
|
9
|
+
<% if vite? -%>
|
|
10
|
+
<%%= vite_react_refresh_tag %>
|
|
11
|
+
<%%= vite_javascript_tag "<%= namespace_name %>/application" %>
|
|
12
|
+
<% else -%>
|
|
13
|
+
<%%= javascript_include_tag "<%= namespace_name %>", type: "module" %>
|
|
14
|
+
<% end -%>
|
|
15
|
+
<%%= stylesheet_link_tag "<%= namespace_name %>", "data-turbo-track": "reload" %>
|
|
16
|
+
</head>
|
|
17
|
+
<body>
|
|
18
|
+
<%%= yield %>
|
|
19
|
+
</body>
|
|
20
|
+
</html>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require "rails/generators"
|
|
2
|
+
|
|
3
|
+
module Terrazzo
|
|
4
|
+
module Generators
|
|
5
|
+
class RoutesGenerator < Rails::Generators::Base
|
|
6
|
+
source_root File.expand_path("templates", __dir__)
|
|
7
|
+
|
|
8
|
+
class_option :namespace, type: :string, default: "admin",
|
|
9
|
+
desc: "Admin namespace"
|
|
10
|
+
|
|
11
|
+
def insert_routes
|
|
12
|
+
namespace_name = options[:namespace]
|
|
13
|
+
route_content = <<~RUBY.indent(2)
|
|
14
|
+
namespace :#{namespace_name} do
|
|
15
|
+
root to: "#{first_resource}#index"
|
|
16
|
+
end
|
|
17
|
+
RUBY
|
|
18
|
+
|
|
19
|
+
route route_content.strip
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def first_resource
|
|
25
|
+
# Default to "customers" if no models found
|
|
26
|
+
models = application_models
|
|
27
|
+
models.any? ? models.first.model_name.plural : "dashboard"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def application_models
|
|
31
|
+
Rails.application.eager_load! if defined?(Rails)
|
|
32
|
+
ApplicationRecord.descendants.reject(&:abstract_class?)
|
|
33
|
+
rescue
|
|
34
|
+
[]
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require "rails/generators"
|
|
2
|
+
|
|
3
|
+
module Terrazzo
|
|
4
|
+
module Generators
|
|
5
|
+
module Views
|
|
6
|
+
class EditGenerator < Rails::Generators::Base
|
|
7
|
+
source_root File.expand_path("templates", __dir__)
|
|
8
|
+
|
|
9
|
+
class_option :namespace, type: :string, default: "admin",
|
|
10
|
+
desc: "Admin namespace"
|
|
11
|
+
|
|
12
|
+
def copy_edit_template
|
|
13
|
+
copy_file "pages/edit.jsx", "app/views/#{namespace_name}/application/edit.jsx"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def copy_form_partial
|
|
17
|
+
copy_file "pages/_form.jsx", "app/views/#{namespace_name}/application/_form.jsx"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def namespace_name
|
|
23
|
+
options[:namespace]
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require "rails/generators"
|
|
2
|
+
|
|
3
|
+
module Terrazzo
|
|
4
|
+
module Generators
|
|
5
|
+
module Views
|
|
6
|
+
class FieldGenerator < Rails::Generators::Base
|
|
7
|
+
source_root File.expand_path("templates", __dir__)
|
|
8
|
+
|
|
9
|
+
class_option :namespace, type: :string, default: "admin",
|
|
10
|
+
desc: "Admin namespace"
|
|
11
|
+
|
|
12
|
+
argument :field_type, type: :string, required: false, default: "all",
|
|
13
|
+
desc: "Specific field type to copy (e.g., string, number) or 'all'"
|
|
14
|
+
|
|
15
|
+
def copy_field_templates
|
|
16
|
+
if field_type == "all"
|
|
17
|
+
directory "fields", "app/views/#{namespace_name}/fields"
|
|
18
|
+
else
|
|
19
|
+
directory "fields/shared", "app/views/#{namespace_name}/fields/shared"
|
|
20
|
+
directory "fields/#{field_type}", "app/views/#{namespace_name}/fields/#{field_type}"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def namespace_name
|
|
27
|
+
options[:namespace]
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require "rails/generators"
|
|
2
|
+
|
|
3
|
+
module Terrazzo
|
|
4
|
+
module Generators
|
|
5
|
+
module Views
|
|
6
|
+
class IndexGenerator < Rails::Generators::Base
|
|
7
|
+
source_root File.expand_path("templates", __dir__)
|
|
8
|
+
|
|
9
|
+
class_option :namespace, type: :string, default: "admin",
|
|
10
|
+
desc: "Admin namespace"
|
|
11
|
+
|
|
12
|
+
def copy_index_template
|
|
13
|
+
copy_file "pages/index.jsx", "app/views/#{namespace_name}/application/index.jsx"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def namespace_name
|
|
19
|
+
options[:namespace]
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require "rails/generators"
|
|
2
|
+
|
|
3
|
+
module Terrazzo
|
|
4
|
+
module Generators
|
|
5
|
+
module Views
|
|
6
|
+
class LayoutGenerator < Rails::Generators::Base
|
|
7
|
+
source_root File.expand_path("templates", __dir__)
|
|
8
|
+
|
|
9
|
+
class_option :namespace, type: :string, default: "admin",
|
|
10
|
+
desc: "Admin namespace"
|
|
11
|
+
|
|
12
|
+
def copy_layout_components
|
|
13
|
+
%w[Layout.jsx app-sidebar.jsx site-header.jsx FlashMessages.jsx].each do |file|
|
|
14
|
+
copy_file "components/#{file}", "app/views/#{namespace_name}/components/#{file}"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def namespace_name
|
|
21
|
+
options[:namespace]
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require "rails/generators"
|
|
2
|
+
|
|
3
|
+
module Terrazzo
|
|
4
|
+
module Generators
|
|
5
|
+
module Views
|
|
6
|
+
class NavigationGenerator < Rails::Generators::Base
|
|
7
|
+
source_root File.expand_path("templates", __dir__)
|
|
8
|
+
|
|
9
|
+
class_option :namespace, type: :string, default: "admin",
|
|
10
|
+
desc: "Admin namespace"
|
|
11
|
+
|
|
12
|
+
def copy_navigation
|
|
13
|
+
copy_file "components/app-sidebar.jsx", "app/views/#{namespace_name}/components/app-sidebar.jsx"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def namespace_name
|
|
19
|
+
options[:namespace]
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require "rails/generators"
|
|
2
|
+
|
|
3
|
+
module Terrazzo
|
|
4
|
+
module Generators
|
|
5
|
+
module Views
|
|
6
|
+
class NewGenerator < Rails::Generators::Base
|
|
7
|
+
source_root File.expand_path("templates", __dir__)
|
|
8
|
+
|
|
9
|
+
class_option :namespace, type: :string, default: "admin",
|
|
10
|
+
desc: "Admin namespace"
|
|
11
|
+
|
|
12
|
+
def copy_new_template
|
|
13
|
+
copy_file "pages/new.jsx", "app/views/#{namespace_name}/application/new.jsx"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def copy_form_partial
|
|
17
|
+
copy_file "pages/_form.jsx", "app/views/#{namespace_name}/application/_form.jsx"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def namespace_name
|
|
23
|
+
options[:namespace]
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require "rails/generators"
|
|
2
|
+
|
|
3
|
+
module Terrazzo
|
|
4
|
+
module Generators
|
|
5
|
+
module Views
|
|
6
|
+
class ShowGenerator < Rails::Generators::Base
|
|
7
|
+
source_root File.expand_path("templates", __dir__)
|
|
8
|
+
|
|
9
|
+
class_option :namespace, type: :string, default: "admin",
|
|
10
|
+
desc: "Admin namespace"
|
|
11
|
+
|
|
12
|
+
def copy_show_template
|
|
13
|
+
copy_file "pages/show.jsx", "app/views/#{namespace_name}/application/show.jsx"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def namespace_name
|
|
19
|
+
options[:namespace]
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|