Profile Admin updates (#10133)

* feat: cater for when there are no profile fields in a group

* feat: add a modal to create a new group

* feat: add a controller and route for the group

* refactor: add group modal

* feat: edit a group

* chore: remove an instance var

* chore: toggle the section using a toggle button

* feat: add the edit button

* chore: update the messaging

* chore: update the class name

* feat: delete the group

* chore: add some css

* chore: use group_name

* chore: rename the file and add the group select to the file

* feat: render the correct partial and add the profile_field_group_id

* feat: add a profile field

* feat: amend the styles on the card header

* feat: add a cursor pointer

* feat: get the form to redirect after an update

* chore:  remove style

* feat: order by created at so that each time we save the order doesn't change

* fix: change the form

* chore: format the options

* feat: show ungrouped fields at the bottom

* feat: add the profile fields length

* chore: remove unused action

* test: add some specs for profile group workflow

* fix: oops

* refactor: grouped profile fields

* refactor: amend the styles

* chore: rename methods

* chore: update headings

* chore: remove changes from my linter

* refactor: suggestions by Michael - upgrade ternary

* refactor: order by name and label
This commit is contained in:
Ridhwana 2020-09-02 12:32:25 +02:00 committed by GitHub
parent f023c6174f
commit 3474ffda6d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 382 additions and 78 deletions

View file

@ -61,3 +61,35 @@
margin: 1em 0;
text-align: end;
}
#profileFields {
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
}
.card-header__actions {
display: flex;
justify-content: space-between;
}
.card-header__information {
display: flex;
align-items: center;
}
.group__description {
color: $medium-gray;
width: 150px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-left: 16px;
}
.card-header__actions > div {
margin: 0 16px;
cursor: pointer;
}
}

View file

@ -0,0 +1,47 @@
module Admin
class ProfileFieldGroupsController < Admin::ApplicationController
ALLOWED_PARAMS = %i[
name description
].freeze
layout "admin"
def update
profile_field_group = ProfileFieldGroup.find(params[:id])
if profile_field_group.update(profile_field_group_params)
flash[:success] = "Group #{profile_field_group.name} updated"
else
flash[:error] = "Error: #{profile_field_group.errors_as_sentence}"
end
redirect_to admin_profile_fields_path
end
def create
profile_field_group = ProfileFieldGroup.new(profile_field_group_params)
if profile_field_group.save
flash[:success] = "Successfully created group: #{profile_field_group.name}"
else
flash[:error] = "Error: #{profile_field_group.errors_as_sentence}"
end
redirect_to admin_profile_fields_path
end
def destroy
profile_field_group = ProfileFieldGroup.find(params[:id])
if profile_field_group.destroy
flash[:success] = "Group #{profile_field_group.name} deleted"
else
flash[:error] = "Error: #{profile_field_group.errors_as_sentence}"
end
redirect_to admin_profile_fields_path
end
private
private_constant :ALLOWED_PARAMS
def profile_field_group_params
allowed_params = ALLOWED_PARAMS
params.require(:profile_field_group).permit(allowed_params)
end
end
end

View file

@ -1,12 +1,13 @@
module Admin
class ProfileFieldsController < Admin::ApplicationController
ALLOWED_PARAMS = %i[
input_type label active placeholder_text description group
input_type label active placeholder_text description profile_field_group_id
].freeze
layout "admin"
def index
@grouped_profile_fields = ProfileFieldGroup.all.includes(:profile_fields)
@grouped_profile_fields = ProfileFieldGroup.all.includes(:profile_fields).order(:name)
@ungrouped_profile_fields = ProfileField.where(profile_field_group_id: nil).order(:label)
end
def update

View file

@ -0,0 +1,50 @@
<button class="btn btn-secondary" type="button" onclick="showAddGroupModal()">
Add group
</button>
<div id="add-group-modal" class="hidden">
<div class="crayons-modal crayons-modal--s absolute">
<div class="crayons-modal__box">
<header class="crayons-modal__box__header">
<h2>Add Group</h2>
<button type="button" onclick="hideAddGroupModal()" class="crayons-btn crayons-btn--icon crayons-btn--ghost">
<svg width="24" height="24" viewBox="0 0 24 24" class="crayons-icon" xmlns="http://www.w3.org/2000/svg">
<path d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636l4.95 4.95z" />
</svg>
</button>
</header>
<div class="crayons-modal__box__body">
<div class="form-group grid p-6 mb-6 gap-1">
<%= form_for [:admin, ProfileFieldGroup.new] do |form| %>
<div class="form-group">
<%= form.label :name %>
<%= form.text_field :name, class: "form-control" %>
</div>
<div class="form-group">
<%= form.label :description %>
<%= form.text_field :description, class: "form-control" %>
</div>
<%= form.submit class: "btn btn-primary" %>
<% end %>
</div>
</div>
</div>
<div class="crayons-modal__overlay"></div>
</div>
</div>
<script>
function showAddGroupModal() {
const confirmationModal = document.getElementById('add-group-modal');
if (confirmationModal) {
confirmationModal.classList.remove("hidden");
}
}
function hideAddGroupModal() {
const confirmationModal = document.getElementById('add-group-modal');
if (confirmationModal) {
confirmationModal.classList.add("hidden");
}
}
</script>

