<commit_before>/*
 * Waltz - Enterprise Architecture
 * Copyright (C) 2016, 2017, 2018, 2019 Waltz open source project
 * See README.md for more information
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific
 *
 */

import template from "./report-grid-view-panel.html";
import {initialiseData} from "../../../common";
import {mkEntityLinkGridCell} from "../../../common/grid-utils";
import {mkSelectionOptions} from "../../../common/selector-utils";
import {CORE_API} from "../../../common/services/core-api-utils";
import _ from "lodash";
import {mkChunks} from "../../../common/list-utils";
import {determineForegroundColor} from "../../../common/colors";
import {rgb} from "d3-color";
import {scaleLinear} from "d3-scale";
import extent from "d3-array/src/extent";

const bindings = {
    parentEntityRef: "<",
    gridId: "<"
};

const initData = {
    categoryExtId: "CLOUD_READINESS",
    selectedCounterId: null,
    activeSummaryColRefs: [],
    filters: []
};

const nameCol = mkEntityLinkGridCell(
    "Name",
    "application",
    "none",
    "right",
    { pinnedLeft:true, width: 200});

const extIdCol = { field: "application.externalId", displayName: "Ext. Id", width: 100, pinnedLeft:true};

const unknownRating = {
    id: -1,
    color: "#f7f9f9",
    description: "This rating has not been provided",
    name: "Unknown",
    rating: "Z",
    position: 0
};

function mkPropNameForRef(ref) {
    return `${ref.kind}_${ref.id}`;
}

function mkPropNameForCellRef(x) {
    return `${x.columnEntityKind}_${x.columnEntityId}`;
}

function initialiseDataForRow(application, columnRefs) {
    return _.reduce(
        columnRefs,
        (acc, c) => {
            acc[c] = unknownRating;
            return acc;
        },
        {application});
}

function prepareColumnDefs(gridData) {
    const colDefs = _.get(gridData, ["definition", "columnDefinitions"], []);

    const mkColumnCustomProps = (c) =>  {
        switch (c.columnEntityReference.kind) {
            case "COST_KIND":
                return {
                    allowSummary: false,
                    cellTemplate:`

                                <waltz-currency-amount amount="COL_FIELD.value"></waltz-currency-amount>

`
                };
            case "INVOLVEMENT_KIND":
            case "SURVEY_QUESTION":
                return {
                    allowSummary: false,
                    width: 150,
                    toSearchTerm: d => _.get(d, [mkPropNameForRef(c.columnEntityReference), "text"], ""),
                    cellTemplate:`
                        <div class="waltz-grid-report-cell"
                             ng-class="{'wgrc-involvement-cell': COL_FIELD.text && ${c.columnEntityReference.kind === 'INVOLVEMENT_KIND'},
                                        'wgrc-survey-question-cell': COL_FIELD.text && ${c.columnEntityReference.kind === 'SURVEY_QUESTION'},
                                        'wgrc-no-data-cell': !COL_FIELD.text}"

`
                };
            default:
                return {
                    allowSummary: true,
                    toSearchTerm: d => _.get(d, [mkPropNameForRef(c.columnEntityReference), "name"], ""),
                    cellTemplate:
                        `<div class="waltz-grid-report-cell"
                              ng-bind="COL_FIELD.name"
                              uib-popover-html="COL_FIELD.comment"
                              popover-trigger="mouseenter"
                              popover-enable="COL_FIELD.comment != null"
                              popover-popup-delay="500"
                              popover-append-to-body="true"
                              popover-placement="left"
                              ng-style="{
                                'border-bottom-right-radius': COL_FIELD.comment ? '15% 50%' : 0,
                                'background-color': COL_FIELD.color,
                                'color': COL_FIELD.fontColor}">

`
                };
        }
    };

    const additionalColumns = _
        .chain(colDefs)
        .map(c => {
            return Object.assign(
                {
                    field: mkPropNameForRef(c.columnEntityReference),
                    displayName: c.columnEntityReference.name,
                    columnDef: c,
                    width: 100,
                    headerTooltip: c.columnEntityReference.description,
                    enableSorting: false
                },
                mkColumnCustomProps(c));
        })
        .value();

    return _.concat([nameCol, extIdCol], additionalColumns);
}

function mkPopoverHtml(cellData, ratingSchemeItem) {
    const comment = cellData.comment;
    if (_.isEmpty(comment)) {
        return "";
    } else {
        const ratingDesc = ratingSchemeItem.description === ratingSchemeItem.name
            ? ""
            : `<div class='help-block'>${ratingSchemeItem.description}</div>`;

        return `

                Comment: ${cellData.comment}

                Rating: ${ratingSchemeItem.name}
                ${ratingDesc}

`;
    }
}

function prepareTableData(gridData) {
    const appsById = _.keyBy(gridData.instance.applications, d => d.id);
    const ratingSchemeItemsById = _
        .chain(gridData.instance.ratingSchemeItems)
        .map(d => {
            const c = rgb(d.color);
            return Object.assign({}, d, { fontColor: determineForegroundColor(c.r, c.g, c.b)})
        })
        .keyBy(d => d.id)
        .value();

    const colDefs = _.get(gridData, ["definition", "columnDefinitions"], []);
    const columnRefs = _.map(colDefs, c => mkPropNameForRef(c.columnEntityReference));

    const costColorScalesByColumnEntityId = _
        .chain(gridData.instance.cellData)
        .filter(d => d.columnEntityKind === "COST_KIND")
        .groupBy(d => d.columnEntityId)
        .mapValues(v => scaleLinear()
            .domain(extent(v, d => d.value))
            .range(["#e2f5ff", "#86e4ff"]))
        .value();

    function mkTableCell(x) {
        switch(x.columnEntityKind) {
            case "COST_KIND":
                const color = costColorScalesByColumnEntityId[x.columnEntityId](x.value);
                return {
                    color: color,
                    value: x.value };
            case "INVOLVEMENT_KIND":
            case "SURVEY_QUESTION":
                return {
                    text: x.text };
            default:
                const ratingSchemeItem = ratingSchemeItemsById[x.ratingId];
                const popoverHtml = mkPopoverHtml(x, ratingSchemeItem);

                return Object.assign({}, ratingSchemeItem, { comment: popoverHtml });
        }}

    return _
        .chain(gridData.instance.cellData)
        .groupBy(d => d.applicationId)
        .map((xs, k) => _.reduce(
            xs,
            (acc, x) => {
                acc[mkPropNameForCellRef(x)] = mkTableCell(x);
                return acc;
            },
            initialiseDataForRow(appsById[k], columnRefs)))
        .orderBy(d => d.application.name)
        .value();
}

/**
 * We are not interested in some properties in the table data.
 * @param k
 * @returns {boolean}
 */
function isSummarisableProperty(k) {
    return ! (k === "application"
        || k === "$$hashKey"
        || k === "visible"
        || k === _.startsWith("COST_KIND"));
}

function refreshSummaries(tableData, columnDefinitions, ratingSchemeItems) {

    // increments a pair of counters referenced by `prop` in the object `acc`
    const accInc = (acc, prop, visible) => {
        const counts = _.get(acc, prop, {visible: 0, total:  0});
        counts.total++;
        if (visible) {
            counts.visible++;
        }
        acc[prop] = counts;
    };

    // reduce each value in an object representing a row by incrementing counters based on the property / value
    const reducer = (acc, row) => {
        _.forEach(
            row,
            (v, k) => isSummarisableProperty(k)
                ? accInc(
                    acc,
                    k + "#" + v.id,
                    _.get(row, ["visible"], true))
                : acc);
        return acc;
    };

    const ratingSchemeItemsById = _.keyBy(ratingSchemeItems, d => d.id);
    const columnsByRef = _.keyBy(columnDefinitions, d => mkPropNameForRef(d.columnEntityReference));

    return _
        .chain(tableData)
        .reduce(reducer, {})  // transform into a raw summary object for all rows
        .map((counts, k) => { // convert basic prop-val/count pairs in the summary object into a list of enriched objects
            const [colRef, ratingId] = _.split(k, "#");
            return {counterId: k, counts, colRef, rating: _.get(ratingSchemeItemsById, ratingId, unknownRating)};
        })
        .groupBy(d => d.colRef)  // group by the prop (colRef)
        .map((counters, colRef) => ({ // convert each prop group into a summary object with the actual column and a sorted set of counters
            column: columnsByRef[colRef],
            counters: _.orderBy(  // sort counters according to the rating ordering
                counters,
                [
                    c => c.rating.position,
                    c => c.rating.name
                ]),
            total: _.sumBy(counters, c => c.counts.total),
            totalVisible: _.sumBy(counters, c => c.counts.visible)
        }))
        .orderBy([  // order the summaries so they reflect the column order
            d => d.column.position,
            d => d.column.columnEntityReference.name
        ])
        .value();
}

/**
 * Returns a function which acts as a predicate to test rows against.
 *
 * The set of filters is first grouped by the row property they test.
 * For a row to pass, _at least_ one filter for _each_ group (prop)
 * needs to pass.
 *
 * @param filters
 * @returns {function(*=): boolean}
 */
function mkRowFilter(filters = []) {
    const filtersByPropName = _.groupBy(filters, f => f.propName);
    return td => _.every(
        filtersByPropName,
        (filtersForProp, prop) => {
            const propRating = _.get(td, [prop, "id"], null);
            return _.some(filtersForProp, f => propRating === f.ratingId);
        });
}

