b4um_generators 0.2.17 → 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.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +30 -0
  3. data/README.de.md +418 -4
  4. data/README.md +405 -5
  5. data/lib/b4um_generators/version.rb +1 -1
  6. data/lib/generators/b4um/admin/admin_generator.rb +614 -0
  7. data/lib/generators/b4um/admin/templates/add_admin_fields_migration.rb.tt +10 -0
  8. data/lib/generators/b4um/admin/templates/admin_base_controller.rb.tt +5 -0
  9. data/lib/generators/b4um/admin/templates/admin_dashboard.html.erb.tt +19 -0
  10. data/lib/generators/b4um/admin/templates/admin_dashboard_controller.rb.tt +6 -0
  11. data/lib/generators/b4um/admin/templates/admin_users_controller.rb.tt +70 -0
  12. data/lib/generators/b4um/admin/templates/admin_users_index.html.erb.tt +110 -0
  13. data/lib/generators/b4um/admin/templates/b4um_admin.rake.tt +106 -0
  14. data/lib/generators/b4um/authentication/authentication_generator.rb +90 -1
  15. data/lib/generators/b4um/guest/guest_generator.rb +673 -0
  16. data/lib/generators/b4um/guest/templates/add_guest_fields_migration.rb.tt +12 -0
  17. data/lib/generators/b4um/guest/templates/b4um_guests.rake.tt +12 -0
  18. data/lib/generators/b4um/guest/templates/edit.html.erb.tt +156 -0
  19. data/lib/generators/b4um/guest/templates/guest_expiration_warning.html.erb.tt +12 -0
  20. data/lib/generators/b4um/guest/templates/guests_controller.rb.tt +88 -0
  21. data/lib/generators/b4um/guest/templates/new.html.erb.tt +136 -0
  22. data/lib/generators/b4um/help/help_generator.rb +228 -0
  23. data/lib/generators/b4um/install/templates/b4um/sortable.css +158 -0
  24. data/lib/generators/b4um/install/templates/b4um.css +1 -0
  25. data/lib/generators/b4um/pagination/pagination_generator.rb +32 -5
  26. data/lib/generators/b4um/scaffold/scaffold_generator.rb +50 -0
  27. data/lib/generators/b4um/scaffold/templates/_form.html.erb.tt +18 -14
  28. data/lib/generators/b4um/search/search_generator.rb +22 -8
  29. data/lib/generators/b4um/sortable/sortable_generator.rb +529 -0
  30. data/lib/generators/b4um/sortable/templates/_sortable.html.erb.tt +40 -0
  31. data/lib/generators/b4um/sortable/templates/add_position_migration.rb.tt +26 -0
  32. data/lib/generators/b4um/sortable/templates/sort.html.erb.tt +3 -0
  33. data/lib/generators/b4um/sortable/templates/sortable_controller.js +166 -0
  34. metadata +22 -1
