plutonium 0.8.0 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +6 -0
- data/app/assets/js/controllers/application.js +1 -0
- data/app/assets/js/controllers/index.js +9 -0
- data/app/assets/js/controllers/resource_dismiss_controller.js +37 -0
- data/app/assets/js/controllers/resource_drop_down_controller.js +29 -0
- data/app/assets/js/turbo/index.js +1 -1
- data/app/views/application/_flash.html.erb +1 -1
- data/app/views/application/_flash_alerts.html.erb +51 -7
- data/app/views/application/_flash_toasts.html.erb +53 -23
- data/app/views/application/_resource_header.html.erb +563 -561
- data/app/views/components/form/form_builder.rb +2 -2
- data/app/views/components/form/form_component.html.erb +5 -6
- data/app/views/components/interactive_action_form/interactive_action_form_component.html.erb +1 -1
- data/app/views/components/nested_resource_form_fields/nested_resource_form_fields_component.html.erb +66 -0
- data/app/views/components/nested_resource_form_fields/nested_resource_form_fields_component.rb +23 -0
- data/app/views/components/nested_resource_form_fields/nested_resource_form_fields_controller.js +64 -0
- data/app/views/components/sidebar/sidebar_component.html.erb +61 -63
- data/app/views/components/table_search_input/table_search_input_component.html.erb +1 -1
- data/app/views/layouts/resource.html copy.erb +0 -2
- data/app/views/layouts/resource.html.erb +1 -3
- data/app/views/layouts/rodauth.html.erb +0 -1
- data/app/views/resource/_interactive_resource_action_form.html.erb +1 -1
- data/exe/pug +6 -0
- data/lib/generators/pu/gen/component/component_generator.rb +1 -1
- data/lib/generators/pu/gen/pug/pug_generator.rb +1 -1
- data/lib/generators/pu/lib/plutonium_generators/cli.rb +42 -0
- data/lib/generators/pu/lib/plutonium_generators/generator.rb +1 -5
- data/lib/generators/pu/lib/plutonium_generators/model_generator.rb +1 -1
- data/lib/generators/pu/lib/plutonium_generators.rb +8 -0
- data/lib/plutonium/core/actions/collection.rb +1 -1
- data/lib/plutonium/core/controllers/authorizable.rb +4 -4
- data/lib/plutonium/core/fields/inputs/base.rb +1 -1
- data/lib/plutonium/core/fields/inputs/nested_input.rb +57 -0
- data/lib/plutonium/core/fields/inputs/noop_input.rb +1 -1
- data/lib/plutonium/core/fields/inputs/phone_input.rb +1 -1
- data/lib/plutonium/core/fields/inputs/polymorphic_belongs_to_association_input.rb +1 -1
- data/lib/plutonium/core/fields/inputs/simple_form_association_input.rb +1 -1
- data/lib/plutonium/core/fields/inputs/simple_form_input.rb +1 -1
- data/lib/plutonium/core/fields/renderers/factory.rb +1 -0
- data/lib/plutonium/core/fields/renderers/map_renderer.rb +19 -0
- data/lib/plutonium/resource/policy.rb +6 -0
- data/lib/plutonium/resource/presenter.rb +35 -0
- data/lib/plutonium/resource/record.rb +40 -7
- data/lib/plutonium/version.rb +1 -1
- data/package-lock.json +7 -0
- data/package.json +5 -4
- data/templates/base.rb +8 -0
- metadata +14 -4
- data/app/assets/js/controllers/dropdown_controller.js +0 -12
@@ -4,12 +4,12 @@ module Plutonium::Ui
|
|
4
4
|
class FormBuilder < SimpleForm::FormBuilder
|
5
5
|
def input(attribute_name, options = {}, &block)
|
6
6
|
label_class = options.dig(:label_html, :class)
|
7
|
-
if object
|
7
|
+
if object&.errors&.[](attribute_name).present?
|
8
8
|
# Don't show the hint
|
9
9
|
options.delete(:hint)
|
10
10
|
# Apply error class if there are errors
|
11
11
|
label_class = [label_class, "text-red-700 dark:text-red-500"].compact.join(" ")
|
12
|
-
elsif object
|
12
|
+
elsif object&.persisted? || !object&.errors&.empty?
|
13
13
|
# Apply success class if the object is persisted, has been validated (errors are not empty), and the field has no errors
|
14
14
|
label_class = [label_class, "block mb-2 text-sm font-medium text-green-700 dark:text-green-500"].compact.join(" ")
|
15
15
|
else
|
@@ -21,13 +21,13 @@
|
|
21
21
|
|
22
22
|
<div>
|
23
23
|
<% form.inputs.values.each do |input| %>
|
24
|
-
<%= input.render f, form.record %>
|
24
|
+
<%= input.render self, f, form.record %>
|
25
25
|
<% end %>
|
26
26
|
</div>
|
27
27
|
|
28
28
|
<div class="flex justify-end space-x-2">
|
29
29
|
<%# TODO: move this into its own component %>
|
30
|
-
<div class="flex">
|
30
|
+
<div class="flex" data-controller="resource-drop-down">
|
31
31
|
<button type="submit"
|
32
32
|
name="commit"
|
33
33
|
value="<%= preferred_action_after_submit %>"
|
@@ -40,19 +40,18 @@
|
|
40
40
|
</button>
|
41
41
|
<button type="button"
|
42
42
|
id="form-submit-options-toggle"
|
43
|
-
data-
|
43
|
+
data-resource-drop-down-target="trigger"
|
44
44
|
class="inline-flex items-center px-4 py-2 bg-primary-600 text-white
|
45
45
|
rounded-e-md hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-primary-500
|
46
46
|
dark:border-gray-300
|
47
|
-
border-s"
|
48
|
-
>
|
47
|
+
border-s">
|
49
48
|
<svg class="w-2.5 h-2.5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 10 6">
|
50
49
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
|
51
50
|
</svg>
|
52
51
|
</button>
|
53
52
|
|
54
53
|
<!-- Dropdown menu -->
|
55
|
-
<div
|
54
|
+
<div data-resource-drop-down-target="menu"
|
56
55
|
class="z-10 hidden w-56 bg-white divide-y divide-gray-100 rounded-lg shadow dark:bg-gray-700 dark:divide-gray-600">
|
57
56
|
<ul class="p-3 space-y-3 text-sm text-gray-700 dark:text-gray-200"
|
58
57
|
aria-labelledby="form-submit-options-toggle">
|
data/app/views/components/nested_resource_form_fields/nested_resource_form_fields_component.html.erb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
<div data-controller="nested-resource-form-fields"
|
2
|
+
data-nested-resource-form-fields-limit-value="<%= limit %>"
|
3
|
+
<%= render_component_attributes %>>
|
4
|
+
<h2 class="text-lg font-bold dark:text-white ">
|
5
|
+
<%= label %>
|
6
|
+
</h2>
|
7
|
+
<p class="mb-4 text-md font-normal text-gray-500 dark:text-gray-400">
|
8
|
+
<%= description %>
|
9
|
+
</p>
|
10
|
+
|
11
|
+
<template data-nested-resource-form-fields-target="template">
|
12
|
+
<% new_record = resource_class.new %>
|
13
|
+
<%= form.simple_fields_for name, new_record, child_index: 'NEW_RECORD' do |nested| %>
|
14
|
+
<fieldset class="border-t mt-4 pt-4 first:border-t-0 first:pt-0 nested-resource-form-fields" data-new-record="<%= nested.object.new_record? %>">
|
15
|
+
<% inputs.values.each do |input| %>
|
16
|
+
<%= input.render self, nested, new_record %>
|
17
|
+
<% end %>
|
18
|
+
|
19
|
+
<div class="text-right">
|
20
|
+
<% if nested.object.new_record? || allow_destroy %>
|
21
|
+
<label class="text-md font-medium text-red-900">
|
22
|
+
<%= "Delete" %>
|
23
|
+
<input type="checkbox" value=""
|
24
|
+
data-action="nested-resource-form-fields#remove"
|
25
|
+
class="w-4 h-4 ms-2 text-red-600 bg-red-100 border-red-300 rounded focus:ring-red-500 dark:focus:ring-red-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600">
|
26
|
+
</label>
|
27
|
+
<% end %>
|
28
|
+
</div>
|
29
|
+
|
30
|
+
<%= nested.hidden_field :_destroy %>
|
31
|
+
</fieldset>
|
32
|
+
<% end %>
|
33
|
+
</template>
|
34
|
+
|
35
|
+
<div>
|
36
|
+
<%= form.simple_fields_for name do |nested| %>
|
37
|
+
<fieldset class="border-t mt-4 pt-4 first:border-t-0 first:pt-0 nested-resource-form-fields" data-new-record="<%= nested.object.new_record? %>">
|
38
|
+
<% inputs.values.each do |input| %>
|
39
|
+
<%= input.render self, nested, new_record %>
|
40
|
+
<% end %>
|
41
|
+
|
42
|
+
<div>
|
43
|
+
<% if nested.object.new_record? || allow_destroy %>
|
44
|
+
<label class="flex items-center justify-end text-md font-medium text-red-900">
|
45
|
+
<%= "Delete" %>
|
46
|
+
<input type="checkbox" value=""
|
47
|
+
data-action="nested-resource-form-fields#remove"
|
48
|
+
class="w-4 h-4 ms-2 text-red-600 bg-red-100 border-red-300 rounded focus:ring-red-500 dark:focus:ring-red-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600">
|
49
|
+
</label>
|
50
|
+
<% end %>
|
51
|
+
</div>
|
52
|
+
|
53
|
+
<%= nested.hidden_field :_destroy %>
|
54
|
+
</fieldset>
|
55
|
+
<% end %>
|
56
|
+
|
57
|
+
<div data-nested-resource-form-fields-target="target" hidden></div>
|
58
|
+
</div>
|
59
|
+
|
60
|
+
<%=
|
61
|
+
render_component :button, label: "Add #{name.to_s.singularize.humanize}", classname: "mt-4", data: {
|
62
|
+
action: "nested-resource-form-fields#add",
|
63
|
+
"nested-resource-form-fields-target": "addButton"
|
64
|
+
}
|
65
|
+
%>
|
66
|
+
</div>
|
data/app/views/components/nested_resource_form_fields/nested_resource_form_fields_component.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
module Plutonium::Ui
|
2
|
+
class NestedResourceFormFieldsComponent < Plutonium::Ui::Base
|
3
|
+
option :name
|
4
|
+
option :resource_class
|
5
|
+
option :form
|
6
|
+
option :inputs
|
7
|
+
option :label, optional: true
|
8
|
+
option :description, optional: true
|
9
|
+
option :allow_destroy, optional: true
|
10
|
+
option :update_only, optional: true
|
11
|
+
option :limit, optional: true
|
12
|
+
|
13
|
+
def base_classname
|
14
|
+
"mt-6 mb-4"
|
15
|
+
end
|
16
|
+
|
17
|
+
def label
|
18
|
+
super || name.to_s.humanize
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
Plutonium::ComponentRegistry.register :nested_resource_form_fields, to: Plutonium::Ui::NestedResourceFormFieldsComponent
|
data/app/views/components/nested_resource_form_fields/nested_resource_form_fields_controller.js
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
|
3
|
+
// Connects to data-controller="nested-resource-form-fields"
|
4
|
+
// Copied from https://github.com/stimulus-components/stimulus-rails-nested-form/blob/master/src/index.ts
|
5
|
+
export default class extends Controller {
|
6
|
+
static targets = ["target", "template", "addButton"]
|
7
|
+
|
8
|
+
static values = {
|
9
|
+
wrapperSelector: {
|
10
|
+
type: String,
|
11
|
+
default: ".nested-resource-form-fields",
|
12
|
+
},
|
13
|
+
limit: Number,
|
14
|
+
}
|
15
|
+
|
16
|
+
connect() {
|
17
|
+
this.updateState()
|
18
|
+
}
|
19
|
+
|
20
|
+
add(e) {
|
21
|
+
e.preventDefault()
|
22
|
+
|
23
|
+
const content = this.templateTarget.innerHTML.replace(/NEW_RECORD/g, new Date().getTime().toString())
|
24
|
+
this.targetTarget.insertAdjacentHTML("beforebegin", content)
|
25
|
+
|
26
|
+
const event = new CustomEvent("nested-resource-form-fields:add", { bubbles: true })
|
27
|
+
this.element.dispatchEvent(event)
|
28
|
+
|
29
|
+
this.updateState()
|
30
|
+
}
|
31
|
+
|
32
|
+
remove(e) {
|
33
|
+
e.preventDefault()
|
34
|
+
|
35
|
+
const wrapper = e.target.closest(this.wrapperSelectorValue)
|
36
|
+
|
37
|
+
if (wrapper.dataset.newRecord === "true") {
|
38
|
+
wrapper.remove()
|
39
|
+
} else {
|
40
|
+
wrapper.style.display = "none"
|
41
|
+
|
42
|
+
const input = wrapper.querySelector("input[name*='_destroy']")
|
43
|
+
input.value = "1"
|
44
|
+
}
|
45
|
+
|
46
|
+
const event = new CustomEvent("nested-resource-form-fields:remove", { bubbles: true })
|
47
|
+
this.element.dispatchEvent(event)
|
48
|
+
|
49
|
+
this.updateState()
|
50
|
+
}
|
51
|
+
|
52
|
+
updateState() {
|
53
|
+
if (!this.hasAddButtonTarget || this.limitValue == 0) return
|
54
|
+
|
55
|
+
if (this.childCount >= this.limitValue)
|
56
|
+
this.addButtonTarget.style.display = "none"
|
57
|
+
else
|
58
|
+
this.addButtonTarget.style.display = "initial"
|
59
|
+
}
|
60
|
+
|
61
|
+
get childCount() {
|
62
|
+
return this.element.querySelectorAll(this.wrapperSelectorValue).length
|
63
|
+
}
|
64
|
+
}
|
@@ -86,69 +86,67 @@
|
|
86
86
|
updateColorMode()
|
87
87
|
</script>
|
88
88
|
|
89
|
-
|
90
|
-
<
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
</li>
|
151
|
-
</ul>
|
89
|
+
<!-- Color Modes -->
|
90
|
+
<div data-controller="resource-drop-down">
|
91
|
+
<button
|
92
|
+
type="button"
|
93
|
+
data-resource-drop-down-target="trigger"
|
94
|
+
class="inline-flex justify-center p-2 text-gray-500 rounded cursor-pointer dark:hover:text-white dark:text-gray-200 hover:text-gray-900 hover:bg-gray-100 dark:hover:bg-gray-600">
|
95
|
+
<svg class="w-6 h-6 text-gray-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 24 24">
|
96
|
+
<path d="M5 13.17a3.001 3.001 0 0 0 0 5.66V20a1 1 0 1 0 2 0v-1.17a3.001 3.001 0 0 0 0-5.66V4a1 1 0 0 0-2 0v9.17ZM11 20v-9.17a3.001 3.001 0 0 1 0-5.66V4a1 1 0 1 1 2 0v1.17a3.001 3.001 0 0 1 0 5.66V20a1 1 0 1 1-2 0Zm6-1.17V20a1 1 0 1 0 2 0v-1.17a3.001 3.001 0 0 0 0-5.66V4a1 1 0 1 0-2 0v9.17a3.001 3.001 0 0 0 0 5.66Z"/>
|
97
|
+
</svg>
|
98
|
+
</button>
|
99
|
+
<div
|
100
|
+
class="hidden z-50 my-4 text-base list-none bg-white rounded divide-y divide-gray-100 shadow dark:bg-gray-700"
|
101
|
+
data-resource-drop-down-target="menu">
|
102
|
+
<ul class="py-1" role="none">
|
103
|
+
<li>
|
104
|
+
<button
|
105
|
+
type="button"
|
106
|
+
class="w-full block py-2 px-4 text-sm text-gray-700 hover:bg-gray-100 dark:hover:text-white dark:text-gray-300 dark:hover:bg-gray-600"
|
107
|
+
role="menuitem"
|
108
|
+
onclick="setLightColorMode()"
|
109
|
+
>
|
110
|
+
<div class="flex justify-start">
|
111
|
+
<svg class="w-6 h-6 me-2 text-gray-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
|
112
|
+
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 5V3m0 18v-2M7.05 7.05 5.636 5.636m12.728 12.728L16.95 16.95M5 12H3m18 0h-2M7.05 16.95l-1.414 1.414M18.364 5.636 16.95 7.05M16 12a4 4 0 1 1-8 0 4 4 0 0 1 8 0Z"/>
|
113
|
+
</svg>
|
114
|
+
Light
|
115
|
+
</div>
|
116
|
+
</a>
|
117
|
+
</li>
|
118
|
+
<li>
|
119
|
+
<button
|
120
|
+
type="button"
|
121
|
+
class="w-full block py-2 px-4 text-sm text-gray-700 hover:bg-gray-100 dark:hover:text-white dark:text-gray-300 dark:hover:bg-gray-600"
|
122
|
+
role="menuitem"
|
123
|
+
onclick="setDarkColorMode()"
|
124
|
+
>
|
125
|
+
<div class="flex justify-start">
|
126
|
+
<svg class="w-6 h-6 me-2 text-gray-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
|
127
|
+
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 21a9 9 0 0 1-.5-17.986V3c-.354.966-.5 1.911-.5 3a9 9 0 0 0 9 9c.239 0 .254.018.488 0A9.004 9.004 0 0 1 12 21Z"/>
|
128
|
+
</svg>
|
129
|
+
Dark
|
130
|
+
</div>
|
131
|
+
</a>
|
132
|
+
</li>
|
133
|
+
<li>
|
134
|
+
<button
|
135
|
+
type="button"
|
136
|
+
class="w-full block py-2 px-4 text-sm text-gray-700 hover:bg-gray-100 dark:hover:text-white dark:text-gray-300 dark:hover:bg-gray-600"
|
137
|
+
role="menuitem"
|
138
|
+
onclick="setSystemColorMode()"
|
139
|
+
>
|
140
|
+
<div class="flex justify-start">
|
141
|
+
<svg class="w-6 h-6 me-2 text-gray-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
|
142
|
+
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 16H5a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v1M9 12H4m8 8V9h8v11h-8Zm0 0H9m8-4a1 1 0 1 0-2 0 1 1 0 0 0 2 0Z"/>
|
143
|
+
</svg>
|
144
|
+
System
|
145
|
+
</div>
|
146
|
+
</a>
|
147
|
+
</li>
|
148
|
+
</ul>
|
149
|
+
</div>
|
152
150
|
</div>
|
153
151
|
</div>
|
154
152
|
</aside>
|
@@ -7,7 +7,7 @@
|
|
7
7
|
<path fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z" clip-rule="evenodd" />
|
8
8
|
</svg>
|
9
9
|
</div>
|
10
|
-
<%= search_object.search_filter.input_definitions[:search].render f, nil,
|
10
|
+
<%= search_object.search_filter.input_definitions[:search].render self, f, nil,
|
11
11
|
wrapper: false, label: false,
|
12
12
|
as: :string, # force string for search
|
13
13
|
placeholder: "search...",
|
@@ -15,8 +15,6 @@
|
|
15
15
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/slim-select/2.6.0/slimselect.min.css" integrity="sha512-GvqWM4KWH8mbgWIyvwdH8HgjUbyZTXrCq0sjGij9fDNiXz3vJoy3jCcAaWNekH2rJe4hXVWCJKN+bEW8V7AAEQ==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
16
16
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/slim-select/2.6.0/slimselect.min.js" integrity="sha512-0E8oaoA2v32h26IycsmRDShtQ8kMgD91zWVBxdIvUCjU3xBw81PV61QBsBqNQpWkp/zYJZip8Ag3ifmzz1wCKQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
17
17
|
|
18
|
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/flowbite/2.3.0/flowbite.min.js"></script>
|
19
|
-
|
20
18
|
<%== Plutonium::Config.stylesheet_tag.call self %>
|
21
19
|
<%== Plutonium::Config.script_tag.call self %>
|
22
20
|
<%= yield(:head) %>
|
@@ -21,8 +21,6 @@
|
|
21
21
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/slim-select/2.6.0/slimselect.min.css" integrity="sha512-GvqWM4KWH8mbgWIyvwdH8HgjUbyZTXrCq0sjGij9fDNiXz3vJoy3jCcAaWNekH2rJe4hXVWCJKN+bEW8V7AAEQ==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
22
22
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/slim-select/2.6.0/slimselect.min.js" integrity="sha512-0E8oaoA2v32h26IycsmRDShtQ8kMgD91zWVBxdIvUCjU3xBw81PV61QBsBqNQpWkp/zYJZip8Ag3ifmzz1wCKQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
23
23
|
|
24
|
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/flowbite/2.3.0/flowbite.min.js"></script>
|
25
|
-
|
26
24
|
<%= yield(:assets) %>
|
27
25
|
|
28
26
|
<%== Plutonium::Config.stylesheet_tag.call self %>
|
@@ -34,6 +32,7 @@
|
|
34
32
|
<%= render("resource_header") %>
|
35
33
|
<%= render("resource_sidebar") %>
|
36
34
|
<main class="p-4 lg:ml-64 h-auto pt-20">
|
35
|
+
<%= render "flash" %>
|
37
36
|
<%= yield %>
|
38
37
|
</main>
|
39
38
|
</body>
|
@@ -45,6 +44,5 @@
|
|
45
44
|
<%= modal_frame_tag do %>
|
46
45
|
<%= yield(:modal) %>
|
47
46
|
<% end %>
|
48
|
-
<%= render "flash" %>
|
49
47
|
|
50
48
|
-->
|
@@ -12,7 +12,6 @@
|
|
12
12
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
13
13
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
14
14
|
<link href="https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,100;0,300;0,400;0,700;0,900;1,100;1,300;1,400;1,700;1,900&display=swap" rel="stylesheet">
|
15
|
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/flowbite/2.3.0/flowbite.min.js"></script>
|
16
15
|
<%== Plutonium::Config.stylesheet_tag.call self %>
|
17
16
|
<%== Plutonium::Config.script_tag.call self %>
|
18
17
|
</head>
|
data/exe/pug
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails/generators"
|
4
|
+
require File.expand_path("../plutonium_generators", __dir__)
|
5
|
+
|
6
|
+
module PlutoniumGenerators
|
7
|
+
class CLI < Thor
|
8
|
+
map "i" => :install
|
9
|
+
map "g" => :generate
|
10
|
+
map "ls" => :list
|
11
|
+
map %w[--version -v] => :__print_version
|
12
|
+
|
13
|
+
# desc "install", "Install Plutonium"
|
14
|
+
# def install
|
15
|
+
# Rails::Generators.invoke("pu:install")
|
16
|
+
# end
|
17
|
+
|
18
|
+
desc "generate, g GENERATOR [options]", "Run plutonium generator"
|
19
|
+
def generate(generator, *options)
|
20
|
+
Rails::Generators.invoke("pu:#{generator}", options)
|
21
|
+
end
|
22
|
+
|
23
|
+
desc "list, ls", "View list of available generators"
|
24
|
+
def list
|
25
|
+
generators = Rails::Generators.sorted_groups.to_h["pu"]
|
26
|
+
puts
|
27
|
+
generators.each { |gen| puts gen.sub(/^pu:/, "") }
|
28
|
+
puts
|
29
|
+
end
|
30
|
+
|
31
|
+
desc "--version, -v", "Print gem version"
|
32
|
+
def __print_version
|
33
|
+
puts "Plutonium generators #{PlutoniumGenerators::VERSION}"
|
34
|
+
end
|
35
|
+
|
36
|
+
class << self
|
37
|
+
def exit_on_failure?
|
38
|
+
true
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -64,12 +64,8 @@ module PlutoniumGenerators
|
|
64
64
|
@prompt ||= TTY::Prompt.new
|
65
65
|
end
|
66
66
|
|
67
|
-
def rails?
|
68
|
-
PlutoniumGenerators.rails?
|
69
|
-
end
|
70
|
-
|
71
67
|
def appname
|
72
|
-
|
68
|
+
defined?(Rails.application) ? Rails.application.class.module_parent.name : "PlutoniumGenerators"
|
73
69
|
end
|
74
70
|
|
75
71
|
def app_name
|
@@ -7,7 +7,7 @@ module PlutoniumGenerators
|
|
7
7
|
class ModelGenerator < ActiveRecord::Generators::ModelGenerator
|
8
8
|
include PlutoniumGenerators::Generator
|
9
9
|
|
10
|
-
remove_hook_for :test_framework
|
10
|
+
# remove_hook_for :test_framework
|
11
11
|
remove_task :create_migration_file
|
12
12
|
remove_task :create_model_file
|
13
13
|
remove_task :create_module_file
|
@@ -1,7 +1,15 @@
|
|
1
1
|
require "zeitwerk"
|
2
2
|
|
3
3
|
loader = Zeitwerk::Loader.for_gem # (warn_on_extra_files: false)
|
4
|
+
loader.inflector.inflect(
|
5
|
+
"cli" => "CLI"
|
6
|
+
)
|
4
7
|
loader.setup
|
5
8
|
|
6
9
|
module PlutoniumGenerators
|
10
|
+
class << self
|
11
|
+
def cli?
|
12
|
+
ENV["PU_CLI"] == "1"
|
13
|
+
end
|
14
|
+
end
|
7
15
|
end
|
@@ -9,7 +9,7 @@ module Plutonium
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def permitted_for(policy)
|
12
|
-
Collection.new(@collection.select { |name, action| policy.
|
12
|
+
Collection.new(@collection.select { |name, action| policy.send_with_report :"#{action.name}?" })
|
13
13
|
end
|
14
14
|
|
15
15
|
def collection_actions
|
@@ -42,7 +42,7 @@ module Plutonium
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def permitted_attributes
|
45
|
-
@permitted_attributes ||= current_policy.
|
45
|
+
@permitted_attributes ||= current_policy.send_with_report :"permitted_attributes_for_#{action_name}"
|
46
46
|
end
|
47
47
|
|
48
48
|
def current_policy
|
@@ -52,9 +52,9 @@ module Plutonium
|
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
55
|
-
def parent_policy
|
56
|
-
|
57
|
-
end
|
55
|
+
# def parent_policy
|
56
|
+
# @parent_policy ||= policy(current_parent) if current_parent.present?
|
57
|
+
# end
|
58
58
|
end
|
59
59
|
end
|
60
60
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Plutonium
|
2
|
+
module Core
|
3
|
+
module Fields
|
4
|
+
module Inputs
|
5
|
+
class NestedInput < Base
|
6
|
+
include Plutonium::Core::Definers::InputDefiner
|
7
|
+
|
8
|
+
attr_reader :inputs, :resource_class
|
9
|
+
|
10
|
+
def initialize(name, inputs:, resource_class:, allow_destroy:, update_only:, limit:, **user_options)
|
11
|
+
@inputs = inputs
|
12
|
+
@resource_class = resource_class
|
13
|
+
@allow_destroy = allow_destroy
|
14
|
+
@update_only = update_only
|
15
|
+
@limit = limit
|
16
|
+
|
17
|
+
super(name, **user_options)
|
18
|
+
end
|
19
|
+
|
20
|
+
def render(view_context, f, record, **opts)
|
21
|
+
opts = options.deep_merge opts
|
22
|
+
view_context.render_component :nested_resource_form_fields, form: f, **opts
|
23
|
+
end
|
24
|
+
|
25
|
+
def collect(params)
|
26
|
+
attributes = {}
|
27
|
+
params[param].each do |index, nested_params|
|
28
|
+
collected = defined_inputs.collect_all(nested_params)
|
29
|
+
collected[:id] = nested_params[:id] if nested_params.key?(:id) && !@update_only
|
30
|
+
collected[:_destroy] = nested_params[:_destroy] if @allow_destroy
|
31
|
+
attributes[index] = collected
|
32
|
+
end
|
33
|
+
|
34
|
+
{param => attributes}
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def param = :"#{name}_attributes"
|
40
|
+
|
41
|
+
def input_options = {
|
42
|
+
name:,
|
43
|
+
resource_class:,
|
44
|
+
allow_destroy: @allow_destroy,
|
45
|
+
update_only: @update_only,
|
46
|
+
limit: @limit,
|
47
|
+
inputs: defined_inputs
|
48
|
+
}
|
49
|
+
|
50
|
+
def defined_inputs
|
51
|
+
@defined_inputs ||= defined_inputs_for(*inputs)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|