Add webpacker and use it for new_design

This commit is contained in:
Paul Chavard 2018-07-12 11:50:47 +02:00
parent f13056437c
commit bf7c023380
68 changed files with 8534 additions and 480 deletions

18
.babelrc Normal file
View file

@ -0,0 +1,18 @@
{
"presets": [
["env", {
"modules": false,
"targets": {
"browsers": "> 1%",
"uglify": true
},
"useBuiltIns": true
}]
],
"plugins": [
"syntax-dynamic-import",
"transform-object-rest-spread",
["transform-class-properties", { "spec": true }]
]
}

View file

@ -25,6 +25,21 @@ bundle_install: &bundle_install
name: Install Ruby Dependencies
command: bundle install --path ~/vendor/bundle
yarn_restore_cache: &yarn_restore_cache
restore_cache:
key: yarn-install-v8-{{ arch }}-{{ checksum "yarn.lock" }}
yarn_save_cache: &yarn_save_cache
save_cache:
key: yarn-install-v8-{{ arch }}-{{ checksum "yarn.lock" }}
paths:
- ~/node_modules
yarn_install: &yarn_install
run:
name: Install JS Dependencies
command: yarn install --non-interactive
jobs:
build:
<<: *defaults
@ -33,6 +48,9 @@ jobs:
- *bundle_restore_cache
- *bundle_install
- *bundle_save_cache
- *yarn_restore_cache
- *yarn_save_cache
- *yarn_install
test:
<<: *defaults
parallelism: 3
@ -40,6 +58,8 @@ jobs:
- checkout
- *bundle_restore_cache
- *bundle_install
- *yarn_restore_cache
- *yarn_install
- run:
environment:
DATABASE_URL: "postgres://tps_test@localhost:5432/tps_test"
@ -63,6 +83,11 @@ jobs:
- checkout
- *bundle_restore_cache
- *bundle_install
- *yarn_restore_cache
- *yarn_install
- run:
name: Run eslint
command: yarn lint:js
- run:
name: Run rubocop
command: bundle exec rubocop
@ -81,6 +106,8 @@ jobs:
- checkout
- *bundle_restore_cache
- *bundle_install
- *yarn_restore_cache
- *yarn_install
- add_ssh_keys:
fingerprints:
- "0a:67:42:7d:7e:b7:e1:3c:48:8f:bf:68:10:51:a8:44"

18
.eslintrc.js Normal file
View file

@ -0,0 +1,18 @@
module.exports = {
root: true,
parserOptions: {
ecmaVersion: 2017,
sourceType: 'module'
},
globals: {
'$': true
},
plugins: ['prettier'],
extends: ['eslint:recommended', 'prettier'],
env: {
browser: true
},
rules: {
'prettier/prettier': 'error'
}
};

5
.gitignore vendored
View file

@ -37,3 +37,8 @@ coverage/**/*
.env
Procfile.dev
storage/
/public/packs
/public/packs-test
/node_modules
yarn-debug.log*
.yarn-integrity

3
.postcssrc.yml Normal file
View file

@ -0,0 +1,3 @@
plugins:
postcss-import: {}
postcss-cssnext: {}

3
.prettierrc.js Normal file
View file

@ -0,0 +1,3 @@
module.exports = {
singleQuote: true
};

View file

@ -5,6 +5,7 @@ AllCops:
Exclude:
- "db/schema.rb"
- "bin/*"
- "node_modules/**/*"
Bundler/DuplicatedGem:
Enabled: true

View file

@ -105,6 +105,8 @@ gem 'flipflop'
gem 'aasm'
gem 'webpacker', '>= 4.0.x'
# Cron jobs
gem 'delayed_job_active_record'
gem "daemons"

View file

@ -565,6 +565,8 @@ GEM
rack
rack-protection (2.0.3)
rack
rack-proxy (0.6.4)
rack
rack-test (1.0.0)
rack (>= 1.0, < 3)
rails (5.2.0)
@ -780,6 +782,10 @@ GEM
addressable (>= 2.3.6)
crack (>= 0.3.2)
hashdiff
webpacker (4.0.0.pre.pre.2)
activesupport (>= 4.2)
rack-proxy (>= 0.6.1)
railties (>= 4.2)
websocket-driver (0.7.0)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.3)
@ -885,6 +891,7 @@ DEPENDENCIES
warden!
web-console
webmock
webpacker (>= 4.0.x)
xray-rails
BUNDLED WITH

View file

