Changed item#_form input user/room
Some checks failed
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled

This commit is contained in:
2026-05-31 23:34:57 +02:00
parent 68d31090f7
commit 8c7482c1d7
2 changed files with 114 additions and 30 deletions

View File

@@ -0,0 +1,65 @@
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = ["input", "results", "item"]
connect() {
// Schließt die Liste, wenn man irgendwo außerhalb hinklickt
this.closeHandler = (e) => {
if (!this.element.contains(e.target)) {
this.hideResults()
}
}
document.addEventListener("click", this.closeHandler)
}
disconnect() {
document.removeEventListener("click", this.closeHandler)
}
// Wird aufgerufen, wenn der Nutzer tippt (input-Event)
filter() {
const filterValue = this.inputTarget.value.toLowerCase().trim()
if (filterValue === "") {
this.showAll()
return
}
this.showResults()
let hasMatches = false
this.itemTargets.forEach(item => {
const text = item.textContent.toLowerCase()
if (text.includes(filterValue)) {
item.classList.remove("hidden")
hasMatches = true
} else {
item.classList.add("hidden")
}
})
// Falls gar nichts gefunden wird, blenden wir die Liste aus
if (!hasMatches) this.hideResults()
}
// Ein Klick auf einen Eintrag wählt ihn aus
select(event) {
const value = event.currentTarget.dataset.value
this.inputTarget.value = value
this.hideResults()
}
showResults() {
this.resultsTarget.classList.remove("hidden")
}
hideResults() {
this.resultsTarget.classList.add("hidden")
}
showAll() {
this.showResults()
this.itemTargets.forEach(item => item.classList.remove("hidden"))
}
}

View File

