Make checklist entries sortable by d&d
Some checks failed
/ Run tests (push) Failing after 15s
/ Run system tests (push) Failing after 14s
/ Build, push and deploy image (push) Has been skipped

This commit is contained in:
david 2024-10-27 22:37:11 +01:00
parent 1e1d80a2c3
commit 7acc0559ae
10 changed files with 98 additions and 20 deletions

View file

@ -33,8 +33,13 @@ class ChecklistEntriesController < ApplicationController
# PATCH/PUT /checklist_entries/1
def update
if @checklist_entry.update(checklist_entry_params)
redirect_to @checklist_entry.checklist, notice: "Checklist entry was successfully updated.",
status: :see_other
respond_to do |format|
format.turbo_stream
format.html do
redirect_to @checklist_entry.checklist, notice: "Checklist entry was successfully updated.",
status: :see_other
end
end
else
render :edit, status: :unprocessable_entity
end

View file

@ -0,0 +1,38 @@
import { Controller } from "@hotwired/stimulus"
import Sortable from "sortablejs"
import { put } from "@rails/request.js";
// Connects to data-controller="drag"
export default class extends Controller {
static values = {
group: String
}
connect() {
this.element.style.cursor = "grab"
new Sortable(this.element, {
group: this.groupValue,
onEnd: this.onEnd,
animation: 150,
swap: true,
swapThreshold: 0.65,
ghostClass: 'dragFrom',
swapClass: 'dragTo',
forceFallback: false,
})
}
onEnd(event) {
const position = event.newIndex + 1
const url = event.item.dataset["sortableUrl"]
// Expect backend to update list items via turbo if necessary
put(url, {
body: JSON.stringify({ checklist_entry: { position: position }}),
contentType: "application/json",
headers: {
"Accept": "text/vnd.turbo-stream.html, text/html, application/xhtml+xml"
}
})
}
}

View file

@ -13,6 +13,9 @@ application.register("check-link", CheckLinkController)
import CollapseChevronTogglerController from "./collapse_chevron_toggler_controller"
application.register("collapse-chevron-toggler", CollapseChevronTogglerController)
import DragController from "./drag_controller"
application.register("drag", DragController)
import HelloController from "./hello_controller"
application.register("hello", HelloController)

View file

@ -105,14 +105,24 @@ class Check < ApplicationRecord
end
def display_target_disabilities
%i[visual auditory physical cognitive].select { |d| send(:"#{d}?") }.map { |d| I18n.t("disability.#{d}") }.join(", ")
%i[visual auditory physical cognitive].select { |d| send(:"#{d}?") }
.map { |d| I18n.t("disability.#{d}") }
.sort
.join(", ")
end
def display_applicabilities
%i[applicable_to_analogue applicable_to_app applicable_to_document applicable_to_non_web applicable_to_web].select { |a| send(:"#{a}?") }.map { |a| I18n.t("applicability.#{a}") }.join(", ")
%i[applicable_to_analogue
applicable_to_app
applicable_to_document
applicable_to_non_web
applicable_to_web].select { |a| send(:"#{a}?") }
.map { |a| I18n.t("applicability.#{a}") }
.sort
.join(", ")
end
def display_label
"#{name_de} (#{[number, external_number].compact_blank.join("/")})"
[external_number, name_de].compact_blank.join(" ")
end
end

View file

@ -3,13 +3,18 @@
<div id="<%= dom_id checklist_entry %>" class="hover-row py-1">
<div class="d-flex">
<div style="width: 56px">
<%= button_to tag.i(class: "bi bi-arrow-down"), checklist_entry_path(checklist_entry), method: :patch, class: "btn btn-link p-0 #{"pe-3" if is_last } float-start", data: { turbo_frame: "checklist_entries" }, params: { checklist_entry: { position: checklist_entry.position + 1 }} unless is_last %>
<%= button_to tag.i(class: "bi bi-arrow-up"), checklist_entry_path(checklist_entry), method: :patch, class: "btn btn-link p-0 pe-3 float-start", data: { turbo_frame: "checklist_entries" }, params: { checklist_entry: { position: checklist_entry.position - 1 }} unless is_first %>
<i class="bi bi-grip-vertical text-secondary mt-2"></i>
<div style="">
<% button_to tag.i(class: "bi bi-arrow-down"), checklist_entry_path(checklist_entry), method: :patch, class: "btn btn-link p-0 #{"pe-3" if is_last } float-start", data: { turbo_frame: "checklist_entries" }, params: { checklist_entry: { position: checklist_entry.position + 1 }} unless is_last %>
<% button_to tag.i(class: "bi bi-arrow-up"), checklist_entry_path(checklist_entry), method: :patch, class: "btn btn-link p-0 pe-3 float-start", data: { turbo_frame: "checklist_entries" }, params: { checklist_entry: { position: checklist_entry.position - 1 }} unless is_first %>
</div>
<div class="d-inline-flex flex-grow-1">
<%# checklist_entry.position %>
<%= link_to([checklist_entry.position, checklist_entry.check.display_label].join(" "), checklist_entry.check, data: { turbo_frame: "_top" }, class: "flex-grow-1") %>
<span class="flex-grow-1 mt-2">
<%= checklist_entry.check.display_label %>
</span>
<%= link_to(tag.id(class: "bi bi-search"), checklist_entry.check, data: { turbo_frame: "_top" }, class: "btn btn-link") %>
<%= button_to tag.i(class: "bi bi-trash"), checklist_entry_path(checklist_entry), method: :delete, class: "btn btn-link" %>
<%= link_to tag.i(class: "bi bi-pencil"), edit_checklist_entry_path(checklist_entry), class: "btn btn-link" %>
</div>

