1<#-- Variables -->
2<#assign specificationName = 'ctn' />
3<#assign paramNameSpecificationName = cpSpecificationOptionsSearchFacetDisplayContext.getParameterName() />
4<#assign isDebug = false />
5<#assign showAllSpecificationsName = false />
6<#assign staticHost = getCXConfig("ecom-static-files") />
7
8<#-- ========================= -->
9<#-- Functions freemaker -->
10<#-- ========================= -->
11<#function getCXConfig clientExtensionName field="webApiUrl">
12 <#if !clientExtensionName?has_content><#return "" /></#if>
13 <#local encoded = urlCodec.encodeURL(clientExtensionName) />
14 <#local items = restClient.get("/c/customconfigs/?fields=clientExtensionConfig&filter=clientExtensionName eq '" + encoded + "'").items![] />
15 <#return items?has_content?then(items[0].clientExtensionConfig?eval[field]!"", "") />
16</#function>
17
18 <#function getTaxonomyVocabularyId vocabularyName>
19 <#local response = restClient.get("/headless-admin-taxonomy/v1.0/sites/" + groupId + "/taxonomy-vocabularies?pageSize=50")!{} />
20 <#list response.items![] as vocab>
21 <#if vocab.name?lower_case == vocabularyName?lower_case>
22 <#return vocab.id />
23 </#if>
24 </#list>
25 <#return "" />
26</#function>
27
28<#function getTaxonomyCategoryMap vocabularyId pageSize=200>
29 <#local response = restClient.get("/headless-admin-taxonomy/v1.0/taxonomy-vocabularies/" + vocabularyId + "/taxonomy-categories?pageSize=" + pageSize)!{} />
30 <#local categoryMap = {} />
31 <#list response.items![] as cat>
32 <#local categoryMap = categoryMap + {vocabularyId + "-" + cat.id: cat.name} />
33 </#list>
34 <#return categoryMap />
35</#function>
36
37<#function getSpecOrganismosMap specificationKey organismoMap aggResult>
38 <#local specMap = {} />
39 <#if !aggResult?has_content><#return specMap /></#if>
40 <#list aggResult.getBuckets() as specBucket>
41 <#local specKey = specBucket.getKey() />
42 <#local organismoAgg = specBucket.getChildAggregationResult("organismos") />
43 <#local orgNames = [] />
44 <#list organismoAgg.getBuckets() as orgBucket>
45 <#local orgName = organismoMap[orgBucket.getKey()]!"?" />
46 <#local orgNames = orgNames + [orgName] />
47 </#list>
48 <#local specMap = specMap + {specKey: orgNames} />
49 </#list>
50 <#return specMap />
51</#function>
52
53<#function getAggregationResult specificationKey>
54 <#local sharedSearchResponse = renderRequest.getAttribute("LIFERAY_SHARED_PortletSharedSearchResponse") />
55 <#local searchResponse = sharedSearchResponse.get() />
56 <#local esResponse = searchResponse.getSearchResponse() />
57 <#return esResponse.getAggregationResult(specificationKey + "_with_organismos") />
58</#function>
59
60<#if paramNameSpecificationName == specificationName || showAllSpecificationsName>
61
62 <#-- ========================= -->
63 <#-- Datos -->
64 <#-- ========================= -->
65 <#assign organismoMap = getTaxonomyCategoryMap("39789900") />
66 <#assign aggResult = getAggregationResult(paramNameSpecificationName) />
67 <#assign specOrganismosMap = getSpecOrganismosMap(paramNameSpecificationName, organismoMap, aggResult) />
68
69 <#-- ========================= -->
70 <#-- Debug -->
71 <#-- ========================= -->
72 <#if isDebug>
73 <div style="background:#f0f0f0;padding:8px;margin-bottom:8px;font-size:11px;border:1px solid #ccc;">
74 <p><strong>paramName:</strong> ${paramNameSpecificationName}</p>
75 <p><strong>organismoMap size:</strong> ${organismoMap?size}</p>
76 <p><strong>specOrganismosMap size:</strong> ${specOrganismosMap?size}</p>
77 <#list specOrganismosMap?keys as spec>
78 <p>${spec}: ${specOrganismosMap[spec]?join(", ")}</p>
79 </#list>
80 </div>
81 </#if>
82
83 <#-- Variables -->
84 <#assign facetId = "facet-" + paramNameSpecificationName + "-" + renderResponse.getNamespace() />
85 <#assign facetClass = "facet-" + paramNameSpecificationName + "-combo-search-wrapper" />
86
87 <#-- ========================= -->
88 <#-- TOM SELECT framework -->
89 <#-- ========================= -->
90 <@liferay_util["html-top"] outputKey="tom-select">
91 <link href="${staticHost}/scripts/vendor/tom-select/tom-select.css" rel="stylesheet">
92 <script src="${staticHost}/scripts/vendor/tom-select/tom-select.complete.min.js"></script>
93 </@>
94
95 <#-- ========================= -->
96 <#-- CSS -->
97 <#-- ========================= -->
98 <style>
99 #${facetId} {
100 --facet-font-family: Inter;
101 --facet-font-size: 14px;
102 --facet-font-weight: 400;
103 --facet-line-height: 18px;
104 --facet-letter-spacing: 0.5px;
105 --facet-color: #66757f;
106 }
107 #${facetId}-select {
108 appearance: none;
109 -webkit-appearance: none;
110 visibility: hidden;
111 position: absolute;
112 }
113 #${facetId} .ts-wrapper {
114 width: 100%;
115 margin-bottom: 16px;
116 opacity: 0;
117 transition: opacity 0.1s ease;
118 }
119 #${facetId} .ts-wrapper .ts-control {
120 padding: 15.5px 16px;
121 font-family: var(--facet-font-family);
122 font-size: var(--facet-font-size);
123 font-weight: var(--facet-font-weight);
124 line-height: var(--facet-line-height);
125 letter-spacing: var(--facet-letter-spacing);
126 color: var(--facet-color);
127 text-align: left;
128 height: 55px;
129 width: 100%;
130 border-radius: 4px;
131 border: none;
132 background-color: #F5F5F5;
133 background-image: url(/documents/d/global/ico-chevron-down-2);
134 background-repeat: no-repeat;
135 background-position: right 1rem center;
136 background-size: 18px 10px;
137 box-shadow: none;
138 cursor: pointer;
139 box-sizing: border-box;
140 }
141 #${facetId} .ts-wrapper .ts-control:focus,
142 #${facetId} .ts-wrapper .ts-control:focus-visible {
143 background-image: url(/documents/d/global/ico-chevron-down-2);
144 background-position: right 1rem center;
145 background-size: 18px 10px;
146 outline: none;
147 box-shadow: none;
148 }
149 #${facetId} .ts-wrapper .ts-control .item {
150 padding-right: 0.75rem;
151 overflow: hidden;
152 text-overflow: ellipsis;
153 max-width: calc(100% - 0.75rem);
154 }
155 #${facetId} .ts-wrapper.single .ts-control::after {
156 display: none;
157 }
158 #${facetId} .ts-dropdown .dropdown-input-wrap .dropdown-input {
159 font-family: var(--facet-font-family);
160 font-size: var(--facet-font-size);
161 font-weight: var(--facet-font-weight);
162 line-height: var(--facet-line-height);
163 letter-spacing: var(--facet-letter-spacing);
164 color: var(--facet-color);
165 padding: 8px 16px;
166 border: none;
167 border-bottom: 1px solid #d9d9d9;
168 background-color: #fff;
169 width: 100%;
170 box-sizing: border-box;
171 }
172 #${facetId} .ts-dropdown .dropdown-input-wrap .dropdown-input:focus {
173 outline: none;
174 box-shadow: none;
175 }
176 #${facetId} .ts-dropdown .ts-dropdown-content .option {
177 font-family: var(--facet-font-family);
178 font-size: var(--facet-font-size);
179 font-weight: var(--facet-font-weight);
180 line-height: var(--facet-line-height);
181 letter-spacing: var(--facet-letter-spacing);
182 color: var(--facet-color);
183 padding: 8px 16px;
184 }
185 #${facetId} .ts-dropdown .ts-dropdown-content .option:hover,
186 #${facetId} .ts-dropdown .ts-dropdown-content .option.active {
187 background-color: #6a9bd3;
188 color: #fff;
189 }
190 #${facetId} .ts-dropdown .ts-dropdown-content .option:hover .badge,
191 #${facetId} .ts-dropdown .ts-dropdown-content .option.active .badge {
192 background-color: #fff;
193 color: #3a6a9b;
194 }
195 </style>
196
197 <div class="checks-container ${facetClass}" id="${facetId}">
198 <div class="d-flex flex-column w-100">
199 <label class="panel-title mb-2" for="${facetId}-select">
200 ${languageUtil.get(locale, "norma." + paramNameSpecificationName)}
201 <#if isDebug>
202 <p style="font-size:11px;color:#999;font-weight:normal;">
203 (total options: ${entries?size})
204 </p>
205 <p style="font-size:11px;color:#999;font-weight:normal;">
206 (facetId: ${facetId})
207 </p>
208 </#if>
209 </label>
210
211 <#-- ========================= -->
212 <#-- SELECT UI -->
213 <#-- ========================= -->
214 <select id="${facetId}-select" data-parameter-name="${paramNameSpecificationName}">
215 <option value="">${languageUtil.get(locale, "search.cualquiera")}</option>
216 <#list entries?sort_by("displayName") as entry>
217 <#assign specName = entry.getDisplayName() />
218 <#assign orgNames = specOrganismosMap[specName]![] />
219 <option value="${htmlUtil.escape(specName)}"
220 <#if entry.isSelected()>selected</#if>
221 data-organismos="${htmlUtil.escape(orgNames?join(","))}">
222 ${htmlUtil.escape(specName)} (${entry.getFrequency()})
223 </option>
224 </#list>
225 </select>
226 </div>
227 </div>
228
229 <#-- ========================= -->
230 <#-- SCRIPT -->
231 <#-- ========================= -->
232 <script>
233 (function () {
234 var FACET_ID = '${facetId}';
235 var PARAM_NAME = '${paramNameSpecificationName}';
236
237 function applyUrlFilter(value) {
238 var url = new URL(window.location.href);
239 if (value) {
240 url.searchParams.set(PARAM_NAME, value);
241 } else {
242 url.searchParams.delete(PARAM_NAME);
243 }
244 window.location.href = url.toString();
245 }
246
247 function initFacet() {
248 var select = document.getElementById(FACET_ID + '-select');
249 if (!select) return;
250 if (select.tomselect) select.tomselect.destroy();
251 if (select.dataset.bound === 'true') return;
252 select.dataset.bound = 'true';
253
254 new TomSelect(select, {
255 allowEmptyOption: true,
256 maxItems: 1,
257 create: false,
258 render: {
259 option: function(data, escape) {
260 var organismos = data.$option ? data.$option.getAttribute('data-organismos') : '';
261 var badges = '';
262 if (organismos) {
263 organismos.split(',').forEach(function(org) {
264 badges += '<span class="badge badge-secondary mr-1">' + escape(org.trim()) + '</span>';
265 });
266 }
267 return '<div class="option py-1">' + escape(data.text) + '<div class="mt-1">' + badges + '</div></div>';
268 },
269 no_results: function(data, escape) {
270 return '<div class="no-results">${languageUtil.get(locale, "occurrence-not-found")}</div>';
271 }
272 },
273 plugins: {
274 dropdown_input: {}
275 },
276 onDelete: function(value) {
277 this.isDelete = true;
278 },
279 onChange: function(value) {
280 if (value) {
281 this.lastValidValue = value;
282 applyUrlFilter(value);
283 return;
284 }
285 this.lastValidValue = '';
286 applyUrlFilter('');
287 },
288 onInitialize: function() {
289 this.lastValidValue = this.getValue() || '';
290 this.isDelete = false;
291 var wrapper = document.querySelector('#' + FACET_ID + ' .ts-wrapper');
292 if (wrapper) wrapper.style.opacity = '1';
293 }
294 });
295 }
296
297 if (document.readyState === 'loading') {
298 document.addEventListener('DOMContentLoaded', initFacet);
299 } else {
300 initFacet();
301 }
302 })();
303 </script>
304
305</#if>