How can I apply a domain filter to a JSON field in Odoo?

I have a field in my Python model:

analytic_distribution = fields.Json(inverse=”_inverse_analytic_distribution”)

In the XML, this field utilizes a widget:

<t t-name="analytic.AnalyticDistributionPopup">
    <div class="analytic_distribution_popup dropdown-menu o-dropdown--menu show rounded py-0 overflow-x-hidden" t-if="state.showDropdown" t-ref="analyticDropdown">
        <div class="p-2 table-responsive">
            <span t-if="!allPlans.length">No analytic plans found</span>
            <table t-else="" class="o_list_table table table-sm table-hover o_analytic_table mb-2 table-striped">
                <t t-set="totals" t-value="planTotals()"/>
                <thead>
                    <tr class="border-bottom">
                        <th t-foreach="allPlans" t-as="plan" t-key="plan.id">
                            <span t-out="plan.name"/> (<span t-att-class="totals[plan.id].class" t-out="totals[plan.id].formattedValue"/>)
                        </th>
                        <th t-out="'Percentage'" class="numeric_column_width"/>
                        <th t-if="valueColumnEnabled" class="numeric_column_width" t-out="props.record.fields[props.amount_field].string"/>
                        <th class="deleteColumn w-20px"/>
                    </tr>
                </thead>
                <tbody>
                    <tr t-foreach="state.formattedData" t-as="line" t-key="line.id" t-att-id="line_index" t-att-name="'line_' + line_index">
                        <Record t-props="recordProps(line)" t-slot-scope="data">
                            <td t-foreach="Object.keys(data.record.fields).filter((f) => f.startsWith('x_plan'))" t-as="field" t-key="field">
                                <Field id="field" name="field" record="data.record" domain="data.record.fields[field].domain" canOpen="false" canCreate="false" canCreateEdit="false" canQuickCreate="false"/>
                            </td>
                            <td class="numeric_column_width">
                                <Field id="'percentage'" name="'percentage'" record="data.record"/>
                            </td>
                            <td t-if="valueColumnEnabled" class="numeric_column_width">
                                <Field id="props.amount_field" name="props.amount_field" record="data.record"/>
                            </td>
                            <td class="w-20px">
                                <span class="fa fa-trash-o cursor-pointer" t-on-click.stop="() => this.deleteLine(line_index)"/>
                            </td>
                        </Record>
                    </tr>
                    <tr>
                        <td t-on-click.stop.prevent="addLine" class="o_field_x2many_list_row_add" t-att-colspan="allPlans.length + 2 + valueColumnEnabled">
                            <a href="#" t-ref="addLineButton" tabindex="0">Add a Line</a>
                        </td>
                    </tr>
                </tbody>
            </table>
        </div>
    </div>
</t>

And this is the JavaScript code of the widget:

export class AnalyticDistribution extends Component {
    static template = "analytic.AnalyticDistribution";
    static components = {
        TagsList,
        Record,
        Field,
    }

    static props = {
        ...standardFieldProps,
        business_domain: { type: String, optional: true },
        account_field: { type: String, optional: true },
        product_field: { type: String, optional: true },
        amount_field: { type: String, optional: true },
        business_domain_compute: { type: String, optional: true },
        force_applicability: { type: String, optional: true },
        allow_save: { type: Boolean, optional: true },
    }

    setup(){
        this.orm = useService("orm");
        this.batchedOrm = useService("batchedOrm");

        this.state = useState({
            showDropdown: false,
            formattedData: [],
        });

        this.widgetRef = useRef("analyticDistribution");
        this.dropdownRef = useRef("analyticDropdown");
        this.mainRef = useRef("mainElement");
        this.addLineButton = useRef("addLineButton");
        usePosition("analyticDropdown", () => this.widgetRef.el);

        this.nextId = 1;
        this.focusSelector = false;

        this.currentValue = this.props.record.data[this.props.name];

        onWillStart(this.willStart);
        useRecordObserver(this.willUpdateRecord.bind(this));
        onPatched(this.patched);

        useExternalListener(window, "click", this.onWindowClick, true);
        useExternalListener(window, "resize", this.onWindowResized);

        this.openTemplate = useOpenMany2XRecord({
            resModel: "account.analytic.distribution.model",
            activeActions: {
                create: true,
                edit: false,
                write: true,
            },
            isToMany: false,
            onRecordSaved: async (record) => {
                if (!this.props.record.model.multiEdit) {
                    this.mainRef.el.focus();
                }
            },
            onClose: () => {
                if (!this.props.record.model.multiEdit) {
                    this.mainRef.el.focus();
                }
            },
            fieldString: _t("Analytic Distribution Model"),
        });
        this.allPlans = [];
        this.lastAccount = this.props.account_field && this.props.record.data[this.props.account_field] || false;
        this.lastProduct = this.props.product_field && this.props.record.data[this.props.product_field] || false;
    }

    // Lifecycle
    async willStart() {
        if (this.editingRecord) {
            // for performance in list views, plans are not retrieved until they are required.
            await this.fetchAllPlans(this.props);
        }
        await this.jsonToData(this.props.record.data[this.props.name]);
    }

    async willUpdateRecord(record) {
        const valueChanged =
            JSON.stringify(this.currentValue) !==
            JSON.stringify(record.data[this.props.name]);
        const currentAccount = this.props.account_field && record.data[this.props.account_field] || false;
        const currentProduct = this.props.product_field && record.data[this.props.product_field] || false;
        const accountChanged = !shallowEqual(this.lastAccount, currentAccount);
        const productChanged = !shallowEqual(this.lastProduct, currentProduct);
        if (valueChanged || accountChanged || productChanged) {
            if (!this.props.force_applicability) {
                await this.fetchAllPlans({ record });
            }
            this.lastAccount = accountChanged && currentAccount || this.lastAccount;
            this.lastProduct = productChanged && currentProduct || this.lastProduct;
            await this.jsonToData(record.data[this.props.name]);
        }
        this.currentValue = record.data[this.props.name];
    }

