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