Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
97e463c
Füge Baugruppen hinzu
Oct 17, 2025
36e25ad
Leerzeilen beim Import überspringen
Dec 12, 2025
be75eaf
AssemblyBomEntriesDataTable: Sortierfelder für Beschreibung und Desig…
Dec 12, 2025
081e98d
Assembly-Filter und -Tab ergänzen
Dec 12, 2025
70802d5
Assembly-BOM-Einträge und Übersetzungen aktualisieren
Dec 12, 2025
d57e420
EntityExporter: Konstruktor-Definition bereinigen
Dec 12, 2025
7a0b716
EntityExporter: Konstruktor-Deklaration korrigieren; Dienstkonfigurat…
Dec 12, 2025
4911b5b
services.yaml: AssemblyPartAggregator registrieren
Dec 12, 2025
74513b7
Unterstützung für Projekt- und Baugruppensuche zum QuickSearch-Sugges…
Feb 12, 2026
d67e930
Statistik-Bereich um Tab für Projekte/Baugruppen erweitern
Feb 12, 2026
b08df9b
Cleanup-Logik für Baugruppen und BOM-Einträge im Statistik-Bereich üb…
Feb 13, 2026
4b6018f
statistics_assembly_controller bzgl. Alert-Ausgabe aktualisieren
Feb 16, 2026
5b95513
Typdefinitionen in BOMImporter anpassen
Feb 16, 2026
e4cd243
Übergabe an stringToBOMEntries in BOMImporter anpassen
Feb 16, 2026
dde91ff
Überprüfung auf Tabellenexistenz in Migration hinzufügen.
Mar 23, 2026
ca6254c
Datasource-spezifische Suche für Projects/Assemblies sowie Parts umse…
Apr 1, 2026
a126a8f
Unterstützung für IPN-Suche in der BOM-Konfiguration bei Part- und As…
Apr 8, 2026
66c9057
Status- und Herstellerinformationen in Readable Assembly-Exports hinz…
Apr 14, 2026
563d39f
Turbo-Kompatibilität verbessern.
Apr 16, 2026
ad3fd27
Fehlerbehandlung für Update-Checker verbessern.
Apr 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 0 additions & 91 deletions Makefile

This file was deleted.

79 changes: 79 additions & 0 deletions assets/controllers/elements/assembly_select_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import {Controller} from "@hotwired/stimulus";

import "tom-select/dist/css/tom-select.bootstrap5.css";
import '../../css/components/tom-select_extensions.css';
import TomSelect from "tom-select";
import {marked} from "marked";

export default class extends Controller {
_tomSelect;

connect() {

//Check if tomselect is inside an modal and do not attach the dropdown to body in that case (as it breaks the modal)
let dropdownParent = "body";
if (this.element.closest('.modal')) {
dropdownParent = null
}

let settings = {
allowEmptyOption: true,
plugins: ['dropdown_input', this.element.required ? null : 'clear_button'],
searchField: ["name", "description", "category", "footprint", "ipn"],
valueField: "id",
labelField: "name",
dropdownParent: dropdownParent,
preload: "focus",
render: {
item: (data, escape) => {
return '<span>' + (data.image ? "<img style='height: 1.5rem; margin-right: 5px;' ' src='" + data.image + "'/>" : "") + escape(data.name) + '</span>';
},
option: (data, escape) => {
if(data.text) {
return '<span>' + escape(data.text) + '</span>';
}

let tmp = '<div class="row m-0">' +
"<div class='col-2 p-0 d-flex align-items-center' style='max-width: 80px;'>" +
(data.image ? "<img class='typeahead-image' src='" + data.image + "'/>" : "") +
"</div>" +
"<div class='col-10'>" +
'<h6 class="m-0">' + escape(data.name) + '</h6>' +
(data.description ? '<p class="m-0">' + marked.parseInline(data.description) + '</p>' : "") +
(data.category ? '<p class="m-0"><span class="fa-solid fa-tags fa-fw"></span> ' + escape(data.category) : "");

return tmp + '</p>' +
'</div></div>';
}
}
};


if (this.element.dataset.autocomplete || this.element.querySelector('[data-autocomplete]')) {
const autocompleteElement = this.element.dataset.autocomplete ? this.element : this.element.querySelector('[data-autocomplete]');
const base_url = autocompleteElement.dataset.autocomplete;
settings.valueField = "id";
settings.load = (query, callback) => {
const url = base_url.replace('__QUERY__', encodeURIComponent(query));

fetch(url)
.then(response => response.json())
.then(json => {callback(json);})
.catch(() => {
callback()
});
};


const targetElement = this.element instanceof HTMLInputElement || this.element instanceof HTMLSelectElement ? this.element : this.element.querySelector('select, input');
this._tomSelect = new TomSelect(targetElement, settings);
//this._tomSelect.clearOptions();
}
}

disconnect() {
super.disconnect();
//Destroy the TomSelect instance
this._tomSelect.destroy();
}
}
94 changes: 94 additions & 0 deletions assets/controllers/elements/bom_name_sync_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import {Controller} from "@hotwired/stimulus";

