Profile Fields Admin UI v2 (#9830)
* feat: add a select and a new text field * feat: groups need to be unique and present * feat: make the ui a little better * chore: update the label * chore: just present * feat: add some js ( still a work in progress) * feat: update the interface to work better :) * chore: indent * feat: make sure the toggles work * chore: rename more aptly * refactor: toggling pulled out into two functions * refactor: move group form to a partial so that we can reuse it * chore: move the js to the group form * chore: update alignment and nav element * chore: update text * chore: update specs
This commit is contained in:
parent
5f45f5ed0e
commit
b0bacf60fb
8 changed files with 135 additions and 48 deletions
|
|
@ -97,3 +97,16 @@ body {
|
|||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
#profileFields .card-header {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.profile__group-toggle {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
color: grey;
|
||||
margin: 1em 0;
|
||||
text-align: end;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,32 +6,32 @@ module Admin
|
|||
# This is used in app/views/admin/shared/_navbar.html.erb to build the
|
||||
# side navbar in alphabetical order.
|
||||
MENU_ITEMS = [
|
||||
{ name: "articles", controller: "articles" },
|
||||
{ name: "broadcasts", controller: "broadcasts" },
|
||||
{ name: "badges", controller: "badges" },
|
||||
{ name: "chat_channels", controller: "chat_channels" },
|
||||
{ name: "comments", controller: "comments" },
|
||||
{ name: "config", controller: "config" },
|
||||
{ name: "events", controller: "events" },
|
||||
{ name: "growth", controller: "growth" },
|
||||
{ name: "listings", controller: "listings" },
|
||||
{ name: "moderator_actions", controller: "moderator_actions" },
|
||||
{ name: "mods", controller: "mods" },
|
||||
{ name: "privileged_reactions", controller: "privileged_reactions" },
|
||||
{ name: "organizations", controller: "organizations" },
|
||||
{ name: "pages", controller: "pages" },
|
||||
{ name: "permissions", controller: "permissions" },
|
||||
{ name: "podcasts", controller: "podcasts" },
|
||||
{ name: "profile setup", controller: "profile_fields" },
|
||||
{ name: "reports", controller: "reports" },
|
||||
{ name: "response_templates", controller: "response_templates" },
|
||||
{ name: "sponsorships", controller: "sponsorships" },
|
||||
{ name: "tags", controller: "tags" },
|
||||
{ name: "tools", controller: "tools" },
|
||||
{ name: "users", controller: "users" },
|
||||
{ name: "vault secrets", controller: "secrets" },
|
||||
{ name: "webhooks", controller: "webhook_endpoints" },
|
||||
{ name: "welcome", controller: "welcome" },
|
||||
{ name: "articles", controller: "articles" },
|
||||
{ name: "broadcasts", controller: "broadcasts" },
|
||||
{ name: "badges", controller: "badges" },
|
||||
{ name: "chat_channels", controller: "chat_channels" },
|
||||
{ name: "comments", controller: "comments" },
|
||||
{ name: "config", controller: "config" },
|
||||
{ name: "events", controller: "events" },
|
||||
{ name: "growth", controller: "growth" },
|
||||
{ name: "listings", controller: "listings" },
|
||||
{ name: "moderator_actions", controller: "moderator_actions" },
|
||||
{ name: "mods", controller: "mods" },
|
||||
{ name: "privileged_reactions", controller: "privileged_reactions" },
|
||||
{ name: "organizations", controller: "organizations" },
|
||||
{ name: "pages", controller: "pages" },
|
||||
{ name: "permissions", controller: "permissions" },
|
||||
{ name: "podcasts", controller: "podcasts" },
|
||||
{ name: "config: profile setup", controller: "profile_fields" },
|
||||
{ name: "reports", controller: "reports" },
|
||||
{ name: "response_templates", controller: "response_templates" },
|
||||
{ name: "sponsorships", controller: "sponsorships" },
|
||||
{ name: "tags", controller: "tags" },
|
||||
{ name: "tools", controller: "tools" },
|
||||
{ name: "users", controller: "users" },
|
||||
{ name: "vault secrets", controller: "secrets" },
|
||||
{ name: "webhooks", controller: "webhook_endpoints" },
|
||||
{ name: "welcome", controller: "welcome" },
|
||||
].sort_by { |menu_item| menu_item[:name] }.freeze
|
||||
|
||||
private
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ module Admin
|
|||
layout "admin"
|
||||
|
||||
def index
|
||||
@profile_fields = ProfileField.all.group_by(&:group)
|
||||
@grouped_profile_fields = ProfileField.all.group_by(&:group)
|
||||
end
|
||||
|
||||
def update
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ class ProfileField < ApplicationRecord
|
|||
validates :label, presence: true, uniqueness: { case_sensitive: false }
|
||||
validates :attribute_name, presence: true, on: :update
|
||||
validates :show_in_onboarding, inclusion: { in: [true, false] }
|
||||
validates :group, presence: true
|
||||
|
||||
def type
|
||||
return :boolean if check_box?
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
<div class="form-group">
|
||||
<%= form.label :group %>
|
||||
<%= form.text_field :group, class: "form-control" %>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<%= form.label :label %>
|
||||
<%= form.text_field :label, class: "form-control" %>
|
||||
|
|
|
|||
36
app/views/admin/profile_fields/_group_form.erb
Normal file
36
app/views/admin/profile_fields/_group_form.erb
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<div>
|
||||
<div class="form-group">
|
||||
<%= form.label :group %>
|
||||
<%= select_tag "profile_field[group]",
|
||||
select_option_tags,
|
||||
select_options %>
|
||||
<%= form.text_field :group, placeholder: "Add a new group", class: "form-control profile__group-text-input hidden", disabled: true %>
|
||||
</div>
|
||||
<div class="profile__group-toggle" onclick="toggleGroup(event)">
|
||||
<%= toggle_text %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function toggleGroup(event) {
|
||||
let groupToggle = event.target
|
||||
let groupDropdown = $( event.target ).siblings().find('.profile__group-dropdown')[0]
|
||||
let groupInput = $( event.target ).siblings().find('.profile__group-text-input')[0]
|
||||
|
||||
if(groupDropdown.classList.contains('hidden')) {
|
||||
performToggling(groupInput, groupDropdown, "or create a new group", groupToggle);
|
||||
} else {
|
||||
performToggling(groupDropdown, groupInput, "or choose an existing group", groupToggle);
|
||||
}
|
||||
}
|
||||
|
||||
function performToggling(elementToHide, elementToShow, updatedText, groupToggle) {
|
||||
elementToShow.classList.remove('hidden');
|
||||
elementToShow.disabled = false;
|
||||
|
||||
elementToHide.classList.add('hidden');
|
||||
elementToHide.disabled = true;
|
||||
|
||||
groupToggle.innerHTML = updatedText;
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,24 +1,65 @@
|
|||
<main>
|
||||
<main id="profileFields">
|
||||
<article class="crayons-card grid p-6 mb-6 gap-1">
|
||||
<%= form_for [:admin, ProfileField.new], html: { class: "inline-form" } do |form| %>
|
||||
<%= render "group_form",
|
||||
form: form,
|
||||
select_option_tags: options_for_select(ProfileField.distinct.pluck(:group)),
|
||||
select_options: {
|
||||
include_blank: "Choose an existing group",
|
||||
class: "form-control selectpicker profile__group-dropdown"
|
||||
},
|
||||
toggle_text: "or create a new group"
|
||||
%>
|
||||
<div class="form-group">
|
||||
<%= render "form", form: form %>
|
||||
<%= form.submit class: "btn btn-primary" %>
|
||||
<%= form.submit "Create New Profile Field", class: "btn btn-primary" %>
|
||||
</div>
|
||||
<% end %>
|
||||
</article>
|
||||
|
||||
<% @profile_fields.each do |group, fields| %>
|
||||
<h2>Group: <%= group %></h3>
|
||||
<% @grouped_profile_fields.each do |group, fields| %>
|
||||
<% group_attribute = group.gsub(/\s+/, "_") %>
|
||||
|
||||
<% fields.each do |field| %>
|
||||
<article class="crayons-card grid p-6 mb-6 gap-1">
|
||||
<%= form_for [:admin, field] do |form| %>
|
||||
<%= render "form", form: form %>
|
||||
<%= form.submit class: "btn btn-primary" %>
|
||||
<% end %>
|
||||
<%= button_to "Delete", admin_profile_field_path(field), data: { confirm: "Are you sure?" }, method: :delete, class: "btn btn-secondary" %>
|
||||
</article>
|
||||
<% end %>
|
||||
<article class="row my-3">
|
||||
<div class="card w-100">
|
||||
<div class="card-header" id="<%= group_attribute %>Header" data-toggle="collapse"
|
||||
data-target="#<%= group_attribute %>BodyContainer" aria-expanded="false" aria-controls="<%= group_attribute %>BodyContainer">
|
||||
<h2 class="d-inline"><%= group %></h2>
|
||||
<button class="btn btn-secondary float-right" type="button">
|
||||
Toggle <%= group %>
|
||||
</button>
|
||||
</div>
|
||||
<div id="<%= group_attribute %>BodyContainer" class="collapse hide hide p-3" aria-labelledby="<%= group %>Header">
|
||||
<% fields.each do |field| %>
|
||||
<div class="card mt-3">
|
||||
<%= render partial: "admin/configs/card_header",
|
||||
locals: {
|
||||
header: field.label,
|
||||
state: "collapse",
|
||||
target: "#{field.attribute_name}_container",
|
||||
expanded: "false"
|
||||
} %>
|
||||
<div id="<%= field.attribute_name %>_container" class="card-body collapse hide" aria-labelledby="<%= field.attribute_name %>_container">
|
||||
<div class="form-group grid p-6 mb-6 gap-1">
|
||||
<%= form_for [:admin, field] do |form| %>
|
||||
<%= render "group_form",
|
||||
form: form,
|
||||
select_option_tags: options_for_select(ProfileField.distinct.pluck(:group), selected: field[:group]),
|
||||
select_options: {
|
||||
class: "form-control selectpicker profile__group-dropdown"
|
||||
},
|
||||
toggle_text: "or create a new group"
|
||||
%>
|
||||
<%= render "form", form: form %>
|
||||
<%= form.submit class: "btn btn-primary" %>
|
||||
<% end %>
|
||||
<%= button_to "Delete Profile Field", admin_profile_field_path(field), data: { confirm: "Are you sure?" }, method: :delete, class: "btn btn-secondary" %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
<% end %>
|
||||
</main>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -7,13 +7,13 @@ RSpec.describe ProfileFields::Add, type: :service do
|
|||
it "creates a new profile field and adds a store accessor", :aggregate_failures do
|
||||
expect(profile.respond_to?(:new_field)).to be false
|
||||
expect do
|
||||
described_class.call(label: "New Field")
|
||||
described_class.call(label: "New Field", group: "Basic")
|
||||
end.to change(ProfileField, :count).by(1)
|
||||
expect(profile.respond_to?(:new_field)).to be true
|
||||
end
|
||||
|
||||
it "returns the correct response object", :aggregate_failures do
|
||||
add_response = described_class.call(label: "Another New Field")
|
||||
add_response = described_class.call(label: "Another New Field", group: "Basic")
|
||||
expect(add_response.success?).to be true
|
||||
expect(add_response.profile_field).to be_an_instance_of(ProfileField)
|
||||
expect(add_response.error_message).to be_blank
|
||||
|
|
@ -29,7 +29,7 @@ RSpec.describe ProfileFields::Add, type: :service do
|
|||
add_response = described_class.call({})
|
||||
expect(add_response.success?).to be false
|
||||
expect(add_response.profile_field).to be_an_instance_of(ProfileField)
|
||||
expect(add_response.error_message).to eq "Label can't be blank"
|
||||
expect(add_response.error_message).to eq "Label can't be blank and Group can't be blank"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue