terrazzo 0.3.2 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7c7c9b21f28f73f5cf332ac0d4b21d7eb32ae2461bfb2f52c5652eecd7642a8b
4
- data.tar.gz: 7185c42a9d472e4d84ff7047a36a5468f761f272b9af5e0d9cb86930b130857f
3
+ metadata.gz: 6da1376c6052a70bacdbb878ee703543e04e0dc044f57cb5b04c35aa08ea536c
4
+ data.tar.gz: 9fe6747b3ee47446e501a37c82978a1e1d00fc33b517ad8c8a34ec13b9620889
5
5
  SHA512:
6
- metadata.gz: f751974222e4ad11bd348dc07e3d8563c67454cf96d1b6faf3054b0b77f978dbba9dd1a10b16084b544e17799d8969df3e7ee50bf4485c0b1ce5849ab8f44716
7
- data.tar.gz: b22856b13ec0ab28ba3e689cb92e99427311834f4e0e807e1a15506488832ce33f7c90a6d982b55d1c474ff4a1837394eed02d4a9bf3882164b9da988bb3b2e8
6
+ metadata.gz: 316fb61ca947bd8a4d581bcd46a6b1c74a4b0b6a2b6c8113e27ceed6df52f124721a2c8b12d09fb7fbe9f09eb69e8a4ead22d79175946d9d1f74a33da23612a9
7
+ data.tar.gz: d15cdb461bfc96aa705c463f0f174ce8490a836afaf8755225d6268edd90462e94019687f3f717a4559e32de3a4dc6711d3b580546477b8f7e674e164f866710
@@ -0,0 +1,63 @@
1
+ param_key = resource_class.model_name.param_key
2
+
3
+ json.pageTitle t("terrazzo.actions.edit", resource_name: resource_name)
4
+
5
+ json.form do
6
+ json.props({
7
+ action: polymorphic_path([namespace, @resource]),
8
+ method: "post",
9
+ encType: "multipart/form-data"
10
+ })
11
+ json.extras do
12
+ if protect_against_forgery?
13
+ json.set!("authenticity_token") do
14
+ json.name "authenticity_token"
15
+ json.type "hidden"
16
+ json.defaultValue form_authenticity_token
17
+ json.autoComplete "off"
18
+ end
19
+ end
20
+ json.set!("_method") do
21
+ json.name "_method"
22
+ json.type "hidden"
23
+ json.value "patch"
24
+ end
25
+ end
26
+ json.fields do
27
+ json.array! @page.attributes("update") do |field|
28
+ json.attribute field.attribute.to_s
29
+ json.label field.attribute.to_s.humanize
30
+ json.fieldType field.field_type
31
+ json.value field.serialize_value(:form)
32
+ json.options field.serializable_options
33
+ json.required field.required?
34
+ json.input field.form_input_attributes(param_key)
35
+ end
36
+ end
37
+ json.fieldGroups do
38
+ json.array! @page.grouped_attributes("update") do |group|
39
+ json.name group[:name]
40
+ json.fields do
41
+ json.array! group[:fields] do |field|
42
+ json.attribute field.attribute.to_s
43
+ json.label field.attribute.to_s.humanize
44
+ json.fieldType field.field_type
45
+ json.value field.serialize_value(:form)
46
+ json.options field.serializable_options
47
+ json.required field.required?
48
+ json.input field.form_input_attributes(param_key)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ json.errors @resource.errors.full_messages
56
+
57
+ json.showPath polymorphic_path([namespace, @resource])
58
+ json.indexPath begin
59
+ url_for(controller: controller_path, action: :index, only_path: true)
60
+ rescue ActionController::UrlGenerationError
61
+ nil
62
+ end
63
+ json.resourceName resource_name
@@ -0,0 +1,91 @@
1
+ json.searchBar do
2
+ json.searchTerm @search_term
3
+ json.searchPath request.path
4
+ end
5
+
6
+ json.filters do
7
+ json.available dashboard.collection_filters.keys.map(&:to_s)
8
+ json.active @active_filter
9
+ json.value @filter_value
10
+ end
11
+
12
+ json.table do
13
+ json.headers do
14
+ json.array! @page.attribute_names do |attr|
15
+ field_class = dashboard.attribute_type_for(attr)
16
+ json.label attr.to_s.humanize
17
+ json.attribute attr.to_s
18
+ json.sortable field_class.sortable?
19
+ json.sortDirection @page.sort_direction_for(attr)
20
+ json.sortUrl url_for(
21
+ @page.order_params_for(attr).merge(
22
+ search: @search_term,
23
+ filter: @active_filter,
24
+ filter_value: @filter_value,
25
+ _page: 1,
26
+ only_path: true
27
+ )
28
+ )
29
+ end
30
+ end
31
+
32
+ json.rows do
33
+ json.array! @resources do |resource|
34
+ json.id resource.id
35
+ json.showPath polymorphic_path([namespace, resource]) rescue nil
36
+ json.collectionItemActions collection_item_actions(resource)
37
+
38
+ json.cells do
39
+ json.array! @page.attribute_names do |attr|
40
+ field = dashboard.attribute_type_for(attr).new(attr, nil, :index, resource: resource)
41
+ json.attribute attr.to_s
42
+ json.fieldType field.field_type
43
+ json.value field.serialize_value(:index)
44
+
45
+ if field.class.associative? && field.data.present?
46
+ json.showPath polymorphic_path([namespace, field.data]) rescue nil
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ json.pagination do
55
+ json.currentPage @resources.current_page
56
+ json.totalPages @resources.total_pages
57
+ json.totalCount @resources.total_count
58
+ json.perPage @resources.limit_value
59
+ if @resources.next_page
60
+ json.nextPagePath url_for(
61
+ _page: @resources.next_page,
62
+ search: @search_term,
63
+ order: params[:order],
64
+ direction: params[:direction],
65
+ filter: @active_filter,
66
+ filter_value: @filter_value,
67
+ props_at: "data.pagination",
68
+ only_path: true
69
+ )
70
+ else
71
+ json.nextPagePath nil
72
+ end
73
+ if @resources.prev_page
74
+ json.prevPagePath url_for(
75
+ _page: @resources.prev_page,
76
+ search: @search_term,
77
+ order: params[:order],
78
+ direction: params[:direction],
79
+ filter: @active_filter,
80
+ filter_value: @filter_value,
81
+ props_at: "data.pagination",
82
+ only_path: true
83
+ )
84
+ else
85
+ json.prevPagePath nil
86
+ end
87
+ end
88
+
89
+ json.resourceName resource_name.pluralize
90
+ json.singularResourceName resource_name
91
+ json.newResourcePath route_exists?(:new) ? (new_polymorphic_path([namespace, resource_class]) rescue nil) : nil
@@ -0,0 +1,58 @@
1
+ action = @resource.persisted? ? "update" : "create"
2
+ param_key = resource_class.model_name.param_key
3
+
4
+ json.pageTitle t("terrazzo.actions.new", resource_name: resource_name)
5
+
6
+ json.form do
7
+ json.props({
8
+ action: polymorphic_path([namespace, resource_class]),
9
+ method: "post",
10
+ encType: "multipart/form-data"
11
+ })
12
+ json.extras do
13
+ if protect_against_forgery?
14
+ json.set!("authenticity_token") do
15
+ json.name "authenticity_token"
16
+ json.type "hidden"
17
+ json.defaultValue form_authenticity_token
18
+ json.autoComplete "off"
19
+ end
20
+ end
21
+ end
22
+ json.fields do
23
+ json.array! @page.attributes(action) do |field|
24
+ json.attribute field.attribute.to_s
25
+ json.label field.attribute.to_s.humanize
26
+ json.fieldType field.field_type
27
+ json.value field.serialize_value(:form)
28
+ json.options field.serializable_options
29
+ json.required field.required?
30
+ json.input field.form_input_attributes(param_key)
31
+ end
32
+ end
33
+ json.fieldGroups do
34
+ json.array! @page.grouped_attributes(action) do |group|
35
+ json.name group[:name]
36
+ json.fields do
37
+ json.array! group[:fields] do |field|
38
+ json.attribute field.attribute.to_s
39
+ json.label field.attribute.to_s.humanize
40
+ json.fieldType field.field_type
41
+ json.value field.serialize_value(:form)
42
+ json.options field.serializable_options
43
+ json.required field.required?
44
+ json.input field.form_input_attributes(param_key)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ json.errors @resource.errors.full_messages
52
+
53
+ json.indexPath begin
54
+ url_for(controller: controller_path, action: :index, only_path: true)
55
+ rescue ActionController::UrlGenerationError
56
+ nil
57
+ end
58
+ json.resourceName resource_name
@@ -0,0 +1,48 @@
1
+ json.pageTitle @page.page_title
2
+
3
+ show_field_json = ->(json, field) do
4
+ json.attribute field.attribute.to_s
5
+ json.label field.attribute.to_s.humanize
6
+ json.fieldType field.field_type
7
+ json.value field.serialize_value(:show)
8
+
9
+ if field.class.associative? && field.data.present?
10
+ if field.is_a?(Terrazzo::Field::HasMany)
11
+ json.itemShowPaths(field.data.each_with_object({}) do |record, paths|
12
+ paths[record.id.to_s] = polymorphic_path([namespace, record]) rescue nil
13
+ end)
14
+ json.collectionItemActions(field.data.each_with_object({}) do |record, hash|
15
+ hash[record.id.to_s] = collection_item_actions(record)
16
+ end)
17
+ else
18
+ json.showPath polymorphic_path([namespace, field.data]) rescue nil
19
+ end
20
+ end
21
+ end
22
+
23
+ json.attributeGroups do
24
+ json.array! @page.grouped_attributes do |group|
25
+ json.name group[:name]
26
+ json.attributes do
27
+ json.array! group[:fields] do |field|
28
+ show_field_json.call(json, field)
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ json.attributes do
35
+ json.array! @page.attributes do |field|
36
+ show_field_json.call(json, field)
37
+ end
38
+ end
39
+
40
+ json.editPath route_exists?(:edit) ? (edit_polymorphic_path([namespace, @resource]) rescue nil) : nil
41
+ json.deletePath route_exists?(:destroy) ? (polymorphic_path([namespace, @resource]) rescue nil) : nil
42
+ json.indexPath begin
43
+ url_for(controller: controller_path, action: :index, only_path: true)
44
+ rescue ActionController::UrlGenerationError
45
+ nil
46
+ end
47
+ json.resourceName resource_name
48
+ json.pluralResourceName resource_name.pluralize
@@ -1,63 +1 @@
1
- param_key = resource_class.model_name.param_key
2
-
3
- json.pageTitle t("terrazzo.actions.edit", resource_name: resource_name)
4
-
5
- json.form do
6
- json.props({
7
- action: polymorphic_path([namespace, @resource]),
8
- method: "post",
9
- encType: "multipart/form-data"
10
- })
11
- json.extras do
12
- if protect_against_forgery?
13
- json.set!("authenticity_token") do
14
- json.name "authenticity_token"
15
- json.type "hidden"
16
- json.defaultValue form_authenticity_token
17
- json.autoComplete "off"
18
- end
19
- end
20
- json.set!("_method") do
21
- json.name "_method"
22
- json.type "hidden"
23
- json.value "patch"
24
- end
25
- end
26
- json.fields do
27
- json.array! @page.attributes("update") do |field|
28
- json.attribute field.attribute.to_s
29
- json.label field.attribute.to_s.humanize
30
- json.fieldType field.field_type
31
- json.value field.serialize_value(:form)
32
- json.options field.serializable_options
33
- json.required field.required?
34
- json.input field.form_input_attributes(param_key)
35
- end
36
- end
37
- json.fieldGroups do
38
- json.array! @page.grouped_attributes("update") do |group|
39
- json.name group[:name]
40
- json.fields do
41
- json.array! group[:fields] do |field|
42
- json.attribute field.attribute.to_s
43
- json.label field.attribute.to_s.humanize
44
- json.fieldType field.field_type
45
- json.value field.serialize_value(:form)
46
- json.options field.serializable_options
47
- json.required field.required?
48
- json.input field.form_input_attributes(param_key)
49
- end
50
- end
51
- end
52
- end
53
- end
54
-
55
- json.errors @resource.errors.full_messages
56
-
57
- json.showPath polymorphic_path([namespace, @resource])
58
- json.indexPath begin
59
- url_for(controller: controller_path, action: :index, only_path: true)
60
- rescue ActionController::UrlGenerationError
61
- nil
62
- end
63
- json.resourceName resource_name
1
+ json.partial! partial: "terrazzo/application/edit_base"
@@ -1,91 +1 @@
1
- json.searchBar do
2
- json.searchTerm @search_term
3
- json.searchPath request.path
4
- end
5
-
6
- json.filters do
7
- json.available dashboard.collection_filters.keys.map(&:to_s)
8
- json.active @active_filter
9
- json.value @filter_value
10
- end
11
-
12
- json.table do
13
- json.headers do
14
- json.array! @page.attribute_names do |attr|
15
- field_class = dashboard.attribute_type_for(attr)
16
- json.label attr.to_s.humanize
17
- json.attribute attr.to_s
18
- json.sortable field_class.sortable?
19
- json.sortDirection @page.sort_direction_for(attr)
20
- json.sortUrl url_for(
21
- @page.order_params_for(attr).merge(
22
- search: @search_term,
23
- filter: @active_filter,
24
- filter_value: @filter_value,
25
- _page: 1,
26
- only_path: true
27
- )
28
- )
29
- end
30
- end
31
-
32
- json.rows do
33
- json.array! @resources do |resource|
34
- json.id resource.id
35
- json.showPath polymorphic_path([namespace, resource]) rescue nil
36
- json.collectionItemActions collection_item_actions(resource)
37
-
38
- json.cells do
39
- json.array! @page.attribute_names do |attr|
40
- field = dashboard.attribute_type_for(attr).new(attr, nil, :index, resource: resource)
41
- json.attribute attr.to_s
42
- json.fieldType field.field_type
43
- json.value field.serialize_value(:index)
44
-
45
- if field.class.associative? && field.data.present?
46
- json.showPath polymorphic_path([namespace, field.data]) rescue nil
47
- end
48
- end
49
- end
50
- end
51
- end
52
- end
53
-
54
- json.pagination do
55
- json.currentPage @resources.current_page
56
- json.totalPages @resources.total_pages
57
- json.totalCount @resources.total_count
58
- json.perPage @resources.limit_value
59
- if @resources.next_page
60
- json.nextPagePath url_for(
61
- _page: @resources.next_page,
62
- search: @search_term,
63
- order: params[:order],
64
- direction: params[:direction],
65
- filter: @active_filter,
66
- filter_value: @filter_value,
67
- props_at: "data.pagination",
68
- only_path: true
69
- )
70
- else
71
- json.nextPagePath nil
72
- end
73
- if @resources.prev_page
74
- json.prevPagePath url_for(
75
- _page: @resources.prev_page,
76
- search: @search_term,
77
- order: params[:order],
78
- direction: params[:direction],
79
- filter: @active_filter,
80
- filter_value: @filter_value,
81
- props_at: "data.pagination",
82
- only_path: true
83
- )
84
- else
85
- json.prevPagePath nil
86
- end
87
- end
88
-
89
- json.resourceName resource_name.pluralize
90
- json.singularResourceName resource_name
91
- json.newResourcePath route_exists?(:new) ? (new_polymorphic_path([namespace, resource_class]) rescue nil) : nil
1
+ json.partial! partial: "terrazzo/application/index_base"
@@ -1,58 +1 @@
1
- action = @resource.persisted? ? "update" : "create"
2
- param_key = resource_class.model_name.param_key
3
-
4
- json.pageTitle t("terrazzo.actions.new", resource_name: resource_name)
5
-
6
- json.form do
7
- json.props({
8
- action: polymorphic_path([namespace, resource_class]),
9
- method: "post",
10
- encType: "multipart/form-data"
11
- })
12
- json.extras do
13
- if protect_against_forgery?
14
- json.set!("authenticity_token") do
15
- json.name "authenticity_token"
16
- json.type "hidden"
17
- json.defaultValue form_authenticity_token
18
- json.autoComplete "off"
19
- end
20
- end
21
- end
22
- json.fields do
23
- json.array! @page.attributes(action) do |field|
24
- json.attribute field.attribute.to_s
25
- json.label field.attribute.to_s.humanize
26
- json.fieldType field.field_type
27
- json.value field.serialize_value(:form)
28
- json.options field.serializable_options
29
- json.required field.required?
30
- json.input field.form_input_attributes(param_key)
31
- end
32
- end
33
- json.fieldGroups do
34
- json.array! @page.grouped_attributes(action) do |group|
35
- json.name group[:name]
36
- json.fields do
37
- json.array! group[:fields] do |field|
38
- json.attribute field.attribute.to_s
39
- json.label field.attribute.to_s.humanize
40
- json.fieldType field.field_type
41
- json.value field.serialize_value(:form)
42
- json.options field.serializable_options
43
- json.required field.required?
44
- json.input field.form_input_attributes(param_key)
45
- end
46
- end
47
- end
48
- end
49
- end
50
-
51
- json.errors @resource.errors.full_messages
52
-
53
- json.indexPath begin
54
- url_for(controller: controller_path, action: :index, only_path: true)
55
- rescue ActionController::UrlGenerationError
56
- nil
57
- end
58
- json.resourceName resource_name
1
+ json.partial! partial: "terrazzo/application/new_base"
@@ -1,48 +1 @@
1
- json.pageTitle @page.page_title
2
-
3
- show_field_json = ->(json, field) do
4
- json.attribute field.attribute.to_s
5
- json.label field.attribute.to_s.humanize
6
- json.fieldType field.field_type
7
- json.value field.serialize_value(:show)
8
-
9
- if field.class.associative? && field.data.present?
10
- if field.is_a?(Terrazzo::Field::HasMany)
11
- json.itemShowPaths(field.data.each_with_object({}) do |record, paths|
12
- paths[record.id.to_s] = polymorphic_path([namespace, record]) rescue nil
13
- end)
14
- json.collectionItemActions(field.data.each_with_object({}) do |record, hash|
15
- hash[record.id.to_s] = collection_item_actions(record)
16
- end)
17
- else
18
- json.showPath polymorphic_path([namespace, field.data]) rescue nil
19
- end
20
- end
21
- end
22
-
23
- json.attributeGroups do
24
- json.array! @page.grouped_attributes do |group|
25
- json.name group[:name]
26
- json.attributes do
27
- json.array! group[:fields] do |field|
28
- show_field_json.call(json, field)
29
- end
30
- end
31
- end
32
- end
33
-
34
- json.attributes do
35
- json.array! @page.attributes do |field|
36
- show_field_json.call(json, field)
37
- end
38
- end
39
-
40
- json.editPath route_exists?(:edit) ? (edit_polymorphic_path([namespace, @resource]) rescue nil) : nil
41
- json.deletePath route_exists?(:destroy) ? (polymorphic_path([namespace, @resource]) rescue nil) : nil
42
- json.indexPath begin
43
- url_for(controller: controller_path, action: :index, only_path: true)
44
- rescue ActionController::UrlGenerationError
45
- nil
46
- end
47
- json.resourceName resource_name
48
- json.pluralResourceName resource_name.pluralize
1
+ json.partial! partial: "terrazzo/application/show_base"
@@ -6,18 +6,66 @@ module Terrazzo
6
6
  class EditGenerator < Rails::Generators::Base
