Difference between revisions of "Widget:DCTList"

From LINKS Community Center
Jump to: navigation, search
 
(34 intermediate revisions by the same user not shown)
Line 1: Line 1:
<noinclude>Current version of the DCT List.<br><span style="color: red; font-weight: bold;">Not ready for production!</span></noinclude>
+
<noinclude>DCT list widget.<br><span style="color: red; font-weight: bold;">Currently in use &ndash; do not modify!</span></noinclude>
 
<includeonly>
 
<includeonly>
     <link href="/resources/assets/tabulator.min.css" rel="stylesheet">
+
     <link href="/resources/assets/tabulator/dist/css/tabulator.min.css" rel="stylesheet">
     <script type="text/javascript" src="/resources/assets/tabulator.min.js"></script>
+
     <script type="text/javascript" src="/resources/assets/tabulator/dist/js/tabulator.min.js"></script>
  
 
     <style>
 
     <style>
Line 261: Line 261:
 
             font-weight: 300;
 
             font-weight: 300;
 
         }
 
         }
 
 
     </style>
 
     </style>
  
Line 283: Line 282:
 
         * @property {FuncData} functions
 
         * @property {FuncData} functions
 
         * @property {string} usedByDmo
 
         * @property {string} usedByDmo
 +
        * @property {string} archived
 
         * @property {string} hasUC
 
         * @property {string} hasUC
 
         * @property {string} logo
 
         * @property {string} logo
Line 367: Line 367:
 
             const DMO_PROP = 'Used by Practitioners';
 
             const DMO_PROP = 'Used by Practitioners';
 
             const UC_PROP = 'Use Cases available';
 
             const UC_PROP = 'Use Cases available';
 +
            const ARCHIVED = 'Is Archived';
 
             const dctQuery = '[[Category:Disaster Community Technology]]'
 
             const dctQuery = '[[Category:Disaster Community Technology]]'
                 + '[[Is Archived::No]]'
+
                 // + '[[Is Archived::No]]'
 
                 + '|limit=500'
 
                 + '|limit=500'
 +
                + '|?' + ARCHIVED
 
                 + '|?' + IMG_PROP
 
                 + '|?' + IMG_PROP
 
                 + '|?' + DATASRC_PROP
 
                 + '|?' + DATASRC_PROP
Line 391: Line 393:
 
                 dct.usedByDmo = dctResult.printouts[DMO_PROP][0] === 't' ? 'yes' : 'no';    // not quite, but we only care about yes
 
                 dct.usedByDmo = dctResult.printouts[DMO_PROP][0] === 't' ? 'yes' : 'no';    // not quite, but we only care about yes
 
                 dct.hasUC = dctResult.printouts[UC_PROP][0] === 't' ? 'yes' : 'no';    // not quite, but we only care about yes
 
                 dct.hasUC = dctResult.printouts[UC_PROP][0] === 't' ? 'yes' : 'no';    // not quite, but we only care about yes
 +
                dct.archived = dctResult.printouts[ARCHIVED][0] === 't' ? 'yes' : 'no';    // not quite, but we only care about yes
  
 
                 dct.functions = new Map();
 
                 dct.functions = new Map();
Line 423: Line 426:
 
                 });
 
                 });
  
                 // Empty categories should only be checked by their name, nonempty - ONLY by subfunctions.
+
                 // Empty categories should only be checked by their name
                 const checkEmpty = emptyCategories.some(cat => dct.functions.has(cat));
+
                 const checkEmpty = emptyCategories.every(cat => dct.functions.has(cat));
                 const checkNonempty = nonemptyCategories.some(cat => {
+
 
 +
                // Nonempty categories should only be checked for presence of their subfunctions. Category itself is irrelevant.
 +
                 const checkNonempty = nonemptyCategories.every(cat => {
 
                     const selectedSubs = filterState.functions.get(cat);
 
                     const selectedSubs = filterState.functions.get(cat);
                     const dctCat = dct.functions.get(cat);
+
                     const dctSubs = dct.functions.get(cat);
                     return dctCat && dctCat[FUNC_KEY] && dctCat[FUNC_KEY].some(sub => selectedSubs.includes(sub));
+
                     return dctSubs && dctSubs[FUNC_KEY] && selectedSubs.every(sub => dctSubs[FUNC_KEY].includes(sub));
 
                 });
 
                 });
  
                 functionsCheck = checkEmpty || checkNonempty;
+
                 functionsCheck = checkEmpty && checkNonempty;
 +
                // TODO: Empty should no longer exist, once every category is fully listed in the filter.
 +
                // TODO: The filtering of functions therefore should be reduced to nonempty only.
 
             }
 
             }
  
 
             const sourcesCheck = filterState.dataSources
 
             const sourcesCheck = filterState.dataSources
                 ? dct.dataSources.some(source => filterState.dataSources.includes(source))