View file

@ -0,0 +1,43 @@
<div onclick="showAddProfileFieldModal('<%= group_name %>')">
Add Field
</div>
<div id="add-<%= group_name %>-profile-field-modal" class="hidden">
<div class="crayons-modal crayons-modal--s">
<div class="crayons-modal__box">
<header class="crayons-modal__box__header">
<h2>Add Profile Field</h2>
<button type="button" onclick="hideProfileFieldModal('<%= group_name %>')" class="crayons-btn crayons-btn--icon crayons-btn--ghost">
<svg width="24" height="24" viewBox="0 0 24 24" class="crayons-icon" xmlns="http://www.w3.org/2000/svg">
<path d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636l4.95 4.95z" />
</svg>
</button>
</header>
<div class="crayons-modal__box__body">
<div class="form-group grid p-6 mb-6 gap-1">
<%= form_for [:admin, ProfileField.new] do |form| %>
<%= render "profile_field_form", form: form, group: group, group_name: group_name %>
<%= form.submit class: "btn btn-primary" %>
<% end %>
</div>
</div>
</div>
<div class="crayons-modal__overlay"></div>
</div>
</div>
<script>
function showAddProfileFieldModal(group_name) {
const confirmationModal = document.getElementById(`add-${group_name}-profile-field-modal`);
if (confirmationModal) {
confirmationModal.classList.remove("hidden");
}
}
function hideProfileFieldModal(group_name) {
const confirmationModal = document.getElementById(`add-${group_name}-profile-field-modal`);
if (confirmationModal) {
confirmationModal.classList.add("hidden");
}
}
</script>

View file

@ -0,0 +1,50 @@
<div onclick="showEditGroupModal('<%= group_name %>')" class="color-accent-warning-darker">
Edit group
</div>
<div id="edit-group-<%= group_name %>-modal" class="hidden">
<div class="crayons-modal crayons-modal--s">
<div class="crayons-modal__box">
<header class="crayons-modal__box__header">
<h2>Edit Group <%= group_name %></h2>
<button type="button" onclick="hideEditGroupModal('<%= group_name %>')" class="crayons-btn crayons-btn--icon crayons-btn--ghost">
<svg width="24" height="24" viewBox="0 0 24 24" class="crayons-icon" xmlns="http://www.w3.org/2000/svg">
<path d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636l4.95 4.95z" />
</svg>
</button>
</header>
<div class="crayons-modal__box__body">
<div class="form-group grid p-6 mb-6 gap-1">
<%= form_for [:admin, group] do |form| %>
<div class="form-group">
<%= form.label :name %>
<%= form.text_field :name, class: "form-control" %>
</div>
<div class="form-group">
<%= form.label :description %>
<%= form.text_field :description, class: "form-control" %>
</div>
<%= form.submit class: "btn btn-primary" %>
<% end %>
</div>
</div>
</div>
<div class="crayons-modal__overlay"></div>
</div>
</div>
<script>
function showEditGroupModal(group_name) {
const confirmationModal = document.getElementById(`edit-group-${group_name}-modal`);
if (confirmationModal) {
confirmationModal.classList.remove("hidden");
}
}
function hideEditGroupModal(group_name) {
const confirmationModal = document.getElementById(`edit-group-${group_name}-modal`);
if (confirmationModal) {
confirmationModal.classList.add("hidden");
}
}
</script>

View file

@ -1,36 +0,0 @@
<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

@ -0,0 +1,52 @@
<% @grouped_profile_fields.each do |group| %>
<% group_name = group.name.gsub(/\s+/, "_") %>
<article class="row my-3">
<div class="card w-100">
<div class="card-header">
<div class="card-header__information">
<div class="fw-bold fs-l"><%= group_name %></div>
<div class="fw-bold fs-s">&nbsp;(<%= group.profile_fields.length %>)</div>
<% if group.description %>
<div class="fs-s group__description"><%= group.description %></div>
<% end %>
</div>
<div class="card-header__actions">
<%= render partial: "add_profile_field_modal", locals: { group: group, group_name: group_name } %>
<%= render partial: "edit_group_modal", locals: { group: group, group_name: group_name } %>
<div><%= link_to "Delete Group", admin_profile_field_group_path(group), data: { confirm: "Are you sure?" }, method: :delete, class: "color-accent-danger" %></div>
<div id="<%= group_name %>Header" data-toggle="collapse" class="color-base-70"
data-target="#<%= group_name %>BodyContainer" aria-expanded="false" aria-controls="<%= group_name %>BodyContainer">Toggle</div>
</div>
</div>
<div id="<%= group_name %>BodyContainer" class="collapse hide p-3" aria-labelledby="<%= group_name %>Header">
<% if group.profile_fields.empty? %>
<div> There are no profile fields configured for this group. </div>
<% else %>
<% group.profile_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 "profile_field_form", form: form, group: group %>
<%= 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 %>
<% end %>
</div>
</div>
</article>
<% end %>