View file

@ -1,7 +1,9 @@
<div id="<%= dom_id checklist_entry %>">
<%= bootstrap_form_with(model: checklist_entry, layout: :horizontal) do |form| %>
<%= link_to "Abbrechen", checklist_entry.persisted? ? checklist_entry : checklist_entry.checklist, class: "btn btn-outline-secondary float-end", data: { turbo_frame: "checklist_entries" } %>
<%= form.submit class: "btn btn-secondary float-end me-2" %>
<%= form.hidden_field :checklist_id %>
<%= form.collection_select :check_id, Check.all.order(:name_de), :id, :name_de %>
<%= form.collection_select :check_id, Check.all.order(:external_number), :id, :display_label %>
<%# form.number_field :position %>
<% end %>
<% end %>
</div>

View file

@ -0,0 +1 @@
<%== @checklist_entry.checklist.checklist_entries.map { turbo_stream.update _1 }.join %>

View file

@ -1,10 +1,7 @@
<h1><i class="bi bi-card-checklist me-2"></i><%= "#{@checklist.name}" %></h1>
<div class="row">
<div class="col col-md-12">
<%= render @checklist %>
</div>
<div class="col col-md-12">
<div class="col col-12 col-lg-8">
<div class="mt-3">
<%= link_to tag.i(class: "bi bi-plus-lg"), new_checklist_entry_path(checklist_id: @checklist.id), class: "btn btn-primary float-end", data: { turbo_frame: "checklist_entries" } %>
<h2>Checks</h2>
@ -14,13 +11,18 @@
<% end %>
</div>
<%= turbo_frame_tag "checklist_entries" do %>
<% @checklist.checklist_entries.each do |entry| %>
<%= turbo_frame_tag dom_id(entry, :frame) do %>
<%= render entry %>
<div data-controller="drag">
<% @checklist.checklist_entries.each do |entry| %>
<%= turbo_frame_tag dom_id(entry, :frame), data: { id: entry.id, "sortable-url": url_for(entry) } do %>
<%= render entry %>
<% end %>
<% end %>
<%= tag.i "Es sind keine Checks zugewiesen." if @checklist.empty? %>
<% end %>
<%= tag.i "Es sind keine Checks zugewiesen." if @checklist.empty? %>
<% end %>
</div>
</div>
<div class="col col-12 col-lg-4">
<%= render @checklist %>
</div>
</div>

View file

@ -6,6 +6,7 @@
"@hotwired/turbo-rails": "^8.0.4",
"@popperjs/core": "^2.11.8",
"@rails/actiontext": "^7.1.3-4",
"@rails/request.js": "^0.0.11",
"autoprefixer": "^10.4.19",
"bootstrap": "^5.3.3",
"bootstrap-icons": "^1.11.3",
@ -14,6 +15,7 @@
"postcss": "^8.4.39",
"postcss-cli": "^11.0.0",
"sass": "^1.77.8",
"sortablejs": "^1.15.3",
"trix": "^2.1.3"
},
"scripts": {

View file

@ -185,6 +185,11 @@
dependencies:
spark-md5 "^3.0.1"
"@rails/request.js@^0.0.11":
version "0.0.11"
resolved "https://registry.yarnpkg.com/@rails/request.js/-/request.js-0.0.11.tgz#4d9be25a49d97911c64ccd0f00b79d57fca4c3b4"
integrity sha512-2U3uYS0kbljt+pAstN+LIlZOl7xmOKig5N6FrvtUWO1wq0zR1Hf90fHfD2SYiyV8yH1nyKpoTmbLqWT0xe1zDg==
"@sindresorhus/merge-streams@^2.1.0":
version "2.3.0"
resolved "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz"
@ -712,6 +717,11 @@ slash@^5.0.0, slash@^5.1.0:
resolved "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz"
integrity sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==
sortablejs@^1.15.3:
version "1.15.3"
resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.15.3.tgz#033668db5ebfb11167d1249ab88e748f27959e29"
integrity sha512-zdK3/kwwAK1cJgy1rwl1YtNTbRmc8qW/+vgXf75A7NHag5of4pyI6uK86ktmQETyWRH7IGaE73uZOOBcGxgqZg==
"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.2.0:
version "1.2.0"
resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz"