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