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:
parent
f023c6174f
commit
3474ffda6d
13 changed files with 382 additions and 78 deletions
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
47
app/controllers/admin/profile_field_groups_controller.rb
Normal file
47
app/controllers/admin/profile_field_groups_controller.rb
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
50
app/views/admin/profile_fields/_add_group_modal.html.erb
Normal file
50
app/views/admin/profile_fields/_add_group_modal.html.erb
Normal 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>
|
||||
|
|
@ -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>
|
||||
50
app/views/admin/profile_fields/_edit_group_modal.html.erb
Normal file
50
app/views/admin/profile_fields/_edit_group_modal.html.erb
Normal 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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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"> (<%= 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 %>
|
||||
|
|
@ -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" %>
|
||||
|
|
@ -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 %>
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
65
spec/requests/admin/profile_field_groups_spec.rb
Normal file
65
spec/requests/admin/profile_field_groups_spec.rb
Normal 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
|
||||
Loading…
Add table
Reference in a new issue