Skip to content

Commit

Permalink
Merge pull request #409 from gencat/add/decidim_finder
Browse files Browse the repository at this point in the history
Add Decidims finder
  • Loading branch information
laurajaime authored Mar 28, 2023
2 parents ae2836a + baa94f9 commit 19732af
Show file tree
Hide file tree
Showing 31 changed files with 1,268 additions and 41 deletions.
5 changes: 5 additions & 0 deletions .erb-lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
EnableDefaultLinters: true
linters:
AllowedScriptType:
enabled: false
11 changes: 11 additions & 0 deletions app/controllers/decidims_finder_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

class DecidimsFinderController < Decidim::ApplicationController
layout "layouts/decidim/application"

helper_method :current_translations

def show
@current_translations = I18n.t("participagencat.decidims_finder_page")
end
end
24 changes: 6 additions & 18 deletions app/packs/entrypoints/application.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,7 @@
/* eslint no-console:0 */
// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/packs and only use these pack files to reference
// that code so it'll be compiled.
//
// To reference this file, add <%= javascript_pack_tag 'application' %> to the appropriate
// layout file, like app/views/layouts/application.html.erb
// This file is compiled inside Decidim core pack. Code can be added here and will be executed
// as part of that pack
import "src/decidim/admin/process"
import "src/decidim/participa/regulations"

// Uncomment to copy all static images under ../images to the output folder and reference
// them with the image_pack_tag helper in views (e.g <%= image_pack_tag 'rails.png' %>)
// or the `imagePath` JavaScript helper below.
//
// const images = require.context('../images', true)
// const imagePath = (name) => images(name, true)

// Activate Active Storage
// import * as ActiveStorage from "@rails/activestorage"
// ActiveStorage.start()
// Load images
require.context("../images", true)
2 changes: 2 additions & 0 deletions app/packs/entrypoints/decidims_finder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import "src/decidim/dates-range-widget"
import "src/decidim/decidims_finder"
Empty file removed app/packs/images/.keep
Empty file.
Binary file added app/packs/images/finder-first.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/packs/images/finder-first_disabled.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/packs/images/finder-last.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/packs/images/finder-last_disabled.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/packs/images/finder-next.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/packs/images/finder-next_disabled.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/packs/images/finder-previous.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/packs/images/finder-previous_disabled.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file removed app/packs/src/decidim/.keep
Empty file.
164 changes: 164 additions & 0 deletions app/packs/src/decidim/dates-range-widget.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
/* global instantsearch */

export const datesRange = function(args) {
var container = args['container'];
var attributeName = args['attributeName'];
var id = args['id'];

return {
fromInput: null,
toInput: null,
_refine: function (helper, from, to) {
const facetValues = this._extractRefinedRange(helper);
helper.clearRefinements(attributeName);
if (facetValues.length === 0 || facetValues[0].from !== from || facetValues[0].to !== to) {
if (typeof from !== 'undefined' && !isNaN(from)) {
helper.addNumericRefinement(attributeName, '>=', from);
}
if (typeof to !== 'undefined' && !isNaN(to)) {
helper.addNumericRefinement(attributeName, '<=', to);
}
}

helper.search();
},
_extractRefinedRange(helper) {
const refinements = helper.getRefinements(attributeName);
let from;
let to;

if (refinements.length === 0) {
return [];
}

refinements.forEach(v => {
if (v.operator.indexOf('>') !== -1) {
from = Math.floor(v.value[0]);
} else if (v.operator.indexOf('<') !== -1) {
to = Math.ceil(v.value[0]);
}
});

return [{from, to, isRefined: true}];
},
_createComponent: function(fromValue, toValue) {
var self = this;

var createContainer = function(className, elements) {
var _container = document.createElement("div");

if (className) {
_container.classList.add(className);
}

if (elements && Array.isArray(elements)) {
elements.forEach(function(element) {
_container.append(element);
})
}

return _container;
}

var generateWidgetId = function(name) {
return id + '_' + name;
}

var createWidgetStructure = function(label, inputElement) {
document.querySelector(container).append(
createContainer("form-group", [
label, createContainer(null, [inputElement])
])
);
}

var createInput = function(name, value, onChange) {
var input = document.createElement('input');
input.setAttribute("type", "date");
input.id = generateWidgetId(name);

if (value) {
input.value = new Date(value).toJSON().substring(0,10);
}

input.onchange = onChange;

return input;
}

var createLabel = function(name, text) {
var label = document.createElement('label');
label.setAttribute('for', generateWidgetId(name));
label.innerHTML = text;

return label;
}

var onChange = function() {
var fromValue = new Date(self.fromInput.value).getTime();
var toValue = new Date(self.toInput.value).getTime();

self._refine(fromValue, toValue);
}

this.fromInput = createInput("from", fromValue, onChange);
this.toInput = createInput("to", toValue, onChange);

createWidgetStructure(createLabel("from", getTranslation("from")), this.fromInput);
createWidgetStructure(createLabel("to", getTranslation("to")), this.toInput);

},
getConfiguration: function () {
return {
facets: [attributeName]
}
},
init: function (options) {
this._refine = this._refine.bind(this, options.helper);
},
render: function (options) {
var results = options['results'];
var helper = options['helper'];
var state = options['state'];
var createURL = options['createURL'];

let facetValues = [];

if (results.hits.length > 0) {
facetValues = this._extractRefinedRange(helper);
}

var mappedValues = facetValues.map(facetValue => {
let newState = state.clearRefinements(attributeName);
if (!facetValue.isRefined) {
if (facetValue.from !== undefined) {
newState = newState.addNumericRefinement(attributeName, '>=', facetValue.from);
}
if (facetValue.to !== undefined) {
newState = newState.addNumericRefinement(attributeName, '<=', facetValue.to);
}
}

facetValue.url = createURL(newState);

return facetValue;
});

var values = mappedValues.find(function(value) {
return value.hasOwnProperty("from") && value.hasOwnProperty("to");
});

var valueFrom = null;
var valueTo = null;

if (values) {
valueFrom = values["from"];
valueTo = values["to"];
}

if (!this.fromInput && !this.toInput) {
this._createComponent(valueFrom, valueTo);
}
}
};
}
5 changes: 0 additions & 5 deletions app/packs/src/decidim/decidim_application.js

This file was deleted.

145 changes: 145 additions & 0 deletions app/packs/src/decidim/decidims_finder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/* global instantsearch */
const algoliasearch = require("algoliasearch");
import instantsearch from "instantsearch.js";

import {
searchBox,
hits,
clearRefinements,
stats,
pagination,
hierarchicalMenu } from 'instantsearch.js/es/widgets';
import { datesRange } from './dates-range-widget';

// Replace with your own values
const searchClient = algoliasearch(
getAlgolia().id,
getAlgolia().key // search only API key, not admin API key
);

const search = instantsearch({
indexName: getAlgolia().index,
searchClient,
routing: true,
});

const searchBoxWidget = searchBox({
container: '#query',
placeholder: getTranslation("query_placeholder"),
searchAsYouType: true,
showReset: false,
showSubmit: false,
showLoadingIndicator: false,
})

const hitsWidget = hits({
container: '#hits',
hitsPerPage: 10,
templates: {
item: getTemplate('hit'),
empty: getTemplate('no-results')
}
})

const clearRefinementsTerritory = clearRefinements({
container: '#clear-territory',
includedAttributes: ['province', 'region', 'municipality'],
templates: {
resetLabel: getTranslation("clear"),
}
})

const clearRefinementsStartDate = clearRefinements({
container: '#clear-start_date',
includedAttributes: ['start_date_timestamp'],
templates: {
resetLabel: getTranslation("clear"),
}
})

const clearRefinementsEndDate = clearRefinements({
container: '#clear-end_date',
includedAttributes: ['end_date_timestamp'],
templates: {
resetLabel: getTranslation("clear"),
}
})

const statsWidget = stats({
container: '#stats',
templates: {
text: '{{#hasManyResults}}' + getTranslation("results") + '{{/hasManyResults}}'
}
})

const paginationWidget = pagination({
container: '#pagination',
autoHideContainer: false,
scrollTo: '#query',
showFirstLast: true,
maxpages: 12,
labels: {
previous: getTranslation("previous"),
next: getTranslation("next"),
first: getTranslation("first"),
last: getTranslation("last")
}
})

const hierarchicalMenuWidget = hierarchicalMenu({
container: '#territory .panel-body-finder',
attributes: ['province', 'region', 'municipality'],
sortBy: function compare(a, b) {
if (a.name[0] < b.name[0] && b.name != 'Catalunya' || a.name == 'Catalunya') return -1;
if (a.name[0] > b.name[0] || b.name == 'Catalunya') return 1;
return 0;
}
})

const datesRangeStart = datesRange({
container: "#start_date .panel-body-finder",
attributeName: 'start_date_timestamp',
id: "start_date"
})

const datesRangeEnd = datesRange({
container: "#end_date .panel-body-finder",
attributeName: 'end_date_timestamp',
id: "end_date"
})

search.addWidgets([
searchBoxWidget,
hitsWidget,
clearRefinementsTerritory,
clearRefinementsStartDate,
clearRefinementsEndDate,
statsWidget,
paginationWidget,
hierarchicalMenuWidget,
datesRangeStart,
datesRangeEnd
])

// Event listener to reset the Date Range widgets as they aren't supported by InstantSearch v1.
//Start Date range
document.getElementById("clear-start_date").addEventListener("click", function () {
document.getElementById("start_date_from").value = ""
document.getElementById("start_date_from").dispatchEvent(new Event('change'));
document.getElementById("start_date_to").value = ""
document.getElementById("start_date_to").dispatchEvent(new Event('change'));
});

//End Date range
document.getElementById("clear-end_date").addEventListener("click", function () {
document.getElementById("end_date_from").value = ""
document.getElementById("end_date_from").dispatchEvent(new Event('change'));
document.getElementById("end_date_to").value = ""
document.getElementById("end_date_to").dispatchEvent(new Event('change'));
});

function getTemplate(templateName) {
return document.querySelector('#' + templateName + '-template').innerHTML;
}

search.start();
Empty file.
Loading

0 comments on commit 19732af

Please sign in to comment.