Skip to content

Commit

Permalink
Merge pull request nanego#291 from pantographe/343818-improve-display…
Browse files Browse the repository at this point in the history
…-input-on-enclosure-fields-form
  • Loading branch information
nicolas-brousse authored Dec 18, 2024
2 parents ab0b83e + 188d209 commit 7c18871
Show file tree
Hide file tree
Showing 28 changed files with 502 additions and 172 deletions.
15 changes: 10 additions & 5 deletions app/assets/stylesheets/application.scss
Original file line number Diff line number Diff line change
Expand Up @@ -412,21 +412,21 @@ span.port_container.unreferenced_client a {
padding: 0;
max-width: 600px;
display: grid;
border: 1px gray solid;
border: 1px #999999 solid;
grid-template-columns: auto;
grid-template-rows: auto;
gap: 1px;
background-color: lightgray;

.component-label {
color: darkgray;
color: var(--bs-body-color);
z-index: 1;
padding-left: 5px;
font-size: 10px;
height: 1px;
}

.item {
background-color: white;
color: darkgray;
color: var(--bs-body-color);
display: flex;
justify-content: center;
align-items: center;
Expand Down Expand Up @@ -801,6 +801,11 @@ body {
max-width: 100px;
}

.icon-rotated {
transform: rotate(90deg);
display: inline-block;
}

nav.pagy-bootstrap.nav .pagination {
margin-bottom: 0;
}
Expand Down
42 changes: 28 additions & 14 deletions app/controllers/modeles_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,25 +35,39 @@ def edit
def create
@modele = Modele.new(modele_params)

respond_to do |format|
if @modele.save
format.html { redirect_to modele_path(@modele), notice: t(".flashes.created") }
format.json { render :show, status: :created, location: @modele }
else
format.html { render :new }
format.json { render json: @modele.errors, status: :unprocessable_entity }
if params[:preview]
respond_to do |format|
format.turbo_stream { render :preview, status: :unprocessable_entity }
end
else
respond_to do |format|
if @modele.save
format.html { redirect_to modele_path(@modele), notice: t(".flashes.created") }
format.json { render :show, status: :created, location: @modele }
else
format.html { render :new }
format.json { render json: @modele.errors, status: :unprocessable_entity }
end
end
end
end

def update
respond_to do |format|
if @modele.update(modele_params)
format.html { redirect_to modele_path(@modele), notice: t(".flashes.updated") }
format.json { render :show, status: :ok, location: @modele }
else
format.html { render :edit }
format.json { render json: @modele.errors, status: :unprocessable_entity }
@modele.assign_attributes(modele_params)

if params[:preview]
respond_to do |format|
format.turbo_stream { render :preview, status: :unprocessable_entity }
end
else
respond_to do |format|
if @modele.save
format.html { redirect_to modele_path(@modele), notice: t(".flashes.updated") }
format.json { render :show, status: :ok, location: @modele }
else
format.html { render :edit }
format.json { render json: @modele.errors, status: :unprocessable_entity }
end
end
end
end
Expand Down
10 changes: 10 additions & 0 deletions app/decorators/modele_decorator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,14 @@ def network_types_to_human

n_t.map { |type| Modele.human_attribute_name("network_types.#{type}") }.join(", ")
end

def displays_to_human
return nil unless enclosures.any? # TODO : manage "-" in table component

enclosures.map do |enclosure|
next Enclosure.human_attribute_name("display.blank") if enclosure.display.nil?

Enclosure.human_attribute_name("display.#{enclosure.display}")
end.join(", ")
end
end
43 changes: 43 additions & 0 deletions app/javascript/controllers/enclosure_fields_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Controller } from "@hotwired/stimulus"
import Sortable from "sortablejs"

const sortableOptions = {
handle: ".handle",
animation: 150,
filter: ".fitlered",
onEnd: function (evt) {
evt.to.querySelectorAll("li.composant").forEach((item, index) => {
item.querySelector("input[name*='[position]']").value = index + 1
});
},
onMove: function (evt) {
return evt.related.className.indexOf("filtered") === -1;
}
}

export default class extends Controller {
static targets = ["sortableList", "gridArea", "displayRadioButton"]

connect() {
this.sortable = Sortable.create(this.sortableListTarget, sortableOptions)

// Show grid area on page load
this.displayRadioButtonTargets.forEach((radio) => {
if (radio.checked) this.toggleGridTextArea(radio)
})
}

disconnect() {
this.sortable.destroy()
}

displayValueChange(event) {
this.toggleGridTextArea(event.target)
}

toggleGridTextArea(target) {
this.gridAreaTarget.hidden = true

if (target.value == "grid") this.gridAreaTarget.hidden = false
}
}
3 changes: 0 additions & 3 deletions app/javascript/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ import "src/functions"
import "src/bays"
import "src/servers"

import Sortable from "sortablejs"
window.Sortable = Sortable

$(document).on("click", ".draw_connections_link", function (event) {
$(event.target).html('<span class="bi bi-three-dots" aria-hidden="true"></span>')
})
Expand Down
1 change: 1 addition & 0 deletions app/models/composant.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class Composant < ApplicationRecord
validates :name, format: { without: /\s/ }, allow_blank: true

scope :slots, -> { where(type_composant: TypeComposant.find_by_name('SLOT')).order("composants.position ASC") }
scope :ordered, -> { order(position: :asc) }

def to_s
name.to_s
Expand Down
8 changes: 5 additions & 3 deletions app/models/enclosure.rb
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
# frozen_string_literal: true

class Enclosure < ApplicationRecord
DISPLAYS = %i[vertical horizontal grid].freeze

has_changelog
acts_as_list scope: [:modele_id]

belongs_to :modele
has_many :composants, -> { order(position: :asc) }
has_many :composants, dependent: :destroy

accepts_nested_attributes_for :composants,
:allow_destroy => true,
:reject_if => :all_blank
allow_destroy: true,
reject_if: :all_blank

def deep_dup
dup.tap do |enclosure|
Expand Down
4 changes: 2 additions & 2 deletions app/models/modele.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ class Modele < ApplicationRecord
belongs_to :category, counter_cache: true

accepts_nested_attributes_for :enclosures,
:allow_destroy => true,
:reject_if => :all_blank
allow_destroy: true,
reject_if: :all_blank

validate :validate_network_types_values
normalizes :network_types, with: ->(values) { values.compact_blank }
Expand Down
2 changes: 1 addition & 1 deletion app/views/islets/_network_capacity.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
<% if server.modele.present? %>
<% server.modele.enclosures.each do |enclosure| %>
<% cards = server.cards %>
<% components_per_enclosure = enclosure.composants.reject { |c| c.name&.downcase == "alim" } %>
<% components_per_enclosure = enclosure.composants.ordered.reject { |c| c.name&.downcase == "alim" } %>
<% if enclosure.display != 'horizontal' %>
<table>
<tr>
Expand Down
2 changes: 1 addition & 1 deletion app/views/modeles/_composant_fields.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
<span class="input-group input-group-sm">
<% if f.object.enclosure&.persisted? %>
<span class="input-group-text">
<span class="bi bi-arrows-vertical"></span>
<span class="handle bi bi-arrows-vertical"></span>
</span>
<% end %>

Expand Down
73 changes: 31 additions & 42 deletions app/views/modeles/_enclosure_fields.html.erb
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<div class="nested-form-wrapper col-12 col-md-6 col-xxl-4" data-new-record="<%= f.object.new_record? %>">
<div class="nested-form-wrapper col-12 col-md-6 col-xxl-4"
data-new-record="<%= f.object.new_record? %>"
data-controller="enclosure-fields">
<div class="card enclosure-fields">
<div class="card-header d-flex justify-content-between align-items-center">
<h6 class="mb-0">
Expand All @@ -16,30 +18,50 @@
<% child_index = "__#{Digest::MD5.hexdigest("Enclosure_composants")}_NEW_RECORD__" %>
<div data-controller="nested-form" data-nested-form-child-index-name-value="<%= child_index %>">
<template data-nested-form-target="template">
<%= f.fields_for :composants, f.object.composants.slots.klass.new,
<%= f.fields_for :composants, f.object.composants.ordered.slots.klass.new,
child_index: child_index do |composant_fields| %>
<%= render partial: "composant_fields", locals: { f: composant_fields, type_composant: "SLOT" } %>
<% end %>
</template>

<div class="card-body">
<ul id="enclosureList_<%= f.object.id %>" class="list-group bg-body-tertiary">
<ul class="list-group bg-body-tertiary" data-enclosure-fields-target="sortableList">
<li class="list-group-item bg-transparent filtered">
<fieldset>
<%= f.label :display, class: "form-label" %> :
<%= f.select :display, options_for_select([:vertical, :horizontal, :grid], f.object.display),
{},
class: "form-select form-select-sm" %>
<%= f.label :display, class:"form-check-label" %>

<div class="btn-group btn-group-sm w-100 my-2"
role="group"
aria-label="Display attribute radio toggle button group"
data-enclosure-fields-target="display-radios">
<% Enclosure::DISPLAYS.each do |display| %>
<%= f.radio_button :display,
display,
class: "btn-check",
data: {
action: "change->enclosure-fields#displayValueChange",
"enclosure-fields-target": "displayRadioButton"
} %>
<%= f.label :display, value: display, class: "btn btn-outline-secondary" do %>
<i class="<%= class_names("bi",
"bi-layout-three-columns": display == :vertical,
"bi-layout-three-columns icon-rotated": display == :horizontal,
"bi-columns-gap": display == :grid
)%>"></i>
<%= Enclosure.human_attribute_name("display.#{display}") %>
<% end %>
<% end %>
</div>
</fieldset>
</li>
<li class="list-group-item bg-transparent filtered">
<li class="list-group-item bg-transparent filtered" data-enclosure-fields-target="gridArea" hidden>
<fieldset>
<%= f.label :grid_areas, class: "form-label" %>
<%= f.text_area :grid_areas, class: "font-monospace form-control", style: "height:110px;" %>
</fieldset>
</li>
<%
composants = f.object.composants.select { |c| c.type_composant == TypeComposant.find_by_name('SLOT') }
composants = f.object.composants.ordered.select { |c| c.type_composant == TypeComposant.find_by_name("SLOT") }
composants = composants.sort_by! { |c| c.try(:position) || (composants.size + 1) }
%>
<%= f.fields_for :composants, composants do |composant_fields| %>
Expand All @@ -58,36 +80,3 @@
</div>
</div>
</div>