+
                 ? filterState.dataSources.every(source => dct.dataSources.includes(source))
 
                 : true;
 
                 : true;
 
             const businessModelCheck = filterState.businessModel
 
             const businessModelCheck = filterState.businessModel
Line 446: Line 453:
 
                 ? filterState.hasUC === dct.hasUC
 
                 ? filterState.hasUC === dct.hasUC
 
                 : true;
 
                 : true;
             return sourcesCheck && functionsCheck && businessModelCheck && dmUseCheck && ucCheck;
+
            const archivedCheck = filterState.showArchived
 +
                ? true
 +
                : dct.archived !== 'yes';
 +
 
 +
             return sourcesCheck && functionsCheck && businessModelCheck && dmUseCheck && ucCheck && archivedCheck;
 
         }
 
         }
  
Line 458: Line 469:
 
             }
 
             }
  
            /** @type {Partial<Omit<DCT, 'usedByDmo'> & { usedByDmo: string[]>}} */
 
 
             const filterState = {};
 
             const filterState = {};
  
             if (
+
             if (document.querySelectorAll('#functions-filter input[type="checkbox"]:checked').length > 0) {
                // Don't pass the filter on if nothing is selected.
 
                document.querySelectorAll('#functions-filter input[type="checkbox"]:checked').length > 0
 
            ) {
 
 
                 const functionFilterBlocks = document.querySelectorAll('#functions-filter .func-filter-block');
 
                 const functionFilterBlocks = document.querySelectorAll('#functions-filter .func-filter-block');
 
                 const funcOpts = new Map();
 
                 const funcOpts = new Map();
Line 484: Line 491:
 
             if (selectedSources.length > 0) filterState.dataSources = selectedSources;
 
             if (selectedSources.length > 0) filterState.dataSources = selectedSources;
  
             const bmOptions = Array.from(document.querySelectorAll('#business-model-filter input[type="checkbox"]'));
+
             const selectedBModels = Array.from(document.querySelectorAll('#business-model-filter input[type="checkbox"]:checked'))
            const selectedBModels = bmOptions.filter(checkbox => checkbox.checked).map(checkbox => checkbox.value);
+
                .map(checkbox => checkbox.value);
             filterState.businessModel = selectedBModels.length === bmOptions.length ? undefined : selectedBModels;
+
             if (selectedBModels.length > 0) filterState.businessModel = selectedBModels;
  
             filterState.usedByDmo = document.getElementById('used-by-practitioners').checked ? 'yes' : undefined;
+
             if (document.getElementById('used-by-practitioners').checked) filterState.usedByDmo = 'yes';
             filterState.hasUC = document.getElementById('has-use-case').checked ? 'yes' : undefined;
+
             if (document.getElementById('has-use-case').checked) filterState.hasUC = 'yes';
 +
            if (document.getElementById('show-archived').checked) filterState.showArchived = 'yes';
  
 
             table.setFilter(dctFilter, filterState);
 
             table.setFilter(dctFilter, filterState);
Line 502: Line 510:
 
             Array.from(functionsData).forEach(([fnCat, fnInfo], index) => {
 
             Array.from(functionsData).forEach(([fnCat, fnInfo], index) => {
 
                 const identifier = 'func-filter-' + escapeAttr(fnCat);
 
                 const identifier = 'func-filter-' + escapeAttr(fnCat);
                 funcFilterHtml +=
+
                 funcFilterHtml +=  
 
                     `<div class="func-filter-block">
 
                     `<div class="func-filter-block">
 
                         <div>
 
                         <div>
Line 510: Line 518:
  
 
                 // add subfunctions  
 
                 // add subfunctions  
                 if (index < 4) {
+
                 funcFilterHtml += '<div class="subfunc-filter-block">';
                    funcFilterHtml += '<div class="subfunc-filter-block">';
+
                for (const func of fnInfo.functions) {
                    for (const func of fnInfo.functions) {
+
                    const subfuncId = 'subfunc-filter-' + escapeAttr(func);
                        const subfuncId = 'subfunc-filter-' + escapeAttr(func);
+
                    funcFilterHtml +=
                        funcFilterHtml +=
+
                        `<div>
                            `<div>
+
                            <input type="checkbox" id="${subfuncId}" value="${func}">
                                <input type="checkbox" id="${subfuncId}" value="${func}">
+
                            <label for="${subfuncId}">${func}</label>
                                <label for="${subfuncId}">${func}</label>
+
                        </div>`;
                            </div>`;
 
                    }
 
                    funcFilterHtml += '</div>';
 
 
                 }
 
                 }
 +
                funcFilterHtml += '</div>';
 +
 
                 funcFilterHtml += '</div>';
 
                 funcFilterHtml += '</div>';
 
             });
 
             });
Line 564: Line 571:
 
             }, '');
 
             }, '');
 
             document.getElementById('business-model-filter').innerHTML = pricingFilterHtml;
 
             document.getElementById('business-model-filter').innerHTML = pricingFilterHtml;
 
            // const dmUse = ['Yes', 'Use Case', 'Unknown'];
 
            // let dmUseFilterHtml = dmUse.reduce((acc, curr) => {
 
            //    const identifier = escapeAttr(curr);
 
            //    return acc
 
            //        + '<div><input type="checkbox" id="dm-use-filter-' + identifier
 
            //        + '" value="' + curr + '">'
 
            //        + '<label for="dm-use-filter-' + identifier + '">' + curr + '</label></div>'
 
            // }, '');
 
            // document.getElementById('dm-use-filter').innerHTML = dmUseFilterHtml;
 
  
 
             // Set up table.
 
             // Set up table.
