Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
10f0132
Assemblies einführen
Mar 19, 2025
31093ab
Default-Sortierung für Assemblies per YAML-Konfiguration einführen
Mar 20, 2025
39d3c49
configuration.md: Info für Default-Sortierung zu Assemblies einfügen
Mar 20, 2025
618f0ed
Anpassungen aus Benutzersicht, um den Fokus auf die reine Baugruppen-…
Apr 2, 2025
87d2f06
Übersetzung zu "assembly.bom_import.template.kicad_pcbnew.table" anpa…
Apr 3, 2025
228f6e5
Assembly Konfiguration in BOM ausblenden, wenn bisher keine Zuordnung…
Apr 4, 2025
d5389ac
PartController -> new Methode Variablennamen korrigieren
Apr 11, 2025
4653065
JSON Importer mit Minimaldaten weiterentwickeln. Validierung mit Viol…
Apr 11, 2025
725c739
Anpassungen zu JSON Importer vornehmen.
Apr 16, 2025
ca2110a
Umstellung Migrationen bzgl. Multi-Plattform-Support.
Apr 22, 2025
35de4c4
Projekt BOM-Konfiguration um Assemblies bereinigen.
Jun 17, 2025
902f6dc
SQL-Formatierung in Migration verbessern
Jun 17, 2025
47ec342
Projekt-Importer um JSON/CSV Importer analog zu Assemblies erweitern
Jun 24, 2025
90678b4
Part-Übersicht sowie -Detailansicht um Assembly Information erweitern
Jun 24, 2025
8cbbbb3
Assembly um IPN-Eingabemöglichkeit und Automatismus zur Name-Angabe e…
Jun 26, 2025
77bb5f3
Baugruppen Stückliste um referenzierte Baugruppe erweitern
Jul 3, 2025
4deab8e
AssemblyBomEntriesDataTable anpassen
Jul 3, 2025
b7cb51d
Assembly Listenübersicht umsetzen
Jul 3, 2025
6dd9152
Assembly Konstanten in .env einfügen bzw. anpassen
Jul 3, 2025
657f9c9
Assembly getReferencedAssemblies korrigieren
Jul 3, 2025
6f8f745
Füge Unterstützung für Datenquellen-Synonyme hinzu.
Jul 7, 2025
d26585b
Füge Validierung für zyklische Baugruppenreferenzen hinzu
Jul 8, 2025
360f4f0
Anpassungen aus Analyse vornehmen
Jul 17, 2025
40778c4
Stücklisten beim Löschen: Markieren von referenzierten Baugruppen als…
Jul 18, 2025
db9c766
Migration: Spaltenname korrigieren
Jul 21, 2025
64c9773
BOMImporter: Verbesserung des CSV-Parsing
Jul 22, 2025
15ad64c
Reihenfolge der Tabs in Baugruppenansicht korrigiert
Jul 22, 2025
08a3333
BOMImporter und AssemblyCycleValidator: Verbesserte Import-Logik
Jul 22, 2025
771c6c5
BOMValidator: Validierung für rekursive Baugruppen-Eintragsprüfung er…
Jul 24, 2025
eb6e590
Tabs und BOMImporter: Verbesserte Anzeige und Validierung
Jul 24, 2025
c08c2f0
BOMImporter und AssemblyBomEntriesDataTable: Mountnames hinzugefügt
Jul 28, 2025
1c45054
Füge Option für lesbares CSV beim Export hinzu (APS-3)
Sep 8, 2025
d4fe88e
Rebase auf Part-DB v2.1.2
Sep 10, 2025
b782d97
Assemblies einführen
Mar 19, 2025
f666327
Default-Sortierung für Assemblies per YAML-Konfiguration einführen
Mar 20, 2025
59a2669
configuration.md: Info für Default-Sortierung zu Assemblies einfügen
Mar 20, 2025
bc0df76
Anpassungen aus Benutzersicht, um den Fokus auf die reine Baugruppen-…
Apr 2, 2025
6125708
Übersetzung zu "assembly.bom_import.template.kicad_pcbnew.table" anpa…
Apr 3, 2025
3ed01f1
Assembly Konfiguration in BOM ausblenden, wenn bisher keine Zuordnung…
Apr 4, 2025
29398e2
PartController -> new Methode Variablennamen korrigieren
Apr 11, 2025
762fee5
JSON Importer mit Minimaldaten weiterentwickeln. Validierung mit Viol…
Apr 11, 2025
304156c
Anpassungen zu JSON Importer vornehmen.
Apr 16, 2025
7970f04
Umstellung Migrationen bzgl. Multi-Plattform-Support.
Apr 22, 2025
3b7890c
Projekt BOM-Konfiguration um Assemblies bereinigen.
Jun 17, 2025
7bf83de
SQL-Formatierung in Migration verbessern
Jun 17, 2025
dd9bf56
Projekt-Importer um JSON/CSV Importer analog zu Assemblies erweitern
Jun 24, 2025
b3e2986
Part-Übersicht sowie -Detailansicht um Assembly Information erweitern
Jun 24, 2025
c4f34ef
Assembly um IPN-Eingabemöglichkeit und Automatismus zur Name-Angabe e…
Jun 26, 2025
ca11898
Baugruppen Stückliste um referenzierte Baugruppe erweitern
Jul 3, 2025
47f2801
AssemblyBomEntriesDataTable anpassen
Jul 3, 2025
195f190
Assembly Listenübersicht umsetzen
Jul 3, 2025
669ad37
Assembly Konstanten in .env einfügen bzw. anpassen
Jul 3, 2025
e1c6b42
Assembly getReferencedAssemblies korrigieren
Jul 3, 2025
1861051
Füge Unterstützung für Datenquellen-Synonyme hinzu.
Jul 7, 2025
9bf1d2b
Füge Validierung für zyklische Baugruppenreferenzen hinzu
Jul 8, 2025
3d4f55d
Erweiterungstätigkeiten zur IPN-Vorschlagsliste anhand von Präfixen a…
Apr 1, 2025
dee090f
Umstellung Migrationen bzgl. Multi-Plattform-Support.
Apr 23, 2025
22ae4e5
Postgre Statements integrieren
Jun 17, 2025
01915f6
SQL-Formatierung in Migration verbessern
Jun 17, 2025
13840c4
Erweitere IPN-Suggest um Bauteilbeschreibung.
Jul 9, 2025
0c6bbef
Benutzerdefinierten Bauteilstatus einführen
Mar 24, 2025
f94fbe8
PartCustomStateController hinzufügen
Mar 24, 2025
76d6aff
Umstellung Migrationen bzgl. Multi-Plattform-Support.
Apr 23, 2025
ad3be39
Postgre Statements integrieren
Jun 17, 2025
6b4d7f1
Semikolon in Migration entfernen
Jun 17, 2025
a27f59d
Anpassungen aus Analyse vornehmen
Jul 17, 2025
73f1fa9
Migration für PartCustomState aktualisieren
Jul 17, 2025
04ad457
Anpassungen aus Analyse vornehmen
Jul 17, 2025
0e542cb
Anpassung Migration aus Zusammenführung Feature Branches
Jul 17, 2025
4845da7
Stücklisten beim Löschen: Markieren von referenzierten Baugruppen als…
Jul 18, 2025
bdb8ae0
Migration: Spaltenname korrigieren
Jul 21, 2025
f5f3435
BOMImporter: Verbesserung des CSV-Parsing
Jul 22, 2025
5deaad2
Reihenfolge der Tabs in Baugruppenansicht korrigiert
Jul 22, 2025
6ed084f
BOMImporter und AssemblyCycleValidator: Verbesserte Import-Logik
Jul 22, 2025
31b1829
BOMValidator: Validierung für rekursive Baugruppen-Eintragsprüfung er…
Jul 24, 2025
412818a
Tabs und BOMImporter: Verbesserte Anzeige und Validierung
Jul 24, 2025
e21ed95
BOMImporter und AssemblyBomEntriesDataTable: Mountnames hinzugefügt
Jul 28, 2025
9e8eb93
IPN-Validierung für Parts überarbeiten
Aug 25, 2025
b2f8e21
Füge Option für lesbares CSV beim Export hinzu (APS-3)
Sep 8, 2025
abbadb9
Merge branch 'feature/create-assemblies' into feature/all-features
Sep 11, 2025
6bb20fb
Entferne Projektbezogene Logik bzw. Verweise auf Baugruppen, da nicht…
Sep 12, 2025
7befa53
Erweitere Exportfunktion um lesbare BOM-Option (PDF-Ausgabe).
Sep 15, 2025
bcd5da9
PDF Exportmöglichkeit zunächst nur anzeigen, wenn es sich um ein Asse…
Sep 17, 2025
8717a07
Anzeige und Logik für zugehörige Build-Parts sowie Build-Tab bei Asse…
Sep 19, 2025
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
8 changes: 7 additions & 1 deletion .env
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ DATABASE_EMULATE_NATURAL_SORT=0
# This must end with a slash!
DEFAULT_URI="https://partdb.changeme.invalid/"