<script>
var display_select = $("#modele_enclosures_attributes_<%= f.object.position.to_i - 1 %>_display")
var grid_text_area = $("#modele_enclosures_attributes_<%= f.object.position.to_i - 1 %>_grid_areas").closest(".list-group-item")
var toggle_grid_text_area = (function f() {
var val = display_select.val()
grid_text_area.hide()
if (val == "grid") {
grid_text_area.show()
}
return f
})()

display_select.change(function () {
toggle_grid_text_area()
});

$(document).ready(function() {
Sortable.create(enclosureList_<%= f.object.id %>, {
handle: ".bi-arrows-vertical",
animation: 150,
filter: ".fitlered",
onEnd: function (evt) {
evt.to.querySelectorAll("li.composant").forEach((item, index) => {
item.querySelector("input[name*='[position]']").value = index + 1
});
},
onMove: function (evt) {
return evt.related.className.indexOf("filtered") === -1;
}
});
});
</script>
32 changes: 25 additions & 7 deletions app/views/modeles/_form.html.erb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<%= form_for(@modele, html: { class: "col-12 col-md-10 col-lg-8 mx-auto", role: "form" }) do |f| %>
<%= form_for(@modele, html: { class: "col-12 col-md-10 col-lg-8 mx-auto", role: "form" }, data: { turbo: true }) do |f| %>
<%= render FormErrorsComponent.new(@modele) %>
<%= render CardComponent.new(extra_classes: "bg-body-tertiary") do |card| %>
<fieldset class="col-12">
Expand Down Expand Up @@ -68,8 +68,20 @@
<%= Modele.human_attribute_name("network_types.help") %>
</div>
</fieldset>
<% end %>

<fieldset data-controller="nested-form" class="col-12">
<%= render CardComponent.new(extra_classes: "bg-body-tertiary mt-4") do |card| %>
<% card.with_header do %>
<div class="d-flex justify-content-between align-items-center">
<%= Enclosure.model_name.human.pluralize %>
<%= f.submit t("action.preview"),
class: "btn btn-outline-primary btn-sm",
name: :preview,
data: { "bs-toggle": "modal", "bs-target": "#previewModal" } %>
</div>
<% end %>

<fieldset data-controller="nested-form" class="col-12 mt-4">
<div class="row g-3">
<template data-nested-form-target="template">
<%= f.fields_for :enclosures, f.object.enclosures.klass.new,
Expand All @@ -78,21 +90,27 @@
<% end %>
</template>

<%= f.label :enclosures, class: "form-label" %>
<%= f.fields_for :enclosures do |enclosure_fields| %>
<%= render partial: "enclosure_fields", locals: { f: enclosure_fields } %>
<% end %>

<div data-nested-form-target="target"></div>
</div>
<div class="modal fade" tabindex="-1" id="previewModal">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content" id="ts_modele_form_preview"></div>
</div>
</div>

<span class="text-center mt-2">
<% card.with_footer do %>
<span class="mx-auto">
<button type="button" class="btn btn-outline-success btn-sm" data-action="nested-form#add">
<span class="bi bi-plus-lg" aria-hidden="true"></span> <%= t(".add_enclosure") %>
</button>
</span>
</div>
</fieldset>
<% end %>
<% end %>
<% end %>
</fieldset>

<%= render Form::ActionsComponent.new(f) %>
<% end %>
2 changes: 1 addition & 1 deletion app/views/modeles/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
<% end %>

<% table.with_column(Enclosure.human_attribute_name(:display)) do |modele| %>
<%= modele.enclosures.map(&:display).join(', ') %>
<%= modele.decorated.displays_to_human %>
<% end %>

<% table.with_column(Server.model_name.human.pluralize, sort_by: :servers_count) do |modele| %>
Expand Down
Loading

0 comments on commit 7c18871

Please sign in to comment.