| /*
 * Pim
 * Free Extension
 * Copyright (c) TreoLabs GmbH
 * Copyright (c) Kenner Soft Service GmbH
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
Espo.define('pim:views/product/record/catalog-tree-panel/category-tree', 'view',
    Dep => Dep.extend({
        template: 'pim:product/record/catalog-tree-panel/category-tree',
        loadedCategories: [],
        expandableCategory: null,
        events: {
            'show.bs.collapse div.panel-collapse.collapse[class^="catalog-"]': function (e) {
                e.stopPropagation();
                $(e.currentTarget).prev('button.catalog-link').find('span.caret').addClass('caret-up');
                this.$el.parent().find(`.panel-collapse.collapse[class^="catalog-"].in`).collapse('hide');
            },
            'hide.bs.collapse div.panel-collapse.collapse[class^="catalog-"]': function (e) {
                e.stopPropagation();
                $(e.currentTarget).prev('button.catalog-link').find('span.caret').removeClass('caret-up');
            },
            'show.bs.collapse div.panel-collapse.collapse[class^="category-"]': function (e) {
                e.stopPropagation();
            },
            'shown.bs.collapse div.panel-collapse.collapse[class^="category-"]': function (e) {
                e.stopPropagation();
                this.unfold($(e.currentTarget).data('id'));
            },
            'hide.bs.collapse div.panel-collapse.collapse[class^="category-"]': function (e) {
                e.stopPropagation();
            },
            'hidden.bs.collapse div.panel-collapse.collapse[class^="category-"]': function (e) {
                e.stopPropagation();
                this.fold($(e.currentTarget).data('id'));
            },
            'click button.category.child-category': function (e) {
                const id = $(e.currentTarget).data('id');
                this.setCategoryActive(id);
                this.selectCategory(id);
            }
        },
        
        data() {
            return {
                catalog: this.options.catalog,
                rootCategoriesList: this.getRootCategoriesList(),
                hash: this.getRandomHash()
            }
        },
        setup() {
            this.categories = this.options.categories || [];
            this.catalog = this.options.catalog;
            this.loadedCategories = [];
        },
        getRootCategoriesList() {
            return this.categories
                .filter(category => this.catalog.categoriesIds.includes(category.id))
                .map(category => {
                    return {
                        id: category.id,
                        html: category.childrenCount ? this.getParentHtml(category, this.isRendered()) : this.getChildHtml(category)
                    };
                });
        },
        getParentHtml(category, fullLoad) {
            let hash = this.getRandomHash();
            let html = '';
            if (fullLoad) {
                (category.childs || []).forEach(child => {
                    html += child.childrenCount ? this.getParentHtml(child, (child.childs || []).length) : this.getChildHtml(child);
                });
            }
            return `
                <li data-id="${category.id}" class="list-group-item child">
                    <button class="btn btn-link category category-icons" data-toggle="collapse" data-target=".category-${hash}" data-id="${category.id}" data-name="${category.name}">
                        <span class="fas fa-chevron-right"></span>
                        <span class="fas fa-chevron-down hidden"></span>
                    </button>
                    <button class="btn btn-link category child-category" data-id="${category.id}">
                        ${category.name}
                    </button>
                    <div class="category-${hash} panel-collapse collapse" data-id="${category.id}">
                        <ul class="list-group list-group-tree">${html}</ul>
                    </div>
                </li>`;
        },
        getChildHtml(category) {
            return `
                <li data-id="${category.id}" class="list-group-item child">
                    <button class="btn btn-link category child-category" data-id="${category.id}" data-name="${category.name}">
                        ${category.name}
                    </button>
                </li>`;
        },
        getRandomHash() {
            return Math.floor((1 + Math.random()) * 0x100000000)
                .toString(16)
                .substring(1);
        },
        fold(id) {
            let button = this.$el.find(`button.category-icons[data-id="${id}"]`);
            button.find('span.fa-chevron-right').removeClass('hidden');
            button.find('span.fa-chevron-down').addClass('hidden');
        },
        unfold(id) {
            this.setupCategoryTree(id, () => {
                let button = this.$el.find(`button.category-icons[data-id="${id}"]`);
                button.find('span.fa-chevron-right').addClass('hidden');
                button.find('span.fa-chevron-down').removeClass('hidden');
                this.expandCategoriesFromRoute(id);
            });
        },
        setupCategoryTree(id, callback) {
            let promise;
            let category = this.loadedCategories.find(item => item.id === id);
            if (!category) {
                promise = new Promise(resolve => {
                    this.getTreeCategories(id, categories => {
                        category = this.categories.find(item => item.id === id);
                        this.categories = this.categories.concat(categories);
                        this.setCategoryChilds(category, categories);
                        resolve();
                    });
                });
            } else {
                promise = new Promise(resolve => resolve());
            }
            promise.then(() => {
                this.buildCategoryHtml(category);
                callback();
            });
        },
        getTreeCategories(id, callback) {
            this.getFullEntity('Category', {
                select: 'name,categoryParentId,categoryRoute,childrenCount',
                where: [
                    {
                        type: 'equals',
                        attribute: 'categoryParentId',
                        value: id
                    }
                ]
            }, categories => {
                callback(categories);
            });
        },
        setCategoryChilds(category, categories) {
            categories.sort((a, b) => {
                if (a.childrenCount && !b.childrenCount) {
                    return -1;
                } else if (!a.childrenCount && b.childrenCount) {
                    return 1;
                } else {
                    return a.name.localeCompare(b.name);
                }
            });
            category.childs = categories;
            this.loadedCategories.push(category);
        },
        buildCategoryHtml(category) {
            let button = this.$el.find(`button.category-icons[data-id="${category.id}"]`);
            let listEl = button.parent().find(`.panel-collapse[data-id="${category.id}"] .list-group-tree`);
            if (!listEl.find('li').size()) {
                let html = '';
                category.childs.forEach(item => {
                    html += item.childrenCount ? this.getParentHtml(item) : this.getChildHtml(item);
                });
                listEl.append(html);
            }
        },
        getFullEntity(url, params, callback, container) {
            if (url) {
                container = container || [];
                let options = params || {};
                options.maxSize = options.maxSize || 200;
                options.offset = options.offset || 0;
                this.ajaxGetRequest(url, options).then(response => {
                    container = container.concat(response.list || []);
                    options.offset = container.length;
                    if (response.total > container.length || response.total === -1) {
                        this.getFullEntity(url, options, callback, container);
                    } else {
                        callback(container);
                    }
                });
            }
        },
        expandCategoryHandler(category) {
            if (typeof category === 'string') {
                category = this.categories.find(item => item.id === category);
            }
            if (category) {
                if (this.$el.size()) {
                    this.expandCategory(category);
                } else {
                    this.listenTo(this, 'after:render', () => this.expandCategory(category));
                }
            }
        },
        expandCategory(category) {
            this.expandableCategory = category;
            let catalogCollapse = this.$el.find('.collapse[class^="catalog-"]');
            catalogCollapse.collapse("show");
            let routes = (category.categoryRoute || '').split('|').filter(element => element);
            if (routes.length) {
                routes.some(routeId => {
                    const nextCollapse = catalogCollapse.find(`.collapse[data-id="${routeId}"]`);
                    if (nextCollapse.hasClass('in')) {
                        this.expandCategoriesFromRoute(routeId);
                        return false;
                    } else {
                        nextCollapse.collapse('show');
                        return true;
                    }
                });
            } else {
                this.setCategoryActive(this.expandableCategory.id);
            }
        },
        expandCategoriesFromRoute(categoryId) {
            if (this.expandableCategory) {
                let routes = (this.expandableCategory.categoryRoute || '').split('|').filter(element => element);
                let atLeastOne = routes.some(routeCategoryId => {
                    let nextCollapse = this.$el.find(`.collapse[data-id="${routeCategoryId}"]:not(.in)`);
                    if (categoryId !== routeCategoryId && nextCollapse.size()) {
                        nextCollapse.collapse('show');
                        return true;
                    }
                });
                if (!atLeastOne && this.expandableCategory) {
                    let expandableCategory = this.$el.find(`.category[data-id="${this.expandableCategory.id}"]`);
                    if (expandableCategory.size()) {
                        this.setCategoryActive(this.expandableCategory.id);
                        this.expandableCategory = null;
                    }
                }
            }
        },
        selectCategory(category) {
            if (typeof category === 'string') {
                category = this.categories.find(item => item.id === category);
                category.catalogId = this.catalog.id;
            }
            this.trigger('category-tree-select', category);
        },
        setCategoryActive(id) {
            if (id) {
                let panel = this.$el.parents('.category-panel');
                panel.find('.category-buttons > button').removeClass('active');
                panel.find('li.child.active').removeClass('active');
                this.$el.find(`li.child[data-id="${id}"]`).addClass('active');
            }
        }
    })
);
 |