diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 0733206fb..16382e948 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -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; @@ -801,6 +801,11 @@ body { max-width: 100px; } + .icon-rotated { + transform: rotate(90deg); + display: inline-block; + } + nav.pagy-bootstrap.nav .pagination { margin-bottom: 0; } diff --git a/app/controllers/modeles_controller.rb b/app/controllers/modeles_controller.rb index 9a43d805b..150c79e94 100644 --- a/app/controllers/modeles_controller.rb +++ b/app/controllers/modeles_controller.rb @@ -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 diff --git a/app/decorators/modele_decorator.rb b/app/decorators/modele_decorator.rb index beb4cbf77..aceb62a14 100644 --- a/app/decorators/modele_decorator.rb +++ b/app/decorators/modele_decorator.rb @@ -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 diff --git a/app/javascript/controllers/enclosure_fields_controller.js b/app/javascript/controllers/enclosure_fields_controller.js new file mode 100644 index 000000000..b5503c37b --- /dev/null +++ b/app/javascript/controllers/enclosure_fields_controller.js @@ -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 + } +} diff --git a/app/javascript/src/index.js b/app/javascript/src/index.js index f0b355dd7..204a21dc6 100644 --- a/app/javascript/src/index.js +++ b/app/javascript/src/index.js @@ -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('') }) diff --git a/app/models/composant.rb b/app/models/composant.rb index 077aef86f..cf319f898 100644 --- a/app/models/composant.rb +++ b/app/models/composant.rb @@ -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 diff --git a/app/models/enclosure.rb b/app/models/enclosure.rb index 876ab82e0..f38fe74ec 100644 --- a/app/models/enclosure.rb +++ b/app/models/enclosure.rb @@ -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| diff --git a/app/models/modele.rb b/app/models/modele.rb index e8f7c6fb6..7c24a1010 100644 --- a/app/models/modele.rb +++ b/app/models/modele.rb @@ -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 } diff --git a/app/views/islets/_network_capacity.html.erb b/app/views/islets/_network_capacity.html.erb index da1ffeb49..0acd101c8 100644 --- a/app/views/islets/_network_capacity.html.erb +++ b/app/views/islets/_network_capacity.html.erb @@ -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' %> diff --git a/app/views/modeles/_composant_fields.html.erb b/app/views/modeles/_composant_fields.html.erb index c2f751ef4..8e831d83d 100644 --- a/app/views/modeles/_composant_fields.html.erb +++ b/app/views/modeles/_composant_fields.html.erb @@ -27,7 +27,7 @@ <% if f.object.enclosure&.persisted? %> - + <% end %> diff --git a/app/views/modeles/_enclosure_fields.html.erb b/app/views/modeles/_enclosure_fields.html.erb index 332997102..c518c4e32 100644 --- a/app/views/modeles/_enclosure_fields.html.erb +++ b/app/views/modeles/_enclosure_fields.html.erb @@ -1,4 +1,6 @@ -
+
@@ -16,30 +18,50 @@ <% child_index = "__#{Digest::MD5.hexdigest("Enclosure_composants")}_NEW_RECORD__" %>
-
    +
    • - <%= 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" %> + +
      + <% 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 %> + "> + <%= Enclosure.human_attribute_name("display.#{display}") %> + <% end %> + <% end %> +
    • -
    • +
    • <% - 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| %> @@ -58,36 +80,3 @@
