diff --git a/aktionsKonsole.js b/aktionsKonsole.js new file mode 100644 index 0000000..7cb26a4 --- /dev/null +++ b/aktionsKonsole.js @@ -0,0 +1,134 @@ +/** + * Der Bereich in dem der aktuell geladene/bearbeitete Eintrag angezeigt wird. + * @returns {Component} + */ +function valueRow() { + return builder.row() + .addStyleClass("current-entry-area") + .modifier( + new Modifier() + .fillMaxWidth() + .background(Colors.gold_1) + .clip(Shapes.RoundedCorner.all(6)) + .padding(6) + ) +} + +/** + * + * @param {HTMLElement} entryElement + * @param {HTMLElement} to das Elternelement in welches das Eintragselement verschoben werden soll + * @returns {HTMLElement} + */ +function moveEntry(entryElement, to) { + entryElement.remove(); + [...entryElement.querySelectorAll('.material-icons')] + .forEach(e => { + e.setAttribute("hidden", "hidden"); + e.setAttribute("visibility", "hidden"); + e.style.visibility = "hidden"; + }); + + to.insertAdjacentElement( + "afterbegin", + entryElement + ); + return entryElement; +} + +function moveCurrent(reverse = false) { + let currentArea = document.querySelector('div.current-entry-area'); + if (currentArea.children.length === 0) { + return + } + + let current = currentArea.children[0]; + return moveEntry( + current, + document.querySelector('div.plist' + (reverse + ? '-upcoming' + : '-done' + )) + ); +} + +function loadNext() { + moveCurrent(); + let upcomingArea = document.querySelector('div.plist-upcoming'); + if (upcomingArea.children.length === 0) { + return + } + let upcoming = upcomingArea.children[0]; + + upcoming = moveEntry( + upcoming, + document.querySelector('div.current-entry-area') + ); + + [...upcoming.querySelectorAll('.material-icons')] + .forEach(e => { + e.removeAttribute("hidden"); + e.removeAttribute("visibility"); + e.style.visibility = ""; + }); +} + +function loadPrev() { + moveCurrent(true); + let prevArea = document.querySelector('div.plist-done'); + if (prevArea.children.length === 0) { + return + } + let prev = prevArea.children[0]; + + prev = moveEntry( + prev, + document.querySelector('div.current-entry-area') + ); + + [...prev.querySelectorAll('.material-icons')] + .forEach(e => { + e.removeAttribute("hidden"); + e.removeAttribute("visibility"); + e.style.visibility = ""; + }); +} + +function aktionsKonsole(modifier = new Modifier()) { + return builder.column() + .modifier(componentModifiers.majorArea) + .modifier( + new Modifier() + .fillMaxWidth() + .background(colorModifiers.active.secondary) + .padding(32) + ) + .modifier(modifier) + .addStyleClass("area") + .addStyleClass("area-aktiv") + .childContext([ + builder.div() + .modifier( + new Modifier() + .fillMaxSize() + .margin().bottom(6) + ) + .alignment(Alignment.SPACE_EVENLY) + .arrangement(Arrangement.SPACE_EVENLY) + .childContext([ + builder.button().text("NEXT") + .addEventListener( + "click", + loadNext + ) + , + builder.button().text("PREV") + .addEventListener( + "click", + loadPrev + ) + ]) + , + valueRow() + ]) +} diff --git a/comutingArea.js b/comutingArea.js new file mode 100644 index 0000000..66dcdbd --- /dev/null +++ b/comutingArea.js @@ -0,0 +1,36 @@ + +/** + * Ein Knopf oder Kippschalter um den Klickselect zu (de-)aktivieren. + * Wenn aktiviert kann werden in einem generiertem feld, + * die angeklickten Felder referenziert. + */ +function clickFormulateToggle() { + return builder.span() +} + + +function infoDisplayField(){ + return builder.column() + .childContext([ + builder.inputTags.checkbox({"name": "use_field"}) + , + builder.row() + .childContext([ + builder.inputTags.text() + , + builder.extensions.iconMIO(ICONS.EDIT) + ]) + ]) +} + +function computedArea() { + return builder.row() + .overflow(false, true) + .modifier( + new Modifier() + .padding(12) + ) + .childContext([ + clickFormulateToggle() + ]) +} \ No newline at end of file diff --git a/content.js b/content.js new file mode 100644 index 0000000..4f9f687 --- /dev/null +++ b/content.js @@ -0,0 +1,90 @@ +const listRow = function (content) { + return builder.tableRow() + .componentChildren([ + builder.tableCell().text("1"), + builder.tableCell().text("Info 1"), + builder.tableCell().text("Info 2"), + builder.tableCell().text("Info 3") + ]) +} + +function listColumn(colText, bckColor, id) { + return builder.column() + .setAttribute("id", id) + .alignment(Alignment.CENTER) + .modifier( + new Modifier() + .fillMaxWidth(0.497) + .background(bckColor) + .clip(Shapes.RoundedCorner.all(15)) + ) + .componentChildren([ + builder.header(2) + .text(colText) + .alignment(Alignment.CENTER) + .chainModifier() + .padding(new Siding().all(32)) + , + builder.table() + .chainModifier() + .fillMaxWidth() + .linkPadding().all(16) + .componentChild( + builder.tableBody() + .addStyleClass("list-table-body") + .componentChild( + listRow() + ) + ) + ]) +} + +const currentElementDisplay = function (fields) { + return builder.column() + .modifier( + new Modifier() + .fillMaxWidth() + ) + .componentChildren([ + builder.row() + .componentChildren([ + builder.button() + .text("Load Next") + .setEvent(CommonEvents.ONCLICK, "rotateCurrentElement") + .chainModifier() + .fillMaxWidth(0.2) + , + builder.input({ "type": "number" }) + ]) + , + builder.table() + .setAttribute("hidden", "true") + .componentChild(builder.tableBody()) + ]) +} + +const currentSection = function () { + return builder.column() + .setAttribute("id", "current") + .alignment(Alignment.CENTER) + .chainModifier() + .fillMaxWidth() + .linkPadding().vertical(8) + .componentChild( + builder.column() + .chainModifier() + .fillMaxWidth() + .background(MaterialFiveHundredlColors.GOLD) + .clip(Shapes.RoundedCorner.all(15)) + .linkPadding().horizontal(32).vertical(32) + .componentChildren([ + builder.header(2) + .text("Current Element") + .chainModifier() + .linkPadding() + .bottom(8) + , + currentElementDisplay() + ]) + ) +} diff --git a/dataImportArea.js b/dataImportArea.js new file mode 100644 index 0000000..5adf7ac --- /dev/null +++ b/dataImportArea.js @@ -0,0 +1,262 @@ +/** + * Im Grunde eine gekapselte Textarea + * Das Feld in welches roher Datentext eingefügt wird. + * Wir in der Zukunft eventuell in einen sepperaten View verschoben. + * + * @param {Modifier} modifier + * @returns {Component} + */ +function rawTextImportArea(modifier = new Modifier()) { + return builder.div() + .modifier( + new Modifier() + .fillMaxSize() + .padding(6) + ) + .modifier(modifier) + .setAttribute("id", "import-area") + .chainChild() + .textarea() + .name("import-textarea") + .modifier( + new Modifier() + .clip(Shapes.RoundedCorner.all(12)) + .fillMaxSize() + .padding(6) + .dimensions().height(120) + ) +} + +class ProcessingRule { + use; + value; + constructor(display, id, type, placeholder, desc, isFlagged) { + this.display = display; + this.id = id; + this.type = type; + this.placeholder = placeholder; + this.desc = desc; + this.isFlagged = isFlagged; + } + + _getCorrespondingComponent() { + return document.getElementById(this.id) + } + + _getToBeUsedState() { + return !this.isFlagged + || this._getCorrespondingComponent() + .querySelector('input[name="is_to_be_used"]').checked === true; + } + + _getInputTagValue() { + let container = this._getCorrespondingComponent() + .querySelector('.value-container'); + if (this.type === "checkbox") { + return container.checked === true; + } + + if (container.value === "") { + return this.placeholder; + } + return container.value; + } + + updateFromUi() { + this.use = this._getToBeUsedState(); + this.value = this._getInputTagValue(); + } +} + + + +/** + * Die Überschrift und unter der Bedingung rule.isFlagged auch eine checkbox die bestimmt + * ob die ProcessingRule genutzt werden soll. + * + * @param {ProcessingRule} rule + * @returns {Component} + */ +function prdUseboxAndLabel(rule) { + return builder.row() + .arrangement(Arrangement.CENTER) + .childContext([ + builder.inputTags.checkbox({ "name": "is_to_be_used" }) + .modifier(new Modifier().margin().right(6)) + .hiddenByCondition(!rule.isFlagged) + , + builder.label() + .setAttribute("for", rule.id) + .text(rule.display) + ]); +} + +/** + * Componente die eine ProcessingRule definiert. + * - Ob sie verwendet werden soll + * - welche Standartwert erwartet/verwendet werden soll + * + * @param {ProcessingRule} rule + * @returns {Component} + */ +function processingRuleDefineComponent(rule, index, arr, modifier = new Modifier()) { + return builder.column() + .isHigherComponent() + .setAttribute("id", rule.id) + .title(rule.desc) + .modifier(modifier) + .arrangement(Arrangement.CENTER) + .childContext([ + prdUseboxAndLabel(rule) + , + builder.span() + .modifier(new Modifier().fillMaxSize()) + .arrangement(Arrangement.CENTER) + .chainChild() + .input(rule.type) + .addStyleClass("value-container") + .setAttribute("placeholder", rule.placeholder) + .modifier( + new Modifier() + .padding(4) + .setStyleRule("field-sizing", "content") + ) + .apply(function (el) { + if (!rule.isFlagged) { + el.setAttribute("deactivated", ""); + } + }) + ]) +} + + +/** + * Bereich der ProcessingRules + * + * @param {ProcessingRule} rule + * @returns {Component} + */ +function processingRulesArea(modifier = new Modifier()) { + return builder.row() + .modifier( + new Modifier() + .fillMaxWidth() + .padding(8) + ) + .modifier(modifier) + .arrangement(Arrangement.SPACE_EVENLY) + .childContext( + Object.keys(processingRules) + .map(def => processingRules[def]) + .map(processingRuleDefineComponent) + ) +} + +let importedData = []; + +/** + * Funktion keine Komponente + */ +function importEntries() { + let rawtext = document.getElementById("import-area") + .querySelector("textarea").value; + let prules = Object.keys(processingRules) + .reduce((a, c) => { + let r = processingRules[c]; + let pr = new ProcessingRule(r.display, r.id, r.type, r.placeholder, r.desc, r.isFlagged); + pr.updateFromUi(); + a[c] = pr; + return a; + }, processingRules); + + let lines = rawtext.split(prules.line_splitter.value); + let splittedLines = lines + .filter(e => e.trim() !== ""); + + console.log(splittedLines); + + if (prules.entry_splitter.use) { + console.log(prules.entry_splitter.value); + splittedLines = splittedLines + .map(line => line.split(prules.entry_splitter.value)) + } else { + splittedLines = splittedLines + .map(e => [e]); + } + + importedData = splittedLines; + console.log(splittedLines); + + let upcoming = document.querySelector(".plist-upcoming"); + + splittedLines = splittedLines.map((e, i) => listEntryRow(e, i).generate().compext); + + splittedLines + .forEach(element => { + upcoming.insertAdjacentElement( + "beforeend", + element + ) + }); +} + + +/** + * Bereich mit ausführbaren Elementen + * + * @param {ProcessingRule} rule + * @returns {Component} + */ +function commandRow(modifier = new Modifier()) { + return builder.row() + .modifier(modifier) + .childContext([ + builder.button() + .text("Import") + .addStyleClass("import-trigger") + .addEventListener( + "click", + importEntries + ) + ]) +} + + +/** + * + * + * @param {ProcessingRule} rule + * @returns {Component} + */ +function importArea(modifier = new Modifier()) { + return builder.column() + .addStyleClass("area") + .addStyleClass("area-import") + .modifier( + new Modifier() + .fillMaxWidth() + .padding(12) + .background(colorModifiers.import.secondary) + ) + .modifier(componentModifiers.majorArea) + .modifier(modifier) + .arrangement(Arrangement.CENTER) + .childContext([ + builder.header(4).text("Import") + , + builder.column() + .arrangement(Arrangement.CENTER) + .modifier( + new Modifier() + .padding().all(32).top(16) + ) + .childContext([ + + rawTextImportArea() + , + processingRulesArea() + , + commandRow() + ]) + ]) +} diff --git a/fieldForCopyValue.js b/fieldForCopyValue.js new file mode 100644 index 0000000..c02718d --- /dev/null +++ b/fieldForCopyValue.js @@ -0,0 +1,78 @@ +/** + * @deprecated Verwendet die ebenfalls deprecated function document.execCommand('copy') + * @param {*} triggerElement + */ +function copyFieldToClipboard2(triggerElement) { + var copyText = document.getElementById("myText"); + copyText.select(); + copyText.setSelectionRange(0, 99999); // For mobile devices + + // Copy the text inside the textarea + document.execCommand("copy"); + + let valuecontainer = triggerElement.parent + .querySelector('.valuecontainer'); + + let fieldvalue = valuecontainer.value; + + navigator.clipboard.writeText(fieldvalue); + + // Optional: Alert the copied text + alert("Copied: " + copyText.value); +} + +function copyFieldToClipboard(triggerElement) { + let valcon = triggerElement.closest('.value-to-clipboard-field') + .querySelector('.valuecontainer'); + navigator.clipboard.writeText(valcon.value) +} + + +/** + * + * @param {string} value + * @param {number} index + * @param {Array} arr + * @param {boolean} hideCopy + * @param {string} [type="text"] Falls ein anderer typ gebraucht wird + * @returns {Component} + */ +function fieldForValueCopy( + value, + index, + arr, + hideCopy = true, + type="text", + setName = "field-" + (index + 3) +) { + return builder.div() + .addStyleClass("value-to-clipboard-field") + .childContext([ + builder.input(type) + .readonly() + .addStyleClass("valuecontainer") + .text(value) + .name(setName) + .modifier( + new Modifier() + .padding(0) + .border(0) + , + ".inputfield-border-none" + ) + , + builder.extensions.iconMIO(ICONS.CONTENT_COPY) + .hiddenByCondition(hideCopy) + .modifier( + new Modifier() + .setStyleRule("cursor", "pointer") + .setStyleRule("visibility", "hidden") + + ) + .setAttribute( + "onclick", + "copyFieldToClipboard(this)" + ) + ]) +} + diff --git a/fun.js b/fun.js new file mode 100644 index 0000000..07e2c6a --- /dev/null +++ b/fun.js @@ -0,0 +1,16 @@ +function rotateCurrentElement() { + let next = document.querySelector('#open table tbody tr'); + let currentTarget = document.querySelector('#current table tbody'); + let current = currentTarget.querySelector("tr"); + let target = document.querySelector('#processed table tbody'); + + if(current){ + current.remove(); + target.insertAdjacentElement("afterbegin", current); + } + + if(next){ + next.remove(); + currentTarget.append(next); + } +} \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..d635791 --- /dev/null +++ b/index.html @@ -0,0 +1,105 @@ + + + + + + + Processing List + + + + + + + + + + +
+ +
+ + + + \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..efa3160 --- /dev/null +++ b/index.js @@ -0,0 +1,28 @@ +Page.inDev(45) +Page.addExtension(MaterialIcons); + +builder.page( + builder.column() + .arrangement(Arrangement.SPACE_EVENLY) + .alignment(Alignment.CENTER) + .modifier( + new Modifier() + .border(new Border().all(15)) + .padding(32) + .dimensions().width(1200) + ) + .childContext([ + builder.header(1) + .alignment(Alignment.CENTER) + .text("Process List") + , + aktionsKonsole() + , + listArea(new Modifier().margin().vertical(5)) + , + importArea( + new Modifier() + .setStyleRule("float", "bottom") + ) + ]) +) diff --git a/package.json b/package.json new file mode 100644 index 0000000..9a4a0c9 --- /dev/null +++ b/package.json @@ -0,0 +1,21 @@ +{ + "name": "semi-automatic-ui-process-list", + "version": "1.0.0", + "description": "WebUi to list and process entries, splitted into seperated fields if needed, with data for a task/process. Each entry will be used in a process of a partially manual/automated manner. Tasks of a repetetive kind that are to be executed on the ui (of application/s).", + "license": "ISC", + "keywords": [ + "automation", + "webui" + ], + "author": "cm", + "type": "commonjs", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "dependencies": { + "jpc-like-websites": "git+https://git.labos.goip.de/chris/jpc-like-websites.git", + "jpclw-extension": "git+https://git.labos.goip.de/chris/jpclw-extension.git", + "jpclwe-materialicons": "git+https://git.labos.goip.de/chris/jpclwe-materialIcons.git" + } +} diff --git a/processingList.js b/processingList.js new file mode 100644 index 0000000..52235d3 --- /dev/null +++ b/processingList.js @@ -0,0 +1,118 @@ +function listEntryRowControls() { + const controls = [ + select, + de_activate, + edit, + ] +} + +function listEntryRowControlsContextMenu() { + const menuOptions = { + copy: { + copy_below, + copy_bottom + }, + move: { + top, + bottom + }, + + } + +} + +/** + * Stellt den Datensatz eines Prozesses dar + * + * @param {String[]} entry + * @param {number} index + * @returns {Component} + */ +function listEntryRow(entry, index) { + return builder.row() + .modifyChildren(new Modifier().padding(2)) + .modifier( + new Modifier() + .fillMaxWidth() + .padding(4) + .background(Colors.white) + .clip(Shapes.RoundedCorner.all(4)) + ) + .alignment(Alignment.CENTER) + .childContext([ + builder.span() + .addStyleClass("entrydata-container") + .setAttribute( + "data-entrydata", + JSON.stringify(entry) + ) + .modifier( + new Modifier() + .fillMaxWidth(0.09) + .margin().right(4) + ) + .arrangement(Arrangement.SPACE_BETWEEN) + .alignment(Alignment.CENTER) + .childContext([ + builder.inputTags.checkbox({ "name": "entry_select" }) + , + fieldForValueCopy(index, -1, [], true, "number","entry-index") + .alignment(Alignment.CENTER) + .modifier( + new Modifier() + .fillMaxWidth(0.7) + .margin().left(2) + ) + + ]) + , + ...entry + .filter(e => e.trim() !== "") + .map(fieldForValueCopy) + ]); +} + +/** + * + * @param {ComponentColor} colorTheme + * @param {Modifier} modifier + * @returns {Component} + */ +function processingList(colorTheme, modifier = new Modifier()) { + return builder.column() + .modifier(componentModifiers.majorArea) + .modifier( + new Modifier() + .background(colorTheme.secondary) + .border(new Border(1)) + .padding(8) + ) + .modifier(modifier) + .chainChild() + .column() + .modifier( + new Modifier() + .background(Colors.white) + .clip(Shapes.RoundedCorner.all(6)) + ) +} + + +function listArea(modifier = new Modifier()) { + return builder.row() + .addStyleClass("area") + .addStyleClass("area-lists") + .modifier(modifier) + .modifier(new Modifier().fillMaxWidth()) + .arrangement(Arrangement.SPACE_EVENLY) + .childContext([ + processingList(colorModifiers.upcoming) + .addStyleClass("plist") + .addStyleClass("plist-upcoming") + , + processingList(colorModifiers.done) + .addStyleClass("plist") + .addStyleClass("plist-done") + ]) + .distibuteSpacingEvenly(true, 0.05) +} diff --git a/processingRules.js b/processingRules.js new file mode 100644 index 0000000..99b99c6 --- /dev/null +++ b/processingRules.js @@ -0,0 +1,37 @@ +const processingRules = { + line_splitter: { + display: "Line splitter", + id: "line_splitter", + type: "text", + placeholder: "\n", + desc: "Das Zeichen um Zeilen umzubrechen.", + isFlagged: false + }, + + entry_splitter: { + display: "Entry Info Splitter", + id: "entry_splitter", + type: "text", + placeholder: ".", + desc: "Das Zeichen das eine Zeile in einzelne Informationen teilt.", + isFlagged: true + }, + + use_headrow: { + display: "Use Head Row", + id: "use_headrow", + type: "checkbox", + placeholder: "false", + desc: "Bestimmt das im Text, die erste Reihe potentielle Spaltenüberschriften beinhaltet.", + isFlagged: true + }, + + generated_fields: { + display: "Generate Fields", + id: "generated_fields", + type: "number", + placeholder: "1", + desc: "Definiert das zusätzliche Informationen aus den anderen generiert werden sollen.", + isFlagged: true + } +} diff --git a/sampleData.txt b/sampleData.txt new file mode 100644 index 0000000..9db65d0 --- /dev/null +++ b/sampleData.txt @@ -0,0 +1,40 @@ +202406-Rimpido-Schulung.mm +3D_Modelle.mm +Bastel-Projekte.mm +Bastel_Handwerksressoursen.mm +Besorgungen.mm +Bodenseeschifferpatent-Präsentation-Gruppe.mm +Bodenseeschifferpatent-Präsentation-Gruppe.pdf +'Bogenbau_ Sehnenbau_ Mittenwicklung – Wikibooks, Sammlung freier Lehr-, Sach- und Fachbücher.pdf' +'Büroumzug Aicheler.mm' +Checkliste.mm +ContentOrientatedText.mm +Data-Analysten_Augen.md +Data-Analysten_Augen.mm +Data-Analysten_Augen.pdf +Data-Analysten_Augen.png +Digitalisierung_von_analogen_Informationen.mm +'GTD - Fokus.mm' +Hochzeit.mm +'How to write Code.mm' +Krankenversicherung-2.mm +Krankenversicherung.mm +Kugelrechner.ods +Kugelrechner_quickVersion.ods +Linksammlung.mm +'Methanol - Synthese.mm' +Netzwerkkabel-Plan.ods +'Offene Recherchen.mm' +Projekt_LehrenMitarbeitenLernen.mm +Projekte.mm +'Rimpido - Unternehmens Roadmap.mm' +Rimpido-Sicherheitskonzept-Umstellung.mm +Systemalternativen.mm +Trauzeugenschaft_Stefan.mm +Typescript_Applikation.mm +Unternehmungen-n.pdf +Unternehmungen.mm +Unternehmungen.pdf +Wasser-Stromgenerator.mm +papbeton_betonplatten_volumenberchnungen.ods +rimpido.mm diff --git a/theming.js b/theming.js new file mode 100644 index 0000000..02405cb --- /dev/null +++ b/theming.js @@ -0,0 +1,43 @@ + +class ComponentColor { + constructor(primary, secondary, text = Colors.black, accent = Colors.seagreen_1) { + this.primary = primary; + this.secondary = secondary; + this.text = text; + this.accent = accent; + } +} + + +const colorModifiers = { + upcoming: new ComponentColor( + MaterialFiveHundredlColors.BLUE, + MaterialFiveHundredlColors.BLUE, + MaterialFiveHundredlColors.BLUE, + MaterialFiveHundredlColors.BLUE, + ), + done: new ComponentColor( + MaterialFiveHundredlColors.GREEN, + MaterialFiveHundredlColors.SEA_GREEN, + MaterialFiveHundredlColors.BLUE, + Colors.white, + ), + deactivated: new ComponentColor( + Colors.gray, + Colors.lightgrey + ), + active: new ComponentColor( + MaterialFiveHundredlColors.ORANGE, + MaterialFiveHundredlColors.SOFT_ORANGE + ), + import: new ComponentColor( + MaterialFiveHundredlColors.BLUE, + MaterialFiveHundredlColors.BLUE, + MaterialFiveHundredlColors.BLUE, + MaterialFiveHundredlColors.BLUE, + ) +} + +const componentModifiers={ + majorArea: new Modifier().clip(Shapes.RoundedCorner.all(16)), +} \ No newline at end of file