super 0.0.14 → 0.18.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 +4 -4
- data/README.md +2 -2
- data/app/assets/javascripts/super/application.js +148 -15
- data/app/assets/stylesheets/super/application.css +15 -22
- data/app/controllers/super/application_controller.rb +44 -42
- data/app/controllers/super/substructure_controller.rb +313 -0
- data/app/helpers/super/form_builder_helper.rb +7 -0
- data/app/views/layouts/super/application.html.erb +3 -21
- data/app/views/super/application/_collection_header.html.erb +2 -2
- data/app/views/super/application/_display_actions.html.erb +1 -1
- data/app/views/super/application/_display_index.html.erb +2 -2
- data/app/views/super/application/_display_show.html.erb +1 -1
- data/app/views/super/application/_filter.html.erb +62 -2
- data/app/views/super/application/_form_field.html.erb +5 -0
- data/app/views/super/application/_member_header.html.erb +2 -2
- data/app/views/super/application/_pagination.html.erb +1 -1
- data/app/views/super/application/_site_footer.html.erb +3 -0
- data/app/views/super/application/_site_header.html.erb +17 -0
- data/app/views/super/application/_sort_expression.html.erb +2 -2
- data/app/views/super/application/index.csv.erb +14 -0
- data/app/views/super/feather/README.md +0 -1
- data/frontend/super-frontend/dist/application.css +15 -22
- data/frontend/super-frontend/dist/application.js +148 -15
- data/lib/generators/super/install/install_generator.rb +0 -16
- data/lib/generators/super/install/templates/base_controller.rb.tt +0 -8
- data/lib/generators/super/resource/templates/resources_controller.rb.tt +4 -4
- data/lib/super/action_inquirer.rb +18 -3
- data/lib/super/badge.rb +60 -0
- data/lib/super/cheat.rb +6 -6
- data/lib/super/display/guesser.rb +1 -1
- data/lib/super/display/schema_types.rb +49 -1
- data/lib/super/display.rb +2 -1
- data/lib/super/error.rb +3 -0
- data/lib/super/filter/form_object.rb +74 -48
- data/lib/super/filter/guesser.rb +2 -0
- data/lib/super/filter/operator.rb +90 -64
- data/lib/super/filter/schema_types.rb +63 -80
- data/lib/super/filter.rb +1 -1
- data/lib/super/form/builder.rb +30 -39
- data/lib/super/form/field_transcript.rb +43 -0
- data/lib/super/form/guesser.rb +4 -4
- data/lib/super/form/schema_types.rb +66 -22
- data/lib/super/link.rb +2 -2
- data/lib/super/pagination.rb +2 -44
- data/lib/super/reset.rb +25 -0
- data/lib/super/schema.rb +4 -0
- data/lib/super/useful/builder.rb +4 -4
- data/lib/super/version.rb +1 -1
- data/lib/super.rb +3 -1
- data/lib/tasks/super/cheat.rake +1 -1
- metadata +25 -19
- data/app/views/super/application/_filter_type_select.html.erb +0 -21
- data/app/views/super/application/_filter_type_text.html.erb +0 -18
- data/app/views/super/application/_filter_type_timestamp.html.erb +0 -24
- data/app/views/super/application/_form_field_checkbox.html.erb +0 -1
- data/app/views/super/application/_form_field_flatpickr_date.html.erb +0 -8
- data/app/views/super/application/_form_field_flatpickr_datetime.html.erb +0 -8
- data/app/views/super/application/_form_field_flatpickr_time.html.erb +0 -8
- data/app/views/super/application/_form_field_rich_text_area.html.erb +0 -1
- data/app/views/super/application/_form_field_select.html.erb +0 -1
- data/app/views/super/application/_form_field_text.html.erb +0 -1
- data/app/views/super/feather/_chevron_down.html +0 -1
- data/docs/cheat.md +0 -41
- data/lib/super/controls/optional.rb +0 -113
- data/lib/super/controls/steps.rb +0 -106
- data/lib/super/controls/view.rb +0 -55
- data/lib/super/controls.rb +0 -22
@@ -0,0 +1,313 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Super
|
4
|
+
# Various methods that determine the behavior of your controllers. These
|
5
|
+
# methods can and should be overridden.
|
6
|
+
class SubstructureController < ActionController::Base
|
7
|
+
private
|
8
|
+
|
9
|
+
helper_method def model
|
10
|
+
raise Error::NotImplementedError
|
11
|
+
end
|
12
|
+
|
13
|
+
# This is an optional method
|
14
|
+
#
|
15
|
+
# @return [String]
|
16
|
+
helper_method def title
|
17
|
+
model.name.pluralize
|
18
|
+
end
|
19
|
+
|
20
|
+
# This defines what to set in the <title> tag. It works in conjunction with
|
21
|
+
# the `#site_title` method
|
22
|
+
#
|
23
|
+
# @return [String, void]
|
24
|
+
helper_method def page_title
|
25
|
+
model.name.pluralize
|
26
|
+
rescue Error::NotImplementedError, NameError
|
27
|
+
return nil
|
28
|
+
end
|
29
|
+
|
30
|
+
# Configures what database records are visible on load. This is an optional
|
31
|
+
# method, it defaults to "`all`" methods
|
32
|
+
#
|
33
|
+
# @return [ActiveRecord::Relation]
|
34
|
+
helper_method def base_scope
|
35
|
+
model.all
|
36
|
+
end
|
37
|
+
|
38
|
+
# Configures the fields that are displayed on the index and show actions.
|
39
|
+
# This is a required method
|
40
|
+
#
|
41
|
+
# @return [Display]
|
42
|
+
helper_method def display_schema
|
43
|
+
Display.new do |fields, type|
|
44
|
+
Display::Guesser.new(model: model, action: current_action, fields: fields, type: type).call
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Configures the editable fields on the new and edit actions. This is a
|
49
|
+
# required method
|
50
|
+
#
|
51
|
+
# @return [Form]
|
52
|
+
helper_method def form_schema
|
53
|
+
Form.new do |fields, type|
|
54
|
+
Form::Guesser.new(model: model, fields: fields, type: type).call
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Configures which parameters could be written to the database. This is a
|
59
|
+
# required method
|
60
|
+
#
|
61
|
+
# @return [ActionController::Parameters]
|
62
|
+
helper_method def permitted_params
|
63
|
+
strong_params =
|
64
|
+
if current_action.create?
|
65
|
+
with_current_action("new") do
|
66
|
+
Super::Form::StrongParams.new(form_schema)
|
67
|
+
end
|
68
|
+
elsif current_action.update?
|
69
|
+
with_current_action("edit") do
|
70
|
+
Super::Form::StrongParams.new(form_schema)
|
71
|
+
end
|
72
|
+
else
|
73
|
+
Super::Form::StrongParams.new(form_schema)
|
74
|
+
end
|
75
|
+
params.require(strong_params.require(model)).permit(strong_params.permit)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Configures the actions linked to on the index page. This is an optional
|
79
|
+
# method
|
80
|
+
#
|
81
|
+
# @return [Array<Link>]
|
82
|
+
helper_method def collection_actions
|
83
|
+
Super::Link.find_all(:new)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Configures the actions linked to on the show page as well as each row of
|
87
|
+
# the table on the index page. This is an optional method
|
88
|
+
#
|
89
|
+
# Favor the `record` argument over the `@record` instance variable;
|
90
|
+
# `@record` won't always be set, notably on the index page where it's
|
91
|
+
# called on every row
|
92
|
+
#
|
93
|
+
# @return [Array<Link>]
|
94
|
+
helper_method def member_actions(record)
|
95
|
+
if current_action.show?
|
96
|
+
Super::Link.find_all(:edit, :destroy)
|
97
|
+
elsif current_action.edit?
|
98
|
+
Super::Link.find_all(:show, :destroy)
|
99
|
+
else
|
100
|
+
Super::Link.find_all(:show, :edit, :destroy)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
helper_method def filters_enabled?
|
105
|
+
true
|
106
|
+
end
|
107
|
+
|
108
|
+
helper_method def filter_schema
|
109
|
+
Super::Filter.new do |fields, type|
|
110
|
+
Super::Filter::Guesser.new(model: model, fields: fields, type: type).call
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
helper_method def sort_enabled?
|
115
|
+
true
|
116
|
+
end
|
117
|
+
|
118
|
+
helper_method def sortable_columns
|
119
|
+
action = ActionInquirer.new(
|
120
|
+
ActionInquirer.default_for_resources,
|
121
|
+
"index"
|
122
|
+
)
|
123
|
+
attribute_names =
|
124
|
+
display_schema.each_attribute.map do |key, val|
|
125
|
+
val = val.build if val.respond_to?(:build)
|
126
|
+
key if val.real?
|
127
|
+
end
|
128
|
+
|
129
|
+
attribute_names.compact
|
130
|
+
end
|
131
|
+
|
132
|
+
helper_method def default_sort
|
133
|
+
{ id: :desc }
|
134
|
+
end
|
135
|
+
|
136
|
+
# Specifies how many records to show per page
|
137
|
+
#
|
138
|
+
# @return [ActiveRecord::Relation]
|
139
|
+
helper_method def records_per_page
|
140
|
+
Super.configuration.index_records_per_page
|
141
|
+
end
|
142
|
+
|
143
|
+
def load_records
|
144
|
+
base_scope
|
145
|
+
end
|
146
|
+
|
147
|
+
def load_record
|
148
|
+
base_scope.find(params[:id])
|
149
|
+
end
|
150
|
+
|
151
|
+
def build_record
|
152
|
+
base_scope.build
|
153
|
+
end
|
154
|
+
|
155
|
+
def set_record_attributes
|
156
|
+
@record.attributes = permitted_params
|
157
|
+
end
|
158
|
+
|
159
|
+
def save_record
|
160
|
+
@record.save
|
161
|
+
end
|
162
|
+
|
163
|
+
def destroy_record
|
164
|
+
@record.destroy
|
165
|
+
end
|
166
|
+
|
167
|
+
def initialize_query_form
|
168
|
+
Super::Query::FormObject.new(
|
169
|
+
model: model,
|
170
|
+
params: params,
|
171
|
+
namespace: :q,
|
172
|
+
current_path: request.path,
|
173
|
+
)
|
174
|
+
end
|
175
|
+
|
176
|
+
def apply_queries
|
177
|
+
@query_form.apply_changes(@records)
|
178
|
+
end
|
179
|
+
|
180
|
+
def initialize_filter_form
|
181
|
+
if filters_enabled?
|
182
|
+
@query_form.add(
|
183
|
+
Super::Filter::FormObject,
|
184
|
+
namespace: :f,
|
185
|
+
schema: filter_schema
|
186
|
+
)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def initialize_sort_form
|
191
|
+
if sort_enabled?
|
192
|
+
@query_form.add(
|
193
|
+
Super::Sort::FormObject,
|
194
|
+
namespace: :s,
|
195
|
+
default: default_sort,
|
196
|
+
sortable_columns: sortable_columns
|
197
|
+
)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def pagination_disabled_param
|
202
|
+
:_all_pages
|
203
|
+
end
|
204
|
+
|
205
|
+
def pagination_enabled?
|
206
|
+
!params.key?(pagination_disabled_param)
|
207
|
+
end
|
208
|
+
|
209
|
+
# Sets up pagination
|
210
|
+
#
|
211
|
+
# @return [Pagination]
|
212
|
+
def initialize_pagination
|
213
|
+
Pagination.new(
|
214
|
+
total_count: @records.size,
|
215
|
+
limit: records_per_page,
|
216
|
+
query_params: request.GET,
|
217
|
+
page_query_param: :page
|
218
|
+
)
|
219
|
+
end
|
220
|
+
|
221
|
+
# Paginates
|
222
|
+
#
|
223
|
+
# @return [ActiveRecord::Relation]
|
224
|
+
def paginate_records
|
225
|
+
return @records if !pagination_enabled?
|
226
|
+
|
227
|
+
@records
|
228
|
+
.limit(@pagination.limit)
|
229
|
+
.offset(@pagination.offset)
|
230
|
+
end
|
231
|
+
|
232
|
+
def index_view
|
233
|
+
Super::Layout.new(
|
234
|
+
mains: [
|
235
|
+
Super::Panel.new(
|
236
|
+
Super::Partial.new("collection_header"),
|
237
|
+
:@display
|
238
|
+
),
|
239
|
+
],
|
240
|
+
asides: [
|
241
|
+
:@query_form,
|
242
|
+
]
|
243
|
+
)
|
244
|
+
end
|
245
|
+
|
246
|
+
def show_view
|
247
|
+
Super::Layout.new(
|
248
|
+
mains: [
|
249
|
+
Super::Panel.new(
|
250
|
+
Super::Partial.new("member_header"),
|
251
|
+
:@display
|
252
|
+
),
|
253
|
+
]
|
254
|
+
)
|
255
|
+
end
|
256
|
+
|
257
|
+
def new_view
|
258
|
+
Super::Layout.new(
|
259
|
+
mains: [
|
260
|
+
Super::Panel.new(
|
261
|
+
Super::Partial.new("collection_header"),
|
262
|
+
:@form
|
263
|
+
),
|
264
|
+
]
|
265
|
+
)
|
266
|
+
end
|
267
|
+
|
268
|
+
def edit_view
|
269
|
+
Super::Layout.new(
|
270
|
+
mains: [
|
271
|
+
Super::Panel.new(
|
272
|
+
Super::Partial.new("member_header"),
|
273
|
+
:@form
|
274
|
+
),
|
275
|
+
]
|
276
|
+
)
|
277
|
+
end
|
278
|
+
|
279
|
+
concerning :Sitewide do
|
280
|
+
included do
|
281
|
+
helper_method :site_title
|
282
|
+
helper_method :site_navigation
|
283
|
+
helper_method :document_title
|
284
|
+
end
|
285
|
+
|
286
|
+
private
|
287
|
+
|
288
|
+
def site_title
|
289
|
+
Super.configuration.title
|
290
|
+
end
|
291
|
+
|
292
|
+
def site_navigation
|
293
|
+
Super::Navigation.new(&:all)
|
294
|
+
end
|
295
|
+
|
296
|
+
def document_title
|
297
|
+
if instance_variable_defined?(:@document_title)
|
298
|
+
return @document_title
|
299
|
+
end
|
300
|
+
|
301
|
+
document_title_segments.map(&:presence).compact.join(document_title_separator)
|
302
|
+
end
|
303
|
+
|
304
|
+
def document_title_segments
|
305
|
+
@document_title_segments ||= [page_title, site_title]
|
306
|
+
end
|
307
|
+
|
308
|
+
def document_title_separator
|
309
|
+
@document_title_separator ||= " - "
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|
@@ -1,7 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Super
|
4
|
+
# These are form builder view helpers. They are similar to what Rails ships
|
5
|
+
# out of the box but adds some styling and functionality.
|
6
|
+
#
|
7
|
+
# These helpers are available both in Super views and in your application's
|
8
|
+
# views.
|
4
9
|
module FormBuilderHelper
|
10
|
+
# Super's version of `#form_for`
|
5
11
|
def super_form_for(record, options = {}, &block)
|
6
12
|
original = ActionView::Base.field_error_proc
|
7
13
|
ActionView::Base.field_error_proc = Form::Builder::FIELD_ERROR_PROC
|
@@ -12,6 +18,7 @@ module Super
|
|
12
18
|
ActionView::Base.field_error_proc = original
|
13
19
|
end
|
14
20
|
|
21
|
+
# Super's version of `#form_with`
|
15
22
|
def super_form_with(**options, &block)
|
16
23
|
original = ActionView::Base.field_error_proc
|
17
24
|
ActionView::Base.field_error_proc = Form::Builder::FIELD_ERROR_PROC
|
@@ -1,7 +1,7 @@
|
|
1
1
|
<!DOCTYPE html>
|
2
2
|
<html class="bg-gray-100">
|
3
3
|
<head>
|
4
|
-
<title><%=
|
4
|
+
<title><%= document_title %></title>
|
5
5
|
<%= csrf_meta_tags %>
|
6
6
|
<% if respond_to?(:csp_meta_tag) -%>
|
7
7
|
<%= csp_meta_tag %>
|
@@ -28,23 +28,7 @@
|
|
28
28
|
|
29
29
|
<body class="font-sans pb-4">
|
30
30
|
<div class="px-4">
|
31
|
-
|
32
|
-
<h1 class="text-lg font-bold mr-6 mb-1"><%= Super.configuration.title %></h1>
|
33
|
-
<% navigation.definition.each do |link_or_menu| %>
|
34
|
-
<% if link_or_menu.is_a?(Super::Link) %>
|
35
|
-
<%= link_or_menu.to_s(default_options: { class: "inline-block mr-6" }) %>
|
36
|
-
<% else %>
|
37
|
-
<details class="details-reset select-none inline-block mr-6" data-controller="click-outside-to-close" data-action="click@window->click-outside-to-close#close">
|
38
|
-
<summary class="text-blue-700"><%= link_or_menu.title %></summary>
|
39
|
-
<ul class="absolute bg-white shadow-md px-4 py-3">
|
40
|
-
<% link_or_menu.links.each do |link| %>
|
41
|
-
<li><%= link.to_s(default_options: { class: "" }) %></li>
|
42
|
-
<% end %>
|
43
|
-
</ul>
|
44
|
-
</details>
|
45
|
-
<% end %>
|
46
|
-
<% end %>
|
47
|
-
</header>
|
31
|
+
<%= render "site_header" %>
|
48
32
|
|
49
33
|
<%= render "flash" %>
|
50
34
|
|
@@ -52,9 +36,7 @@
|
|
52
36
|
|
53
37
|
<%= yield %>
|
54
38
|
|
55
|
-
|
56
|
-
<%= t("super.layout.powered_by", env: Rails.env.capitalize, version: Super::VERSION) %>
|
57
|
-
</div>
|
39
|
+
<%= render "site_footer" %>
|
58
40
|
</div>
|
59
41
|
</body>
|
60
42
|
</html>
|
@@ -1,9 +1,9 @@
|
|
1
1
|
<header class="flex justify-between content-end">
|
2
2
|
<h1 class="text-xl">
|
3
|
-
<%=
|
3
|
+
<%= title %>
|
4
4
|
</h1>
|
5
5
|
<div>
|
6
|
-
<%
|
6
|
+
<% collection_actions.each do |link| %>
|
7
7
|
<%= link.to_s(
|
8
8
|
default_options: {
|
9
9
|
class: "super-button super-button--border-blue super-button-sm inline-block ml-2"
|
@@ -1,10 +1,10 @@
|
|
1
1
|
<div class="mt-4 overflow-x-auto">
|
2
|
-
<table class="w-full border-separate
|
2
|
+
<table class="w-full border-separate" cellspacing="0" cellpadding="0">
|
3
3
|
<thead class="">
|
4
4
|
<tr class="">
|
5
5
|
<% display_index.each_attribute_name do |attribute_name| %>
|
6
6
|
<th class="p-2 first:pl-6 border-b border-b-2 border-gray-400 text-gray-600 text-left text-sm font-normal bg-white top-0 z-10">
|
7
|
-
<%=
|
7
|
+
<%= model.human_attribute_name(attribute_name) %>
|
8
8
|
</th>
|
9
9
|
<% end %>
|
10
10
|
</tr>
|
@@ -1,7 +1,7 @@
|
|
1
1
|
<table class="max-w-full mt-4">
|
2
2
|
<% display_show.each_attribute_name do |attribute_name| %>
|
3
3
|
<tr>
|
4
|
-
<th class="py-1 align-baseline text-right px-4"><%=
|
4
|
+
<th class="py-1 align-baseline text-right px-4"><%= model.human_attribute_name(attribute_name) %></th>
|
5
5
|
<td class="py-1 align-baseline"><%= display_show.render_attribute(template: self, record: @record, column: attribute_name) %></td>
|
6
6
|
</tr>
|
7
7
|
<% end %>
|
@@ -1,6 +1,66 @@
|
|
1
1
|
<h1 class="text-lg">Filter</h1>
|
2
|
-
<% filter.
|
2
|
+
<% filter.each_attribute do |attribute_form_object| %>
|
3
3
|
<div class="mt-4">
|
4
|
-
<%=
|
4
|
+
<%= form.fields_for(attribute_form_object.field_name, attribute_form_object) do |attribute_form| %>
|
5
|
+
<% selected_index = 0 %>
|
6
|
+
<div data-controller="tab-container" data-tab-container-tab-identifier-getter-value="tabIdentifierValue" data-tab-container-tab-controller-name-value="tab">
|
7
|
+
<div>
|
8
|
+
<span class="inline-block"><%= attribute_form_object.humanized_attribute_name %></span>
|
9
|
+
<select data-tab-container-target="control" data-action="tab-container#change" class="super-input super-input-select inline-block">
|
10
|
+
<% selected = false %>
|
11
|
+
<% attribute_form_object.each_operator.with_index do |operator_form_object, index| %>
|
12
|
+
<%
|
13
|
+
selected_attribute =
|
14
|
+
if !selected && operator_form_object.specified?
|
15
|
+
selected = true
|
16
|
+
selected_index = index
|
17
|
+
%(selected=selected)
|
18
|
+
else
|
19
|
+
""
|
20
|
+
end
|
21
|
+
%>
|
22
|
+
<option value="<%= operator_form_object.identifier %>" <%= selected_attribute %>><%= operator_form_object.operator.humanized_operator_name %></option>
|
23
|
+
<% end %>
|
24
|
+
</select>
|
25
|
+
</div>
|
26
|
+
<div>
|
27
|
+
<% attribute_form_object.each_operator.with_index do |operator_form_object, index| %>
|
28
|
+
<div data-controller="tab" data-tab-tab-container-getter-value="tabContainer" data-tab-container-target="tab" data-tab-identifier-value="<%= operator_form_object.identifier %>">
|
29
|
+
<% form_html = capture do %>
|
30
|
+
<div data-tab-target="content">
|
31
|
+
<%= attribute_form.fields_for(operator_form_object.identifier, operator_form_object) do |operator_form| %>
|
32
|
+
<div class="flex gap-x-2 mt-2">
|
33
|
+
<% operator_form_object.each_field.with_index do |operator_field_name, index| %>
|
34
|
+
<div class="flex-1">
|
35
|
+
<% if operator_field_name == Super::Filter::FormObject::OperatorForm::NULLARY %>
|
36
|
+
<%= operator_form.super.check_box(operator_field_name, {}, "1", "") %>
|
37
|
+
<%= operator_form.super.label(operator_field_name, nil, super: { class: "select-none ml-1" }) %>
|
38
|
+
<% elsif operator_form_object.operator.respond_to?(:field_transcript) && operator_form_object.operator.field_transcript %>
|
39
|
+
<% field_transcript = operator_form_object.operator.field_transcript %>
|
40
|
+
<% if field_transcript.super? %>
|
41
|
+
<%= operator_form.super.public_send(field_transcript.method_name, operator_field_name, *field_transcript.args, **field_transcript.kwargs) %>
|
42
|
+
<% else %>
|
43
|
+
<%= operator_form.public_send(field_transcript.method_name, operator_field_name, *field_transcript.args, **field_transcript.kwargs) %>
|
44
|
+
<% end %>
|
45
|
+
<% else %>
|
46
|
+
<%= operator_form.super.text_field(operator_field_name) %>
|
47
|
+
<% end %>
|
48
|
+
</div>
|
49
|
+
<% end %>
|
50
|
+
</div>
|
51
|
+
<% end %>
|
52
|
+
</div>
|
53
|
+
<% end %>
|
54
|
+
|
55
|
+
<%= form_html if index == selected_index %>
|
56
|
+
|
57
|
+
<template data-tab-target="pocket">
|
58
|
+
<%= form_html %>
|
59
|
+
</template>
|
60
|
+
</div>
|
61
|
+
<% end %>
|
62
|
+
</div>
|
63
|
+
</div>
|
64
|
+
<% end %>
|
5
65
|
</div>
|
6
66
|
<% end %>
|