@ -17,6 +17,7 @@ demarches-simplifiees.fr est un site web conçu afin de répondre au besoin urge
- Overmind :
* Mac : `brew install overmind`
* Linux : voir https://github.com/DarthSim/overmind#installation
- Yarn : voir https://yarnpkg.com/en/docs/install
### Tests
@ -31,6 +32,7 @@ demarches-simplifiees.fr est un site web conçu afin de répondre au besoin urge
Afin d'initialiser l'environnement de développement, exécutez la commande suivante :
bundle install
yarn install
## Création de la base de données
@ -111,6 +113,7 @@ Une fois `overmind` lancé, et un breakpoint `byebug` inséré dans le code, il
- Faire tourner Brakeman : `bundle exec brakeman`
- Linter les fichiers HAML : `bundle exec haml-lint app/views/`
- Linter les fichiers SCSS : `bundle exec scss-lint app/assets/stylesheets/`
- Linter les fichiers JavaScript : `yarn lint:js` (`yarn lint:js --fix`)
- [AccessLint](http://accesslint.com/) tourne automatiquement sur les PRs
## Déploiement

View file

@ -10,12 +10,6 @@
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require activestorage
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require highcharts
//= require chartkick
//= require_tree ./old_design
//= require bootstrap-sprockets
@ -31,12 +25,9 @@
//= require bootstrap-wysihtml5
//= require bootstrap-wysihtml5/locales/fr-FR
//= require handlebars
//= require typeahead.bundle
//= require select2
$(document).on('turbolinks:load', application_init);
function application_init(){
tooltip_init();
scroll_to();

View file

@ -1,24 +0,0 @@
// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require ./init
//= require activestorage
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require leaflet.1.1.0
//= require highcharts
//= require chartkick
//= require select2
//= require select2_locale_fr
//= require typeahead.bundle
//= require_tree .

View file

@ -1,3 +0,0 @@
DS.toggleCondidentielExplanation = function(event) {
$(".confidentiel-explanation").toggle();
}

View file

@ -1,11 +0,0 @@
$(document).on("click", "body", function () {
$(".button.dropdown").removeClass("open");
});
$(document).on("click", ".button.dropdown", function(event) {
event.stopPropagation();
var $target = $(event.target);
if($target.hasClass("button", "dropdown")){
$target.toggleClass("open");
}
});

View file

@ -1,10 +0,0 @@
function drawCadastre (map) {
drawLayerWithItems(map, dossierCadastres, {
fillColor: '#8A6D3B',
weight: 2,
opacity: 0.7,
color: '#8A6D3B',
dashArray: '3',
fillOpacity: 0.5
});
}

View file

@ -1,61 +0,0 @@
function initCarto() {
if ($("#map").length > 0) {
var position = getPosition() || defaultGestionnairePosition();
var map = L.map('map', {
scrollWheelZoom: false
}).setView([position.lat, position.lon], position.zoom);
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
// draw external polygons
drawCadastre(map);
drawQuartiersPrioritaires(map);
// draw user polygon
drawUserSelection(map);
}
}
$(document).on('turbolinks:load', initCarto);
function drawUserSelection(map) {
if (dossierJsonLatLngs.length > 0) {
var polygon = L.polygon(dossierJsonLatLngs, { color: 'red', zIndex: 3 }).addTo(map);
map.fitBounds(polygon.getBounds());
}
}
function defaultGestionnairePosition() {
var LON = '2.428462';
var LAT = '46.538192';
return { lon: LON, lat: LAT, zoom: 5 }
}
function getPosition() {
var position;
$.ajax({
url: getPositionUrl,
dataType: 'json',
async: false
}).done(function (data) {
position = data
});
return position;
}
function drawLayerWithItems(map, items, style) {
if (Array.isArray(items) && items.length > 0) {
var layer = new L.GeoJSON();
items.forEach(function (item) {
layer.addData(item.geometry);
});
layer.setStyle(style).addTo(map);
}
}

View file

@ -1,10 +0,0 @@
function drawQuartiersPrioritaires (map) {
drawLayerWithItems(map, dossierQuartiersPrioritaires, {
fillColor: '#31708F',
weight: 2,
opacity: 0.7,
color: '#31708F',
dashArray: '3',
fillOpacity: 0.5
});
}

View file

@ -1,27 +0,0 @@
(function () {
var display = 'label';
var bloodhound = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace(display),
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: '/ban/search?request=%QUERY',
wildcard: '%QUERY'
}
});
bloodhound.initialize();
var bindTypeahead = function() {
$("input[data-address='true']").typeahead({
minLength: 1
}, {
display: display,
source: bloodhound,
limit: 5
});
};
document.addEventListener('turbolinks:load', bindTypeahead);
})();

View file

@ -1,36 +0,0 @@
(function() {
var showNotFound = function() {
$('.dossier-link .text-info').hide();
$('.dossier-link .text-warning').show();
};
var showData = function(data) {
$('.dossier-link .dossier-text-summary').text(data.textSummary);
$('.dossier-link .text-info').show();
$('.dossier-link .text-warning').hide();
};
var hideEverything = function() {
$('.dossier-link .text-info').hide();
$('.dossier-link .text-warning').hide();
};
var fetchProcedureLibelle = function(e) {
var dossierId = $(e.target).val();
if(dossierId) {
$.get('/users/dossiers/' + dossierId + '/text_summary')
.done(showData)
.fail(showNotFound);
} else {
hideEverything();
}
};
var timeOut = null;
var debounceFetchProcedureLibelle = function(e) {
if(timeOut){ clearTimeout(timeOut); }
timeOut = setTimeout(function() { fetchProcedureLibelle(e); }, 300);
};
$(document).on('input', '[data-type=dossier-link]', debounceFetchProcedureLibelle);
})();

View file

@ -1,30 +0,0 @@
document.addEventListener('turbolinks:load', function() {
var primaries, i;
primaries = document.querySelectorAll('select[data-secondary-options]');
for (i = 0; i < primaries.length; i++) {
primaries[i].addEventListener('change', function(e) {
var option, options, element, primary, secondary, secondaryOptions;
primary = e.target;
secondary = document.querySelector('select[data-secondary-id="' + primary.dataset.primaryId + '"]');
secondaryOptions = JSON.parse(primary.dataset.secondaryOptions);
while ((option = secondary.firstChild)) {
secondary.removeChild(option);
}
options = secondaryOptions[e.target.value];
for (i = 0; i < options.length; i++) {
option = options[i];
element = document.createElement("option");
element.textContent = option;
element.value = option;
secondary.appendChild(element);
}
secondary.selectedIndex = 0;
});
}
});