function controller(serviceBroker) {

    const vm = initialiseData(this, initData);

    function refresh(filters = []) {
        vm.columnDefs = _.map(vm.allColumnDefs, cd => {
            if (cd.allowSummary){
                return Object.assign(cd, { menuItems: [
                    {
                        title: "Add to summary",
                        icon: "ui-grid-icon-info-circled",
                        action: function() {
                            vm.onAddSummary(cd);
                        },
                        context: vm
                    }
                ]})
            } else {
                return cd;
            }
        });

        const rowFilter = mkRowFilter(filters);

        const workingTableData =  _.map(
            vm.allTableData,
            d => Object.assign({}, d, { visible: rowFilter(d) }));

        vm.tableData = _.filter(workingTableData, d => d.visible);

        const summaries = refreshSummaries(
            workingTableData,
            vm.rawGridData.definition.columnDefinitions,
            vm.rawGridData.instance.ratingSchemeItems);

        vm.chunkedSummaryData = mkChunks(
            _.filter(
                summaries,
                d => _.includes(vm.activeSummaryColRefs, mkPropNameForRef(d.column.columnEntityReference))),
            4);

    }

    vm.$onChanges = () => {
        if (! vm.parentEntityRef) return;

        vm.selectionOptions = mkSelectionOptions(vm.parentEntityRef);

        vm.loading = true;
        serviceBroker
            .loadViewData(
                CORE_API.ReportGridStore.getViewById,
                [vm.gridId, mkSelectionOptions(vm.parentEntityRef)])
            .then(r => {
                vm.filters = [];
                vm.loading = false;
                vm.rawGridData = r.data;
                vm.allTableData = prepareTableData(vm.rawGridData);
                vm.allColumnDefs = prepareColumnDefs(vm.rawGridData);
                vm.activeSummaryColRefs = _
                    .chain(vm.rawGridData.definition.columnDefinitions)
                    .filter(d => d.usageKind === "SUMMARY")
                    .map(d => mkPropNameForRef(d.columnEntityReference))
                    .value();
                refresh();
            });
    };

    vm.onToggleFilter = (counter) => {
        if (_.some(vm.filters, f =>f.counterId === counter.counterId)) {
            vm.filters = _.reject(vm.filters, f =>f.counterId === counter.counterId);
            refresh(vm.filters);
        } else {
            const newFilter = {
                counterId: counter.counterId,
                propName: counter.colRef,
                ratingId: counter.rating.id
            };
            vm.filters = _.concat(vm.filters, [newFilter]);
            refresh(vm.filters);
        }
    };

    vm.onRemoveSummary = (summary) => {
        const refToRemove = mkPropNameForRef(summary.column.columnEntityReference);
        vm.activeSummaryColRefs = _.reject(vm.activeSummaryColRefs, ref => ref === refToRemove);
        // remove any filters which refer to the property used by this summary
        vm.filters = _.reject(vm.filters, f => f.propName === refToRemove);
        refresh(vm.filters);
    };

    vm.onAddSummary = (c) => {
        const colRef = mkPropNameForRef(c.columnDef.columnEntityReference);
        vm.activeSummaryColRefs = _.concat(vm.activeSummaryColRefs, [colRef]);
        refresh(vm.filters);
    };

    vm.isSelectedCounter = (cId) => {
        return _.some(vm.filters, f =>f.counterId === cId);
    };
}

controller.$inject = ["ServiceBroker"];

const component = {
    controller,
    bindings,
    template
};

export default {
    id: "waltzReportGridViewPanel",
    component,
}<commit_msg>Display lifecycle phase<commit_after>/*
 * Waltz - Enterprise Architecture
 * Copyright (C) 2016, 2017, 2018, 2019 Waltz open source project
 * See README.md for more information
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific
 *
 */

import template from "./report-grid-view-panel.html";
import {initialiseData} from "../../../common";
import {mkEntityLinkGridCell} from "../../../common/grid-utils";
import {mkSelectionOptions} from "../../../common/selector-utils";
import {CORE_API} from "../../../common/services/core-api-utils";
import _ from "lodash";
import {mkChunks} from "../../../common/list-utils";
import {determineForegroundColor} from "../../../common/colors";
import {rgb} from "d3-color";
import {scaleLinear} from "d3-scale";
import extent from "d3-array/src/extent";

const bindings = {
    parentEntityRef: "<",
    gridId: "<"
};

const initData = {
    categoryExtId: "CLOUD_READINESS",
    selectedCounterId: null,
    activeSummaryColRefs: [],
    filters: []
};

const nameCol = mkEntityLinkGridCell(
    "Name",
    "application",
    "none",
    "right",
    { pinnedLeft:true, width: 200});

const extIdCol = { field: "application.externalId", displayName: "Ext. Id", width: 100, pinnedLeft:true};

const lifecyclePhaseCol = {
    field: "application.lifecyclePhase",
    displayName: "Lifecycle Phase",
    width: 100,
    pinnedLeft: true,
    cellTemplate:`
        <div class="waltz-grid-report-cell"

`
};

const unknownRating = {
    id: -1,
    color: "#f7f9f9",
    description: "This rating has not been provided",
    name: "Unknown",
    rating: "Z",
    position: 0
};

function mkPropNameForRef(ref) {
    return `${ref.kind}_${ref.id}`;
}

function mkPropNameForCellRef(x) {
    return `${x.columnEntityKind}_${x.columnEntityId}`;
}

function initialiseDataForRow(application, columnRefs) {
    return _.reduce(
        columnRefs,
        (acc, c) => {
            acc[c] = unknownRating;
            return acc;
        },
        {application});
}

function prepareColumnDefs(gridData) {
    const colDefs = _.get(gridData, ["definition", "columnDefinitions"], []);

    const mkColumnCustomProps = (c) =>  {
        switch (c.columnEntityReference.kind) {
            case "COST_KIND":
                return {
                    allowSummary: false,
                    cellTemplate:`

                                <waltz-currency-amount amount="COL_FIELD.value"></waltz-currency-amount>

`
                };
            case "INVOLVEMENT_KIND":
            case "SURVEY_QUESTION":
                return {
                    allowSummary: false,
                    width: 150,
                    toSearchTerm: d => _.get(d, [mkPropNameForRef(c.columnEntityReference), "text"], ""),
                    cellTemplate:`
                        <div class="waltz-grid-report-cell"
                             ng-class="{'wgrc-involvement-cell': COL_FIELD.text && ${c.columnEntityReference.kind === 'INVOLVEMENT_KIND'},
                                        'wgrc-survey-question-cell': COL_FIELD.text && ${c.columnEntityReference.kind === 'SURVEY_QUESTION'},
                                        'wgrc-no-data-cell': !COL_FIELD.text}"

`
                };
            default:
                return {
                    allowSummary: true,
                    toSearchTerm: d => _.get(d, [mkPropNameForRef(c.columnEntityReference), "name"], ""),
                    cellTemplate:
                        `<div class="waltz-grid-report-cell"
                              ng-bind="COL_FIELD.name"
                              uib-popover-html="COL_FIELD.comment"
                              popover-trigger="mouseenter"
                              popover-enable="COL_FIELD.comment != null"
                              popover-popup-delay="500"
                              popover-append-to-body="true"
                              popover-placement="left"
                              ng-style="{
                                'border-bottom-right-radius': COL_FIELD.comment ? '15% 50%' : 0,
                                'background-color': COL_FIELD.color,
                                'color': COL_FIELD.fontColor}">

`
                };
        }
    };

    const additionalColumns = _
        .chain(colDefs)
        .map(c => {
            return Object.assign(
                {
                    field: mkPropNameForRef(c.columnEntityReference),
                    displayName: c.columnEntityReference.name,
                    columnDef: c,
                    width: 100,
                    headerTooltip: c.columnEntityReference.description,
                    enableSorting: false
                },
                mkColumnCustomProps(c));
        })
        .value();

    return _.concat([nameCol, extIdCol, lifecyclePhaseCol], additionalColumns);
}

function mkPopoverHtml(cellData, ratingSchemeItem) {
    const comment = cellData.comment;
    if (_.isEmpty(comment)) {
        return "";
    } else {
        const ratingDesc = ratingSchemeItem.description === ratingSchemeItem.name
            ? ""
            : `<div class='help-block'>${ratingSchemeItem.description}</div>`;

        return `

                Comment: ${cellData.comment}

                Rating: ${ratingSchemeItem.name}
                ${ratingDesc}

`;
    }
}

function prepareTableData(gridData) {
    const appsById = _.keyBy(gridData.instance.applications, d => d.id);
    const ratingSchemeItemsById = _
        .chain(gridData.instance.ratingSchemeItems)
        .map(d => {
            const c = rgb(d.color);
            return Object.assign({}, d, { fontColor: determineForegroundColor(c.r, c.g, c.b)})
        })
        .keyBy(d => d.id)
        .value();

    const colDefs = _.get(gridData, ["definition", "columnDefinitions"], []);
    const columnRefs = _.map(colDefs, c => mkPropNameForRef(c.columnEntityReference));

    const costColorScalesByColumnEntityId = _
        .chain(gridData.instance.cellData)
        .filter(d => d.columnEntityKind === "COST_KIND")
        .groupBy(d => d.columnEntityId)
        .mapValues(v => scaleLinear()
            .domain(extent(v, d => d.value))
            .range(["#e2f5ff", "#86e4ff"]))
        .value();

    function mkTableCell(x) {
        switch(x.columnEntityKind) {
            case "COST_KIND":
                const color = costColorScalesByColumnEntityId[x.columnEntityId](x.value);
                return {
                    color: color,
                    value: x.value };
            case "INVOLVEMENT_KIND":
            case "SURVEY_QUESTION":
                return {
                    text: x.text };
            default:
                const ratingSchemeItem = ratingSchemeItemsById[x.ratingId];
                const popoverHtml = mkPopoverHtml(x, ratingSchemeItem);

                return Object.assign({}, ratingSchemeItem, { comment: popoverHtml });
        }}

    return _
        .chain(gridData.instance.cellData)
        .groupBy(d => d.applicationId)
        .map((xs, k) => _.reduce(
            xs,
            (acc, x) => {
                acc[mkPropNameForCellRef(x)] = mkTableCell(x);
                return acc;
            },
            initialiseDataForRow(appsById[k], columnRefs)))
        .orderBy(d => d.application.name)
        .value();
}

/**
 * We are not interested in some properties in the table data.
 * @param k
 * @returns {boolean}
 */
function isSummarisableProperty(k) {
    return ! (k === "application"
        || k === "$$hashKey"
        || k === "visible"
        || k === _.startsWith("COST_KIND"));
}