7
7
  source_root File.expand_path("templates", __dir__)
8
8
 
9
+ argument :resource, type: :string, required: false,
10
+ desc: "Resource model (e.g., User) to eject a resource-specific edit view"
11
+
9
12
  class_option :namespace, type: :string, default: "admin",
10
13
  desc: "Admin namespace"
11
14
 
12
15
  def copy_edit_template
13
- copy_file "pages/edit.jsx", "app/views/#{namespace_name}/application/edit.jsx"
16
+ if resource.present?
17
+ eject_json_props
18
+ copy_file "pages/edit.jsx", "app/views/#{namespace_name}/#{resource_path}/edit.jsx"
19
+ copy_file "pages/_form.jsx", "app/views/#{namespace_name}/#{resource_path}/_form.jsx"
20
+ eject_new_view if should_eject_new?
21
+ else
22
+ copy_file "pages/edit.jsx", "app/views/#{namespace_name}/application/edit.jsx"
23
+ copy_file "pages/_form.jsx", "app/views/#{namespace_name}/application/_form.jsx"
24
+ eject_new_view if should_eject_new?
25
+ end
14
26
  end
15
27
 
16
- def copy_form_partial
17
- copy_file "pages/_form.jsx", "app/views/#{namespace_name}/application/_form.jsx"
28
+ private
29
+
30
+ def eject_json_props
31
+ create_file "app/views/#{namespace_name}/#{resource_path}/edit.json.props", <<~RUBY
32
+ json.partial! partial: "terrazzo/application/edit_base"
33
+ # Add custom props below:
34
+ # json.customProp @resource.some_method
35
+ RUBY
18
36
  end