View file

@ -1,13 +0,0 @@
document.addEventListener('turbolinks:load', function() {
$('select.select2').select2({
'language': 'fr',
'width': '100%'
});
$('select.select2-limited').select2({
'language': 'fr',
'placeholder': 'Sélectionnez des colonnes',
'maximumSelectionLength': '5',
'width': '300px'
});
});

View file

@ -1,30 +0,0 @@
document.addEventListener('turbolinks:load', function() {
$('[data-siret]').on('input', function(evt) {
var input = $(evt.target);
var value = input.val();
var url = input.attr('data-siret');
switch (value.length) {
case 0:
$.get(url+'?siret=blank');
break;
case 14:
input.attr('disabled', 'disabled');
$('.spinner').show();
$.get(url+'?siret='+value).then(function() {
input.removeAttr('data-invalid');
input.removeAttr('disabled');
$('.spinner').hide();
}, function() {
input.removeAttr('disabled');
input.attr('data-invalid', true);
$('.spinner').hide();
});
break;
default:
if (!input.attr('data-invalid')) {
input.attr('data-invalid', true);
$.get(url+'?siret=invalid');
}
}
});
});

View file

@ -1,69 +0,0 @@
addEventListener("direct-upload:initialize", function (event) {
var target = event.target,
detail = event.detail,
id = detail.id,
file = detail.file;
target.insertAdjacentHTML("beforebegin", "\n<div id=\"direct-upload-" +
id +
"\" class=\"direct-upload direct-upload--pending\">\n<div id=\"direct-upload-progress-" +
id + "\" class=\"direct-upload__progress\" style=\"width: 0%\"></div>\n<span class=\"direct-upload__filename\">" +
file.name +
"</span>\n</div>\n");
});
addEventListener("direct-upload:start", function (event) {
var id = event.detail.id,
element = document.getElementById("direct-upload-" + id);
element.classList.remove("direct-upload--pending");
});
addEventListener("direct-upload:progress", function (event) {
var id = event.detail.id,
progress = event.detail.progress,
progressElement = document.getElementById("direct-upload-progress-" + id);
progressElement.style.width = progress + "%";
});
addEventListener("direct-upload:error", function (event) {
event.preventDefault();
var id = event.detail.id,
error = event.detail.error,
element = document.getElementById("direct-upload-" + id);
element.classList.add("direct-upload--error");
element.setAttribute("title", error);
});
addEventListener("direct-upload:end", function (event) {
var id = event.detail.id,
element = document.getElementById("direct-upload-" + id);
element.classList.add("direct-upload--complete");
});
addEventListener('turbolinks:load', function() {
var submitButtons = document.querySelectorAll('form button[type=submit][data-action]');
var hiddenInput = document.querySelector('form input[type=hidden][name=submit_action]');
submitButtons = [].slice.call(submitButtons);
submitButtons.forEach(function(button) {
button.addEventListener('click', function() {
// Active Storage will intercept the form.submit event to upload
// the attached files, and then fire the submit action again but forgetting
// which button was clicked. So we manually set the type of action that trigerred
// the form submission.
var action = button.getAttribute('data-action');
hiddenInput.value = action;
// Some form fields are marked as mandatory, but when saving a draft we don't want them
// to be enforced by the browser.
if (action === 'submit') {
button.form.removeAttribute('novalidate');
} else {
button.form.setAttribute('novalidate', 'novalidate');
}
});
});
});

View file

@ -1,8 +0,0 @@
$(document).on("click", "body", function () {
$(".print-menu").removeClass("open fade-in-down");
});
DS.togglePrintMenu = function(event) {
event.stopPropagation();
$(".print-menu").toggleClass("open fade-in-down");
}

View file

@ -1,8 +0,0 @@
$(document).on("click", "body", function () {
$(".header-menu").removeClass("open fade-in-down");
});
DS.toggleHeaderMenu = function(event) {
event.stopPropagation();
$(".header-menu").toggleClass("open fade-in-down");
}

View file

@ -1,2 +0,0 @@
// namespace
window.DS = window.DS || {};

View file

@ -1,24 +0,0 @@
DS.scrollMessagerie = function () {
var scrollTo = function ($container, $scrollTo) {
$container.scrollTop(
$scrollTo.offset().top - $container.offset().top + $container.scrollTop()
);
}
var scrollToBottom = function ($container) {
$container.scrollTop($container.prop('scrollHeight'));
}
var $ul = $(".messagerie ul").first();
if($ul.length) {
var $elementToScroll = $('.date.highlighted').first();
if ($elementToScroll.length != 0) {
scrollTo($ul, $elementToScroll);
} else {
scrollToBottom($ul);
}
}
};
document.addEventListener("turbolinks:load", DS.scrollMessagerie);

View file

@ -1,9 +0,0 @@
DS.showMotivation = function (state) {
$(".motivation." + state).show();
$(".dropdown-items").hide();
};
DS.motivationCancel = function () {
$(".motivation").hide();
$(".dropdown-items").show();
};

View file