function refreshSummaries(tableData, columnDefinitions, ratingSchemeItems) {

    // increments a pair of counters referenced by `prop` in the object `acc`
    const accInc = (acc, prop, visible) => {
        const counts = _.get(acc, prop, {visible: 0, total:  0});
        counts.total++;
        if (visible) {
            counts.visible++;
        }
        acc[prop] = counts;
    };

    // reduce each value in an object representing a row by incrementing counters based on the property / value
    const reducer = (acc, row) => {
        _.forEach(
            row,
            (v, k) => isSummarisableProperty(k)
                ? accInc(
                    acc,
                    k + "#" + v.id,
                    _.get(row, ["visible"], true))
                : acc);
        return acc;
    };

    const ratingSchemeItemsById = _.keyBy(ratingSchemeItems, d => d.id);
    const columnsByRef = _.keyBy(columnDefinitions, d => mkPropNameForRef(d.columnEntityReference));

    return _
        .chain(tableData)
        .reduce(reducer, {})  // transform into a raw summary object for all rows
        .map((counts, k) => { // convert basic prop-val/count pairs in the summary object into a list of enriched objects
            const [colRef, ratingId] = _.split(k, "#");
            return {counterId: k, counts, colRef, rating: _.get(ratingSchemeItemsById, ratingId, unknownRating)};
        })
        .groupBy(d => d.colRef)  // group by the prop (colRef)
        .map((counters, colRef) => ({ // convert each prop group into a summary object with the actual column and a sorted set of counters
            column: columnsByRef[colRef],
            counters: _.orderBy(  // sort counters according to the rating ordering
                counters,
                [
                    c => c.rating.position,
                    c => c.rating.name
                ]),
            total: _.sumBy(counters, c => c.counts.total),
            totalVisible: _.sumBy(counters, c => c.counts.visible)
        }))
        .orderBy([  // order the summaries so they reflect the column order
            d => d.column.position,
            d => d.column.columnEntityReference.name
        ])
        .value();
}

/**
 * Returns a function which acts as a predicate to test rows against.
 *
 * The set of filters is first grouped by the row property they test.
 * For a row to pass, _at least_ one filter for _each_ group (prop)
 * needs to pass.
 *
 * @param filters
 * @returns {function(*=): boolean}
 */
function mkRowFilter(filters = []) {
    const filtersByPropName = _.groupBy(filters, f => f.propName);
    return td => _.every(
        filtersByPropName,
        (filtersForProp, prop) => {
            const propRating = _.get(td, [prop, "id"], null);
            return _.some(filtersForProp, f => propRating === f.ratingId);
        });
}

function controller(serviceBroker) {

    const vm = initialiseData(this, initData);

    function refresh(filters = []) {
        vm.columnDefs = _.map(vm.allColumnDefs, cd => {
            if (cd.allowSummary){
                return Object.assign(cd, { menuItems: [
                    {
                        title: "Add to summary",
                        icon: "ui-grid-icon-info-circled",
                        action: function() {
                            vm.onAddSummary(cd);
                        },
                        context: vm
                    }
                ]})
            } else {
                return cd;
            }
        });

        const rowFilter = mkRowFilter(filters);

        const workingTableData =  _.map(
            vm.allTableData,
            d => Object.assign({}, d, { visible: rowFilter(d) }));

        vm.tableData = _.filter(workingTableData, d => d.visible);

        const summaries = refreshSummaries(
            workingTableData,
            vm.rawGridData.definition.columnDefinitions,
            vm.rawGridData.instance.ratingSchemeItems);

        vm.chunkedSummaryData = mkChunks(
            _.filter(
                summaries,
                d => _.includes(vm.activeSummaryColRefs, mkPropNameForRef(d.column.columnEntityReference))),
            4);

    }

    vm.$onChanges = () => {
        if (! vm.parentEntityRef) return;

        vm.selectionOptions = mkSelectionOptions(vm.parentEntityRef);

        vm.loading = true;
        serviceBroker
            .loadViewData(
                CORE_API.ReportGridStore.getViewById,
                [vm.gridId, mkSelectionOptions(vm.parentEntityRef)])
            .then(r => {
                vm.filters = [];
                vm.loading = false;
                vm.rawGridData = r.data;
                vm.allTableData = prepareTableData(vm.rawGridData);
                vm.allColumnDefs = prepareColumnDefs(vm.rawGridData);
                vm.activeSummaryColRefs = _
                    .chain(vm.rawGridData.definition.columnDefinitions)
                    .filter(d => d.usageKind === "SUMMARY")
                    .map(d => mkPropNameForRef(d.columnEntityReference))
                    .value();
                refresh();
            });
    };

    vm.onToggleFilter = (counter) => {
        if (_.some(vm.filters, f =>f.counterId === counter.counterId)) {
            vm.filters = _.reject(vm.filters, f =>f.counterId === counter.counterId);
            refresh(vm.filters);
        } else {
            const newFilter = {
                counterId: counter.counterId,
                propName: counter.colRef,
                ratingId: counter.rating.id
            };
            vm.filters = _.concat(vm.filters, [newFilter]);
            refresh(vm.filters);
        }
    };

    vm.onRemoveSummary = (summary) => {
        const refToRemove = mkPropNameForRef(summary.column.columnEntityReference);
        vm.activeSummaryColRefs = _.reject(vm.activeSummaryColRefs, ref => ref === refToRemove);
        // remove any filters which refer to the property used by this summary
        vm.filters = _.reject(vm.filters, f => f.propName === refToRemove);
        refresh(vm.filters);
    };

    vm.onAddSummary = (c) => {
        const colRef = mkPropNameForRef(c.columnDef.columnEntityReference);
        vm.activeSummaryColRefs = _.concat(vm.activeSummaryColRefs, [colRef]);
        refresh(vm.filters);
    };

    vm.isSelectedCounter = (cId) => {
        return _.some(vm.filters, f =>f.counterId === cId);
    };
}

controller.$inject = ["ServiceBroker"];

const component = {
    controller,
    bindings,
    template
};

export default {
    id: "waltzReportGridViewPanel",
    component,
}<|endoftext|>


<commit_before>/*
 * Copyright (c) 2011 Yahoo! Inc. All rights reserved.
 */