    patched() {
        this.focusToSelector();
    }

    accountTotalsByPlan() {
        const accountTotals = {};
        this.state.formattedData.map((line) => {
            line.analyticAccounts.map((column) => {
                if (column.accountId) {
                    let {
                        accId = column.accountId,
                        accName = column.accountDisplayName,
                        total = 0.0,
                        planId = column.accountRootPlanId,
                        planColor = column.accountColor,
                    } = accountTotals[column.accountRootPlanId]?.[column.accountId] || {};

                    total += roundDecimals(line.percentage, this.decimalPrecision.digits[1] + 2);

                    accountTotals[planId] = accountTotals[planId] || {};
                    accountTotals[planId][accId] = { accId, accName, planId, total, planColor};
                }
            })
        });
        return accountTotals;
    }

    planTotals() {
        const summary = this.accountTotalsByPlan();
        this.allPlans.map((plan) => {
            const planTotal = (summary[plan.id] && Object.values(summary[plan.id]) || []).reduce((prev, next) => prev + next.total, 0.0);
            const className = plan.applicability === "mandatory" && !this.planIsComplete(planTotal) ? 'text-danger' : plan.applicability === "mandatory" ? 'text-success' : '';
            summary[plan.id] = {
                value: planTotal,
                formattedValue: formatPercentage(planTotal, this.decimalPrecision),
                class: className,
                applicability: plan.applicability,
            }
        });
        return summary;
    }

    planIsComplete(total) {
        return roundDecimals(total, this.decimalPrecision.digits[1] + 2) === 1;
    }

    planSummaryTags() {
        const accountTotals = this.accountTotalsByPlan();
        return Object.values(accountTotals).map((planSummary) => {
            const accs = Object.values(planSummary);
            return {
                id: accs[0].planId,
                text: accs.reduce((p, n) => p + (p.length ? " | " : "") + (this.planIsComplete(n.total) ? n.accName : `${formatPercentage(n.total)} ${n.accName}`) , ""),
                colorIndex: accs[0].planColor,
                onClick: (ev) => this.tagClicked(ev),
            };
        });
    }

    plansToArray() {
        return this.allPlans.map((plan) => ({
            planId: plan.id,
            planName: plan.name,
            planColor: plan.color,
        }));
    }

    async jsonToData(jsonFieldValue) {
        const analyticAccountIds = jsonFieldValue ? Object.keys(jsonFieldValue).map((key) => key.split(',')).flat().map((id) => parseInt(id)) : [];
        const analyticAccountDict = analyticAccountIds.length ? await this.fetchAnalyticAccounts([["id", "in", analyticAccountIds]]) : [];

        let distribution = [];
        let accountNotFound = false;

        for (const [accountIds, percentage] of Object.entries(jsonFieldValue)) {
            const defaultVals = this.plansToArray(); // empty if the popup was not opened
            const ids = accountIds.split(',');

            for (const id of ids) {
                const account = analyticAccountDict[parseInt(id)];
                if (account) {
                    // since tags are displayed even though plans might not be retrieved (ie defaultVals is empty)
                    // push the accounts anyway, as order doesn't matter
                    // once the popup is opened, plans are fetched and the analyticAccounts list will be ordered
                    Object.assign(defaultVals.find((plan) => plan.planId == account.root_plan_id[0]) || defaultVals.push({}) && defaultVals[defaultVals.length-1],
                    {
                        accountId: parseInt(id),
                        accountDisplayName: account.display_name,
                        accountColor: account.color,
                        accountRootPlanId: account.root_plan_id[0],
                    });
                } else {
                    accountNotFound = true;
                }
            }
            distribution.push({
                analyticAccounts: defaultVals,
                percentage: percentage / 100,
                id: this.nextId++,
            })
        }
        this.state.formattedData = distribution;
        if (accountNotFound) {
            // Analytic accounts in the json were not found, save the json without them
            await this.save();
        }
    }

    recordProps(line) {
        const analyticAccountFields = {
            id: { type: "int" },
            display_name: { type: "char" },
            color: { type: "int" },
            plan_id: { type: "many2one" },
            root_plan_id: { type: "many2one" },
        };
        let recordFields = {};
        const values = {};
        // Analytic Account fields
        line.analyticAccounts.map((account) => {
            const fieldName = `x_plan${account.planId}_id`;
            recordFields[fieldName] = {
                string: account.planName,
                relation: "account.analytic.account",
                type: "many2one",
                related: {
                    fields: analyticAccountFields,
                    activeFields: analyticAccountFields,
                },
                // company domain might be required here
                domain: [["root_plan_id", "=", account.planId]],
            };
            values[fieldName] =  account?.accountId || false;
        });
        // Percentage field
        recordFields['percentage'] = {
            string: _t("Percentage"),
            type: "percentage",
            cellClass: "numeric_column_width",
            ...this.decimalPrecision,
        };
        values['percentage'] = line.percentage;
        if (this.props.amount_field) {
            const { string, name, type, currency_field } = this.props.record.fields[this.props.amount_field];
            recordFields[name] = { string, name, type, currency_field, cellClass: "numeric_column_width" };
            values[name] = this.props.record.data[name] * values['percentage'];
            if (currency_field) {
                const { string, name, type, relation } = this.props.record.fields[currency_field];
                recordFields[currency_field] = { name, string, type, relation, invisible: true };
                values[currency_field] = this.props.record.data[currency_field][0];
            }
        }
        return {
            fields: recordFields,
            values: values,
            activeFields: recordFields,
            onRecordChanged: async (record, changes) => await this.lineChanged(record, changes, line),
        }
    }