@ -1,18 +0,0 @@
DS.toggleChart = function(event, chartClass) {
var nextSelectorItem = $(event.target),
nextChart = $(chartClass),
nextChartId = nextChart.children().first().attr("id"),
currentSelectorItem = nextSelectorItem.parent().find(".segmented-control-item-active"),
currentChart = nextSelectorItem.parent().parent().find(".chart:not(.hidden)");
// Change the current selector and the next selector states
currentSelectorItem.toggleClass("segmented-control-item-active");
nextSelectorItem.toggleClass("segmented-control-item-active");
// Hide the currently shown chart and show the new one
currentChart.toggleClass("hidden");
nextChart.toggleClass("hidden");
// Reflow needed, see https://github.com/highcharts/highcharts/issues/1979
Chartkick.charts[nextChartId].getChartObject().reflow();
}

View file

@ -1,57 +0,0 @@
addEventListener("direct-upload:initialize", function (event) {
var target = event.target,
detail = event.detail,
id = detail.id,
file = detail.file;
target.insertAdjacentHTML("beforebegin", "\n<div id=\"direct-upload-" +
id +
"\" class=\"direct-upload direct-upload--pending\">\n<div id=\"direct-upload-progress-" +
id + "\" class=\"direct-upload__progress\" style=\"width: 0%\"></div>\n<span class=\"direct-upload__filename\">" +
file.name +
"</span>\n</div>\n");
});
addEventListener("direct-upload:start", function (event) {
var id = event.detail.id,
element = document.getElementById("direct-upload-" + id);
element.classList.remove("direct-upload--pending");
});
addEventListener("direct-upload:progress", function (event) {
var id = event.detail.id,
progress = event.detail.progress,
progressElement = document.getElementById("direct-upload-progress-" + id);
progressElement.style.width = progress + "%";
});
addEventListener("direct-upload:error", function (event) {
event.preventDefault();
var id = event.detail.id,
error = event.detail.error,
element = document.getElementById("direct-upload-" + id);
element.classList.add("direct-upload--error");
element.setAttribute("title", error);
});
addEventListener("direct-upload:end", function (event) {
var id = event.detail.id,
element = document.getElementById("direct-upload-" + id);
element.classList.add("direct-upload--complete");
});
addEventListener('load', function() {
var submitButtons = document.querySelectorAll('form button[type=submit][data-action]');
var hiddenInput = document.querySelector('form input[type=hidden][name=submit_action]');
submitButtons = [].slice.call(submitButtons);
submitButtons.forEach(function(button) {
button.addEventListener('click', function() {
hiddenInput.value = button.getAttribute('data-action');
});
});
});

View file

@ -5,7 +5,7 @@ $(document)
function the_terms() {
var the_terms = $("#dossier_autorisation_donnees");
if (the_terms.size() == 0)
if (the_terms.length == 0)
return;
check_value(the_terms);

View file

@ -0,0 +1,3 @@
export function toggleCondidentielExplanation() {
$('.confidentiel-explanation').toggle();
}

View file

@ -0,0 +1,11 @@
$(document).on('click', 'body', () => {
$('.button.dropdown').removeClass('open');
});
$(document).on('click', '.button.dropdown', event => {
event.stopPropagation();
const $target = $(event.target);
if ($target.hasClass('button', 'dropdown')) {
$target.toggleClass('open');
}
});

View file

@ -0,0 +1,44 @@
import L from 'leaflet';
import { getData } from '../shared/data';
import {
drawCadastre,
drawQuartiersPrioritaires,
drawUserSelection
} from './carto/draw';
const LON = '2.428462';
const LAT = '46.538192';
const DEFAULT_POSITION = { lon: LON, lat: LAT, zoom: 5 };
function initialize() {
if ($('#map').length > 0) {
$.getJSON(getData('carto').getPositionUrl).then(
position => initializeWithPosition(position),
() => initializeWithPosition(DEFAULT_POSITION)
);
}
}
addEventListener('turbolinks:load', initialize);
function initializeWithPosition(position) {
const map = L.map('map', {
scrollWheelZoom: false
}).setView([position.lat, position.lon], position.zoom);
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution:
'&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
const data = getData('carto');
// draw external polygons
drawCadastre(map, data);
drawQuartiersPrioritaires(map, data);
// draw user polygon
drawUserSelection(map, data);
}

View file

@ -0,0 +1,48 @@
import L from 'leaflet';
function drawLayerWithItems(map, items, style) {
if (Array.isArray(items) && items.length > 0) {
const layer = new L.GeoJSON();
items.forEach(function(item) {
layer.addData(item.geometry);
});
layer.setStyle(style).addTo(map);
}
}
export function drawCadastre(map, { dossierCadastres }) {
drawLayerWithItems(map, dossierCadastres, {
fillColor: '#8A6D3B',
weight: 2,
opacity: 0.7,
color: '#8A6D3B',
dashArray: '3',
fillOpacity: 0.5
});
}
export function drawQuartiersPrioritaires(
map,
{ dossierQuartiersPrioritaires }
) {
drawLayerWithItems(map, dossierQuartiersPrioritaires, {
fillColor: '#31708F',
weight: 2,
opacity: 0.7,
color: '#31708F',
dashArray: '3',
fillOpacity: 0.5
});
}
export function drawUserSelection(map, { dossierJsonLatLngs }) {
if (dossierJsonLatLngs.length > 0) {
const polygon = L.polygon(dossierJsonLatLngs, {
color: 'red',
zIndex: 3
}).addTo(map);
map.fitBounds(polygon.getBounds());
}
}

View file

@ -0,0 +1,30 @@
import Bloodhound from 'bloodhound-js';
const display = 'label';
const bloodhound = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace(display),
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: '/ban/search?request=%QUERY',
wildcard: '%QUERY'
}
});
bloodhound.initialize();
function bindTypeahead() {
$('input[data-address="true"]').typeahead(
{
minLength: 1
},
{
display: display,
source: bloodhound,
limit: 5
}
);
}
addEventListener('turbolinks:load', bindTypeahead);