# Use an %%ipn%% placeholder in the name of a assembly. Placeholder is replaced with the ipn input while saving.
CREATE_ASSEMBLY_USE_IPN_PLACEHOLDER_IN_NAME=0
# Set this to 0 to allow to enter already available IPN. In this case a unique increment is appended to the user input.
ENFORCE_UNIQUE_IPN=1
# Define the number of digits used for the incremental numbering of parts in the IPN (Internal Part Number) autocomplete system.
AUTOCOMPLETE_PART_DIGITS=4

###################################################################################
# Email settings
###################################################################################
Expand All @@ -60,7 +67,6 @@ ERROR_PAGE_ADMIN_EMAIL=''
# If this is set to true, solutions to common problems are shown on error pages. Disable this, if you do not want your users to see them...
ERROR_PAGE_SHOW_HELP=1


###################################################################################
# SAML Single sign on-settings
###################################################################################
Expand Down
70 changes: 70 additions & 0 deletions assets/controllers/elements/assembly_select_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
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() {

let settings = {
allowEmptyOption: true,
plugins: ['dropdown_input', 'clear_button'],
searchField: ["name", "description", "category", "footprint"],
valueField: "id",
labelField: "name",
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) {
const base_url = this.element.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()
});
};


this._tomSelect = new TomSelect(this.element, settings);
//this._tomSelect.clearOptions();
}
}