    accountCount(line) {
        return line.analyticAccounts.map((acc) => acc.accountId).filter(Boolean).length;
    }

    lineIsValid(line) {
        return this.accountCount(line) && line.percentage;
    }

    // ORM
    fetchPlansArgs({ record }) {
        let args = {};
        if (this.props.business_domain_compute) {
            args['business_domain'] = evaluateExpr(this.props.business_domain_compute, record.evalContext);
        }
        if (this.props.business_domain) {
            args['business_domain'] = this.props.business_domain;
        }
        if (this.props.product_field && record.data[this.props.product_field]) {
            args['product'] = record.data[this.props.product_field][0];
        }
        if (this.props.account_field && record.data[this.props.account_field]) {
            args['account'] = record.data[this.props.account_field][0];
        }
        if (this.props.force_applicability) {
            args['applicability'] = this.props.force_applicability;
        }
        const existing_account_ids = Object.keys(record.data[this.props.name]).map((k) => k.split(",")).flat().map((i) => parseInt(i));
        if (existing_account_ids.length) {
            args['existing_account_ids'] = existing_account_ids;
        }
        if (record.data.company_id) {
            args['company_id'] = record.data.company_id[0];
        }
        return args;
    }

    async fetchAllPlans(props) {
        const argsPlan = this.fetchPlansArgs(props);
        this.allPlans = await this.orm.call("account.analytic.plan", "get_relevant_plans", [], argsPlan);
    }

    async fetchAnalyticAccounts(domain) {
        const args = {
            domain: domain,
            fields: ["id", "display_name", "root_plan_id", "color"],
            context: [],
        }
        // batched call
        const records = await this.batchedOrm.read("account.analytic.account", domain[0][2], args.fields, {});
        return Object.assign({}, ...records.map((r) => {
            const {id, ...rest} = r;
            return {[id]: rest};
        }));
    }

    // Editing Distributions
    async lineChanged(record, changes, line) {
        // record analytic account changes to the state
        for (const account of line.analyticAccounts) {
            const selected = record.data[`x_plan${account.planId}_id`];
            account.accountId = selected[0];
            account.accountDisplayName = selected[1];
            account.accountColor = account.planColor;
            account.accountRootPlanId = account.planId;
        }
        if (changes.percentage != line.percentage) {
            roundDecimals(line.percentage = record.data.percentage, this.decimalPrecision.digits[1] + 2);
        } else if (
            this.valueColumnEnabled &&
            changes[this.props.amount_field] != line[this.props.amount_field]
        ) {
            line.percentage = roundDecimals(
                record.data[this.props.amount_field] / this.props.record.data[this.props.amount_field],
                this.decimalPrecision.digits[1] + 2);
        }
    }

    get valueColumnEnabled() {
        return Boolean(this.props.amount_field && this.props.record.data[this.props.amount_field]);
    }

    get decimalPrecision() {
        return { digits: [12, this.props.record.data.analytic_precision || 2] };
    }

    get allowSave() {
        return this.props.allow_save && this.state.formattedData.some((line) => this.lineIsValid(line));
    }

    get editingRecord() {
        return !this.props.readonly;
    }

    get isDropdownOpen() {
        return this.state.showDropdown && !!this.dropdownRef.el;
    }

    // actions
    addLine() {
        let maxMandatory = 0, maxOptional = 0, hasMandatory = false;

        Object.values(this.planTotals()).filter((plan) => plan.value < 1).map((plan) => {
            if (plan.applicability == "mandatory"){
                maxMandatory = Math.max(plan.value, maxMandatory);
                hasMandatory = true;
            } else {
                maxOptional = Math.max(plan.value, maxOptional);
            }
        });
        let noPlanTotal = this.state.formattedData.filter((line) => !this.accountCount(line)).reduce((p, n) => p + n.percentage, 0);
        const remainder = roundDecimals(1 - (hasMandatory ? maxMandatory : (maxOptional || noPlanTotal)), this.decimalPrecision.digits[1] + 2);
        const lineToAdd = {
            id: this.nextId++,
            analyticAccounts: this.plansToArray(),
            percentage: Math.max(remainder, 0) || 1,
        }
        this.state.formattedData.push(lineToAdd);
        this.setFocusSelector(`[name=line_${this.state.formattedData.length - 1}] td:first-of-type`);
    }

    deleteLine(index) {
        this.state.formattedData.splice(index, 1);
        if (!this.state.formattedData.length) {
            this.addLine();
        }
    }

    dataToJson() {
        const result = {};
        this.state.formattedData = this.state.formattedData.filter((line) => this.accountCount(line));
        this.state.formattedData.map((line) => {
            const key = line.analyticAccounts.reduce((p, n) => p.concat(n.accountId ? n.accountId : []), []);
            result[key] = (result[key] || 0) + line.percentage * 100;
        });
        return result;
    }

    async save() {
        await this.props.record.update({ [this.props.name]: this.dataToJson() });
    }

    onSaveNew() {
        this.closeAnalyticEditor();
        const { record, product_field, account_field } = this.props;
        this.openTemplate({ resId: false, context: {
            'default_analytic_distribution': this.dataToJson(),
            'default_partner_id': record.data['partner_id'] ? record.data['partner_id'][0] : undefined,
            'default_product_id': product_field ? record.data[product_field][0] : undefined,
            'default_account_prefix': account_field ? record.data[account_field][1].substr(0, 3) : undefined,
        }});
    }

    forceCloseEditor() {
        this.preventOpen = true;
        this.closeAnalyticEditor();
        this.mainRef.el.focus();
        this.preventOpen = false;
    }

    closeAnalyticEditor() {
        this.save();
        this.state.showDropdown = false;
    }

    async openAnalyticEditor() {
        if (!this.allPlans.length) {
            await this.fetchAllPlans(this.props);
            await this.jsonToData(this.props.record.data[this.props.name]);
        }
        if (!this.state.formattedData.length) {
            await this.addLine();
        }
        this.setFocusSelector("[name='line_0'] td:first-of-type");
        this.state.showDropdown = true;
    }

    async tagClicked(ev) {
        if (this.editingRecord && !this.isDropdownOpen) {

            await this.openAnalyticEditor();
        }
        if (this.isDropdownOpen) {
            this.setFocusSelector("[name='line_0'] td:first-of-type");
            this.focusToSelector();
            ev.stopPropagation();
        }
    }

    // Focus
    onMainElementFocus(ev) {
        if (!this.isDropdownOpen && !this.preventOpen) {
            this.openAnalyticEditor();
        }
    }

    focusToSelector() {
        if (this.focusSelector && this.isDropdownOpen) {
            this.focus(this.adjacentElementToFocus("next", this.dropdownRef.el.querySelector(this.focusSelector)));
        }
        this.focusSelector = false;
    }

    setFocusSelector(selector) {
        this.focusSelector = selector;
    }

    adjacentElementToFocus(direction, el = null) {
        if (!this.isDropdownOpen) {
            return null;
        }
        if (!el) {
            el = this.dropdownRef.el;
        }
        return direction == "next" ? getNextTabableElement(el) : getPreviousTabableElement(el);
    }

    focusAdjacent(direction) {
        const elementToFocus = this.adjacentElementToFocus(direction);
        if (elementToFocus){
            this.focus(elementToFocus);
            return true;
        }
        return false;
    }

    focus(el) {
        if (!el) return;
        el.focus();
        if (["INPUT", "TEXTAREA"].includes(el.tagName)) {
            if (el.selectionStart) {
                el.selectionStart = 0;
                el.selectionEnd = el.value.length;
            }
            el.select();
        }
    }

    async onWidgetKeydown(ev) {
        if (!this.editingRecord) {
            return;
        }
        const hotkey = getActiveHotkey(ev);
        switch (hotkey) {
            case "enter":
            case "tab": {
                if (this.isDropdownOpen) {
                    const closestCell = ev.target.closest("td, th");
                    const row = closestCell.parentElement;
                    const line = this.state.formattedData[parseInt(row.id)];
                    if (this.adjacentElementToFocus("next") == this.addLineButton.el && line && this.lineIsValid(line)) {
                        this.addLine();
                        break;
                    }
                    this.focusAdjacent("next") || this.forceCloseEditor();
                    break;
                };
                return;
            }
            case "shift+tab": {
                if (this.isDropdownOpen) {
                    this.focusAdjacent("previous") || this.forceCloseEditor();
                    break;
                };
                return;
            }
            case "escape": {
                if (this.isDropdownOpen) {
                    this.forceCloseEditor();
                    break;
                }
            }
            case "arrowdown": {
                if (!this.isDropdownOpen) {
                    this.onMainElementFocus();
                    break;
                }
                return;
            }
            default: {
                return;
            }
        }
        ev.preventDefault();
        ev.stopPropagation();
    }

    onWindowClick(ev) {
        const selectors = [
            ".o_popover",
            ".modal:not(.o_inactive_modal):not(:has(.o_act_window))",
        ];
        if (this.isDropdownOpen
            && !this.widgetRef.el.contains(ev.target)
            && !ev.target.closest(selectors.join(","))
            && !ev.target.isSameNode(document.documentElement)
           ) {
            this.forceCloseEditor();
        }
    }

    onWindowResized() {
        // popup ui is ugly when window is resized, so close it
        if (this.isDropdownOpen && !isMobileOS()) {
            this.forceCloseEditor();
        }
    }
}

export const analyticDistribution = {
    component: AnalyticDistribution,
    supportedTypes: ["char", "text"],
    fieldDependencies: [{ name:"analytic_precision", type: "integer" }],
    supportedOptions: [
        {
            label: _t("Disable save"),
            name: "disable_save",
            type: "boolean",
        },
        {
            label: _t("Force applicability"),
            name: "force_applicability",
            type: "boolean",
        },
        {
            label: _t("Business domain"),
            name: "business_domain",
            type: "string",
        },
        {
            label: _t("Product field"),
            name: "product_field",
            type: "field",
            availableTypes: ["many2one"],
        },
        {
            label: _t("Amount field"),
            name: "amount_field",
            type: "field",
            availableTypes: ["monetary"],
        },
        {
            label: _t("Account field"),
            name: "account_field",
            type: "field",
            availableTypes: ["many2one"],
        }
    ],
    extractProps: ({ attrs, options }) => ({
        business_domain: options.business_domain,
        account_field: options.account_field,
        product_field: options.product_field,
        amount_field: options.amount_field,
        business_domain_compute: attrs.business_domain_compute,
        force_applicability: options.force_applicability,
        allow_save: !options.disable_save,
    }),
};

registry.category("fields").add("analytic_distribution", analyticDistribution);

The analytic account is selected via a dropdown, as shown in the image (the field next to the percentage). I want to add a filter so that the accounts displayed are filtered with the following conditions: if budget.lines has a budget_id and budget_general_id that match general_budget_id, then display its analytic_account_id. If budget_id and budget_general_id are empty, then display all accounts. Thank you in advance!

Is there a way to use collations in javascript applications

Looking for a way to compare strings in a javascript browser application using collations supported by relational databases.

Lets say my javascript application talks to a SQL server that’s using “SQL_Latin1_General_CP1_CI_AS” or a Snowflake instance that’s using “es-ci” etc. I want to compare strings like the database would. The application is using some C++ compiled to WebAssembly without wasi (target wasm32-unknown-unknown).

Here’s what I was able to find, some of which might be outdated.

(1) wasi isn’t supported by browsers
(2) wasi has strcoll
(3) wasi is missing libicu

Couldn’t find a javascript module for collations.

Is there a way?

Classic ASP & Google Analytic : How do I set up the tracking properties for my website

I’m maintaining a classic asp website and I’m trying to implement Google Analytic on one of the pages. And I have 2 types of javascript codes, which I used inside my classic asp.

With [Code 1], although the page is being tracked, but I CANNOT modify any data such as the page title and so on.

With [Code 2], I can modify the tracking properties, but my page got counted twice. How do i fix this?

How can I modify the tracking info and make my google analytic count my page only one time?

[Code 1]

<body>
    <script type="module">
        import { initializeApp } from "https://www.gstatic.com/firebasejs/11.4.0/firebase-app.js";
        import { getAnalytics, logEvent } from "https://www.gstatic.com/firebasejs/11.4.0/firebase-analytics.js";

        // Firebase Configuration
        const firebaseConfig = {"-------"};

        // Initialize Firebase
        const app = initializeApp(firebaseConfig);
        const analytics = getAnalytics(app);
    </script>
</body>

[Code 2]

<body>
    <script type="module">
        import { initializeApp } from "https://www.gstatic.com/firebasejs/11.4.0/firebase-app.js";
        import { getAnalytics, logEvent } from "https://www.gstatic.com/firebasejs/11.4.0/firebase-analytics.js";

        // Firebase Configuration
        const firebaseConfig = {"-------"};

        // Initialize Firebase
        const app = initializeApp(firebaseConfig);
        const analytics = getAnalytics(app);

         if (!window.gaEventLogged) {
            window.gaEventLogged = true;
            logEvent(analytics, 'page_view', { 
                page_title: document.title, 
                page_location: window.location.href,  
                page_path: window.location.pathname,  
                page_query: window.location.search,   
                page_hash: window.location.hash,      
                page_referrer: document.referrer      
            });
            console.log("Page View Event Sent to Firebase Analytics");
        }
    </script>
</body>

javascript free lib to display xls / xlsx on browser [closed]

I looking JavaScript open source/free library enable me to display (only, no need edit) xls/xlsx on browser like pdf .

my system allow user to upload documents (image,pdf, xls) . when user want to view uploaded documents , the system will display on browser using object tag for image, pdf. for unsupported , the system create a download , user open the file using program installed on their machine .

i only need display xls/xlsx contents , download and print (like pdf) . I google around for xls/xlsx viewer but all need pay .

Why is the object in Axios POST request valid? [duplicate]

The code below contains an assignment:

const newItem = { name, quantity: Number(quantity), price: Number(price) };

My understanding from JavaScript is that an object is composed of key: value pairs. The first item in the object, i.e. name, is not part of a key: value pair.

In fact, when I try to create in my browser console the object above or similar objects, I get an error, Uncaught SyntaxError: missing : after property id.

However, when I run the code, it performs well, no error is thrown, and I can see the data added to my MySQL database table (inventory). For reference, I also include below the back-end route inventoryRoutes.js.

AddInventoryItem.jsx (front end):

const AddInventoryItem = () => {
  const [name, setName] = useState('');
  const [quantity, setQuantity] = useState('');
  const [price, setPrice] = useState('');
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState('');

  const handleSubmit = async (e) => {
    e.preventDefault();
    setLoading(true);
    setError('');

    try {
      const newItem = { name, quantity: Number(quantity), price: Number(price) };
      await axios.post('http://localhost:5000/api/inventory/add', newItem);
      . . . 

inventoryRoutes.js (backend):

/ Route to add a new inventory item
router.post('/add', async (req, res) => {
  const { name, quantity, price } = req.body;
  console.log(JSON.stringify(req.body));
  try {
    console.log("In POST route . . . ");
    console.log(`Name: ${name}, Quantity: ${quantity}, Price: ${price}`);
    await db.query('INSERT INTO inventory (name, quantity, price) VALUES (?, ?, ?)', [name, quantity, price]);
    res.status(201).json({ name, quantity, price });
  } catch (error) {
    res.status(500).json({ error: 'Error adding item' });
  }
});

Why does this work? Or, put another way, why does my browser console throw an error, but JSX/Axios/Express are able to handle the syntax in the object (if in fact this is an “object”)?

i get the call exception error from ethers when try to call a smart contract function

I get this error –
ethers error gotten from the console

The code

const signer = await getSigner();
const response = await axios.post(`${API}/certificate/get-metadata-url`, { certificateName, name, type, description });

const metadataURI = response.data.metadataURI;

const certificateContract = new ethers.Contract(contract, abi, signer);

if (price === 0) {
  const minted = await certificateContract.NoPriceMint(metadataURI);
  console.log({ minted });

  await minted.wait();
  return minted.hash;
}

the abi

const abi = ["function NoPriceMint(string memory metadataURI) external", "function priceMint (string memory metadataURI) external payable"]

How to Increase the Size of Mermaid Flowchart Nodes and Justify Text Inside Without Increasing the Node Height?

I am working with Mermaid flowcharts and trying to increase the size of the nodes, particularly the first two nodes, but I’m facing an issue. When I increase the size, the text inside the node doesn’t fully justify, and the node’s height increases too much, taking up unnecessary space. Here’s what I’ve tried so far:

enter image description here“`

%%| fig-width: 8
%%| fig-height: 10
flowchart 
  classDef sectionTitle fill:#006D6B,font-size:30px,color:white;
  classDef pathway fill:#30bfbf,stroke:#006D6B,stroke-width:2px,color:black, text-align:left, font-size:18px;
  classDef output fill:#ccFFFF,stroke:#006D6B,stroke-width:2px,color:black;
  classDef outcome fill:#97FFFF,stroke:#006D6B,stroke-width:2px,color:black;
  classDef impact fill:#00FFFF,stroke:#006D6B,stroke-width:2px,color:black;
  %% Problem
    A[&emsp;&emsp;&emsp;<span style="font-weight:bold;font-size:30px">Survey:</span><br> A survey was conducted among seminar participants, asking questions about attendance at the seminar;⠀emsp;&emsp;&emsp;emsp;&emsp;&emsp;]
  style A fill:red,font-size:28px, 
  
  %% Solution
B[&emsp;&emsp;&emsp; The survey assessed overall feedback on the dissemination seminar. &emsp;&emsp;&emsp;]

  style B fill:#87CEEB,font-size:28px 

 %% Force Straight Lines
  linkStyle default stroke: black,stroke-width:3px, color: black
  %% Pathways
  subgraph " "
    PA["Pathways"]:::sectionTitle
  C1[<span style="font-weight:bold;font-size:28px">C1]
  style C1 fill: green,font-size:26px

  C2[<span style="font-weight:bold;font-size:28px">C2]
  style C2 fill:green,font-size:26px
  end
 
  %% Outputs
   subgraph " "
   OT["Outputs"]:::sectionTitle
  D1[D1]
  style D1 fill:#DDA0DD,stroke:#333,stroke-width:2px,color:black,font-size:20px

  D2[D2]
  style D2 fill:#DDA0DD,stroke:#333,stroke-width:2px,color:black,font-size:26px
 end
 
  %% Outcomes
   subgraph " "
   OC["Outcomes"]:::sectionTitle
  E1[E1]
  style E1 fill:#FFA07A,stroke:#333,stroke-width:2px,color:black,font-size:26px
  E2[E2]
  style E2 fill:#FFA07A,stroke:#333,stroke-width:2px,color:black,font-size:26px
  E3[E3]
  style E3 fill:#FFA07A,stroke:#333,stroke-width:2px,color:black,font-size:26px
  E4[E4]
  style E4 fill:#FFA07A,stroke:#333,stroke-width:2px,color:black,font-size:26px
  end
  
  %% Impacts
   subgraph " "
   IM["Impacts"]:::sectionTitle
  F1[F1]
  style F1 fill:#FFA07A,stroke:#333,stroke-width:2px,color:black,font-size:26px
  F2[F2]
  style F2 fill:#FFA07A,stroke:#333,stroke-width:2px,color:black,font-size:26px
  F3[F3]
  style F3 fill:#FFA07A,stroke:#333,stroke-width:2px,color:black,font-size:26px
  F4[F4]
  style F4 fill:#FFA07A,stroke:#333,stroke-width:2px,color:black,font-size:26px
  F5[F5]
  style F5 fill:#FFA07A,stroke:#333,stroke-width:2px,color:black,font-size:26px
  F6[F6]
  style F6 fill:#FFA07A,stroke:#333,stroke-width:2px,color:black,font-size:26px
  end
  %% Define Flow
  A --> B
  B --> C1
  B --> C2
  C1 --> D1
  C2 --> D2
  D1 --> E1
  D1 --> E2
  D1 --> E3
  D2 --> E4
  E1 --> F1
  E2 --> F2
  E3 --> F3
  E3 --> F1
  E3 --> F4
  E4 --> F4
  E4 --> F5
  E4 --> F6
  E1 --> F3

Exceljs and playwright: Problems with the scope a variable into playwright

I try to read information from a excel file through ‘exceljs’. I put into a variable a data from the excel file and then, I try to use this variable to fill a form of a webpage through playwright. This is a extract of the code:

    const { chromium } = require("playwright");
    const ExcelJS = require('exceljs');
    const wb = new ExcelJS.Workbook();
    const fileName = 'Book1.xlsx';
 
    wb.xlsx.readFile(fileName).then(() => {
    const ws = wb.getWorksheet('RenM1ACont');
    var salary = Number(ws.getCell('I69').value.result).toFixed(0).toString();
  
    console.log(salary);
    return salary;
    
    }).catch(err => {
    console.log(err.message);
    });

    (async () => {
      // launch a new Chromium browser instance
     const browser = await chromium.launch({
     headless: false,
     });
    const page = await browser.newPage();

    // browser automation for web scraping...
 
    await page.goto("https://someweb");

    await page.getByTitle('Field1').fill("123");
    await page.getByTitle('Field2').fill("1971");

    await page.getByTitle('name.').fill(salary);

I get the error: salary is not defined.

I would appreciate your help. Thanks in advance.

I try to read an Excel file to obtain a value from a cell and use that cell value as an argument to fill in a web form. I try to use Playwright and Exceljs.

Custom Dropdown Remains Selected Even After Clicking Clear Button

I am building a custom dropdown menu using JavaScript and CSS, and I want the clear button to reset the selection. However, even after clicking the clear button, the dropdown remains selected, and the selected class is not removed properly.

Expected Behavior:
• Clicking the clear button (.clear-btn) should:
• Remove the selected class from the dropdown.
• Reset the selected text to “Select an option”.
• Hide the clear button.
• Close the dropdown list.

Current Behavior:
• The clear button resets the text, but the dropdown remains selected.
• The selected class does not get removed properly.
• The dropdown stays open instead of closing.

Code Below:

  1. HTML
<div class="custom-dropdown selected active" id="locationDropdown">
    <div class="dropdown-header" tabindex="0">
        <span class="selected-text">Sapporo, Hokkaido</span>
        <span class="arrow">▼</span>
        <span class="clear-btn" style="display: inline-block;">✖</span>
    </div>
    <ul class="dropdown-list show">
        <li>Tokyo</li>
        <li>Osaka</li>
        <li>Sapporo, Hokkaido</li>
    </ul>
</div>

2.JS


function activateDropdowns() {
    document.querySelectorAll(".custom-dropdown").forEach(dropdown => {
        const list = dropdown.querySelector(".dropdown-list");
        const clearBtn = dropdown.querySelector(".clear-btn");
        const selectedText = dropdown.querySelector(".selected-text");

        dropdown.addEventListener("click", function (event) {
            event.stopPropagation(); // Prevents dropdown from closing immediately

            if (event.target.classList.contains("clear-btn")) {  
                console.log("Clear button clicked!"); // Debugging Step 1

                dropdown.classList.remove("selected"); // ❌ This doesn't seem to remove the class properly
                selectedText.textContent = "Select an option";  
                clearBtn.style.display = "none";

                dropdown.classList.remove("active");
                list.classList.remove("show");

            } else if (event.target.closest(".dropdown-header")) {  
                // Toggle dropdown only if clicking on header (not clear-btn)
                console.log("Dropdown header clicked"); 

                document.querySelectorAll(".custom-dropdown").forEach(d => {
                    if (d !== dropdown) {
                        d.classList.remove("active");
                        d.querySelector(".dropdown-list").classList.remove("show");
                    }
                });

                dropdown.classList.toggle("active");
                list.classList.toggle("show");
            }
        });
    });

    // Close dropdown when clicking outside
    document.addEventListener("click", function () {
        document.querySelectorAll(".custom-dropdown").forEach(dropdown => {
            dropdown.classList.remove("active");
            dropdown.querySelector(".dropdown-list").classList.remove("show");
        });
    });
}
activateDropdowns();

3.CSS

.custom-dropdown {
  position: relative;
  width: 200px;
  background: #E3DCC3; /* Neutral beige */
  border-radius: 8px;
  cursor: pointer;
}

.dropdown-header {
  display: flex;
  justify-content: space-between;
  padding: 10px;
  background: white;
}

.clear-btn {
  display: none;
  cursor: pointer;
}

.custom-dropdown.selected .clear-btn {
  display: inline-block;
}

.custom-dropdown.active .dropdown-list {
  display: block;
}

Steps that I have tried:
✅ Logged the clear button click event → It fires correctly.
✅ Manually removed the .selected class in DevTools → It works there.
✅ Tried .classList.toggle(“selected”) instead of .remove() → No change.
✅ Checked if any CSS styles are preventing changes → Nothing found.

Question:

Why is my .selected class not being removed properly when I click the clear button?
• Is my event delegation incorrect?
• Could CSS be overriding the class change?
• Am I missing an additional property that needs to be reset?

Thank you in advanse for any help.

Text Wrapping in Javascript, what am I missing?

I’m building a custom pdf for my organization to use on its documents. I was able to give it fields for things like name, title and date. But they would like me to field to enter a short description or sentence. The issue I’m running into is while users can input long strings it becomes cut off once they reach the end of the text box. The box itself is created using our PDF editors’ built in tools while the script is applied via properties. I know I need to give it a wrapping function to show the full user input but I have a very rudimentary understanding of JavaScript as I’m learning as I go and could use some help.

I applied my current code to show what I’m working with.

var cAsk = "Enter Narrative" ;
var cTitle = "Narrative";

if(stampState == 'applying')
{​​​​
    var cMsg = app.response(cAsk, cTitle);
    event.value = cMsg;
}​​​​

this is a link to a visual example of the application I’m working within

node_modules/react-native/scripts/react_native_pods_utils/script_phases.sh:line 34:/opt/homebrew/Cellar/node/23.6.1/bin/node:No such file or directory

“Hello developers, I have an issue in my React Native project (version 0.77). Android is working fine, but when I try to run it on the iOS simulator in Xcode, I get an error. Here are the error logs:

Node found at: /opt/homebrew/Cellar/node/23.6.1/bin/node  
/Users/Documents/ProjectName/ios/../node_modules/react-native/scripts/react_native_pods_utils/script_phases.sh: line 34: /opt/homebrew/Cellar/node/23.6.1/bin/node: No such file or directory  
Command PhaseScriptExecution failed with a nonzero exit code  

I have tried every solution, but it has not worked.

My machine is a MacBook Air 2020 (M1) with the latest macOS Sequoia and the latest Xcode version 15.”

How do I get applications submitted and appointments booked from Facebook Marketing API?

Within Ads Manager I can see applications submitted and appointments booked. When I try to grab all the metrics I need from Ads Manager using Facebook API I can get all the ones I need EXCEPT applications submitted and appointments booked. I’ve tried everything I can think of, have spent 2 days scouring google, used ChatGPT AND Meta ai to no avail. I really need to get this figured out for a project I’m working on ASAP. Anyone have any ideas?

If it helps, here’s the code i have thus far that is the closest I have to working. It works with leads at least but still doesn’t get me applications submitted and appointments scheduled which are two of the most important metrics.

    var urlAdAccountInsights = "https://graph.facebook.com/v22.0/act_" + adAccountId + 
"/insights?fields=campaign_name%2cimpressions%2cspend%2ccpc%2cctr%2ccpm%2cfrequency%2cclicks%2cinline_link_clicks%2cactions%2caction_values%2cconversion_values" + 
"&date_preset=this_month&access_token=" + token;


var responseCampaignInsights = UrlFetchApp.fetch(urlAdAccountInsights);
var json = JSON.parse(responseCampaignInsights);
var data = json.data;


if (data.length > 0) {
var et = JSON.stringify(data);
var sub = et.substring(1, et.length - 1);
var campaignData = JSON.parse(sub);

var campaignName = campaignData.campaign_name;
var impressions = campaignData.impressions;
var spend = campaignData.spend;
var cpc = campaignData.cpc;
var cpm = campaignData.cpm;
var ctr = campaignData.ctr;
var frequency = campaignData.frequency;
var clicks = campaignData.clicks;
var linkclicks = campaignData.inline_link_clicks;


var leads = 0;
  var applications = 0;
  var appointments = 0;

if (campaignData.actions) {
    campaignData.actions.forEach(action => {
        if (action.action_type === "lead") {
            leads = action.value;
        } else if (action.action_type === "SubmitApplication") {
            applications = action.value;
        } else if (action.action_type === "schedule_total") {
            appointments = action.value;
        }
    });
}


Logger.log(campaignData);

ERR_NETWORK_IMPORT_DISALLOWED when I run `firebase emulators:start`

I am implementing a payment gateway. I am using Firebase Cloud Functions to run these APIs. I have a functions folder that has my functions and everything is set up for them. I am emulating the functions right now to check if everything is working.

When I run firebase emulators:start in the terminal, it shows ERR_NETWORK_IMPORT_DISALLOWED this error with error stack information. When I looked up the error it says that it could have something to do with using CommonJS and ESModule. I added in the package.json relating to the functions folder to use ESModule by adding this "type": "module" and still the error persists. The whole project is using ESModule. Also the functions were loaded before so I do not know what could cause this. I also tried using eslint and configuring it but it didn’t show me anything.

ps: It also gave me a warning that the requested node version was 18 while the version I am using is 22. I tried to change it in the package.json to be 22 but still the same warning. I don’t know if that contributes to my problem or not.

I will post the debug info that appeared when I ran firebase emulators:start --debug, it is quite long lol.

package.json contents:

{
  "name": "functions",
  "description": "Cloud Functions for Firebase",
  "type": "module",
  "scripts": {
    "serve": "firebase emulators:start --only functions",
    "shell": "firebase functions:shell",
    "start": "npm run shell",
    "deploy": "firebase deploy --only functions",
    "logs": "firebase functions:log"
  },
  "engines": {
    "node": "18"
  },
  "main": "index.js",
  "dependencies": {
    "firebase-admin": "^12.7.0",
    "firebase-functions": "^6.3.0",
    "functions": "file:"
  },
  "devDependencies": {
    "firebase-functions-test": "^3.1.0"
  },
  "private": true
}
[2025-03-05T20:49:28.298Z] Building nodejs source
[2025-03-05T20:49:28.299Z] Failed to find version of module node: reached end of search path D:DeveloTicketRunnersTicketRunnersfunctionsnode_modules
!  functions: Your requested "node" version "18" doesn't match your global version "22". Using node@22 from host.
[2025-03-05T20:49:28.302Z] Could not find functions.yaml. Must use http discovery
[2025-03-05T20:49:28.308Z] Found firebase-functions binary at 'D:DeveloTicketRunnersTicketRunnersfunctionsnode_modules.binfirebase-functions'
Serving at port 8866

TypeError: ERR_NETWORK_IMPORT_DISALLOWED is not a constructor
    at ModuleLoader.getModuleJobForRequire (node:internal/modules/esm/loader:376:15)
    at new ModuleJobSync (node:internal/modules/esm/module_job:341:34)
    at ModuleLoader.getModuleJobForRequire (node:internal/modules/esm/loader:444:11)
    at new ModuleJobSync (node:internal/modules/esm/module_job:341:34)
    at ModuleLoader.getModuleJobForRequire (node:internal/modules/esm/loader:444:11)
    at new ModuleJobSync (node:internal/modules/esm/module_job:341:34)
    at ModuleLoader.importSyncForRequire (node:internal/modules/esm/loader:357:11)
    at loadESMFromCJS (node:internal/modules/cjs/loader:1385:24)
    at Module._compile (node:internal/modules/cjs/loader:1536:5)
    at Object..js (node:internal/modules/cjs/loader:1706:10)

[2025-03-05T20:49:28.566Z] Got response code 400; body Failed to generate manifest from function source: TypeError: ERR_NETWORK_IMPORT_DISALLOWED is not a constructor   
!!  functions: Failed to load function definition from source: FirebaseError: Functions codebase could not be analyzed successfully. It may have a syntax or runtime error {"metadata":{"emulator":{"name":"functions"},"message":"Failed to load function definition from source: FirebaseError: Functions codebase could not be analyzed successfully. It may have a syntax or runtime error"}}
[2025-03-05T20:49:32.595Z] Could not find VSCode notification endpoint: FetchError: request to http://localhost:40001/vscode/notify failed, reason: . If you are not running the Firebase Data Connect VSCode extension, this is expected and not an issue.