All update forms are in their own subpages now and there is no other components in those subpages, so additional ring makes the UI look more cluttered than it should be.
122 lines
5.2 KiB
Plaintext
122 lines
5.2 KiB
Plaintext
<%# locals: (action:, form_elements:, option_tree:, option_parents:, method: "POST", pre_selected_values: {}, mode: "create") %>
|
|
<% nonce = SecureRandom.base64(32)
|
|
content_security_policy.add_script_src [:nonce, nonce] %>
|
|
|
|
<script nonce="<%==nonce%>">
|
|
let option_tree = <%==JSON.generate(option_tree)%>
|
|
let option_children = <%==JSON.generate(option_parents.each_with_object(Hash.new { |h, k| h[k] = [] }) { |(child, parents), h| h[parents.last] << child if parents.any? })%>
|
|
let option_dirty = <%==JSON.generate(form_elements.map { it[:name] }.each_with_object(Hash.new { |h, k| h[k] = [] }) { |option, h| h[option] = flash.dig("old", option) || pre_selected_values[option] })%>
|
|
</script>
|
|
|
|
<div>
|
|
<div class="grid gap-6">
|
|
<form action="<%= action %>" method="POST" id="creation-form" class="<%= method %>">
|
|
<%== csrf_tag(action, method) %>
|
|
<% container_class = (mode == "create") ? "overflow-hidden rounded-lg shadow ring-1 ring-black ring-opacity-5 bg-white" : "" %>
|
|
<div class="<%= container_class %>">
|
|
<div class="px-4 py-5 sm:p-6">
|
|
<div class="space-y-12">
|
|
<div>
|
|
<div class="mt-2 grid grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
|
|
<% selected_options = {}
|
|
pre_selected_values.each { |k, v| selected_options[k] = v.to_s }
|
|
|
|
form_elements.each do |element|
|
|
container_opening_tag = element[:opening_tag] || "<div class='col-span-full'>"
|
|
container_closing_tag = element[:closing_tag] || "</div>"
|
|
|
|
name, type, label, required, placeholder, content_generator = element.values_at(:name, :type, :label, :required, :placeholder, :content_generator)
|
|
|
|
has_option = ["radio_small_cards", "select", "checkbox"].include?(type)
|
|
options = OptionTreeGenerator.generate_allowed_options(name, option_tree, option_parents).map do |option|
|
|
opt_val = if option[name].is_a?(Sequel::Model)
|
|
option[name].ubid
|
|
elsif type == "select"
|
|
option[name][:value]
|
|
else
|
|
option[name]
|
|
end
|
|
|
|
opt_text = content_generator.call(*option.values)
|
|
|
|
parents = option.reject { |k, v| k == name }.transform_values { |v| v.is_a?(Sequel::Model) ? v.ubid : v }
|
|
|
|
parents_selected = parents.all? { |k, v| selected_options[k] == v }
|
|
submitted_value = flash.dig("old", name)
|
|
selected_in_form_submission = submitted_value == opt_val
|
|
selected_in_pre_selection = submitted_value.nil? && selected_options[name] == opt_val
|
|
first_of_its_kind = submitted_value.nil? && selected_options[name].nil?
|
|
checked = parents_selected && (selected_in_form_submission || selected_in_pre_selection || first_of_its_kind)
|
|
selected_options[name] = opt_val if checked
|
|
|
|
opt_classes = parents.map { |k, v| "form_#{k} form_#{k}_#{v}" }
|
|
opt_classes << "hidden" unless parents_selected
|
|
opt_classes = opt_classes.join(" ")
|
|
|
|
opt_attrs = {}
|
|
opt_attrs[:disabled] = "disabled" unless parents_selected
|
|
opt_attrs[:checked] = checked
|
|
|
|
[opt_val, opt_text, opt_classes, opt_attrs]
|
|
end if has_option
|
|
|
|
if has_option && selected_options[name].nil?
|
|
options.find{ |_, _, _, opt_attrs| opt_attrs[:disabled].nil? }&.tap do |opt_val, _, _, opt_attrs|
|
|
opt_attrs[:checked] = true
|
|
selected_options[name] = opt_val
|
|
end
|
|
end
|
|
|
|
attributes = {required:}
|
|
|
|
case type
|
|
when "radio_small_cards"
|
|
locals = {name:, label:, options:, attributes:}
|
|
when "select"
|
|
locals = {name:, label:, options:, placeholder:, attributes:, description_html: element[:description_html]}
|
|
when "checkbox"
|
|
locals = {name:, label:, options:, description: element[:description]}
|
|
when "text"
|
|
locals = {name:, label:, value: element[:value], attributes: attributes.merge!({placeholder:})}
|
|
when "number"
|
|
locals = {name:, label:, type: "number", attributes: attributes.merge!({placeholder:})}
|
|
type = "text"
|
|
when "textarea"
|
|
locals = {name:, label:, description: element[:description], attributes: attributes.merge!({placeholder:, rows: 3})}
|
|
when "hidden"
|
|
selected_options[name] = element[:value]
|
|
locals = {name:, value: element[:value]}
|
|
container_opening_tag, container_closing_tag = "", ""
|
|
when "partnership_notice"
|
|
description, tos_text = content_generator.call(@flavor)
|
|
locals = {description:, tos_text:}
|
|
when "section"
|
|
locals = {label:, content: element[:content], separator: element[:separator]}
|
|
# :nocov:
|
|
else
|
|
raise "Unknown element type: #{type}"
|
|
# :nocov:
|
|
end %>
|
|
<%== container_opening_tag %>
|
|
<%== part("components/form/#{type}", **locals) %>
|
|
<%== container_closing_tag %>
|
|
<% end %>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="px-4 py-5 sm:p-6">
|
|
<div class="flex items-center justify-end gap-x-6">
|
|
<% if mode == "create" %>
|
|
<a href="<%= action %>" class="text-sm font-semibold leading-6 text-gray-900">Cancel</a>
|
|
<%== part("components/form/submit_button", text: "Create") %>
|
|
<% else %>
|
|
<%== part("components/form/submit_button", text: "Update") %>
|
|
<% end %>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|