Merge branch 'dev'

This commit is contained in:
gregoirenovel 2018-08-01 15:41:11 +02:00
commit 9a4c1360ab
108 changed files with 9038 additions and 549 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

19
.eslintrc.js Normal file
View file

@ -0,0 +1,19 @@
module.exports = {
root: true,
parserOptions: {
ecmaVersion: 2017,
sourceType: 'module'
},
globals: {
'$': true,
'process': 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

@ -19,7 +19,7 @@ GEM
remote: https://rubygems.org/
specs:
CFPropertyList (2.3.6)
aasm (4.12.3)
aasm (5.0.0)
concurrent-ruby (~> 1.0)
actioncable (5.2.0)
actionpack (= 5.2.0)
@ -88,7 +88,7 @@ GEM
sass-rails (~> 5.0)
selectize-rails (~> 0.6)
aes_key_wrap (1.0.1)
apipie-rails (0.5.9)
apipie-rails (0.5.10)
rails (>= 4.1)
arel (9.0.0)
ast (2.4.0)
@ -565,7 +565,9 @@ GEM
rack
rack-protection (2.0.3)
rack
rack-test (1.0.0)
rack-proxy (0.6.4)
rack
rack-test (1.1.0)
rack (>= 1.0, < 3)
rails (5.2.0)
actioncable (= 5.2.0)
@ -653,7 +655,7 @@ GEM
rspec-support (3.7.1)
rspec_junit_formatter (0.4.1)
rspec-core (>= 2, < 4, != 2.12.0)
rubocop (0.58.1)
rubocop (0.58.2)
jaro_winkler (~> 1.5.1)
parallel (~> 1.10)
parser (>= 2.5, != 2.5.1.1)
@ -752,13 +754,13 @@ GEM
ethon (>= 0.9.0)
tzinfo (1.2.5)
thread_safe (~> 0.1)
uglifier (4.1.15)
uglifier (4.1.17)
execjs (>= 0.3.0, < 3)
unf (0.1.4)
unf_ext
unf_ext (0.0.7.5)
unicode-display_width (1.4.0)
unicorn (5.4.0)
unicorn (5.4.1)
kgio (~> 2.6)
raindrops (~> 0.7)
validate_email (0.1.6)
@ -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

@ -150,6 +150,7 @@
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
position: absolute;
right: 0;
text-align: left;
top: 5 * $default-spacer;
cursor: default;
z-index: 10;

View file

@ -64,10 +64,6 @@
visibility: visible;
}
.etablissement-titre {
margin-bottom: 2 * $default-padding;
}
// Align checkboxes on the top-left side of the label
&.editable-champ-checkbox,
&.editable-champ-engagement {

View file

@ -14,7 +14,7 @@
.right-spinner {
display: none;
position: absolute;
bottom: 1.3em;
top: 3.7em;
right: 1.2em;
transform: scale(0.3);
}

View file

@ -12,14 +12,16 @@ class Champs::SiretController < ApplicationController
if @etablissement
@etablissement.mark_for_destruction
end
@error = "SIRET invalide"
@error = "Le numéro de SIRET doit comporter exactement 14 chiffres."
else
etablissement_attributes = ApiEntrepriseService.get_etablissement_params_for_siret(siret, @champ.dossier.procedure_id)
if etablissement_attributes.present?
@etablissement = @champ.build_etablissement(etablissement_attributes)
@etablissement.champ = @champ
else
@error = "SIRET invalide"
message = ['Nous navons pas trouvé détablissement correspondant à ce numéro de SIRET.']
message << helpers.link_to('Plus dinformations', "https://faq.demarches-simplifiees.fr/article/4-erreur-siret", target: '_blank')
@error = helpers.safe_join(message, ' ')
end
end
respond_to do |format|

View file

@ -101,8 +101,10 @@ module NewGestionnaire
NotificationMailer.send_without_continuation_notification(dossier).deliver_later
when "accepter"
dossier.accepte!
dossier.attestation = dossier.build_attestation
dossier.save
if dossier.attestation.nil?
dossier.attestation = dossier.build_attestation
dossier.save
end
flash.notice = "Dossier traité avec succès."
NotificationMailer.send_closed_notification(dossier).deliver_later
end

View file

@ -0,0 +1,19 @@
// Administrate injects its own copy of jQuery, and it is the one
// configured by rails to send csrf-token
const $ = window.$;
$(document).on('change', '#features input[type=checkbox]', ({ target }) => {
target = $(target);
const url = target.data('url');
const key = target.data('key');
const value = target.prop('checked');
$.ajax(url, {
method: 'put',
contentType: 'application/json',
dataType: 'json',
data: JSON.stringify({
features: { [key]: value }
})
});
});

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,35 @@
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:
input.removeAttr('data-invalid');
$.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,31 @@
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();
// Disable jQuery-driven animations during tests
if (process.env['RAILS_ENV'] === 'test') {
jQuery.fx.off = true;
}
// Expose globals
window.Bloodhound = Bloodhound;
window.Chartkick = Chartkick;
// Export jQuery globally for legacy Javascript files used in the old design
window.$ = jQuery;
window.jQuery = jQuery;

View file

@ -0,0 +1,56 @@
import Turbolinks from 'turbolinks';
import Rails from 'rails-ujs';
import * as ActiveStorage from 'activestorage';
import Chartkick from 'chartkick';
import Highcharts from 'highcharts';
import jQuery from 'jquery';
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();
// Disable jQuery-driven animations during tests
if (process.env['RAILS_ENV'] === 'test') {
jQuery.fx.off = true;
}
// Expose globals
window.DS = window.DS || DS;
window.Chartkick = Chartkick;

View file

@ -0,0 +1 @@
import '../manager/fields/features';

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

@ -20,6 +20,10 @@ class Champ < ApplicationRecord
mandatory? && value.blank?
end
def search_terms
[ to_s ]
end
def to_s
if value.present?
string_value

View file

@ -1,2 +1,7 @@
class Champs::CheckboxChamp < Champ
def search_terms
if value == 'on'
[ libelle ]
end
end
end

View file

@ -1,6 +1,10 @@
class Champs::DateChamp < Champ
before_save :format_before_save
def search_terms
# Text search is pretty useless for dates so were not including these champs
end
private
def format_before_save

View file

@ -9,6 +9,10 @@ class Champs::DatetimeChamp < Champ
same_date?(num, '%M')
end
def search_terms
# Text search is pretty useless for datetimes so were not including these champs
end
private
def same_date?(num, compare)

View file

@ -1,2 +1,7 @@
class Champs::EngagementChamp < Champs::CheckboxChamp
def search_terms
if value == 'on'
[ libelle ]
end
end
end

View file

@ -1,2 +1,5 @@
class Champs::ExplicationChamp < Champs::TextChamp
def search_terms
# The user cannot enter any information here so it doesnt make much sense to search
end
end

View file

@ -1,2 +1,5 @@
class Champs::HeaderSectionChamp < Champ
def search_terms
# The user cannot enter any information here so it doesnt make much sense to search
end
end

View file

@ -37,6 +37,10 @@ class Champs::LinkedDropDownListChamp < Champ
mandatory? && (primary_value.blank? || secondary_value.blank?)
end
def search_terms
[ primary_value, secondary_value ]
end
private
def string_value

View file

@ -1,6 +1,10 @@
class Champs::MultipleDropDownListChamp < Champ
before_save :format_before_save
def search_terms
drop_down_list.selected_options_without_decorator(self)
end
private
def format_before_save

View file

@ -18,6 +18,10 @@ class Champs::PieceJustificativeChamp < Champ
"image/jpeg"
]
def search_terms
# We dont know how to search inside documents yet
end
def mandatory_and_blank?
mandatory? && !piece_justificative_file.attached?
end

View file

@ -39,4 +39,8 @@ class Champs::SiretChamp < Champ
belongs_to :etablissement, dependent: :destroy
accepts_nested_attributes_for :etablissement, allow_destroy: true, update_only: true
def search_terms
etablissement.present? ? etablissement.search_terms : [ value ]
end
end

View file

@ -1,4 +1,10 @@
class Champs::YesNoChamp < Champs::CheckboxChamp
def search_terms
if value == 'true'
[ libelle ]
end
end
private
def value_for_export

View file

@ -14,7 +14,13 @@ class Commentaire < ApplicationRecord
after_create :notify
def header
"#{email}, #{I18n.l(created_at.localtime, format: '%d %b %Y %H:%M')}"
"#{sender}, #{I18n.l(created_at.localtime, format: '%d %b %Y %H:%M')}"
end
def sender
if email.present?
email.split('@').first
end
end
def file_url

View file

@ -69,6 +69,7 @@ class Dossier < ApplicationRecord
delegate :france_connect_information, to: :user
before_validation :update_state_dates, if: -> { state_changed? }
before_save :update_search_terms
after_save :build_default_champs, if: Proc.new { saved_change_to_procedure_id? }
after_save :build_default_individual, if: Proc.new { procedure.for_individual? }
@ -78,6 +79,19 @@ class Dossier < ApplicationRecord
validates :user, presence: true
def update_search_terms
self.search_terms = [
user&.email,
france_connect_information&.given_name,
france_connect_information&.family_name,
*ordered_champs.flat_map(&:search_terms),
*etablissement&.search_terms,
individual&.nom,
individual&.prenom
].compact.join(' ')
self.private_search_terms = ordered_champs_private.flat_map(&:search_terms).compact.join(' ')
end
def was_piece_justificative_uploaded_for_type_id?(type_id)
pieces_justificatives.where(type_de_piece_justificative_id: type_id).count > 0
end

View file

@ -11,6 +11,30 @@ class Etablissement < ApplicationRecord
validate :validate_signature
def search_terms
[
entreprise_siren,
entreprise_numero_tva_intracommunautaire,
entreprise_forme_juridique,
entreprise_forme_juridique_code,
entreprise_nom_commercial,
entreprise_raison_sociale,
entreprise_siret_siege_social,
entreprise_nom,
entreprise_prenom,
association_rna,
association_titre,
association_objet,
siret,
naf,
libelle_naf,
adresse,
code_postal,
localite,
code_insee_localite
]
end
def siren
entreprise_siren
end

View file

@ -4,5 +4,5 @@
<% else %>
var html = "<%= escape_javascript(render partial: 'shared/champs/siret/etablissement', locals: { position: @champ.order_place, etablissement: @etablissement }) %>";
<% end %>
$("#etablissement-for-<%= @champ.id %>").html(html);
document.querySelector("#etablissement-for-<%= @champ.id %>").innerHTML = html;
})();