19
37
 
20
- private
38
+ def should_eject_new?
39
+ new_path = if resource.present?
40
+ "app/views/#{namespace_name}/#{resource_path}/new.jsx"
41
+ else
42
+ "app/views/#{namespace_name}/application/new.jsx"
43
+ end
44
+ return false if File.exist?(new_path)
45
+
46
+ yes?("Also eject the new view to share the custom form partial? (y/n)")
47
+ end
48
+
49
+ def eject_new_view
50
+ if resource.present?
51
+ eject_new_json_props
52
+ copy_file "pages/new.jsx", "app/views/#{namespace_name}/#{resource_path}/new.jsx"
53
+ else
54
+ copy_file "pages/new.jsx", "app/views/#{namespace_name}/application/new.jsx"
55
+ end
56
+ end
57
+
58
+ def eject_new_json_props
59
+ create_file "app/views/#{namespace_name}/#{resource_path}/new.json.props", <<~RUBY
60
+ json.partial! partial: "terrazzo/application/new_base"
61
+ # Add custom props below:
62
+ # json.customProp SomeModel.some_value
63
+ RUBY
64
+ end
65
+
66
+ def resource_path
67
+ resource.underscore.pluralize
68
+ end
21
69
 
22
70
  def namespace_name
23
71
  options[:namespace]