@@ -0,0 +1,158 @@
1
+ /* -------------------------------------------------------
2
+ Sortable
3
+ ------------------------------------------------------- */
4
+
5
+ .b4um-sortable {
6
+ display: grid;
7
+ gap: 0.75rem;
8
+ }
9
+
10
+ .b4um-sortable__item {
11
+ display: grid;
12
+ grid-template-columns: auto minmax(0, 1fr) auto;
13
+ align-items: center;
14
+ gap: 1.25rem;
15
+
16
+ padding: 1rem 1.25rem;
17
+
18
+ background: var(--b4um-surface);
19
+ border: 1px solid var(--b4um-border-light);
20
+ border-radius: var(--b4um-radius);
21
+
22
+ transition:
23
+ border-color 0.2s ease,
24
+ box-shadow 0.2s ease,
25
+ opacity 0.2s ease;
26
+ }
27
+
28
+ .b4um-sortable__item:hover {
29
+ border-color: var(--b4um-border-hover);
30
+ }
31
+
32
+ .b4um-sortable__item--dragging {
33
+ opacity: 0.5;
34
+ box-shadow: 0 8px 24px var(--b4um-primary-shadow-soft);
35
+ }
36
+
37
+ /* -------------------------------------------------------
38
+ Controls
39
+ ------------------------------------------------------- */
40
+
41
+ .b4um-sortable__controls {
42
+ display: flex;
43
+ align-items: center;
44
+ gap: 0.5rem;
45
+ }
46
+
47
+ .b4um-sortable__position {
48
+ box-sizing: border-box;
49
+
50
+ width: 4rem;
51
+ min-width: 4rem;
52
+ height: 2.75rem;
53
+
54
+ margin: 0;
55
+ padding: 0.5rem;
56
+
57
+ background: var(--b4um-surface);
58
+ color: var(--b4um-text);
59
+
60
+ border: 1px solid var(--b4um-border);
61
+ border-radius: 10px;
62
+
63
+ font: inherit;
64
+ text-align: center;
65
+ }
66
+
67
+ .b4um-sortable__position:hover {
68
+ border-color: var(--b4um-border-hover);
69
+ }
70
+
71
+ .b4um-sortable__position:focus {
72
+ border-color: var(--b4um-primary);
73
+ outline: 3px solid var(--b4um-primary-focus);
74
+ outline-offset: 1px;
75
+ }
76
+
77
+ /* -------------------------------------------------------
78
+ Drag handle
79
+ ------------------------------------------------------- */
80
+
81
+ .b4um-sortable__handle {
82
+ display: inline-flex;
83
+ align-items: center;
84
+ justify-content: center;
85
+
86
+ width: 2.75rem;
87
+ height: 2.75rem;
88
+
89
+ margin: 0;
90
+ padding: 0;
91
+
92
+ background: transparent;
93
+ color: var(--b4um-text-secondary);
94
+
95
+ border: 0;
96
+ border-radius: 10px;
97
+
98
+ font: inherit;
99
+ font-size: 1.2rem;
100
+ line-height: 1;
101
+
102
+ cursor: grab;
103
+
104
+ transition:
105
+ background 0.2s ease,
106
+ color 0.2s ease;
107
+ }
108
+
109
+ .b4um-sortable__handle:hover {
110
+ background: var(--b4um-primary-soft);
111
+ color: var(--b4um-primary);
112
+ }
113
+
114
+ .b4um-sortable__handle:active {
115
+ cursor: grabbing;
116
+ }
117
+
118
+ /* -------------------------------------------------------
119
+ Content
120
+ ------------------------------------------------------- */
121
+
122
+ .b4um-sortable__content {
123
+ min-width: 0;
124
+ }
125
+
126
+ .b4um-sortable__content .resource {
127
+ margin: 0;
128
+ }
129
+
130
+ /* -------------------------------------------------------
131
+ Actions
132
+ ------------------------------------------------------- */
133
+
134
+ .b4um-sortable__actions {
135
+ display: flex;
136
+ align-items: center;
137
+ justify-content: flex-end;
138
+ }
139
+
140
+ .b4um-sortable__actions .button {
141
+ margin: 0;
142
+ white-space: nowrap;
143
+ }
144
+
145
+ /* -------------------------------------------------------
146
+ Responsive
147
+ ------------------------------------------------------- */
148
+
149
+ @media (max-width: 48rem) {
150
+ .b4um-sortable__item {
151
+ grid-template-columns: 1fr;
152
+ gap: 0.75rem;
153
+ }
154
+
155
+ .b4um-sortable__actions {
156
+ justify-content: flex-start;
157
+ }
158
+ }
@@ -22,6 +22,7 @@
22
22
  @import 'b4um/hero.css';
23
23
  @import 'b4um/cards.css';
24
24
  @import 'b4um/tables.css';
25
+ @import 'b4um/sortable.css';
25
26
  @import 'b4um/pagination.css';
26
27
  @import 'b4um/badges.css';
27
28
  @import 'b4um/callouts.css';
@@ -108,20 +108,33 @@ module B4um
108
108
  /
109
109
  @#{Regexp.escape(plural_table_name)}\s*=\s*
110
110
  b4um_search\(\s*
111
- #{Regexp.escape(class_name)}\.all,\s*
111
+ (?<collection>
112
+ #{Regexp.escape(class_name)}
113
+ \.(?:all|order\(\s*:position\s*,\s*:id\s*\))
114
+ )\s*,\s*
112
115
  params\[:q\]\s*
113
116
  \)
114
117
  /x
115
118
  end
116
119
 
117
120
  def paginate_existing_search