View file

@ -5,22 +5,3 @@
%td= feature.title
%td
= check_box_tag "enable-feature", "enable", field.data[feature.name], data: { url: enable_feature_manager_administrateur_path(field.resource.id), key: feature.key }
:javascript
window.onload = function() {
$('#features input[type=checkbox]').on('change', function(evt) {
let url = $(evt.target).data('url');
let key = $(evt.target).data('key');
let features = {};
features[key] = $(evt.target).prop('checked');
$.ajax(url, {
method: 'put',
contentType: 'application/json',
dataType: 'json',
data: JSON.stringify({
features: features
})
});
});
};

View file

@ -10,11 +10,14 @@
= 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'
@ -22,14 +25,8 @@
#beta
Env Test
- if Rails.env == 'test'
%script{ type: 'text/javascript' }
(typeof jQuery !== 'undefined') && (jQuery.fx.off = true);
= render partial: 'layouts/ie_lt_10'
#wrap
.row
#header.navbar

View file

@ -10,13 +10,6 @@
.badge.progress-bar-info
= @facade.dossier.invites.count
.dropdown-menu.dropdown-menu-right.dropdown-pannel
%h4= t('dynamics.dossiers.followers.title')
%ul
- if @facade.followers.present?
- @facade.followers.each do |follower|
%li= follower.email
- else
= t('dynamics.dossiers.followers.empty')
%h4= t('dynamics.dossiers.invites.title')
%ul
- if @facade.invites.present?

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,12 +40,7 @@
= 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
= yield :charts_js
- if Rails.env == "test"
%script{ type: "text/javascript" }
(typeof jQuery !== "undefined") && (jQuery.fx.off = true);

