terrazzo 0.5.1 → 0.5.3
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 +1 -1
- data/app/views/terrazzo/application/_edit_base.json.props +2 -2
- data/app/views/terrazzo/application/_new_base.json.props +2 -2
- data/app/views/terrazzo/application/_show_base.json.props +6 -12
- data/lib/generators/terrazzo/field/templates/field.rb.erb +1 -1
- data/lib/generators/terrazzo/views/templates/fields/has_many/ShowField.jsx +29 -71
- data/lib/generators/terrazzo/views/templates/pages/_collection.jsx +4 -48
- data/lib/generators/terrazzo/views/templates/pages/show.jsx +2 -2
- data/lib/terrazzo/field/base.rb +1 -1
- data/lib/terrazzo/field/belongs_to.rb +6 -9
- data/lib/terrazzo/field/has_many.rb +16 -7
- data/lib/terrazzo/field/has_one.rb +3 -6
- data/lib/terrazzo/field/money.rb +1 -1
- data/lib/terrazzo/field/number.rb +1 -1
- data/lib/terrazzo/field/polymorphic.rb +4 -4
- data/lib/terrazzo/field/select.rb +2 -1
- data/lib/terrazzo/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6976f4987f794484e19de73da573e1b7d65dfc75a0852133c1eebf005d22167e
|
|
4
|
+
data.tar.gz: 4dc5597241838c5523ed2a5a50d23185a0e21199b18ae9dd07128d7687e836e6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 673cd873e8c295de85c4bc2d798a33d4879e37cfded9357d01c08b1617e050931502193c3be3c445f0577f4bc29372bc15867b3e4fafe519161f6209d894ca4f
|
|
7
|
+
data.tar.gz: ae15ddf2abd934407d97f3e17d4344e68f5ff93e7fce588f9d520998d017404f980c7f75d07709ba2e984ae49490d65d9a573d4f169949c8b03cce849402f46b
|
|
@@ -29,7 +29,7 @@ json.form do
|
|
|
29
29
|
json.label field.attribute.to_s.humanize
|
|
30
30
|
json.fieldType field.field_type
|
|
31
31
|
json.value field.serialize_value(:form)
|
|
32
|
-
json.options field.serializable_options
|
|
32
|
+
json.options field.serializable_options(:form)
|
|
33
33
|
json.required field.required?
|
|
34
34
|
json.input field.form_input_attributes(param_key)
|
|
35
35
|
end
|
|
@@ -43,7 +43,7 @@ json.form do
|
|
|
43
43
|
json.label field.attribute.to_s.humanize
|
|
44
44
|
json.fieldType field.field_type
|
|
45
45
|
json.value field.serialize_value(:form)
|
|
46
|
-
json.options field.serializable_options
|
|
46
|
+
json.options field.serializable_options(:form)
|
|
47
47
|
json.required field.required?
|
|
48
48
|
json.input field.form_input_attributes(param_key)
|
|
49
49
|
end
|
|
@@ -25,7 +25,7 @@ json.form do
|
|
|
25
25
|
json.label field.attribute.to_s.humanize
|
|
26
26
|
json.fieldType field.field_type
|
|
27
27
|
json.value field.serialize_value(:form)
|
|
28
|
-
json.options field.serializable_options
|
|
28
|
+
json.options field.serializable_options(:form)
|
|
29
29
|
json.required field.required?
|
|
30
30
|
json.input field.form_input_attributes(param_key)
|
|
31
31
|
end
|
|
@@ -39,7 +39,7 @@ json.form do
|
|
|
39
39
|
json.label field.attribute.to_s.humanize
|
|
40
40
|
json.fieldType field.field_type
|
|
41
41
|
json.value field.serialize_value(:form)
|
|
42
|
-
json.options field.serializable_options
|
|
42
|
+
json.options field.serializable_options(:form)
|
|
43
43
|
json.required field.required?
|
|
44
44
|
json.input field.form_input_attributes(param_key)
|
|
45
45
|
end
|
|
@@ -9,11 +9,11 @@ show_field_json = ->(json, field) do
|
|
|
9
9
|
|
|
10
10
|
if field.class.associative? && field.data.present?
|
|
11
11
|
if field.is_a?(Terrazzo::Field::HasMany)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
hash[record.id.to_s] =
|
|
12
|
+
render_actions = field.options.fetch(:render_actions, true)
|
|
13
|
+
json.hasManyRowExtras(field.data.each_with_object({}) do |record, hash|
|
|
14
|
+
extras = { showPath: (polymorphic_path([namespace, record]) rescue nil) }
|
|
15
|
+
extras[:collectionItemActions] = collection_item_actions(record) if render_actions
|
|
16
|
+
hash[record.id.to_s] = extras
|
|
17
17
|
end)
|
|
18
18
|
else
|
|
19
19
|
json.showPath polymorphic_path([namespace, field.data]) rescue nil
|
|
@@ -21,7 +21,7 @@ show_field_json = ->(json, field) do
|
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
json.
|
|
24
|
+
json.attributes do
|
|
25
25
|
json.array! @page.grouped_attributes do |group|
|
|
26
26
|
json.name group[:name]
|
|
27
27
|
json.attributes do
|
|
@@ -32,12 +32,6 @@ json.attributeGroups do
|
|
|
32
32
|
end
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
-
json.attributes do
|
|
36
|
-
json.array! @page.attributes do |field|
|
|
37
|
-
show_field_json.call(json, field)
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
|
|
41
35
|
json.editPath route_exists?(:edit) ? (edit_polymorphic_path([namespace, @resource]) rescue nil) : nil
|
|
42
36
|
json.deletePath route_exists?(:destroy) ? (polymorphic_path([namespace, @resource]) rescue nil) : nil
|
|
43
37
|
json.indexPath begin
|
|
@@ -1,85 +1,36 @@
|
|
|
1
|
-
import React, { useState
|
|
2
|
-
import { NavigationContext } from "@thoughtbot/superglue";
|
|
1
|
+
import React, { useState } from "react";
|
|
3
2
|
|
|
4
|
-
import {
|
|
5
|
-
Table,
|
|
6
|
-
TableHeader,
|
|
7
|
-
TableBody,
|
|
8
|
-
TableRow,
|
|
9
|
-
TableHead,
|
|
10
|
-
TableCell,
|
|
11
|
-
} from "terrazzo/ui";
|
|
3
|
+
import { ResourceTable } from "terrazzo/components";
|
|
12
4
|
import { Badge } from "terrazzo/ui";
|
|
13
5
|
import { Button } from "terrazzo/ui";
|
|
14
|
-
import { CollectionItemActions } from "terrazzo/components";
|
|
15
|
-
import { FieldRenderer } from "../FieldRenderer";
|
|
16
6
|
|
|
17
|
-
export function ShowField({ value,
|
|
7
|
+
export function ShowField({ value, hasManyRowExtras, options }) {
|
|
18
8
|
if (!value) return <span className="text-muted-foreground">None</span>;
|
|
19
9
|
|
|
20
|
-
const {
|
|
10
|
+
const { rows, headers, total, initialLimit, items } = value;
|
|
21
11
|
const [expanded, setExpanded] = useState(false);
|
|
22
|
-
const { visit } = useContext(NavigationContext);
|
|
23
12
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
13
|
+
// Table mode: collection_attributes specified
|
|
14
|
+
if (headers && rows) {
|
|
15
|
+
if (rows.length === 0) {
|
|
16
|
+
return <span className="text-muted-foreground">None</span>;
|
|
17
|
+
}
|
|
27
18
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const visibleItems = expanded || !hasMore ? items : items.slice(0, initialLimit);
|
|
19
|
+
const hasMore = initialLimit && initialLimit > 0 && total > initialLimit;
|
|
20
|
+
const visibleRows = expanded || !hasMore ? rows : rows.slice(0, initialLimit);
|
|
31
21
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
22
|
+
const enrichedRows = visibleRows.map((row) => {
|
|
23
|
+
const extras = hasManyRowExtras?.[String(row.id)] || {};
|
|
24
|
+
return {
|
|
25
|
+
...row,
|
|
26
|
+
showPath: extras.showPath,
|
|
27
|
+
collectionItemActions: extras.collectionItemActions,
|
|
28
|
+
};
|
|
29
|
+
});
|
|
38
30
|
|
|
39
|
-
// Table mode: collection_attributes specified
|
|
40
|
-
if (headers) {
|
|
41
31
|
return (
|
|
42
32
|
<div>
|
|
43
|
-
<
|
|
44
|
-
<Table>
|
|
45
|
-
<TableHeader>
|
|
46
|
-
<TableRow>
|
|
47
|
-
{headers.map((header) =>
|
|
48
|
-
<TableHead key={header.attribute}>{header.label}</TableHead>
|
|
49
|
-
)}
|
|
50
|
-
{collectionItemActions && <TableHead></TableHead>}
|
|
51
|
-
</TableRow>
|
|
52
|
-
</TableHeader>
|
|
53
|
-
<TableBody>
|
|
54
|
-
{visibleItems.map((item) => {
|
|
55
|
-
const showPath = pathFor(item.id);
|
|
56
|
-
return (
|
|
57
|
-
<TableRow
|
|
58
|
-
key={item.id}
|
|
59
|
-
className={showPath ? "cursor-pointer" : ""}
|
|
60
|
-
onClick={(e) => handleRowClick(e, showPath)}>
|
|
61
|
-
{item.columns.map((col, colIndex) =>
|
|
62
|
-
<TableCell key={col.attribute}>
|
|
63
|
-
{showPath && colIndex === 0 ? (
|
|
64
|
-
<a href={showPath} data-sg-visit className="hover:underline">
|
|
65
|
-
<FieldRenderer mode="index" {...col} />
|
|
66
|
-
</a>
|
|
67
|
-
) : (
|
|
68
|
-
<FieldRenderer mode="index" {...col} />
|
|
69
|
-
)}
|
|
70
|
-
</TableCell>
|
|
71
|
-
)}
|
|
72
|
-
{collectionItemActions && (
|
|
73
|
-
<TableCell>
|
|
74
|
-
<CollectionItemActions actions={collectionItemActions?.[String(item.id)]} />
|
|
75
|
-
</TableCell>
|
|
76
|
-
)}
|
|
77
|
-
</TableRow>
|
|
78
|
-
);
|
|
79
|
-
})}
|
|
80
|
-
</TableBody>
|
|
81
|
-
</Table>
|
|
82
|
-
</div>
|
|
33
|
+
<ResourceTable headers={headers} rows={enrichedRows} showActions={options?.renderActions !== false} />
|
|
83
34
|
{hasMore && (
|
|
84
35
|
<Button
|
|
85
36
|
variant="link"
|
|
@@ -93,12 +44,19 @@ export function ShowField({ value, itemShowPaths, collectionItemActions }) {
|
|
|
93
44
|
);
|
|
94
45
|
}
|
|
95
46
|
|
|
96
|
-
// Simple list mode
|
|
47
|
+
// Simple list mode (no collection_attributes)
|
|
48
|
+
if (!items || items.length === 0) {
|
|
49
|
+
return <span className="text-muted-foreground">None</span>;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const hasMore = initialLimit && initialLimit > 0 && total > initialLimit;
|
|
53
|
+
const visibleItems = expanded || !hasMore ? items : items.slice(0, initialLimit);
|
|
54
|
+
|
|
97
55
|
return (
|
|
98
56
|
<div>
|
|
99
57
|
<div className="flex flex-wrap items-center gap-1.5">
|
|
100
58
|
{visibleItems.map((item) => {
|
|
101
|
-
const showPath =
|
|
59
|
+
const showPath = hasManyRowExtras?.[String(item.id)]?.showPath;
|
|
102
60
|
return showPath ? (
|
|
103
61
|
<a key={item.id} href={showPath} data-sg-visit>
|
|
104
62
|
<Badge variant="secondary" className="cursor-pointer">{item.display}</Badge>
|
|
@@ -1,55 +1,11 @@
|
|
|
1
|
-
import React
|
|
2
|
-
import { NavigationContext } from "@thoughtbot/superglue";
|
|
1
|
+
import React from "react";
|
|
3
2
|
|
|
4
|
-
import {
|
|
5
|
-
import { FieldRenderer } from "terrazzo/fields";
|
|
6
|
-
import { Table, TableHeader, TableBody, TableRow, TableHead, TableCell } from "terrazzo/ui";
|
|
3
|
+
import { ResourceTable } from "terrazzo/components";
|
|
7
4
|
|
|
8
5
|
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
6
|
return (
|
|
19
|
-
<div className="overflow-x-auto
|
|
20
|
-
<
|
|
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>
|
|
7
|
+
<div className="overflow-x-auto">
|
|
8
|
+
<ResourceTable headers={table.headers} rows={table.rows} />
|
|
53
9
|
</div>
|
|
54
10
|
);
|
|
55
11
|
}
|
|
@@ -9,7 +9,7 @@ export default function AdminShow() {
|
|
|
9
9
|
const Layout = getLayout();
|
|
10
10
|
const {
|
|
11
11
|
pageTitle,
|
|
12
|
-
|
|
12
|
+
attributes,
|
|
13
13
|
editPath,
|
|
14
14
|
deletePath,
|
|
15
15
|
indexPath,
|
|
@@ -54,7 +54,7 @@ export default function AdminShow() {
|
|
|
54
54
|
</div>
|
|
55
55
|
}>
|
|
56
56
|
|
|
57
|
-
{
|
|
57
|
+
{attributes.map((group, groupIndex) =>
|
|
58
58
|
<Card key={groupIndex}>
|
|
59
59
|
{group.name && (
|
|
60
60
|
<CardHeader>
|
data/lib/terrazzo/field/base.rb
CHANGED
|
@@ -11,16 +11,13 @@ module Terrazzo
|
|
|
11
11
|
end
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
def serializable_options
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
collection = [["", nil]] + collection
|
|
20
|
-
end
|
|
21
|
-
opts[:resourceOptions] = collection
|
|
14
|
+
def serializable_options(page = nil)
|
|
15
|
+
return {} unless page == :form && resource
|
|
16
|
+
collection = ordered_resource_options
|
|
17
|
+
if options[:include_blank]
|
|
18
|
+
collection = [["", nil]] + collection
|
|
22
19
|
end
|
|
23
|
-
|
|
20
|
+
{ resourceOptions: collection }
|
|
24
21
|
end
|
|
25
22
|
|
|
26
23
|
class << self
|
|
@@ -19,11 +19,14 @@ module Terrazzo
|
|
|
19
19
|
end
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
def serializable_options
|
|
22
|
+
def serializable_options(page = nil)
|
|
23
23
|
opts = {}
|
|
24
|
-
if resource
|
|
24
|
+
if page == :form && resource
|
|
25
25
|
opts[:resourceOptions] = resource_options
|
|
26
26
|
end
|
|
27
|
+
if options.key?(:render_actions)
|
|
28
|
+
opts[:renderActions] = options[:render_actions]
|
|
29
|
+
end
|
|
27
30
|
opts
|
|
28
31
|
end
|
|
29
32
|
|
|
@@ -75,8 +78,8 @@ module Terrazzo
|
|
|
75
78
|
{ attribute: attr.to_s, label: attr.to_s.humanize }
|
|
76
79
|
end
|
|
77
80
|
|
|
78
|
-
|
|
79
|
-
|
|
81
|
+
rows = records.map do |record|
|
|
82
|
+
cells = col_attrs.map do |attr|
|
|
80
83
|
field = dashboard_class.new.attribute_type_for(attr).new(attr, nil, :index, resource: record)
|
|
81
84
|
{
|
|
82
85
|
attribute: attr.to_s,
|
|
@@ -84,12 +87,12 @@ module Terrazzo
|
|
|
84
87
|
value: field.serialize_value(:index)
|
|
85
88
|
}
|
|
86
89
|
end
|
|
87
|
-
{ id: record.id.to_s,
|
|
90
|
+
{ id: record.id.to_s, cells: cells }
|
|
88
91
|
end
|
|
89
92
|
|
|
90
93
|
{
|
|
91
94
|
headers: headers,
|
|
92
|
-
|
|
95
|
+
rows: rows,
|
|
93
96
|
total: total,
|
|
94
97
|
initialLimit: limit
|
|
95
98
|
}
|
|
@@ -115,7 +118,13 @@ module Terrazzo
|
|
|
115
118
|
return records unless sort_by
|
|
116
119
|
|
|
117
120
|
direction = options.fetch(:direction, :asc)
|
|
118
|
-
|
|
121
|
+
|
|
122
|
+
if records.respond_to?(:reorder)
|
|
123
|
+
records.reorder(sort_by => direction)
|
|
124
|
+
else
|
|
125
|
+
sorted = records.sort_by { |r| r.public_send(sort_by) }
|
|
126
|
+
direction.to_sym == :desc ? sorted.reverse : sorted
|
|
127
|
+
end
|
|
119
128
|
end
|
|
120
129
|
|
|
121
130
|
def find_associated_dashboard
|
|
@@ -11,12 +11,9 @@ module Terrazzo
|
|
|
11
11
|
end
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
def serializable_options
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
opts[:resourceOptions] = resource_options
|
|
18
|
-
end
|
|
19
|
-
opts
|
|
14
|
+
def serializable_options(page = nil)
|
|
15
|
+
return {} unless page == :form && resource
|
|
16
|
+
{ resourceOptions: resource_options }
|
|
20
17
|
end
|
|
21
18
|
|
|
22
19
|
class << self
|
data/lib/terrazzo/field/money.rb
CHANGED
|
@@ -12,16 +12,16 @@ module Terrazzo
|
|
|
12
12
|
end
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
def serializable_options
|
|
16
|
-
|
|
15
|
+
def serializable_options(page = nil)
|
|
16
|
+
return {} unless page == :form
|
|
17
17
|
classes = options[:classes] || []
|
|
18
18
|
order = options[:order]
|
|
19
|
-
|
|
19
|
+
grouped = classes.each_with_object({}) do |klass, hash|
|
|
20
20
|
klass = klass.constantize if klass.is_a?(::String)
|
|
21
21
|
scope = order ? klass.order(order) : klass.all
|
|
22
22
|
hash[klass.name] = scope.map { |r| [display_name(r), r.id.to_s] }
|
|
23
23
|
end
|
|
24
|
-
|
|
24
|
+
{ groupedOptions: grouped }
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
class << self
|
data/lib/terrazzo/version.rb
CHANGED