disconnect() {
super.disconnect();
//Destroy the TomSelect instance
this._tomSelect.destroy();
}
}
9 changes: 9 additions & 0 deletions assets/controllers/elements/ckeditor_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,15 @@ export default class extends Controller {
editor_div.classList.add(...new_classes.split(","));
}

// Automatic synchronization of source input
editor.model.document.on("change:data", () => {
editor.updateSourceElement();

// Dispatch the input event for further treatment
const event = new Event("input");
this.element.dispatchEvent(event);
});

//This return is important! Otherwise we get mysterious errors in the console
//See: https://github.com/ckeditor/ckeditor5/issues/5897#issuecomment-628471302
return editor;
Expand Down
250 changes: 250 additions & 0 deletions assets/controllers/elements/ipn_suggestion_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
import { Controller } from "@hotwired/stimulus";
import "../../css/components/autocomplete_bootstrap_theme.css";

export default class extends Controller {
static targets = ["input"];
static values = {
partId: Number,
partCategoryId: Number,
partDescription: String,
suggestions: Object,
commonSectionHeader: String, // Dynamic header for common Prefixes
partIncrementHeader: String, // Dynamic header for new possible part increment
suggestUrl: String,
};

connect() {
this.configureAutocomplete();
this.watchCategoryChanges();
this.watchDescriptionChanges();
}

templates = {
commonSectionHeader({ title, html }) {
return html`
<section class="aa-Source">
<div class="aa-SourceHeader">
<span class="aa-SourceHeaderTitle">${title}</span>
<div class="aa-SourceHeaderLine"></div>
</div>
</section>
`;
},
partIncrementHeader({ title, html }) {
return html`
<section class="aa-Source">
<div class="aa-SourceHeader">
<span class="aa-SourceHeaderTitle">${title}</span>
<div class="aa-SourceHeaderLine"></div>
</div>
</section>
`;
},
list({ html }) {
return html`
<ul class="aa-List" role="listbox"></ul>
`;
},
item({ suggestion, description, html }) {
return html`
<li class="aa-Item" role="option" data-suggestion="${suggestion}" aria-selected="false">
<div class="aa-ItemWrapper">
<div class="aa-ItemContent">
<div class="aa-ItemIcon aa-ItemIcon--noBorder">
<svg viewBox="0 0 24 24" fill="currentColor">
<path d="M12 21c4.971 0 9-4.029 9-9s-4.029-9-9-9-9 4.029-9 9 4.029 9 9 9z"></path>
</svg>
</div>
<div class="aa-ItemContentBody">
<div class="aa-ItemContentTitle">${suggestion}</div>
<div class="aa-ItemContentDescription">${description}</div>
</div>
</div>
</div>
</li>
`;
},
};

configureAutocomplete() {
const inputField = this.inputTarget;
const commonPrefixes = this.suggestionsValue.commonPrefixes || [];
const prefixesPartIncrement = this.suggestionsValue.prefixesPartIncrement || [];
const commonHeader = this.commonSectionHeaderValue;
const partIncrementHeader = this.partIncrementHeaderValue;

if (!inputField || (!commonPrefixes.length && !prefixesPartIncrement.length)) return;

// Check whether the panel should be created at the update
if (this.isPanelInitialized) {
const existingPanel = inputField.parentNode.querySelector(".aa-Panel");
if (existingPanel) {
// Only remove the panel in the update phase

existingPanel.remove();
}
}

// Create panel
const panel = document.createElement("div");
panel.classList.add("aa-Panel");
panel.style.display = "none";

// Create panel layout
const panelLayout = document.createElement("div");
panelLayout.classList.add("aa-PanelLayout", "aa-Panel--scrollable");

// Section for prefixes part increment
if (prefixesPartIncrement.length) {
const partIncrementSection = document.createElement("section");
partIncrementSection.classList.add("aa-Source");

const partIncrementHeaderHtml = this.templates.partIncrementHeader({
title: partIncrementHeader,
html: String.raw,
});
partIncrementSection.innerHTML += partIncrementHeaderHtml;

const partIncrementList = document.createElement("ul");
partIncrementList.classList.add("aa-List");
partIncrementList.setAttribute("role", "listbox");

prefixesPartIncrement.forEach((prefix) => {
const itemHTML = this.templates.item({
suggestion: prefix.title,
description: prefix.description,
html: String.raw,
});
partIncrementList.innerHTML += itemHTML;
});

partIncrementSection.appendChild(partIncrementList);
panelLayout.appendChild(partIncrementSection);
}

// Section for common prefixes
if (commonPrefixes.length) {
const commonSection = document.createElement("section");
commonSection.classList.add("aa-Source");

const commonSectionHeader = this.templates.commonSectionHeader({
title: commonHeader,
html: String.raw,
});
commonSection.innerHTML += commonSectionHeader;

const commonList = document.createElement("ul");
commonList.classList.add("aa-List");
commonList.setAttribute("role", "listbox");

commonPrefixes.forEach((prefix) => {
const itemHTML = this.templates.item({
suggestion: prefix.title,
description: prefix.description,
html: String.raw,
});
commonList.innerHTML += itemHTML;
});

commonSection.appendChild(commonList);
panelLayout.appendChild(commonSection);
}

panel.appendChild(panelLayout);
inputField.parentNode.appendChild(panel);

inputField.addEventListener("focus", () => {
panel.style.display = "block";
});

inputField.addEventListener("blur", () => {
setTimeout(() => {
panel.style.display = "none";
}, 100);
});

// Selection of an item
panelLayout.addEventListener("mousedown", (event) => {
const target = event.target.closest("li");

if (target) {
inputField.value = target.dataset.suggestion;
panel.style.display = "none";
}
});

this.isPanelInitialized = true;
};

watchCategoryChanges() {
const categoryField = document.querySelector('[data-ipn-suggestion="categoryField"]');
const descriptionField = document.querySelector('[data-ipn-suggestion="descriptionField"]');
this.previousCategoryId = Number(this.partCategoryIdValue);

if (categoryField) {
categoryField.addEventListener("change", () => {
const categoryId = Number(categoryField.value);
const description = String(descriptionField.value);

// Check whether the category has changed compared to the previous ID
if (categoryId !== this.previousCategoryId) {
this.fetchNewSuggestions(categoryId, description);
this.previousCategoryId = categoryId;
}
});
}
}

watchDescriptionChanges() {
const categoryField = document.querySelector('[data-ipn-suggestion="categoryField"]');
const descriptionField = document.querySelector('[data-ipn-suggestion="descriptionField"]');
this.previousDescription = String(this.partDescriptionValue);

if (descriptionField) {
descriptionField.addEventListener("input", () => {
const categoryId = Number(categoryField.value);
const description = String(descriptionField.value);

// Check whether the description has changed compared to the previous one
if (description !== this.previousDescription) {
this.fetchNewSuggestions(categoryId, description);
this.previousDescription = description;
}
});
}
}

fetchNewSuggestions(categoryId, description) {
const baseUrl = this.suggestUrlValue;
const partId = this.partIdValue;
const truncatedDescription = description.length > 150 ? description.substring(0, 150) : description;
const encodedDescription = this.base64EncodeUtf8(truncatedDescription);
const url = `${baseUrl}?partId=${partId}&categoryId=${categoryId}&description=${encodedDescription}`;

fetch(url, {
method: "GET",
headers: {
"Content-Type": "application/json",
"Accept": "application/json",
},
})
.then((response) => {
if (!response.ok) {
throw new Error(`Error when calling up the IPN-suggestions: ${response.status}`);
}
return response.json();
})
.then((data) => {
this.suggestionsValue = data;
this.configureAutocomplete();
})
.catch((error) => {
console.error("Errors when loading the new IPN-suggestions:", error);
});
};

base64EncodeUtf8(text) {
const utf8Bytes = new TextEncoder().encode(text);
return btoa(String.fromCharCode(...utf8Bytes));
};
}
2 changes: 1 addition & 1 deletion assets/controllers/elements/part_select_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default class extends Controller {

let settings = {
allowEmptyOption: true,
plugins: ['dropdown_input'],
plugins: ['dropdown_input', 'clear_button'],
searchField: ["name", "description", "category", "footprint"],
valueField: "id",
labelField: "name",
Expand Down
Loading