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:
Ridhwana 2020-08-25 17:39:12 +02:00 committed by GitHub
parent 5f45f5ed0e
commit b0bacf60fb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 135 additions and 48 deletions

View file

@ -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;
}

View file

@ -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

View file

@ -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

View file

@ -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?

View file

@ -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" %>

View 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>

View file

@ -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>

View file

@ -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