@@ -6,15 +6,37 @@ module Terrazzo
6
6
  class IndexGenerator < Rails::Generators::Base
7
7
  source_root File.expand_path("templates", __dir__)
8
8
 
9
+ argument :resource, type: :string, required: false,
10
+ desc: "Resource model (e.g., User) to eject a resource-specific index view"
11
+
9
12
  class_option :namespace, type: :string, default: "admin",
10
13
  desc: "Admin namespace"
11
14
 
12
15
  def copy_index_template
13
- copy_file "pages/index.jsx", "app/views/#{namespace_name}/application/index.jsx"
16
+ if resource.present?
17
+ eject_json_props
18
+ copy_file "pages/index.jsx", "app/views/#{namespace_name}/#{resource_path}/index.jsx"
19
+ copy_file "pages/_collection.jsx", "app/views/#{namespace_name}/#{resource_path}/_collection.jsx"
20
+ else
21
+ copy_file "pages/index.jsx", "app/views/#{namespace_name}/application/index.jsx"
22
+ copy_file "pages/_collection.jsx", "app/views/#{namespace_name}/application/_collection.jsx"
23
+ end
14
24
  end
15
25
 
16
26
  private
17
27
 
28
+ def eject_json_props
29
+ create_file "app/views/#{namespace_name}/#{resource_path}/index.json.props", <<~RUBY
30
+ json.partial! partial: "terrazzo/application/index_base"
31
+ # Add custom props below:
32
+ # json.customProp SomeModel.count
33
+ RUBY
34
+ end
35
+
36
+ def resource_path
37
+ resource.underscore.pluralize
38
+ end
39
+
18
40
  def namespace_name
19
41
  options[:namespace]
20
42
  end
@@ -6,18 +6,66 @@ module Terrazzo
6
6
  class NewGenerator < Rails::Generators::Base
7
7
  source_root File.expand_path("templates", __dir__)
8
8
 
9
+ argument :resource, type: :string, required: false,
10
+ desc: "Resource model (e.g., User) to eject a resource-specific new view"
11
+
9
12
  class_option :namespace, type: :string, default: "admin",
10
13
  desc: "Admin namespace"
11
14
 
12
15
  def copy_new_template
13
- copy_file "pages/new.jsx", "app/views/#{namespace_name}/application/new.jsx"
16
+ if resource.present?
17
+ eject_json_props
18
+ copy_file "pages/new.jsx", "app/views/#{namespace_name}/#{resource_path}/new.jsx"
19
+ copy_file "pages/_form.jsx", "app/views/#{namespace_name}/#{resource_path}/_form.jsx"
20
+ eject_edit_view if should_eject_edit?
21
+ else
22
+ copy_file "pages/new.jsx", "app/views/#{namespace_name}/application/new.jsx"
23
+ copy_file "pages/_form.jsx", "app/views/#{namespace_name}/application/_form.jsx"
24
+ eject_edit_view if should_eject_edit?
25
+ end
14
26
  end