@@ -88,7 +88,7 @@
Item.conditions.keys.map { |cond| [Item.human_attribute_name("conditions.#{cond}"), cond] }, Item.conditions.keys.map { |cond| [Item.human_attribute_name("conditions.#{cond}"), cond] },
{}, {},
class: "py-2.5 px-3 block w-full border border-gray-300 rounded-lg text-sm bg-gray-50/50 focus:border-blue-500 focus:ring-blue-500 appearance-none pr-10" %> class: "py-2.5 px-3 block w-full border border-gray-300 rounded-lg text-sm bg-gray-50/50 focus:border-blue-500 focus:ring-blue-500 appearance-none pr-10" %>
<!-- Kleiner Custom-Pfeil rechts --> <!-- Kleiner Custom-Pfeil rechts -->
<div class="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none text-gray-400"> <div class="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none text-gray-400">
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5" /></svg> <svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5" /></svg>
@@ -103,7 +103,7 @@
<!-- ========================================================================= --> <!-- ========================================================================= -->
<div class="space-y-4"> <div class="space-y-4">
<label class="block text-sm font-semibold text-gray-800">Standort / Zuweisung festlegen</label> <label class="block text-sm font-semibold text-gray-800">Standort / Zuweisung festlegen</label>
<%= turbo_frame_tag "item_assignment_frame" do %> <%= turbo_frame_tag "item_assignment_frame" do %>
<% <%
current_type = if item.user_id.present? current_type = if item.user_id.present?
@@ -122,67 +122,85 @@
class: "py-2 px-3 text-xs font-medium rounded-md text-center transition flex items-center justify-center gap-1 #{type == 'storage' ? 'bg-white text-blue-600 shadow-sm font-semibold' : 'text-gray-600 hover:text-gray-900'}" do %> class: "py-2 px-3 text-xs font-medium rounded-md text-center transition flex items-center justify-center gap-1 #{type == 'storage' ? 'bg-white text-blue-600 shadow-sm font-semibold' : 'text-gray-600 hover:text-gray-900'}" do %>
📦 Lager 📦 Lager
<% end %> <% end %>
<%= link_to item.new_record? ? new_item_path(assignment_type: "user") : edit_item_path(item, assignment_type: "user"), <%= link_to item.new_record? ? new_item_path(assignment_type: "user") : edit_item_path(item, assignment_type: "user"),
class: "py-2 px-3 text-xs font-medium rounded-md text-center transition flex items-center justify-center gap-1 #{type == 'user' ? 'bg-white text-blue-600 shadow-sm font-semibold' : 'text-gray-600 hover:text-gray-900'}" do %> class: "py-2 px-3 text-xs font-medium rounded-md text-center transition flex items-center justify-center gap-1 #{type == 'user' ? 'bg-white text-blue-600 shadow-sm font-semibold' : 'text-gray-600 hover:text-gray-900'}" do %>
👤 Mitarbeiter 👤 Mitarbeiter
<% end %> <% end %>
<%= link_to item.new_record? ? new_item_path(assignment_type: "room") : edit_item_path(item, assignment_type: "room"), <%= link_to item.new_record? ? new_item_path(assignment_type: "room") : edit_item_path(item, assignment_type: "room"),
class: "py-2 px-3 text-xs font-medium rounded-md text-center transition flex items-center justify-center gap-1 #{type == 'room' ? 'bg-white text-blue-600 shadow-sm font-semibold' : 'text-gray-600 hover:text-gray-900'}" do %> class: "py-2 px-3 text-xs font-medium rounded-md text-center transition flex items-center justify-center gap-1 #{type == 'room' ? 'bg-white text-blue-600 shadow-sm font-semibold' : 'text-gray-600 hover:text-gray-900'}" do %>
📍 Raum 📍 Raum
<% end %> <% end %>
</div> </div>
<!-- Live-Suche (Reiner Text, keine versteckten IDs) --> <!-- Live-Suche (Mobil-optimierter Autocomplete-Ersatz für Datalist) -->
<div class="mt-4"> <div class="mt-4">
<% if type == "user" %> <% if type == "user" %>
<!-- Signalisiert Rails, dass die room_id gelöscht werden soll -->
<input type="hidden" name="item[room_id]" value=""> <input type="hidden" name="item[room_id]" value="">
<label for="item_user_name" class="block text-sm font-medium mb-1.5 text-gray-700">Mitarbeiter suchen (Vorname, Nachname oder E-Mail)...</label> <label for="item_user_name" class="block text-sm font-medium mb-1.5 text-gray-700">Mitarbeiter suchen (Vorname, Nachname)...</label>
<div class="relative">
<!-- Wir senden den Klartext-Namen an ein virtuelles Feld 'user_name' --> <!-- STIMULUS CONTAINER FÜR USER -->
<div class="relative" data-controller="autocomplete">
<input type="text" <input type="text"
id="item_user_name" id="item_user_name"
name="item[user_name]" name="item[user_name]"
list="users_datalist"
value="<%= item.user&.name %>" value="<%= item.user&.name %>"
data-autocomplete-target="input"
data-action="input->autocomplete#filter focus->autocomplete#showAll"
class="py-2.5 px-3 block w-full border border-gray-300 rounded-lg text-sm bg-white focus:border-blue-500 focus:ring-blue-500" class="py-2.5 px-3 block w-full border border-gray-300 rounded-lg text-sm bg-white focus:border-blue-500 focus:ring-blue-500"
placeholder="Tippe den Namen ein..."> placeholder="Tippe den Namen ein..."
autocomplete="off">
<datalist id="users_datalist">
<!-- DIE NEUE MOBILE ERGEBNIS-LISTE -->
<div data-autocomplete-target="results" class="hidden absolute z-50 left-0 right-0 mt-1 max-h-60 overflow-y-auto bg-white border border-gray-200 rounded-lg shadow-lg divide-y divide-gray-100">
<% User.all.order(:first_name, :last_name).each do |user| %> <% User.all.order(:first_name, :last_name).each do |user| %>
<!-- Hier nutzen wir den reinen Vor- und Nachnamen als Value --> <div data-autocomplete-target="item"
<option value="<%= user.name %>"></option> data-action="click->autocomplete#select"
data-value="<%= user.name %>"
class="px-4 py-2.5 text-sm text-gray-800 hover:bg-blue-50 cursor-pointer transition flex items-center gap-2">
<!-- Kleines Symbol für visuelles Feedback -->
<svg class="h-4 w-4 text-gray-400" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M15.75 6a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0ZM4.501 20.118a7.5 7.5 0 0 1 14.998 0A17.933 17.933 0 0 1 12 21.75c-2.676 0-5.216-.584-7.499-1.632Z" /></svg>
<%= user.name %>
</div>
<% end %> <% end %>
</datalist> </div>
</div> </div>
<% elsif type == "room" %> <% elsif type == "room" %>
<!-- Signalisiert Rails, dass die user_id gelöscht werden soll -->
<input type="hidden" name="item[user_id]" value=""> <input type="hidden" name="item[user_id]" value="">
<label for="item_room_name" class="block text-sm font-medium mb-1.5 text-gray-700">Raum suchen (Raumnummer)...</label> <label for="item_room_name" class="block text-sm font-medium mb-1.5 text-gray-700">Raum suchen (Raumnummer)...</label>
<div class="relative">
<!-- Wir senden den Klartext-Namen an ein virtuelles Feld 'room_name' --> <!-- STIMULUS CONTAINER FÜR RÄUME -->
<div class="relative" data-controller="autocomplete">
<input type="text" <input type="text"
id="item_room_name" id="item_room_name"
name="item[room_name]" name="item[room_name]"
list="rooms_datalist"
value="<%= item.room&.name %>" value="<%= item.room&.name %>"
data-autocomplete-target="input"
data-action="input->autocomplete#filter focus->autocomplete#showAll"
class="py-2.5 px-3 block w-full border border-gray-300 rounded-lg text-sm bg-white focus:border-blue-500 focus:ring-blue-500" class="py-2.5 px-3 block w-full border border-gray-300 rounded-lg text-sm bg-white focus:border-blue-500 focus:ring-blue-500"
placeholder="Tippe die Raumnummer ein..."> placeholder="Tippe die Raumnummer ein..."
autocomplete="off">
<datalist id="rooms_datalist">
<!-- DIE NEUE MOBILE ERGEBNIS-LISTE -->
<div data-autocomplete-target="results" class="hidden absolute z-50 left-0 right-0 mt-1 max-h-60 overflow-y-auto bg-white border border-gray-200 rounded-xl shadow-lg divide-y divide-gray-100">
<% Room.all.order(:name).each do |room| %> <% Room.all.order(:name).each do |room| %>
<option value="<%= room.name %>"></option> <div data-autocomplete-target="item"
data-action="click->autocomplete#select"
data-value="<%= room.name %>"
class="px-4 py-2.5 text-sm text-gray-800 hover:bg-blue-50 cursor-pointer transition flex items-center gap-2">
<svg class="h-4 w-4 text-gray-400" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M15 10.5a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" /><path stroke-linecap="round" stroke-linejoin="round" d="M19.5 10.5c0 7.142-7.5 11.25-7.5 11.25s-7.5-4.108-7.5-11.25a7.5 7.5 0 1 1 15 0Z" /></svg>
<div class="font-medium"><%= room.name %></div>
<div class="text-xs text-gray-400 font-normal ml-auto"><%= room.building %></div>
</div>
<% end %> <% end %>
</datalist> </div>
</div> </div>
<% else %> <% else %>
<!-- Hauptlager gewählt -> Beide IDs nullen -->
<input type="hidden" name="item[user_id]" value=""> <input type="hidden" name="item[user_id]" value="">
<input type="hidden" name="item[room_id]" value=""> <input type="hidden" name="item[room_id]" value="">
<div class="p-4 rounded-lg border border-dashed border-gray-200 bg-gray-50/50 text-center text-xs text-gray-500"> <div class="p-4 rounded-lg border border-dashed border-gray-200 bg-gray-50/50 text-center text-xs text-gray-500">
@@ -190,6 +208,7 @@
</div> </div>
<% end %> <% end %>
</div> </div>
<% end %> <% end %>
</div> </div>