export default class extends Controller {
static targets = ["part", "assembly", "name"];

connect() {
this.updatePlaceholder();
// Give TomSelect some time to initialize and set values
setTimeout(() => this.updatePlaceholder(), 100);
setTimeout(() => this.updatePlaceholder(), 500);
}

updatePlaceholder() {
const partSelect = this.hasPartTarget ? this.partTarget.querySelector('select, input') : null;
const assemblySelect = this.hasAssemblyTarget ? this.assemblyTarget.querySelector('select, input') : null;
const nameInput = this.hasNameTarget ? this.nameTarget : null;

if (!nameInput) return;

let selectedName = "";

// Helper to get name from tomselect
const getNameFromTS = (el) => {
if (el && el.tomselect) {
const val = el.tomselect.getValue();
if (val) {
const data = el.tomselect.options[val];
if (data && data.name) return data.name;
}
}
// Fallback for raw select
if (el && el.value && el.options && el.selectedIndex >= 0) {
return el.options[el.selectedIndex].text;
}
return "";
};

selectedName = getNameFromTS(partSelect);

if (!selectedName) {
selectedName = getNameFromTS(assemblySelect);
}

if (selectedName) {
nameInput.placeholder = selectedName;
if (nameInput.value === "") {
nameInput.style.opacity = "0.6";
} else {
nameInput.style.opacity = "1";
}
} else {
nameInput.placeholder = nameInput.dataset.originalPlaceholder || "";
nameInput.style.opacity = "1";
}
}

// This method will be called via action when a change occurs
sync(event) {
// Handle mutual exclusion: if a part is selected, clear the assembly (and vice-versa)
// We identify which field was changed by looking at the event target
const changedElement = event.target;
const partSelect = this.hasPartTarget ? this.partTarget.querySelector('select, input') : null;
const assemblySelect = this.hasAssemblyTarget ? this.assemblyTarget.querySelector('select, input') : null;

// If part was changed and has a value, clear assembly
if (partSelect && (changedElement === partSelect || partSelect.contains(changedElement))) {
const val = partSelect.tomselect ? partSelect.tomselect.getValue() : partSelect.value;
if (val && assemblySelect) {
if (assemblySelect.tomselect) {
assemblySelect.tomselect.clear(true); // true to silent event to avoid loops
} else {
assemblySelect.value = "";
}
}
}

// If assembly was changed and has a value, clear part
if (assemblySelect && (changedElement === assemblySelect || assemblySelect.contains(changedElement))) {
const val = assemblySelect.tomselect ? assemblySelect.tomselect.getValue() : assemblySelect.value;
if (val && partSelect) {
if (partSelect.tomselect) {
partSelect.tomselect.clear(true); // true to silent event to avoid loops
} else {
partSelect.value = "";
}
}
}

// Delay slightly to allow TomSelect to update its internal state if needed
setTimeout(() => {
this.updatePlaceholder();
}, 100);
}
}
Loading