View file

@ -0,0 +1,23 @@
<%#
# Javascript Partial
This partial imports the necessary javascript on each page.
By default, it includes the application JS,
but each page can define additional JS sources
by providing a `content_for(:javascript)` block.
%>
<% Administrate::Engine.javascripts.each do |js_path| %>
<%= javascript_include_tag js_path %>
<% end %>
<%= javascript_pack_tag 'manager' %>
<%= yield :javascript %>
<% if Rails.env.test? %>
<%= javascript_tag do %>
$.fx.off = true;
$.ajaxSetup({ async: false });
<% end %>
<% end %>

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,4 +1,4 @@
= message
.mandatory= message
- if etablissement.present?
- champ_attributes = etablissement.champ.private? ? 'champs_private_attributes' : 'champs_attributes'
= fields_for "dossier[#{champ_attributes}][#{position}][etablissement_attributes]", etablissement do |form|

View file

@ -1,4 +1,4 @@
.etablissement-titre
.explication
= raison_sociale_or_name(etablissement)
= etablissement.entreprise_forme_juridique
- if etablissement.entreprise_capital_social.present?

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

@ -46,7 +46,6 @@ set :rails_env, ENV["to"]
# They will be linked in the 'deploy:link_shared_paths' step.
set :shared_paths, [
'log',
'bin',
'uploads',
'tmp/pids',
'tmp/cache',
@ -123,6 +122,16 @@ task :setup => :environment do
queue %[echo "-----> Be sure to edit 'shared/environments/staging.rb'."]
end
namespace :yarn do
desc "Install package dependencies using yarn."
task :install do
queue %{
echo "-----> Installing package dependencies using yarn"
#{echo_cmd %[yarn install --non-interactive]}
}
end
end
desc "Deploys the current version to the server."
task :deploy => :environment do
queue 'export PATH=$PATH:/usr/local/rbenv/bin:/usr/local/rbenv/shims'
@ -133,6 +142,7 @@ task :deploy => :environment do
invoke :'git:clone'
invoke :'deploy:link_shared_paths'
invoke :'bundle:install'
invoke :'yarn:install'
invoke :'rails:db_migrate'
invoke :'rails:assets_precompile:force'

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

@ -7,9 +7,6 @@ fr:
dossiers:
depositaite: "Dépositaire"
numéro: 'Dossier nº '
followers:
title: "Personnes suivant l'activité de ce dossier"
empty: "Aucune personne ne suit ce dossier"
invites:
title: "Personnes invitées à voir ce dossier"
empty: "Aucune personne invitée"

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

View file

@ -0,0 +1,8 @@
class AddSearchTermsToDossiers < ActiveRecord::Migration[5.2]
def change
add_column :dossiers, :search_terms, :text
add_column :dossiers, :private_search_terms, :text
add_index :dossiers, "to_tsvector('french', search_terms)", using: :gin, name: 'index_dossiers_on_search_terms'
add_index :dossiers, "to_tsvector('french', search_terms || private_search_terms)", using: :gin, name: 'index_dossiers_on_search_terms_private_search_terms'
end
end

View file

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2018_07_19_125038) do
ActiveRecord::Schema.define(version: 2018_07_24_153247) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -244,6 +244,10 @@ ActiveRecord::Schema.define(version: 2018_07_19_125038) do
t.datetime "processed_at"
t.text "motivation"
t.datetime "hidden_at"
t.text "search_terms"
t.text "private_search_terms"
t.index "to_tsvector('french'::regconfig, (search_terms || private_search_terms))", name: "index_dossiers_on_search_terms_private_search_terms", using: :gin
t.index "to_tsvector('french'::regconfig, search_terms)", name: "index_dossiers_on_search_terms", using: :gin
t.index ["hidden_at"], name: "index_dossiers_on_hidden_at"
t.index ["procedure_id"], name: "index_dossiers_on_procedure_id"
t.index ["user_id"], name: "index_dossiers_on_user_id"

View file

@ -0,0 +1,17 @@
require Rails.root.join("lib", "tasks", "task_helper")
namespace :'2018_07_24_refresh_search_terms' do
task run: :environment do
# For dossiers belonging to an archived procedure, the check for the `build_default_individual` `after_save` callback fails.
# So, we filter those out by joining with `procedure`, whose default scope excludes archived procedures.
ds = Dossier.joins(:procedure)
total_count = ds.count
one_percent = total_count / 100
Dossier.joins(:procedure).find_each(batch_size: 100).with_index do |d, i|
if i % one_percent == 0
rake_puts("#{i}/#{total_count} (#{i / one_percent}%)")
end
d.save(touch: false)
end
end
end

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

@ -227,6 +227,14 @@ describe NewGestionnaire::DossiersController, type: :controller do
is_expected.to redirect_to redirect_to gestionnaire_dossier_path(procedure, dossier)
end
context 'and the dossier has already an attestation' do
it 'should not crash' do
dossier.attestation = Attestation.new
dossier.save
expect(subject).to redirect_to redirect_to gestionnaire_dossier_path(procedure, dossier)
end
end
end
context 'when the attestation template uses the motivation field' do

View file

@ -66,7 +66,7 @@ feature 'As a User I wanna create a dossier' do
login_as user, scope: :user
visit commencer_path(procedure_path: procedure_with_siret.path)
expect(page).to have_current_path(users_dossier_path(procedure_with_siret.dossiers.last.id.to_s))
fill_in 'dossier-siret', with: siret
stub_request(:get, /https:\/\/entreprise.api.gouv.fr\/v2\/etablissements\/#{siret}?.*token=/)
.to_return(status: 200, body: File.read('spec/support/files/etablissement.json'))
stub_request(:get, /https:\/\/entreprise.api.gouv.fr\/v2\/entreprises\/#{siren}?.*token=/)
@ -75,8 +75,11 @@ feature 'As a User I wanna create a dossier' do
.to_return(status: 200, body: File.read('spec/support/files/exercices.json'))
stub_request(:get, /https:\/\/entreprise.api.gouv.fr\/v2\/associations\/#{siret}?.*token=/)
.to_return(status: 404, body: '')
page.find_by_id('dossier-siret').set siret
page.find_by_id('submit-siret').click
wait_for_ajax
expect(page).to have_css('#recap-info-entreprise')
find(:css, "#dossier_autorisation_donnees[value='1']").set(true)
page.find_by_id('etape_suivante').click

Some files were not shown because too many files have changed in this diff Show more