View file

@ -0,0 +1,40 @@
function showNotFound() {
$('.dossier-link .text-info').hide();
$('.dossier-link .text-warning').show();
}
function showData(data) {
$('.dossier-link .dossier-text-summary').text(data.textSummary);
$('.dossier-link .text-info').show();
$('.dossier-link .text-warning').hide();
}
function hideEverything() {
$('.dossier-link .text-info').hide();
$('.dossier-link .text-warning').hide();
}
function fetchProcedureLibelle(e) {
const dossierId = $(e.target).val();
if (dossierId) {
$.get(`/users/dossiers/${dossierId}/text_summary`)
.done(showData)
.fail(showNotFound);
} else {
hideEverything();
}
}
let timeOut;
function debounceFetchProcedureLibelle(e) {
if (timeOut) {
clearTimeout(timeOut);
}
timeOut = setTimeout(() => fetchProcedureLibelle(e), 300);
}
$(document).on(
'input',
'[data-type=dossier-link]',
debounceFetchProcedureLibelle
);

View file

@ -0,0 +1,29 @@
addEventListener('turbolinks:load', () => {
const primaries = document.querySelectorAll('select[data-secondary-options]');
for (let primary of primaries) {
let secondary = document.querySelector(
`select[data-secondary-id="${primary.dataset.primaryId}"]`
);
let secondaryOptions = JSON.parse(primary.dataset.secondaryOptions);
primary.addEventListener('change', e => {
let option, options, element;
while ((option = secondary.firstChild)) {
secondary.removeChild(option);
}
options = secondaryOptions[e.target.value];
for (let option of options) {
element = document.createElement('option');
element.textContent = option;
element.value = option;
secondary.appendChild(element);
}
secondary.selectedIndex = 0;
});
}
});

View file

@ -0,0 +1,13 @@
addEventListener('turbolinks:load', () => {
$('select.select2').select2({
language: 'fr',
width: '100%'
});
$('select.select2-limited').select2({
language: 'fr',
placeholder: 'Sélectionnez des colonnes',
maximumSelectionLength: '5',
width: '300px'
});
});

View file

@ -0,0 +1,34 @@
addEventListener('turbolinks:load', () => {
$('[data-siret]').on('input', evt => {
const input = $(evt.target);
const value = input.val();
const url = input.attr('data-siret');
switch (value.length) {
case 0:
$.get(`${url}?siret=blank`);
break;
case 14:
input.attr('disabled', 'disabled');
$('.spinner').show();
$.get(`${url}?siret=${value}`).then(
() => {
input.removeAttr('data-invalid');
input.removeAttr('disabled');
$('.spinner').hide();
},
() => {
input.removeAttr('disabled');
input.attr('data-invalid', true);
$('.spinner').hide();
}
);
break;
default:
if (!input.attr('data-invalid')) {
input.attr('data-invalid', true);
$.get(`${url}?siret=invalid`);
}
}
});
});

View file

@ -0,0 +1,8 @@
$(document).on('click', 'body', () => {
$('.print-menu').removeClass('open fade-in-down');
});
export function togglePrintMenu(event) {
event.stopPropagation();
$('.print-menu').toggleClass('open fade-in-down');
}

View file

