From 982971b77657473a7607aba57fd9813c3d1fc3b1 Mon Sep 17 00:00:00 2001 From: roguisharcanetrickster Date: Mon, 9 Mar 2026 19:55:35 +0700 Subject: [PATCH 01/11] Squash merge rat/csv_exporter into rat/csv_plugins --- src/plugins/index.js | 8 +++ .../FNAbviewcsvexporter.js} | 48 ++++++++------ .../FNAbviewcsvexporterEditor.js | 63 ++++++++++++++++++ .../web_view_csvImporter}/CSVImporter.js | 0 .../FNAbviewcsvimporter.js} | 59 ++++++++++------- .../FNAbviewcsvimporterEditor.js | 64 +++++++++++++++++++ .../Designer/editors/EditorManager.js | 4 -- .../editors/views/ABViewCSVExporter.js | 57 ----------------- .../editors/views/ABViewCSVImporter.js | 57 ----------------- .../Designer/properties/PropertyManager.js | 3 +- 10 files changed, 201 insertions(+), 162 deletions(-) rename src/{rootPages/Designer/properties/views/ABViewCSVExporter.js => plugins/web_view_csvExporter/FNAbviewcsvexporter.js} (91%) create mode 100644 src/plugins/web_view_csvExporter/FNAbviewcsvexporterEditor.js rename src/{utils => plugins/web_view_csvImporter}/CSVImporter.js (100%) rename src/{rootPages/Designer/properties/views/ABViewCSVImporter.js => plugins/web_view_csvImporter/FNAbviewcsvimporter.js} (91%) create mode 100644 src/plugins/web_view_csvImporter/FNAbviewcsvimporterEditor.js delete mode 100644 src/rootPages/Designer/editors/views/ABViewCSVExporter.js delete mode 100644 src/rootPages/Designer/editors/views/ABViewCSVImporter.js diff --git a/src/plugins/index.js b/src/plugins/index.js index 6bc41ee9..639f5fa8 100644 --- a/src/plugins/index.js +++ b/src/plugins/index.js @@ -1,5 +1,9 @@ import viewPivotProperties from "./web_view_pivot/FNAbviewpivot.js"; import viewPivotEditor from "./web_view_pivot/FNAbviewpivotEditor.js"; +import CsvExporterEditor from "./web_view_csvExporter/FNAbviewcsvexporterEditor.js"; +import CsvExporterProperties from "./web_view_csvExporter/FNAbviewcsvexporter.js"; +import CsvImporterEditor from "./web_view_csvImporter/FNAbviewcsvimporterEditor.js"; +import CsvImporterProperties from "./web_view_csvImporter/FNAbviewcsvimporter.js"; import viewCarouselProperties from "./web_view_carousel/FNAbviewcarousel.js"; import viewCarouselEditor from "./web_view_carousel/FNAbviewcarouselEditor.js"; import viewCommentProperties from "./web_view_comment/FNAbviewcomment.js"; @@ -50,6 +54,10 @@ const AllPlugins = [ viewTextEditor, viewPivotProperties, viewPivotEditor, + CsvExporterEditor, + CsvExporterProperties, + CsvImporterEditor, + CsvImporterProperties, ]; export default { diff --git a/src/rootPages/Designer/properties/views/ABViewCSVExporter.js b/src/plugins/web_view_csvExporter/FNAbviewcsvexporter.js similarity index 91% rename from src/rootPages/Designer/properties/views/ABViewCSVExporter.js rename to src/plugins/web_view_csvExporter/FNAbviewcsvexporter.js index f37e75ab..d95c4d48 100644 --- a/src/rootPages/Designer/properties/views/ABViewCSVExporter.js +++ b/src/plugins/web_view_csvExporter/FNAbviewcsvexporter.js @@ -1,18 +1,31 @@ -/* - * ABViewCSVExporter - * A Property manager for our ABViewCSVExporter widget - */ - -import FViewClass from "./ABView"; - -export default function (AB) { +// FNAbviewcsvexporter Properties +// A properties side import for an ABView. +// +export default function FNAbviewcsvexporterProperties({ + AB, + ABViewPropertiesPlugin, + // ABUIPlugin, +}) { const BASE_ID = "properties_abview_csvexporter"; const ABViewClassProperty = FViewClass(AB); const uiConfig = AB.Config.uiSettings(); const L = ABViewClassProperty.L(); - class ABViewCSVExporterProperty extends ABViewClassProperty { + return class ABAbviewcsvexporterProperties extends ABViewPropertiesPlugin { + + static getPluginKey() { + return this.key; + } + + static getPluginType() { + return "properties-view"; + // properties-view : will display in the properties panel of the ABDesigner + } + + + + constructor(baseID) { super(baseID ?? BASE_ID, { datacollection: "", @@ -22,6 +35,7 @@ export default function (AB) { width: "", buttonFilter: "", fields: "", + dataviewID: "", }); this.AB = AB; @@ -143,9 +157,8 @@ export default function (AB) { autoheight: true, select: false, template: (item) => { - return `  ${item.label}`; + return `  ${item.label}`; }, on: { onItemClick: (id, e, node) => { @@ -192,19 +205,19 @@ export default function (AB) { $$(ids.hasHeader).setValue( view.settings.hasHeader ?? - ABViewCSVExporterPropertyComponentDefaults.hasHeader + ABViewCSVExporterPropertyComponentDefaults.hasHeader ); $$(ids.buttonLabel).setValue( view.settings.buttonLabel ?? - ABViewCSVExporterPropertyComponentDefaults.buttonLabel + ABViewCSVExporterPropertyComponentDefaults.buttonLabel ); $$(ids.filename).setValue( view.settings.filename ?? - ABViewCSVExporterPropertyComponentDefaults.filename + ABViewCSVExporterPropertyComponentDefaults.filename ); $$(ids.width).setValue( view.settings.width ?? - ABViewCSVExporterPropertyComponentDefaults.width + ABViewCSVExporterPropertyComponentDefaults.width ); this.populateFilter(); @@ -341,6 +354,5 @@ export default function (AB) { return super._ViewClass("csvExporter"); } } - - return ABViewCSVExporterProperty; } + diff --git a/src/plugins/web_view_csvExporter/FNAbviewcsvexporterEditor.js b/src/plugins/web_view_csvExporter/FNAbviewcsvexporterEditor.js new file mode 100644 index 00000000..bcf5198a --- /dev/null +++ b/src/plugins/web_view_csvExporter/FNAbviewcsvexporterEditor.js @@ -0,0 +1,63 @@ +// FNAbviewcsvexporter Editor +// An Editor wrapper for the ABView Component. +// The Editor is displayed in the ABDesigner as a view is worked on. +// The Editor allows a widget to be moved and placed on the canvas. +// +export default function FNAbviewcsvexporterEditor({ AB, ABViewEditorPlugin }) { + // var L = UIClass.L(); + // var L = ABViewContainer.L(); + + return class ABAbviewcsvexporterEditor extends ABViewEditorPlugin { + + static getPluginKey() { + return this.key; + } + + /** + * @method getPluginType + * return the plugin type for this editor. + * plugin types are how our ClassManager knows how to store + * the plugin. + * @return {string} plugin type + */ + static getPluginType() { + return "editor-view"; + // editor-view : will display in the editor panel of the ABDesigner + } + + static get key() { + return "csvExporter"; + } + + constructor(view, base = "interface_editor_csvExporter") { + // base: {string} unique base id reference + super(view, base); + } + + ui() { + return super.ui(); + } + + async init(AB) { + await super.init(AB); + + this.component.init(this.AB); + + // this.component.onShow(); + // in our editor, we provide accessLv = 2 + } + + detatch() { + this.component?.detatch?.(); + super.detatch(); + + } + + onShow() { + this.component?.onShow?.(); + super.onShow(); + } + }; + + +} diff --git a/src/utils/CSVImporter.js b/src/plugins/web_view_csvImporter/CSVImporter.js similarity index 100% rename from src/utils/CSVImporter.js rename to src/plugins/web_view_csvImporter/CSVImporter.js diff --git a/src/rootPages/Designer/properties/views/ABViewCSVImporter.js b/src/plugins/web_view_csvImporter/FNAbviewcsvimporter.js similarity index 91% rename from src/rootPages/Designer/properties/views/ABViewCSVImporter.js rename to src/plugins/web_view_csvImporter/FNAbviewcsvimporter.js index 307b2876..2c149c64 100644 --- a/src/rootPages/Designer/properties/views/ABViewCSVImporter.js +++ b/src/plugins/web_view_csvImporter/FNAbviewcsvimporter.js @@ -1,21 +1,33 @@ -/* - * ABViewCSVImporter - * A Property manager for our ABViewCSVImporter widget - */ - -import FViewClass from "./ABView"; -import ABRecordRule from "../rules/ABViewRuleListFormRecordRules"; - -let PopupRecordRule = null; - -export default function (AB) { +// FNAbviewcsvimporter Properties +// A properties side import for an ABView. +// +export default function FNAbviewcsvimporterProperties({ + AB, + ABViewPropertiesPlugin, + // ABUIPlugin, +}) { const BASE_ID = "properties_abview_csvimporter"; const ABViewClassProperty = FViewClass(AB); const uiConfig = AB.Config.uiSettings(); const L = ABViewClassProperty.L(); - class ABViewCSVImporterProperty extends ABViewClassProperty { + + + return class ABAbviewcsvimporterProperties extends ABViewPropertiesPlugin { + + static getPluginKey() { + return this.key; + } + + static getPluginType() { + return "properties-view"; + // properties-view : will display in the properties panel of the ABDesigner + } + + + + constructor(baseID) { super(baseID ?? BASE_ID, { datacollection: "", @@ -71,9 +83,8 @@ export default function (AB) { template: this.listTemplate.bind(this), type: { markCheckbox: function (item) { - return ``; + return ``; }, }, onClick: { @@ -198,7 +209,7 @@ export default function (AB) { f.selected = options.selectAll ? true : availableFields.filter((fieldId) => f.id == fieldId).length > - 0; + 0; return f; }); @@ -229,13 +240,10 @@ export default function (AB) { (v) => v.common().key == componentKey )[0]; - return `${$common.markCheckbox(field)} ${ - field.label - }
${ - formComponent ? L(formComponent.common().labelKey ?? "Label") : "" - }
`; + return `${$common.markCheckbox(field)} ${field.label + }
${formComponent ? L(formComponent.common().labelKey ?? "Label") : "" + }
`; } check(e, fieldId) { @@ -359,5 +367,8 @@ export default function (AB) { } } - return ABViewCSVImporterProperty; + + + } + diff --git a/src/plugins/web_view_csvImporter/FNAbviewcsvimporterEditor.js b/src/plugins/web_view_csvImporter/FNAbviewcsvimporterEditor.js new file mode 100644 index 00000000..7a5f3f3c --- /dev/null +++ b/src/plugins/web_view_csvImporter/FNAbviewcsvimporterEditor.js @@ -0,0 +1,64 @@ +// FNAbviewcsvimporter Editor +// An Editor wrapper for the ABView Component. +// The Editor is displayed in the ABDesigner as a view is worked on. +// The Editor allows a widget to be moved and placed on the canvas. +// +export default function FNAbviewcsvimporterEditor({ AB, ABViewEditorPlugin }) { + // var L = UIClass.L(); + // var L = ABViewContainer.L(); + +return class ABAbviewcsvimporterEditor extends ABViewEditorPlugin { + +static getPluginKey() { + return this.key; + } + +/** + * @method getPluginType + * return the plugin type for this editor. + * plugin types are how our ClassManager knows how to store + * the plugin. + * @return {string} plugin type + */ + static getPluginType() { + return "editor-view"; + // editor-view : will display in the editor panel of the ABDesigner + } + + + + + static get key() { + return "csvImporter"; + } + + constructor(view, base = "interface_editor_csvImporter") { + // base: {string} unique base id reference + + super(view, base); + } + + ui() { + return this.component.ui(); + } + + init(AB) { + this.AB = AB; + + this.component.init(this.AB); + + // this.component.onShow(); + // in our editor, we provide accessLv = 2 + } + + detatch() { + this.component?.detatch?.(); + } + + onShow() { + this.component?.onShow?.(); + } + }; + + +} diff --git a/src/rootPages/Designer/editors/EditorManager.js b/src/rootPages/Designer/editors/EditorManager.js index 1d91816f..663b6e97 100644 --- a/src/rootPages/Designer/editors/EditorManager.js +++ b/src/rootPages/Designer/editors/EditorManager.js @@ -21,8 +21,6 @@ export default function (AB) { // require("./views/ABViewComment"), require("./views/ABViewConditionalContainer"), require("./views/ABViewContainer"), - require("./views/ABViewCSVExporter"), - require("./views/ABViewCSVImporter"), // require("./views/ABViewDataSelect"), // require("./views/ABViewDataview"), // require("./views/ABViewDetail"), @@ -32,8 +30,6 @@ export default function (AB) { require("./views/ABViewGantt"), require("./views/ABViewGrid"), require("./views/ABViewKanban"), - // require("./views/ABViewLabel"), - // require("./views/ABViewLayout"), require("./views/ABViewMenu"), require("./views/ABViewPage"), // require("./views/ABViewPDFImporter"), diff --git a/src/rootPages/Designer/editors/views/ABViewCSVExporter.js b/src/rootPages/Designer/editors/views/ABViewCSVExporter.js deleted file mode 100644 index 92e411da..00000000 --- a/src/rootPages/Designer/editors/views/ABViewCSVExporter.js +++ /dev/null @@ -1,57 +0,0 @@ -/** - * ABViewCSVExporterEditor - * The widget that displays the UI Editor Component on the screen - * when designing the UI. - */ -let myClass = null; -// {singleton} -// we will want to call this factory fn() repeatedly in our imports, -// but we only want to define 1 Class reference. - -import UI_Class from "../../ui_class"; - -export default function (AB) { - if (!myClass) { - const UIClass = UI_Class(AB); - // var L = UIClass.L(); - // var L = ABViewContainer.L(); - - myClass = class ABViewCSVExporterEditor extends UIClass { - static get key() { - return "csvExporter"; - } - - constructor(view, base = "interface_editor_csvExporter") { - // base: {string} unique base id reference - - super(view, base); - - this.view = view; - this.component = this.view.component(); - } - - ui() { - return this.component.ui(); - } - - init(AB) { - this.AB = AB; - - this.component.init(this.AB); - - // this.component.onShow(); - // in our editor, we provide accessLv = 2 - } - - detatch() { - this.component?.detatch?.(); - } - - onShow() { - this.component?.onShow?.(); - } - }; - } - - return myClass; -} diff --git a/src/rootPages/Designer/editors/views/ABViewCSVImporter.js b/src/rootPages/Designer/editors/views/ABViewCSVImporter.js deleted file mode 100644 index 8d0a0662..00000000 --- a/src/rootPages/Designer/editors/views/ABViewCSVImporter.js +++ /dev/null @@ -1,57 +0,0 @@ -/** - * ABViewCSVImporterEditor - * The widget that displays the UI Editor Component on the screen - * when designing the UI. - */ -let myClass = null; -// {singleton} -// we will want to call this factory fn() repeatedly in our imports, -// but we only want to define 1 Class reference. - -import UI_Class from "../../ui_class"; - -export default function (AB) { - if (!myClass) { - const UIClass = UI_Class(AB); - // var L = UIClass.L(); - // var L = ABViewContainer.L(); - - myClass = class ABViewCSVImporterEditor extends UIClass { - static get key() { - return "csvImporter"; - } - - constructor(view, base = "interface_editor_csvImporter") { - // base: {string} unique base id reference - - super(view, base); - - this.view = view; - this.component = this.view.component(); - } - - ui() { - return this.component.ui(); - } - - init(AB) { - this.AB = AB; - - this.component.init(this.AB); - - // this.component.onShow(); - // in our editor, we provide accessLv = 2 - } - - detatch() { - this.component?.detatch?.(); - } - - onShow() { - this.component?.onShow?.(); - } - }; - } - - return myClass; -} diff --git a/src/rootPages/Designer/properties/PropertyManager.js b/src/rootPages/Designer/properties/PropertyManager.js index 570701f1..f3717dd9 100644 --- a/src/rootPages/Designer/properties/PropertyManager.js +++ b/src/rootPages/Designer/properties/PropertyManager.js @@ -79,8 +79,7 @@ export default function (AB) { // require("./views/ABViewComment"), require("./views/ABViewConditionalContainer"), require("./views/ABViewContainer"), - require("./views/ABViewCSVExporter"), - require("./views/ABViewCSVImporter"), + // // require("./views/ABViewCSVImporter"), require("./views/ABViewDataFilter"), // require("./views/ABViewDataSelect"), // require("./views/ABViewDataview"), From 865b65789108f986ce7ba0111ad288ede0f94c1a Mon Sep 17 00:00:00 2001 From: roguisharcanetrickster Date: Thu, 12 Mar 2026 14:09:51 +0700 Subject: [PATCH 02/11] move FieldTypeTool and point tests at new plugin --- .github/workflows/e2e-test.yml | 12 +++++----- .../FNAbviewcsvexporter.js | 4 ++-- .../FNAbviewcsvimporter.js | 11 +++++---- .../web_view_csvImporter/FieldTypeTool.js | 23 +++++++++++++++++++ .../ui_work_object_workspace_popupImport.js | 10 ++++---- .../ui_work_object_list_newObject_csv.js | 2 +- .../Designer/ui_work_object_workspace.js | 8 +++---- test/utils/CSVImporter.test.js | 2 +- 8 files changed, 49 insertions(+), 23 deletions(-) create mode 100644 src/plugins/web_view_csvImporter/FieldTypeTool.js rename src/{rootPages/Designer => plugins/web_view_csvImporter}/ui_work_object_workspace_popupImport.js (83%) diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index f452cebe..e3e84e53 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -30,19 +30,19 @@ jobs: - branch: master webpack: update steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: path: plugins/plugin_ABDesigner ref: ${{ inputs.ref }} - name: Generate a token id: generate-token - uses: actions/create-github-app-token@v2 + uses: actions/create-github-app-token@v3 with: app-id: ${{ inputs.app_id }} private-key: ${{ secrets.app_secret }} repositories: ab_service_web - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: path: web repository: CruGlobal/ab_service_web @@ -64,7 +64,7 @@ jobs: repository: CruGlobal/ab_service_web - name: Check out kitchen-sink tests - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: repository: CruGlobal/kitchensink_app path: ab/test/e2e/cypress/e2e/kitchensink_app @@ -87,13 +87,13 @@ jobs: run: npm run test:e2e:ab-designer -- --browser chrome working-directory: ./ab - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v6 if: failure() with: name: cypress-screenshots path: ./ab/test/e2e/cypress/screenshots - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v6 if: failure() with: name: ABServices.log diff --git a/src/plugins/web_view_csvExporter/FNAbviewcsvexporter.js b/src/plugins/web_view_csvExporter/FNAbviewcsvexporter.js index d95c4d48..20a80d70 100644 --- a/src/plugins/web_view_csvExporter/FNAbviewcsvexporter.js +++ b/src/plugins/web_view_csvExporter/FNAbviewcsvexporter.js @@ -8,9 +8,9 @@ export default function FNAbviewcsvexporterProperties({ }) { const BASE_ID = "properties_abview_csvexporter"; - const ABViewClassProperty = FViewClass(AB); const uiConfig = AB.Config.uiSettings(); - const L = ABViewClassProperty.L(); + const L = AB.Label(); + return class ABAbviewcsvexporterProperties extends ABViewPropertiesPlugin { diff --git a/src/plugins/web_view_csvImporter/FNAbviewcsvimporter.js b/src/plugins/web_view_csvImporter/FNAbviewcsvimporter.js index 2c149c64..56003fe3 100644 --- a/src/plugins/web_view_csvImporter/FNAbviewcsvimporter.js +++ b/src/plugins/web_view_csvImporter/FNAbviewcsvimporter.js @@ -1,6 +1,8 @@ // FNAbviewcsvimporter Properties // A properties side import for an ABView. // +import FABViewRuleListFormRecordRules from "../../rootPages/Designer/properties/rules/ABViewRuleListFormRecordRules.js"; + export default function FNAbviewcsvimporterProperties({ AB, ABViewPropertiesPlugin, @@ -8,10 +10,13 @@ export default function FNAbviewcsvimporterProperties({ }) { const BASE_ID = "properties_abview_csvimporter"; - const ABViewClassProperty = FViewClass(AB); const uiConfig = AB.Config.uiSettings(); - const L = ABViewClassProperty.L(); + const L = AB.Label(); + const PopupRecordRule = FABViewRuleListFormRecordRules( + AB, + `${BASE_ID}_popupRecordRule` + ); return class ABAbviewcsvimporterProperties extends ABViewPropertiesPlugin { @@ -46,8 +51,6 @@ export default function FNAbviewcsvimporterProperties({ ui() { const ids = this.ids; - PopupRecordRule = ABRecordRule(this.AB, this.base); - // PopupRecordRule.component(`${this.base}_recordrule`); return super.ui([ { diff --git a/src/plugins/web_view_csvImporter/FieldTypeTool.js b/src/plugins/web_view_csvImporter/FieldTypeTool.js new file mode 100644 index 00000000..ea374473 --- /dev/null +++ b/src/plugins/web_view_csvImporter/FieldTypeTool.js @@ -0,0 +1,23 @@ +module.exports = class FieldTypeTool { + static getFieldType(value) { + if (value === null || value === undefined || value === "") { + return "string"; + } else if ( + value == 0 || + value == 1 || + value == true || + value == false || + value == "checked" || + value == "unchecked" + ) { + return "boolean"; + } else if (!isNaN(value)) { + return "number"; + } else if (Date.parse(value)) { + return "date"; + } else { + if (value.length > 100) return "LongText"; + else return "string"; + } + } +}; diff --git a/src/rootPages/Designer/ui_work_object_workspace_popupImport.js b/src/plugins/web_view_csvImporter/ui_work_object_workspace_popupImport.js similarity index 83% rename from src/rootPages/Designer/ui_work_object_workspace_popupImport.js rename to src/plugins/web_view_csvImporter/ui_work_object_workspace_popupImport.js index 842d28ee..6d026620 100644 --- a/src/rootPages/Designer/ui_work_object_workspace_popupImport.js +++ b/src/plugins/web_view_csvImporter/ui_work_object_workspace_popupImport.js @@ -4,15 +4,15 @@ * Manage the Import CSV data to our currently selected ABObject. * */ -import UI_Class from "./ui_class"; -import FViewProperties from "./properties/views/ABViewCSVImporter"; + +import UI_Class from "../../rootPages/Designer/ui_class"; +import FNAbviewcsvimporterProperties from "./FNAbviewcsvimporter.js"; export default function (AB, ibase) { ibase = ibase || "ui_work_object_workspace_popupImport"; const UIClass = UI_Class(AB); - // var L = UIClass.L(); - const ViewProperties = FViewProperties(AB); - const viewProperties = new ViewProperties(); + const ViewPropertiesClass = FNAbviewcsvimporterProperties(AB.pluginAPI()); + const viewProperties = new ViewPropertiesClass(); class UI_Work_Object_Workspace_PopupImport extends UIClass { constructor(base) { diff --git a/src/rootPages/Designer/ui_work_object_list_newObject_csv.js b/src/rootPages/Designer/ui_work_object_list_newObject_csv.js index c8b3cf69..93ff3db2 100644 --- a/src/rootPages/Designer/ui_work_object_list_newObject_csv.js +++ b/src/rootPages/Designer/ui_work_object_list_newObject_csv.js @@ -5,7 +5,7 @@ * */ import UI_Class from "./ui_class"; -import CSVImporter from "../../utils/CSVImporter.js"; +import CSVImporter from "../../plugins/web_view_csvImporter/CSVImporter.js"; export default function (AB) { const UIClass = UI_Class(AB); diff --git a/src/rootPages/Designer/ui_work_object_workspace.js b/src/rootPages/Designer/ui_work_object_workspace.js index 9513c00c..d6315b82 100644 --- a/src/rootPages/Designer/ui_work_object_workspace.js +++ b/src/rootPages/Designer/ui_work_object_workspace.js @@ -17,7 +17,7 @@ import FPopupExport from "./ui_work_object_workspace_popupExport"; import FPopupFrozenColumns from "./ui_work_object_workspace_popupFrozenColumns"; import FPopupHeaderEditMenu from "./ui_work_object_workspace_popupHeaderEditMenu"; import FPopupHideFields from "./ui_work_object_workspace_popupHideFields"; -import FPopupImport from "./ui_work_object_workspace_popupImport"; +import FPopupImport from "../../plugins/web_view_csvImporter/ui_work_object_workspace_popupImport.js"; import FPopupNewDataField from "./ui_work_object_workspace_popupNewDataField"; import FPopupSortField from "./ui_work_object_workspace_popupSortFields"; import FPopupViewSettings from "./ui_work_object_workspace_popupViewSettings"; @@ -238,9 +238,9 @@ export default function (AB, ibase, init_settings) { AB, `${base}_import` ); - // this.PopupImportObjectComponent.on("done", () => { - // this.populateObjectWorkspace(this.CurrentObject); - // }); + this.PopupImportObjectComponent.on("done", () => { + this.populateObjectWorkspace(this.CurrentObject); + }); this.PopupViewSettingsComponent = FPopupViewSettings( AB, diff --git a/test/utils/CSVImporter.test.js b/test/utils/CSVImporter.test.js index 4c1c4dc5..e2b7358c 100644 --- a/test/utils/CSVImporter.test.js +++ b/test/utils/CSVImporter.test.js @@ -3,7 +3,7 @@ import assert from "assert"; import sinon from "sinon"; import AB from "../_mock/AB.js"; -import CSVImporter from "../../src/utils/CSVImporter.js"; +import CSVImporter from "../../src/plugins/web_view_csvImporter/CSVImporter.js"; function getMockAB() { return new AB(); From bb03a59c52451b8a8e30f670d21edadff4bf38a5 Mon Sep 17 00:00:00 2001 From: roguisharcanetrickster Date: Tue, 26 May 2026 17:30:32 +0700 Subject: [PATCH 03/11] csv importer handle, replace importer workspace --- .../web_view_csvImporter/CSVImporter.js | 286 +++++++++--------- .../ui_work_object_list_newObject_csv.js | 2 +- .../Designer/ui_work_object_workspace.js | 5 +- .../ui_work_object_workspace_popupImport.js | 4 +- test/_mock/AB.js | 4 + test/utils/CSVImporter.test.js | 11 +- 6 files changed, 159 insertions(+), 153 deletions(-) rename src/{plugins/web_view_csvImporter => rootPages/Designer}/ui_work_object_workspace_popupImport.js (91%) diff --git a/src/plugins/web_view_csvImporter/CSVImporter.js b/src/plugins/web_view_csvImporter/CSVImporter.js index 4dbd549e..b8847c7e 100644 --- a/src/plugins/web_view_csvImporter/CSVImporter.js +++ b/src/plugins/web_view_csvImporter/CSVImporter.js @@ -1,153 +1,155 @@ const FieldTypeTool = require("./FieldTypeTool"); -module.exports = class CSVImporter { - constructor(AB, fileReader = FileReader) { - this._AB = AB; - this._FileReader = fileReader; - } - - L(...params) { - return this._AB.Multilingual.labelPlugin("ABDesigner", ...params); - } - - getSeparateItems() { - return [ - { id: ",", value: this.L("Comma (,)") }, - { - id: "\t", - value: this.L("Tab (      )"), - }, - { id: ";", value: this.L("Semicolon (;)") }, - { id: "s", value: this.L("Space ( )") }, - ]; - } - - /** - * @method validateFile - * Validate file extension - * - * @param {*} fileInfo - https://docs.webix.com/api__ui.uploader_onbeforefileadd_event.html - * - * @return {boolean} - */ - validateFile(fileInfo) { - if (!fileInfo || !fileInfo.file || !fileInfo.file.type) return false; - - // validate file type - let extensionType = fileInfo.file.type.toLowerCase(); - if ( - extensionType == "text/csv" || - extensionType == "application/vnd.ms-excel" - ) { - return true; - } else { - return false; +module.exports = function FCSVImporter({ AB }) { + return class CSVImporter { + constructor(fileReader = FileReader) { + this._AB = AB; + this._FileReader = fileReader; } - } - - /** - * @method getDataRows - * Pull data rows from the CSV file - * - * @param {Object} fileInfo - https://docs.webix.com/api__ui.uploader_onbeforefileadd_event.html - * @param {string} separatedBy - * - * @return {Promise} -[ - * ["Value 1.1", "Value 1.2", "Value 1.3"], - * ["Value 2.1", "Value 2.2", "Value 2.3"], - * ] - */ - async getDataRows(fileInfo, separatedBy) { - if (!this.validateFile(fileInfo)) - return Promise.reject(this.L(".fileInfo parameter is invalid")); - - return new Promise((resolve, reject) => { - // read CSV file - let reader = new this._FileReader(); - reader.onload = (e) => { - const result = this.convertToArray(reader.result, separatedBy); - - resolve(result); - }; - reader.readAsText(fileInfo.file); - }); - } - - /** - * @method convertToArray - * Pull data rows from the CSV file - * - * @param {string} text - * @param {string} separatedBy - * - * @return {Promise} -[ - * ["Value 1.1", "Value 1.2", "Value 1.3"], - * ["Value 2.1", "Value 2.2", "Value 2.3"], - * ] - */ - convertToArray(text = "", separatedBy = ",") { - let result = []; - - // split lines - let dataRows = text - .split(/\r\n|\n|\r/) // CRLF = \r\n; LF = \n; CR = \r; - .filter((row) => row && row.length > 0); - - // split columns - (dataRows || []).forEach((row) => { - let dataCols = []; - if (separatedBy == ",") { - // NOTE: if the file contains ,, .match(), then can not recognize this empty string - row = row.replace(/,,/g, ", ,"); - - // https://stackoverflow.com/questions/11456850/split-a-string-by-commas-but-ignore-commas-within-double-quotes-using-javascript#answer-11457952 - dataCols = row.match(/(".*?"|[^",]+)(?=\s*,|\s*$)/g); + + L(...params) { + return this._AB.Multilingual.labelPlugin("ABDesigner", ...params); + } + + getSeparateItems() { + return [ + { id: ",", value: this.L("Comma (,)") }, + { + id: "\t", + value: this.L("Tab (      )"), + }, + { id: ";", value: this.L("Semicolon (;)") }, + { id: "s", value: this.L("Space ( )") }, + ]; + } + + /** + * @method validateFile + * Validate file extension + * + * @param {*} fileInfo - https://docs.webix.com/api__ui.uploader_onbeforefileadd_event.html + * + * @return {boolean} + */ + validateFile(fileInfo) { + if (!fileInfo || !fileInfo.file || !fileInfo.file.type) return false; + + // validate file type + let extensionType = fileInfo.file.type.toLowerCase(); + if ( + extensionType == "text/csv" || + extensionType == "application/vnd.ms-excel" + ) { + return true; } else { - dataCols = row.split(separatedBy); + return false; } + } - result.push(dataCols.map((dCol) => this.reformat(dCol))); - }); - - return result; - } - - /** - * @method getGuessDataType - * - * @param dataRows {Array} - [ - * ["Value 1.1", "Value 1.2", "Value 1.3"], - * ["Value 2.1", "Value 2.2", "Value 2.3"], - * ] - * @param colIndex {Number} - * - * @return {string} - */ - getGuessDataType(dataRows, colIndex) { - var data, - repeatNum = 10; - - // Loop to find a value - for (var i = 1; i <= repeatNum; i++) { - var line = dataRows[i]; - if (!line) break; - - data = line[colIndex]; - - if (data != null && data.length > 0) break; + /** + * @method getDataRows + * Pull data rows from the CSV file + * + * @param {Object} fileInfo - https://docs.webix.com/api__ui.uploader_onbeforefileadd_event.html + * @param {string} separatedBy + * + * @return {Promise} -[ + * ["Value 1.1", "Value 1.2", "Value 1.3"], + * ["Value 2.1", "Value 2.2", "Value 2.3"], + * ] + */ + async getDataRows(fileInfo, separatedBy) { + if (!this.validateFile(fileInfo)) + return Promise.reject(this.L(".fileInfo parameter is invalid")); + + return new Promise((resolve, reject) => { + // read CSV file + let reader = new this._FileReader(); + reader.onload = (e) => { + const result = this.convertToArray(reader.result, separatedBy); + + resolve(result); + }; + reader.readAsText(fileInfo.file); + }); } - return FieldTypeTool.getFieldType(data); - } + /** + * @method convertToArray + * Pull data rows from the CSV file + * + * @param {string} text + * @param {string} separatedBy + * + * @return {Promise} -[ + * ["Value 1.1", "Value 1.2", "Value 1.3"], + * ["Value 2.1", "Value 2.2", "Value 2.3"], + * ] + */ + convertToArray(text = "", separatedBy = ",") { + let result = []; + + // split lines + let dataRows = text + .split(/\r\n|\n|\r/) // CRLF = \r\n; LF = \n; CR = \r; + .filter((row) => row && row.length > 0); + + // split columns + (dataRows || []).forEach((row) => { + let dataCols = []; + if (separatedBy == ",") { + // NOTE: if the file contains ,, .match(), then can not recognize this empty string + row = row.replace(/,,/g, ", ,"); + + // https://stackoverflow.com/questions/11456850/split-a-string-by-commas-but-ignore-commas-within-double-quotes-using-javascript#answer-11457952 + dataCols = row.match(/(".*?"|[^",]+)(?=\s*,|\s*$)/g); + } else { + dataCols = row.split(separatedBy); + } + + result.push(dataCols.map((dCol) => this.reformat(dCol))); + }); + + return result; + } + + /** + * @method getGuessDataType + * + * @param dataRows {Array} - [ + * ["Value 1.1", "Value 1.2", "Value 1.3"], + * ["Value 2.1", "Value 2.2", "Value 2.3"], + * ] + * @param colIndex {Number} + * + * @return {string} + */ + getGuessDataType(dataRows, colIndex) { + var data, + repeatNum = 10; + + // Loop to find a value + for (var i = 1; i <= repeatNum; i++) { + var line = dataRows[i]; + if (!line) break; + + data = line[colIndex]; + + if (data != null && data.length > 0) break; + } - /** - * @method reformat - * - * @param {string} str - */ - reformat(str) { - if (!str) return ""; + return FieldTypeTool.getFieldType(data); + } + + /** + * @method reformat + * + * @param {string} str + */ + reformat(str) { + if (!str) return ""; - return str.trim().replace(/"/g, "").replace(/'/g, ""); - } + return str.trim().replace(/"/g, "").replace(/'/g, ""); + } + }; }; diff --git a/src/rootPages/Designer/ui_work_object_list_newObject_csv.js b/src/rootPages/Designer/ui_work_object_list_newObject_csv.js index 93ff3db2..ad98934d 100644 --- a/src/rootPages/Designer/ui_work_object_list_newObject_csv.js +++ b/src/rootPages/Designer/ui_work_object_list_newObject_csv.js @@ -5,7 +5,7 @@ * */ import UI_Class from "./ui_class"; -import CSVImporter from "../../plugins/web_view_csvImporter/CSVImporter.js"; +import CSVImporter from "./ui_work_object_workspace_popupImport"; export default function (AB) { const UIClass = UI_Class(AB); diff --git a/src/rootPages/Designer/ui_work_object_workspace.js b/src/rootPages/Designer/ui_work_object_workspace.js index d6315b82..4c861940 100644 --- a/src/rootPages/Designer/ui_work_object_workspace.js +++ b/src/rootPages/Designer/ui_work_object_workspace.js @@ -17,7 +17,7 @@ import FPopupExport from "./ui_work_object_workspace_popupExport"; import FPopupFrozenColumns from "./ui_work_object_workspace_popupFrozenColumns"; import FPopupHeaderEditMenu from "./ui_work_object_workspace_popupHeaderEditMenu"; import FPopupHideFields from "./ui_work_object_workspace_popupHideFields"; -import FPopupImport from "../../plugins/web_view_csvImporter/ui_work_object_workspace_popupImport.js"; +import FPopupImport from "./ui_work_object_workspace_popupImport"; import FPopupNewDataField from "./ui_work_object_workspace_popupNewDataField"; import FPopupSortField from "./ui_work_object_workspace_popupSortFields"; import FPopupViewSettings from "./ui_work_object_workspace_popupViewSettings"; @@ -238,9 +238,6 @@ export default function (AB, ibase, init_settings) { AB, `${base}_import` ); - this.PopupImportObjectComponent.on("done", () => { - this.populateObjectWorkspace(this.CurrentObject); - }); this.PopupViewSettingsComponent = FPopupViewSettings( AB, diff --git a/src/plugins/web_view_csvImporter/ui_work_object_workspace_popupImport.js b/src/rootPages/Designer/ui_work_object_workspace_popupImport.js similarity index 91% rename from src/plugins/web_view_csvImporter/ui_work_object_workspace_popupImport.js rename to src/rootPages/Designer/ui_work_object_workspace_popupImport.js index 6d026620..ff356683 100644 --- a/src/plugins/web_view_csvImporter/ui_work_object_workspace_popupImport.js +++ b/src/rootPages/Designer/ui_work_object_workspace_popupImport.js @@ -5,8 +5,8 @@ * */ -import UI_Class from "../../rootPages/Designer/ui_class"; -import FNAbviewcsvimporterProperties from "./FNAbviewcsvimporter.js"; +import UI_Class from "./ui_class"; +import FNAbviewcsvimporterProperties from "../../plugins/web_view_csvImporter/FNAbviewcsvimporter.js"; export default function (AB, ibase) { ibase = ibase || "ui_work_object_workspace_popupImport"; diff --git a/test/_mock/AB.js b/test/_mock/AB.js index f9243166..fc7e31ba 100644 --- a/test/_mock/AB.js +++ b/test/_mock/AB.js @@ -19,6 +19,10 @@ export default class AB { this.Config = new Config(); this.Multilingual = Multilingual; } + + getPluginAPI() { + return { AB: this }; + } } class ClassUI extends EventEmitter { diff --git a/test/utils/CSVImporter.test.js b/test/utils/CSVImporter.test.js index e2b7358c..b70f1423 100644 --- a/test/utils/CSVImporter.test.js +++ b/test/utils/CSVImporter.test.js @@ -3,7 +3,7 @@ import assert from "assert"; import sinon from "sinon"; import AB from "../_mock/AB.js"; -import CSVImporter from "../../src/plugins/web_view_csvImporter/CSVImporter.js"; +import FCSVImporter from "../../src/plugins/web_view_csvImporter/CSVImporter.js"; function getMockAB() { return new AB(); @@ -11,20 +11,23 @@ function getMockAB() { function getTarget() { const ab = getMockAB(); - return new CSVImporter(ab); + const CSVImporter = FCSVImporter(ab.getPluginAPI()); + return new CSVImporter(); } describe("CSVImporter", function () { it(".constructor - should store AB to a local variable", function () { const ab = getMockAB(); - const target = new CSVImporter(ab); + const CSVImporter = FCSVImporter(ab.getPluginAPI()); + const target = new CSVImporter(); assert.equal(ab, target._AB); }); it(".L - should pass valid parameters and return result of .labelPlugin function", function () { const ab = getMockAB(); - const target = new CSVImporter(ab); + const CSVImporter = FCSVImporter(ab.getPluginAPI()); + const target = new CSVImporter(); const pluginKey = "ABDesigner"; const expectParams = ["A", "B", "C"]; const expectResult = "RESULT"; From 09161ae8820f1a1efc53f7678b5c12d54dba5f77 Mon Sep 17 00:00:00 2001 From: roguisharcanetrickster Date: Thu, 28 May 2026 16:56:44 +0700 Subject: [PATCH 04/11] lint --- .../FNAbviewcsvexporter.js | 21 +++---- .../FNAbviewcsvexporterEditor.js | 18 +++--- .../FNAbviewcsvimporter.js | 31 ++++------ .../FNAbviewcsvimporterEditor.js | 62 +++++++++---------- 4 files changed, 55 insertions(+), 77 deletions(-) diff --git a/src/plugins/web_view_csvExporter/FNAbviewcsvexporter.js b/src/plugins/web_view_csvExporter/FNAbviewcsvexporter.js index 20a80d70..f1beacce 100644 --- a/src/plugins/web_view_csvExporter/FNAbviewcsvexporter.js +++ b/src/plugins/web_view_csvExporter/FNAbviewcsvexporter.js @@ -11,9 +11,7 @@ export default function FNAbviewcsvexporterProperties({ const uiConfig = AB.Config.uiSettings(); const L = AB.Label(); - return class ABAbviewcsvexporterProperties extends ABViewPropertiesPlugin { - static getPluginKey() { return this.key; } @@ -23,9 +21,6 @@ export default function FNAbviewcsvexporterProperties({ // properties-view : will display in the properties panel of the ABDesigner } - - - constructor(baseID) { super(baseID ?? BASE_ID, { datacollection: "", @@ -157,8 +152,9 @@ export default function FNAbviewcsvexporterProperties({ autoheight: true, select: false, template: (item) => { - return `  ${item.label}`; + return `  ${item.label}`; }, on: { onItemClick: (id, e, node) => { @@ -205,19 +201,19 @@ export default function FNAbviewcsvexporterProperties({ $$(ids.hasHeader).setValue( view.settings.hasHeader ?? - ABViewCSVExporterPropertyComponentDefaults.hasHeader + ABViewCSVExporterPropertyComponentDefaults.hasHeader ); $$(ids.buttonLabel).setValue( view.settings.buttonLabel ?? - ABViewCSVExporterPropertyComponentDefaults.buttonLabel + ABViewCSVExporterPropertyComponentDefaults.buttonLabel ); $$(ids.filename).setValue( view.settings.filename ?? - ABViewCSVExporterPropertyComponentDefaults.filename + ABViewCSVExporterPropertyComponentDefaults.filename ); $$(ids.width).setValue( view.settings.width ?? - ABViewCSVExporterPropertyComponentDefaults.width + ABViewCSVExporterPropertyComponentDefaults.width ); this.populateFilter(); @@ -353,6 +349,5 @@ export default function FNAbviewcsvexporterProperties({ ViewClass() { return super._ViewClass("csvExporter"); } - } + }; } - diff --git a/src/plugins/web_view_csvExporter/FNAbviewcsvexporterEditor.js b/src/plugins/web_view_csvExporter/FNAbviewcsvexporterEditor.js index bcf5198a..285c2291 100644 --- a/src/plugins/web_view_csvExporter/FNAbviewcsvexporterEditor.js +++ b/src/plugins/web_view_csvExporter/FNAbviewcsvexporterEditor.js @@ -8,18 +8,17 @@ export default function FNAbviewcsvexporterEditor({ AB, ABViewEditorPlugin }) { // var L = ABViewContainer.L(); return class ABAbviewcsvexporterEditor extends ABViewEditorPlugin { - static getPluginKey() { return this.key; } - /** - * @method getPluginType - * return the plugin type for this editor. - * plugin types are how our ClassManager knows how to store - * the plugin. - * @return {string} plugin type - */ + /** + * @method getPluginType + * return the plugin type for this editor. + * plugin types are how our ClassManager knows how to store + * the plugin. + * @return {string} plugin type + */ static getPluginType() { return "editor-view"; // editor-view : will display in the editor panel of the ABDesigner @@ -50,7 +49,6 @@ export default function FNAbviewcsvexporterEditor({ AB, ABViewEditorPlugin }) { detatch() { this.component?.detatch?.(); super.detatch(); - } onShow() { @@ -58,6 +56,4 @@ export default function FNAbviewcsvexporterEditor({ AB, ABViewEditorPlugin }) { super.onShow(); } }; - - } diff --git a/src/plugins/web_view_csvImporter/FNAbviewcsvimporter.js b/src/plugins/web_view_csvImporter/FNAbviewcsvimporter.js index 56003fe3..efdeefa9 100644 --- a/src/plugins/web_view_csvImporter/FNAbviewcsvimporter.js +++ b/src/plugins/web_view_csvImporter/FNAbviewcsvimporter.js @@ -6,7 +6,6 @@ import FABViewRuleListFormRecordRules from "../../rootPages/Designer/properties/ export default function FNAbviewcsvimporterProperties({ AB, ABViewPropertiesPlugin, - // ABUIPlugin, }) { const BASE_ID = "properties_abview_csvimporter"; @@ -18,9 +17,7 @@ export default function FNAbviewcsvimporterProperties({ `${BASE_ID}_popupRecordRule` ); - return class ABAbviewcsvimporterProperties extends ABViewPropertiesPlugin { - static getPluginKey() { return this.key; } @@ -30,9 +27,6 @@ export default function FNAbviewcsvimporterProperties({ // properties-view : will display in the properties panel of the ABDesigner } - - - constructor(baseID) { super(baseID ?? BASE_ID, { datacollection: "", @@ -86,8 +80,9 @@ export default function FNAbviewcsvimporterProperties({ template: this.listTemplate.bind(this), type: { markCheckbox: function (item) { - return ``; + return ``; }, }, onClick: { @@ -212,7 +207,7 @@ export default function FNAbviewcsvimporterProperties({ f.selected = options.selectAll ? true : availableFields.filter((fieldId) => f.id == fieldId).length > - 0; + 0; return f; }); @@ -243,10 +238,13 @@ export default function FNAbviewcsvimporterProperties({ (v) => v.common().key == componentKey )[0]; - return `${$common.markCheckbox(field)} ${field.label - }
${formComponent ? L(formComponent.common().labelKey ?? "Label") : "" - }
`; + return `${$common.markCheckbox(field)} ${ + field.label + }
${ + formComponent ? L(formComponent.common().labelKey ?? "Label") : "" + }
`; } check(e, fieldId) { @@ -368,10 +366,5 @@ export default function FNAbviewcsvimporterProperties({ base.settings = this.defaultValues(); return base; } - } - - - - + }; } - diff --git a/src/plugins/web_view_csvImporter/FNAbviewcsvimporterEditor.js b/src/plugins/web_view_csvImporter/FNAbviewcsvimporterEditor.js index 7a5f3f3c..f185a69b 100644 --- a/src/plugins/web_view_csvImporter/FNAbviewcsvimporterEditor.js +++ b/src/plugins/web_view_csvImporter/FNAbviewcsvimporterEditor.js @@ -5,18 +5,17 @@ // export default function FNAbviewcsvimporterEditor({ AB, ABViewEditorPlugin }) { // var L = UIClass.L(); - // var L = ABViewContainer.L(); + // var L = ABViewContainer.L(); -return class ABAbviewcsvimporterEditor extends ABViewEditorPlugin { - -static getPluginKey() { + return class ABAbviewcsvimporterEditor extends ABViewEditorPlugin { + static getPluginKey() { return this.key; } -/** + /** * @method getPluginType * return the plugin type for this editor. - * plugin types are how our ClassManager knows how to store + * plugin types are how our ClassManager knows how to store * the plugin. * @return {string} plugin type */ @@ -25,40 +24,35 @@ static getPluginKey() { // editor-view : will display in the editor panel of the ABDesigner } + static get key() { + return "csvImporter"; + } + constructor(view, base = "interface_editor_csvImporter") { + // base: {string} unique base id reference + super(view, base); + } - static get key() { - return "csvImporter"; - } - - constructor(view, base = "interface_editor_csvImporter") { - // base: {string} unique base id reference - - super(view, base); - } - - ui() { - return this.component.ui(); - } - - init(AB) { - this.AB = AB; + ui() { + return this.component.ui(); + } - this.component.init(this.AB); + init(AB) { + this.AB = AB; - // this.component.onShow(); - // in our editor, we provide accessLv = 2 - } + this.component.init(this.AB); - detatch() { - this.component?.detatch?.(); - } + // this.component.onShow(); + // in our editor, we provide accessLv = 2 + } - onShow() { - this.component?.onShow?.(); - } - }; - + detatch() { + this.component?.detatch?.(); + } + onShow() { + this.component?.onShow?.(); + } + }; } From 45e69d867f1d14239bcbe68c428ea9cb87126eb5 Mon Sep 17 00:00:00 2001 From: roguisharcanetrickster Date: Fri, 29 May 2026 13:55:21 +0700 Subject: [PATCH 05/11] restore base CSV importer --- .../Designer/properties/PropertyManager.js | 1 - .../ui_work_object_list_newObject_csv.js | 2 +- .../ui_work_object_workspace_popupImport.js | 4 +- src/utils/CSVImporter.js | 153 ++++++++++++++++++ 4 files changed, 156 insertions(+), 4 deletions(-) create mode 100644 src/utils/CSVImporter.js diff --git a/src/rootPages/Designer/properties/PropertyManager.js b/src/rootPages/Designer/properties/PropertyManager.js index f3717dd9..b5ce7015 100644 --- a/src/rootPages/Designer/properties/PropertyManager.js +++ b/src/rootPages/Designer/properties/PropertyManager.js @@ -79,7 +79,6 @@ export default function (AB) { // require("./views/ABViewComment"), require("./views/ABViewConditionalContainer"), require("./views/ABViewContainer"), - // // require("./views/ABViewCSVImporter"), require("./views/ABViewDataFilter"), // require("./views/ABViewDataSelect"), // require("./views/ABViewDataview"), diff --git a/src/rootPages/Designer/ui_work_object_list_newObject_csv.js b/src/rootPages/Designer/ui_work_object_list_newObject_csv.js index ad98934d..d52af93b 100644 --- a/src/rootPages/Designer/ui_work_object_list_newObject_csv.js +++ b/src/rootPages/Designer/ui_work_object_list_newObject_csv.js @@ -5,7 +5,7 @@ * */ import UI_Class from "./ui_class"; -import CSVImporter from "./ui_work_object_workspace_popupImport"; +import CSVImporter from "../../utils/CSVImporter"; export default function (AB) { const UIClass = UI_Class(AB); diff --git a/src/rootPages/Designer/ui_work_object_workspace_popupImport.js b/src/rootPages/Designer/ui_work_object_workspace_popupImport.js index ff356683..84a6c4c1 100644 --- a/src/rootPages/Designer/ui_work_object_workspace_popupImport.js +++ b/src/rootPages/Designer/ui_work_object_workspace_popupImport.js @@ -6,12 +6,12 @@ */ import UI_Class from "./ui_class"; -import FNAbviewcsvimporterProperties from "../../plugins/web_view_csvImporter/FNAbviewcsvimporter.js"; +import FNAbviewcsvimporterProperties from "./plugins/web_view_csvImporter/FNAbviewcsvimporter"; export default function (AB, ibase) { ibase = ibase || "ui_work_object_workspace_popupImport"; const UIClass = UI_Class(AB); - const ViewPropertiesClass = FNAbviewcsvimporterProperties(AB.pluginAPI()); + const ViewPropertiesClass = FNAbviewcsvimporterProperties(AB); const viewProperties = new ViewPropertiesClass(); class UI_Work_Object_Workspace_PopupImport extends UIClass { diff --git a/src/utils/CSVImporter.js b/src/utils/CSVImporter.js new file mode 100644 index 00000000..7b007346 --- /dev/null +++ b/src/utils/CSVImporter.js @@ -0,0 +1,153 @@ +const FieldTypeTool = require("./FieldTypeTool"); + +module.exports = class CSVImporter { + constructor(AB, fileReader = FileReader) { + this._AB = AB; + this._FileReader = fileReader; + } + + L(...params) { + return this._AB.Multilingual.labelPlugin("ABDesigner", ...params); + } + + getSeparateItems() { + return [ + { id: ",", value: this.L("Comma (,)") }, + { + id: "\t", + value: this.L("Tab (      )"), + }, + { id: ";", value: this.L("Semicolon (;)") }, + { id: "s", value: this.L("Space ( )") }, + ]; + } + + /** + * @method validateFile + * Validate file extension + * + * @param {*} fileInfo - https://docs.webix.com/api__ui.uploader_onbeforefileadd_event.html + * + * @return {boolean} + */ + validateFile(fileInfo) { + if (!fileInfo || !fileInfo.file || !fileInfo.file.type) return false; + + // validate file type + let extensionType = fileInfo.file.type.toLowerCase(); + if ( + extensionType == "text/csv" || + extensionType == "application/vnd.ms-excel" + ) { + return true; + } else { + return false; + } + } + + /** + * @method getDataRows + * Pull data rows from the CSV file + * + * @param {Object} fileInfo - https://docs.webix.com/api__ui.uploader_onbeforefileadd_event.html + * @param {string} separatedBy + * + * @return {Promise} -[ + * ["Value 1.1", "Value 1.2", "Value 1.3"], + * ["Value 2.1", "Value 2.2", "Value 2.3"], + * ] + */ + async getDataRows(fileInfo, separatedBy) { + if (!this.validateFile(fileInfo)) + return Promise.reject(this.L(".fileInfo parameter is invalid")); + + return new Promise((resolve, reject) => { + // read CSV file + let reader = new this._FileReader(); + reader.onload = (e) => { + const result = this.convertToArray(reader.result, separatedBy); + + resolve(result); + }; + reader.readAsText(fileInfo.file); + }); + } + + /** + * @method convertToArray + * Pull data rows from the CSV file + * + * @param {string} text + * @param {string} separatedBy + * + * @return {Promise} -[ + * ["Value 1.1", "Value 1.2", "Value 1.3"], + * ["Value 2.1", "Value 2.2", "Value 2.3"], + * ] + */ + convertToArray(text = "", separatedBy = ",") { + let result = []; + + // split lines + let dataRows = text + .split(/\r\n|\n|\r/) // CRLF = \r\n; LF = \n; CR = \r; + .filter((row) => row && row.length > 0); + + // split columns + (dataRows || []).forEach((row) => { + let dataCols = []; + if (separatedBy == ",") { + // NOTE: if the file contains ,, .match(), then can not recognize this empty string + row = row.replace(/,,/g, ", ,"); + + // https://stackoverflow.com/questions/11456850/split-a-string-by-commas-but-ignore-commas-within-double-quotes-using-javascript#answer-11457952 + dataCols = row.match(/(".*?"|[^",]+)(?=\s*,|\s*$)/g); + } else { + dataCols = row.split(separatedBy); + } + + result.push(dataCols.map((dCol) => this.reformat(dCol))); + }); + + return result; + } + + /** + * @method getGuessDataType + * + * @param dataRows {Array} - [ + * ["Value 1.1", "Value 1.2", "Value 1.3"], + * ["Value 2.1", "Value 2.2", "Value 2.3"], + * ] + * @param colIndex {Number} + * + * @return {string} + */ + getGuessDataType(dataRows, colIndex) { + var data, + repeatNum = 10; + + // Loop to find a value + for (var i = 1; i <= repeatNum; i++) { + var line = dataRows[i]; + if (!line) break; + + data = line[colIndex]; + + if (data != null && data.length > 0) break; + } + + return FieldTypeTool.getFieldType(data); + } + + /** + * @method reformat + * + * @param {string} str + */ + reformat(str) { + if (!str) return ""; + + return str.trim().replace(/"/g, "").replace(/'/g, ""); + } +}; \ No newline at end of file From c49d82ec05e189a4587716c7165126189cb1b5f5 Mon Sep 17 00:00:00 2001 From: roguisharcanetrickster Date: Fri, 29 May 2026 14:07:25 +0700 Subject: [PATCH 06/11] use require --- .../FNAbviewcsvimporter.js | 3 +- .../properties/views/ABViewCSVImporter.js | 57 +++++++++++++++++++ .../ui_work_object_workspace_popupImport.js | 4 +- 3 files changed, 60 insertions(+), 4 deletions(-) create mode 100644 src/rootPages/Designer/properties/views/ABViewCSVImporter.js diff --git a/src/plugins/web_view_csvImporter/FNAbviewcsvimporter.js b/src/plugins/web_view_csvImporter/FNAbviewcsvimporter.js index efdeefa9..6c7bd0e7 100644 --- a/src/plugins/web_view_csvImporter/FNAbviewcsvimporter.js +++ b/src/plugins/web_view_csvImporter/FNAbviewcsvimporter.js @@ -1,7 +1,6 @@ // FNAbviewcsvimporter Properties // A properties side import for an ABView. // -import FABViewRuleListFormRecordRules from "../../rootPages/Designer/properties/rules/ABViewRuleListFormRecordRules.js"; export default function FNAbviewcsvimporterProperties({ AB, @@ -11,7 +10,7 @@ export default function FNAbviewcsvimporterProperties({ const uiConfig = AB.Config.uiSettings(); const L = AB.Label(); - + const FABViewRuleListFormRecordRules = require("../../rootPages/Designer/properties/rules/ABViewRuleListFormRecordRules.js").default; const PopupRecordRule = FABViewRuleListFormRecordRules( AB, `${BASE_ID}_popupRecordRule` diff --git a/src/rootPages/Designer/properties/views/ABViewCSVImporter.js b/src/rootPages/Designer/properties/views/ABViewCSVImporter.js new file mode 100644 index 00000000..39aafaa9 --- /dev/null +++ b/src/rootPages/Designer/properties/views/ABViewCSVImporter.js @@ -0,0 +1,57 @@ +/** + * ABViewCSVImporterEditor + * The widget that displays the UI Editor Component on the screen + * when designing the UI. + */ +let myClass = null; +// {singleton} +// we will want to call this factory fn() repeatedly in our imports, +// but we only want to define 1 Class reference. + +import UI_Class from "../../ui_class"; + +export default function (AB) { + if (!myClass) { + const UIClass = UI_Class(AB); + // var L = UIClass.L(); + // var L = ABViewContainer.L(); + + myClass = class ABViewCSVImporterEditor extends UIClass { + static get key() { + return "csvImporter"; + } + + constructor(view, base = "interface_editor_csvImporter") { + // base: {string} unique base id reference + + super(view, base); + + this.view = view; + this.component = this.view.component(); + } + + ui() { + return this.component.ui(); + } + + init(AB) { + this.AB = AB; + + this.component.init(this.AB); + + // this.component.onShow(); + // in our editor, we provide accessLv = 2 + } + + detatch() { + this.component?.detatch?.(); + } + + onShow() { + this.component?.onShow?.(); + } + }; + } + + return myClass; +} \ No newline at end of file diff --git a/src/rootPages/Designer/ui_work_object_workspace_popupImport.js b/src/rootPages/Designer/ui_work_object_workspace_popupImport.js index 84a6c4c1..2948a851 100644 --- a/src/rootPages/Designer/ui_work_object_workspace_popupImport.js +++ b/src/rootPages/Designer/ui_work_object_workspace_popupImport.js @@ -6,12 +6,12 @@ */ import UI_Class from "./ui_class"; -import FNAbviewcsvimporterProperties from "./plugins/web_view_csvImporter/FNAbviewcsvimporter"; +import FViewProperties from "./properties/views/ABViewCSVImporter"; export default function (AB, ibase) { ibase = ibase || "ui_work_object_workspace_popupImport"; const UIClass = UI_Class(AB); - const ViewPropertiesClass = FNAbviewcsvimporterProperties(AB); + const ViewPropertiesClass = FViewProperties(AB); const viewProperties = new ViewPropertiesClass(); class UI_Work_Object_Workspace_PopupImport extends UIClass { From 123b6cc41b10251b08c7f641622d62575b1febb5 Mon Sep 17 00:00:00 2001 From: roguisharcanetrickster Date: Fri, 29 May 2026 14:38:23 +0700 Subject: [PATCH 07/11] fix component error --- .../web_view_csvImporter/FNAbviewcsvimporterEditor.js | 8 +++++--- test/_mock/AB.js | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/plugins/web_view_csvImporter/FNAbviewcsvimporterEditor.js b/src/plugins/web_view_csvImporter/FNAbviewcsvimporterEditor.js index f185a69b..ed9dcf31 100644 --- a/src/plugins/web_view_csvImporter/FNAbviewcsvimporterEditor.js +++ b/src/plugins/web_view_csvImporter/FNAbviewcsvimporterEditor.js @@ -35,11 +35,11 @@ export default function FNAbviewcsvimporterEditor({ AB, ABViewEditorPlugin }) { } ui() { - return this.component.ui(); + return super.ui(); } - init(AB) { - this.AB = AB; + async init(AB) { + await super.init(AB); this.component.init(this.AB); @@ -49,10 +49,12 @@ export default function FNAbviewcsvimporterEditor({ AB, ABViewEditorPlugin }) { detatch() { this.component?.detatch?.(); + super.detatch(); } onShow() { this.component?.onShow?.(); + super.onShow(); } }; } diff --git a/test/_mock/AB.js b/test/_mock/AB.js index fc7e31ba..7bd28e10 100644 --- a/test/_mock/AB.js +++ b/test/_mock/AB.js @@ -1,4 +1,4 @@ -const EventEmitter = require("events").EventEmitter; +import { EventEmitter } from "events"; export default class AB { constructor(definitions) { From 047533416aef0a81e6f282f242bd900d3fbadba2 Mon Sep 17 00:00:00 2001 From: roguisharcanetrickster Date: Fri, 29 May 2026 15:05:27 +0700 Subject: [PATCH 08/11] update app-id to client-id --- .github/workflows/e2e-test.yml | 2 +- .../web_view_csvExporter/FNAbviewcsvexporterEditor.js | 8 +------- .../web_view_csvImporter/FNAbviewcsvimporterEditor.js | 8 +------- 3 files changed, 3 insertions(+), 15 deletions(-) diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index e3e84e53..90e3ac9b 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -38,7 +38,7 @@ jobs: id: generate-token uses: actions/create-github-app-token@v3 with: - app-id: ${{ inputs.app_id }} + client-id: ${{ inputs.app_id }} private-key: ${{ secrets.app_secret }} repositories: ab_service_web diff --git a/src/plugins/web_view_csvExporter/FNAbviewcsvexporterEditor.js b/src/plugins/web_view_csvExporter/FNAbviewcsvexporterEditor.js index 285c2291..a1ee755a 100644 --- a/src/plugins/web_view_csvExporter/FNAbviewcsvexporterEditor.js +++ b/src/plugins/web_view_csvExporter/FNAbviewcsvexporterEditor.js @@ -38,21 +38,15 @@ export default function FNAbviewcsvexporterEditor({ AB, ABViewEditorPlugin }) { } async init(AB) { + this.AB = AB; await super.init(AB); - - this.component.init(this.AB); - - // this.component.onShow(); - // in our editor, we provide accessLv = 2 } detatch() { - this.component?.detatch?.(); super.detatch(); } onShow() { - this.component?.onShow?.(); super.onShow(); } }; diff --git a/src/plugins/web_view_csvImporter/FNAbviewcsvimporterEditor.js b/src/plugins/web_view_csvImporter/FNAbviewcsvimporterEditor.js index ed9dcf31..9cfcbbcd 100644 --- a/src/plugins/web_view_csvImporter/FNAbviewcsvimporterEditor.js +++ b/src/plugins/web_view_csvImporter/FNAbviewcsvimporterEditor.js @@ -39,21 +39,15 @@ export default function FNAbviewcsvimporterEditor({ AB, ABViewEditorPlugin }) { } async init(AB) { + this.AB = AB; await super.init(AB); - - this.component.init(this.AB); - - // this.component.onShow(); - // in our editor, we provide accessLv = 2 } detatch() { - this.component?.detatch?.(); super.detatch(); } onShow() { - this.component?.onShow?.(); super.onShow(); } }; From ef8c7902a357da9e8b575767ab188485a33ff96f Mon Sep 17 00:00:00 2001 From: roguisharcanetrickster Date: Fri, 29 May 2026 17:20:16 +0700 Subject: [PATCH 09/11] add tests --- .../FNAbviewcsvimporter.js | 5 +- test/_mock/AB.js | 4 ++ test/_mock/ABViewEditorPlugin.js | 26 +++++++ test/_mock/ABViewPropertiesPlugin.js | 30 ++++++++ test/_setup.js | 33 +++++++++ .../FNAbviewcsvimporter.test.js | 51 ++++++++++++++ .../FNAbviewcsvimporterEditor.test.js | 70 +++++++++++++++++++ 7 files changed, 217 insertions(+), 2 deletions(-) create mode 100644 test/_mock/ABViewEditorPlugin.js create mode 100644 test/_mock/ABViewPropertiesPlugin.js create mode 100644 test/plugins/web_view_csvImporter/FNAbviewcsvimporter.test.js create mode 100644 test/plugins/web_view_csvImporter/FNAbviewcsvimporterEditor.test.js diff --git a/src/plugins/web_view_csvImporter/FNAbviewcsvimporter.js b/src/plugins/web_view_csvImporter/FNAbviewcsvimporter.js index 6c7bd0e7..b66992ec 100644 --- a/src/plugins/web_view_csvImporter/FNAbviewcsvimporter.js +++ b/src/plugins/web_view_csvImporter/FNAbviewcsvimporter.js @@ -1,17 +1,18 @@ // FNAbviewcsvimporter Properties // A properties side import for an ABView. // +import FABViewRuleListFormRecordRules from "../../rootPages/Designer/properties/rules/ABViewRuleListFormRecordRules.js"; export default function FNAbviewcsvimporterProperties({ AB, ABViewPropertiesPlugin, + ABViewRuleListFormRecordRules = FABViewRuleListFormRecordRules, }) { const BASE_ID = "properties_abview_csvimporter"; const uiConfig = AB.Config.uiSettings(); const L = AB.Label(); - const FABViewRuleListFormRecordRules = require("../../rootPages/Designer/properties/rules/ABViewRuleListFormRecordRules.js").default; - const PopupRecordRule = FABViewRuleListFormRecordRules( + const PopupRecordRule = ABViewRuleListFormRecordRules( AB, `${BASE_ID}_popupRecordRule` ); diff --git a/test/_mock/AB.js b/test/_mock/AB.js index 7bd28e10..286b4d02 100644 --- a/test/_mock/AB.js +++ b/test/_mock/AB.js @@ -20,6 +20,10 @@ export default class AB { this.Multilingual = Multilingual; } + Label() { + return (key) => key; + } + getPluginAPI() { return { AB: this }; } diff --git a/test/_mock/ABViewEditorPlugin.js b/test/_mock/ABViewEditorPlugin.js new file mode 100644 index 00000000..722a9750 --- /dev/null +++ b/test/_mock/ABViewEditorPlugin.js @@ -0,0 +1,26 @@ +import sinon from "sinon"; + +export default class ABViewEditorPlugin { + constructor(view, base) { + this.view = view; + this.base = base; + this.component = { + ui: sinon.stub().returns({}), + init: sinon.stub(), + detatch: sinon.stub(), + onShow: sinon.stub(), + }; + } + + ui() { + return { view: "mock-editor-ui" }; + } + + async init(AB) { + this.AB = AB; + } + + detatch() {} + + onShow() {} +} diff --git a/test/_mock/ABViewPropertiesPlugin.js b/test/_mock/ABViewPropertiesPlugin.js new file mode 100644 index 00000000..830595b8 --- /dev/null +++ b/test/_mock/ABViewPropertiesPlugin.js @@ -0,0 +1,30 @@ +export default class ABViewPropertiesPlugin { + constructor(baseID, defaults) { + this.baseID = baseID; + this.defaults = defaults; + this.ids = {}; + Object.keys(defaults || {}).forEach((k) => { + this.ids[k] = `${baseID}_${k}`; + }); + } + + ui() { + return []; + } + + async init(AB) { + this.AB = AB; + } + + _ViewClass() { + return null; + } + + values() { + return { settings: {} }; + } + + populate() {} + + onChange() {} +} diff --git a/test/_setup.js b/test/_setup.js index b7c26f40..613fa701 100644 --- a/test/_setup.js +++ b/test/_setup.js @@ -1,7 +1,40 @@ +import Module from "module"; import { JSDOM } from "jsdom"; import webix from "./_mock/webix"; import webixElement from "./_mock/webix_element"; +const origLoad = Module._load; +Module._load = function (request, parent, isMain) { + if (request.endsWith(".css")) { + return {}; + } + if (request === "bpmn-js" || request.startsWith("bpmn-js/")) { + return function BpmnMock() { + return new Proxy( + function () { }, + { + get: () => () => { }, + apply: () => { }, + } + ); + }; + } + if (request.includes("ABViewRuleListFormRecordRules")) { + const popupStub = { + init() { }, + on() { }, + toSettings: () => [], + fromSettings() { }, + objectLoad() { }, + qbFixAfterShow() { }, + }; + const factory = () => popupStub; + factory.default = factory; + return factory; + } + return origLoad.apply(this, arguments); +}; + // Set web browser environment const dom = new JSDOM(""); global.window = dom.window; diff --git a/test/plugins/web_view_csvImporter/FNAbviewcsvimporter.test.js b/test/plugins/web_view_csvImporter/FNAbviewcsvimporter.test.js new file mode 100644 index 00000000..33e73e3d --- /dev/null +++ b/test/plugins/web_view_csvImporter/FNAbviewcsvimporter.test.js @@ -0,0 +1,51 @@ +import "@babel/polyfill"; +import assert from "assert"; + +import AB from "../../_mock/AB.js"; +import ABViewPropertiesPlugin from "../../_mock/ABViewPropertiesPlugin.js"; +import FNAbviewcsvimporterProperties from "../../../src/plugins/web_view_csvImporter/FNAbviewcsvimporter.js"; + +function recordRulesFactory() { + return { + init() {}, + on() {}, + toSettings: () => [], + fromSettings() {}, + objectLoad() {}, + qbFixAfterShow() {}, + }; +} + +function buildPropertiesClass() { + const ab = new AB(); + return FNAbviewcsvimporterProperties({ + AB: ab, + ABViewPropertiesPlugin, + ABViewRuleListFormRecordRules: recordRulesFactory, + }); +} + +describe("FNAbviewcsvimporterProperties", function () { + it("exposes csvImporter plugin metadata", function () { + const PropertiesClass = buildPropertiesClass(); + + assert.equal(PropertiesClass.key, "csvImporter"); + assert.equal(PropertiesClass.getPluginKey(), "csvImporter"); + assert.equal(PropertiesClass.getPluginType(), "properties-view"); + }); + + it("defaultValues() returns expected settings shape", function () { + const PropertiesClass = buildPropertiesClass(); + const props = new PropertiesClass(); + + const values = props.defaultValues(); + + assert.deepEqual(values, { + dataviewID: null, + buttonLabel: "Upload CSV", + width: 0, + recordRules: [], + availableFieldIds: [], + }); + }); +}); diff --git a/test/plugins/web_view_csvImporter/FNAbviewcsvimporterEditor.test.js b/test/plugins/web_view_csvImporter/FNAbviewcsvimporterEditor.test.js new file mode 100644 index 00000000..f9ae6a51 --- /dev/null +++ b/test/plugins/web_view_csvImporter/FNAbviewcsvimporterEditor.test.js @@ -0,0 +1,70 @@ +import "@babel/polyfill"; +import assert from "assert"; +import sinon from "sinon"; + +import AB from "../../_mock/AB.js"; +import ABViewEditorPlugin from "../../_mock/ABViewEditorPlugin.js"; +import FNAbviewcsvimporterEditor from "../../../src/plugins/web_view_csvImporter/FNAbviewcsvimporterEditor.js"; + +function buildEditorClass() { + const ab = new AB(); + return FNAbviewcsvimporterEditor({ + AB: ab, + ABViewEditorPlugin, + }); +} + +describe("FNAbviewcsvimporterEditor", function () { + let EditorClass; + let uiSpy; + let initSpy; + let detatchSpy; + let onShowSpy; + + beforeEach(function () { + EditorClass = buildEditorClass(); + uiSpy = sinon.spy(ABViewEditorPlugin.prototype, "ui"); + initSpy = sinon.spy(ABViewEditorPlugin.prototype, "init"); + detatchSpy = sinon.spy(ABViewEditorPlugin.prototype, "detatch"); + onShowSpy = sinon.spy(ABViewEditorPlugin.prototype, "onShow"); + }); + + afterEach(function () { + sinon.restore(); + }); + + it("exposes csvImporter plugin metadata", function () { + assert.equal(EditorClass.key, "csvImporter"); + assert.equal(EditorClass.getPluginKey(), "csvImporter"); + assert.equal(EditorClass.getPluginType(), "editor-view"); + }); + + it("delegates ui() to the editor base class", function () { + const editor = new EditorClass({}, "interface_editor_csvImporter"); + const result = editor.ui(); + + assert.equal(true, uiSpy.calledOnce); + assert.deepEqual(result, { view: "mock-editor-ui" }); + }); + + it("init(AB) sets AB and delegates to super.init", async function () { + const ab = new AB(); + const editor = new EditorClass({}, "interface_editor_csvImporter"); + + await editor.init(ab); + + assert.equal(ab, editor.AB); + assert.equal(true, initSpy.calledOnce); + assert.equal(ab, initSpy.firstCall.args[0]); + }); + + it("detatch() and onShow() delegate to super", function () { + const editor = new EditorClass({}, "interface_editor_csvImporter"); + + editor.detatch(); + editor.onShow(); + + assert.equal(true, detatchSpy.calledOnce); + assert.equal(true, onShowSpy.calledOnce); + }); +}); From 82dcbc5920734cfa035e50a2dce20be12fbd0894 Mon Sep 17 00:00:00 2001 From: roguisharcanetrickster Date: Wed, 3 Jun 2026 11:29:42 +0700 Subject: [PATCH 10/11] remove commented lines --- src/plugins/web_view_csvExporter/FNAbviewcsvexporter.js | 1 - .../web_view_csvExporter/FNAbviewcsvexporterEditor.js | 5 +---- src/plugins/web_view_csvImporter/FNAbviewcsvimporter.js | 3 +-- .../web_view_csvImporter/FNAbviewcsvimporterEditor.js | 5 +---- 4 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/plugins/web_view_csvExporter/FNAbviewcsvexporter.js b/src/plugins/web_view_csvExporter/FNAbviewcsvexporter.js index f1beacce..666157c3 100644 --- a/src/plugins/web_view_csvExporter/FNAbviewcsvexporter.js +++ b/src/plugins/web_view_csvExporter/FNAbviewcsvexporter.js @@ -4,7 +4,6 @@ export default function FNAbviewcsvexporterProperties({ AB, ABViewPropertiesPlugin, - // ABUIPlugin, }) { const BASE_ID = "properties_abview_csvexporter"; diff --git a/src/plugins/web_view_csvExporter/FNAbviewcsvexporterEditor.js b/src/plugins/web_view_csvExporter/FNAbviewcsvexporterEditor.js index a1ee755a..513ea8ba 100644 --- a/src/plugins/web_view_csvExporter/FNAbviewcsvexporterEditor.js +++ b/src/plugins/web_view_csvExporter/FNAbviewcsvexporterEditor.js @@ -3,10 +3,7 @@ // The Editor is displayed in the ABDesigner as a view is worked on. // The Editor allows a widget to be moved and placed on the canvas. // -export default function FNAbviewcsvexporterEditor({ AB, ABViewEditorPlugin }) { - // var L = UIClass.L(); - // var L = ABViewContainer.L(); - +export default function FNAbviewcsvexporterEditor({ ABViewEditorPlugin }) { return class ABAbviewcsvexporterEditor extends ABViewEditorPlugin { static getPluginKey() { return this.key; diff --git a/src/plugins/web_view_csvImporter/FNAbviewcsvimporter.js b/src/plugins/web_view_csvImporter/FNAbviewcsvimporter.js index b66992ec..6d42375c 100644 --- a/src/plugins/web_view_csvImporter/FNAbviewcsvimporter.js +++ b/src/plugins/web_view_csvImporter/FNAbviewcsvimporter.js @@ -6,13 +6,12 @@ import FABViewRuleListFormRecordRules from "../../rootPages/Designer/properties/ export default function FNAbviewcsvimporterProperties({ AB, ABViewPropertiesPlugin, - ABViewRuleListFormRecordRules = FABViewRuleListFormRecordRules, }) { const BASE_ID = "properties_abview_csvimporter"; const uiConfig = AB.Config.uiSettings(); const L = AB.Label(); - const PopupRecordRule = ABViewRuleListFormRecordRules( + const PopupRecordRule = FABViewRuleListFormRecordRules( AB, `${BASE_ID}_popupRecordRule` ); diff --git a/src/plugins/web_view_csvImporter/FNAbviewcsvimporterEditor.js b/src/plugins/web_view_csvImporter/FNAbviewcsvimporterEditor.js index 9cfcbbcd..9dec4ddc 100644 --- a/src/plugins/web_view_csvImporter/FNAbviewcsvimporterEditor.js +++ b/src/plugins/web_view_csvImporter/FNAbviewcsvimporterEditor.js @@ -3,10 +3,7 @@ // The Editor is displayed in the ABDesigner as a view is worked on. // The Editor allows a widget to be moved and placed on the canvas. // -export default function FNAbviewcsvimporterEditor({ AB, ABViewEditorPlugin }) { - // var L = UIClass.L(); - // var L = ABViewContainer.L(); - +export default function FNAbviewcsvimporterEditor({ ABViewEditorPlugin }) { return class ABAbviewcsvimporterEditor extends ABViewEditorPlugin { static getPluginKey() { return this.key; From 2237534a3edd8ad559080179178e0017f7ab8854 Mon Sep 17 00:00:00 2001 From: roguisharcanetrickster Date: Mon, 8 Jun 2026 17:41:58 +0700 Subject: [PATCH 11/11] fix old view file --- .../properties/views/ABViewCSVImporter.js | 386 ++++++++++++++++-- 1 file changed, 346 insertions(+), 40 deletions(-) diff --git a/src/rootPages/Designer/properties/views/ABViewCSVImporter.js b/src/rootPages/Designer/properties/views/ABViewCSVImporter.js index 39aafaa9..307b2876 100644 --- a/src/rootPages/Designer/properties/views/ABViewCSVImporter.js +++ b/src/rootPages/Designer/properties/views/ABViewCSVImporter.js @@ -1,57 +1,363 @@ -/** - * ABViewCSVImporterEditor - * The widget that displays the UI Editor Component on the screen - * when designing the UI. +/* + * ABViewCSVImporter + * A Property manager for our ABViewCSVImporter widget */ -let myClass = null; -// {singleton} -// we will want to call this factory fn() repeatedly in our imports, -// but we only want to define 1 Class reference. -import UI_Class from "../../ui_class"; +import FViewClass from "./ABView"; +import ABRecordRule from "../rules/ABViewRuleListFormRecordRules"; + +let PopupRecordRule = null; export default function (AB) { - if (!myClass) { - const UIClass = UI_Class(AB); - // var L = UIClass.L(); - // var L = ABViewContainer.L(); - - myClass = class ABViewCSVImporterEditor extends UIClass { - static get key() { - return "csvImporter"; - } + const BASE_ID = "properties_abview_csvimporter"; - constructor(view, base = "interface_editor_csvImporter") { - // base: {string} unique base id reference + const ABViewClassProperty = FViewClass(AB); + const uiConfig = AB.Config.uiSettings(); + const L = ABViewClassProperty.L(); - super(view, base); + class ABViewCSVImporterProperty extends ABViewClassProperty { + constructor(baseID) { + super(baseID ?? BASE_ID, { + datacollection: "", + fields: "", + buttonLabel: "", + buttonRecordRules: "", + width: "", + }); - this.view = view; - this.component = this.view.component(); - } + this.AB = AB; + } - ui() { - return this.component.ui(); - } + static get key() { + return "csvImporter"; + } - init(AB) { - this.AB = AB; + ui() { + const ids = this.ids; + PopupRecordRule = ABRecordRule(this.AB, this.base); + // PopupRecordRule.component(`${this.base}_recordrule`); - this.component.init(this.AB); + return super.ui([ + { + view: "fieldset", + label: L("Data:"), + labelWidth: uiConfig.labelWidthLarge, + body: { + id: ids.datacollection, + name: "datacollection", + view: "richselect", + label: L("Data Source"), + labelWidth: uiConfig.labelWidthLarge, + skipAutoSave: true, + on: { + onChange: (newVal) => this.selectSource(newVal), + }, + }, + }, + { + view: "fieldset", + label: L("Available Fields:"), + labelWidth: uiConfig.labelWidthLarge, + body: { + type: "clean", + padding: 10, + rows: [ + { + id: ids.fields, + name: "fields", + view: "list", + select: false, + minHeight: 250, + template: this.listTemplate.bind(this), + type: { + markCheckbox: function (item) { + return ``; + }, + }, + onClick: { + check: this.check.bind(this), + }, + }, + ], + }, + }, + { + view: "fieldset", + label: L("Rules:"), + labelWidth: uiConfig.labelWidthLarge, + body: { + type: "clean", + padding: 10, + rows: [ + { + cols: [ + { + view: "label", + label: L("Record Rules:"), + width: uiConfig.labelWidthLarge, + }, + { + id: ids.buttonRecordRules, + view: "button", + name: "buttonRecordRules", + css: "webix_primary", + label: L("Settings"), + icon: "fa fa-gear", + type: "icon", + badge: 0, + click: this.recordRuleShow.bind(this), + }, + ], + }, + ], + }, + }, + { + view: "fieldset", + label: L("Customize Display:"), + labelWidth: uiConfig.labelWidthLarge, + body: { + type: "clean", + padding: 10, + rows: [ + { + id: ids.buttonLabel, + name: "buttonLabel", + view: "text", + label: L("Label"), + labelWidth: uiConfig.labelWidthXLarge, + on: { + onChange: () => this.onChange(), + }, + }, + { + id: ids.width, + view: "counter", + name: "width", + label: L("Width:"), + labelWidth: uiConfig.labelWidthXLarge, + on: { + onChange: () => this.onChange(), + }, + }, + ], + }, + }, + ]); + } - // this.component.onShow(); - // in our editor, we provide accessLv = 2 - } + async init(AB) { + this.AB = AB; + + await super.init(AB); - detatch() { - this.component?.detatch?.(); + PopupRecordRule.init(AB); + PopupRecordRule.on("save", () => { + this.populateBadgeNumber(); + }); + } + + selectSource(dcId) { + const view = this.CurrentView; + view.settings = view.settings ?? {}; + view.settings.dataviewID = dcId; + + this.updateRules(); + this.populateAvailableFields({ selectAll: true }); + this.onChange(); + } + + updateRules() { + // Populate values to rules + const selectedDv = this.CurrentView.datacollection; + if (selectedDv?.datasource) { + PopupRecordRule.objectLoad(selectedDv.datasource); } - onShow() { - this.component?.onShow?.(); + PopupRecordRule.fromSettings( + this.CurrentView?.settings?.recordRules ?? [] + ); + } + + populateAvailableFields(options = {}) { + const ids = this.ids; + const view = this.CurrentView; + + const datacollection = this.AB.datacollectionByID( + view.settings.dataviewID + ); + const object = datacollection?.datasource; + + view.settings = view.settings ?? {}; + const availableFields = view.settings.availableFieldIds ?? []; + + // Pull field list + const fieldOptions = object?.fields()?.map((f) => { + f.selected = options.selectAll + ? true + : availableFields.filter((fieldId) => f.id == fieldId).length > + 0; + + return f; + }); + + $$(ids.fields).clearAll(); + $$(ids.fields).parse(fieldOptions); + } + + populateBadgeNumber() { + const ids = this.ids; + const view = this.CurrentView; + if (!view) return; + + $$(ids.buttonRecordRules).define( + "badge", + view.settings?.recordRules?.length ?? null + ); + $$(ids.buttonRecordRules).refresh(); + } + + listTemplate(field, $common) { + const fieldComponent = field.formComponent(); + if (fieldComponent == null) + return ` ${field.label}
Disable
`; + + const componentKey = fieldComponent.common().key; + const formComponent = this.CurrentApplication.viewAll( + (v) => v.common().key == componentKey + )[0]; + + return `${$common.markCheckbox(field)} ${ + field.label + }
${ + formComponent ? L(formComponent.common().labelKey ?? "Label") : "" + }
`; + } + + check(e, fieldId) { + const ids = this.ids; + + // update UI list + let item = $$(ids.fields).getItem(fieldId); + item.selected = item.selected ? 0 : 1; + $$(ids.fields).updateItem(fieldId, item); + this.onChange(); + } + + recordRuleShow() { + this.updateRules(); + if (PopupRecordRule.CurrentObject) PopupRecordRule.show(); + + // Workaround + PopupRecordRule.qbFixAfterShow(); + } + + populate(view) { + super.populate(view); + + const ids = this.ids; + + view.settings = view.settings ?? {}; + + this.populateDataCollections(); + this.populateAvailableFields(); + + $$(ids.buttonLabel).setValue(view.settings.buttonLabel); + $$(ids.width).setValue(view.settings.width); + + view.settings.availableFieldIds = []; + let fields = $$(ids.fields).find({ selected: true }); + (fields || []).forEach((f) => { + view.settings.availableFieldIds.push(f.id); + }); + } + + populateDataCollections() { + const ids = this.ids; + const view = this.CurrentView; + + const datacollections = + this.CurrentApplication.datacollectionsIncluded().map((dc) => { + return { + id: dc.id, + value: dc.label, + icon: + dc.sourceType === "query" + ? "fa fa-filter" + : "fa fa-database", + }; + }); + + const $d = $$(ids.datacollection); + $d.define("options", datacollections); + $d.define("value", view.settings.dataviewID); + $d.refresh(); + } + + defaultValues() { + const values = { + dataviewID: null, + buttonLabel: "Upload CSV", + width: 0, + recordRules: [], + availableFieldIds: [], + }; + + const FieldClass = this.ViewClass(); + if (FieldClass) { + const fcValues = FieldClass.defaultValues(); + Object.keys(fcValues).forEach((k) => { + values[k] = fcValues[k]; + }); } - }; + + return values; + } + + /** + * @method values + * return the values for this form. + * @return {obj} + */ + values() { + const ids = this.ids; + const values = super.values(); + + values.settings = values.settings ?? {}; + values.settings.dataviewID = $$(ids.datacollection).getValue(); + values.settings.recordRules = PopupRecordRule.toSettings(); + values.settings.buttonLabel = $$(ids.buttonLabel).getValue(); + values.settings.width = $$(ids.width).getValue(); + + values.settings.availableFieldIds = []; + $$(ids.fields) + .find({ selected: true }) + .forEach((f) => { + values.settings.availableFieldIds.push(f.id); + }); + + return values; + } + + /** + * @method FieldClass() + * A method to return the proper ABViewXXX Definition. + * NOTE: Must be overwritten by the Child Class + */ + ViewClass() { + return super._ViewClass("csvImporter"); + } + + toSettings() { + var base = this.defaults(); + base.settings = this.defaultValues(); + return base; + } } - return myClass; -} \ No newline at end of file + return ABViewCSVImporterProperty; +}