View file

@ -1,3 +1,14 @@
<div class="form-group">
<%= form.label :group %>
<%= select_tag "profile_field[profile_field_group_id]",
options_from_collection_for_select(
ProfileFieldGroup.all,
:id, :name,
selected: group&.id
),
{ class: "form-control selectpicker profile__group-dropdown",
include_blank: "" } %>
</div>
<div class="form-group">
<%= form.label :label %>
<%= form.text_field :label, class: "form-control" %>

View file

@ -0,0 +1,22 @@
<% @ungrouped_profile_fields.each do |field| %>
<div class="row my-3">
<div class="card w-100">
<%= 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 "profile_field_form", form: form, group: nil %>
<%= 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>
</div>
<% end %>

View file

@ -1,41 +1,6 @@
<header class="mb-6">
<h2 class="fs-2xl s:fs-3xl">Profile fields</h2>
</header>
<main id="profileFields">
<%= render "add_group_modal" %>
<% @grouped_profile_fields.each do |group| %>
<% group_name = group.name.gsub(/\s+/, "_") %>
<div class="crayons-card">
<div class="card-header flex" id="<%= group_name %>Header" data-toggle="collapse"
data-target="#<%= group_name %>BodyContainer" aria-expanded="false" aria-controls="<%= group_name %>BodyContainer">
<div>
<h2 class="d-inline"><%= group_name %></h2>
<% if group.description %>
<div><%= group.description %></div>
<% end %>
</div>
<button class="btn btn-secondary ml-auto" type="button">Toggle <%= group_name %></button>
</div>
<div id="<%= group_name %>BodyContainer" class="collapse hide p-3" aria-labelledby="<%= group %>Header">
<% group.profile_fields.each do |field| %>
<div class="card mb-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="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 "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>
<% end %>
<%= render "grouped_profile_fields" %>
<%= render "ungrouped_profile_fields" %>
</div>

View file

@ -73,6 +73,8 @@ Rails.application.routes.draw do
delete :remove_admin
end
end
resources :profile_field_groups, only: %i[update create destroy]
resources :profile_fields, only: %i[index update create destroy]
resources :reactions, only: [:update]
resources :response_templates, only: %i[index new edit create update destroy]

View file

@ -0,0 +1,65 @@
require "rails_helper"
RSpec.describe "/admin/profile_field_groups", type: :request do
let(:admin) { create(:user, :super_admin) }
before do
sign_in admin
end
describe "POST /admin/profile_field_groups" do
let(:new_profile_field_group) do
{
name: "Group 1",
description: "Description"
}
end
it "redirects successfully" do
post admin_profile_field_groups_path, params: { profile_field_group: new_profile_field_group }
expect(response).to redirect_to admin_profile_fields_path
end
it "creates a profile_field_group" do
expect do
post admin_profile_field_groups_path, params: { profile_field_group: new_profile_field_group }
end.to change { ProfileFieldGroup.all.count }.by(1)
last_profile_field_record = ProfileFieldGroup.last
expect(last_profile_field_record.name).to eq(new_profile_field_group[:name])
expect(last_profile_field_record.description).to eq(new_profile_field_group[:description])
end
end
describe "PUT /admin/profile_field_groups/:id" do
let(:profile_field_group) { create(:profile_field_group) }
it "redirects successfully" do
put "#{admin_profile_field_groups_path}/#{profile_field_group.id}",
params: { profile_field_group: { name: "Group 2" } }
expect(response).to redirect_to admin_profile_fields_path
end
it "updates the profile field values" do
put "#{admin_profile_field_groups_path}/#{profile_field_group.id}",
params: { profile_field_group: { name: "Group 2" } }
changed_profile_group_record = ProfileFieldGroup.find(profile_field_group.id)
expect(changed_profile_group_record.name).to eq("Group 2")
end
end
describe "DELETE /admin/profile_fields/:id" do
let(:profile_field_group) { create(:profile_field_group) }
it "redirects successfully" do
delete "#{admin_profile_field_groups_path}/#{profile_field_group.id}"
expect(response).to redirect_to admin_profile_fields_path
end
it "removes a profile_field_group" do
delete "#{admin_profile_field_groups_path}/#{profile_field_group.id}"
expect(ProfileFieldGroup.count).to eq(0)
end
end
end