@ -1,8 +1,8 @@
$(document).on('blur keydown', 'input, textarea', function() {
$(document).on('blur keydown', 'input, textarea', () => {
$(this).addClass('touched');
});
$(document).on('click', 'input[type="submit"]:not([formnovalidate])', function() {
var $form = $(this).closest('form');
$(document).on('click', 'input[type="submit"]:not([formnovalidate])', () => {
const $form = $(this).closest('form');
$('input, textarea', $form).addClass('touched');
});

View file

@ -0,0 +1,8 @@
$(document).on('click', 'body', () => {
$('.header-menu').removeClass('open fade-in-down');
});
export function toggleHeaderMenu(event) {
event.stopPropagation();
$('.header-menu').toggleClass('open fade-in-down');
}

View file

@ -0,0 +1,25 @@
export function scrollMessagerie() {
const $ul = $('.messagerie ul').first();
if ($ul.length) {
const $elementToScroll = $('.date.highlighted').first();
if ($elementToScroll.length != 0) {
scrollTo($ul, $elementToScroll);
} else {
scrollToBottom($ul);
}
}
}
function scrollTo($container, $scrollTo) {
$container.scrollTop(
$scrollTo.offset().top - $container.offset().top + $container.scrollTop()
);
}
function scrollToBottom($container) {
$container.scrollTop($container.prop('scrollHeight'));
}
addEventListener('turbolinks:load', scrollMessagerie);

View file

@ -0,0 +1,9 @@
export function showMotivation(state) {
$(`.motivation.${state}`).show();
$('.dropdown-items').hide();
}
export function motivationCancel() {
$('.motivation').hide();
$('.dropdown-items').show();
}

View file

@ -0,0 +1,28 @@
import Chartkick from 'chartkick';
export function toggleChart(event, chartClass) {
const nextSelectorItem = $(event.target),
nextChart = $(chartClass),
nextChartId = nextChart
.children()
.first()
.attr('id'),
currentSelectorItem = nextSelectorItem
.parent()
.find('.segmented-control-item-active'),
currentChart = nextSelectorItem
.parent()
.parent()
.find('.chart:not(.hidden)');
// Change the current selector and the next selector states
currentSelectorItem.toggleClass('segmented-control-item-active');
nextSelectorItem.toggleClass('segmented-control-item-active');
// Hide the currently shown chart and show the new one
currentChart.toggleClass('hidden');
nextChart.toggleClass('hidden');
// Reflow needed, see https://github.com/highcharts/highcharts/issues/1979
Chartkick.charts[nextChartId].getChartObject().reflow();
}

View file

@ -0,0 +1,25 @@
import Turbolinks from 'turbolinks';
import Rails from 'rails-ujs';
import * as ActiveStorage from 'activestorage';
import Chartkick from 'chartkick';
import Highcharts from 'highcharts';
import Bloodhound from 'bloodhound-js';
import jQuery from 'jquery';
import 'select2';
import 'typeahead.js';
import '../shared/rails-ujs-fix';
import '../shared/direct-uploads';
// Start Rails helpers
Chartkick.addAdapter(Highcharts);
Rails.start();
Turbolinks.start();
ActiveStorage.start();
// Expose globals
window.Bloodhound = Bloodhound;
window.Chartkick = Chartkick;
window.$ = jQuery;
window.jQuery = jQuery;

View file

@ -0,0 +1,50 @@
import Turbolinks from 'turbolinks';
import Rails from 'rails-ujs';
import * as ActiveStorage from 'activestorage';
import Chartkick from 'chartkick';
import Highcharts from 'highcharts';
import 'select2';
import 'typeahead.js';
import '../shared/rails-ujs-fix';
import '../shared/direct-uploads';
import '../new_design/buttons';
import '../new_design/form-validation';
import '../new_design/carto';
import '../new_design/champs/address';
import '../new_design/champs/dossier-link';
import '../new_design/champs/linked-drop-down-list';
import '../new_design/champs/multiple-drop-down-list';
import '../new_design/champs/siret';
import { toggleCondidentielExplanation } from '../new_design/avis';
import { togglePrintMenu } from '../new_design/dossier';
import { toggleHeaderMenu } from '../new_design/header';
import { scrollMessagerie } from '../new_design/messagerie';
import { showMotivation, motivationCancel } from '../new_design/state-button';
import { toggleChart } from '../new_design/toggle-chart';
// This is the global application namespace where we expose helpers used from rails views
const DS = {
toggleCondidentielExplanation,
togglePrintMenu,
toggleHeaderMenu,
scrollMessagerie,
showMotivation,
motivationCancel,
toggleChart
};
// Start Rails helpers
Chartkick.addAdapter(Highcharts);
Rails.start();
Turbolinks.start();
ActiveStorage.start();
// Expose globals
window.DS = window.DS || DS;
window.Chartkick = Chartkick;

View file

@ -0,0 +1,13 @@
const _DATA = {};
function setupData() {
if (window.DATA.length) {
Object.assign(_DATA, ...window.DATA);
window.DATA.length = 0;
}
}
export function getData(namespace) {
setupData();
return namespace ? _DATA[namespace] : _DATA;
}

View file

@ -0,0 +1,76 @@
addEventListener('direct-upload:initialize', event => {
const target = event.target,
detail = event.detail,
id = detail.id,
file = detail.file;
target.insertAdjacentHTML(
'beforebegin',
'\n<div id="direct-upload-' +
id +
'" class="direct-upload direct-upload--pending">\n<div id="direct-upload-progress-' +
id +
'" class="direct-upload__progress" style="width: 0%"></div>\n<span class="direct-upload__filename">' +
file.name +
'</span>\n</div>\n'
);
});
addEventListener('direct-upload:start', event => {
const id = event.detail.id,
element = document.getElementById('direct-upload-' + id);
element.classList.remove('direct-upload--pending');
});
addEventListener('direct-upload:progress', event => {
const id = event.detail.id,
progress = event.detail.progress,
progressElement = document.getElementById('direct-upload-progress-' + id);
progressElement.style.width = `${progress} %`;
});
addEventListener('direct-upload:error', event => {
event.preventDefault();
const id = event.detail.id,
error = event.detail.error,
element = document.getElementById('direct-upload-' + id);
element.classList.add('direct-upload--error');
element.setAttribute('title', error);
});
addEventListener('direct-upload:end', event => {
const id = event.detail.id,
element = document.getElementById('direct-upload-' + id);
element.classList.add('direct-upload--complete');
});
addEventListener('turbolinks:load', () => {
const submitButtons = document.querySelectorAll(
'form button[type=submit][data-action]'
);
const hiddenInput = document.querySelector(
'form input[type=hidden][name=submit_action]'
);
for (let button of submitButtons) {
button.addEventListener('click', () => {
// Active Storage will intercept the form.submit event to upload
// the attached files, and then fire the submit action again but forgetting
// which button was clicked. So we manually set the type of action that trigerred
// the form submission.
const action = button.getAttribute('data-action');
hiddenInput.value = action;
// Some form fields are marked as mandatory, but when saving a draft we don't want them
// to be enforced by the browser.
if (action === 'submit') {
button.form.removeAttribute('novalidate');
} else {
button.form.setAttribute('novalidate', 'novalidate');
}
});
}
});

View file

@ -0,0 +1,26 @@
import Rails from 'rails-ujs';
import jQuery from 'jquery';
// We use `jQuery.active` in our capybara suit to wait for ajax requests.
// Newer jQuery-less version of rails-ujs is breaking it.
// We have to set `ajax:complete` listener on the same element as the one
// we catch ajax:send on as by the end of the request
// the old element may be removed from DOM.
Rails.delegate(document, '[data-remote]', 'ajax:send', ({ target }) => {
let callback = () => {
jQuery.active--;
target.removeEventListener('ajax:complete', callback);
};
target.addEventListener('ajax:complete', callback);
jQuery.active++;
});
// `smart_listing` gem is overriding `$.rails.href` method. When using newer
// jQuery-less version of rails-ujs it breaks.
// https://github.com/Sology/smart_listing/blob/master/app/assets/javascripts/smart_listing.coffee.erb#L9
addEventListener('load', () => {
const { href } = $.rails;
$.rails.href = function(element) {
return element.href || href(element);
};
});

View file

@ -10,11 +10,15 @@
= favicon_link_tag(image_url("favicons/32x32.png"), type: "image/png", sizes: "32x32")
= favicon_link_tag(image_url("favicons/96x96.png"), type: "image/png", sizes: "96x96")
= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => "reload"
= stylesheet_link_tag 'print', media: 'print', 'data-turbolinks-track' => "reload"
= javascript_include_tag 'application', 'data-turbolinks-track' => true
= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': "reload"
= stylesheet_link_tag 'print', media: 'print', 'data-turbolinks-track': "reload"
= javascript_pack_tag 'application-old', defer: true, 'data-turbolinks-track': 'reload'
= javascript_include_tag 'application', defer: true, 'data-turbolinks-track': 'reload'
= csrf_meta_tags
:javascript
DATA = [];
%body
= render partial: 'layouts/support_navigator_banner'
= render partial: 'layouts/pre_maintenance'

View file

@ -14,12 +14,15 @@
= favicon_link_tag(image_url("favicons/32x32.png"), type: "image/png", sizes: "32x32")
= favicon_link_tag(image_url("favicons/96x96.png"), type: "image/png", sizes: "96x96")
= stylesheet_link_tag "new_design/new_application", media: "all", "data-turbolinks-track": "reload"
= stylesheet_link_tag "new_design/print", media: "print", "data-turbolinks-track": true
= javascript_pack_tag 'application', defer: true, 'data-turbolinks-track': 'reload'
= stylesheet_link_tag 'new_design/new_application', media: 'all', 'data-turbolinks-track': 'reload'
= stylesheet_link_tag 'new_design/print', media: 'print', 'data-turbolinks-track': 'reload'
- if Rails.env.development?
= stylesheet_link_tag :xray
:javascript
DATA = [];
%body
.page-wrapper
= render partial: "layouts/support_navigator_banner"
@ -37,8 +40,6 @@
= content_for(:footer)
= render partial: "layouts/mailjet_newsletter"
= javascript_include_tag "new_design/application", "data-turbolinks-eval": false
- if Rails.env.development?
= javascript_include_tag :xray

View file

@ -17,7 +17,11 @@
= "Parcelle n° #{p.numero} - Feuille #{p.code_arr} #{p.section} #{p.feuille}"
:javascript
var getPositionUrl = "#{position_gestionnaire_dossier_path(dossier.procedure, dossier)}";
var dossierJsonLatLngs = #{raw(ensure_safe_json(dossier.json_latlngs))};
var dossierCadastres = #{raw(ensure_safe_json(dossier.cadastres.to_json))};
var dossierQuartiersPrioritaires = #{raw(ensure_safe_json(dossier.quartier_prioritaires.to_json))};
DATA.push({
carto: {
getPositionUrl: "#{position_gestionnaire_dossier_path(dossier.procedure, dossier)}",
dossierJsonLatLngs: #{raw(ensure_safe_json(dossier.json_latlngs))},
dossierCadastres: #{raw(ensure_safe_json(dossier.cadastres.to_json))},
dossierQuartiersPrioritaires: #{raw(ensure_safe_json(dossier.quartier_prioritaires.to_json))}
}
});

View file

@ -1,3 +1,5 @@
%script{ type: 'text/javascript' }
= "var dossier_id =#{dossier.id}"
$(document).on('turbolinks:load', initCarto);
:javascript
var dossier_id = #{dossier.id};
addEventListener('turbolinks:load', function() {
initCarto();
});

15
bin/webpack Executable file
View file

@ -0,0 +1,15 @@
#!/usr/bin/env ruby
ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
ENV["NODE_ENV"] ||= ENV["NODE_ENV"] || "development"
require "pathname"
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
Pathname.new(__FILE__).realpath)
require "rubygems"
require "bundler/setup"
require "webpacker"
require "webpacker/webpack_runner"
Webpacker::WebpackRunner.run(ARGV)

15
bin/webpack-dev-server Executable file
View file

@ -0,0 +1,15 @@
#!/usr/bin/env ruby
ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
ENV["NODE_ENV"] ||= ENV["NODE_ENV"] || "development"
require "pathname"
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
Pathname.new(__FILE__).realpath)
require "rubygems"
require "bundler/setup"
require "webpacker"
require "webpacker/dev_server_runner"
Webpacker::DevServerRunner.run(ARGV)

View file

@ -1,6 +1,9 @@
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
# Verifies that versions and hashed value of the package contents in the project's package.json
config.webpacker.check_yarn_integrity = true
# In the development environment your application's code is reloaded on
# every request. This slows down response time but is perfect for development
# since you don't have to restart the web server when you make code changes.

View file

@ -1,6 +1,9 @@
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
# Verifies that versions and hashed value of the package contents in the project's package.json
config.webpacker.check_yarn_integrity = false
# Code is not reloaded between requests.
config.cache_classes = true

View file

@ -0,0 +1,5 @@
process.env.NODE_ENV = process.env.NODE_ENV || 'development'
const environment = require('./environment')
module.exports = environment.toWebpackConfig()

View file

@ -0,0 +1,9 @@
const { environment } = require('@rails/webpacker')
const webpack = require('webpack');
environment.plugins.append('Provide', new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery'
}));
module.exports = environment