Line 584: Line 581:
 
                         field: 'name',
 
                         field: 'name',
 
                         minWidth: 300, // required for responsiveness when using fitColumns
 
                         minWidth: 300, // required for responsiveness when using fitColumns
 +
                        widthGrow: 2,
 
                         formatter: function (cell) {
 
                         formatter: function (cell) {
 
                             /** @type {DCT} */
 
                             /** @type {DCT} */
 
                             const dct = cell.getData();
 
                             const dct = cell.getData();
                             let out = '<a href="' + dct.url + '">' + dct.name + '</a><br>';
+
                             let out = '<a href="' + dct.url + '" translate="no">' + dct.name + '</a><br>';
 +
                            if (dct.archived.toLowerCase() === 'yes') {
 +
                                out += '<small><span class="badge lcc-badge-grey">Archived</span></small> ';
 +
                            }
 
                             if (dct.businessModel.includes(FREE_KEY)) {
 
                             if (dct.businessModel.includes(FREE_KEY)) {
                                 out += '<small><span class="badge badge-success">' + FREE_KEY + '</span></small> ';
+
                                 out += '<small><span class="badge lcc-badge-green">' + FREE_KEY + '</span></small> ';
 
                             }
 
                             }
 
                             if (dct.businessModel.includes(FREE_PLAN_KEY)) {
 
                             if (dct.businessModel.includes(FREE_PLAN_KEY)) {
                                 out += '<small><span class="badge badge-success">' + FREE_PLAN_KEY + '</span></small> ';
+
                                 out += '<small><span class="badge lcc-badge-green">' + FREE_PLAN_KEY + '</span></small> ';
 
                             }
 
                             }
 
                             if (dct.usedByDmo.toLowerCase() === 'yes') {
 
                             if (dct.usedByDmo.toLowerCase() === 'yes') {
                                 out += '<small><span class="badge badge-danger">Used by practitioners</span></small> ';
+
                                 out += '<small><span class="badge lcc-badge-red">Used by practitioners</span></small> ';
 
                             }
 
                             }
 
                             if (dct.hasUC.toLowerCase() === 'yes') {
 
                             if (dct.hasUC.toLowerCase() === 'yes') {
                                 out += '<small><span class="badge badge-warning">Use case available</span></small> ';
+
                                 out += '<small><span class="badge lcc-badge-purple">Use case available</span></small> ';
 
                             }
 
                             }
  
Line 607: Line 608:
 
                         title: 'Functions',
 
                         title: 'Functions',
 
                         field: 'functions',
 
                         field: 'functions',
                         minWidth: 300, // required for responsiveness when using fitColumns
+
                         minWidth: 200, // required for responsiveness when using fitColumns
 
                         cssClass: 'functions-cell',
 
                         cssClass: 'functions-cell',
 
                         formatter: function (cell) {
 
                         formatter: function (cell) {
Line 616: Line 617:
 
                                     alt="${fn}"  
 
                                     alt="${fn}"  
 
                                     title="${fn}\n\n${functionsData.get(fn)[DESC_KEY]}">`
 
                                     title="${fn}\n\n${functionsData.get(fn)[DESC_KEY]}">`
                                    )
+
                                )
 
                                 .join('');
 
                                 .join('');
 
                         }
 
                         }