15
27
 
16
- def copy_form_partial
17
- copy_file "pages/_form.jsx", "app/views/#{namespace_name}/application/_form.jsx"
28
+ private
29
+
30
+ def eject_json_props
31
+ create_file "app/views/#{namespace_name}/#{resource_path}/new.json.props", <<~RUBY
32
+ json.partial! partial: "terrazzo/application/new_base"
33
+ # Add custom props below:
34
+ # json.customProp SomeModel.some_value
35
+ RUBY
18
36
  end
19
37
 
20
- private
38
+ def should_eject_edit?
39
+ edit_path = if resource.present?
40
+ "app/views/#{namespace_name}/#{resource_path}/edit.jsx"
41
+ else
42
+ "app/views/#{namespace_name}/application/edit.jsx"
43
+ end
44
+ return false if File.exist?(edit_path)
45
+
46
+ yes?("Also eject the edit view to share the custom form partial? (y/n)")
47
+ end
48
+
49
+ def eject_edit_view
50
+ if resource.present?
51
+ eject_edit_json_props
52
+ copy_file "pages/edit.jsx", "app/views/#{namespace_name}/#{resource_path}/edit.jsx"
53
+ else
54
+ copy_file "pages/edit.jsx", "app/views/#{namespace_name}/application/edit.jsx"
55
+ end
56
+ end
57
+
58
+ def eject_edit_json_props
59
+ create_file "app/views/#{namespace_name}/#{resource_path}/edit.json.props", <<~RUBY
60
+ json.partial! partial: "terrazzo/application/edit_base"
61
+ # Add custom props below:
62
+ # json.customProp @resource.some_method
63
+ RUBY
64
+ end
65
+
66
+ def resource_path
67
+ resource.underscore.pluralize
68
+ end
21
69
 
22
70
  def namespace_name
23
71
  options[:namespace]
@@ -6,15 +6,34 @@ module Terrazzo
6
6
  class ShowGenerator < Rails::Generators::Base
7
7
  source_root File.expand_path("templates", __dir__)
8
8
 
9
+ argument :resource, type: :string, required: false,
10
+ desc: "Resource model (e.g., User) to eject a resource-specific show.json.props"
11
+
9
12
  class_option :namespace, type: :string, default: "admin",
10
13
  desc: "Admin namespace"
11
14
 
12
15
  def copy_show_template
13
- copy_file "pages/show.jsx", "app/views/#{namespace_name}/application/show.jsx"
16
+ if resource.present?
17
+ eject_json_props
18
+ else
19
+ copy_file "pages/show.jsx", "app/views/#{namespace_name}/application/show.jsx"
20
+ end
14
21
  end
15
22
 
16
23
  private
17
24
 
25
+ def eject_json_props
26
+ create_file "app/views/#{namespace_name}/#{resource_path}/show.json.props", <<~RUBY
27
+ json.partial! partial: "terrazzo/application/show_base"
28
+ # Add custom props below:
29
+ # json.customProp @resource.some_method
30
+ RUBY
31
+ end
32
+
33
+ def resource_path
34
+ resource.underscore.pluralize
35
+ end
36
+
18
37
  def namespace_name
19
38
  options[:namespace]
20
39
  end
@@ -0,0 +1,55 @@
1
+ import React, { useContext } from "react";
2
+ import { NavigationContext } from "@thoughtbot/superglue";
3
+
4
+ import { SortableHeader, CollectionItemActions } from "../components";
5
+ import { FieldRenderer } from "../fields";
6
+ import { Table, TableHeader, TableBody, TableRow, TableHead, TableCell } from "../components/ui";
7
+
8
+ export function AdminCollection({ table }) {
9
+ const { visit } = useContext(NavigationContext);
10
+
11
+ const handleRowClick = (e, showPath) => {
12
+ if (!showPath) return;
13
+ if (e.target.closest("a, button, form")) return;
14
+ if (window.getSelection().toString()) return;
15
+ visit(showPath, {});
16
+ };
17
+
18
+ return (
19
+ <div className="overflow-x-auto rounded-md border">
20
+ <Table>
21
+ <TableHeader>
22
+ <TableRow>
23
+ {table.headers.map((header) =>
24
+ <SortableHeader key={header.attribute} {...header} />
25
+ )}
26
+ <TableHead className="w-[120px]">Actions</TableHead>
27
+ </TableRow>
28
+ </TableHeader>
29
+ <TableBody>
30
+ {table.rows.map((row) =>
31
+ <TableRow
32
+ key={row.id}
33
+ className={row.showPath ? "cursor-pointer" : ""}
34
+ onClick={(e) => handleRowClick(e, row.showPath)}>
35
+ {row.cells.map((cell) =>
36
+ <TableCell key={cell.attribute}>
37
+ {cell.showPath ? (
38
+ <a href={cell.showPath} data-sg-visit className="hover:underline">
39
+ <FieldRenderer mode="index" {...cell} />
40
+ </a>
41
+ ) : (
42
+ <FieldRenderer mode="index" {...cell} />
43
+ )}
44
+ </TableCell>
45
+ )}
46
+ <TableCell>
47
+ <CollectionItemActions actions={row.collectionItemActions} />
48
+ </TableCell>
49
+ </TableRow>
50
+ )}
51
+ </TableBody>
52
+ </Table>
53
+ </div>
54
+ );
55
+ }
@@ -1,12 +1,11 @@
1
- import React, { useContext } from "react";
2
- import { useContent, NavigationContext } from "@thoughtbot/superglue";
1
+ import React from "react";
2
+ import { useContent } from "@thoughtbot/superglue";
3
3
 
4
- import { Layout, SearchBar, Pagination, SortableHeader, CollectionItemActions } from "terrazzo/components";
5
- import { FieldRenderer } from "terrazzo/fields";
6
- import { Button, Table, TableHeader, TableBody, TableRow, TableHead, TableCell } from "terrazzo/ui";
4
+ import { Layout, SearchBar, Pagination } from "../components";
5
+ import { AdminCollection } from "./_collection";
6
+ import { Button } from "../components/ui";
7
7
 
8
8
  export default function AdminIndex() {
9
- const { visit } = useContext(NavigationContext);
10
9
  const {
11
10
  table,
12
11
  searchBar,
@@ -17,18 +16,11 @@ export default function AdminIndex() {
17
16
  singularResourceName
18
17
  } = useContent();
19
18
 
20
- const handleRowClick = (e, showPath) => {
21
- if (!showPath) return;
22
- if (e.target.closest("a, button, form")) return;
23
- if (window.getSelection().toString()) return;
24
- visit(showPath, {});
25
- };
26
-
27
19
  return (
28
20
  <Layout
29
21
  navigation={navigation}
30
22
  title={resourceName}
31
- actions={
23
+ actions={newResourcePath &&
32
24
  <a href={newResourcePath} data-sg-visit>
33
25
  <Button size="sm">New {singularResourceName}</Button>
34
26
  </a>
@@ -38,41 +30,7 @@ export default function AdminIndex() {
38
30
  <SearchBar {...searchBar} />
39
31
  </div>
40
32
 
41
- <div className="overflow-x-auto rounded-md border">
42
- <Table>
43
- <TableHeader>
44
- <TableRow>
45
- {table.headers.map((header) =>
46
- <SortableHeader key={header.attribute} {...header} />
47
- )}
48
- <TableHead className="w-[120px]">Actions</TableHead>
49
- </TableRow>
50
- </TableHeader>
51
- <TableBody>
52
- {table.rows.map((row) =>
53
- <TableRow
54
- key={row.id}
55
- className={row.showPath ? "cursor-pointer" : ""}
56
- onClick={(e) => handleRowClick(e, row.showPath)}>
57
- {row.cells.map((cell) =>
58
- <TableCell key={cell.attribute}>
59
- {cell.showPath ? (
60
- <a href={cell.showPath} data-sg-visit className="hover:underline">
61
- <FieldRenderer mode="index" {...cell} />
62
- </a>
63
- ) : (
64
- <FieldRenderer mode="index" {...cell} />
65
- )}
66
- </TableCell>
67
- )}
68
- <TableCell>
69
- <CollectionItemActions actions={row.collectionItemActions} />
70
- </TableCell>
71
- </TableRow>
72
- )}
73
- </TableBody>
74
- </Table>
75
- </div>
33
+ <AdminCollection table={table} />
76
34
 
77
35
  <Pagination {...pagination} />
78
36
  </Layout>);
@@ -14,7 +14,7 @@ module Terrazzo
14
14
  def serializable_options
15
15
  opts = {}
16
16
  if resource
17
- collection = resource_options
17
+ collection = ordered_resource_options
18
18
  if options[:include_blank]
19
19
  collection = [["", nil]] + collection
20
20
  end
@@ -38,6 +38,21 @@ module Terrazzo
38
38
 
39
39
  private
40
40
 
41
+ def ordered_resource_options
42
+ return [] unless associated_class
43
+ scope = if options[:scope].is_a?(Proc)
44
+ options[:scope].call(associated_class)
45
+ elsif options[:scope]
46
+ associated_class.public_send(options[:scope])
47
+ else
48
+ associated_class.all
49
+ end
50
+ scope = scope.reorder(options[:order]) if options[:order]
51
+ pk = association_primary_key
52
+ dashboard = associated_dashboard
53
+ scope.map { |r| [dashboard ? dashboard.display_resource(r) : display_name(r), r.public_send(pk).to_s] }
54
+ end
55
+
41
56
  def foreign_key_value
42
57
  return nil unless resource
43
58
 
@@ -3,8 +3,15 @@ module Terrazzo
3
3
  class Date < Base
4
4
  def serialize_value(_mode)
5
5
  return nil if data.nil?
6
+ value = if options[:timezone]
7
+ data.in_time_zone(options[:timezone]).to_date
8
+ elsif data.respond_to?(:in_time_zone)
9
+ data.in_time_zone.to_date
10
+ else
11
+ data
12
+ end
6
13
  format = options[:format]
7
- format ? data.strftime(format) : data.to_s
14
+ format ? value.strftime(format) : value.to_s
8
15
  end
9
16
  end
10
17
  end
@@ -3,8 +3,15 @@ module Terrazzo
3
3
  class DateTime < Base
4
4
  def serialize_value(_mode)
5
5
  return nil if data.nil?
6
+ value = if options[:timezone]
7
+ data.in_time_zone(options[:timezone])
8
+ elsif data.respond_to?(:in_time_zone)
9
+ data.in_time_zone
10
+ else
11
+ data
12
+ end
6
13
  format = options[:format]
7
- format ? data.strftime(format) : data.iso8601
14
+ format ? value.strftime(format) : value.iso8601
8
15
  end
9
16
  end
10
17
  end
@@ -45,7 +45,8 @@ module Terrazzo
45
45
 
46
46
  def serialize_show_value
47
47
  limit = options.fetch(:limit, 5)
48
- all_records = data.to_a
48
+ records = apply_sorting(data)
49
+ all_records = records.to_a
49
50
  total = all_records.size
50
51
  col_attrs = options[:collection_attributes] || resolve_default_collection_attributes
51
52
 
@@ -94,6 +95,29 @@ module Terrazzo
94
95
  }
95
96
  end
96
97
 
98
+ def resource_options
99
+ return [] unless associated_class
100
+ scope = if options[:scope].is_a?(Proc)
101
+ options[:scope].call(associated_class)
102
+ elsif options[:scope]
103
+ associated_class.public_send(options[:scope])
104
+ else
105
+ associated_class.all
106
+ end
107
+ scope = scope.includes(*options[:includes]) if options.key?(:includes)
108
+ pk = association_primary_key
109
+ dashboard = associated_dashboard
110
+ scope.map { |r| [dashboard ? dashboard.display_resource(r) : display_name(r), r.public_send(pk).to_s] }
111
+ end
112
+
113
+ def apply_sorting(records)
114
+ sort_by = options[:sort_by]
115
+ return records unless sort_by
116
+
117
+ direction = options.fetch(:direction, :asc)
118
+ records.reorder(sort_by => direction)
119
+ end
120
+
97
121
  def find_associated_dashboard
98
122
  klass = associated_class
99
123
  "#{klass.name}Dashboard".constantize
@@ -1,10 +1,20 @@
1
+ require "active_support/number_helper"
2
+
1
3
  module Terrazzo
2
4
  module Field
3
5
  class Number < Base
4
6
  def serialize_value(mode)
5
- return data if data.nil? || mode == :form || !options.key?(:multiplier)
7
+ return data if data.nil? || mode == :form
8
+
9
+ value = options.key?(:multiplier) ? data * options[:multiplier] : data
6
10
 
7
- data * options[:multiplier]
11
+ if options[:format]
12
+ formatter = options[:format][:formatter]
13
+ formatter_options = options[:format][:formatter_options].to_h
14
+ ActiveSupport::NumberHelper.try(formatter, value, **formatter_options) || value
15
+ else
16
+ value
17
+ end
8
18
  end
9
19
 
10
20
  def serializable_options
@@ -15,9 +15,11 @@ module Terrazzo
15
15
  def serializable_options
16
16
  opts = {}
17
17
  classes = options[:classes] || []
18
+ order = options[:order]
18
19
  opts[:groupedOptions] = classes.each_with_object({}) do |klass, hash|
19
20
  klass = klass.constantize if klass.is_a?(::String)
20
- hash[klass.name] = klass.all.map { |r| [display_name(r), r.id.to_s] }
21
+ scope = order ? klass.order(order) : klass.all
22
+ hash[klass.name] = scope.map { |r| [display_name(r), r.id.to_s] }
21
23
  end
22
24
  opts
23
25
  end
@@ -1,3 +1,3 @@
1
1
  module Terrazzo
2
- VERSION = "0.3.2"
2
+ VERSION = "0.4.0"
3
3
  end
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.3.2
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Terrazzo Contributors
@@ -118,7 +118,11 @@ files:
118
118
  - Rakefile
119
119
  - app/controllers/terrazzo/application_controller.rb
120
120
  - app/helpers/terrazzo/collection_actions_helper.rb
121
+ - app/views/terrazzo/application/_edit_base.json.props
122
+ - app/views/terrazzo/application/_index_base.json.props
121
123
  - app/views/terrazzo/application/_navigation.json.props
124
+ - app/views/terrazzo/application/_new_base.json.props
125
+ - app/views/terrazzo/application/_show_base.json.props
122
126
  - app/views/terrazzo/application/edit.json.props
123
127
  - app/views/terrazzo/application/index.json.props
124
128
  - app/views/terrazzo/application/new.json.props
@@ -233,6 +237,7 @@ files:
233
237
  - lib/generators/terrazzo/views/templates/fields/url/FormField.jsx
234
238
  - lib/generators/terrazzo/views/templates/fields/url/IndexField.jsx
235
239
  - lib/generators/terrazzo/views/templates/fields/url/ShowField.jsx
240
+ - lib/generators/terrazzo/views/templates/pages/_collection.jsx
236
241
  - lib/generators/terrazzo/views/templates/pages/_form.jsx
237
242
  - lib/generators/terrazzo/views/templates/pages/_navigation.json.props
238
243
  - lib/generators/terrazzo/views/templates/pages/edit.jsx