View file

@ -0,0 +1,5 @@
process.env.NODE_ENV = process.env.NODE_ENV || 'production'
const environment = require('./environment')
module.exports = environment.toWebpackConfig()

5
config/webpack/test.js Normal file
View file

@ -0,0 +1,5 @@
process.env.NODE_ENV = process.env.NODE_ENV || 'development'
const environment = require('./environment')
module.exports = environment.toWebpackConfig()

77
config/webpacker.yml Normal file
View file

@ -0,0 +1,77 @@
# Note: You must restart bin/webpack-dev-server for changes to take effect
default: &default
source_path: app/javascript
source_entry_path: packs
public_output_path: packs
cache_path: tmp/cache/webpacker
# Additional paths webpack should lookup modules
# ['app/assets', 'engine/foo/app/assets']
resolved_paths: []
# Reload manifest.json on all requests so we reload latest compiled packs
cache_manifest: false
extensions:
- .js
- .sass
- .scss
- .css
- .module.sass
- .module.scss
- .module.css
- .png
- .svg
- .gif
- .jpeg
- .jpg
development:
<<: *default
compile: true
# Reference: https://webpack.js.org/configuration/dev-server/
dev_server:
https: false
host: localhost
port: 3035
public: localhost:3035
hmr: false
# Inline should be set to true if using HMR
inline: true
overlay: true
compress: true
disable_host_check: true
use_local_ip: false
quiet: false
headers:
'Access-Control-Allow-Origin': '*'
watch_options:
ignored: /node_modules/
test:
<<: *default
compile: true
# Compile test packs to a separate directory
public_output_path: packs-test
staging:
<<: *default
# Production depends on precompilation of packs prior to booting for performance.
compile: false
# Cache manifest.json for performance
cache_manifest: true
production:
<<: *default
# Production depends on precompilation of packs prior to booting for performance.
compile: false
# Cache manifest.json for performance
cache_manifest: true