121
+ controller_content = File.read(controller_path)
122
+ match = controller_content.match(search_pattern)
123
+
124
+ unless match
125
+ raise Thor::Error,
126
+ "Could not find the search collection in app/controllers/#{plural_table_name}_controller.rb"
127
+ end
128
+
129
+ collection = match[:collection]
130
+
118
131
  gsub_file(
119
132
  controller_path,
120
133
  search_pattern,
121
134
  [
122
135
  "@#{plural_table_name}, @pagination = b4um_paginate(",
123
136
  " b4um_search(",
124
- " #{class_name}.all,",
137
+ " #{collection},",
125
138
  " params[:q]",
126
139
  " ),",
127
140
  " per_page: #{options[:per_page]}",
@@ -131,19 +144,33 @@ module B4um
131
144
  end
132
145
 
133
146
  def paginate_plain_collection(controller_content)
134
- old_index = "@#{plural_table_name} = #{class_name}.all"
147
+ plain_collection = "#{class_name}.all"
148
+ sortable_collection = "#{class_name}.order(:position, :id)"
135
149
 
136
- unless controller_content.include?(old_index)
150
+ collection =
151
+ if controller_content.include?(
152
+ "@#{plural_table_name} = #{sortable_collection}"
153
+ )
154
+ sortable_collection
155
+ elsif controller_content.include?(
156
+ "@#{plural_table_name} = #{plain_collection}"
157
+ )
158
+ plain_collection
159
+ end
160
+
161
+ unless collection
137
162
  raise Thor::Error,
138
163
  "Could not find the index collection in app/controllers/#{plural_table_name}_controller.rb"
139
164
  end
140
165
 
166
+ old_index = "@#{plural_table_name} = #{collection}"
167
+
141
168
  gsub_file(
142
169
  controller_path,
143
170
  old_index,
144
171
  [
145
172
  "@#{plural_table_name}, @pagination = b4um_paginate(",
146
- " #{class_name}.all,",
173
+ " #{collection},",
147
174
  " per_page: #{options[:per_page]}",
148
175
  " )"
149
176
  ].join("\n")
@@ -33,6 +33,39 @@ module B4um
33
33
  type: :string,
34
34
  desc: "Renders a field as radio buttons. Example: condition:new=Neu,used=Gebraucht"
35
35
 
36
+ class_option :required,
37
+ type: :string,
38
+ desc: "Adds presence validation to fields. Example: name,email"
39
+
40
+ def initialize(args, options, config)
41
+ super
42
+
43
+ validate_required_fields
44
+ end
45
+
46
+ no_tasks do
47
+ def validate_required_fields
48
+ return if required_fields.empty?
49
+
50
+ attribute_names = attributes.map(&:name)
51
+
52
+ unknown_fields = required_fields - attribute_names
53
+
54
+ return if unknown_fields.empty?
55
+
56
+ raise Thor::Error,
57
+ "Unknown required field: #{unknown_fields.first}"
58
+ end
59
+
60
+ def required_fields
61
+ options[:required]
62
+ .to_s
63
+ .split(",")
64
+ .map(&:strip)
65
+ .reject(&:empty?)
66
+ end
67
+ end
68
+
36
69
  def validate_editor_options
37
70
  configurations = {
38
71
  select: select_configuration,
@@ -147,6 +180,23 @@ module B4um
147
180
  )
148
181
  end
149
182
 
183
+ def add_required_validations
184
+ return if required_fields.empty?
185
+
186
+ validation = required_fields
187
+ .map { |field| ":#{field}" }
188
+ .join(", ")
189
+
190
+ inject_into_class(
191
+ File.join(
192
+ "app/models",
193
+ "#{singular_table_name}.rb"
194
+ ),
195
+ class_name,
196
+ " validates #{validation}, presence: true\n"
197
+ )
198
+ end
199
+
150
200
  def add_secure_password
151
201
  return unless attributes.any? { |attribute| attribute.name == "password_digest" }
152
202
 
@@ -15,6 +15,8 @@
15
15
  <%% end %>
16
16
 
17
17
  <% attributes.each do |attribute| -%>
18
+ <% required = required_fields.include?(attribute.name) -%>
19
+ <% label = attribute.name.humanize + (required ? " *" : "") -%>
18
20
  <% if attribute.name == "password_digest" -%>
19
21
  <div class="form-field">
20
22
  <%%= form.password_field :password,
@@ -35,7 +37,7 @@
35
37
  <%%= form.email_field :<%= attribute.name %>,
36
38
  class: "form-input",
37
39
  placeholder: " " %>
38
- <%%= form.label :<%= attribute.name %>, class: "form-label" %>
40
+ <%%= form.label :<%= attribute.name %>, "<%= label %>", class: "form-label" %>
39
41
  </div>
40
42
 
41
43
  <% elsif attribute.name.match?(/(?:^|_)(?:password|password_confirmation)(?:_|$)/) -%>
@@ -43,7 +45,7 @@
43
45
  <%%= form.password_field :<%= attribute.name %>,
44
46
  class: "form-input",
45
47
  placeholder: " " %>
46
- <%%= form.label :<%= attribute.name %>, class: "form-label" %>
48
+ <%%= form.label :<%= attribute.name %>, "<%= label %>", class: "form-label" %>
47
49
  </div>
48
50
 
49
51
  <% elsif attribute.name.match?(/(?:^|_)(?:phone|telephone|mobile)(?:_|$)/) -%>
@@ -51,7 +53,7 @@
51
53
  <%%= form.telephone_field :<%= attribute.name %>,
52
54
  class: "form-input",
53
55
  placeholder: " " %>
54
- <%%= form.label :<%= attribute.name %>, class: "form-label" %>
56
+ <%%= form.label :<%= attribute.name %>, "<%= label %>", class: "form-label" %>
55
57
  </div>
56
58
 
57
59
  <% elsif attribute.name.match?(/(?:^|_)(?:url|website|homepage)(?:_|$)/) -%>
@@ -59,12 +61,12 @@
59
61
  <%%= form.url_field :<%= attribute.name %>,
60
62
  class: "form-input",
61
63
  placeholder: " " %>
62
- <%%= form.label :<%= attribute.name %>, class: "form-label" %>
64
+ <%%= form.label :<%= attribute.name %>, "<%= label %>", class: "form-label" %>
63
65
  </div>
64
66
 
65
67
  <% elsif select_field?(attribute.name) -%>
66
68
  <div class="form-field form-field--select">
67
- <%%= form.label :<%= attribute.name %>, class: "form-label" %>
69
+ <%%= form.label :<%= attribute.name %>, "<%= label %>", class: "form-label" %>
68
70
 
69
71
  <%%= form.select :<%= attribute.name %>,
70
72
  <%= select_configuration[:values].inspect %>.map { |choice|
@@ -76,7 +78,7 @@
76
78
 
77
79
  <% elsif radio_field?(attribute.name) -%>
78
80
  <div class="form-field form-field--radio">
79
- <%%= form.label :<%= attribute.name %>, class: "form-label" %>
81
+ <%%= form.label :<%= attribute.name %>, "<%= label %>", class: "form-label" %>
80
82
 
81
83
  <div class="form-radio-options">
82
84
  <%% <%= radio_configuration[:values].inspect %>.each do |choice| %>
@@ -94,7 +96,7 @@
94
96
  <% elsif attribute.type == :boolean -%>
95
97
  <div class="form-field form-field--checkbox">
96
98
  <%%= form.check_box :<%= attribute.name %>, class: "form-checkbox" %>
97
- <%%= form.label :<%= attribute.name %>, class: "form-label form-label--checkbox" %>
99
+ <%%= form.label :<%= attribute.name %>, "<%= label %>", class: "form-label form-label--checkbox" %>
98
100
  </div>
99
101
 
100
102
  <% elsif attribute.type == :text -%>
@@ -102,7 +104,7 @@
102
104
  <%%= form.text_area :<%= attribute.name %>,
103
105
  class: "form-textarea",
104
106
  placeholder: " " %>
105
- <%%= form.label :<%= attribute.name %>, class: "form-label" %>
107
+ <%%= form.label :<%= attribute.name %>, "<%= label %>", class: "form-label" %>
106
108
  </div>
107
109
 
108
110
  <% elsif attribute.type == :integer -%>
@@ -111,7 +113,7 @@
111
113
  class: "form-input",
112
114
  placeholder: " ",
113
115
  step: 1 %>
114
- <%%= form.label :<%= attribute.name %>, class: "form-label" %>
116
+ <%%= form.label :<%= attribute.name %>, "<%= label %>", class: "form-label" %>
115
117
  </div>
116
118
 
117
119
  <% elsif [:float, :decimal].include?(attribute.type) -%>
@@ -120,7 +122,7 @@
120
122
  class: "form-input",
121
123
  placeholder: " ",
122
124
  step: "any" %>
123
- <%%= form.label :<%= attribute.name %>, class: "form-label" %>
125
+ <%%= form.label :<%= attribute.name %>, "<%= label %>", class: "form-label" %>
124
126
  </div>
125
127
 
126
128
  <% elsif attribute.type == :date -%>
@@ -128,7 +130,7 @@
128
130
  <%%= form.date_field :<%= attribute.name %>,
129
131
  class: "form-input",
130
132
  placeholder: " " %>
131
- <%%= form.label :<%= attribute.name %>, class: "form-label" %>
133
+ <%%= form.label :<%= attribute.name %>, "<%= label %>", class: "form-label" %>
132
134
  </div>
133
135
 
134
136
  <% elsif [:datetime, :timestamp].include?(attribute.type) -%>
@@ -136,7 +138,7 @@
136
138
  <%%= form.datetime_local_field :<%= attribute.name %>,
137
139
  class: "form-input",
138
140
  placeholder: " " %>
139
- <%%= form.label :<%= attribute.name %>, class: "form-label" %>
141
+ <%%= form.label :<%= attribute.name %>, "<%= label %>", class: "form-label" %>
140
142
  </div>
141
143
 
142
144
  <% elsif attribute.type == :time -%>
@@ -144,7 +146,7 @@
144
146
  <%%= form.time_field :<%= attribute.name %>,
145
147
  class: "form-input",
146
148
  placeholder: " " %>
147
- <%%= form.label :<%= attribute.name %>, class: "form-label" %>
149
+ <%%= form.label :<%= attribute.name %>, "<%= label %>", class: "form-label" %>
148
150
  </div>
149
151
 
150
152
  <% elsif attribute.type == :attachment -%>
@@ -153,6 +155,7 @@
153
155
 
154
156
  <div class="form-file-control">
155
157
  <%%= form.label :<%= attribute.name %>,
158
+ "<%= label %>",
156
159
  class: "form-file-label" %>
157
160
 
158
161
  <%%= form.file_field :<%= attribute.name %>,
@@ -189,6 +192,7 @@
189
192
 
190
193
  <div class="form-file-control">
191
194
  <%%= form.label :<%= attribute.name %>,
195
+ "<%= label %>",
192
196
  class: "form-file-label" %>
193
197
 
194
198
  <%% <%= singular_table_name %>.<%= attribute.name %>.each do |image| %>
@@ -246,7 +250,7 @@
246
250
  <%%= form.<%= attribute.field_type %> :<%= attribute.name %>,
247
251
  class: "form-input",
248
252
  placeholder: " " %>
249
- <%%= form.label :<%= attribute.name %>, class: "form-label" %>
253
+ <%%= form.label :<%= attribute.name %>, "<%= label %>", class: "form-label" %>
250
254
  </div>
251
255
  <% end -%>
252
256
  <% end -%>
@@ -92,19 +92,33 @@ module B4um
92
92
  return
93
93
  end
94
94
 
95
- old_index = "@#{file_name.pluralize} = #{class_name}.all"
95
+ plain_collection = "#{class_name}.all"
96
+ sortable_collection = "#{class_name}.order(:position, :id)"
96
97
 
97
- unless controller_content.include?(old_index)
98
+ collection =
99
+ if controller_content.include?(
100
+ "@#{file_name.pluralize} = #{sortable_collection}"
101
+ )
102
+ sortable_collection
103
+ elsif controller_content.include?(
104
+ "@#{file_name.pluralize} = #{plain_collection}"
105
+ )
106
+ plain_collection
107
+ end
108
+
109
+ unless collection
98
110
  raise Thor::Error,
99
111
  "Could not find the index collection in #{controller_path}"
100
112
  end
101
113
 
102
- search_code = <<~RUBY.chomp
103
- @#{file_name.pluralize} = b4um_search(
104
- #{class_name}.all,
105
- params[:q]
106
- )
107
- RUBY
114
+ old_index = "@#{file_name.pluralize} = #{collection}"
115
+
116
+ search_code = [
117
+ "@#{file_name.pluralize} = b4um_search(",
118
+ " #{collection},",
119
+ " params[:q]",
120
+ " )"
121
+ ].join("\n")
108
122
 
109
123
  gsub_file(
110
124
  controller_path,