Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AppBuilder/core
Submodule core updated from 9c645d to c66267
2 changes: 2 additions & 0 deletions AppBuilder/platform/plugins/included/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import viewList from "./view_list/FNAbviewlist.js";
import viewTab from "./view_tab/FNAbviewtab.js";
import viewDetail from "./view_detail/FNAbviewdetail.js";
import viewText from "./view_text/FNAbviewtext.js";
import viewImage from "./view_image/FNAbviewimage.js";
import viewDataSelect from "./view_data-select/FNAbviewdataselect.js";
Expand All @@ -8,6 +9,7 @@ import viewPdfImporter from "./view_pdfImporter/FNAbviewpdfimporter.js";
const AllPlugins = [
viewTab,
viewList,
viewDetail,
viewText,
viewImage,
viewDataSelect,
Expand Down
140 changes: 140 additions & 0 deletions AppBuilder/platform/plugins/included/view_detail/FNAbviewdetail.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import FNAbviewdetailComponent from "./FNAbviewdetailComponent.js";

// Detail view plugin: replaces the original ABViewDetail / ABViewDetailCore.
// All logic from both Core and platform is contained in this file.
export default function FNAbviewdetail({
ABViewContainer,
ABViewContainerComponent,
ABViewComponentPlugin,
}) {
const ABViewDetailComponent = FNAbviewdetailComponent({
ABViewContainerComponent,
ABViewComponentPlugin,
});

const ABViewDetailDefaults = {
key: "detail",
icon: "file-text-o",
labelKey: "Detail(plugin)",
};

const ABViewDetailPropertyComponentDefaults = {
dataviewID: null,
showLabel: true,
labelPosition: "left",
labelWidth: 120,
height: 0,
};

return class ABViewDetailPlugin extends ABViewContainer {
/**
* @param {obj} values key=>value hash of ABView values
* @param {ABApplication} application the application object this view is under
* @param {ABView} parent the ABView this view is a child of. (can be null)
*/
constructor(values, application, parent, defaultValues) {
super(
values,
application,
parent,
defaultValues ?? ABViewDetailDefaults
);
}

static getPluginType() {
return "view";
}

static getPluginKey() {
return this.common().key;
}

static common() {
return ABViewDetailDefaults;
}

static defaultValues() {
return ABViewDetailPropertyComponentDefaults;
}

/**
* @method fromValues()
* Initialize this object with the given set of values.
* @param {obj} values
*/
fromValues(values) {
super.fromValues(values);

this.settings.labelPosition =
this.settings.labelPosition ||
ABViewDetailPropertyComponentDefaults.labelPosition;

this.settings.showLabel = JSON.parse(
this.settings.showLabel != null
? this.settings.showLabel
: ABViewDetailPropertyComponentDefaults.showLabel
);

this.settings.labelWidth = parseInt(
this.settings.labelWidth ||
ABViewDetailPropertyComponentDefaults.labelWidth
);
this.settings.height = parseInt(
this.settings.height ??
ABViewDetailPropertyComponentDefaults.height
);
}

/**
* @method componentList
* Return the list of components available on this view to display in the editor.
*/
componentList() {
const viewsToAllow = ["label", "text"];
const allComponents = this.application.viewAll();
return allComponents.filter((c) =>
viewsToAllow.includes(c.common().key)
);
}

addFieldToDetail(field, yPosition) {
if (field == null) return;

const newView = field
.detailComponent()
.newInstance(this.application, this);
if (newView == null) return;

newView.settings = newView.settings ?? {};
newView.settings.fieldId = field.id;
newView.settings.labelWidth =
this.settings.labelWidth ||
ABViewDetailPropertyComponentDefaults.labelWidth;
newView.settings.alias = field.alias;
newView.position.y = yPosition;

this._views.push(newView);
return newView;
}

/**
* @method component()
* Return a UI component based upon this view.
* @return {obj} UI component
*/
component() {
return new ABViewDetailComponent(this);
}

warningsEval() {
super.warningsEval();

const DC = this.datacollection;
if (!DC) {
this.warningsMessage(
`can't resolve it's datacollection[${this.settings.dataviewID}]`
);
}
}
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
export default function FNAbviewdetailComponent({
ABViewContainerComponent,
ABViewComponentPlugin,
}) {
const ContainerComponent =
ABViewContainerComponent?.default ?? ABViewContainerComponent;
const Base = ContainerComponent ?? ABViewComponentPlugin;
if (!Base) {
return class ABAbviewdetailComponent {};
}

return class ABAbviewdetailComponent extends Base {
constructor(baseView, idBase, ids) {
super(
baseView,
idBase || `ABViewDetail_${baseView.id}`,
Object.assign({ detail: "" }, ids)
);
this.idBase = idBase || `ABViewDetail_${baseView.id}`;
}

ui() {
if (!ContainerComponent) {
return this._uiDataviewFallback();
}
const _ui = super.ui();
return {
type: "form",
id: this.ids.component,
borderless: true,
rows: [{ body: _ui }],
};
}

_uiDataviewFallback() {
const settings = this.settings;
const _uiDetail = {
id: this.ids.detail,
view: "dataview",
type: { width: 1000, height: 30 },
template: (item) => (item ? JSON.stringify(item) : ""),
};
if (settings.height !== 0) _uiDetail.height = settings.height;
else _uiDetail.autoHeight = true;
const _ui = super.ui([_uiDetail]);
delete _ui.type;
return _ui;
}

onShow() {
const baseView = this.view;
try {
const dataCy = `Detail ${baseView.name?.split(".")[0]} ${baseView.id}`;
$$(this.ids.component)?.$view?.setAttribute("data-cy", dataCy);
} catch (e) {
console.warn("Problem setting data-cy", e);
}

const dv = this.datacollection;
if (dv) {
const currData = dv.getCursor();
if (currData) this.displayData(currData);

["changeCursor", "cursorStale", "collectionEmpty"].forEach((key) => {
this.eventAdd({
emitter: dv,
eventName: key,
listener: (...p) => this.displayData(...p),
});
});
this.eventAdd({
emitter: dv,
eventName: "create",
listener: (createdRow) => {
if (dv.getCursor()?.id === createdRow.id)
this.displayData(createdRow);
},
});
this.eventAdd({
emitter: dv,
eventName: "update",
listener: (updatedRow) => {
if (dv.getCursor()?.id === updatedRow.id)
this.displayData(updatedRow);
},
});
}

super.onShow?.();
}

displayData(rowData = {}) {
if (!ContainerComponent) return;
if (rowData == null && this.datacollection)
rowData = this.datacollection.getCursor() ?? {};

const views = (this.view.views() || []).sort((a, b) => {
if (!a?.field?.() || !b?.field?.()) return 0;
if (a.field().key === "formula" && b.field().key === "calculate")
return -1;
if (a.field().key === "calculate" && b.field().key === "formula")
return 1;
return 0;
});

views.forEach((f) => {
let val;
if (f.field) {
const field = f.field();
if (!field) return;

switch (field.key) {
case "connectObject":
val = field.pullRelationValues(rowData);
break;
case "list":
val = rowData?.[field.columnName];
if (!val || (Array.isArray(val) && val.length === 0)) {
val = "";
break;
}
if (field.settings.isMultiple === 0) {
let myVal = "";
(field.settings.options || []).forEach((opt) => {
if (opt.id === val) myVal = opt.text;
});
if (field.settings.hasColors) {
let hasCustomColor = "";
(field.settings.options || []).forEach((h) => {
if (h.text === myVal) {
hasCustomColor = "hascustomcolor";
}
});
const hex = (field.settings.options || []).find(
(o) => o.text === myVal
)?.hex ?? "#66666";
myVal = `<span class="webix_multicombo_value ${hasCustomColor}" style="background-color: ${hex} !important;"><span>${myVal}</span></span>`;
}
val = myVal;
} else {
const items = (val || []).map((value) => {

Check warning

Code scanning / CodeQL

Useless conditional Warning

This use of variable 'val' always evaluates to true.

Copilot Autofix

AI 4 days ago

In general, to fix useless conditionals where a variable is always truthy/falsey at a given point, remove the redundant logical operation or rewrite the logic to reflect the true set of possible values. Here, val is already ensured to be a non-empty array when the else branch is executed, because falsy and empty-array cases are caught earlier and converted to "". Thus (val || []) will always evaluate to val, making the || [] part useless. The best fix is to remove the fallback and iterate directly over val.

Concretely, in AppBuilder/platform/plugins/included/view_detail/FNAbviewdetailComponent.js, locate the case "list": handling, in the else block for field.settings.isMultiple !== 0. On line 141, change:

const items = (val || []).map((value) => {

to:

const items = val.map((value) => {

No additional imports, methods, or definitions are needed; this is a localized change that preserves existing functionality while removing the useless conditional.

Suggested changeset 1
AppBuilder/platform/plugins/included/view_detail/FNAbviewdetailComponent.js

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/AppBuilder/platform/plugins/included/view_detail/FNAbviewdetailComponent.js b/AppBuilder/platform/plugins/included/view_detail/FNAbviewdetailComponent.js
--- a/AppBuilder/platform/plugins/included/view_detail/FNAbviewdetailComponent.js
+++ b/AppBuilder/platform/plugins/included/view_detail/FNAbviewdetailComponent.js
@@ -138,7 +138,7 @@
                         }
                         val = myVal;
                      } else {
-                        const items = (val || []).map((value) => {
+                        const items = val.map((value) => {
                            let myVal = "";
                            (field.settings.options || []).forEach((opt) => {
                               if (opt.id === value.id) myVal = opt.text;
EOF
@@ -138,7 +138,7 @@
}
val = myVal;
} else {
const items = (val || []).map((value) => {
const items = val.map((value) => {
let myVal = "";
(field.settings.options || []).forEach((opt) => {
if (opt.id === value.id) myVal = opt.text;
Copilot is powered by AI and may make mistakes. Always verify output.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have changed this line of code to "if (!val || (Array.isArray(val) && val.length === 0))".

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you update line 141? or did you update it back at line 118?

let myVal = "";
(field.settings.options || []).forEach((opt) => {
if (opt.id === value.id) myVal = opt.text;
});
const optionHex =
field.settings.hasColors && value.hex
? `background: ${value.hex};`
: "";
const hasCustomColor =
field.settings.hasColors && value.hex
? "hascustomcolor"
: "";
return `<span class="webix_multicombo_value ${hasCustomColor}" style="${optionHex}" optvalue="${value.id}"><span>${myVal}</span></span>`;
});
val = items.join("");
}
break;
case "user":
val = field.pullRelationValues(rowData);
break;
case "file":
val = rowData?.[field.columnName] ?? "";
break;
case "formula":
val = rowData ? field.format(rowData, false) : "";
break;
default:
val = field.format(rowData);
}
}

const vComponent = f.component(this.idBase);
vComponent?.setValue?.(val);
vComponent?.displayText?.(rowData);
});
}
};
}
20 changes: 0 additions & 20 deletions test/AppBuilder/platform/views/ABViewDetail.test.js

This file was deleted.

Loading