- - diff --git a/app/views/modeles/_form.html.erb b/app/views/modeles/_form.html.erb index 53d078226..d4e765adc 100644 --- a/app/views/modeles/_form.html.erb +++ b/app/views/modeles/_form.html.erb @@ -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| %>
@@ -68,8 +68,20 @@ <%= Modele.human_attribute_name("network_types.help") %>
+ <% end %> + +
+ <%= render CardComponent.new(extra_classes: "bg-body-tertiary mt-4") do |card| %> + <% card.with_header do %> +
+ <%= 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" } %> +
+ <% end %> -
- <%= f.label :enclosures, class: "form-label" %> <%= f.fields_for :enclosures do |enclosure_fields| %> <%= render partial: "enclosure_fields", locals: { f: enclosure_fields } %> <% end %>
+
+ - + <% card.with_footer do %> + -
- - <% end %> + <% end %> + <% end %> + <%= render Form::ActionsComponent.new(f) %> <% end %> diff --git a/app/views/modeles/index.html.erb b/app/views/modeles/index.html.erb index ba7d30a08..aef3b854a 100644 --- a/app/views/modeles/index.html.erb +++ b/app/views/modeles/index.html.erb @@ -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| %> diff --git a/app/views/modeles/preview.turbo_stream.erb b/app/views/modeles/preview.turbo_stream.erb new file mode 100644 index 000000000..077ccd0ca --- /dev/null +++ b/app/views/modeles/preview.turbo_stream.erb @@ -0,0 +1,68 @@ +<%= turbo_stream.update :ts_modele_form_preview do %> + +
0) %>"> + + <% if enclosure.display == "horizontal" %> + <% composants_per_enclosure.each do |component| %> + + + + + <% end %> + <% else %> + + <% composants_per_enclosure.each do |component| %> + + <% end %> + + + + + <% end %> + +
<%= component %>
<%= component %>
+ <% end %> + <% end %> + + +<% end %> diff --git a/app/views/pdus/_pdu.html.erb b/app/views/pdus/_pdu.html.erb index bb3290a3e..79ad794b8 100644 --- a/app/views/pdus/_pdu.html.erb +++ b/app/views/pdus/_pdu.html.erb @@ -1,5 +1,5 @@
<%= link_to pdu.name[-1], edit_server_path(pdu.id) %>
-<% pdu.modele.enclosures.first.composants.each do |component| %> +<% pdu.modele.enclosures.first.composants.ordered.each do |component| %>
<%= link_to "#", class: "pdu-line" do %> <%= component.name ? component.name[-2..-1] : ' ' %> diff --git a/app/views/servers/_card_fields.html.erb b/app/views/servers/_card_fields.html.erb index d447e474d..1000ea39d 100644 --- a/app/views/servers/_card_fields.html.erb +++ b/app/views/servers/_card_fields.html.erb @@ -1,8 +1,8 @@ <% if local_assigns[:enclosure].present? - composants = enclosure.composants.slots + composants = enclosure.composants.ordered.slots else - composants = server.modele.composants.slots + composants = server.modele.composants.ordered.slots end %> diff --git a/app/views/servers/_draw_patch_panel.html.erb b/app/views/servers/_draw_patch_panel.html.erb index 04b85fe49..a153de100 100644 --- a/app/views/servers/_draw_patch_panel.html.erb +++ b/app/views/servers/_draw_patch_panel.html.erb @@ -2,7 +2,7 @@ <%= link_to h(server.name), server_path(server, :view => params[:view]) %>
- <% slots_sur_modele = server.modele.composants.slots.to_a %> + <% slots_sur_modele = server.modele.composants.ordered.slots.to_a %> <% half_slots = slots_sur_modele.length / 2 %> <% slots_sur_modele.slice(0, half_slots).each do |slot| %> diff --git a/app/views/servers/_draw_server.html.erb b/app/views/servers/_draw_server.html.erb index ae8ed27af..924109618 100644 --- a/app/views/servers/_draw_server.html.erb +++ b/app/views/servers/_draw_server.html.erb @@ -1,6 +1,6 @@ <% if server.modele.present? %>
- <% slots_sur_modele = server.modele.composants.slots %> + <% slots_sur_modele = server.modele.composants.ordered.slots %> <% slots_sur_modele.each do |slot| %> @@ -33,7 +33,7 @@ @@ -41,7 +41,7 @@
<%= ports_by_card(port_type: PortType.find_by_name('RJ'), - port_quantity: server.modele.composants.where(type_composant_id: 3).count, + port_quantity: server.modele.composants.ordered.where(type_composant_id: 3).count, ports_data: Port.where(card: server.cards.joins(composant: :type_composant).where('type_composants.name = ?', 'CM')) )%>
- <% if server.modele.composants.where(type_composant_id: 2).count > 0 %> + <% if server.modele.composants.ordered.where(type_composant_id: 2).count > 0 %>
<%= ports_by_card(port_type: PortType.find_by_name('IPMI'), port_quantity: 1, @@ -60,7 +60,7 @@
<%= link_to server.modele.try(:name), modele_path(server.modele) %>
- <% alims_sur_modele = server.modele.composants.where(type_composant_id: 1) %> + <% alims_sur_modele = server.modele.composants.ordered.where(type_composant_id: 1) %> <% alims_sur_modele.each_with_index do |composant_alim, i| %> ALIM <%= i+1 %> <% end %> diff --git a/app/views/servers/_draw_server_compact.html.erb b/app/views/servers/_draw_server_compact.html.erb index 7cf778a49..bdd6e75d1 100644 --- a/app/views/servers/_draw_server_compact.html.erb +++ b/app/views/servers/_draw_server_compact.html.erb @@ -2,11 +2,8 @@ <% if server.modele.present? %>
- <% server.modele.enclosures.each_with_index do |enclosure, index| %> - - <% composants_per_enclosure = enclosure.composants.slots %> - + <% composants_per_enclosure = enclosure.composants.ordered.slots %> <% if enclosure.display == "grid" %> -
+
<% composants_per_enclosure.each do |component| %> <% class_name = "item-#{server.id}-#{component.name}" %>