28
package.json Normal file
View file

@ -0,0 +1,28 @@
{
"dependencies": {
"@rails/webpacker": "4.0.0-pre.2",
"activestorage": "^5.2.0",
"bloodhound-js": "^1.2.2",
"chartkick": "^2.3.6",
"highcharts": "^6.1.1",
"jquery": "^3.3.1",
"leaflet": "^1.3.1",
"rails-ujs": "^5.2.0",
"select2": "^4.0.6-rc.1",
"turbolinks": "^5.1.1",
"typeahead.js": "^0.11.1"
},
"devDependencies": {
"eslint": "^5.2.0",
"eslint-config-prettier": "^2.9.0",
"eslint-plugin-prettier": "^2.6.2",
"prettier": "^1.13.7",
"webpack-dev-server": "^3.1.4"
},
"scripts": {
"lint:js": "eslint ./app/javascript"
},
"engines": {
"node": "6.* || 8.* || >= 10.*"
}
}

View file

@ -39,7 +39,7 @@ end
Capybara.register_driver :headless_chrome do |app|
capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
chromeOptions: { args: %w(headless disable-gpu window-size=2560,1600) }
chromeOptions: { args: %w(headless disable-gpu disable-dev-shm-usage disable-software-rasterizer mute-audio window-size=1440,900) }
)
Capybara::Selenium::Driver.new app,

View file

@ -13,7 +13,7 @@ describe 'new_gestionnaire/dossiers/_map.html.haml', type: :view do
before { subject }
it { expect(rendered).to have_content('var dossierJsonLatLngs = [[{"lat":50.659255436656736,"lng":3.080635070800781},{"lat":50.659255436656736,"lng":3.079690933227539},{"lat":50.659962770886516,"lng":3.0800342559814453},{"lat":50.659962770886516,"lng":3.0811500549316406},{"lat":50.659255436656736,"lng":3.080635070800781}]];') }
it { expect(rendered).to have_content('dossierJsonLatLngs: [[{"lat":50.659255436656736,"lng":3.080635070800781},{"lat":50.659255436656736,"lng":3.079690933227539},{"lat":50.659962770886516,"lng":3.0800342559814453},{"lat":50.659962770886516,"lng":3.0811500549316406},{"lat":50.659255436656736,"lng":3.080635070800781}]],') }
end
context 'without a correct json' do
@ -21,7 +21,7 @@ describe 'new_gestionnaire/dossiers/_map.html.haml', type: :view do
before { subject }
it { expect(rendered).to have_content('var dossierJsonLatLngs = {};') }
it { expect(rendered).to have_content('dossierJsonLatLngs: {},') }
end
end
end

7730
yarn.lock Normal file

File diff suppressed because it is too large Load diff