YUI.add('RESTLib', function(Y, NAME) {

/**
 * The RESTLib module.
 *
 * @module RESTLib
 */

    /**
     * Constructor for the Controller class.
     *
     * @class Controller
     * @constructor
     */
    Y.namespace('mojito.controllers')[NAME] = {

        init: function(config) {
            this.config = config;
        },

        /**
         * Method corresponding to the 'index' action.
         *
         * @param ac {Object} The action context that provides access
         *        to the Mojito API.
         */
	        index: function(ac) {
		        //console.log(ac.http.getRequest());
	        	var data = {
	        		hostname: ac.http.getRequest().headers.host
	        	};
	        	ac.done(data);
        		//ac.done();
	        },

	        simpleWSCall: function(ac) {
	        	var hostPort = getHostNameAndPort(ac);
	            ac.models.get('RESTLib').callWSGET(hostPort, function(error, response){
	                if (!error)
	                {
	                    //console.log("This is the response: " + response);
	                	ac.http.setHeader('content-type', 'text/html');
	                    ac.done(response.getBody());
	                }
	                else
	                {
	                    //console.log("This is error: " + JSON.stringify(error));
	               		//ac.http.setStatusCode(error.status);
	                    ac.done(error.responseText);
	                }

	            });
	        },

	        inspectResponse: function(ac) {
	        	var hostPort = getHostNameAndPort(ac);
	            //console.log("***************************HostPort: " + hostPort);
	            ac.models.get('RESTLib').callWSGET(hostPort, function(error, response){
	                if (!error)
	                {
	                    //console.log(response.getHeaders());
	                    var responseParts = {
	                        statusCode: response.getStatusCode(),
	                        statusMsg: response.getStatusMessage(),
	                        body: response.getBody(),
	                        headers: JSON.stringify(response.getHeaders()),
	                        header_powered_by: response.getHeader('x-powered-by'),
	                        header_content_type: response.getHeader('content-type'),
	                        header_encoding: response.getHeader('transfer-encoding'),
	                        header_not_exist: response.getHeader('custom-header')
	                    };
	                    ac.done(responseParts);
	                }
	                else
	                {
	               		//ac.http.setStatusCode(error.status);
	                    ac.done(error.responseText);
	                }
	            });
	        },
	        WSTimeout: function(ac)
	        {
	        	var hostPort = getHostNameAndPort(ac);
	            ac.models.get('RESTLib').callTimeoutWS(hostPort, function(error, response){
	                if (!error)
	                {
	                    ac.done(response);
	                }
	                else
	                {
	                    ac.done(error.responseText);
	                    //console.log("This is the error: " + JSON.stringify(error));
	                }

	            });
	        },

	        inspectError: function(ac)
	        {
	        	var hostPort = getHostNameAndPort(ac);
	            ac.models.get('RESTLib').callInvalidWS(hostPort, function(error, response){
	                if (!error)
	                {
	                	ac.http.setHeader('content-type', 'text/html');
	                    ac.done(response);
	                }
	                else
	                {
	               		//ac.http.setStatusCode(error.status);
	                	console.log(error);
	                	var err = new Error("This is my error message");
	                	err.code = 404;
	                	ac.error(err);
	                    //ac.done(error.responseText);
	                    //console.log("This is the error: " + JSON.stringify(error));
	                }

	            });
	        },

	        testGETParam: function(ac)
	        {
	        	var hostPort = getHostNameAndPort(ac);
	            var sprintNumToPass = ac.params.getFromMerged("sprint_num");
	            var negativeTest = ac.params.getFromMerged("negative_test");
	            var outFunction = function(error, response)
	            {
	                if (!error)
	                {
	                	ac.http.setHeader('content-type', 'text/html');
	                    ac.done(response);
	                }
	                else
	                {
	                    ac.done(error.responseText);
	                    //console.log("This is the error: " + JSON.stringify(error));
	                }
	            };

	            if (negativeTest === "true")
	            {
	                ac.models.get('RESTLib').wsWithGETParamsNeg(hostPort, sprintNumToPass, outFunction);
	            }
	            else
	            {
	                ac.models.get('RESTLib').wsWithGETParams(hostPort, sprintNumToPass, outFunction);
	            }
	        },

	        testPOSTParam: function(ac)
	        {
	        	var hostPort = getHostNameAndPort(ac);
	            var sprintNumToPass = ac.params.getFromMerged("sprint_num");
	            var negativeTest = ac.params.getFromMerged("negative_test");
	            var outFunction = function(error, response)
	            {
	                if (!error)
	                {
	                	ac.http.setHeader('content-type', 'text/html');
	                    ac.done(response);
	                }
	                else
	                {
	                    ac.done(error.responseText);
	                    //console.log("This is the error: " + JSON.stringify(error));
	                }
	            };

	            if (negativeTest === "true")
	            {
	                ac.models.get('RESTLib').wsWithPOSTParamsNeg(hostPort, sprintNumToPass, outFunction);
	            }
	            else
	            {
	                ac.models.get('RESTLib').wsWithPOSTParams(hostPort, sprintNumToPass, outFunction);
	            }
	        },

	        testPUTParam: function(ac)
	        {
	        	var hostPort = getHostNameAndPort(ac);
	            var sprintNumToPass = ac.params.getFromMerged("sprint_num");
	            //var negativeTest = ac.params.getFromMerged("negative_test");
	            var outFunction = function(error, response)
	            {
	                if (!error)
	                {
	                	ac.http.setHeader('content-type', 'text/html');
	                    var statusCode = response.getStatusCode();
	                    ac.done("
" + statusCode + "
" + response.getBody());
	                }
	                else
	                {
	                    ac.done(error.responseText);
	                    console.log("This is the error: " + JSON.stringify(error));
	                }
	            };

	            ac.models.get('RESTLib').wsWithPUTParams(hostPort, sprintNumToPass, outFunction);

	            /*if (negativeTest === "true")
	            {
	                ac.models.get('RESTLib').wsWithPUTParamsNeg(hostPort, sprintNumToPass, outFunction);
	            }
	            else
	            {
	                ac.models.get('RESTLib').wsWithPUTParams(hostPort, sprintNumToPass, outFunction);
	            }*/
	        },

	        testDELETEParam: function(ac)
	        {
	        	var hostPort = getHostNameAndPort(ac);
	            var sprintNumToPass = ac.params.getFromMerged("sprint_num");
	            //var negativeTest = ac.params.getFromMerged("negative_test");
	            var outFunction = function(error, response)
	            {
	                if (!error)
	                {
	                	ac.http.setHeader('content-type', 'text/html');
	                    var statusCode = response.getStatusCode();
	                    ac.done("
" + statusCode + "
" + response.getBody());
	                }
	                else
	                {
	                    ac.done(error.responseText);
	                    console.log("This is the error: " + JSON.stringify(error));
	                }
	            };

	            ac.models.get('RESTLib').wsWithDELETEParams(hostPort, sprintNumToPass, outFunction);
	        },

	        testHEAD: function(ac)
	        {
	        	var hostPort = getHostNameAndPort(ac);
	            var sprintNumToPass = ac.params.getFromMerged("sprint_num");
	            var outFunction = function(error, response)
	            {
	                if (!error)
	                {
	                	ac.http.setHeader('content-type', 'text/html');
	                    var statusCode = response.getStatusCode();
	                    var headerInfo = response._resp.headers.new_header;
	                    ac.done("
"+ statusCode +"

new_header = " + headerInfo + "
" + response.getBody());
	                }
	                else
	                {
	                    ac.done(error.responseText);
	                    console.log("This is the error: " + JSON.stringify(error));
	                }
	            };

	            ac.models.get('RESTLib').wsWithHEAD(hostPort, sprintNumToPass, outFunction);
	        },

	        testHeaders: function(ac)
	        {
	        	var hostPort = getHostNameAndPort(ac);
	            var outFunction = function(error, response)
	            {
	                if (!error)
	                {
	                	ac.http.setHeader('content-type', 'text/html');
	                    ac.done(response);
	                }
	                else
	                {
	                    ac.done(error.responseText);
	                    console.log("This is the error: " + JSON.stringify(error));
	                }
	            };
	            ac.models.get('RESTLib').wsWithHeadersSettings(hostPort, outFunction);

	        },

	        //My WebServices

	        simpleWS: function(ac) {
	        	//console.log("I am here");
	        	//ac.http.setHeader('content-type', 'text/html');
	        	//ac.done("
This is a very simple web service
");
	        	ac.done({output: "This is a very simple web service"}, {view: {name: "wsOutput"}});
	        },

	        myWS: function(ac){
	            ac.models.get('RESTLib').myTimeConsumingWS(function (output){
	            	ac.http.setHeader('content-type', 'text/html');
	                ac.done(output);
	            });
	        },

	        printGETParams: function(ac){
	            var project = ac.params.getFromUrl("project");
	            var sprint = ac.params.getFromUrl("sprint");
	            var method = ac.http.getRequest().method;

	            var output = "
(METHOD: " + method + ") This is sprint " + sprint + " for the project " + project + "
";
	            ac.http.setHeader('content-type', 'text/html');
	            ac.done(output);
	        },

	        printPOSTParams: function(ac){
	            var project = ac.params.getFromBody("project");
	            var sprint = ac.params.getFromBody("sprint");
	            var method = ac.http.getRequest().method;

	            var output = "
(METHOD: " + method + ") This is sprint " + sprint + " for the project " + project + "
";
	            ac.http.setHeader('content-type', 'text/html');
	            ac.done(output);
	        },

	        printPUTParams: function(ac){
	            var project = ac.params.getFromBody("project");
	            var sprint = ac.params.getFromBody("sprint");
	            var method = ac.http.getRequest().method;

	            var output = "
(METHOD: " + method + ") This is sprint " + sprint + " for the project " + project + "
";
	            ac.http.setHeader('content-type', 'text/html');
	            ac.done(output);
	        },

	        printDELETEParams: function(ac){
	            var project = ac.params.getFromMerged("project");
	            var sprint = ac.params.getFromMerged("sprint");
	            var method = ac.http.getRequest().method;

	            //var output = "
(METHOD: " + method + ") This is sprint " + sprint + " for the project " + project + "
";
	            var output = "
(METHOD: " + method + ")
";
	            ac.http.setHeader('content-type', 'text/html');
	            ac.done(output);
	        },

	        printHEADParams: function(ac){
	            /*var project = ac.params.getFromMerged("project");
	            var sprint = ac.params.getFromMerged("sprint");

	            var output = "
This is sprint " + sprint + " for the project " + project + "
";
	            */
	            ac.http.addHeader('new_header','dummy_value');
	            ac.done();
	        },

	        getParticularHeader: function(ac){
	            var reqObj = ac.http.getRequest();
	            var headers = reqObj.headers;
	            ac.http.setHeader('content-type', 'text/html');
	            ac.done("
" + JSON.stringify(headers) + "
" + "
" + headers.myheader + "
" + "
" + headers.connection + "
");
	        }
	    };

	    function getHostNameAndPort(ac) {
	    	var hostPort;
	    	/*fromClient = ac.params.getFromMerged('fromClient');
	    	if (fromClient === "true")
	    	{
	    		hostPort = ac.params.getFromMerged('hostPort');
	    	}
	    	else
	    	{
	    		var reqObj = ac.http.getRequest();
	    		hostPort = reqObj.headers.host;
	    	}*/
	    	var reqObj = ac.http.getRequest();
			hostPort = reqObj.headers.host;

	    	return hostPort;
	    }

}, '0.0.1', {requires: [
    'mojito',
    'mojito-models-addon',
    'mojito-http-addon',
    'RESTLibModel']});
<commit_msg>added missing params addon<commit_after>/*
 * Copyright (c) 2011 Yahoo! Inc. All rights reserved.
 */
YUI.add('RESTLib', function(Y, NAME) {

/**
 * The RESTLib module.
 *
 * @module RESTLib
 */

    /**
     * Constructor for the Controller class.
     *
     * @class Controller
     * @constructor
     */
    Y.namespace('mojito.controllers')[NAME] = {

        init: function(config) {
            this.config = config;
        },

        /**
         * Method corresponding to the 'index' action.
         *
         * @param ac {Object} The action context that provides access
         *        to the Mojito API.
         */
	        index: function(ac) {
		        //console.log(ac.http.getRequest());
	        	var data = {
	        		hostname: ac.http.getRequest().headers.host
	        	};
	        	ac.done(data);
        		//ac.done();
	        },

	        simpleWSCall: function(ac) {
	        	var hostPort = getHostNameAndPort(ac);
	            ac.models.get('RESTLib').callWSGET(hostPort, function(error, response){
	                if (!error)
	                {
	                    //console.log("This is the response: " + response);
	                	ac.http.setHeader('content-type', 'text/html');
	                    ac.done(response.getBody());
	                }
	                else
	                {
	                    //console.log("This is error: " + JSON.stringify(error));
	               		//ac.http.setStatusCode(error.status);
	                    ac.done(error.responseText);
	                }

	            });
	        },

	        inspectResponse: function(ac) {
	        	var hostPort = getHostNameAndPort(ac);
	            //console.log("***************************HostPort: " + hostPort);
	            ac.models.get('RESTLib').callWSGET(hostPort, function(error, response){
	                if (!error)
	                {
	                    //console.log(response.getHeaders());
	                    var responseParts = {
	                        statusCode: response.getStatusCode(),
	                        statusMsg: response.getStatusMessage(),
	                        body: response.getBody(),
	                        headers: JSON.stringify(response.getHeaders()),
	                        header_powered_by: response.getHeader('x-powered-by'),
	                        header_content_type: response.getHeader('content-type'),
	                        header_encoding: response.getHeader('transfer-encoding'),
	                        header_not_exist: response.getHeader('custom-header')
	                    };
	                    ac.done(responseParts);
	                }
	                else
	                {
	               		//ac.http.setStatusCode(error.status);
	                    ac.done(error.responseText);
	                }
	            });
	        },
	        WSTimeout: function(ac)
	        {
	        	var hostPort = getHostNameAndPort(ac);
	            ac.models.get('RESTLib').callTimeoutWS(hostPort, function(error, response){
	                if (!error)
	                {
	                    ac.done(response);
	                }
	                else
	                {
	                    ac.done(error.responseText);
	                    //console.log("This is the error: " + JSON.stringify(error));
	                }

	            });
	        },

	        inspectError: function(ac)
	        {
	        	var hostPort = getHostNameAndPort(ac);
	            ac.models.get('RESTLib').callInvalidWS(hostPort, function(error, response){
	                if (!error)
	                {
	                	ac.http.setHeader('content-type', 'text/html');
	                    ac.done(response);
	                }
	                else
	                {
	               		//ac.http.setStatusCode(error.status);
	                	console.log(error);
	                	var err = new Error("This is my error message");
	                	err.code = 404;
	                	ac.error(err);
	                    //ac.done(error.responseText);
	                    //console.log("This is the error: " + JSON.stringify(error));
	                }

	            });
	        },

	        testGETParam: function(ac)
	        {
	        	var hostPort = getHostNameAndPort(ac);
	            var sprintNumToPass = ac.params.getFromMerged("sprint_num");
	            var negativeTest = ac.params.getFromMerged("negative_test");
	            var outFunction = function(error, response)
	            {
	                if (!error)
	                {
	                	ac.http.setHeader('content-type', 'text/html');
	                    ac.done(response);
	                }
	                else
	                {
	                    ac.done(error.responseText);
	                    //console.log("This is the error: " + JSON.stringify(error));
	                }
	            };

	            if (negativeTest === "true")
	            {
	                ac.models.get('RESTLib').wsWithGETParamsNeg(hostPort, sprintNumToPass, outFunction);
	            }
	            else
	            {
	                ac.models.get('RESTLib').wsWithGETParams(hostPort, sprintNumToPass, outFunction);
	            }
	        },

	        testPOSTParam: function(ac)
	        {
	        	var hostPort = getHostNameAndPort(ac);
	            var sprintNumToPass = ac.params.getFromMerged("sprint_num");
	            var negativeTest = ac.params.getFromMerged("negative_test");
	            var outFunction = function(error, response)
	            {
	                if (!error)
	                {
	                	ac.http.setHeader('content-type', 'text/html');
	                    ac.done(response);
	                }
	                else
	                {
	                    ac.done(error.responseText);
	                    //console.log("This is the error: " + JSON.stringify(error));
	                }
	            };

	            if (negativeTest === "true")
	            {
	                ac.models.get('RESTLib').wsWithPOSTParamsNeg(hostPort, sprintNumToPass, outFunction);
	            }
	            else
	            {
	                ac.models.get('RESTLib').wsWithPOSTParams(hostPort, sprintNumToPass, outFunction);
	            }
	        },

	        testPUTParam: function(ac)
	        {
	        	var hostPort = getHostNameAndPort(ac);
	            var sprintNumToPass = ac.params.getFromMerged("sprint_num");
	            //var negativeTest = ac.params.getFromMerged("negative_test");
	            var outFunction = function(error, response)
	            {
	                if (!error)
	                {
	                	ac.http.setHeader('content-type', 'text/html');
	                    var statusCode = response.getStatusCode();
	                    ac.done("
" + statusCode + "
" + response.getBody());
	                }
	                else
	                {
	                    ac.done(error.responseText);
	                    console.log("This is the error: " + JSON.stringify(error));
	                }
	            };

	            ac.models.get('RESTLib').wsWithPUTParams(hostPort, sprintNumToPass, outFunction);

	            /*if (negativeTest === "true")
	            {
	                ac.models.get('RESTLib').wsWithPUTParamsNeg(hostPort, sprintNumToPass, outFunction);
	            }
	            else
	            {
	                ac.models.get('RESTLib').wsWithPUTParams(hostPort, sprintNumToPass, outFunction);
	            }*/
	        },

	        testDELETEParam: function(ac)
	        {
	        	var hostPort = getHostNameAndPort(ac);
	            var sprintNumToPass = ac.params.getFromMerged("sprint_num");
	            //var negativeTest = ac.params.getFromMerged("negative_test");
	            var outFunction = function(error, response)
	            {
	                if (!error)
	                {
	                	ac.http.setHeader('content-type', 'text/html');
	                    var statusCode = response.getStatusCode();
	                    ac.done("
" + statusCode + "
" + response.getBody());
	                }
	                else
	                {
	                    ac.done(error.responseText);
	                    console.log("This is the error: " + JSON.stringify(error));
	                }
	            };

	            ac.models.get('RESTLib').wsWithDELETEParams(hostPort, sprintNumToPass, outFunction);
	        },

	        testHEAD: function(ac)
	        {
	        	var hostPort = getHostNameAndPort(ac);
	            var sprintNumToPass = ac.params.getFromMerged("sprint_num");
	            var outFunction = function(error, response)
	            {
	                if (!error)
	                {
	                	ac.http.setHeader('content-type', 'text/html');
	                    var statusCode = response.getStatusCode();
	                    var headerInfo = response._resp.headers.new_header;
	                    ac.done("
"+ statusCode +"

new_header = " + headerInfo + "
" + response.getBody());
	                }
	                else
	                {
	                    ac.done(error.responseText);
	                    console.log("This is the error: " + JSON.stringify(error));
	                }
	            };

	            ac.models.get('RESTLib').wsWithHEAD(hostPort, sprintNumToPass, outFunction);
	        },

	        testHeaders: function(ac)
	        {
	        	var hostPort = getHostNameAndPort(ac);
	            var outFunction = function(error, response)
	            {
	                if (!error)
	                {
	                	ac.http.setHeader('content-type', 'text/html');
	                    ac.done(response);
	                }
	                else
	                {
	                    ac.done(error.responseText);
	                    console.log("This is the error: " + JSON.stringify(error));
	                }
	            };
	            ac.models.get('RESTLib').wsWithHeadersSettings(hostPort, outFunction);

	        },

	        //My WebServices

	        simpleWS: function(ac) {
	        	//console.log("I am here");
	        	//ac.http.setHeader('content-type', 'text/html');
	        	//ac.done("
This is a very simple web service
");
	        	ac.done({output: "This is a very simple web service"}, {view: {name: "wsOutput"}});
	        },

	        myWS: function(ac){
	            ac.models.get('RESTLib').myTimeConsumingWS(function (output){
	            	ac.http.setHeader('content-type', 'text/html');
	                ac.done(output);
	            });
	        },

	        printGETParams: function(ac){
	            var project = ac.params.getFromUrl("project");
	            var sprint = ac.params.getFromUrl("sprint");
	            var method = ac.http.getRequest().method;

	            var output = "
(METHOD: " + method + ") This is sprint " + sprint + " for the project " + project + "
";
	            ac.http.setHeader('content-type', 'text/html');
	            ac.done(output);
	        },

	        printPOSTParams: function(ac){
	            var project = ac.params.getFromBody("project");
	            var sprint = ac.params.getFromBody("sprint");
	            var method = ac.http.getRequest().method;

	            var output = "
(METHOD: " + method + ") This is sprint " + sprint + " for the project " + project + "
";
	            ac.http.setHeader('content-type', 'text/html');
	            ac.done(output);
	        },

	        printPUTParams: function(ac){
	            var project = ac.params.getFromBody("project");
	            var sprint = ac.params.getFromBody("sprint");
	            var method = ac.http.getRequest().method;

	            var output = "
(METHOD: " + method + ") This is sprint " + sprint + " for the project " + project + "
";
	            ac.http.setHeader('content-type', 'text/html');
	            ac.done(output);
	        },

	        printDELETEParams: function(ac){
	            var project = ac.params.getFromMerged("project");
	            var sprint = ac.params.getFromMerged("sprint");
	            var method = ac.http.getRequest().method;

	            //var output = "
(METHOD: " + method + ") This is sprint " + sprint + " for the project " + project + "
";
	            var output = "
(METHOD: " + method + ")
";
	            ac.http.setHeader('content-type', 'text/html');
	            ac.done(output);
	        },

	        printHEADParams: function(ac){
	            /*var project = ac.params.getFromMerged("project");
	            var sprint = ac.params.getFromMerged("sprint");

	            var output = "
This is sprint " + sprint + " for the project " + project + "
";
	            */
	            ac.http.addHeader('new_header','dummy_value');
	            ac.done();
	        },

	        getParticularHeader: function(ac){
	            var reqObj = ac.http.getRequest();
	            var headers = reqObj.headers;
	            ac.http.setHeader('content-type', 'text/html');
	            ac.done("
" + JSON.stringify(headers) + "
" + "
" + headers.myheader + "
" + "
" + headers.connection + "
");
	        }
	    };

	    function getHostNameAndPort(ac) {
	    	var hostPort;
	    	/*fromClient = ac.params.getFromMerged('fromClient');
	    	if (fromClient === "true")
	    	{
	    		hostPort = ac.params.getFromMerged('hostPort');
	    	}
	    	else
	    	{
	    		var reqObj = ac.http.getRequest();
	    		hostPort = reqObj.headers.host;
	    	}*/
	    	var reqObj = ac.http.getRequest();
			hostPort = reqObj.headers.host;

	    	return hostPort;
	    }

}, '0.0.1', {requires: [
    'mojito',
    'mojito-models-addon',
    'mojito-http-addon',
    'mojito-params-addon',
    'RESTLibModel']});
<|endoftext|>


<commit_before>Application.Controllers.controller('toolbarProjectsPageOverviewSamples',
    ["$scope", "mcapi", "$injector", "model.Projects", "alertService", "User", "$stateParams",
        function ($scope, mcapi, $injector, Projects, alertService, User, $stateParams) {

            $scope.refreshProjects = function () {
                mcapi('/objects/%', $scope.sample.id)
                    .success(function (data) {
                        $scope.projects_by_sample = data.projects;
                    }).jsonp();

            };
            $scope.refreshProcesses = function () {
//
            };
            $scope.showForm = function () {
                $scope.default_properties = $scope.bk.selected_treatment.default_properties;
                $scope.bk.tab_item = '';
            };

            $scope.showTab = function (item, index) {
                $scope.bk.tab_item = item.id;
                $scope.bk.tab_details = $scope.doc.treatments[index];
                $scope.default_properties = $scope.doc.treatments[index].default_properties;
                $scope.bk.selected_treatment = {};
            };

            $scope.addTreatment = function () {
                var o = angular.copy($scope.bk.selected_treatment);
                $scope.bk.selected_treatment = '';
                $scope.default_properties = "";
                $scope.doc.treatments.push(o);
            };

            $scope.save = function (form) {
                var $validationProvider = $injector.get('$validation');
                var check = $validationProvider.checkValid(form);
                if (check === true) {
                    mcapi('/objects/new')
                        .arg('order_by=birthtime')
                        .success(function (data) {
                            mcapi('/objects/%', data.id)
                                .success(function (sample_obj) {
                                    $scope.message = "Object has been saved.";
                                    $scope.samples_list.unshift(sample_obj);
                                })
                                .error(function (e) {
                                }).jsonp();
                            $scope.clear();
                        })
                        .error(function (e) {

                        }).post($scope.doc);
                } else {
                    $validationProvider.validate(form);
                }
            };
            $scope.setProperties = function () {
                $scope.doc.default_properties = $scope.bk.classification.default_properties;
                $scope.doc.template = $scope.bk.classification.id;

            };
            $scope.showTreatmentDetails_and_processes = function (sample) {
                $scope.sample = sample;
                $scope.refreshProcesses();
                $scope.refreshProjects();

            };

            $scope.clear = function () {
                $scope.doc = {
                    name: '',
                    notes: [],
                    available: true,
                    default_properties: [],
                    added_properties: [],
                    treatments: [],
                    projects: []
                };
                $scope.bk = {
                    selected_treatment: '',
                    selected_project: '',
                    tab_details: [],
                    tab_item: '',
                    classification: '',
                    new_note: ''
                };
            };

            $scope.populateProjects = function () {
                $scope.doc.projects.push({'id': $scope.bk.selected_project.id, 'name': $scope.bk.selected_project.name});
            };
            $scope.removeProjects = function (index) {
                $scope.doc.projects.splice(index, 1);
            };

            $scope.addProject = function (prj) {
                mcapi('/object/%/project/%', $scope.sample.id, $scope.model.selected_project.id)
                    .success(function (data) {
                        $scope.refreshProjects()
                    }).error(function (data) {
                        alertService.sendMessage(data.error);
                    }).put();
            };

            $scope.deleteProject = function (index) {
                mcapi('/object/%/project/%/remove', $scope.sample.id, $scope.sample.projects[index].id)
                    .success(function (data) {
                        alertService.sendMessage("Project has been deleted");
                        $scope.refreshProjects();
                    }).error(function () {
                    }).put();
            };

            function init() {
                $scope.project_id = $stateParams.id;
                $scope.signed_in_user = User.u()
                $scope.doc = {
                    name: '',
                    notes: [],
                    available: true,
                    default_properties: [],
                    added_properties: [],
                    treatments: [],
                    projects: []
                };
                $scope.bk = {
                    selected_project: ''

                }
                $scope.processes_list =  [];
                $scope.projects_by_sample =  [];
                $scope.clear();
                mcapi('/templates')
                    .argWithValue('filter_by', '"template_pick":"treatment"')
                    .success(function (data) {
                        $scope.treatment_templates = data;
                    })
                    .error(function (e) {

                    }).jsonp();
                mcapi('/templates')
                    .argWithValue('filter_by', '"template_type":"material"')
                    .success(function (data) {
                        $scope.sample_templates = data;
                    })
                    .error(function (e) {

                    }).jsonp();
                mcapi('/objects')
                    .success(function (data) {
                        $scope.samples_list = data;
                    })
                    .error(function (data) {
                    }).jsonp();
//                mcapi('/objects/project/%',$scope.project_id)
//                    .success(function (data) {
//                        $scope.samples_list = data;
//                    })
//                    .error(function (data) {
//                    }).jsonp();
                Projects.getList().then(function (data) {
                    $scope.projects = data;
                });
            }

            init();
        }]);
<commit_msg>Issue-417-on sample creation associate with a default project<commit_after>Application.Controllers.controller('toolbarProjectsPageOverviewSamples',
    ["$scope", "mcapi", "$injector", "model.Projects", "alertService", "User", "$stateParams",
        function ($scope, mcapi, $injector, Projects, alertService, User, $stateParams) {

            $scope.refreshProjects = function () {
                mcapi('/objects/%', $scope.sample.id)
                    .success(function (data) {
                        $scope.projects_by_sample = data.projects;
                    }).jsonp();

            };
            $scope.refreshProcesses = function () {
//
            };
            $scope.showForm = function () {
                $scope.default_properties = $scope.bk.selected_treatment.default_properties;
                $scope.bk.tab_item = '';
            };

            $scope.showTab = function (item, index) {
                $scope.bk.tab_item = item.id;
                $scope.bk.tab_details = $scope.doc.treatments[index];
                $scope.default_properties = $scope.doc.treatments[index].default_properties;
                $scope.bk.selected_treatment = {};
            };

            $scope.addTreatment = function () {
                var o = angular.copy($scope.bk.selected_treatment);
                $scope.bk.selected_treatment = '';
                $scope.default_properties = "";
                $scope.doc.treatments.push(o);
            };

            $scope.save = function (form) {
                var $validationProvider = $injector.get('$validation');
                var check = $validationProvider.checkValid(form);
                if (check === true) {
                    mcapi('/objects/new')
                        .arg('order_by=birthtime')
                        .success(function (data) {
                            mcapi('/objects/%', data.id)
                                .success(function (sample_obj) {
                                    $scope.message = "Object has been saved.";
                                    $scope.samples_list.unshift(sample_obj);
                                })
                                .error(function (e) {
                                }).jsonp();
                            $scope.clear();
                        })
                        .error(function (e) {

                        }).post($scope.doc);
                } else {
                    $validationProvider.validate(form);
                }
            };
            $scope.setProperties = function () {
                $scope.doc.default_properties = $scope.bk.classification.default_properties;
                $scope.doc.template = $scope.bk.classification.id;

            };
            $scope.showTreatmentDetails_and_processes = function (sample) {
                $scope.sample = sample;
                $scope.refreshProcesses();
                $scope.refreshProjects();

            };

            $scope.clear = function () {
                $scope.doc = {
                    name: '',
                    notes: [],
                    available: true,
                    default_properties: [],
                    added_properties: [],
                    treatments: [],
                    projects: []
                };
                $scope.bk = {
                    selected_treatment: '',
                    selected_project: '',
                    tab_details: [],
                    tab_item: '',
                    classification: '',
                    new_note: ''
                };
            };

            $scope.populateProjects = function () {
                $scope.doc.projects.push({'id': $scope.bk.selected_project.id, 'name': $scope.bk.selected_project.name});
            };
            $scope.removeProjects = function (index) {
                $scope.doc.projects.splice(index, 1);
            };

            $scope.addProject = function (prj) {
                mcapi('/object/%/project/%', $scope.sample.id, $scope.model.selected_project.id)
                    .success(function (data) {
                        $scope.refreshProjects()
                    }).error(function (data) {
                        alertService.sendMessage(data.error);
                    }).put();
            };

            $scope.deleteProject = function (index) {
                mcapi('/object/%/project/%/remove', $scope.sample.id, $scope.sample.projects[index].id)
                    .success(function (data) {
                        alertService.sendMessage("Project has been deleted");
                        $scope.refreshProjects();
                    }).error(function () {
                    }).put();
            };

            function init() {
                $scope.project_id = $stateParams.id;
                $scope.signed_in_user = User.u()
                $scope.doc = {
                    name: '',
                    notes: [],
                    available: true,
                    default_properties: [],
                    added_properties: [],
                    treatments: [],
                    projects: []
                };
                $scope.bk = {
                    selected_project: ''

                }
                mcapi('/projects/%', $scope.project_id)
                    .success(function (data) {
                        $scope.default_project = {'id': data.id, 'name': data.name};
                        $scope.doc.projects.push($scope.default_project);
                    })
                    .error(function (data) {
                    }).jsonp();
                $scope.processes_list =  [];
                $scope.projects_by_sample =  [];
                $scope.clear();
                mcapi('/templates')
                    .argWithValue('filter_by', '"template_pick":"treatment"')
                    .success(function (data) {
                        $scope.treatment_templates = data;
                    })
                    .error(function (e) {

                    }).jsonp();
                mcapi('/templates')
                    .argWithValue('filter_by', '"template_type":"material"')
                    .success(function (data) {
                        $scope.sample_templates = data;
                    })
                    .error(function (e) {

                    }).jsonp();
                mcapi('/objects')
                    .success(function (data) {
                        $scope.samples_list = data;
                    })
                    .error(function (data) {
                    }).jsonp();
//                mcapi('/objects/project/%',$scope.project_id)
//                    .success(function (data) {
//                        $scope.samples_list = data;
//                    })
//                    .error(function (data) {
//                    }).jsonp();
                Projects.getList().then(function (data) {
                    $scope.projects = data;
                });
            }

            init();
        }]);
<|endoftext|>


<commit_before>define([
    'uijet_dir/uijet',
    'composites/DropmenuButton'
], function (uijet) {

    var BREADCRUMB_CLASS = 'node_breadcrumb';

    uijet.Widget('Breadcrumbs', {
        //TODO: the type_class still gets messed up if not specified - need to fix inside uijet
        init        : function () {
            var res = this._super.apply(this, arguments);

            this._wrap();

            uijet.start([{
                type    : 'Button',
                config  : {
                    element     : '#node_breadcrumb_main',
                    container   : this.id,
                    dont_wake   : true,
                    signals     : {
                        pre_appear      : function () {
                            this.$element.removeClass('hide');
                        },
                        post_disappear  : function () {
                            this.$element.addClass('hide');
                        }
                    },
                    app_events  : {
                        'nodes_breadcrumbs.hide_root'   : 'sleep',
                        'nodes_breadcrumbs.show_root'   : 'wake'
                    }
                }
            }, {
                type    : 'Button',
                config  : {
                    element     : '#node_breadcrumb_back',
                    container   : this.id,
                    dont_wake   : true,
                    signals     : {
                        pre_wake        : function () {
                            this.$element.text(this.context.name);
                        },
                        pre_appear      : function () {
                            this.$element.removeClass('hide');
                        },
                        post_disappear  : function () {
                            this.$element.addClass('hide');
                        }
                    },
                    app_events  : {
                        'nodes_breadcrumbs.hide_back'   : 'sleep',
                        'nodes_breadcrumbs.set_back'    : 'wake+'
                    }
                }
            }, {
                type    : 'DropmenuButton',
                config  : {
                    element         : '#nodes_breadcrumbs_history',
                    container       : this.id,
                    dont_wake       : true,
                    wrapper_class   : 'hide',
                    menu            : {
                        mixins          : ['Templated', 'Translated'],
                        float_position  : 'top: 2rem',
                        signals         : {
                            pre_select  : function ($selected) {
                                var id = +$selected.attr('data-id'),
                                    i = 0;
                                if ( id ) {
                                    //! Array.prototype.some
                                    this.context.some(function (crumb) {
                                        if ( crumb.id == id ) {
                                            return true;
                                        }
                                        i++;
                                    });
                                }
                                this.context.length = i;
                            }
                        }
                    },
                    signals         : {
                        pre_appear      : function () {
                            this.$wrapper.removeClass('hide');
                        },
                        post_disappear  : function () {
                            this.$wrapper.addClass('hide');
                        }
                    },
                    app_events  : {
                        'nodes_breadcrumbs.updated' : function (crumbs) {
                            if ( crumbs && crumbs.length ) {
                                this.wake(crumbs);
                            }
                            else {
                                this.sleep();
                            }
                        }
                    }
                }
            }]);

            this.subscribe('node_breadcrumb_main.clicked', function () {
                this.resource.reset([]);
            });
            this.subscribe('node_breadcrumb_back.clicked', function (data) {
                var id = data.context.id,
                    models = [];
                if ( id ) {
                    //! Array.prototype.some
                    this.resource.models.some(function (model) {
                        models.push(model);
                        if ( model.id === id ) {
                            return true;
                        }
                    });
                }
                this.resource.reset(models);
            });
            this.subscribe('nodes_breadcrumbs_history_menu.selected', function ($selected) {
                var node_id = +$selected.attr('data-id');
                this.resource.reset(
                    uijet.Resource('LatestSheet').branch(node_id)
                );
            });

            return res;
        },
        render      : function () {
            var length = this.resource.length,
                $title = uijet.$('#nodes_picker_header_description'),
                history;
            this._super();

            length ? $title.addClass('hide') : $title.removeClass('hide');

            switch ( length ) {
                case 0:
                    this.publish('hide_root')
                        .publish('hide_back');
                    break;
                case 1:
                    this.publish('show_root')
                        .publish('hide_back');
                    break;
                case 2:
                    this.publish('show_root')
                        .publish('set_back', this.resource.at(length - 2).attributes);
                    break;
                default:
                    this.publish('hide_root')
                        .publish('set_back', this.resource.at(length - 2).attributes);
                    break;
            }
            if ( length ) {
                history = this.resource.slice(0, -2).map(uijet.utils.prop('attributes'));
            }
            this.publish('updated', history);
            return this;
        }
    });

});
<commit_msg>Deprecate old Breadcrumbs widget<commit_after><|endoftext|>


<commit_before>'use strict';

var fs = require('fs');
var angular = require('angular');

var template = fs.readFileSync(__dirname + '/process-instance-table.html', 'utf8');
var searchConfig = JSON.parse(fs.readFileSync(__dirname + '/process-instance-search-config.json', 'utf8'));

module.exports = [ 'ViewsProvider', function(ViewsProvider) {

  ViewsProvider.registerDefaultView('cockpit.processDefinition.runtime.tab', {
    id: 'process-instances-table',
    label: 'PLUGIN_PROCESS_INSTANCES_LABEL',
    template: template,
    controller: [
      '$scope', '$location', 'search', 'routeUtil', 'PluginProcessInstanceResource', '$translate',
      function($scope,   $location,   search,   routeUtil,   PluginProcessInstanceResource, $translate) {

        var processDefinition = $scope.processDefinition;
        $scope.onSearchChange = updateView;
        $scope.onSortChange   = updateView;

        $scope.headColumns = [
          { class: 'state',        request: 'state',       sortable: false, content: 'PLUGIN_PROCESS_INSTANCE_STATE'},
          { class: 'instance-id',  request: 'instanceId',  sortable: false, content: 'PLUGIN_PROCESS_INSTANCE_ID'},
          { class: 'start-time',   request: 'startTime',   sortable: true,  content: 'PLUGIN_PROCESS_INSTANCE_START_TIME'},
          { class: 'business-key', request: 'businessKey', sortable: false, content: 'PLUGIN_PROCESS_INSTANCE_BUSINESS_KEY'}
        ];
        // Default Sorting
        $scope.sortObj = { sortBy: 'startTime', sortOrder: 'desc' };

        $scope.searchConfig = angular.copy(searchConfig);
        angular.forEach(searchConfig.tooltips, function(translation, tooltip) {
          $scope.searchConfig.tooltips[tooltip] = $translate.instant(translation);
        });
        $scope.searchConfig.types.map(function(type) {
          type.id.value = $translate.instant(type.id.value);
          if (type.operators) {
            type.operators = type.operators.map(function(op) {
              op.value = $translate.instant(op.value);
              return op;
            });
          }
          return type;
        });

        function updateView(query, pages, sortObj) {
          $scope.pagesObj = pages   || $scope.pagesObj ;
          $scope.queryObj = query   || $scope.queryObj;
          sortObj         = sortObj || $scope.sortObj;

          var page        =  $scope.pagesObj.current,
              queryParams =  $scope.queryObj,
              count       =  $scope.pagesObj.size,
              firstResult = (page - 1) * count;

          var defaultParams = {
            processDefinitionId: processDefinition.id
          };

          var pagingParams = {
            firstResult    : firstResult,
            maxResults     : count,
            sortBy         : sortObj.sortBy,
            sortOrder      : sortObj.sortOrder
          };

          var params = angular.extend({}, query, pagingParams, defaultParams);
          var countParams = angular.extend({}, queryParams, defaultParams);

          $scope.processInstances = null;
          $scope.loadingState = 'LOADING';

          return PluginProcessInstanceResource.count(countParams).$promise.then(function(data) {
            var total = data.count;

            return PluginProcessInstanceResource.query(pagingParams, params).$promise.then(function(data) {
              $scope.processInstances = data;
              $scope.loadingState = data.length ? 'LOADED' : 'EMPTY';
              return total;
            });
          });
        }

        $scope.getProcessInstanceUrl = function(processInstance, params) {
          var path = '#/process-instance/' + processInstance.id;
          var searches = angular.extend({}, ($location.search() || {}), (params || {}));

          var keepSearchParams = [ 'viewbox' ];
          for (var i in params) {
            keepSearchParams.push(i);
          }

          return routeUtil.redirectTo(path, searches, keepSearchParams);
        };
      }],
    priority: 10
  });
}];
<commit_msg>feat(cockpit): Add persistence to process instance table<commit_after>'use strict';

var fs = require('fs');
var angular = require('angular');

var template = fs.readFileSync(__dirname + '/process-instance-table.html', 'utf8');
var searchConfig = JSON.parse(fs.readFileSync(__dirname + '/process-instance-search-config.json', 'utf8'));

module.exports = [ 'ViewsProvider', function(ViewsProvider) {

  ViewsProvider.registerDefaultView('cockpit.processDefinition.runtime.tab', {
    id: 'process-instances-table',
    label: 'PLUGIN_PROCESS_INSTANCES_LABEL',
    template: template,
    controller: [
      '$scope', '$location', 'search', 'routeUtil', 'PluginProcessInstanceResource', '$translate', 'localConf',
      function($scope,   $location,   search,   routeUtil,   PluginProcessInstanceResource, $translate, localConf) {

        var processDefinition = $scope.processDefinition;
        $scope.onSearchChange = updateView;
        $scope.onSortChange   = updateView;

        $scope.headColumns = [
          { class: 'state',        request: 'state',       sortable: false, content: 'PLUGIN_PROCESS_INSTANCE_STATE'},
          { class: 'instance-id',  request: 'instanceId',  sortable: false, content: 'PLUGIN_PROCESS_INSTANCE_ID'},
          { class: 'start-time',   request: 'startTime',   sortable: true,  content: 'PLUGIN_PROCESS_INSTANCE_START_TIME'},
          { class: 'business-key', request: 'businessKey', sortable: false, content: 'PLUGIN_PROCESS_INSTANCE_BUSINESS_KEY'}
        ];

        // Default sorting
        var defaultValue=  {sortBy: 'startTime', sortOrder: 'desc'};
        $scope.sortObj   = loadLocal(defaultValue);

        $scope.searchConfig = angular.copy(searchConfig);
        angular.forEach(searchConfig.tooltips, function(translation, tooltip) {
          $scope.searchConfig.tooltips[tooltip] = $translate.instant(translation);
        });
        $scope.searchConfig.types.map(function(type) {
          type.id.value = $translate.instant(type.id.value);
          if (type.operators) {
            type.operators = type.operators.map(function(op) {
              op.value = $translate.instant(op.value);
              return op;
            });
          }
          return type;
        });

        function updateView(query, pages, sortObj) {
          $scope.pagesObj = pages   || $scope.pagesObj ;
          $scope.queryObj = query   || $scope.queryObj;
          sortObj         = sortObj || $scope.sortObj;

          saveLocal(sortObj);

          var page        =  $scope.pagesObj.current,
              queryParams =  $scope.queryObj,
              count       =  $scope.pagesObj.size,
              firstResult = (page - 1) * count;

          var defaultParams = {
            processDefinitionId: processDefinition.id
          };

          var pagingParams = {
            firstResult    : firstResult,
            maxResults     : count,
            sortBy         : sortObj.sortBy,
            sortOrder      : sortObj.sortOrder
          };

          var params = angular.extend({}, query, pagingParams, defaultParams);
          var countParams = angular.extend({}, queryParams, defaultParams);

          $scope.processInstances = null;
          $scope.loadingState = 'LOADING';

          return PluginProcessInstanceResource.count(countParams).$promise.then(function(data) {
            var total = data.count;

            return PluginProcessInstanceResource.query(pagingParams, params).$promise.then(function(data) {
              $scope.processInstances = data;
              $scope.loadingState = data.length ? 'LOADED' : 'EMPTY';
              return total;
            });
          });
        }

        function saveLocal(sortObj) {
          localConf.set('sortProcInst', sortObj);

        }

        function loadLocal(defaultValue) {
          return localConf.get('sortProcInst', defaultValue);
        }

        $scope.getProcessInstanceUrl = function(processInstance, params) {
          var path = '#/process-instance/' + processInstance.id;
          var searches = angular.extend({}, ($location.search() || {}), (params || {}));

          var keepSearchParams = [ 'viewbox' ];
          for (var i in params) {
            keepSearchParams.push(i);
          }

          return routeUtil.redirectTo(path, searches, keepSearchParams);
        };
      }],
    priority: 10
  });
}];
<|endoftext|>


<commit_before>import PropTypes from 'prop-types'
import {useEffect, useState} from 'react'
import {field} from 'tocco-app-extensions'
import {Table, EditableValue, FormattedValue} from 'tocco-ui'
import {api} from 'tocco-util'

import {arrowKeyHandler} from './keyHandler'
import {StyledCell, StyledTableWrapper} from './StyledComponents'

const dataCellRenderer = (fieldDefinition, data) => {
  const {path, dataType} = fieldDefinition
  const Field = field.factory('list', dataType)
  const value = data[path]

  return (
    <StyledCell key={path}>
      <Field type={dataType} formField={fieldDefinition} value={value} breakWords={false} />
    </StyledCell>
  )
}

const keyBuilder = (row, column) => `${row}:${column}`

const inputCellRenderer = (fieldDefinition, data, updateValue, rowIdx) => {
  const {readonly, id, dataType, options} = fieldDefinition.definition

  const pk = data.pk
  const value = data[id]

  if (readonly) {
    return (
      <StyledCell key={id}>
        <FormattedValue id={id} type={dataType} value={value} options={options} />
      </StyledCell>
    )
  } else {
    return (
      <StyledCell key={id}>
        <EditableValue
          id={keyBuilder(rowIdx, fieldDefinition.idx)}
          type={dataType}
          value={value}
          options={options}
          events={{
            onChange: changedValue => {
              updateValue(pk, id, changedValue)
            },
            onFocus: ({target}) => target.select()
          }}
        />
      </StyledCell>
    )
  }
}

const InputEditTable = ({
  data,
  dataFormColumns,
  inputEditForm,
  updateValue,
  sorting,
  setSorting,
  dataLoadingInProgress,
  totalCount,
  currentPage,
  recordsPerPage,
  setCurrentPage
}) => {
  const [columns, setColumns] = useState([])
  const [columnPosition, setColumnPosition] = useState([])

  useEffect(() => {
    const dataColumns = api.getColumnDefinition(dataFormColumns, sorting, dataCellRenderer)

    const inputColumns = inputEditForm.map((c, idx) => ({
      idx,
      id: c.id,
      label: c.label,
      sorting: {
        sortable: false
      },
      definition: c,
      resizable: true,
      dynamic: true,
      CellRenderer: ({rowData, column, rowIdx}) => inputCellRenderer(column, rowData, updateValue, rowIdx)
    }))

    const cc = [...dataColumns, ...inputColumns].sort(
      (a, b) => columnPosition.findIndex(e => e === a.id) - columnPosition.findIndex(e => e === b.id)
    )
    setColumns(cc)
  }, [columnPosition, dataFormColumns, inputEditForm, sorting, updateValue])

  const onColumnPositionChange = (dragColumn, dropColumn) => {
    setColumnPosition(
      columns.reduce((acc, c) => {
        return [...acc, ...(c.id === dropColumn ? [c.id, dragColumn] : []), ...(c.id === dragColumn ? [] : [c.id])]
      }, [])
    )
  }

  if (columns.length === 0) {
    return null
  }

  return (
    <StyledTableWrapper onKeyDown={arrowKeyHandler}>
      <Table
        dataLoadingInProgress={dataLoadingInProgress}
        columns={columns}
        data={data}
        onSortingChange={setSorting}
        onColumnPositionChange={onColumnPositionChange}
        paginationInfo={{
          currentPage,
          recordsPerPage,
          totalCount
        }}
        onPageChange={setCurrentPage}
      />
    </StyledTableWrapper>
  )
}

InputEditTable.propTypes = {
  data: PropTypes.array.isRequired,
  dataFormColumns: PropTypes.arrayOf(PropTypes.shape({id: PropTypes.string})),
  sorting: PropTypes.arrayOf(
    PropTypes.shape({
      field: PropTypes.string,
      order: PropTypes.string
    })
  ),
  inputEditForm: PropTypes.array.isRequired,
  updateValue: PropTypes.func.isRequired,
  setSorting: PropTypes.func.isRequired,
  dataLoadingInProgress: PropTypes.bool,
  totalCount: PropTypes.number,
  currentPage: PropTypes.number.isRequired,
  recordsPerPage: PropTypes.number.isRequired,
  setCurrentPage: PropTypes.func.isRequired
}

export default InputEditTable
<commit_msg>fix(input-edit): don't break table on collapsed sidepanel<commit_after>import PropTypes from 'prop-types'
import {useEffect, useState} from 'react'
import {field} from 'tocco-app-extensions'
import {Table, EditableValue, FormattedValue} from 'tocco-ui'
import {api, env} from 'tocco-util'

import {arrowKeyHandler} from './keyHandler'
import {StyledCell, StyledTableWrapper} from './StyledComponents'

const dataCellRenderer = (fieldDefinition, data) => {
  const {path, dataType} = fieldDefinition
  const Field = field.factory('list', dataType)
  const value = data[path]

  return (
    <StyledCell key={path}>
      <Field type={dataType} formField={fieldDefinition} value={value} breakWords={false} />
    </StyledCell>
  )
}

const keyBuilder = (row, column) => `${row}:${column}`

const inputCellRenderer = (fieldDefinition, data, updateValue, rowIdx) => {
  const {readonly, id, dataType, options} = fieldDefinition.definition

  const pk = data.pk
  const value = data[id]

  if (readonly) {
    return (
      <StyledCell key={id}>
        <FormattedValue id={id} type={dataType} value={value} options={options} />
      </StyledCell>
    )
  } else {
    return (
      <StyledCell key={id}>
        <EditableValue
          id={keyBuilder(rowIdx, fieldDefinition.idx)}
          type={dataType}
          value={value}
          options={options}
          events={{
            onChange: changedValue => {
              updateValue(pk, id, changedValue)
            },
            onFocus: ({target}) => target.select()
          }}
        />
      </StyledCell>
    )
  }
}

const InputEditTable = ({
  data,
  dataFormColumns,
  inputEditForm,
  updateValue,
  sorting,
  setSorting,
  dataLoadingInProgress,
  totalCount,
  currentPage,
  recordsPerPage,
  setCurrentPage
}) => {
  const [columns, setColumns] = useState([])
  const [columnPosition, setColumnPosition] = useState([])

  useEffect(() => {
    const dataColumns = api.getColumnDefinition(dataFormColumns, sorting, dataCellRenderer)

    const inputColumns = inputEditForm.map((c, idx) => ({
      idx,
      id: c.id,
      label: c.label,
      sorting: {
        sortable: false
      },
      definition: c,
      resizable: true,
      dynamic: true,
      CellRenderer: ({rowData, column, rowIdx}) => inputCellRenderer(column, rowData, updateValue, rowIdx)
    }))

    const cc = [...dataColumns, ...inputColumns].sort(
      (a, b) => columnPosition.findIndex(e => e === a.id) - columnPosition.findIndex(e => e === b.id)
    )
    setColumns(cc)
  }, [columnPosition, dataFormColumns, inputEditForm, sorting, updateValue])

  const onColumnPositionChange = (dragColumn, dropColumn) => {
    setColumnPosition(
      columns.reduce(
        (acc, c) => [
          ...acc,
          ...(c.id === dropColumn ? [c.id, dragColumn] : []),
          ...(c.id === dragColumn ? [] : [c.id])
        ],
        []
      )
    )
  }

  if (columns.length === 0) {
    return null
  }

  const embedType = env.getEmbedType()

  return (
    <StyledTableWrapper onKeyDown={arrowKeyHandler}>
      <Table
        dataLoadingInProgress={dataLoadingInProgress}
        columns={columns}
        data={data}
        onSortingChange={setSorting}
        onColumnPositionChange={onColumnPositionChange}
        paginationInfo={{
          currentPage,
          recordsPerPage,
          totalCount
        }}
        onPageChange={setCurrentPage}
        scrollBehaviour={embedType === 'admin' ? 'inline' : 'none'}
      />
    </StyledTableWrapper>
  )
}

InputEditTable.propTypes = {
  data: PropTypes.array.isRequired,
  dataFormColumns: PropTypes.arrayOf(PropTypes.shape({id: PropTypes.string})),
  sorting: PropTypes.arrayOf(
    PropTypes.shape({
      field: PropTypes.string,
      order: PropTypes.string
    })
  ),
  inputEditForm: PropTypes.array.isRequired,
  updateValue: PropTypes.func.isRequired,
  setSorting: PropTypes.func.isRequired,
  dataLoadingInProgress: PropTypes.bool,
  totalCount: PropTypes.number,
  currentPage: PropTypes.number.isRequired,
  recordsPerPage: PropTypes.number.isRequired,
  setCurrentPage: PropTypes.func.isRequired
}

export default InputEditTable
<|endoftext|>


<commit_before>/* @flow */
// $FlowFixMe
import { Dimensions } from 'react-native'

import * as React from 'react'

type Props = {
  children: React.Node,
}

export default class DimensionProvider extends React.Component<Props> {
  componentDidMount() {
    Dimensions.addEventListener('change', this.forceUpdate)
  }

  componentWillUnmount() {
    Dimensions.removeEventListener('change', this.forceUpdate)
  }

  render() {
    const { children } = this.props
    return React.Children.only(children)
  }
}
<commit_msg>fix eslint<commit_after>/* @flow */
/* eslint-disable */
// $FlowFixMe
import { Dimensions } from 'react-native'
/* eslint-enable */

import * as React from 'react'

type Props = {
  children: React.Node,
}

export default class DimensionProvider extends React.Component<Props> {
  componentDidMount() {
    Dimensions.addEventListener('change', this.forceUpdate)
  }

  componentWillUnmount() {
    Dimensions.removeEventListener('change', this.forceUpdate)
  }

  render() {
    const { children } = this.props
    return React.Children.only(children)
  }
}
<|endoftext|>