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