Line 664: Line 665:
  
 
                 if (encoded) {
 
                 if (encoded) {
                     const action = JSON.parse(decodeURIComponent(atob(encoded)));
+
                     const actions = JSON.parse(decodeURIComponent(atob(encoded)));
  
                     const filter = action.filter;
+
                     const filter = actions.filter;
 
                     if (filter) {
 
                     if (filter) {
 
                         // Functions filter
 
                         // Functions filter
 
                         const functions = filter.functions;
 
                         const functions = filter.functions;
 
                         if (functions) {
 
                         if (functions) {
                             Object.keys(functions).forEach(fnCat => {
+
                             Object.keys(functions).forEach(subfun => {
                                 document.getElementById('func-filter-' + escapeAttr(fnCat))
+
                                 const subfunEl = document.getElementById('subfunc-filter-' + escapeAttr(subfun));
                                    .closest('.func-filter-block')
+
                                subfunEl.checked = !!functions[subfun];
                                    .querySelectorAll('input[type="checkbox"]').forEach(box => box.checked = !!functions[fnCat]);
+
                                subfunEl.dispatchEvent(new Event('change', { bubbles: true }));
 
                             });
 
                             });
  
Line 690: Line 691:
 
                     // ...
 
                     // ...
 
                 }
 
                 }
 +
 +
                applyFilters();
 
             });
 
             });
  
Line 781: Line 784:
 
                     const checkedSubs = Array.from(subfunctions).filter(sub => sub.checked).length;
 
                     const checkedSubs = Array.from(subfunctions).filter(sub => sub.checked).length;
 
                     category.checked = checkedSubs > 0;
 
                     category.checked = checkedSubs > 0;
                    // if (checkedSubs > 0) { category.checked = true; } else { category.checked = false; }
 
 
                 }
 
                 }
  
Line 892: Line 894:
 
         <div id="dct-intro">
 
         <div id="dct-intro">
 
             <p>The overall goal of the Social Media and Crowdsourcing (SMCS) Technologies Library is to face the growing
 
             <p>The overall goal of the Social Media and Crowdsourcing (SMCS) Technologies Library is to face the growing
                 heterogeneous use of technologies in disasters and the overwhelming number of technologies on the market. It
+
                 heterogeneous use of technologies in disasters and the overwhelming number of technologies on the
                 gathers and structures information about existing technologies to provide an up-to-date overview and thus
+
                market. It
 +
                 gathers and structures information about existing technologies to provide an up-to-date overview and
 +
                thus
 
                 support the selection of suitable technologies.
 
                 support the selection of suitable technologies.
 
             </p>
 
             </p>
 
             <p>
 
             <p>
                 You can use the filters to identify relevant technologies according to your needs and then click on the name of
+
                 You can use the filters to identify relevant technologies according to your needs and then click on the
 +
                name of
 
                 the technology to get further information.
 
                 the technology to get further information.
 
             </p>
 
             </p>
Line 914: Line 919:
 
             </div>
 
             </div>
  
             <h2 style="margin-top: 2.5rem; margin-bottom: 0;">Results: <span id="result-count"></span></h2>
+
             <div style="display: flex; justify-content: space-between; align-items: center; margin-top: 2.5rem;">
 +
                <h2 style="margin-bottom: 0;">Results: <span id="result-count"></span></h2>
 +
                <div><a href="/index.php/Form:Disaster_Community_Technology" style="color: var(--links-blue); font-size: 1.5em; font-variant:small-caps">Add new technology</a></div>
 +
            </div>
  
 
             <div id="dct-filters">
 
             <div id="dct-filters">
Line 936: Line 944:
 
                 </h2>
 
                 </h2>
 
                 <div style="text-align: center;">
 
                 <div style="text-align: center;">
                     <button class="large-button" type="button" onclick="clearFilters()">Show All</button>
+
                     <button class="large-button" type="button" onclick="clearFilters()">Clear Filters</button>
 
                 </div>
 
                 </div>
 
                 <div class="filter-wrapper">
 
                 <div class="filter-wrapper">
Line 982: Line 990:
 
                     </div>
 
                     </div>
 
                 </div> -->
 
                 </div> -->
                 <div id="bool-filters" style="border-top: 1px solid var(--links-blue); margin-top: 1em; padding-top: 1em;">
+
                 <div id="bool-filters"
 +
                    style="border-top: 1px solid var(--links-blue); margin-top: 1em; padding-top: 1em;">
 
                     <div>
 
                     <div>
 
                         <input type="checkbox" id="used-by-practitioners" value="yes">
 
                         <input type="checkbox" id="used-by-practitioners" value="yes">
Line 990: Line 999:
 
                         <input type="checkbox" id="has-use-case" value="yes">
 
                         <input type="checkbox" id="has-use-case" value="yes">
 
                         <label for="has-use-case">Use case available</label>
 
                         <label for="has-use-case">Use case available</label>
 +
                    </div>
 +
                    <div>
 +
                        <input type="checkbox" id="show-archived" value="yes">
 +
                        <label for="show-archived">Show archived</label>
 
                     </div>
 
                     </div>
 
                 </div>
 
                 </div>

Latest revision as of 15:30, 19 December 2023

DCT list widget.
Currently in use – do not modify!