Apply prettier rules

This commit is contained in:
2024-11-14 18:47:38 +01:00
parent 610227815a
commit aa0785fc71
291 changed files with 23646 additions and 22071 deletions

View File

@@ -4,47 +4,44 @@
*/
/// import jQuery
const $ = require('jquery');
const $ = require("jquery");
global.$ = global.jQuery = $;
/// import select2
const select2 = require('select2');
const select2 = require("select2");
global.select2 = select2;
require('select2/dist/css/select2.css');
require('select2-bootstrap-theme/dist/select2-bootstrap.css');
require("select2/dist/css/select2.css");
require("select2-bootstrap-theme/dist/select2-bootstrap.css");
/*
* Load Chill themes assets
*/
require('./chillmain.scss');
require("./chillmain.scss");
import { chill } from './js/chill.js';
import { chill } from "./js/chill.js";
global.chill = chill;
require('./js/date');
require('./js/counter.js');
require("./js/date");
require("./js/counter.js");
/// Load fonts
require('./fonts/OpenSans/OpenSans.scss')
require("./fonts/OpenSans/OpenSans.scss");
/// Load images
require('./img/favicon.ico');
require('./img/logo-chill-sans-slogan_white.png');
require('./img/logo-chill-outil-accompagnement_white.png');
require("./img/favicon.ico");
require("./img/logo-chill-sans-slogan_white.png");
require("./img/logo-chill-outil-accompagnement_white.png");
/*
* Load local libs
* Some libs are only used in a few pages, they are loaded on a case by case basis
*/
require('../lib/breadcrumb/index.js');
require('../lib/download-report/index.js');
require('../lib/select_interactive_loading/index.js');
require("../lib/breadcrumb/index.js");
require("../lib/download-report/index.js");
require("../lib/select_interactive_loading/index.js");
//require('../lib/show_hide/index.js');
//require('../lib/tabs/index.js');

View File

@@ -1,107 +1,111 @@
/* jslint vars: true */
/*jslint indent: 4 */
/* global moment, $, window */
'use strict';
"use strict";
var chill = function() {
var chill = (function () {
/**
* Display an alert message when the user wants to leave a page containing a given form
* in a given state.
*
* The action of displaying the form be parametrised as :
* - always display the alert message when leaving
* - only display the alert message when the form contains some modified fields.
*
* @param{string} form_id An identification string of the form
* @param{string} alert_message The alert message to display
* @param{boolean} check_unsaved_data If true display the alert message only when the form
* contains some modified fields otherwise always display the alert when leaving
* @return nothing
*/
function _generalDisplayAlertWhenLeavingForm(
form_id,
alert_message,
check_unsaved_data,
) {
var form_submitted = false;
var unsaved_data = false;
/**
* Display an alert message when the user wants to leave a page containing a given form
* in a given state.
*
* The action of displaying the form be parametrised as :
* - always display the alert message when leaving
* - only display the alert message when the form contains some modified fields.
*
* @param{string} form_id An identification string of the form
* @param{string} alert_message The alert message to display
* @param{boolean} check_unsaved_data If true display the alert message only when the form
* contains some modified fields otherwise always display the alert when leaving
* @return nothing
*/
function _generalDisplayAlertWhenLeavingForm(form_id, alert_message, check_unsaved_data) {
var form_submitted = false;
var unsaved_data = false;
$(form_id)
.submit(function () {
form_submitted = true;
})
.on("reset", function () {
unsaved_data = false;
});
$(form_id)
.submit(function() {
form_submitted = true;
})
.on('reset', function() {
unsaved_data = false;
})
;
$.each($(form_id).find(":input"), function (i, e) {
$(e).change(function () {
unsaved_data = true;
});
});
$.each($(form_id).find(':input'), function(i,e) {
$(e).change(function() {
unsaved_data = true;
});
});
$(window).bind("beforeunload", function () {
if (!form_submitted && (unsaved_data || !check_unsaved_data)) {
return alert_message;
}
});
}
$(window).bind('beforeunload', function(){
if((!form_submitted) && (unsaved_data || !check_unsaved_data)) {
return alert_message;
}
});
}
/**
* Mark the choices "not specified" as check by default.
*
* This function apply to `custom field choices` when the `required`
* option is false and `expanded` is true (checkboxes or radio buttons).
*
* @param{string} choice_name the name of the input
*/
function checkNullValuesInChoices(choice_name) {
var choices;
choices = $("input[name='"+choice_name+"']:checked");
if (choices.size() === 0) {
$.each($("input[name='"+choice_name+"']"), function (i, e) {
if (e.value === "") {
e.checked = true;
}
});
/**
* Mark the choices "not specified" as check by default.
*
* This function apply to `custom field choices` when the `required`
* option is false and `expanded` is true (checkboxes or radio buttons).
*
* @param{string} choice_name the name of the input
*/
function checkNullValuesInChoices(choice_name) {
var choices;
choices = $("input[name='" + choice_name + "']:checked");
if (choices.size() === 0) {
$.each($("input[name='" + choice_name + "']"), function (i, e) {
if (e.value === "") {
e.checked = true;
}
});
}
}
/**
* Display an alert message when the user wants to leave a page containing a given
* modified form.
*
* @param{string} form_id An identification string of the form
* @param{string} alert_message The alert message to display
* @return nothing
*/
function displayAlertWhenLeavingModifiedForm(form_id, alert_message) {
_generalDisplayAlertWhenLeavingForm(form_id, alert_message, true);
}
/**
* Display an alert message when the user wants to leave a page containing a given
* modified form.
*
* @param{string} form_id An identification string of the form
* @param{string} alert_message The alert message to display
* @return nothing
*/
function displayAlertWhenLeavingModifiedForm(form_id, alert_message) {
_generalDisplayAlertWhenLeavingForm(form_id, alert_message, true);
}
/**
* Display an alert message when the user wants to leave a page containing a given
* form that was not submitted.
*
* @param{string} form_id An identification string of the form
* @param{string} alert_message The alert message to display
* @return nothing
*/
function displayAlertWhenLeavingUnsubmittedForm(form_id, alert_message) {
_generalDisplayAlertWhenLeavingForm(form_id, alert_message, false);
}
/**
* Display an alert message when the user wants to leave a page containing a given
* form that was not submitted.
*
* @param{string} form_id An identification string of the form
* @param{string} alert_message The alert message to display
* @return nothing
*/
function displayAlertWhenLeavingUnsubmittedForm(form_id, alert_message) {
_generalDisplayAlertWhenLeavingForm(form_id, alert_message, false);
}
/* Enable the following behavior : when the user change the value
/* Enable the following behavior : when the user change the value
of an other field, its checkbox is checked.
*/
function checkOtherValueOnChange() {
$('.input-text-other-value').each(function() {
$(this).change(function() {
var checkbox = $(this).parent().find('input[type=checkbox][value=_other]')[0];
$(checkbox).prop('checked', ($(this).val() !== ''));
});
});
}
function checkOtherValueOnChange() {
$(".input-text-other-value").each(function () {
$(this).change(function () {
var checkbox = $(this)
.parent()
.find("input[type=checkbox][value=_other]")[0];
$(checkbox).prop("checked", $(this).val() !== "");
});
});
}
/**
/**
* Create an interraction between two select element (the parent and the
* child) of a given form : each parent option has a category, the
* child select only display options that have the same category of the
@@ -138,208 +142,231 @@ var chill = function() {
- quand vide
- quand choix
*/
function categoryLinkParentChildSelect() {
var forms_to_link = $('form:has(select.chill-category-link-parent)');
function categoryLinkParentChildSelect() {
var forms_to_link = $("form:has(select.chill-category-link-parent)");
forms_to_link.each(function(i,form_selector) {
var form = $(form_selector), parent_multiple;
form.old_category = null;
form.link_parent = $(form).find('.chill-category-link-parent');
form.link_child = $(form).find('.chill-category-link-child');
forms_to_link.each(function (i, form_selector) {
var form = $(form_selector),
parent_multiple;
form.old_category = null;
form.link_parent = $(form).find(".chill-category-link-parent");
form.link_child = $(form).find(".chill-category-link-child");
// check if the parent allow multiple or single results
parent_multiple = $(form).find('.chill-category-link-parent').get(0).multiple;
// if we use select2, parent_multiple will be `undefined`
if (typeof parent_multiple == 'undefined') {
// currently, I do not know how to check if multiple using select2.
// we suppose that multiple is false (old behaviour)
parent_multiple = false
}
// check if the parent allow multiple or single results
parent_multiple = $(form)
.find(".chill-category-link-parent")
.get(0).multiple;
// if we use select2, parent_multiple will be `undefined`
if (typeof parent_multiple == "undefined") {
// currently, I do not know how to check if multiple using select2.
// we suppose that multiple is false (old behaviour)
parent_multiple = false;
}
$(form.link_parent).addClass('select2');
$(form.link_parant).select2({allowClear: true}); // it is weird: when I fix the typo here, the whole stuff does not work anymore...
$(form.link_parent).addClass("select2");
$(form.link_parant).select2({ allowClear: true }); // it is weird: when I fix the typo here, the whole stuff does not work anymore...
if (parent_multiple == false) {
if (parent_multiple == false) {
form.old_category = null;
if ($(form.link_parent).select2("data") !== null) {
form.old_category = $(form.link_parent).select2(
"data",
).element[0].dataset.linkCategory;
}
form.old_category = null;
if($(form.link_parent).select2('data') !== null) {
form.old_category = ($(form.link_parent).select2('data').element[0].dataset.linkCategory);
}
$(form.link_child).find('option')
.each(function(i,e) {
if(
((!$(e).data('link-category')) || $(e).data('link-category') == form.old_category) &&
((!$(e).data('link-categories')) || form.old_category in $(e).data('link-categories').split(','))
) {
$(e).show();
// here, we should handle the optgroup
} else {
$(e).hide();
// here, we should handle the optgroup
}
});
form.link_parent.change(function() {
var new_category = ($(form.link_parent).select2('data').element[0].dataset.linkCategory);
if(new_category != form.old_category) {
$(form.link_child).find('option')
.each(function(i,e) {
if(
((!$(e).data('link-category')) || $(e).data('link-category') == new_category) &&
((!$(e).data('link-categories')) || new_category in $(e).data('link-categories').split(','))
) {
$(e).show();
// here, we should handle the optgroup
} else {
$(e).hide();
// here, we should handle the opgroup
}
});
$(form.link_child).find('option')[0].selected = true;
form.old_category = new_category;
}
});
$(form.link_child)
.find("option")
.each(function (i, e) {
if (
(!$(e).data("link-category") ||
$(e).data("link-category") == form.old_category) &&
(!$(e).data("link-categories") ||
form.old_category in $(e).data("link-categories").split(","))
) {
$(e).show();
// here, we should handle the optgroup
} else {
var i=0,
selected_items = $(form.link_parent).find(':selected');
$(e).hide();
// here, we should handle the optgroup
}
});
form.old_categories = [];
for (i=0;i < selected_items.length; i++) {
form.old_categories.push(selected_items[i].value);
form.link_parent.change(function () {
var new_category = $(form.link_parent).select2("data").element[0]
.dataset.linkCategory;
if (new_category != form.old_category) {
$(form.link_child)
.find("option")
.each(function (i, e) {
if (
(!$(e).data("link-category") ||
$(e).data("link-category") == new_category) &&
(!$(e).data("link-categories") ||
new_category in $(e).data("link-categories").split(","))
) {
$(e).show();
// here, we should handle the optgroup
} else {
$(e).hide();
// here, we should handle the opgroup
}
$(form.link_child).find('option')
.each(function(i,e) {
var visible;
if(form.old_categories.indexOf(e.dataset.linkCategory) != -1) {
$(e).show();
if (e.parentNode instanceof HTMLOptGroupElement) {
$(e.parentNode).show();
}
} else {
$(e).hide();
if (e.parentNode instanceof HTMLOptGroupElement) {
// we check if the other options are visible.
visible = false
for (var l=0; l < e.parentNode.children.length; l++) {
if (window.getComputedStyle(e.parentNode.children[l]).getPropertyValue('display') != 'none') {
visible = true;
}
}
// if any options are visible, we hide the optgroup
if (visible == false) {
$(e.parentNode).hide();
}
}
}
});
form.link_parent.change(function() {
var new_categories = [],
selected_items = $(form.link_parent).find(':selected'),
visible;
for (i=0;i < selected_items.length; i++) {
new_categories.push(selected_items[i].value);
}
if(new_categories != form.old_categories) {
$(form.link_child).find('option')
.each(function(i,e) {
if(new_categories.indexOf(e.dataset.linkCategory) != -1) {
$(e).show();
// if parent is an opgroup, we show it
if (e.parentNode instanceof HTMLOptGroupElement) {
$(e.parentNode).show();
}
} else {
$(e).hide();
// we check if the parent is an optgroup
if (e.parentNode instanceof HTMLOptGroupElement) {
// we check if other options are visible
visible = false
for (var l=0; l < e.parentNode.children.length; l++) {
if (window.getComputedStyle(e.parentNode.children[l]).getPropertyValue('display') != 'none') {
visible = true;
}
}
// if any options are visible, we hide the optgroup
if (visible == false) {
$(e.parentNode).hide();
}
}
}
});
//$(form.link_child).find('option')[0].selected = true;
form.old_categories = new_categories;
}
});
}
});
$(form.link_child).find("option")[0].selected = true;
form.old_category = new_category;
}
});
}
} else {
var i = 0,
selected_items = $(form.link_parent).find(":selected");
function _displayHideTargetWithCheckbox(checkbox) {
var target = checkbox.dataset.displayTarget,
hideableElements;
hideableElements = document.querySelectorAll('[data-display-show-hide="' + target + '"]');
if (checkbox.checked) {
for (let i=0; i < hideableElements.length; i = i+1) {
hideableElements[i].style.display = "unset";
}
} else {
for (let i=0; i < hideableElements.length; i = i+1) {
hideableElements[i].style.display = "none";
}
form.old_categories = [];
for (i = 0; i < selected_items.length; i++) {
form.old_categories.push(selected_items[i].value);
}
$(form.link_child)
.find("option")
.each(function (i, e) {
var visible;
if (form.old_categories.indexOf(e.dataset.linkCategory) != -1) {
$(e).show();
if (e.parentNode instanceof HTMLOptGroupElement) {
$(e.parentNode).show();
}
} else {
$(e).hide();
if (e.parentNode instanceof HTMLOptGroupElement) {
// we check if the other options are visible.
visible = false;
for (var l = 0; l < e.parentNode.children.length; l++) {
if (
window
.getComputedStyle(e.parentNode.children[l])
.getPropertyValue("display") != "none"
) {
visible = true;
}
}
// if any options are visible, we hide the optgroup
if (visible == false) {
$(e.parentNode).hide();
}
}
}
});
form.link_parent.change(function () {
var new_categories = [],
selected_items = $(form.link_parent).find(":selected"),
visible;
for (i = 0; i < selected_items.length; i++) {
new_categories.push(selected_items[i].value);
}
if (new_categories != form.old_categories) {
$(form.link_child)
.find("option")
.each(function (i, e) {
if (new_categories.indexOf(e.dataset.linkCategory) != -1) {
$(e).show();
// if parent is an opgroup, we show it
if (e.parentNode instanceof HTMLOptGroupElement) {
$(e.parentNode).show();
}
} else {
$(e).hide();
// we check if the parent is an optgroup
if (e.parentNode instanceof HTMLOptGroupElement) {
// we check if other options are visible
visible = false;
for (var l = 0; l < e.parentNode.children.length; l++) {
if (
window
.getComputedStyle(e.parentNode.children[l])
.getPropertyValue("display") != "none"
) {
visible = true;
}
}
// if any options are visible, we hide the optgroup
if (visible == false) {
$(e.parentNode).hide();
}
}
}
});
//$(form.link_child).find('option')[0].selected = true;
form.old_categories = new_categories;
}
});
}
});
}
function _displayHideTargetWithCheckbox(checkbox) {
var target = checkbox.dataset.displayTarget,
hideableElements;
hideableElements = document.querySelectorAll(
'[data-display-show-hide="' + target + '"]',
);
if (checkbox.checked) {
for (let i = 0; i < hideableElements.length; i = i + 1) {
hideableElements[i].style.display = "unset";
}
} else {
for (let i = 0; i < hideableElements.length; i = i + 1) {
hideableElements[i].style.display = "none";
}
}
}
/**
* create an interaction between a checkbox and element to show if the
* checkbox is checked, or hide if the checkbox is not checked.
*
* The checkbox must have the data `data-display-target` with an id,
* and the parts to show/hide must have the data `data-display-show-hide`
* with the same value.
*
* Example :
*
* ```
* <input data-display-target="export_abc" value="1" type="checkbox">
*
* <div data-display-show-hide="export_abc">
* <!-- your content here will be hidden / shown according to checked state -->
* </div>
* ```
*
* Hint: for forms in symfony, you could use the `id` of the form element,
* accessible through `{{ form.vars.id }}`. This id should be unique.
*
*
* @returns {undefined}
*/
function listenerDisplayCheckbox() {
var elements = document.querySelectorAll("[data-display-target]");
/**
* create an interaction between a checkbox and element to show if the
* checkbox is checked, or hide if the checkbox is not checked.
*
* The checkbox must have the data `data-display-target` with an id,
* and the parts to show/hide must have the data `data-display-show-hide`
* with the same value.
*
* Example :
*
* ```
* <input data-display-target="export_abc" value="1" type="checkbox">
*
* <div data-display-show-hide="export_abc">
* <!-- your content here will be hidden / shown according to checked state -->
* </div>
* ```
*
* Hint: for forms in symfony, you could use the `id` of the form element,
* accessible through `{{ form.vars.id }}`. This id should be unique.
*
*
* @returns {undefined}
*/
function listenerDisplayCheckbox() {
var elements = document.querySelectorAll("[data-display-target]");
for (let i=0; i < elements.length; i = i+1) {
elements[i].addEventListener("change", function(e) {
_displayHideTargetWithCheckbox(e.target);
});
// initial display-hide
_displayHideTargetWithCheckbox(elements[i]);
}
for (let i = 0; i < elements.length; i = i + 1) {
elements[i].addEventListener("change", function (e) {
_displayHideTargetWithCheckbox(e.target);
});
// initial display-hide
_displayHideTargetWithCheckbox(elements[i]);
}
}
return {
checkOtherValueOnChange: checkOtherValueOnChange,
displayAlertWhenLeavingModifiedForm: displayAlertWhenLeavingModifiedForm,
displayAlertWhenLeavingUnsubmittedForm: displayAlertWhenLeavingUnsubmittedForm,
checkNullValuesInChoices: checkNullValuesInChoices,
categoryLinkParentChildSelect: categoryLinkParentChildSelect,
listenerDisplayCheckbox: listenerDisplayCheckbox,
};
} ();
return {
checkOtherValueOnChange: checkOtherValueOnChange,
displayAlertWhenLeavingModifiedForm: displayAlertWhenLeavingModifiedForm,
displayAlertWhenLeavingUnsubmittedForm:
displayAlertWhenLeavingUnsubmittedForm,
checkNullValuesInChoices: checkNullValuesInChoices,
categoryLinkParentChildSelect: categoryLinkParentChildSelect,
listenerDisplayCheckbox: listenerDisplayCheckbox,
};
})();
export { chill };

View File

@@ -9,27 +9,24 @@
const isNum = (v) => !isNaN(v);
const parseCounter = () => {
document.querySelectorAll('span.counter')
.forEach(el => {
let r = [];
el.innerText
.trim()
.split(' ')
.forEach(w => {
if (isNum(w)) {
r.push(`<span>${w}</span>`);
} else {
r.push(w);
}
})
;
el.innerHTML = r.join(' ');
})
;
document.querySelectorAll("span.counter").forEach((el) => {
let r = [];
el.innerText
.trim()
.split(" ")
.forEach((w) => {
if (isNum(w)) {
r.push(`<span>${w}</span>`);
} else {
r.push(w);
}
});
el.innerHTML = r.join(" ");
});
};
window.addEventListener('DOMContentLoaded', function (e) {
parseCounter();
window.addEventListener("DOMContentLoaded", function (e) {
parseCounter();
});
export { parseCounter };

View File

@@ -12,16 +12,16 @@
* Do not take time into account
*
*/
export const dateToISO = (date: Date|null): string|null => {
export const dateToISO = (date: Date | null): string | null => {
if (null === date) {
return null;
}
return [
date.getFullYear(),
(date.getMonth() + 1).toString().padStart(2, '0'),
date.getDate().toString().padStart(2, '0')
].join('-');
(date.getMonth() + 1).toString().padStart(2, "0"),
date.getDate().toString().padStart(2, "0"),
].join("-");
};
/**
@@ -29,7 +29,7 @@ export const dateToISO = (date: Date|null): string|null => {
*
* **Experimental**
*/
export const ISOToDate = (str: string|null): Date|null => {
export const ISOToDate = (str: string | null): Date | null => {
if (null === str) {
return null;
}
@@ -37,34 +37,32 @@ export const ISOToDate = (str: string|null): Date|null => {
return null;
}
const
[year, month, day] = str.split('-').map(p => parseInt(p));
const [year, month, day] = str.split("-").map((p) => parseInt(p));
return new Date(year, month-1, day, 0, 0, 0, 0);
}
return new Date(year, month - 1, day, 0, 0, 0, 0);
};
/**
* Return a date object from iso string formatted as YYYY-mm-dd:HH:MM:ss+01:00
*
*/
export const ISOToDatetime = (str: string|null): Date|null => {
export const ISOToDatetime = (str: string | null): Date | null => {
if (null === str) {
return null;
}
const
[cal, times] = str.split('T'),
[year, month, date] = cal.split('-').map(s => parseInt(s)),
const [cal, times] = str.split("T"),
[year, month, date] = cal.split("-").map((s) => parseInt(s)),
[time, timezone] = times.split(times.charAt(8)),
[hours, minutes, seconds] = time.split(':').map(s => parseInt(s));
;
if ('0000' === timezone) {
return new Date(Date.UTC(year, month-1, date, hours, minutes, seconds));
[hours, minutes, seconds] = time.split(":").map((s) => parseInt(s));
if ("0000" === timezone) {
return new Date(
Date.UTC(year, month - 1, date, hours, minutes, seconds),
);
}
return new Date(year, month-1, date, hours, minutes, seconds);
}
return new Date(year, month - 1, date, hours, minutes, seconds);
};
/**
* Convert a date to ISO8601, valid for usage in api
@@ -74,39 +72,43 @@ export const datetimeToISO = (date: Date): string => {
let cal, time, offset;
cal = [
date.getFullYear(),
(date.getMonth() + 1).toString().padStart(2, '0'),
date.getDate().toString().padStart(2, '0')
].join('-');
(date.getMonth() + 1).toString().padStart(2, "0"),
date.getDate().toString().padStart(2, "0"),
].join("-");
time = [
date.getHours().toString().padStart(2, '0'),
date.getMinutes().toString().padStart(2, '0'),
date.getSeconds().toString().padStart(2, '0')
].join(':');
date.getHours().toString().padStart(2, "0"),
date.getMinutes().toString().padStart(2, "0"),
date.getSeconds().toString().padStart(2, "0"),
].join(":");
offset = [
date.getTimezoneOffset() <= 0 ? '+' : '-',
Math.abs(Math.floor(date.getTimezoneOffset() / 60)).toString().padStart(2, '0'),
':',
Math.abs(date.getTimezoneOffset() % 60).toString().padStart(2, '0'),
].join('');
date.getTimezoneOffset() <= 0 ? "+" : "-",
Math.abs(Math.floor(date.getTimezoneOffset() / 60))
.toString()
.padStart(2, "0"),
":",
Math.abs(date.getTimezoneOffset() % 60)
.toString()
.padStart(2, "0"),
].join("");
const x = cal + 'T' + time + offset;
const x = cal + "T" + time + offset;
return x;
};
export const intervalDaysToISO = (days: number|string|null): string => {
export const intervalDaysToISO = (days: number | string | null): string => {
if (null === days) {
return 'P0D';
return "P0D";
}
return `P${days}D`;
}
};
export const intervalISOToDays = (str: string|null): number|null => {
export const intervalISOToDays = (str: string | null): number | null => {
if (null === str) {
return null
return null;
}
if ("" === str.trim()) {
@@ -121,40 +123,42 @@ export const intervalISOToDays = (str: string|null): number|null => {
continue;
}
switch (str.charAt(i)) {
case 'P':
case "P":
isDate = true;
break;
case 'T':
case "T":
isDate = false;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case "0":
case "1":
case "2":
case "3":
case "4":
case "5":
case "6":
case "7":
case "8":
case "9":
vstring = vstring + str.charAt(i);
break;
case 'Y':
case "Y":
days = days + Number.parseInt(vstring) * 365;
vstring = "";
break;
case 'M':
case "M":
days = days + Number.parseInt(vstring) * 30;
vstring = "";
break;
case 'D':
case "D":
days = days + Number.parseInt(vstring);
vstring = "";
break;
default:
throw Error("this character should not appears: " + str.charAt(i));
throw Error(
"this character should not appears: " + str.charAt(i),
);
}
}
return days;
}
};

View File

@@ -1,35 +1,61 @@
import {Address, GeographicalUnitLayer, SimpleGeographicalUnit} from "../../types";
import {fetchResults, makeFetch} from "./apiMethods";
import {
Address,
GeographicalUnitLayer,
SimpleGeographicalUnit,
} from "../../types";
import { fetchResults, makeFetch } from "./apiMethods";
export const getAddressById = async (address_id: number): Promise<Address> =>
{
const url = `/api/1.0/main/address/${address_id}.json`;
export const getAddressById = async (address_id: number): Promise<Address> => {
const url = `/api/1.0/main/address/${address_id}.json`;
const response = await fetch(url);
const response = await fetch(url);
if (response.ok) {
return response.json();
}
if (response.ok) {
return response.json();
}
throw Error('Error with request resource response');
throw Error("Error with request resource response");
};
export const getGeographicalUnitsByAddress = async (address: Address): Promise<SimpleGeographicalUnit[]> => {
return fetchResults<SimpleGeographicalUnit>(`/api/1.0/main/geographical-unit/by-address/${address.address_id}.json`);
}
export const getGeographicalUnitsByAddress = async (
address: Address,
): Promise<SimpleGeographicalUnit[]> => {
return fetchResults<SimpleGeographicalUnit>(
`/api/1.0/main/geographical-unit/by-address/${address.address_id}.json`,
);
};
export const getAllGeographicalUnitLayers = async (): Promise<GeographicalUnitLayer[]> => {
return fetchResults<GeographicalUnitLayer>(`/api/1.0/main/geographical-unit-layer.json`);
}
export const getAllGeographicalUnitLayers = async (): Promise<
GeographicalUnitLayer[]
> => {
return fetchResults<GeographicalUnitLayer>(
`/api/1.0/main/geographical-unit-layer.json`,
);
};
export const syncAddressWithReference = async (address: Address): Promise<Address> => {
return makeFetch<null, Address>("POST", `/api/1.0/main/address/reference-match/${address.address_id}/sync-with-reference`);
}
export const syncAddressWithReference = async (
address: Address,
): Promise<Address> => {
return makeFetch<null, Address>(
"POST",
`/api/1.0/main/address/reference-match/${address.address_id}/sync-with-reference`,
);
};
export const markAddressReviewed = async (address: Address): Promise<Address> => {
return makeFetch<null, Address>("POST", `/api/1.0/main/address/reference-match/${address.address_id}/set/reviewed`);
}
export const markAddressReviewed = async (
address: Address,
): Promise<Address> => {
return makeFetch<null, Address>(
"POST",
`/api/1.0/main/address/reference-match/${address.address_id}/set/reviewed`,
);
};
export const markAddressToReview = async (address: Address): Promise<Address> => {
return makeFetch<null, Address>("POST", `/api/1.0/main/address/reference-match/${address.address_id}/set/to_review`);
}
export const markAddressToReview = async (
address: Address,
): Promise<Address> => {
return makeFetch<null, Address>(
"POST",
`/api/1.0/main/address/reference-match/${address.address_id}/set/to_review`,
);
};

View File

@@ -1,58 +1,61 @@
import {Scope} from '../../types';
import { Scope } from "../../types";
export type body = Record<string, boolean|string|number|null>;
export type fetchOption = Record<string, boolean|string|number|null>;
export type body = Record<string, boolean | string | number | null>;
export type fetchOption = Record<string, boolean | string | number | null>;
export type Params = Record<string, number|string>;
export type Params = Record<string, number | string>;
export interface PaginationResponse<T> {
pagination: {
more: boolean;
items_per_page: number;
};
results: T[];
count: number;
pagination: {
more: boolean;
items_per_page: number;
};
results: T[];
count: number;
}
export type FetchParams = Record<string, string|number|null>;;
export type FetchParams = Record<string, string | number | null>;
export interface TransportExceptionInterface {
name: string;
name: string;
}
export interface ValidationExceptionInterface extends TransportExceptionInterface {
name: 'ValidationException';
error: object;
violations: string[];
titles: string[];
propertyPaths: string[];
export interface ValidationExceptionInterface
extends TransportExceptionInterface {
name: "ValidationException";
error: object;
violations: string[];
titles: string[];
propertyPaths: string[];
}
export interface ValidationErrorResponse extends TransportExceptionInterface {
violations: {
title: string;
propertyPath: string;
}[];
violations: {
title: string;
propertyPath: string;
}[];
}
export interface AccessExceptionInterface extends TransportExceptionInterface {
name: 'AccessException';
violations: string[];
name: "AccessException";
violations: string[];
}
export interface NotFoundExceptionInterface extends TransportExceptionInterface {
name: 'NotFoundException';
export interface NotFoundExceptionInterface
extends TransportExceptionInterface {
name: "NotFoundException";
}
export interface ServerExceptionInterface extends TransportExceptionInterface {
name: 'ServerException';
message: string;
code: number;
body: string;
name: "ServerException";
message: string;
code: number;
body: string;
}
export interface ConflictHttpExceptionInterface extends TransportExceptionInterface {
name: 'ConflictHttpException';
export interface ConflictHttpExceptionInterface
extends TransportExceptionInterface {
name: "ConflictHttpException";
violations: string[];
}
@@ -60,35 +63,33 @@ export interface ConflictHttpExceptionInterface extends TransportExceptionInterf
* Generic api method that can be adapted to any fetch request
*/
export const makeFetch = <Input, Output>(
method: 'POST'|'GET'|'PUT'|'PATCH'|'DELETE',
url: string, body?: body | Input | null,
options?: FetchParams
method: "POST" | "GET" | "PUT" | "PATCH" | "DELETE",
url: string,
body?: body | Input | null,
options?: FetchParams,
): Promise<Output> => {
let opts = {
method: method,
headers: {
'Content-Type': 'application/json;charset=utf-8'
"Content-Type": "application/json;charset=utf-8",
},
};
if (body !== null && typeof body !== 'undefined') {
Object.assign(opts, {body: JSON.stringify(body)})
if (body !== null && typeof body !== "undefined") {
Object.assign(opts, { body: JSON.stringify(body) });
}
if (typeof options !== 'undefined') {
if (typeof options !== "undefined") {
opts = Object.assign(opts, options);
}
return fetch(url, opts)
.then(response => {
return fetch(url, opts).then((response) => {
if (response.ok) {
return response.json();
}
if (response.status === 422) {
return response.json().then(response => {
throw ValidationException(response)
return response.json().then((response) => {
throw ValidationException(response);
});
}
@@ -101,79 +102,94 @@ export const makeFetch = <Input, Output>(
}
throw {
name: 'Exception',
name: "Exception",
sta: response.status,
txt: response.statusText,
err: new Error(),
violations: response.body
violations: response.body,
};
});
}
};
/**
* Fetch results with certain parameters
*/
function _fetchAction<T>(page: number, uri: string, params?: FetchParams): Promise<PaginationResponse<T>> {
function _fetchAction<T>(
page: number,
uri: string,
params?: FetchParams,
): Promise<PaginationResponse<T>> {
const item_per_page = 50;
const searchParams = new URLSearchParams();
searchParams.append('item_per_page', item_per_page.toString());
searchParams.append('page', page.toString());
searchParams.append("item_per_page", item_per_page.toString());
searchParams.append("page", page.toString());
if (params !== undefined) {
Object.keys(params).forEach(key => {
const v = params[key];
if (typeof v === 'string') {
searchParams.append(key, v);
} else if (typeof v === 'number') {
searchParams.append(key, v.toString());
} else if (v === null) {
searchParams.append(key, '');
}
});
Object.keys(params).forEach((key) => {
const v = params[key];
if (typeof v === "string") {
searchParams.append(key, v);
} else if (typeof v === "number") {
searchParams.append(key, v.toString());
} else if (v === null) {
searchParams.append(key, "");
}
});
}
const url = uri + '?' + searchParams.toString();
const url = uri + "?" + searchParams.toString();
return fetch(url, {
method: 'GET',
method: "GET",
headers: {
'Content-Type': 'application/json;charset=utf-8'
"Content-Type": "application/json;charset=utf-8",
},
}).then((response) => {
if (response.ok) { return response.json(); }
})
.then((response) => {
if (response.ok) {
return response.json();
}
if (response.status === 404) {
throw NotFoundException(response);
}
if (response.status === 404) {
throw NotFoundException(response);
}
if (response.status === 422) {
return response.json().then(response => {
throw ValidationException(response)
});
}
if (response.status === 422) {
return response.json().then((response) => {
throw ValidationException(response);
});
}
if (response.status === 403) {
throw AccessException(response);
}
if (response.status === 403) {
throw AccessException(response);
}
if (response.status >= 500) {
return response.text().then(body => {
throw ServerException(response.status, body);
});
}
if (response.status >= 500) {
return response.text().then((body) => {
throw ServerException(response.status, body);
});
}
throw new Error("other network error");
}).catch((reason: any) => {
console.error(reason);
throw new Error(reason);
});
};
throw new Error("other network error");
})
.catch((reason: any) => {
console.error(reason);
throw new Error(reason);
});
}
export const fetchResults = async<T> (uri: string, params?: FetchParams): Promise<T[]> => {
export const fetchResults = async <T>(
uri: string,
params?: FetchParams,
): Promise<T[]> => {
let promises: Promise<T[]>[] = [],
page = 1;
const firstData: PaginationResponse<T> = await _fetchAction(page, uri, params) as PaginationResponse<T>;
const firstData: PaginationResponse<T> = (await _fetchAction(
page,
uri,
params,
)) as PaginationResponse<T>;
promises.push(Promise.resolve(firstData.results));
@@ -181,61 +197,74 @@ export const fetchResults = async<T> (uri: string, params?: FetchParams): Promis
do {
page = ++page;
promises.push(
_fetchAction<T>(page, uri, params)
.then(r => Promise.resolve(r.results))
_fetchAction<T>(page, uri, params).then((r) =>
Promise.resolve(r.results),
),
);
} while (page * firstData.pagination.items_per_page < firstData.count)
} while (page * firstData.pagination.items_per_page < firstData.count);
}
return Promise.all(promises).then((values) => values.flat());
};
export const fetchScopes = (): Promise<Scope[]> => {
return fetchResults('/api/1.0/main/scope.json');
return fetchResults("/api/1.0/main/scope.json");
};
/**
* Error objects to be thrown
*/
const ValidationException = (response: ValidationErrorResponse): ValidationExceptionInterface => {
const ValidationException = (
response: ValidationErrorResponse,
): ValidationExceptionInterface => {
const error = {} as ValidationExceptionInterface;
error.name = 'ValidationException';
error.violations = response.violations.map((violation) => `${violation.title}: ${violation.propertyPath}`);
error.name = "ValidationException";
error.violations = response.violations.map(
(violation) => `${violation.title}: ${violation.propertyPath}`,
);
error.titles = response.violations.map((violation) => violation.title);
error.propertyPaths = response.violations.map((violation) => violation.propertyPath);
error.propertyPaths = response.violations.map(
(violation) => violation.propertyPath,
);
return error;
}
};
const AccessException = (response: Response): AccessExceptionInterface => {
const error = {} as AccessExceptionInterface;
error.name = 'AccessException';
error.violations = ['You are not allowed to perform this action'];
error.name = "AccessException";
error.violations = ["You are not allowed to perform this action"];
return error;
}
};
const NotFoundException = (response: Response): NotFoundExceptionInterface => {
const error = {} as NotFoundExceptionInterface;
error.name = 'NotFoundException';
return error;
}
const ServerException = (code: number, body: string): ServerExceptionInterface => {
const error = {} as ServerExceptionInterface;
error.name = 'ServerException';
error.code = code;
error.body = body;
return error;
}
const ConflictHttpException = (response: Response): ConflictHttpExceptionInterface => {
const error = {} as ConflictHttpExceptionInterface;
error.name = 'ConflictHttpException';
error.violations = ['Sorry, but someone else has already changed this entity. Please refresh the page and apply the changes again']
const error = {} as NotFoundExceptionInterface;
error.name = "NotFoundException";
return error;
}
};
const ServerException = (
code: number,
body: string,
): ServerExceptionInterface => {
const error = {} as ServerExceptionInterface;
error.name = "ServerException";
error.code = code;
error.body = body;
return error;
};
const ConflictHttpException = (
response: Response,
): ConflictHttpExceptionInterface => {
const error = {} as ConflictHttpExceptionInterface;
error.name = "ConflictHttpException";
error.violations = [
"Sorry, but someone else has already changed this entity. Please refresh the page and apply the changes again",
];
return error;
};

View File

@@ -1,4 +1,3 @@
// const _fetchAction = (page, uri, params) => {
// const item_per_page = 50;
// if (params === undefined) {

View File

@@ -1,6 +1,8 @@
import {fetchResults} from "./apiMethods";
import {Location, LocationType} from "../../types";
import { fetchResults } from "./apiMethods";
import { Location, LocationType } from "../../types";
export const getLocations = (): Promise<Location[]> => fetchResults('/api/1.0/main/location.json');
export const getLocations = (): Promise<Location[]> =>
fetchResults("/api/1.0/main/location.json");
export const getLocationTypes = (): Promise<LocationType[]> => fetchResults('/api/1.0/main/location-type.json');
export const getLocationTypes = (): Promise<LocationType[]> =>
fetchResults("/api/1.0/main/location-type.json");

View File

@@ -1,25 +1,24 @@
import {User} from "../../types";
import {makeFetch} from "./apiMethods";
import { User } from "../../types";
import { makeFetch } from "./apiMethods";
export const whoami = (): Promise<User> => {
const url = `/api/1.0/main/whoami.json`;
return fetch(url)
.then(response => {
if (response.ok) {
return response.json();
}
throw {
msg: 'Error while getting whoami.',
sta: response.status,
txt: response.statusText,
err: new Error(),
body: response.body
};
const url = `/api/1.0/main/whoami.json`;
return fetch(url).then((response) => {
if (response.ok) {
return response.json();
}
throw {
msg: "Error while getting whoami.",
sta: response.status,
txt: response.statusText,
err: new Error(),
body: response.body,
};
});
};
export const whereami = (): Promise<Location | null> => {
const url = `/api/1.0/main/user-current-location.json`;
const url = `/api/1.0/main/user-current-location.json`;
return makeFetch<null, Location|null>("GET", url);
}
return makeFetch<null, Location | null>("GET", url);
};

View File

@@ -1,3 +1 @@
require("./layout.scss");

View File

@@ -15,58 +15,61 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import mime from 'mime';
import mime from "mime";
export const download_report = (url, container) => {
var download_text = container.dataset.downloadText,
alias = container.dataset.alias;
var download_text = container.dataset.downloadText,
alias = container.dataset.alias;
window.fetch(url, { credentials: 'same-origin' })
.then(response => {
if (!response.ok) {
throw Error(response.statusText);
}
window
.fetch(url, { credentials: "same-origin" })
.then((response) => {
if (!response.ok) {
throw Error(response.statusText);
}
return response.blob();
}).then(blob => {
return response.blob();
})
.then((blob) => {
var content = URL.createObjectURL(blob),
link = document.createElement("a"),
type = blob.type,
hasForcedType = "mimeType" in container.dataset,
extension;
var content = URL.createObjectURL(blob),
link = document.createElement("a"),
type = blob.type,
hasForcedType = 'mimeType' in container.dataset,
extension;
if (hasForcedType) {
// force a type
type = container.dataset.mimeType;
blob = new Blob([blob], { type: type });
content = URL.createObjectURL(blob);
}
if (hasForcedType) {
// force a type
type = container.dataset.mimeType;
blob = new Blob([ blob ], { 'type': type });
content = URL.createObjectURL(blob);
}
const extensions = new Map();
extensions.set(
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"xlsx",
);
extensions.set("application/vnd.oasis.opendocument.spreadsheet", "ods");
extensions.set("application/vnd.ms-excel", "xlsx");
extensions.set("text/csv", "csv");
extensions.set("text/csv; charset=utf-8", "csv");
const extensions = new Map();
extensions.set('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'xlsx');
extensions.set('application/vnd.oasis.opendocument.spreadsheet', 'ods');
extensions.set('application/vnd.ms-excel', 'xlsx');
extensions.set('text/csv', 'csv');
extensions.set('text/csv; charset=utf-8', 'csv');
extension = extensions.get(type);
extension = extensions.get(type);
link.appendChild(document.createTextNode(download_text));
link.classList.add("btn", "btn-action");
link.href = content;
link.download = alias;
if (extension !== false) {
link.download = link.download + "." + extension;
}
container.innerHTML = "";
container.appendChild(link);
})
.catch(function (error) {
console.error(error);
var problem_text = document.createTextNode("Problem during download");
link.appendChild(document.createTextNode(download_text));
link.classList.add("btn", "btn-action");
link.href = content;
link.download = alias;
if (extension !== false) {
link.download = link.download + '.' + extension;
}
container.innerHTML = "";
container.appendChild(link);
}).catch(function(error) {
console.error(error);
var problem_text =
document.createTextNode("Problem during download");
container
.replaceChild(problem_text, container.firstChild);
});
container.replaceChild(problem_text, container.firstChild);
});
};

View File

@@ -1,4 +1,4 @@
/*
/*
* Copyright (C) 2018 Champs Libres Cooperative <info@champs-libres.coop>
*
* This program is free software: you can redistribute it and/or modify

View File

@@ -1,20 +1,22 @@
const buildLinkCreate = function (relatedEntityClass: string, relatedEntityId: number, to: number | null, returnPath: string | null): string
{
const buildLinkCreate = function (
relatedEntityClass: string,
relatedEntityId: number,
to: number | null,
returnPath: string | null,
): string {
const params = new URLSearchParams();
params.append('entityClass', relatedEntityClass);
params.append('entityId', relatedEntityId.toString());
params.append("entityClass", relatedEntityClass);
params.append("entityId", relatedEntityId.toString());
if (null !== to) {
params.append('tos[0]', to.toString());
params.append("tos[0]", to.toString());
}
if (null !== returnPath) {
params.append('returnPath', returnPath);
params.append("returnPath", returnPath);
}
return `/fr/notification/create?${params.toString()}`;
}
};
export {
buildLinkCreate,
}
export { buildLinkCreate };

View File

@@ -1,12 +1,14 @@
const buildLinkCreate = function(workflowName, relatedEntityClass, relatedEntityId) {
let params = new URLSearchParams();
params.set('entityClass', relatedEntityClass);
params.set('entityId', relatedEntityId);
params.set('workflow', workflowName);
const buildLinkCreate = function (
workflowName,
relatedEntityClass,
relatedEntityId,
) {
let params = new URLSearchParams();
params.set("entityClass", relatedEntityClass);
params.set("entityId", relatedEntityId);
params.set("workflow", workflowName);
return `/fr/main/workflow/create?`+params.toString();
return `/fr/main/workflow/create?` + params.toString();
};
export {
buildLinkCreate,
};
export { buildLinkCreate };

View File

@@ -1,36 +1,30 @@
window.addEventListener('load', function (e) {
var
postalCodes = document.querySelectorAll('[data-select-interactive-loading]')
;
for (let i = 0; i < postalCodes.length; i++) {
let
searchUrl = postalCodes[i].dataset.searchUrl,
noResultsLabel = postalCodes[i].dataset.noResultsLabel,
errorLoadLabel = postalCodes[i].dataset.errorLoadLabel,
searchingLabel = postalCodes[i].dataset.searchingLabel
;
$(postalCodes[i]).select2({
allowClear: true,
language: {
errorLoading: function () {
return errorLoadLabel;
},
noResults: function () {
return noResultsLabel;
},
searching: function () {
return searchingLabel;
}
},
ajax: {
url: searchUrl,
dataType: 'json',
delay: 250
}
});
}
window.addEventListener("load", function (e) {
var postalCodes = document.querySelectorAll(
"[data-select-interactive-loading]",
);
for (let i = 0; i < postalCodes.length; i++) {
let searchUrl = postalCodes[i].dataset.searchUrl,
noResultsLabel = postalCodes[i].dataset.noResultsLabel,
errorLoadLabel = postalCodes[i].dataset.errorLoadLabel,
searchingLabel = postalCodes[i].dataset.searchingLabel;
$(postalCodes[i]).select2({
allowClear: true,
language: {
errorLoading: function () {
return errorLoadLabel;
},
noResults: function () {
return noResultsLabel;
},
searching: function () {
return searchingLabel;
},
},
ajax: {
url: searchUrl,
dataType: "json",
delay: 250,
},
});
}
});

View File

@@ -1,5 +1,5 @@
//require("./show_hide.js");
import { ShowHide } from './show_hide.js'
import { ShowHide } from "./show_hide.js";
export { ShowHide }
export { ShowHide };

View File

@@ -16,132 +16,137 @@
*
* @param object options
*/
var ShowHide = function(options) {
var
froms = typeof options.froms[Symbol.iterator] === "function" ? options.froms : [ options.froms ], //options.froms;
test = options.test,
container = typeof options.container[Symbol.iterator] === "function" ? options.container : [ options.container ],
is_shown = true,
event_name = 'event_name' in options ? options.event_name : 'change',
container_content = [],
debug = 'debug' in options ? options.debug : false,
load_event = 'load_event' in options ? options.load_event : 'load',
id = 'uid' in options ? options.id : Math.random(),
toggle_callback = 'toggle_callback' in options ? options.toggle_callback : null
;
var bootstrap = function(event) {
if (debug) {
console.log('debug is activated on this show-hide', this);
}
// keep the content in memory
for (let c of container.values()) {
let contents = [];
for (let el of c.childNodes.values()) {
contents.push(el);
}
container_content.push(contents);
}
// attach the listener on each input
for (let f of froms.values()) {
let
inputs = f.querySelectorAll('input'),
selects = f.querySelectorAll('select');
for (let input of inputs.values()) {
if (debug) {
console.log('attaching event to input', input);
}
input.addEventListener(event_name, function(e) {
onChange(e);
});
}
for (let input of selects.values()) {
if (debug) {
console.log('attaching event to selects', input);
}
input.addEventListener(event_name, function(e) {
onChange(e);
});
}
}
// first launch of the show/hide
onChange(event);
};
var onChange = function (event) {
var result = test(froms, event), me;
if (result === true) {
if (is_shown === false) {
forceShow();
me = new CustomEvent('show-hide-show', { detail: { id: id, container: container, froms: froms } });
window.dispatchEvent(me);
}
} else if (result === false) {
if (is_shown) {
forceHide();
me = new CustomEvent('show-hide-hide', { detail: { id: id, container: container, froms: froms } });
window.dispatchEvent(me);
}
} else {
throw "the result of test is not a boolean";
}
};
var forceHide = function() {
if (debug) {
console.log('force hide');
}
if (toggle_callback !== null) {
toggle_callback(container, 'hide');
} else {
for (let contents of container_content.values()) {
for (let el of contents.values()) {
el.remove();
}
}
}
is_shown = false;
};
var forceShow = function() {
if (debug) {
console.log('show');
}
if (toggle_callback !== null) {
toggle_callback(container, 'show');
} else {
for (let i of container_content.keys()) {
var contents = container_content[i];
for (let el of contents.values()) {
container[i].appendChild(el);
}
}
}
is_shown = true;
};
var forceCompute = function(event) {
onChange(event);
};
if (load_event !== null) {
window.addEventListener('load', bootstrap);
} else {
bootstrap(null);
var ShowHide = function (options) {
var froms =
typeof options.froms[Symbol.iterator] === "function"
? options.froms
: [options.froms], //options.froms;
test = options.test,
container =
typeof options.container[Symbol.iterator] === "function"
? options.container
: [options.container],
is_shown = true,
event_name = "event_name" in options ? options.event_name : "change",
container_content = [],
debug = "debug" in options ? options.debug : false,
load_event = "load_event" in options ? options.load_event : "load",
id = "uid" in options ? options.id : Math.random(),
toggle_callback =
"toggle_callback" in options ? options.toggle_callback : null;
var bootstrap = function (event) {
if (debug) {
console.log("debug is activated on this show-hide", this);
}
// keep the content in memory
for (let c of container.values()) {
let contents = [];
for (let el of c.childNodes.values()) {
contents.push(el);
}
container_content.push(contents);
}
return {
forceHide: forceHide,
forceShow: forceShow,
forceCompute: forceCompute,
};
// attach the listener on each input
for (let f of froms.values()) {
let inputs = f.querySelectorAll("input"),
selects = f.querySelectorAll("select");
for (let input of inputs.values()) {
if (debug) {
console.log("attaching event to input", input);
}
input.addEventListener(event_name, function (e) {
onChange(e);
});
}
for (let input of selects.values()) {
if (debug) {
console.log("attaching event to selects", input);
}
input.addEventListener(event_name, function (e) {
onChange(e);
});
}
}
// first launch of the show/hide
onChange(event);
};
var onChange = function (event) {
var result = test(froms, event),
me;
if (result === true) {
if (is_shown === false) {
forceShow();
me = new CustomEvent("show-hide-show", {
detail: { id: id, container: container, froms: froms },
});
window.dispatchEvent(me);
}
} else if (result === false) {
if (is_shown) {
forceHide();
me = new CustomEvent("show-hide-hide", {
detail: { id: id, container: container, froms: froms },
});
window.dispatchEvent(me);
}
} else {
throw "the result of test is not a boolean";
}
};
var forceHide = function () {
if (debug) {
console.log("force hide");
}
if (toggle_callback !== null) {
toggle_callback(container, "hide");
} else {
for (let contents of container_content.values()) {
for (let el of contents.values()) {
el.remove();
}
}
}
is_shown = false;
};
var forceShow = function () {
if (debug) {
console.log("show");
}
if (toggle_callback !== null) {
toggle_callback(container, "show");
} else {
for (let i of container_content.keys()) {
var contents = container_content[i];
for (let el of contents.values()) {
container[i].appendChild(el);
}
}
}
is_shown = true;
};
var forceCompute = function (event) {
onChange(event);
};
if (load_event !== null) {
window.addEventListener("load", bootstrap);
} else {
bootstrap(null);
}
return {
forceHide: forceHide,
forceShow: forceShow,
forceCompute: forceCompute,
};
};
export { ShowHide };

View File

@@ -1,118 +1,104 @@
/*
* Remove active class on both elements: link and content
*/
let resetActive = function(links, contents)
{
for (items of [links, contents]) {
items.forEach(function(item) {
if (item.classList.contains('active')) {
item.classList.remove('active');
}
});
}
}
let resetActive = function (links, contents) {
for (items of [links, contents]) {
items.forEach(function (item) {
if (item.classList.contains("active")) {
item.classList.remove("active");
}
});
}
};
/*
* Count links array and return rank of given link
*/
let countNewActive = function(links, link)
{
let rank = 0;
for (let i = 0; i < links.length; ++i) {
rank++;
if(links[i] == link) {
return rank;
}
let countNewActive = function (links, link) {
let rank = 0;
for (let i = 0; i < links.length; ++i) {
rank++;
if (links[i] == link) {
return rank;
}
}
}
};
/*
* Set class active on both new elements: link and content
*/
let setNewActive = function(links, contents, rank)
{
if (! links[rank-1]) { rank = 1; }
link = links[rank-1];
let setNewActive = function (links, contents, rank) {
if (!links[rank - 1]) {
rank = 1;
}
link = links[rank - 1];
link.classList.add('active');
link.classList.add("active");
count = 0;
contents.forEach(function(pane) {
count++;
if (rank == count) {
pane.classList.add('active');
}
});
}
count = 0;
contents.forEach(function (pane) {
count++;
if (rank == count) {
pane.classList.add("active");
}
});
};
/*
* Set height of content pane
*/
let setPaneHeight = function(contents)
{
contents.forEach(function(pane) {
// let computedStyle = getComputedStyle(pane);
// console.log(computedStyle.height);
// comment prendre la hauteur d'une div masquée avec display:none
});
}
let setPaneHeight = function (contents) {
contents.forEach(function (pane) {
// let computedStyle = getComputedStyle(pane);
// console.log(computedStyle.height);
// comment prendre la hauteur d'une div masquée avec display:none
});
};
/*
* Check if links are defined in controller
* If true, disable javascript listener
*/
let isLinkRef = function(link) {
if (link.getAttribute('href') == "#") {
return false;
}
return true;
}
let isLinkRef = function (link) {
if (link.getAttribute("href") == "#") {
return false;
}
return true;
};
/*
* Main function
*/
window.addEventListener('load', function()
{
tabParams.forEach(function(unit) {
let tabPanel = document.querySelector('#'+ unit.id );
if (tabPanel) {
window.addEventListener("load", function () {
tabParams.forEach(function (unit) {
let tabPanel = document.querySelector("#" + unit.id);
if (tabPanel) {
let nav = tabPanel.querySelector("nav"),
tabs = nav.querySelectorAll("ul.nav-tabs li.nav-item"),
links = nav.querySelectorAll("ul.nav-tabs li.nav-item a.nav-link"),
contents = tabPanel.querySelectorAll("div.tab-content div.tab-pane");
if (unit.type == "pill") {
tabPanel.classList.add("pills");
}
let
nav = tabPanel.querySelector('nav'),
tabs = nav.querySelectorAll('ul.nav-tabs li.nav-item'),
links = nav.querySelectorAll('ul.nav-tabs li.nav-item a.nav-link'),
contents = tabPanel.querySelectorAll('div.tab-content div.tab-pane')
;
if (!unit.initPane) {
unit.initPane = 1;
}
if (unit.type == 'pill') {
tabPanel.classList.add('pills');
}
setPaneHeight(contents);
if (! unit.initPane) {
unit.initPane = 1;
}
setPaneHeight(contents);
// initial position
setNewActive(links, contents, unit.initPane);
// listen
links.forEach(function(link) {
if (isLinkRef(link) == false) {
link.addEventListener('click', function()
{
resetActive(links, contents);
setNewActive(links, contents, countNewActive(links, link));
});
}
});
// initial position
setNewActive(links, contents, unit.initPane);
// listen
links.forEach(function (link) {
if (isLinkRef(link) == false) {
link.addEventListener("click", function () {
resetActive(links, contents);
setNewActive(links, contents, countNewActive(links, link));
});
}
});
});
});
}
});
});

View File

@@ -1,39 +1,49 @@
import AddressDetailsButton from "../../vuejs/_components/AddressDetails/AddressDetailsButton.vue";
import {createApp} from "vue";
import {createI18n} from "vue-i18n";
import {_createI18n} from "../../vuejs/_js/i18n";
import {Address} from "../../types";
import { createApp } from "vue";
import { createI18n } from "vue-i18n";
import { _createI18n } from "../../vuejs/_js/i18n";
import { Address } from "../../types";
const i18n = _createI18n({});
document.querySelectorAll<HTMLSpanElement>('span[data-address-details]').forEach((el) => {
const dataset = el.dataset as {
addressId: string,
addressRefStatus: string,
};
document
.querySelectorAll<HTMLSpanElement>("span[data-address-details]")
.forEach((el) => {
const dataset = el.dataset as {
addressId: string;
addressRefStatus: string;
};
const app = createApp({
components: {AddressDetailsButton},
data() {
return {
addressId: Number.parseInt(dataset.addressId),
addressRefStatus: dataset.addressRefStatus,
}
},
template: '<address-details-button :address_id="addressId" :address_ref_status="addressRefStatus" @update-address="onUpdateAddress"></address-details-button>',
methods: {
onUpdateAddress: (address: Address): void => {
if (address.refStatus === 'to_review' || address.refStatus === 'reviewed') {
// in this two case, the address content do not change
return;
}
if (window.confirm("L'adresse a été modifiée. Vous pouvez continuer votre travail. Cependant, pour afficher les données immédiatement, veuillez recharger la page. \n\n Voulez-vous recharger la page immédiatement ?")) {
window.location.reload();
}
}
}
});
const app = createApp({
components: { AddressDetailsButton },
data() {
return {
addressId: Number.parseInt(dataset.addressId),
addressRefStatus: dataset.addressRefStatus,
};
},
template:
'<address-details-button :address_id="addressId" :address_ref_status="addressRefStatus" @update-address="onUpdateAddress"></address-details-button>',
methods: {
onUpdateAddress: (address: Address): void => {
if (
address.refStatus === "to_review" ||
address.refStatus === "reviewed"
) {
// in this two case, the address content do not change
return;
}
if (
window.confirm(
"L'adresse a été modifiée. Vous pouvez continuer votre travail. Cependant, pour afficher les données immédiatement, veuillez recharger la page. \n\n Voulez-vous recharger la page immédiatement ?",
)
) {
window.location.reload();
}
},
},
});
app.use(i18n);
app.mount(el);
});
app.use(i18n);
app.mount(el);
});

View File

@@ -1,20 +1,20 @@
require('./blur.scss');
require("./blur.scss");
document.querySelectorAll('.confidential').forEach(function (el) {
let i = document.createElement('i');
const classes = ['fa', 'fa-eye-slash', 'toggle-twig'];
i.classList.add(...classes);
el.appendChild(i);
document.querySelectorAll(".confidential").forEach(function (el) {
let i = document.createElement("i");
const classes = ["fa", "fa-eye-slash", "toggle-twig"];
i.classList.add(...classes);
el.appendChild(i);
const toggleBlur = function(e) {
for (let child of el.children) {
if (!child.classList.contains('toggle-twig')) {
child.classList.toggle('blur');
}
}
i.classList.toggle('fa-eye-slash');
i.classList.toggle('fa-eye');
const toggleBlur = function (e) {
for (let child of el.children) {
if (!child.classList.contains("toggle-twig")) {
child.classList.toggle("blur");
}
}
i.addEventListener('click', toggleBlur);
toggleBlur();
});
i.classList.toggle("fa-eye-slash");
i.classList.toggle("fa-eye");
};
i.addEventListener("click", toggleBlur);
toggleBlur();
});

View File

@@ -2,62 +2,65 @@
//require('bootstrap/scss/bootstrap.scss')
// Or compile bootstrap only enabled assets
require('./bootstrap.scss');
require("./bootstrap.scss");
// You can specify which plugins you need
import Dropdown from 'bootstrap/js/src/dropdown';
import Modal from 'bootstrap/js/dist/modal';
import Collapse from 'bootstrap/js/src/collapse';
import Carousel from 'bootstrap/js/src/carousel';
import Popover from 'bootstrap/js/src/popover';
import 'bootstrap-icons/font/bootstrap-icons.css';
import Dropdown from "bootstrap/js/src/dropdown";
import Modal from "bootstrap/js/dist/modal";
import Collapse from "bootstrap/js/src/collapse";
import Carousel from "bootstrap/js/src/carousel";
import Popover from "bootstrap/js/src/popover";
import "bootstrap-icons/font/bootstrap-icons.css";
//
// Carousel: ACHeaderSlider is a small slider used in banner of AccompanyingCourse Section
// Initialize options, and show/hide controls in first/last slides
//
let ACHeaderSlider = document.querySelector('#ACHeaderSlider');
let ACHeaderSlider = document.querySelector("#ACHeaderSlider");
if (ACHeaderSlider) {
let controlPrev = ACHeaderSlider.querySelector('button[data-bs-slide="prev"]'),
controlNext = ACHeaderSlider.querySelector('button[data-bs-slide="next"]'),
length = ACHeaderSlider.querySelectorAll('.carousel-item').length,
last = length-1,
carousel = new Carousel(ACHeaderSlider, {
interval: false,
wrap: false,
ride: false,
keyboard: false,
touch: true
})
;
document.addEventListener('DOMContentLoaded', (e) => {
controlNext.classList.remove('visually-hidden');
let controlPrev = ACHeaderSlider.querySelector(
'button[data-bs-slide="prev"]',
),
controlNext = ACHeaderSlider.querySelector('button[data-bs-slide="next"]'),
length = ACHeaderSlider.querySelectorAll(".carousel-item").length,
last = length - 1,
carousel = new Carousel(ACHeaderSlider, {
interval: false,
wrap: false,
ride: false,
keyboard: false,
touch: true,
});
ACHeaderSlider.addEventListener('slid.bs.carousel', (e) => {
//console.log('from slide', e.direction, e.relatedTarget, e.from, e.to );
switch (e.to) {
case 0:
controlPrev.classList.add('visually-hidden');
controlNext.classList.remove('visually-hidden');
break;
case last:
controlPrev.classList.remove('visually-hidden');
controlNext.classList.add('visually-hidden');
break;
default:
controlPrev.classList.remove('visually-hidden');
controlNext.classList.remove('visually-hidden');
}
})
document.addEventListener("DOMContentLoaded", (e) => {
controlNext.classList.remove("visually-hidden");
});
ACHeaderSlider.addEventListener("slid.bs.carousel", (e) => {
//console.log('from slide', e.direction, e.relatedTarget, e.from, e.to );
switch (e.to) {
case 0:
controlPrev.classList.add("visually-hidden");
controlNext.classList.remove("visually-hidden");
break;
case last:
controlPrev.classList.remove("visually-hidden");
controlNext.classList.add("visually-hidden");
break;
default:
controlPrev.classList.remove("visually-hidden");
controlNext.classList.remove("visually-hidden");
}
});
}
//
// Popover: used in workflow breadcrumb,
// (expected in: contextual help, notification-box, workflow-box )
//
const triggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'));
const triggerList = [].slice.call(
document.querySelectorAll('[data-bs-toggle="popover"]'),
);
const popoverList = triggerList.map(function (el) {
return new Popover(el, {
html: true,
});
return new Popover(el, {
html: true,
});
});

View File

@@ -1,13 +1,13 @@
import ClassicEditorBase from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
import EssentialsPlugin from '@ckeditor/ckeditor5-essentials/src/essentials';
import MarkdownPlugin from '@ckeditor/ckeditor5-markdown-gfm/src/markdown';
import BoldPlugin from '@ckeditor/ckeditor5-basic-styles/src/bold';
import ItalicPlugin from '@ckeditor/ckeditor5-basic-styles/src/italic';
import BlockQuotePlugin from '@ckeditor/ckeditor5-block-quote/src/blockquote';
import HeadingPlugin from '@ckeditor/ckeditor5-heading/src/heading';
import LinkPlugin from '@ckeditor/ckeditor5-link/src/link';
import ListPlugin from '@ckeditor/ckeditor5-list/src/list';
import ParagraphPlugin from '@ckeditor/ckeditor5-paragraph/src/paragraph';
import ClassicEditorBase from "@ckeditor/ckeditor5-editor-classic/src/classiceditor";
import EssentialsPlugin from "@ckeditor/ckeditor5-essentials/src/essentials";
import MarkdownPlugin from "@ckeditor/ckeditor5-markdown-gfm/src/markdown";
import BoldPlugin from "@ckeditor/ckeditor5-basic-styles/src/bold";
import ItalicPlugin from "@ckeditor/ckeditor5-basic-styles/src/italic";
import BlockQuotePlugin from "@ckeditor/ckeditor5-block-quote/src/blockquote";
import HeadingPlugin from "@ckeditor/ckeditor5-heading/src/heading";
import LinkPlugin from "@ckeditor/ckeditor5-link/src/link";
import ListPlugin from "@ckeditor/ckeditor5-list/src/list";
import ParagraphPlugin from "@ckeditor/ckeditor5-paragraph/src/paragraph";
import "./index.scss";
export default class ClassicEditor extends ClassicEditorBase {}
@@ -21,23 +21,23 @@ ClassicEditor.builtinPlugins = [
HeadingPlugin,
LinkPlugin,
ListPlugin,
ParagraphPlugin
ParagraphPlugin,
];
ClassicEditor.defaultConfig = {
toolbar: {
items: [
'heading',
'|',
'bold',
'italic',
'link',
'bulletedList',
'numberedList',
'blockQuote',
'undo',
'redo'
]
"heading",
"|",
"bold",
"italic",
"link",
"bulletedList",
"numberedList",
"blockQuote",
"undo",
"redo",
],
},
language: 'fr',
language: "fr",
};

View File

@@ -1,15 +1,14 @@
import ClassicEditor from "./editor_config";
const ckeditorFields: NodeListOf<HTMLTextAreaElement> = document.querySelectorAll('textarea[ckeditor]');
const ckeditorFields: NodeListOf<HTMLTextAreaElement> =
document.querySelectorAll("textarea[ckeditor]");
ckeditorFields.forEach((field: HTMLTextAreaElement): void => {
ClassicEditor
.create( field )
.then( editor => {
ClassicEditor.create(field)
.then((editor) => {
//console.log( 'CkEditor was initialized', editor );
})
.catch( error => {
console.error( error.stack );
})
;
.catch((error) => {
console.error(error.stack);
});
});
//Fields.push.apply(Fields, document.querySelectorAll('.cf-fields textarea'));

View File

@@ -28,7 +28,7 @@
* });
* ```
*/
import './collection.scss';
import "./collection.scss";
export class CollectionEventPayload {
collection: HTMLUListElement;
@@ -41,27 +41,31 @@ export class CollectionEventPayload {
}
export const handleAdd = (button: any): void => {
const
form_name = button.dataset.collectionAddTarget,
const form_name = button.dataset.collectionAddTarget,
prototype = button.dataset.formPrototype,
collection: HTMLUListElement | null = document.querySelector('ul[data-collection-name="' + form_name + '"]');
collection: HTMLUListElement | null = document.querySelector(
'ul[data-collection-name="' + form_name + '"]',
);
if (collection === null) {
return;
}
const
empty_explain: HTMLLIElement | null = collection.querySelector('li[data-collection-empty-explain]'),
entry = document.createElement('li'),
counter = collection.querySelectorAll('li.entry').length, // Updated counter logic
const empty_explain: HTMLLIElement | null = collection.querySelector(
"li[data-collection-empty-explain]",
),
entry = document.createElement("li"),
counter = collection.querySelectorAll("li.entry").length, // Updated counter logic
content = prototype.replace(/__name__/g, counter.toString()),
event = new CustomEvent('collection-add-entry', {detail: new CollectionEventPayload(collection, entry)});
event = new CustomEvent("collection-add-entry", {
detail: new CollectionEventPayload(collection, entry),
});
console.log(counter)
console.log(content)
console.log(counter);
console.log(content);
entry.innerHTML = content;
entry.classList.add('entry');
entry.classList.add("entry");
if ("collectionRegular" in collection.dataset) {
initializeRemove(collection, entry);
@@ -75,7 +79,10 @@ export const handleAdd = (button: any): void => {
window.dispatchEvent(event);
};
const initializeRemove = (collection: HTMLUListElement, entry: HTMLLIElement): void => {
const initializeRemove = (
collection: HTMLUListElement,
entry: HTMLLIElement,
): void => {
const button = buildRemoveButton(collection, entry);
if (null === button) {
return;
@@ -83,21 +90,24 @@ const initializeRemove = (collection: HTMLUListElement, entry: HTMLLIElement): v
entry.appendChild(button);
};
export const buildRemoveButton = (collection: HTMLUListElement, entry: HTMLLIElement): HTMLButtonElement|null => {
export const buildRemoveButton = (
collection: HTMLUListElement,
entry: HTMLLIElement,
): HTMLButtonElement | null => {
const button = document.createElement("button"),
isPersisted = entry.dataset.collectionIsPersisted || "",
content = collection.dataset.collectionButtonRemoveLabel || "",
allowDelete = collection.dataset.collectionAllowDelete || "",
event = new CustomEvent("collection-remove-entry", {
detail: new CollectionEventPayload(collection, entry),
});
const
button = document.createElement('button'),
isPersisted = entry.dataset.collectionIsPersisted || '',
content = collection.dataset.collectionButtonRemoveLabel || '',
allowDelete = collection.dataset.collectionAllowDelete || '',
event = new CustomEvent('collection-remove-entry', {detail: new CollectionEventPayload(collection, entry)});
if (allowDelete === '0' && isPersisted === '1') {
if (allowDelete === "0" && isPersisted === "1") {
return null;
}
button.classList.add('btn', 'btn-delete', 'remove-entry');
button.classList.add("btn", "btn-delete", "remove-entry");
button.textContent = content;
button.addEventListener('click', (e: Event) => {
button.addEventListener("click", (e: Event) => {
e.preventDefault();
entry.remove();
collection.dispatchEvent(event);
@@ -105,22 +115,26 @@ export const buildRemoveButton = (collection: HTMLUListElement, entry: HTMLLIEle
});
return button;
}
};
window.addEventListener('load', () => {
const
addButtons: NodeListOf<HTMLButtonElement> = document.querySelectorAll("button[data-collection-add-target]"),
collections: NodeListOf<HTMLUListElement> = document.querySelectorAll("ul[data-collection-regular]");
window.addEventListener("load", () => {
const addButtons: NodeListOf<HTMLButtonElement> = document.querySelectorAll(
"button[data-collection-add-target]",
),
collections: NodeListOf<HTMLUListElement> = document.querySelectorAll(
"ul[data-collection-regular]",
);
for (let i = 0; i < addButtons.length; i++) {
const addButton = addButtons[i];
addButton.addEventListener('click', (e: Event) => {
addButton.addEventListener("click", (e: Event) => {
e.preventDefault();
handleAdd(e.target);
});
}
for (let i = 0; i < collections.length; i++) {
const entries: NodeListOf<HTMLLIElement> = collections[i].querySelectorAll(':scope > li');
const entries: NodeListOf<HTMLLIElement> =
collections[i].querySelectorAll(":scope > li");
for (let j = 0; j < entries.length; j++) {
if (entries[j].dataset.collectionEmptyExplain === "1") {
continue;

View File

@@ -3,14 +3,11 @@
* ID 'create-form' must be added to submit forms.
*/
var form = document.getElementById('create-form');
var form = document.getElementById("create-form");
var submitButtons = document.querySelectorAll("[type=submit]");
form.addEventListener('submit', function(e){
for(var i=0; i<submitButtons.length; i++){
submitButtons[i].disabled = true;
}
})
form.addEventListener("submit", function (e) {
for (var i = 0; i < submitButtons.length; i++) {
submitButtons[i].disabled = true;
}
});

View File

@@ -1,34 +1,32 @@
import { createApp } from "vue";
import ListWorkflowModalVue from 'ChillMainAssets/vuejs/_components/EntityWorkflow/ListWorkflowModal.vue';
import ListWorkflowModalVue from "ChillMainAssets/vuejs/_components/EntityWorkflow/ListWorkflowModal.vue";
import { _createI18n } from "ChillMainAssets/vuejs/_js/i18n";
const i18n = _createI18n({});
// list workflow
document.querySelectorAll('[data-list-workflows]')
.forEach(function (el) {
const app = {
components: {
ListWorkflowModalVue,
},
template:
'<list-workflow-modal-vue ' +
':workflows="workflows" ' +
':allowCreate="allowCreate" ' +
':relatedEntityClass="relatedEntityClass" ' +
':relatedEntityId="relatedEntityId" ' +
':workflowsAvailables="workflowsAvailables" ' +
'></list-workflow-modal-vue>',
data() {
return {
workflows: JSON.parse(el.dataset.workflows),
allowCreate: el.dataset.allowCreate === "1",
relatedEntityClass: el.dataset.relatedEntityClass,
relatedEntityId: Number.parseInt(el.dataset.relatedEntityId),
workflowsAvailables: JSON.parse(el.dataset.workflowsAvailables),
}
}
};
createApp(app).use(i18n).mount(el);
})
;
document.querySelectorAll("[data-list-workflows]").forEach(function (el) {
const app = {
components: {
ListWorkflowModalVue,
},
template:
"<list-workflow-modal-vue " +
':workflows="workflows" ' +
':allowCreate="allowCreate" ' +
':relatedEntityClass="relatedEntityClass" ' +
':relatedEntityId="relatedEntityId" ' +
':workflowsAvailables="workflowsAvailables" ' +
"></list-workflow-modal-vue>",
data() {
return {
workflows: JSON.parse(el.dataset.workflows),
allowCreate: el.dataset.allowCreate === "1",
relatedEntityClass: el.dataset.relatedEntityClass,
relatedEntityId: Number.parseInt(el.dataset.relatedEntityId),
workflowsAvailables: JSON.parse(el.dataset.workflowsAvailables),
};
},
};
createApp(app).use(i18n).mount(el);
});

View File

@@ -1,32 +1,33 @@
import {createApp} from "vue";
import EntityWorkflowVueSubscriber from 'ChillMainAssets/vuejs/_components/EntityWorkflow/EntityWorkflowVueSubscriber.vue';
import { _createI18n } from 'ChillMainAssets/vuejs/_js/i18n';
import { appMessages } from 'ChillMainAssets/vuejs/PickEntity/i18n';
import { createApp } from "vue";
import EntityWorkflowVueSubscriber from "ChillMainAssets/vuejs/_components/EntityWorkflow/EntityWorkflowVueSubscriber.vue";
import { _createI18n } from "ChillMainAssets/vuejs/_js/i18n";
import { appMessages } from "ChillMainAssets/vuejs/PickEntity/i18n";
const i18n = _createI18n(appMessages);
let containers = document.querySelectorAll('[data-entity-workflow-subscribe]');
let containers = document.querySelectorAll("[data-entity-workflow-subscribe]");
containers.forEach(container => {
let app = {
components: {
EntityWorkflowVueSubscriber,
},
template: '<entity-workflow-vue-subscriber :entityWorkflowId="this.entityWorkflowId" :subscriberStep="this.subscriberStep" :subscriberFinal="this.subscriberFinal" @subscriptionUpdated="onUpdate"></entity-workflow-vue-subscriber>',
data() {
return {
entityWorkflowId: Number.parseInt(container.dataset.entityWorkflowId),
subscriberStep: container.dataset.subscribeStep === "1",
subscriberFinal: container.dataset.subscribeFinal === "1",
}
},
methods: {
onUpdate(status) {
this.subscriberStep = status.step;
this.subscriberFinal = status.final;
}
}
}
containers.forEach((container) => {
let app = {
components: {
EntityWorkflowVueSubscriber,
},
template:
'<entity-workflow-vue-subscriber :entityWorkflowId="this.entityWorkflowId" :subscriberStep="this.subscriberStep" :subscriberFinal="this.subscriberFinal" @subscriptionUpdated="onUpdate"></entity-workflow-vue-subscriber>',
data() {
return {
entityWorkflowId: Number.parseInt(container.dataset.entityWorkflowId),
subscriberStep: container.dataset.subscribeStep === "1",
subscriberFinal: container.dataset.subscribeFinal === "1",
};
},
methods: {
onUpdate(status) {
this.subscriberStep = status.step;
this.subscriberFinal = status.final;
},
},
};
createApp(app).use(i18n).mount(container);
})
createApp(app).use(i18n).mount(container);
});

View File

@@ -1,2 +1,2 @@
// Compile all Fork-Awesome fonts assets from node-modules
require('fork-awesome/scss/fork-awesome.scss');
require("fork-awesome/scss/fork-awesome.scss");

View File

@@ -1 +1 @@
import './index.scss';
import "./index.scss";

View File

@@ -60,4 +60,3 @@ window.addEventListener("DOMContentLoaded", function (e) {
.mount(el);
});
});

View File

@@ -8,7 +8,7 @@ document.addEventListener("DOMContentLoaded", function () {
const elements = document.querySelectorAll(".notification_all_read");
elements.forEach((element) => {
console.log('launch');
console.log("launch");
createApp({
template: `<notification-read-all-toggle @markAsRead="markAsRead" @markAsUnRead="markAsUnread"></notification-read-all-toggle>`,
components: {
@@ -16,24 +16,28 @@ document.addEventListener("DOMContentLoaded", function () {
},
methods: {
markAsRead(id: number) {
const el = document.querySelector<HTMLDivElement>(`div.notification-status[data-notification-id="${id}"]`);
const el = document.querySelector<HTMLDivElement>(
`div.notification-status[data-notification-id="${id}"]`,
);
if (el === null) {
return;
}
el.classList.add('read');
el.classList.remove('unread');
el.classList.add("read");
el.classList.remove("unread");
},
markAsUnread(id: number) {
const el = document.querySelector<HTMLDivElement>(`div.notification-status[data-notification-id="${id}"]`);
const el = document.querySelector<HTMLDivElement>(
`div.notification-status[data-notification-id="${id}"]`,
);
if (el === null) {
return;
}
el.classList.remove('read');
el.classList.add('unread');
el.classList.remove("read");
el.classList.add("unread");
},
}
},
})
.use(i18n)
.mount(element);
.use(i18n)
.mount(element);
});
});

View File

@@ -1,7 +1,7 @@
import { createApp } from 'vue';
import PickEntity from 'ChillMainAssets/vuejs/PickEntity/PickEntity.vue';
import { _createI18n } from 'ChillMainAssets/vuejs/_js/i18n';
import { appMessages } from 'ChillMainAssets/vuejs/PickEntity/i18n';
import { createApp } from "vue";
import PickEntity from "ChillMainAssets/vuejs/PickEntity/PickEntity.vue";
import { _createI18n } from "ChillMainAssets/vuejs/_js/i18n";
import { appMessages } from "ChillMainAssets/vuejs/PickEntity/i18n";
const i18n = _createI18n(appMessages);
@@ -9,155 +9,169 @@ let appsOnPage = new Map();
let appsPerInput = new Map();
function loadDynamicPicker(element) {
let apps = element.querySelectorAll('[data-module="pick-dynamic"]');
let apps = element.querySelectorAll('[data-module="pick-dynamic"]');
apps.forEach(function (el) {
const isMultiple = parseInt(el.dataset.multiple) === 1,
uniqId = el.dataset.uniqid,
input = element.querySelector(
'[data-input-uniqid="' + el.dataset.uniqid + '"]',
),
// the "picked" will always be an array, even if multiple is false
picked = isMultiple
? JSON.parse(input.value)
: input.value === "[]" || input.value === ""
? null
: [JSON.parse(input.value)];
(suggested = JSON.parse(el.dataset.suggested)),
(as_id = parseInt(el.dataset.asId) === 1),
(submit_on_adding_new_entity =
parseInt(el.dataset.submitOnAddingNewEntity) === 1);
label = el.dataset.label;
apps.forEach(function(el) {
if (!isMultiple) {
if (input.value === "[]") {
input.value = null;
}
}
const
isMultiple = parseInt(el.dataset.multiple) === 1,
uniqId = el.dataset.uniqid,
input = element.querySelector('[data-input-uniqid="'+ el.dataset.uniqid +'"]'),
// the "picked" will always be an array, even if multiple is false
picked = isMultiple ?
JSON.parse(input.value) : (
(input.value === '[]' || input.value === '') ?
null : [ JSON.parse(input.value) ]
)
suggested = JSON.parse(el.dataset.suggested),
as_id = parseInt(el.dataset.asId) === 1,
submit_on_adding_new_entity = parseInt(el.dataset.submitOnAddingNewEntity) === 1
label = el.dataset.label;
if (!isMultiple) {
if (input.value === '[]'){
input.value = null;
}
}
const app = createApp({
template: '<pick-entity ' +
':multiple="multiple" ' +
':types="types" ' +
':picked="picked" ' +
':uniqid="uniqid" ' +
':suggested="notPickedSuggested" ' +
':label="label" ' +
'@addNewEntity="addNewEntity" ' +
'@removeEntity="removeEntity"></pick-entity>',
components: {
PickEntity,
},
data() {
return {
multiple: isMultiple,
types: JSON.parse(el.dataset.types),
picked: picked === null ? [] : picked,
uniqid: el.dataset.uniqid,
suggested,
as_id,
submit_on_adding_new_entity,
label,
}
},
computed: {
notPickedSuggested() {
const pickedIds = new Set();
for (const p of this.picked) {
pickedIds.add(`${p.type}${p.id}`);
}
return this.suggested.filter(e => !pickedIds.has(`${e.type}${e.id}`))
const app = createApp({
template:
"<pick-entity " +
':multiple="multiple" ' +
':types="types" ' +
':picked="picked" ' +
':uniqid="uniqid" ' +
':suggested="notPickedSuggested" ' +
':label="label" ' +
'@addNewEntity="addNewEntity" ' +
'@removeEntity="removeEntity"></pick-entity>',
components: {
PickEntity,
},
data() {
return {
multiple: isMultiple,
types: JSON.parse(el.dataset.types),
picked: picked === null ? [] : picked,
uniqid: el.dataset.uniqid,
suggested,
as_id,
submit_on_adding_new_entity,
label,
};
},
computed: {
notPickedSuggested() {
const pickedIds = new Set();
for (const p of this.picked) {
pickedIds.add(`${p.type}${p.id}`);
}
return this.suggested.filter(
(e) => !pickedIds.has(`${e.type}${e.id}`),
);
},
},
methods: {
addNewEntity({ entity }) {
if (this.multiple) {
if (
!this.picked.some((el) => {
return el.type === entity.type && el.id === entity.id;
})
) {
this.picked.push(entity);
if (!as_id) {
input.value = JSON.stringify(this.picked);
} else {
const ids = this.picked.map((el) => el.id);
input.value = ids.join(",");
}
},
methods: {
addNewEntity({entity}) {
if (this.multiple) {
if (!this.picked.some(el => {
return el.type === entity.type && el.id === entity.id;
})) {
this.picked.push(entity);
if (!as_id) {
input.value = JSON.stringify(this.picked);
} else {
const ids = this.picked.map(el => el.id);
input.value = ids.join(',');
}
console.log(entity)
}
} else {
if (!this.picked.some(el => {
return el.type === entity.type && el.id === entity.id;
})) {
this.picked.splice(0, this.picked.length);
this.picked.push(entity);
if (!as_id) {
input.value = JSON.stringify(this.picked[0]);
} else {
input.value = this.picked.map(el => el.id);
}
}
}
if (this.submit_on_adding_new_entity) {
input.form.submit();
}
},
removeEntity({entity}) {
if (-1 === this.suggested.findIndex(e => e.type === entity.type && e.id === entity.id)) {
this.suggested.push(entity);
}
this.picked = this.picked.filter(e => !(e.type === entity.type && e.id === entity.id));
if (this.multiple) {
input.value = JSON.stringify(this.picked);
} else {
input.value = "";
}
},
console.log(entity);
}
})
.use(i18n)
.mount(el);
} else {
if (
!this.picked.some((el) => {
return el.type === entity.type && el.id === entity.id;
})
) {
this.picked.splice(0, this.picked.length);
this.picked.push(entity);
if (!as_id) {
input.value = JSON.stringify(this.picked[0]);
} else {
input.value = this.picked.map((el) => el.id);
}
}
}
appsOnPage.set(uniqId, app);
appsPerInput.set(input.name, app);
});
if (this.submit_on_adding_new_entity) {
input.form.submit();
}
},
removeEntity({ entity }) {
if (
-1 ===
this.suggested.findIndex(
(e) => e.type === entity.type && e.id === entity.id,
)
) {
this.suggested.push(entity);
}
this.picked = this.picked.filter(
(e) => !(e.type === entity.type && e.id === entity.id),
);
if (this.multiple) {
input.value = JSON.stringify(this.picked);
} else {
input.value = "";
}
},
},
})
.use(i18n)
.mount(el);
appsOnPage.set(uniqId, app);
appsPerInput.set(input.name, app);
});
}
document.addEventListener('show-hide-show', function(e) {
loadDynamicPicker(e.detail.container)
document.addEventListener("show-hide-show", function (e) {
loadDynamicPicker(e.detail.container);
});
document.addEventListener('show-hide-hide', function(e) {
console.log('hiding event caught')
e.detail.container.querySelectorAll('[data-module="pick-dynamic"]').forEach((el) => {
let uniqId = el.dataset.uniqid;
if (appsOnPage.has(uniqId)) {
appsOnPage.get(uniqId).unmount();
appsOnPage.delete(uniqId);
}
})
});
document.addEventListener('pick-entity-type-action', function (e) {
console.log('pick entity event', e);
if (!appsPerInput.has(e.detail.name)) {
console.error('no app with this name');
return;
}
const app = appsPerInput.get(e.detail.name);
if (e.detail.action === 'add') {
app.addNewEntity(e.detail.entity);
} else if (e.detail.action === 'remove') {
app.removeEntity(e.detail.entity);
} else {
console.error('action not supported: '+e.detail.action);
document.addEventListener("show-hide-hide", function (e) {
console.log("hiding event caught");
e.detail.container
.querySelectorAll('[data-module="pick-dynamic"]')
.forEach((el) => {
let uniqId = el.dataset.uniqid;
if (appsOnPage.has(uniqId)) {
appsOnPage.get(uniqId).unmount();
appsOnPage.delete(uniqId);
}
});
});
document.addEventListener('DOMContentLoaded', function(e) {
loadDynamicPicker(document)
})
document.addEventListener("pick-entity-type-action", function (e) {
console.log("pick entity event", e);
if (!appsPerInput.has(e.detail.name)) {
console.error("no app with this name");
return;
}
const app = appsPerInput.get(e.detail.name);
if (e.detail.action === "add") {
app.addNewEntity(e.detail.entity);
} else if (e.detail.action === "remove") {
app.removeEntity(e.detail.entity);
} else {
console.error("action not supported: " + e.detail.action);
}
});
document.addEventListener("DOMContentLoaded", function (e) {
loadDynamicPicker(document);
});
window.loadDynamicPicker = loadDynamicPicker;

View File

@@ -1,60 +1,57 @@
import { createApp } from 'vue';
import PickPostalCode from 'ChillMainAssets/vuejs/PickPostalCode/PickPostalCode';
import { _createI18n } from 'ChillMainAssets/vuejs/_js/i18n';
import { appMessages } from 'ChillMainAssets/vuejs/PickEntity/i18n';
import { makeFetch } from 'ChillMainAssets/lib/api/apiMethods';
import { createApp } from "vue";
import PickPostalCode from "ChillMainAssets/vuejs/PickPostalCode/PickPostalCode";
import { _createI18n } from "ChillMainAssets/vuejs/_js/i18n";
import { appMessages } from "ChillMainAssets/vuejs/PickEntity/i18n";
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
const i18n = _createI18n(appMessages);
function loadOnePicker(el, input, uniqId, city) {
const app = createApp({
template: '<pick-postal-code @select-city="onCitySelected" @removeCity="onCityRemoved" :picked="city"></pick-postal-code>',
components: {
PickPostalCode,
},
data() {
return {
city: city,
}
},
methods: {
onCitySelected(city) {
this.city = city;
input.value = city.id;
},
onCityRemoved(city) {
this.city = null;
input.value = '';
}
}
})
.use(i18n)
.mount(el);
const app = createApp({
template:
'<pick-postal-code @select-city="onCitySelected" @removeCity="onCityRemoved" :picked="city"></pick-postal-code>',
components: {
PickPostalCode,
},
data() {
return {
city: city,
};
},
methods: {
onCitySelected(city) {
this.city = city;
input.value = city.id;
},
onCityRemoved(city) {
this.city = null;
input.value = "";
},
},
})
.use(i18n)
.mount(el);
}
function loadDynamicPickers(element) {
let apps = element.querySelectorAll('[data-module="pick-postal-code"]');
let apps = element.querySelectorAll('[data-module="pick-postal-code"]');
apps.forEach(function(el) {
const
uniqId = el.dataset.uniqid,
input = document.querySelector(`input[data-input-uniqid="${uniqId}"]`),
cityIdValue = input.value === '' ? null : input.value
;
if (cityIdValue !== null) {
makeFetch('GET', `/api/1.0/main/postal-code/${cityIdValue}.json`).then(city => {
loadOnePicker(el, input, uniqId, city);
})
} else {
loadOnePicker(el, input, uniqId, null);
}
});
apps.forEach(function (el) {
const uniqId = el.dataset.uniqid,
input = document.querySelector(`input[data-input-uniqid="${uniqId}"]`),
cityIdValue = input.value === "" ? null : input.value;
if (cityIdValue !== null) {
makeFetch("GET", `/api/1.0/main/postal-code/${cityIdValue}.json`).then(
(city) => {
loadOnePicker(el, input, uniqId, city);
},
);
} else {
loadOnePicker(el, input, uniqId, null);
}
});
}
document.addEventListener('DOMContentLoaded', function(e) {
loadDynamicPickers(document)
})
document.addEventListener("DOMContentLoaded", function (e) {
loadDynamicPickers(document);
});

View File

@@ -1,23 +1,23 @@
import {ShowHide} from 'ChillMainAssets/lib/show_hide/index';
import { ShowHide } from "ChillMainAssets/lib/show_hide/index";
document.addEventListener('DOMContentLoaded', function(_e) {
document.querySelectorAll('div[data-rolling-date]').forEach( (picker) => {
const
roll_wrapper = picker.querySelector('div.roll-wrapper'),
fixed_wrapper = picker.querySelector('div.fixed-wrapper');
document.addEventListener("DOMContentLoaded", function (_e) {
document.querySelectorAll("div[data-rolling-date]").forEach((picker) => {
const roll_wrapper = picker.querySelector("div.roll-wrapper"),
fixed_wrapper = picker.querySelector("div.fixed-wrapper");
new ShowHide({
froms: [roll_wrapper],
container: [fixed_wrapper],
test: function (elems) {
for (let el of elems) {
for (let select_roll of el.querySelectorAll('select[data-roll-picker]')) {
return select_roll.value === 'fixed_date';
}
}
return false;
}
})
new ShowHide({
froms: [roll_wrapper],
container: [fixed_wrapper],
test: function (elems) {
for (let el of elems) {
for (let select_roll of el.querySelectorAll(
"select[data-roll-picker]",
)) {
return select_roll.value === "fixed_date";
}
}
return false;
},
});
});
});

View File

@@ -1,10 +1,9 @@
import { createApp } from 'vue';
import OpenWopiLink from 'ChillMainAssets/vuejs/_components/OpenWopiLink';
import {_createI18n} from "ChillMainAssets/vuejs/_js/i18n";
import { createApp } from "vue";
import OpenWopiLink from "ChillMainAssets/vuejs/_components/OpenWopiLink";
import { _createI18n } from "ChillMainAssets/vuejs/_js/i18n";
const i18n = _createI18n({});
//TODO move to chillDocStore or ChillWopi
/*
@@ -19,25 +18,28 @@ tags to load module:
*/
window.addEventListener('DOMContentLoaded', function (e) {
document.querySelectorAll('span[data-module="wopi-link"]')
.forEach(function (el) {
createApp({
template: '<open-wopi-link :wopiUrl="wopiUrl" :type="type" :options="options"></open-wopi-link>',
components: {
OpenWopiLink
},
data() {
return {
wopiUrl: el.dataset.wopiUrl,
type: el.dataset.docType,
options: el.dataset.options !== 'null' ? JSON.parse(el.dataset.options) : {}
}
}
})
.use(i18n)
.mount(el)
;
})
;
window.addEventListener("DOMContentLoaded", function (e) {
document
.querySelectorAll('span[data-module="wopi-link"]')
.forEach(function (el) {
createApp({
template:
'<open-wopi-link :wopiUrl="wopiUrl" :type="type" :options="options"></open-wopi-link>',
components: {
OpenWopiLink,
},
data() {
return {
wopiUrl: el.dataset.wopiUrl,
type: el.dataset.docType,
options:
el.dataset.options !== "null"
? JSON.parse(el.dataset.options)
: {},
};
},
})
.use(i18n)
.mount(el);
});
});

View File

@@ -1,16 +1,14 @@
import {download_report} from "../../lib/download-report/download-report";
import { download_report } from "../../lib/download-report/download-report";
window.addEventListener("DOMContentLoaded", function(e) {
window.addEventListener("DOMContentLoaded", function (e) {
const export_generate_url = window.export_generate_url;
if (typeof export_generate_url === 'undefined') {
console.error('Alias not found!');
throw new Error('Alias not found!');
if (typeof export_generate_url === "undefined") {
console.error("Alias not found!");
throw new Error("Alias not found!");
}
const query = window.location.search,
container = document.querySelector("#download_container")
;
container = document.querySelector("#download_container");
download_report(export_generate_url + query.toString(), container);
});

View File

@@ -1,5 +1,5 @@
// old method to show/hide filters when checking checkbox
import { chill } from 'ChillMainAssets/chill/js/chill';
import { chill } from "ChillMainAssets/chill/js/chill";
window.addEventListener("DOMContentLoaded", chill.listenerDisplayCheckbox);
// TODO should be replaced by more recent showHide library

View File

@@ -1,16 +1,15 @@
import { createApp } from 'vue';
import { _createI18n } from 'ChillMainAssets/vuejs/_js/i18n';
import { appMessages } from 'ChillMainAssets/vuejs/HomepageWidget/js/i18n';
import { store } from 'ChillMainAssets/vuejs/HomepageWidget/js/store';
import App from 'ChillMainAssets/vuejs/HomepageWidget/App';
import { createApp } from "vue";
import { _createI18n } from "ChillMainAssets/vuejs/_js/i18n";
import { appMessages } from "ChillMainAssets/vuejs/HomepageWidget/js/i18n";
import { store } from "ChillMainAssets/vuejs/HomepageWidget/js/store";
import App from "ChillMainAssets/vuejs/HomepageWidget/App";
const i18n = _createI18n(appMessages);
const app = createApp({
template: `<app></app>`,
template: `<app></app>`,
})
.use(store)
.use(i18n)
.component('app', App)
.mount('#homepage_widget')
;
.use(store)
.use(i18n)
.component("app", App)
.mount("#homepage_widget");

View File

@@ -1,66 +1,66 @@
const contactDataBlock = document.querySelector('div.location-form-contact');
const addressBlock = document.querySelector('div.location-form-address');
const locationType = document.getElementById('chill_mainbundle_location_locationType');
const getSelectedAttributes =
(select, attr) => select.selectedOptions[0].getAttribute(attr)
const contactDataBlock = document.querySelector("div.location-form-contact");
const addressBlock = document.querySelector("div.location-form-address");
const locationType = document.getElementById(
"chill_mainbundle_location_locationType",
);
const getSelectedAttributes = (select, attr) =>
select.selectedOptions[0].getAttribute(attr);
const removeRequired = (formBlock) => {
formBlock.querySelectorAll('label').forEach(
l => l.classList.remove('required')
);
formBlock.querySelectorAll('input').forEach(
i => i.removeAttribute('required')
);
}
const addRequired = (formBlock) => {
formBlock.querySelectorAll('label').forEach(
l => l.classList.add('required')
);
formBlock.querySelectorAll('input').forEach(
i => i.setAttribute('required', '')
);
}
const onLocationTypeChange = () => {
console.log(getSelectedAttributes(locationType, 'data-address'))
console.log(getSelectedAttributes(locationType, 'data-contact'))
switch (getSelectedAttributes(locationType, 'data-address')) {
case 'optional':
default:
removeRequired(addressBlock);
addressBlock.classList.remove('d-none');
break;
case 'required':
addRequired(addressBlock);
addressBlock.classList.remove('d-none');
break;
case 'never':
removeRequired(addressBlock);
addressBlock.classList.add('d-none');
break;
}
switch (getSelectedAttributes(locationType, 'data-contact')) {
case 'optional':
default:
removeRequired(contactDataBlock);
contactDataBlock.classList.remove('d-none');
break;
case 'required':
addRequired(contactDataBlock);
contactDataBlock.classList.remove('d-none');
break;
case 'never':
removeRequired(contactDataBlock);
contactDataBlock.classList.add('d-none');
break;
}
formBlock
.querySelectorAll("label")
.forEach((l) => l.classList.remove("required"));
formBlock
.querySelectorAll("input")
.forEach((i) => i.removeAttribute("required"));
};
document.addEventListener('DOMContentLoaded', _e => {
onLocationTypeChange();
locationType.addEventListener('change', onLocationTypeChange);
const addRequired = (formBlock) => {
formBlock
.querySelectorAll("label")
.forEach((l) => l.classList.add("required"));
formBlock
.querySelectorAll("input")
.forEach((i) => i.setAttribute("required", ""));
};
const onLocationTypeChange = () => {
console.log(getSelectedAttributes(locationType, "data-address"));
console.log(getSelectedAttributes(locationType, "data-contact"));
switch (getSelectedAttributes(locationType, "data-address")) {
case "optional":
default:
removeRequired(addressBlock);
addressBlock.classList.remove("d-none");
break;
case "required":
addRequired(addressBlock);
addressBlock.classList.remove("d-none");
break;
case "never":
removeRequired(addressBlock);
addressBlock.classList.add("d-none");
break;
}
switch (getSelectedAttributes(locationType, "data-contact")) {
case "optional":
default:
removeRequired(contactDataBlock);
contactDataBlock.classList.remove("d-none");
break;
case "required":
addRequired(contactDataBlock);
contactDataBlock.classList.remove("d-none");
break;
case "never":
removeRequired(contactDataBlock);
contactDataBlock.classList.add("d-none");
break;
}
};
document.addEventListener("DOMContentLoaded", (_e) => {
onLocationTypeChange();
locationType.addEventListener("change", onLocationTypeChange);
});

View File

@@ -1,3 +1 @@
require('./login.scss');
require("./login.scss");

View File

@@ -1,70 +1,62 @@
import {ShowHide} from 'ChillMainAssets/lib/show_hide/show_hide.js';
window.addEventListener('DOMContentLoaded', function() {
let
divTransitions = document.querySelector('#transitions'),
futureDestUsersContainer = document.querySelector('#futureDests')
;
if (null !== divTransitions) {
new ShowHide({
load_event: null,
froms: [divTransitions],
container: [futureDestUsersContainer],
test: function(divs, arg2, arg3) {
for (let div of divs) {
for (let input of div.querySelectorAll('input')) {
if (input.checked) {
if (input.dataset.toFinal === "1") {
return false;
} else {
return true;
}
}
}
}
import { ShowHide } from "ChillMainAssets/lib/show_hide/show_hide.js";
window.addEventListener("DOMContentLoaded", function () {
let divTransitions = document.querySelector("#transitions"),
futureDestUsersContainer = document.querySelector("#futureDests");
if (null !== divTransitions) {
new ShowHide({
load_event: null,
froms: [divTransitions],
container: [futureDestUsersContainer],
test: function (divs, arg2, arg3) {
for (let div of divs) {
for (let input of div.querySelectorAll("input")) {
if (input.checked) {
if (input.dataset.toFinal === "1") {
return false;
} else {
return true;
},
});
}
}
}
}
}
let
transitionFilterContainer = document.querySelector('#transitionFilter'),
transitions = document.querySelector('#transitions')
;
return true;
},
});
}
if (null !== transitionFilterContainer) {
transitions.querySelectorAll('.form-check').forEach(function(row) {
const isForward = row.querySelector('input').dataset.isForward;
new ShowHide({
load_event: null,
froms: [transitionFilterContainer],
container: row,
test: function (containers) {
for (let container of containers) {
for (let input of container.querySelectorAll('input')) {
if (input.checked) {
return isForward === input.value;
}
}
}
},
toggle_callback: function (c, dir) {
for (let div of c) {
let input = div.querySelector('input');
if ('hide' === dir) {
input.checked = false;
input.disabled = true;
} else {
input.disabled = false;
}
}
},
});
});
}
let transitionFilterContainer = document.querySelector("#transitionFilter"),
transitions = document.querySelector("#transitions");
if (null !== transitionFilterContainer) {
transitions.querySelectorAll(".form-check").forEach(function (row) {
const isForward = row.querySelector("input").dataset.isForward;
new ShowHide({
load_event: null,
froms: [transitionFilterContainer],
container: row,
test: function (containers) {
for (let container of containers) {
for (let input of container.querySelectorAll("input")) {
if (input.checked) {
return isForward === input.value;
}
}
}
},
toggle_callback: function (c, dir) {
for (let div of c) {
let input = div.querySelector("input");
if ("hide" === dir) {
input.checked = false;
input.disabled = true;
} else {
input.disabled = false;
}
}
},
});
});
}
});

View File

@@ -1,164 +1,164 @@
export interface DateTime {
datetime: string;
datetime8601: string
datetime: string;
datetime8601: string;
}
export interface Civility {
id: number;
// TODO
id: number;
// TODO
}
export interface Job {
id: number;
type: "user_job";
label: {
"fr": string; // could have other key. How to do that in ts ?
}
id: number;
type: "user_job";
label: {
fr: string; // could have other key. How to do that in ts ?
};
}
export interface Center {
id: number;
type: "center";
name: string;
id: number;
type: "center";
name: string;
}
export interface Scope {
id: number;
type: "scope";
name: {
"fr": string
}
id: number;
type: "scope";
name: {
fr: string;
};
}
export interface User {
type: "user";
id: number;
username: string;
text: string;
text_without_absence: string;
email: string;
user_job: Job;
label: string;
// todo: mainCenter; mainJob; etc..
type: "user";
id: number;
username: string;
text: string;
text_without_absence: string;
email: string;
user_job: Job;
label: string;
// todo: mainCenter; mainJob; etc..
}
export interface UserAssociatedInterface {
type: "user";
id: number;
};
type: "user";
id: number;
}
export interface TranslatableString {
fr?: string;
nl?: string;
fr?: string;
nl?: string;
}
export interface Postcode {
id: number;
name: string;
code: string;
center: Point;
id: number;
name: string;
code: string;
center: Point;
}
export interface Point {
type: "Point";
coordinates: [lat: number, lon: number];
type: "Point";
coordinates: [lat: number, lon: number];
}
export interface Country {
id: number;
name: TranslatableString;
code: string;
id: number;
name: TranslatableString;
code: string;
}
export type AddressRefStatus = 'match'|'to_review'|'reviewed';
export type AddressRefStatus = "match" | "to_review" | "reviewed";
export interface Address {
type: "address";
address_id: number;
text: string;
street: string;
streetNumber: string;
postcode: Postcode;
country: Country;
floor: string | null;
corridor: string | null;
steps: string | null;
flat: string | null;
buildingName: string | null;
distribution: string | null;
extra: string | null;
confidential: boolean;
lines: string[];
addressReference: AddressReference | null;
validFrom: DateTime;
validTo: DateTime | null;
point: Point | null;
refStatus: AddressRefStatus;
isNoAddress: boolean;
type: "address";
address_id: number;
text: string;
street: string;
streetNumber: string;
postcode: Postcode;
country: Country;
floor: string | null;
corridor: string | null;
steps: string | null;
flat: string | null;
buildingName: string | null;
distribution: string | null;
extra: string | null;
confidential: boolean;
lines: string[];
addressReference: AddressReference | null;
validFrom: DateTime;
validTo: DateTime | null;
point: Point | null;
refStatus: AddressRefStatus;
isNoAddress: boolean;
}
export interface AddressWithPoint extends Address {
point: Point
point: Point;
}
export interface AddressReference {
id: number;
createdAt: DateTime | null;
deletedAt: DateTime | null;
municipalityCode: string;
point: Point;
postcode: Postcode;
refId: string;
source: string;
street: string;
streetNumber: string;
updatedAt: DateTime | null;
id: number;
createdAt: DateTime | null;
deletedAt: DateTime | null;
municipalityCode: string;
point: Point;
postcode: Postcode;
refId: string;
source: string;
street: string;
streetNumber: string;
updatedAt: DateTime | null;
}
export interface SimpleGeographicalUnit {
id: number;
layerId: number;
unitName: string;
unitRefId: string;
id: number;
layerId: number;
unitName: string;
unitRefId: string;
}
export interface GeographicalUnitLayer {
id: number;
name: TranslatableString;
refId: string;
id: number;
name: TranslatableString;
refId: string;
}
export interface Location {
type: "location";
id: number;
active: boolean;
address: Address | null;
availableForUsers: boolean;
createdAt: DateTime | null;
createdBy: User | null;
updatedAt: DateTime | null;
updatedBy: User | null;
email: string | null
name: string;
phonenumber1: string | null;
phonenumber2: string | null;
locationType: LocationType;
type: "location";
id: number;
active: boolean;
address: Address | null;
availableForUsers: boolean;
createdAt: DateTime | null;
createdBy: User | null;
updatedAt: DateTime | null;
updatedBy: User | null;
email: string | null;
name: string;
phonenumber1: string | null;
phonenumber2: string | null;
locationType: LocationType;
}
export interface LocationAssociated {
type: "location";
id: number;
type: "location";
id: number;
}
export interface LocationType {
type: "location-type";
id: number;
active: boolean;
addressRequired: "optional" | "required";
availableForUsers: boolean;
editableByUsers: boolean;
contactData: "optional" | "required";
title: TranslatableString;
type: "location-type";
id: number;
active: boolean;
addressRequired: "optional" | "required";
availableForUsers: boolean;
editableByUsers: boolean;
contactData: "optional" | "required";
title: TranslatableString;
}
export interface NewsItemType {

View File

@@ -1,111 +1,130 @@
<template>
<add-address
:key="key"
:context="context"
:options="options"
:address-changed-callback="submitAddress"
ref="addAddress"
/>
<add-address
:key="key"
:context="context"
:options="options"
:address-changed-callback="submitAddress"
ref="addAddress"
/>
</template>
<script>
import AddAddress from './components/AddAddress.vue';
import { postAddressToHousehold, postAddressToPerson } from "ChillPersonAssets/vuejs/_api/AddAddress";
import AddAddress from "./components/AddAddress.vue";
import {
postAddressToHousehold,
postAddressToPerson,
} from "ChillPersonAssets/vuejs/_api/AddAddress";
export default {
name: "App",
components: {
AddAddress
},
props: ['addAddress', 'callback'],
emits: ['addressEdited', 'addressCreated'],
computed: {
context() {
return this.addAddress.context;
},
options() {
return this.addAddress.options;
},
key() {
return (this.context.edit) ? 'address_' + this.context.addressId
: this.context.target.name + '_' + this.context.target.id ;
}
},
mounted() {
//console.log('AddAddress: data context', this.context);
//console.log('AddAddress: data options', this.options);
},
methods: {
displayErrors() {
return this.$refs.addAddress.errorMsg;
},
submitAddress(payload) {
console.log('@@@ click on Submit Address Button', payload);
name: "App",
components: {
AddAddress,
},
props: ["addAddress", "callback"],
emits: ["addressEdited", "addressCreated"],
computed: {
context() {
return this.addAddress.context;
},
options() {
return this.addAddress.options;
},
key() {
return this.context.edit
? "address_" + this.context.addressId
: this.context.target.name + "_" + this.context.target.id;
},
},
mounted() {
//console.log('AddAddress: data context', this.context);
//console.log('AddAddress: data options', this.options);
},
methods: {
displayErrors() {
return this.$refs.addAddress.errorMsg;
},
submitAddress(payload) {
console.log("@@@ click on Submit Address Button", payload);
// Existing address
if (this.context.edit) {
// Existing address
if (this.context.edit) {
// address is already linked, just finish !
this.$refs.addAddress.afterLastPaneAction({});
this.$emit("addressEdited", payload);
// address is already linked, just finish !
this.$refs.addAddress.afterLastPaneAction({});
this.$emit('addressEdited', payload);
// New created address
} else {
this.postAddressTo(payload);
}
},
// New created address
} else {
this.postAddressTo(payload);
}
},
/*
* Post new created address to targetEntity
*/
postAddressTo(payload) {
this.$emit("addressCreated", payload);
/*
* Post new created address to targetEntity
*/
postAddressTo(payload) {
this.$emit('addressCreated', payload);
console.log(
"postAddress",
payload.addressId,
"To",
payload.target,
payload.targetId,
);
switch (payload.target) {
case "household":
postAddressToHousehold(payload.targetId, payload.addressId)
.then(
(address) =>
new Promise((resolve, reject) => {
console.log("..household address", address);
this.$refs.addAddress.flag.loading = false;
this.$refs.addAddress.flag.success = true;
console.log('postAddress', payload.addressId, 'To', payload.target, payload.targetId);
switch (payload.target) {
case 'household':
postAddressToHousehold(payload.targetId, payload.addressId)
.then(address => new Promise((resolve, reject) => {
console.log('..household address', address);
this.$refs.addAddress.flag.loading = false;
this.$refs.addAddress.flag.success = true;
// finish
this.$refs.addAddress.afterLastPaneAction({
addressId: address.address_id,
});
// finish
this.$refs.addAddress.afterLastPaneAction({ addressId: address.address_id });
resolve();
}),
)
.catch((error) => {
this.$refs.addAddress.errorMsg.push(error);
this.$refs.addAddress.flag.loading = false;
});
break;
case "person":
postAddressToPerson(payload.targetId, payload.addressId)
.then(
(address) =>
new Promise((resolve, reject) => {
console.log("..person address", address);
this.$refs.addAddress.flag.loading = false;
this.$refs.addAddress.flag.success = true;
resolve();
}))
.catch((error) => {
this.$refs.addAddress.errorMsg.push(error);
this.$refs.addAddress.flag.loading = false;
})
;
break;
case 'person':
postAddressToPerson(payload.targetId, payload.addressId)
.then(address => new Promise((resolve, reject) => {
console.log('..person address', address);
this.$refs.addAddress.flag.loading = false;
this.$refs.addAddress.flag.success = true;
// finish
this.$refs.addAddress.afterLastPaneAction({
addressId: address.address_id,
});
// finish
this.$refs.addAddress.afterLastPaneAction({ addressId: address.address_id });
resolve();
}))
.catch((error) => {
this.$refs.addAddress.errorMsg.push(error);
this.$refs.addAddress.flag.loading = false;
})
;
break;
case 'thirdparty':
console.log('TODO write postAddressToThirdparty');
break;
default:
this.$refs.addAddress.errorMsg.push('That entity is not managed by address !');
}
}
}
}
resolve();
}),
)
.catch((error) => {
this.$refs.addAddress.errorMsg.push(error);
this.$refs.addAddress.flag.loading = false;
});
break;
case "thirdparty":
console.log("TODO write postAddressToThirdparty");
break;
default:
this.$refs.addAddress.errorMsg.push(
"That entity is not managed by address !",
);
}
},
},
};
</script>

View File

@@ -1,122 +1,128 @@
import {getAddressById} from 'ChillMainAssets/lib/api/address';
import { getAddressById } from "ChillMainAssets/lib/api/address";
/**
* Endpoint chill_api_single_country__index
* method GET, get Country Object
* @returns {Promise} a promise containing all Country object
*/
* Endpoint chill_api_single_country__index
* method GET, get Country Object
* @returns {Promise} a promise containing all Country object
*/
const fetchCountries = () => {
//console.log('<<< fetching countries');
//console.log('<<< fetching countries');
const url = `/api/1.0/main/country.json?item_per_page=1000`;
return fetch(url)
.then(response => {
if (response.ok) { return response.json(); }
throw Error('Error with request resource response');
});
const url = `/api/1.0/main/country.json?item_per_page=1000`;
return fetch(url).then((response) => {
if (response.ok) {
return response.json();
}
throw Error("Error with request resource response");
});
};
/**
* Endpoint chill_api_single_postal_code__index
* method GET, get Cities Object
* @params {object} a country object
* @returns {Promise} a promise containing all Postal Code objects filtered with country
*/
* Endpoint chill_api_single_postal_code__index
* method GET, get Cities Object
* @params {object} a country object
* @returns {Promise} a promise containing all Postal Code objects filtered with country
*/
const fetchCities = (country) => {
//console.log('<<< fetching cities for', country);
// warning: do not use fetchResults (in apiMethods): we need only a **part** of the results in the db
const url = `/api/1.0/main/postal-code.json?item_per_page=1000&country=${country.id}`;
return fetch(url)
.then(response => {
if (response.ok) { return response.json(); }
throw Error('Error with request resource response');
});
//console.log('<<< fetching cities for', country);
// warning: do not use fetchResults (in apiMethods): we need only a **part** of the results in the db
const url = `/api/1.0/main/postal-code.json?item_per_page=1000&country=${country.id}`;
return fetch(url).then((response) => {
if (response.ok) {
return response.json();
}
throw Error("Error with request resource response");
});
};
/**
* Endpoint chill_main_postalcodeapi_search
* method GET, get Cities Object
* @params {string} search a search string
* @params {object} country a country object
* @returns {Promise} a promise containing all Postal Code objects filtered with country and a search string
*/
* Endpoint chill_main_postalcodeapi_search
* method GET, get Cities Object
* @params {string} search a search string
* @params {object} country a country object
* @returns {Promise} a promise containing all Postal Code objects filtered with country and a search string
*/
const searchCities = (search, country) => {
const url = `/api/1.0/main/postal-code/search.json?q=${search}&country=${country.id}`;
return fetch(url)
.then(response => {
if (response.ok) { return response.json(); }
throw Error('Error with request resource response');
});
const url = `/api/1.0/main/postal-code/search.json?q=${search}&country=${country.id}`;
return fetch(url).then((response) => {
if (response.ok) {
return response.json();
}
throw Error("Error with request resource response");
});
};
/**
* Endpoint chill_main_addressreferenceapi_search
* method GET, get AddressReference Object
* @params {string} search a search string
* @params {object} postalCode a postalCode object
* @returns {Promise} a promise containing all Postal Code objects filtered with country and a search string
*/
* Endpoint chill_main_addressreferenceapi_search
* method GET, get AddressReference Object
* @params {string} search a search string
* @params {object} postalCode a postalCode object
* @returns {Promise} a promise containing all Postal Code objects filtered with country and a search string
*/
const searchReferenceAddresses = (search, postalCode) => {
const url = `/api/1.0/main/address-reference/by-postal-code/${postalCode.id}/search.json?q=${search}`;
return fetch(url)
.then(response => {
if (response.ok) { return response.json(); }
throw Error('Error with request resource response');
});
const url = `/api/1.0/main/address-reference/by-postal-code/${postalCode.id}/search.json?q=${search}`;
return fetch(url).then((response) => {
if (response.ok) {
return response.json();
}
throw Error("Error with request resource response");
});
};
/**
* Endpoint chill_api_single_address_reference__index
* method GET, get AddressReference Object
* @returns {Promise} a promise containing all AddressReference objects filtered with postal code
*/
* Endpoint chill_api_single_address_reference__index
* method GET, get AddressReference Object
* @returns {Promise} a promise containing all AddressReference objects filtered with postal code
*/
const fetchReferenceAddresses = (postalCode) => {
//console.log('<<< fetching references addresses for', postalCode);
const url = `/api/1.0/main/address-reference.json?item_per_page=1000&postal_code=${postalCode.id}`;
return fetch(url)
.then(response => {
if (response.ok) { return response.json(); }
throw Error('Error with request resource response');
});
//console.log('<<< fetching references addresses for', postalCode);
const url = `/api/1.0/main/address-reference.json?item_per_page=1000&postal_code=${postalCode.id}`;
return fetch(url).then((response) => {
if (response.ok) {
return response.json();
}
throw Error("Error with request resource response");
});
};
/**
* Endpoint chill_api_single_address_reference__index
* method GET, get AddressReference Object
* @returns {Promise} a promise containing all AddressReference objects filtered with postal code
*/
* Endpoint chill_api_single_address_reference__index
* method GET, get AddressReference Object
* @returns {Promise} a promise containing all AddressReference objects filtered with postal code
*/
const fetchAddresses = () => {
//console.log('<<< fetching addresses');
//console.log('<<< fetching addresses');
//TODO deal with huge number of addresses... we should do suggestion...
const url = `/api/1.0/main/address.json?item_per_page=1000`;
return fetch(url)
.then(response => {
if (response.ok) { return response.json(); }
throw Error('Error with request resource response');
});
const url = `/api/1.0/main/address.json?item_per_page=1000`;
return fetch(url).then((response) => {
if (response.ok) {
return response.json();
}
throw Error("Error with request resource response");
});
};
/**
* Endpoint chill_api_single_address__entity__create
* method POST, post Address Object
* @returns {Promise}
*/
* Endpoint chill_api_single_address__entity__create
* method POST, post Address Object
* @returns {Promise}
*/
const postAddress = (address) => {
const url = `/api/1.0/main/address.json?`;
const body = address;
const url = `/api/1.0/main/address.json?`;
const body = address;
return fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8'
},
body: JSON.stringify(body)
}).then(response => {
if (response.ok) { return response.json(); }
throw Error('Error with request resource response');
});
return fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json;charset=utf-8",
},
body: JSON.stringify(body),
}).then((response) => {
if (response.ok) {
return response.json();
}
throw Error("Error with request resource response");
});
};
/**
@@ -127,82 +133,85 @@ const postAddress = (address) => {
const duplicateAddress = (address) => {
const url = `/api/1.0/main/address/${address.address_id}/duplicate.json`;
return fetch(url, {
'method': 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8'
},
}).then(response => {
if (response.ok) {
return response.json();
}
throw Error('Error with request resource response');
});
method: "POST",
headers: {
"Content-Type": "application/json;charset=utf-8",
},
}).then((response) => {
if (response.ok) {
return response.json();
}
throw Error("Error with request resource response");
});
};
/**
* Endpoint chill_api_single_address__entity__create
* method PATCH, patch Address Instance
*
* @id integer - id of address
* @body Object - dictionary with changes to post
*/
* Endpoint chill_api_single_address__entity__create
* method PATCH, patch Address Instance
*
* @id integer - id of address
* @body Object - dictionary with changes to post
*/
const patchAddress = (id, body) => {
const url = `/api/1.0/main/address/${id}.json`;
return fetch(url, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json;charset=utf-8'
},
body: JSON.stringify(body)
})
.then(response => {
if (response.ok) { return response.json(); }
throw Error('Error with request resource response');
});
const url = `/api/1.0/main/address/${id}.json`;
return fetch(url, {
method: "PATCH",
headers: {
"Content-Type": "application/json;charset=utf-8",
},
body: JSON.stringify(body),
}).then((response) => {
if (response.ok) {
return response.json();
}
throw Error("Error with request resource response");
});
};
/*
* Endpoint chill_api_single_postal_code__entity_create
* method POST, post Postal Code Object
* @returns {Promise}
*/
const postPostalCode = (postalCode) => { //<--
const url = `/api/1.0/main/postal-code.json?`;
const body = postalCode;
* Endpoint chill_api_single_postal_code__entity_create
* method POST, post Postal Code Object
* @returns {Promise}
*/
const postPostalCode = (postalCode) => {
//<--
const url = `/api/1.0/main/postal-code.json?`;
const body = postalCode;
return fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8'
},
body: JSON.stringify(body)
}).then(response => {
if (response.ok) { return response.json(); }
throw Error('Error with request resource response');
});
return fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json;charset=utf-8",
},
body: JSON.stringify(body),
}).then((response) => {
if (response.ok) {
return response.json();
}
throw Error("Error with request resource response");
});
};
/*
* Endpoint chill_api_single_address__index
* method GET, get Address Object
* @params {id} the address id
* @returns {Promise} a promise containing a Address object
*/
* Endpoint chill_api_single_address__index
* method GET, get Address Object
* @params {id} the address id
* @returns {Promise} a promise containing a Address object
*/
const getAddress = (id) => {
return getAddressById(id);
};
export {
duplicateAddress,
fetchCountries,
fetchCities,
fetchReferenceAddresses,
fetchAddresses,
postAddress,
patchAddress,
postPostalCode,
getAddress,
searchCities,
searchReferenceAddresses
duplicateAddress,
fetchCountries,
fetchCities,
fetchReferenceAddresses,
fetchAddresses,
postAddress,
patchAddress,
postPostalCode,
getAddress,
searchCities,
searchReferenceAddresses,
};

View File

@@ -1,37 +1,32 @@
<template>
<ul
class="record_actions"
v-if="!options.onlyButton"
:class="{ 'sticky-form-buttons': isStickyForm }"
>
<li
v-if="isStickyForm"
class="cancel"
<ul
class="record_actions"
v-if="!options.onlyButton"
:class="{ 'sticky-form-buttons': isStickyForm }"
>
<slot name="before" />
</li>
<li>
<slot name="action" />
</li>
<li v-if="isStickyForm">
<slot name="after" />
</li>
</ul>
<slot
v-else
name="action"
/>
<li v-if="isStickyForm" class="cancel">
<slot name="before" />
</li>
<li>
<slot name="action" />
</li>
<li v-if="isStickyForm">
<slot name="after" />
</li>
</ul>
<slot v-else name="action" />
</template>
<script>
export default {
name: "ActionButtons",
props: ['options', 'defaultz'],
computed: {
isStickyForm() {
return (typeof this.options.stickyActions !== 'undefined') ?
this.options.stickyActions : this.defaultz.stickyActions;
},
}
}
name: "ActionButtons",
props: ["options", "defaultz"],
computed: {
isStickyForm() {
return typeof this.options.stickyActions !== "undefined"
? this.options.stickyActions
: this.defaultz.stickyActions;
},
},
};
</script>

View File

@@ -1,99 +1,119 @@
<template>
<div id="address_map" />
<div id="address_map" />
</template>
<script>
import L from 'leaflet';
import markerIconPng from 'leaflet/dist/images/marker-icon.png'
import 'leaflet/dist/leaflet.css';
import L from "leaflet";
import markerIconPng from "leaflet/dist/images/marker-icon.png";
import "leaflet/dist/leaflet.css";
const lonLatForLeaflet = (coordinates) => {
return [coordinates[1], coordinates[0]];
}
return [coordinates[1], coordinates[0]];
};
export default {
name: 'AddressMap',
props: ['entity'],
data() {
return {
map: null,
marker: null
}
},
computed: {
center() {
return this.entity.addressMap.center;
},
hasAddressPoint() {
if (Object.keys(this.entity.address).length === 0) {
return false;
}
if (null !== this.entity.address.addressReference) {
return true;
}
if (null !== this.entity.address.postcode && null !== this.entity.address.postcode.center) {
return true;
}
return false;
},
/**
*
* @returns {coordinates: [float, float], type: "Point"}
*/
addressPoint() {
if (Object.keys(this.entity.address).length === 0) {
return null;
}
name: "AddressMap",
props: ["entity"],
data() {
return {
map: null,
marker: null,
};
},
computed: {
center() {
return this.entity.addressMap.center;
},
hasAddressPoint() {
if (Object.keys(this.entity.address).length === 0) {
return false;
}
if (null !== this.entity.address.addressReference) {
return true;
}
if (
null !== this.entity.address.postcode &&
null !== this.entity.address.postcode.center
) {
return true;
}
return false;
},
/**
*
* @returns {coordinates: [float, float], type: "Point"}
*/
addressPoint() {
if (Object.keys(this.entity.address).length === 0) {
return null;
}
if (null !== this.entity.address.addressReference) {
return this.entity.address.addressReference.point;
}
if (null !== this.entity.address.addressReference) {
return this.entity.address.addressReference.point;
}
if (null !== this.entity.address.postcode && null !== this.entity.address.postcode.center) {
return this.entity.address.postcode.center;
}
if (
null !== this.entity.address.postcode &&
null !== this.entity.address.postcode.center
) {
return this.entity.address.postcode.center;
}
return null;
},
},
methods:{
init() {
this.map = L.map('address_map');
return null;
},
},
methods: {
init() {
this.map = L.map("address_map");
if (!this.hasAddressPoint) {
this.map.setView(lonLatForLeaflet(this.entity.addressMap.center), this.entity.addressMap.zoom);
} else {
this.map.setView(lonLatForLeaflet(this.addressPoint.coordinates), 15);
}
if (!this.hasAddressPoint) {
this.map.setView(
lonLatForLeaflet(this.entity.addressMap.center),
this.entity.addressMap.zoom,
);
} else {
this.map.setView(
lonLatForLeaflet(this.addressPoint.coordinates),
15,
);
}
this.map.scrollWheelZoom.disable();
this.map.scrollWheelZoom.disable();
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(this.map);
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
attribution:
'&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
}).addTo(this.map);
const markerIcon = L.icon({
iconUrl: markerIconPng,
iconAnchor: [12, 41],
});
const markerIcon = L.icon({
iconUrl: markerIconPng,
iconAnchor: [12, 41],
});
if (!this.hasAddressPoint) {
this.marker = L.marker(lonLatForLeaflet(this.entity.addressMap.center), {icon: markerIcon});
} else {
this.marker = L.marker(lonLatForLeaflet(this.addressPoint.coordinates), {icon: markerIcon});
}
this.marker.addTo(this.map);
},
update() {
if (this.marker && this.entity.addressMap.center) {
this.marker.setLatLng(lonLatForLeaflet(this.entity.addressMap.center));
this.map.panTo(lonLatForLeaflet(this.entity.addressMap.center));
}
}
},
mounted() {
this.init();
},
}
if (!this.hasAddressPoint) {
this.marker = L.marker(
lonLatForLeaflet(this.entity.addressMap.center),
{ icon: markerIcon },
);
} else {
this.marker = L.marker(
lonLatForLeaflet(this.addressPoint.coordinates),
{ icon: markerIcon },
);
}
this.marker.addTo(this.map);
},
update() {
if (this.marker && this.entity.addressMap.center) {
this.marker.setLatLng(
lonLatForLeaflet(this.entity.addressMap.center),
);
this.map.panTo(lonLatForLeaflet(this.entity.addressMap.center));
}
},
},
mounted() {
this.init();
},
};
</script>

View File

@@ -1,158 +1,149 @@
<template>
<h4 class="h3">
{{ $t('fill_an_address') }}
</h4>
<div class="row my-3">
<div
class="col-lg-6"
v-if="!isNoAddress"
>
<div class="form-floating my-1">
<input
class="form-control"
type="text"
name="floor"
:placeholder="$t('floor')"
v-model="floor"
>
<label for="floor">{{ $t('floor') }}</label>
</div>
<div class="form-floating my-1">
<input
class="form-control"
type="text"
name="corridor"
:placeholder="$t('corridor')"
v-model="corridor"
>
<label for="corridor">{{ $t('corridor') }}</label>
</div>
<div class="form-floating my-1">
<input
class="form-control"
type="text"
name="steps"
:placeholder="$t('steps')"
v-model="steps"
>
<label for="steps">{{ $t('steps') }}</label>
</div>
<div class="form-floating my-1">
<input
class="form-control"
type="text"
name="flat"
:placeholder="$t('flat')"
v-model="flat"
>
<label for="flat">{{ $t('flat') }}</label>
</div>
<h4 class="h3">
{{ $t("fill_an_address") }}
</h4>
<div class="row my-3">
<div class="col-lg-6" v-if="!isNoAddress">
<div class="form-floating my-1">
<input
class="form-control"
type="text"
name="floor"
:placeholder="$t('floor')"
v-model="floor"
/>
<label for="floor">{{ $t("floor") }}</label>
</div>
<div class="form-floating my-1">
<input
class="form-control"
type="text"
name="corridor"
:placeholder="$t('corridor')"
v-model="corridor"
/>
<label for="corridor">{{ $t("corridor") }}</label>
</div>
<div class="form-floating my-1">
<input
class="form-control"
type="text"
name="steps"
:placeholder="$t('steps')"
v-model="steps"
/>
<label for="steps">{{ $t("steps") }}</label>
</div>
<div class="form-floating my-1">
<input
class="form-control"
type="text"
name="flat"
:placeholder="$t('flat')"
v-model="flat"
/>
<label for="flat">{{ $t("flat") }}</label>
</div>
</div>
<div :class="isNoAddress ? 'col-lg-12' : 'col-lg-6'">
<div class="form-floating my-1" v-if="!isNoAddress">
<input
class="form-control"
type="text"
name="buildingName"
maxlength="255"
:placeholder="$t('buildingName')"
v-model="buildingName"
/>
<label for="buildingName">{{ $t("buildingName") }}</label>
</div>
<div class="form-floating my-1">
<input
class="form-control"
type="text"
name="extra"
maxlength="255"
:placeholder="$t('extra')"
v-model="extra"
/>
<label for="extra">{{ $t("extra") }}</label>
</div>
<div class="form-floating my-1" v-if="!isNoAddress">
<input
class="form-control"
type="text"
name="distribution"
maxlength="255"
:placeholder="$t('distribution')"
v-model="distribution"
/>
<label for="distribution">{{ $t("distribution") }}</label>
</div>
</div>
</div>
<div :class="isNoAddress ? 'col-lg-12' : 'col-lg-6'">
<div
class="form-floating my-1"
v-if="!isNoAddress"
>
<input
class="form-control"
type="text"
name="buildingName"
maxlength="255"
:placeholder="$t('buildingName')"
v-model="buildingName"
>
<label for="buildingName">{{ $t('buildingName') }}</label>
</div>
<div class="form-floating my-1">
<input
class="form-control"
type="text"
name="extra"
maxlength="255"
:placeholder="$t('extra')"
v-model="extra"
>
<label for="extra">{{ $t('extra') }}</label>
</div>
<div
class="form-floating my-1"
v-if="!isNoAddress"
>
<input
class="form-control"
type="text"
name="distribution"
maxlength="255"
:placeholder="$t('distribution')"
v-model="distribution"
>
<label for="distribution">{{ $t('distribution') }}</label>
</div>
</div>
</div>
</template>
<script>
export default {
name: "AddressMore",
props: ['entity', 'isNoAddress'],
computed: {
floor: {
set(value) {
this.entity.selected.address.floor = value;
},
get() {
return this.entity.selected.address.floor;
}
},
corridor: {
set(value) {
this.entity.selected.address.corridor = value;
},
get() {
return this.entity.selected.address.corridor;
}
},
steps: {
set(value) {
this.entity.selected.address.steps = value;
},
get() {
return this.entity.selected.address.steps;
}
},
flat: {
set(value) {
this.entity.selected.address.flat = value;
},
get() {
return this.entity.selected.address.flat;
}
},
buildingName: {
set(value) {
this.entity.selected.address.buildingName = value;
},
get() {
return this.entity.selected.address.buildingName;
}
},
extra: {
set(value) {
this.entity.selected.address.extra = value;
},
get() {
return this.entity.selected.address.extra;
}
},
distribution: {
set(value) {
this.entity.selected.address.distribution = value;
},
get() {
return this.entity.selected.address.distribution;
}
}
}
}
name: "AddressMore",
props: ["entity", "isNoAddress"],
computed: {
floor: {
set(value) {
this.entity.selected.address.floor = value;
},
get() {
return this.entity.selected.address.floor;
},
},
corridor: {
set(value) {
this.entity.selected.address.corridor = value;
},
get() {
return this.entity.selected.address.corridor;
},
},
steps: {
set(value) {
this.entity.selected.address.steps = value;
},
get() {
return this.entity.selected.address.steps;
},
},
flat: {
set(value) {
this.entity.selected.address.flat = value;
},
get() {
return this.entity.selected.address.flat;
},
},
buildingName: {
set(value) {
this.entity.selected.address.buildingName = value;
},
get() {
return this.entity.selected.address.buildingName;
},
},
extra: {
set(value) {
this.entity.selected.address.extra = value;
},
get() {
return this.entity.selected.address.extra;
},
},
distribution: {
set(value) {
this.entity.selected.address.distribution = value;
},
get() {
return this.entity.selected.address.distribution;
},
},
},
};
</script>

View File

@@ -1,204 +1,227 @@
<template>
<div class="my-1">
<label
class="col-form-label"
for="addressSelector"
>{{ $t('address') }}</label>
<VueMultiselect
id="addressSelector"
v-model="value"
:placeholder="$t('select_address')"
:tag-placeholder="$t('create_address')"
:select-label="$t('multiselect.select_label')"
:deselect-label="$t('create_address')"
:selected-label="$t('multiselect.selected_label')"
@search-change="listenInputSearch"
:internal-search="false"
ref="addressSelector"
@select="selectAddress"
@remove="remove"
name="field"
track-by="id"
label="value"
:custom-label="transName"
:taggable="true"
:multiple="false"
@tag="addAddress"
:loading="isLoading"
:options="addresses"
/>
</div>
<div class="my-1">
<label class="col-form-label" for="addressSelector">{{
$t("address")
}}</label>
<VueMultiselect
id="addressSelector"
v-model="value"
:placeholder="$t('select_address')"
:tag-placeholder="$t('create_address')"
:select-label="$t('multiselect.select_label')"
:deselect-label="$t('create_address')"
:selected-label="$t('multiselect.selected_label')"
@search-change="listenInputSearch"
:internal-search="false"
ref="addressSelector"
@select="selectAddress"
@remove="remove"
name="field"
track-by="id"
label="value"
:custom-label="transName"
:taggable="true"
:multiple="false"
@tag="addAddress"
:loading="isLoading"
:options="addresses"
/>
</div>
<div
class="custom-address row g-1"
v-if="writeNewAddress || writeNewPostalCode || (isEnteredCustomAddress && !isAddressSelectorOpen)"
>
<div class="col-10">
<div class="form-floating">
<input
class="form-control"
type="text"
name="street"
:placeholder="$t('street')"
v-model="street"
>
<label for="street">{{ $t('street') }}</label>
</div>
<div
class="custom-address row g-1"
v-if="
writeNewAddress ||
writeNewPostalCode ||
(isEnteredCustomAddress && !isAddressSelectorOpen)
"
>
<div class="col-10">
<div class="form-floating">
<input
class="form-control"
type="text"
name="street"
:placeholder="$t('street')"
v-model="street"
/>
<label for="street">{{ $t("street") }}</label>
</div>
</div>
<div class="col-2">
<div class="form-floating">
<input
class="form-control"
type="text"
name="streetNumber"
:placeholder="$t('streetNumber')"
v-model="streetNumber"
/>
<label for="streetNumber">{{ $t("streetNumber") }}</label>
</div>
</div>
</div>
<div class="col-2">
<div class="form-floating">
<input
class="form-control"
type="text"
name="streetNumber"
:placeholder="$t('streetNumber')"
v-model="streetNumber"
>
<label for="streetNumber">{{ $t('streetNumber') }}</label>
</div>
</div>
</div>
</template>
<script>
import VueMultiselect from 'vue-multiselect';
import { searchReferenceAddresses, fetchReferenceAddresses } from '../../api.js';
import VueMultiselect from "vue-multiselect";
import {
searchReferenceAddresses,
fetchReferenceAddresses,
} from "../../api.js";
export default {
name: 'AddressSelection',
components: { VueMultiselect },
props: ['entity', 'context', 'updateMapCenter', 'flag', 'checkErrors'],
data() {
return {
value: this.context.edit ? this.entity.address.addressReference : null,
isLoading: false
}
},
computed: {
writeNewAddress() {
return this.entity.selected.writeNew.address;
},
writeNewPostalCode() {
return this.entity.selected.writeNew.postCode;
},
isAddressSelectorOpen() {
return this.$refs.addressSelector.$data.isOpen;
},
isEnteredCustomAddress() {
return this.$data.value !== null && typeof this.$data.value.text !== 'undefined';
},
addresses() {
return this.entity.loaded.addresses;
},
street: {
set(value) {
this.entity.selected.address.street = value;
},
get() {
return this.entity.selected.address.street;
}
},
streetNumber: {
set(value) {
this.entity.selected.address.streetNumber = value;
},
get() {
return this.entity.selected.address.streetNumber;
}
},
},
methods: {
transName(value) {
return value.streetNumber === undefined ? value.street : `${value.streetNumber}, ${value.street}`
},
selectAddress(value) {
this.entity.selected.address = value;
this.entity.selected.address.addressReference = {
id: value.id
};
this.entity.selected.address.street = value.street;
this.entity.selected.address.streetNumber = value.streetNumber;
this.entity.selected.writeNew.address = false;
this.updateMapCenter(value.point);
this.checkErrors();
},
remove() {
this.entity.selected.address = {};
this.checkErrors();
},
listenInputSearch(query) {
//console.log('listenInputSearch', query, this.isAddressSelectorOpen);
if (!this.entity.selected.writeNew.postcode && 'id' in this.entity.selected.city) {
if (query.length > 2) {
this.isLoading = true;
searchReferenceAddresses(query, this.entity.selected.city).then(
addresses => new Promise((resolve, reject) => {
this.entity.loaded.addresses = addresses.results;
this.isLoading = false;
resolve();
}))
.catch((error) => {
console.log(error); //TODO better error handling
this.isLoading = false;
});
} else {
if (query.length === 0) { // Fetch all cities when suppressing the query
this.isLoading = true;
fetchReferenceAddresses(this.entity.selected.city).then(
addresses => new Promise((resolve, reject) => {
this.entity.loaded.addresses = addresses.results;
this.isLoading = false;
resolve();
}))
.catch((error) => {
console.log(error);
this.isLoading = false;
});
}
}
}
if (this.isAddressSelectorOpen) {
this.$data.value = { text: query };
} else if (this.isEnteredCustomAddress) {
let addr = this.splitAddress(this.$data.value.text);
this.entity.selected.address.street = addr.street;
this.entity.selected.address.streetNumber = addr.number;
this.entity.selected.writeNew.address = true;
name: "AddressSelection",
components: { VueMultiselect },
props: ["entity", "context", "updateMapCenter", "flag", "checkErrors"],
data() {
return {
value: this.context.edit
? this.entity.address.addressReference
: null,
isLoading: false,
};
},
computed: {
writeNewAddress() {
return this.entity.selected.writeNew.address;
},
writeNewPostalCode() {
return this.entity.selected.writeNew.postCode;
},
isAddressSelectorOpen() {
return this.$refs.addressSelector.$data.isOpen;
},
isEnteredCustomAddress() {
return (
this.$data.value !== null &&
typeof this.$data.value.text !== "undefined"
);
},
addresses() {
return this.entity.loaded.addresses;
},
street: {
set(value) {
this.entity.selected.address.street = value;
},
get() {
return this.entity.selected.address.street;
},
},
streetNumber: {
set(value) {
this.entity.selected.address.streetNumber = value;
},
get() {
return this.entity.selected.address.streetNumber;
},
},
},
methods: {
transName(value) {
return value.streetNumber === undefined
? value.street
: `${value.streetNumber}, ${value.street}`;
},
selectAddress(value) {
this.entity.selected.address = value;
this.entity.selected.address.addressReference = {
id: value.id,
};
this.entity.selected.address.street = value.street;
this.entity.selected.address.streetNumber = value.streetNumber;
this.entity.selected.writeNew.address = false;
this.updateMapCenter(value.point);
this.checkErrors();
}
},
splitAddress(address) {
let substr = address
.split(',')
.map(s => s.trim());
if (substr.length === 1) {
substr = address.split(' ');
}
let decimal = [];
substr.forEach((s, i) => { decimal[i] = /^\d+$/.test(s) });
if (decimal[0] === true) {
return {
number: substr.shift(),
street: substr.join(' ')
},
remove() {
this.entity.selected.address = {};
this.checkErrors();
},
listenInputSearch(query) {
//console.log('listenInputSearch', query, this.isAddressSelectorOpen);
if (
!this.entity.selected.writeNew.postcode &&
"id" in this.entity.selected.city
) {
if (query.length > 2) {
this.isLoading = true;
searchReferenceAddresses(query, this.entity.selected.city)
.then(
(addresses) =>
new Promise((resolve, reject) => {
this.entity.loaded.addresses =
addresses.results;
this.isLoading = false;
resolve();
}),
)
.catch((error) => {
console.log(error); //TODO better error handling
this.isLoading = false;
});
} else {
if (query.length === 0) {
// Fetch all cities when suppressing the query
this.isLoading = true;
fetchReferenceAddresses(this.entity.selected.city)
.then(
(addresses) =>
new Promise((resolve, reject) => {
this.entity.loaded.addresses =
addresses.results;
this.isLoading = false;
resolve();
}),
)
.catch((error) => {
console.log(error);
this.isLoading = false;
});
}
}
}
}
else if (decimal[decimal.length - 1] === true) {
return {
number: substr.pop(),
street: substr.join(' ')
if (this.isAddressSelectorOpen) {
this.$data.value = { text: query };
} else if (this.isEnteredCustomAddress) {
let addr = this.splitAddress(this.$data.value.text);
this.entity.selected.address.street = addr.street;
this.entity.selected.address.streetNumber = addr.number;
this.entity.selected.writeNew.address = true;
this.checkErrors();
}
}
return {
number: '',
street: substr.join(' ')
}
},
addAddress() {
this.entity.selected.writeNew.address = true;
}
}
},
splitAddress(address) {
let substr = address.split(",").map((s) => s.trim());
if (substr.length === 1) {
substr = address.split(" ");
}
let decimal = [];
substr.forEach((s, i) => {
decimal[i] = /^\d+$/.test(s);
});
if (decimal[0] === true) {
return {
number: substr.shift(),
street: substr.join(" "),
};
} else if (decimal[decimal.length - 1] === true) {
return {
number: substr.pop(),
street: substr.join(" "),
};
}
return {
number: "",
street: substr.join(" "),
};
},
addAddress() {
this.entity.selected.writeNew.address = true;
},
},
};
</script>

View File

@@ -1,221 +1,251 @@
<template>
<div class="my-1">
<label class="col-form-label">{{ $t('city') }}</label>
<VueMultiselect
id="citySelector"
v-model="value"
@search-change="listenInputSearch"
ref="citySelector"
@select="selectCity"
@remove="remove"
name="field"
track-by="id"
label="value"
:custom-label="transName"
:placeholder="$t('select_city')"
:select-label="$t('multiselect.select_label')"
:deselect-label="$t('create_postal_code')"
:selected-label="$t('multiselect.selected_label')"
:taggable="true"
:multiple="false"
:internal-search="false"
@tag="addPostcode"
:tag-placeholder="$t('create_postal_code')"
:loading="isLoading"
:options="cities"
/>
</div>
<div class="my-1">
<label class="col-form-label">{{ $t("city") }}</label>
<VueMultiselect
id="citySelector"
v-model="value"
@search-change="listenInputSearch"
ref="citySelector"
@select="selectCity"
@remove="remove"
name="field"
track-by="id"
label="value"
:custom-label="transName"
:placeholder="$t('select_city')"
:select-label="$t('multiselect.select_label')"
:deselect-label="$t('create_postal_code')"
:selected-label="$t('multiselect.selected_label')"
:taggable="true"
:multiple="false"
:internal-search="false"
@tag="addPostcode"
:tag-placeholder="$t('create_postal_code')"
:loading="isLoading"
:options="cities"
/>
</div>
<div
class="custom-postcode row g-1"
v-if="writeNewPostcode || (isEnteredCustomCity && !isCitySelectorOpen)"
>
<div class="col-4">
<div class="form-floating">
<input
class="form-control"
type="text"
id="code"
:placeholder="$t('postalCode_code')"
v-model="code"
>
<label for="code">{{ $t('postalCode_code') }}</label>
</div>
<div
class="custom-postcode row g-1"
v-if="writeNewPostcode || (isEnteredCustomCity && !isCitySelectorOpen)"
>
<div class="col-4">
<div class="form-floating">
<input
class="form-control"
type="text"
id="code"
:placeholder="$t('postalCode_code')"
v-model="code"
/>
<label for="code">{{ $t("postalCode_code") }}</label>
</div>
</div>
<div class="col-8">
<div class="form-floating">
<input
class="form-control"
type="text"
id="name"
:placeholder="$t('postalCode_name')"
v-model="name"
/>
<label for="name">{{ $t("postalCode_name") }}</label>
</div>
</div>
</div>
<div class="col-8">
<div class="form-floating">
<input
class="form-control"
type="text"
id="name"
:placeholder="$t('postalCode_name')"
v-model="name"
>
<label for="name">{{ $t('postalCode_name') }}</label>
</div>
</div>
</div>
</template>
<script>
import VueMultiselect from 'vue-multiselect';
import { searchCities, fetchCities } from '../../api.js';
import VueMultiselect from "vue-multiselect";
import { searchCities, fetchCities } from "../../api.js";
export default {
name: 'CitySelection',
components: { VueMultiselect },
props: ['entity', 'context', 'focusOnAddress', 'updateMapCenter', 'flag', 'checkErrors'],
emits: ['getReferenceAddresses'],
data() {
return {
value: this.context.edit ? this.entity.address.postcode : null,
isLoading: false,
}
},
computed: {
writeNewPostcode() {
return this.entity.selected.writeNew.postcode;
},
isCitySelectorOpen() {
return this.$refs.citySelector.$data.isOpen;
},
isEnteredCustomCity() {
return this.$data.value !== null && typeof this.$data.value.text !== 'undefined';
},
cities() {
return this.entity.loaded.cities.sort(
(a, b) => Number(a.code) - Number(b.code) || a.name > b.name
)
},
name: {
set(value) {
this.entity.selected.postcode.name = value;
},
get() {
return this.entity.selected.postcode.name;
}
},
code: {
set(value) {
this.entity.selected.postcode.code= value;
},
get() {
return this.entity.selected.postcode.code;
}
},
},
mounted() {
console.log('writeNew.postcode', this.entity.selected.writeNew.postcode, 'in mounted');
if (this.context.edit) {
this.entity.selected.city = this.value;
this.entity.selected.postcode.name = this.value.name;
this.entity.selected.postcode.code = this.value.code;
this.$emit('getReferenceAddresses', this.value);
if (typeof this.value.center !== 'undefined') {
this.updateMapCenter(this.value.center);
if (this.value.center.coordinates) {
this.entity.selected.postcode.coordinates = this.value.center.coordinates;
name: "CitySelection",
components: { VueMultiselect },
props: [
"entity",
"context",
"focusOnAddress",
"updateMapCenter",
"flag",
"checkErrors",
],
emits: ["getReferenceAddresses"],
data() {
return {
value: this.context.edit ? this.entity.address.postcode : null,
isLoading: false,
};
},
computed: {
writeNewPostcode() {
return this.entity.selected.writeNew.postcode;
},
isCitySelectorOpen() {
return this.$refs.citySelector.$data.isOpen;
},
isEnteredCustomCity() {
return (
this.$data.value !== null &&
typeof this.$data.value.text !== "undefined"
);
},
cities() {
return this.entity.loaded.cities.sort(
(a, b) => Number(a.code) - Number(b.code) || a.name > b.name,
);
},
name: {
set(value) {
this.entity.selected.postcode.name = value;
},
get() {
return this.entity.selected.postcode.name;
},
},
code: {
set(value) {
this.entity.selected.postcode.code = value;
},
get() {
return this.entity.selected.postcode.code;
},
},
},
mounted() {
console.log(
"writeNew.postcode",
this.entity.selected.writeNew.postcode,
"in mounted",
);
if (this.context.edit) {
this.entity.selected.city = this.value;
this.entity.selected.postcode.name = this.value.name;
this.entity.selected.postcode.code = this.value.code;
this.$emit("getReferenceAddresses", this.value);
if (typeof this.value.center !== "undefined") {
this.updateMapCenter(this.value.center);
if (this.value.center.coordinates) {
this.entity.selected.postcode.coordinates =
this.value.center.coordinates;
}
}
}
}
},
methods: {
transName(value) {
return (value.code && value.name) ? `${value.name} (${value.code})` : '';
},
selectCity(value) {
console.log(value)
this.entity.selected.city = value;
this.entity.selected.postcode.name = value.name;
this.entity.selected.postcode.code = value.code;
if (value.center) {
this.entity.selected.postcode.coordinates = value.center.coordinates;
}
this.entity.selected.writeNew.postcode = false;
this.$emit('getReferenceAddresses', value);
this.focusOnAddress();
if (value.center) {
this.updateMapCenter(value.center);
}
this.checkErrors();
},
remove() {
this.entity.selected.city = {};
this.checkErrors();
},
listenInputSearch(query) {
if (query.length > 2) {
this.isLoading = true;
searchCities(query, this.entity.selected.country).then(
cities => new Promise((resolve, reject) => {
this.entity.loaded.cities = cities.results.filter(c => c.origin !== 3); // filter out user-defined cities
this.isLoading = false;
resolve();
}))
.catch((error) => {
console.log(error); //TODO better error handling
this.isLoading = false;
}
},
methods: {
transName(value) {
return value.code && value.name
? `${value.name} (${value.code})`
: "";
},
selectCity(value) {
console.log(value);
this.entity.selected.city = value;
this.entity.selected.postcode.name = value.name;
this.entity.selected.postcode.code = value.code;
if (value.center) {
this.entity.selected.postcode.coordinates =
value.center.coordinates;
}
this.entity.selected.writeNew.postcode = false;
this.$emit("getReferenceAddresses", value);
this.focusOnAddress();
if (value.center) {
this.updateMapCenter(value.center);
}
this.checkErrors();
},
remove() {
this.entity.selected.city = {};
this.checkErrors();
},
listenInputSearch(query) {
if (query.length > 2) {
this.isLoading = true;
searchCities(query, this.entity.selected.country)
.then(
(cities) =>
new Promise((resolve, reject) => {
this.entity.loaded.cities =
cities.results.filter(
(c) => c.origin !== 3,
); // filter out user-defined cities
this.isLoading = false;
resolve();
}),
)
.catch((error) => {
console.log(error); //TODO better error handling
this.isLoading = false;
});
} else {
if (query.length === 0) {
// Fetch all cities when suppressing the query
this.isLoading = true;
fetchCities(this.entity.selected.country)
.then(
(cities) =>
new Promise((resolve, reject) => {
this.entity.loaded.cities =
cities.results.filter(
(c) => c.origin !== 3,
); // filter out user-defined cities
this.isLoading = false;
resolve();
}),
)
.catch((error) => {
console.log(error);
this.isLoading = false;
});
}
}
if (this.isCitySelectorOpen) {
this.$data.value = { text: query };
} else if (this.isEnteredCustomCity) {
let city = this.splitCity(this.$data.value.text);
this.$refs.citySelector.currentOptionLabel = "";
this.entity.selected.city = city;
this.entity.selected.postcode.name = city.name;
this.entity.selected.postcode.code = city.code;
this.entity.selected.writeNew.postcode = true;
console.log("writeNew.postcode true, in listenInputSearch");
}
},
splitCity(city) {
let substr = city.split("-").map((s) => s.trim());
if (substr.length === 1) {
substr = city.split(" ");
}
//console.log('substr', substr);
let decimal = [];
substr.forEach((s, i) => {
decimal[i] = /^\d+$/.test(s);
});
} else {
if (query.length === 0) { // Fetch all cities when suppressing the query
this.isLoading = true;
fetchCities(this.entity.selected.country).then(
cities => new Promise((resolve, reject) => {
this.entity.loaded.cities = cities.results.filter(c => c.origin !== 3); // filter out user-defined cities
this.isLoading = false;
resolve();
}))
.catch((error) => {
console.log(error)
this.isLoading = false;
});
if (decimal[0] === true) {
return {
code: substr.shift(),
name: substr.join(" "),
};
} else if (decimal[decimal.length - 1] === true) {
return {
code: substr.pop(),
name: substr.join(" "),
};
}
}
if (this.isCitySelectorOpen) {
this.$data.value = { text: query };
} else if (this.isEnteredCustomCity) {
let city = this.splitCity(this.$data.value.text);
this.$refs.citySelector.currentOptionLabel = '';
this.entity.selected.city = city;
this.entity.selected.postcode.name = city.name;
this.entity.selected.postcode.code = city.code;
return {
code: "",
name: substr.join(" "),
};
},
addPostcode() {
console.log("addPostcode: pass here ?? never, it seems");
this.entity.selected.writeNew.postcode = true;
console.log('writeNew.postcode true, in listenInputSearch');
}
},
splitCity(city) {
let substr = city
.split('-')
.map(s => s.trim());
if (substr.length === 1) {
substr = city.split(' ');
}
//console.log('substr', substr);
let decimal = [];
substr.forEach((s, i) => { decimal[i] = /^\d+$/.test(s) });
if (decimal[0] === true) {
return {
code: substr.shift(),
name: substr.join(' ')
}
}
else if (decimal[decimal.length - 1] === true) {
return {
code: substr.pop(),
name: substr.join(' ')
}
}
return {
code: '',
name: substr.join(' ')
}
},
addPostcode() {
console.log('addPostcode: pass here ?? never, it seems');
this.entity.selected.writeNew.postcode = true;
console.log('writeNew.postcode true, in addPostcode');
}
}
console.log("writeNew.postcode true, in addPostcode");
},
},
};
</script>

View File

@@ -1,76 +1,86 @@
<template>
<div class="my-1">
<label
class="col-form-label"
for="countrySelect"
>{{ $t('country') }}</label>
<VueMultiselect
id="countrySelect"
label="name"
track-by="id"
:custom-label="transName"
:placeholder="$t('select_country')"
:options="sortedCountries"
v-model="value"
:select-label="$t('multiselect.select_label')"
:deselect-label="$t('multiselect.deselect_label')"
:selected-label="$t('multiselect.selected_label')"
@select="selectCountry"
@remove="remove"
/>
</div>
<div class="my-1">
<label class="col-form-label" for="countrySelect">{{
$t("country")
}}</label>
<VueMultiselect
id="countrySelect"
label="name"
track-by="id"
:custom-label="transName"
:placeholder="$t('select_country')"
:options="sortedCountries"
v-model="value"
:select-label="$t('multiselect.select_label')"
:deselect-label="$t('multiselect.deselect_label')"
:selected-label="$t('multiselect.selected_label')"
@select="selectCountry"
@remove="remove"
/>
</div>
</template>
<script>
import VueMultiselect from 'vue-multiselect';
import VueMultiselect from "vue-multiselect";
export default {
name: 'CountrySelection',
components: { VueMultiselect },
props: ['context', 'entity', 'flag', 'checkErrors'],
emits: ['getCities'],
data() {
return {
value: this.selectCountryByCode(
this.context.edit ? this.entity.selected.country.code : this.context.defaults.default_country
)
}
},
computed: {
sortedCountries() {
const countries = this.entity.loaded.countries;
let sortedCountries = [];
sortedCountries.push(...countries.filter(c => c.countryCode === 'FR'))
sortedCountries.push(...countries.filter(c => c.countryCode === 'BE'))
sortedCountries.push(...countries.filter(c => c.countryCode !== 'FR').filter(c => c.countryCode !== 'BE'))
return sortedCountries;
},
},
mounted() {
console.log('country selection mounted', this.value);
if (this.value !== undefined) {
this.selectCountry(this.value);
}
},
methods: {
selectCountryByCode(countryCode) {
return this.entity.loaded.countries.filter(c => c.countryCode === countryCode)[0];
},
transName ({ name }) {
return name.fr //TODO multilang
},
selectCountry(value) {
//console.log('select country', value);
this.entity.selected.country = value;
this.$emit('getCities', value);
this.checkErrors();
},
remove() {
this.entity.selected.country = null;
this.checkErrors();
},
}
name: "CountrySelection",
components: { VueMultiselect },
props: ["context", "entity", "flag", "checkErrors"],
emits: ["getCities"],
data() {
return {
value: this.selectCountryByCode(
this.context.edit
? this.entity.selected.country.code
: this.context.defaults.default_country,
),
};
},
computed: {
sortedCountries() {
const countries = this.entity.loaded.countries;
let sortedCountries = [];
sortedCountries.push(
...countries.filter((c) => c.countryCode === "FR"),
);
sortedCountries.push(
...countries.filter((c) => c.countryCode === "BE"),
);
sortedCountries.push(
...countries
.filter((c) => c.countryCode !== "FR")
.filter((c) => c.countryCode !== "BE"),
);
return sortedCountries;
},
},
mounted() {
console.log("country selection mounted", this.value);
if (this.value !== undefined) {
this.selectCountry(this.value);
}
},
methods: {
selectCountryByCode(countryCode) {
return this.entity.loaded.countries.filter(
(c) => c.countryCode === countryCode,
)[0];
},
transName({ name }) {
return name.fr; //TODO multilang
},
selectCountry(value) {
//console.log('select country', value);
this.entity.selected.country = value;
this.$emit("getCities", value);
this.checkErrors();
},
remove() {
this.entity.selected.country = null;
this.checkErrors();
},
},
};
</script>

View File

@@ -1,166 +1,187 @@
<template>
<div
v-if="insideModal === false"
class="loading"
>
<i
v-if="flag.loading"
class="fa fa-circle-o-notch fa-spin fa-2x fa-fw"
/>
<span class="sr-only">{{ $t('loading') }}</span>
</div>
<div
v-if="errorMsg && errorMsg.length > 0"
class="alert alert-danger"
>
{{ errorMsg }}
</div>
<address-render-box :address="selectedAddress" />
<div class="row">
<div
v-if="showDateFrom"
class="col-lg-6 address-valid date-since"
>
<h3>{{ $t(getValidFromDateText) }}</h3>
<div class="input-group mb-3">
<span
class="input-group-text"
id="validFrom"
><i class="fa fa-fw fa-calendar" /></span>
<input
type="date"
class="form-control form-control-lg"
name="validFrom"
:placeholder="$t(getValidFromDateText)"
v-model="validFrom"
aria-describedby="validFrom"
>
</div>
<div v-if="insideModal === false" class="loading">
<i
v-if="flag.loading"
class="fa fa-circle-o-notch fa-spin fa-2x fa-fw"
/>
<span class="sr-only">{{ $t("loading") }}</span>
</div>
<div
v-if="showDateTo"
class="col-lg-6 address-valid date-until"
>
<h3>{{ $t(getValidToDateText) }}</h3>
<div class="input-group mb-3">
<span
class="input-group-text"
id="validTo"
><i class="fa fa-fw fa-calendar" /></span>
<input
type="date"
class="form-control form-control-lg"
name="validTo"
:placeholder="$t(getValidToDateText)"
v-model="validTo"
aria-describedby="validTo"
>
</div>
<div v-if="errorMsg && errorMsg.length > 0" class="alert alert-danger">
{{ errorMsg }}
</div>
</div>
<action-buttons
v-if="insideModal === false"
:options="this.options"
:defaultz="this.defaultz"
>
<template #before>
<slot name="before" />
</template>
<template #action>
<slot name="action" />
</template>
<template #after>
<slot name="after" />
</template>
</action-buttons>
<address-render-box :address="selectedAddress" />
<div class="row">
<div v-if="showDateFrom" class="col-lg-6 address-valid date-since">
<h3>{{ $t(getValidFromDateText) }}</h3>
<div class="input-group mb-3">
<span class="input-group-text" id="validFrom"
><i class="fa fa-fw fa-calendar"
/></span>
<input
type="date"
class="form-control form-control-lg"
name="validFrom"
:placeholder="$t(getValidFromDateText)"
v-model="validFrom"
aria-describedby="validFrom"
/>
</div>
</div>
<div v-if="showDateTo" class="col-lg-6 address-valid date-until">
<h3>{{ $t(getValidToDateText) }}</h3>
<div class="input-group mb-3">
<span class="input-group-text" id="validTo"
><i class="fa fa-fw fa-calendar"
/></span>
<input
type="date"
class="form-control form-control-lg"
name="validTo"
:placeholder="$t(getValidToDateText)"
v-model="validTo"
aria-describedby="validTo"
/>
</div>
</div>
</div>
<action-buttons
v-if="insideModal === false"
:options="this.options"
:defaultz="this.defaultz"
>
<template #before>
<slot name="before" />
</template>
<template #action>
<slot name="action" />
</template>
<template #after>
<slot name="after" />
</template>
</action-buttons>
</template>
<script>
import { dateToISO, ISOToDate } from 'ChillMainAssets/chill/js/date';
import AddressRenderBox from 'ChillMainAssets/vuejs/_components/Entity/AddressRenderBox.vue';
import ActionButtons from './ActionButtons.vue';
import { dateToISO, ISOToDate } from "ChillMainAssets/chill/js/date";
import AddressRenderBox from "ChillMainAssets/vuejs/_components/Entity/AddressRenderBox.vue";
import ActionButtons from "./ActionButtons.vue";
export default {
name: "DatePane",
components: {
AddressRenderBox,
ActionButtons
},
props: [
'context',
'options',
'defaultz',
'flag',
'entity',
'errorMsg',
'insideModal'
],
computed: {
address() {
return this.entity.address;
},
validFrom: {
set(value) {
this.entity.selected.valid.from = ISOToDate(value);
},
get() {
return dateToISO(this.entity.selected.valid.from);
}
},
validTo: {
set(value) {
this.entity.selected.valid.to = ISOToDate(value);
},
get() {
return dateToISO(this.entity.selected.valid.to);
}
},
getValidFromDateText() {
return (this.context.target.name === 'household') ? 'move_date' : 'validFrom';
},
getValidToDateText() {
return 'validTo';
},
showDateFrom() {
return !this.context.edit && this.options.useDate.validFrom;
},
showDateTo() {
return !this.context.edit && this.options.useDate.validTo;
},
selectedAddress() {
let address = {};
name: "DatePane",
components: {
AddressRenderBox,
ActionButtons,
},
props: [
"context",
"options",
"defaultz",
"flag",
"entity",
"errorMsg",
"insideModal",
],
computed: {
address() {
return this.entity.address;
},
validFrom: {
set(value) {
this.entity.selected.valid.from = ISOToDate(value);
},
get() {
return dateToISO(this.entity.selected.valid.from);
},
},
validTo: {
set(value) {
this.entity.selected.valid.to = ISOToDate(value);
},
get() {
return dateToISO(this.entity.selected.valid.to);
},
},
getValidFromDateText() {
return this.context.target.name === "household"
? "move_date"
: "validFrom";
},
getValidToDateText() {
return "validTo";
},
showDateFrom() {
return !this.context.edit && this.options.useDate.validFrom;
},
showDateTo() {
return !this.context.edit && this.options.useDate.validTo;
},
selectedAddress() {
let address = {};
address['country'] = (this.entity.selected.country) ? this.entity.selected.country : null;
address['postcode'] = (this.entity.selected.postcode) ? this.entity.selected.postcode : null;
address["country"] = this.entity.selected.country
? this.entity.selected.country
: null;
address["postcode"] = this.entity.selected.postcode
? this.entity.selected.postcode
: null;
if (this.entity.selected.address) {
let number = (this.entity.selected.address.streetNumber) ? this.entity.selected.address.streetNumber : null;
let street = (this.entity.selected.address.street) ? this.entity.selected.address.street : null;
address['text'] = number + ', ' + street;
if (this.entity.selected.address) {
let number = this.entity.selected.address.streetNumber
? this.entity.selected.address.streetNumber
: null;
let street = this.entity.selected.address.street
? this.entity.selected.address.street
: null;
address["text"] = number + ", " + street;
address['street'] = (this.entity.selected.address.street) ? this.entity.selected.address.street : null;
address['streetNumber'] = (this.entity.selected.address.streetNumber) ? this.entity.selected.address.streetNumber : null;
address['floor'] = (this.entity.selected.address.floor) ? this.entity.selected.address.floor : null;
address['corridor'] = (this.entity.selected.address.corridor) ? this.entity.selected.address.corridor : null;
address['steps'] = (this.entity.selected.address.steps) ? this.entity.selected.address.steps : null;
address['flat'] = (this.entity.selected.address.flat) ? this.entity.selected.address.flat : null;
address['buildingName'] = (this.entity.selected.address.buildingName) ? this.entity.selected.address.buildingName : null;
address['distribution'] = (this.entity.selected.address.distribution) ? this.entity.selected.address.distribution : null;
address['extra'] = (this.entity.selected.address.extra) ? this.entity.selected.address.extra : null;
}
address["street"] = this.entity.selected.address.street
? this.entity.selected.address.street
: null;
address["streetNumber"] = this.entity.selected.address
.streetNumber
? this.entity.selected.address.streetNumber
: null;
address["floor"] = this.entity.selected.address.floor
? this.entity.selected.address.floor
: null;
address["corridor"] = this.entity.selected.address.corridor
? this.entity.selected.address.corridor
: null;
address["steps"] = this.entity.selected.address.steps
? this.entity.selected.address.steps
: null;
address["flat"] = this.entity.selected.address.flat
? this.entity.selected.address.flat
: null;
address["buildingName"] = this.entity.selected.address
.buildingName
? this.entity.selected.address.buildingName
: null;
address["distribution"] = this.entity.selected.address
.distribution
? this.entity.selected.address.distribution
: null;
address["extra"] = this.entity.selected.address.extra
? this.entity.selected.address.extra
: null;
}
if (this.entity.selected.valid) {
address['validFrom'] = (this.entity.selected.valid.from) ? dateToISO(this.entity.selected.valid.from) : null;
address['validTo'] = (this.entity.selected.valid.to) ? dateToISO(this.entity.selected.valid.to) : null;
}
if (this.entity.selected.valid) {
address["validFrom"] = this.entity.selected.valid.from
? dateToISO(this.entity.selected.valid.from)
: null;
address["validTo"] = this.entity.selected.valid.to
? dateToISO(this.entity.selected.valid.to)
: null;
}
return address;
}
}
}
return address;
},
},
};
</script>

View File

@@ -1,216 +1,197 @@
<template>
<div class="address-form">
<!-- Not display in modal -->
<div
v-if="insideModal === false"
class="loading"
>
<i
v-if="flag.loading"
class="fa fa-circle-o-notch fa-spin fa-2x fa-fw"
/>
<span class="sr-only">Loading...</span>
</div>
<div class="address-form">
<!-- Not display in modal -->
<div v-if="insideModal === false" class="loading">
<i
v-if="flag.loading"
class="fa fa-circle-o-notch fa-spin fa-2x fa-fw"
/>
<span class="sr-only">Loading...</span>
</div>
<div
v-if="errors.length"
class="alert alert-warning"
>
<ul>
<li
v-for="(e, i) in errors"
:key="i"
<div v-if="errors.length" class="alert alert-warning">
<ul>
<li v-for="(e, i) in errors" :key="i">
{{ e }}
</li>
</ul>
</div>
<h4 class="h3">
{{ $t("select_an_address_title") }}
</h4>
<div class="row my-3">
<div class="col-lg-6">
<div class="form-check">
<input
type="checkbox"
class="form-check-input"
id="isConfidential"
v-model="isConfidential"
:value="valueConfidential"
/>
<label class="form-check-label" for="isConfidential">
{{ $t("isConfidential") }}
</label>
</div>
<div class="form-check">
<input
type="checkbox"
class="form-check-input"
id="isNoAddress"
v-model="isNoAddress"
:value="value"
/>
<label class="form-check-label" for="isNoAddress">
{{ $t("isNoAddress") }}
</label>
</div>
<country-selection
:context="context"
:entity="entity"
:flag="flag"
:check-errors="checkErrors"
@get-cities="$emit('getCities', selected.country)"
/>
<city-selection
:entity="entity"
:context="context"
:focus-on-address="focusOnAddress"
:update-map-center="updateMapCenter"
:flag="flag"
:check-errors="checkErrors"
@get-reference-addresses="
$emit('getReferenceAddresses', selected.city)
"
/>
<address-selection
v-if="!isNoAddress"
:entity="entity"
:context="context"
:update-map-center="updateMapCenter"
:flag="flag"
:check-errors="checkErrors"
/>
</div>
<div class="col-lg-6 mt-3 mt-lg-0">
<address-map :entity="entity" ref="addressMap" />
</div>
</div>
<address-more :entity="entity" :is-no-address="isNoAddress" />
<action-buttons
v-if="insideModal === false"
:options="this.options"
:defaultz="this.defaultz"
>
{{ e }}
</li>
</ul>
<template #before>
<slot name="before" />
</template>
<template #action>
<slot name="action" />
</template>
<template #after>
<slot name="after" />
</template>
</action-buttons>
</div>
<h4 class="h3">
{{ $t('select_an_address_title') }}
</h4>
<div class="row my-3">
<div class="col-lg-6">
<div class="form-check">
<input
type="checkbox"
class="form-check-input"
id="isConfidential"
v-model="isConfidential"
:value="valueConfidential"
>
<label
class="form-check-label"
for="isConfidential"
>
{{ $t('isConfidential') }}
</label>
</div>
<div class="form-check">
<input
type="checkbox"
class="form-check-input"
id="isNoAddress"
v-model="isNoAddress"
:value="value"
>
<label
class="form-check-label"
for="isNoAddress"
>
{{ $t('isNoAddress') }}
</label>
</div>
<country-selection
:context="context"
:entity="entity"
:flag="flag"
:check-errors="checkErrors"
@get-cities="$emit('getCities', selected.country)"
/>
<city-selection
:entity="entity"
:context="context"
:focus-on-address="focusOnAddress"
:update-map-center="updateMapCenter"
:flag="flag"
:check-errors="checkErrors"
@get-reference-addresses="$emit('getReferenceAddresses', selected.city)"
/>
<address-selection
v-if="!isNoAddress"
:entity="entity"
:context="context"
:update-map-center="updateMapCenter"
:flag="flag"
:check-errors="checkErrors"
/>
</div>
<div class="col-lg-6 mt-3 mt-lg-0">
<address-map
:entity="entity"
ref="addressMap"
/>
</div>
</div>
<address-more
:entity="entity"
:is-no-address="isNoAddress"
/>
<action-buttons
v-if="insideModal === false"
:options="this.options"
:defaultz="this.defaultz"
>
<template #before>
<slot name="before" />
</template>
<template #action>
<slot name="action" />
</template>
<template #after>
<slot name="after" />
</template>
</action-buttons>
</div>
</template>
<script>
import CountrySelection from './AddAddress/CountrySelection';
import CitySelection from './AddAddress/CitySelection';
import AddressSelection from './AddAddress/AddressSelection';
import AddressMap from './AddAddress/AddressMap';
import AddressMore from './AddAddress/AddressMore';
import ActionButtons from './ActionButtons.vue';
import CountrySelection from "./AddAddress/CountrySelection";
import CitySelection from "./AddAddress/CitySelection";
import AddressSelection from "./AddAddress/AddressSelection";
import AddressMap from "./AddAddress/AddressMap";
import AddressMore from "./AddAddress/AddressMore";
import ActionButtons from "./ActionButtons.vue";
export default {
name: "EditPane",
components: {
CountrySelection,
CitySelection,
AddressSelection,
AddressMap,
AddressMore,
ActionButtons
},
props: [
'context',
'options',
'defaultz',
'flag',
'entity',
'errorMsg',
'insideModal',
'errors',
'checkErrors',
],
emits: ['getCities', 'getReferenceAddresses'],
data() {
return {
value: false,
valueConfidential: false,
}
},
computed: {
address() {
return this.entity.address;
},
loaded() {
return this.entity.loaded;
},
selected() {
return this.entity.selected;
},
addressMap() {
return this.entity.addressMap;
},
isConfidential: {
set(value) {
this.entity.selected.confidential = value;
},
get() {
return this.entity.selected.confidential;
}
},
isNoAddress: {
set(value) {
console.log('isNoAddress value', value);
this.entity.selected.isNoAddress = value;
this.checkErrors();
},
get() {
return this.entity.selected.isNoAddress;
}
},
},
methods: {
focusOnAddress() {
const addressSelector = document.getElementById('addressSelector');
if (addressSelector !== null) {
addressSelector.focus();
}
},
updateMapCenter(point) {
console.log('point', point);
this.addressMap.center[0] = point.coordinates[0];
this.addressMap.center[1] = point.coordinates[1];
this.$refs.addressMap.update(); // cast child methods
}
}
name: "EditPane",
components: {
CountrySelection,
CitySelection,
AddressSelection,
AddressMap,
AddressMore,
ActionButtons,
},
props: [
"context",
"options",
"defaultz",
"flag",
"entity",
"errorMsg",
"insideModal",
"errors",
"checkErrors",
],
emits: ["getCities", "getReferenceAddresses"],
data() {
return {
value: false,
valueConfidential: false,
};
},
computed: {
address() {
return this.entity.address;
},
loaded() {
return this.entity.loaded;
},
selected() {
return this.entity.selected;
},
addressMap() {
return this.entity.addressMap;
},
isConfidential: {
set(value) {
this.entity.selected.confidential = value;
},
get() {
return this.entity.selected.confidential;
},
},
isNoAddress: {
set(value) {
console.log("isNoAddress value", value);
this.entity.selected.isNoAddress = value;
this.checkErrors();
},
get() {
return this.entity.selected.isNoAddress;
},
},
},
methods: {
focusOnAddress() {
const addressSelector = document.getElementById("addressSelector");
if (addressSelector !== null) {
addressSelector.focus();
}
},
updateMapCenter(point) {
console.log("point", point);
this.addressMap.center[0] = point.coordinates[0];
this.addressMap.center[1] = point.coordinates[1];
this.$refs.addressMap.update(); // cast child methods
},
},
};
</script>
<style lang="scss">
div.address-form {
div#address_map {
height: 400px;
width: 100%;
z-index: 1;
}
div#address_map {
height: 400px;
width: 100%;
z-index: 1;
}
}
</style>

View File

@@ -1,199 +1,213 @@
<template>
<div
v-if="!onlyButton"
class="mt-4 flex-grow-1"
>
<div class="loading">
<i
v-if="flag.loading"
class="fa fa-circle-o-notch fa-spin fa-2x fa-fw"
/>
<span class="sr-only">{{ $t('loading') }}</span>
</div>
<div v-if="!onlyButton" class="mt-4 flex-grow-1">
<div class="loading">
<i
v-if="flag.loading"
class="fa fa-circle-o-notch fa-spin fa-2x fa-fw"
/>
<span class="sr-only">{{ $t("loading") }}</span>
</div>
<div
v-if="errorMsg && errorMsg.length > 0"
class="alert alert-danger"
>
{{ errorMsg }}
</div>
<div v-if="errorMsg && errorMsg.length > 0" class="alert alert-danger">
{{ errorMsg }}
</div>
<div
v-if="flag.success"
class="alert alert-success"
>
{{ $t(getSuccessText) }}
<span v-if="forceRedirect">{{ $t('wait_redirection') }}</span>
</div>
<div v-if="flag.success" class="alert alert-success">
{{ $t(getSuccessText) }}
<span v-if="forceRedirect">{{ $t("wait_redirection") }}</span>
</div>
<div
v-if="(!this.context.edit && !this.flag.success && this.context.target.name !== 'household')"
class="mt-5"
>
<div class="no-address-yet">
<i
class="fa fa-map-marker"
aria-hidden="true"
<div
v-if="
!this.context.edit &&
!this.flag.success &&
this.context.target.name !== 'household'
"
class="mt-5"
>
<div class="no-address-yet">
<i class="fa fa-map-marker" aria-hidden="true" />
<p class="chill-no-data-statement">
{{ $t("not_yet_address") }}
</p>
<action-buttons
:options="this.options"
:defaultz="this.defaultz"
class="add-address-btn"
>
<template #action>
<button
@click.prevent="$emit('openEditPane')"
class="btn"
:class="getClassButton"
type="button"
name="button"
:title="$t(getTextButton)"
>
<span v-if="displayTextButton">{{
$t(getTextButton)
}}</span>
</button>
</template>
</action-buttons>
</div>
</div>
<address-render-box
:address="address"
:is-multiline="false"
:use-date-pane="useDatePane"
/>
<p class="chill-no-data-statement">
{{ $t('not_yet_address') }}
</p>
<div
v-if="this.context.target.name === 'household' || this.context.edit"
>
<action-buttons :options="this.options" :defaultz="this.defaultz">
<template #action>
<button
@click.prevent="$emit('openEditPane')"
class="btn"
:class="getClassButton"
type="button"
name="button"
:title="$t(getTextButton)"
>
<span v-if="displayTextButton">{{
$t(getTextButton)
}}</span>
</button>
</template>
</action-buttons>
</div>
</div>
<div v-if="onlyButton">
<action-buttons
:options="this.options"
:defaultz="this.defaultz"
class="add-address-btn"
:options="this.options"
:defaultz="this.defaultz"
class="add-address-btn"
>
<template #action>
<button
@click.prevent="$emit('openEditPane')"
class="btn"
:class="getClassButton"
type="button"
name="button"
:title="$t(getTextButton)"
>
<span v-if="displayTextButton">{{ $t(getTextButton) }}</span>
</button>
</template>
<template #action>
<button
@click.prevent="$emit('openEditPane')"
class="btn"
:class="getClassButton"
type="button"
name="button"
:title="$t(getTextButton)"
>
<span v-if="displayTextButton">{{
$t(getTextButton)
}}</span>
</button>
</template>
</action-buttons>
</div>
</div>
<address-render-box
:address="address"
:is-multiline="false"
:use-date-pane="useDatePane"
/>
<div v-if="this.context.target.name === 'household' || this.context.edit">
<action-buttons
:options="this.options"
:defaultz="this.defaultz"
>
<template #action>
<button
@click.prevent="$emit('openEditPane')"
class="btn"
:class="getClassButton"
type="button"
name="button"
:title="$t(getTextButton)"
>
<span v-if="displayTextButton">{{ $t(getTextButton) }}</span>
</button>
</template>
</action-buttons>
</div>
</div>
<div v-if="onlyButton">
<action-buttons
:options="this.options"
:defaultz="this.defaultz"
class="add-address-btn"
>
<template #action>
<button
@click.prevent="$emit('openEditPane')"
class="btn"
:class="getClassButton"
type="button"
name="button"
:title="$t(getTextButton)"
>
<span v-if="displayTextButton">{{ $t(getTextButton) }}</span>
</button>
</template>
</action-buttons>
</div>
</template>
<script>
import AddressRenderBox from 'ChillMainAssets/vuejs/_components/Entity/AddressRenderBox.vue';
import ActionButtons from './ActionButtons.vue';
import AddressRenderBox from "ChillMainAssets/vuejs/_components/Entity/AddressRenderBox.vue";
import ActionButtons from "./ActionButtons.vue";
export default {
name: 'ShowPane',
components: {
AddressRenderBox,
ActionButtons
},
props: [
'context',
'defaultz',
'options',
'flag',
'entity',
'errorMsg',
'useDatePane'
],
emits: ['openEditPane'],
mounted() {
//console.log('context', this.context)
},
computed: {
address() {
return this.entity.address;
},
displayTextButton() {
return (typeof this.options.button !== 'undefined' && typeof this.options.button.displayText !== 'undefined') ?
this.options.button.displayText : this.defaultz.button.displayText;
},
getClassButton() {
let type = (this.context.edit) ? this.defaultz.button.type.edit : this.defaultz.button.type.create;
let size = (typeof this.options.button !== 'undefined' && this.options.button.size !== null) ?
`${this.options.button.size} ` : '';
return `${size}${type}`;
},
getTextButton() {
if ( typeof this.options.button.text !== 'undefined'
&& ( this.options.button.text.edit !== null
|| this.options.button.text.create !== null
)) {
return (this.context.edit) ? this.options.button.text.edit : this.options.button.text.create;
}
return (this.context.edit) ? this.defaultz.button.text.edit : this.defaultz.button.text.create;
},
getSuccessText() {
return (this.context.edit) ? 'address_edit_success' : 'address_new_success';
},
onlyButton() {
return (typeof this.options.onlyButton !== 'undefined') ?
this.options.onlyButton : this.defaultz.onlyButton;
},
forceRedirect() {
return (!(this.context.backUrl === null || typeof this.context.backUrl === 'undefined'));
},
// showMessageWhenNoAddress() {
// let showMessageWhenNoAddress = this.options.showMessageWhenNoAddress === undefined ? this.defaultz.showMessageWhenNoAddress : this.options.showMessageWhenNoAddress;
// if (showMessageWhenNoAddress === true || showMessageWhenNoAddress === false) {
// return !this.context.edit && !this.address.id && showMessageWhenNoAddress;
// }
// return !this.context.edit && !this.address.id && this.options.stickyActions;
// }
}
name: "ShowPane",
components: {
AddressRenderBox,
ActionButtons,
},
props: [
"context",
"defaultz",
"options",
"flag",
"entity",
"errorMsg",
"useDatePane",
],
emits: ["openEditPane"],
mounted() {
//console.log('context', this.context)
},
computed: {
address() {
return this.entity.address;
},
displayTextButton() {
return typeof this.options.button !== "undefined" &&
typeof this.options.button.displayText !== "undefined"
? this.options.button.displayText
: this.defaultz.button.displayText;
},
getClassButton() {
let type = this.context.edit
? this.defaultz.button.type.edit
: this.defaultz.button.type.create;
let size =
typeof this.options.button !== "undefined" &&
this.options.button.size !== null
? `${this.options.button.size} `
: "";
return `${size}${type}`;
},
getTextButton() {
if (
typeof this.options.button.text !== "undefined" &&
(this.options.button.text.edit !== null ||
this.options.button.text.create !== null)
) {
return this.context.edit
? this.options.button.text.edit
: this.options.button.text.create;
}
return this.context.edit
? this.defaultz.button.text.edit
: this.defaultz.button.text.create;
},
getSuccessText() {
return this.context.edit
? "address_edit_success"
: "address_new_success";
},
onlyButton() {
return typeof this.options.onlyButton !== "undefined"
? this.options.onlyButton
: this.defaultz.onlyButton;
},
forceRedirect() {
return !(
this.context.backUrl === null ||
typeof this.context.backUrl === "undefined"
);
},
// showMessageWhenNoAddress() {
// let showMessageWhenNoAddress = this.options.showMessageWhenNoAddress === undefined ? this.defaultz.showMessageWhenNoAddress : this.options.showMessageWhenNoAddress;
// if (showMessageWhenNoAddress === true || showMessageWhenNoAddress === false) {
// return !this.context.edit && !this.address.id && showMessageWhenNoAddress;
// }
// return !this.context.edit && !this.address.id && this.options.stickyActions;
// }
},
};
</script>
<style lang="scss">
.address-container {
display:flex;
justify-content:flex-end;
border-radius: 5px;
display: flex;
justify-content: flex-end;
border-radius: 5px;
}
.no-address-yet {
text-align: center;
box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;
padding:1.5rem;
margin-bottom:2rem;
i {
font-size:2rem;
margin-bottom:2rem;
}
.add-address-btn {
display: block
}
text-align: center;
box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;
padding: 1.5rem;
margin-bottom: 2rem;
i {
font-size: 2rem;
margin-bottom: 2rem;
}
.add-address-btn {
display: block;
}
}
</style>

View File

@@ -1,100 +1,94 @@
<template>
<div
v-if="insideModal === false"
class="loading"
>
<i
v-if="flag.loading"
class="fa fa-circle-o-notch fa-spin fa-2x fa-fw"
/>
<span class="sr-only">{{ $t('loading') }}</span>
</div>
<div
v-if="errorMsg && errorMsg.length > 0"
class="alert alert-danger"
>
{{ errorMsg }}
</div>
<h4 class="h3">
{{ $t('address_suggestions') }}
</h4>
<div class="flex-table AddressSuggestionList">
<div
v-for="(a, i) in context.suggestions"
class="item-bloc"
:key="`suggestions-${i}`"
>
<div class="float-button bottom">
<div class="box">
<div class="action">
<!-- QUESTION normal que ça vienne avant l'adresse ? pourquoi pas après avoir affiché le address-render-box ? -->
<ul class="record_actions">
<li>
<button
class="btn btn-sm btn-choose"
@click="this.pickAddress(a)"
>
{{ $t('use_this_address') }}
</button>
</li>
</ul>
</div>
<ul class="list-content fa-ul">
<li>
<i class="fa fa-li fa-map-marker" />
<address-render-box :address="a" />
</li>
</ul>
</div>
</div>
<div v-if="insideModal === false" class="loading">
<i
v-if="flag.loading"
class="fa fa-circle-o-notch fa-spin fa-2x fa-fw"
/>
<span class="sr-only">{{ $t("loading") }}</span>
</div>
</div>
<action-buttons
v-if="insideModal === false"
:options="this.options"
:defaultz="this.defaultz"
>
<template #before>
<slot name="before" />
</template>
<template #action>
<slot name="action" />
</template>
<template #after>
<slot name="after" />
</template>
</action-buttons>
<div v-if="errorMsg && errorMsg.length > 0" class="alert alert-danger">
{{ errorMsg }}
</div>
<h4 class="h3">
{{ $t("address_suggestions") }}
</h4>
<div class="flex-table AddressSuggestionList">
<div
v-for="(a, i) in context.suggestions"
class="item-bloc"
:key="`suggestions-${i}`"
>
<div class="float-button bottom">
<div class="box">
<div class="action">
<!-- QUESTION normal que ça vienne avant l'adresse ? pourquoi pas après avoir affiché le address-render-box ? -->
<ul class="record_actions">
<li>
<button
class="btn btn-sm btn-choose"
@click="this.pickAddress(a)"
>
{{ $t("use_this_address") }}
</button>
</li>
</ul>
</div>
<ul class="list-content fa-ul">
<li>
<i class="fa fa-li fa-map-marker" />
<address-render-box :address="a" />
</li>
</ul>
</div>
</div>
</div>
</div>
<action-buttons
v-if="insideModal === false"
:options="this.options"
:defaultz="this.defaultz"
>
<template #before>
<slot name="before" />
</template>
<template #action>
<slot name="action" />
</template>
<template #after>
<slot name="after" />
</template>
</action-buttons>
</template>
<script>
import AddressRenderBox from 'ChillMainAssets/vuejs/_components/Entity/AddressRenderBox.vue';
import ActionButtons from './ActionButtons.vue';
import AddressRenderBox from "ChillMainAssets/vuejs/_components/Entity/AddressRenderBox.vue";
import ActionButtons from "./ActionButtons.vue";
export default {
name: "SuggestPane",
components: {
AddressRenderBox,
ActionButtons
},
props: [
'context',
'options',
'defaultz',
'flag',
'entity',
'errorMsg',
'insideModal',
],
computed: {},
methods: {
pickAddress(address) {
console.log('pickAddress in suggest pane', address);
this.$emit('pickAddress', address);
},
}
}
name: "SuggestPane",
components: {
AddressRenderBox,
ActionButtons,
},
props: [
"context",
"options",
"defaultz",
"flag",
"entity",
"errorMsg",
"insideModal",
],
computed: {},
methods: {
pickAddress(address) {
console.log("pickAddress in suggest pane", address);
this.$emit("pickAddress", address);
},
},
};
</script>

View File

@@ -1,56 +1,56 @@
import { multiSelectMessages } from 'ChillMainAssets/vuejs/_js/i18n'
import { multiSelectMessages } from "ChillMainAssets/vuejs/_js/i18n";
const addressMessages = {
fr: {
add_an_address_title: 'Créer une adresse',
edit_an_address_title: 'Modifier une adresse',
create_a_new_address: 'Créer une nouvelle adresse',
edit_address: 'Modifier l\'adresse',
select_an_address_title: 'Sélectionner une adresse',
fill_an_address: 'Compléter l\'adresse',
select_country: 'Choisir le pays',
country: 'Pays',
select_city: 'Choisir une localité',
city: 'Localité',
other_city: 'Autre localité',
select_address: 'Choisir une adresse',
address: 'Adresse',
other_address: 'Autre adresse',
create_address: 'Adresse inconnue. Cliquez ici pour créer une nouvelle adresse',
isNoAddress: 'Pas d\'adresse complète',
isConfidential: 'Adresse confidentielle',
street: 'Nom de rue',
streetNumber: 'Numéro',
floor: 'Étage',
corridor: 'Couloir',
steps: 'Escalier',
flat: 'Appartement',
buildingName: 'Résidence',
extra: 'Complément d\'adresse',
distribution: 'Cedex',
create_postal_code: 'Localité inconnue. Cliquez ici pour créer une nouvelle localité',
postalCode_name: 'Nom',
postalCode_code: 'Code postal',
date: "Date de la nouvelle adresse",
validFrom: "L'adresse est valable à partir du",
validTo: "L'adresse est valable jusqu'au",
back_to_the_list: 'Retour à la liste',
loading: 'chargement en cours...',
address_suggestions: "Suggestion d'adresses",
address_new_success: 'La nouvelle adresse est enregistrée.',
address_edit_success: 'L\'adresse a été mise à jour.',
wait_redirection: " La page est redirigée.",
not_yet_address: "Il n'y a pas encore d'adresse. Cliquez sur '+ Créer une adresse'",
use_this_address: "Utiliser cette adresse",
fr: {
add_an_address_title: "Créer une adresse",
edit_an_address_title: "Modifier une adresse",
create_a_new_address: "Créer une nouvelle adresse",
edit_address: "Modifier l'adresse",
select_an_address_title: "Sélectionner une adresse",
fill_an_address: "Compléter l'adresse",
select_country: "Choisir le pays",
country: "Pays",
select_city: "Choisir une localité",
city: "Localité",
other_city: "Autre localité",
select_address: "Choisir une adresse",
address: "Adresse",
other_address: "Autre adresse",
create_address:
"Adresse inconnue. Cliquez ici pour créer une nouvelle adresse",
isNoAddress: "Pas d'adresse complète",
isConfidential: "Adresse confidentielle",
street: "Nom de rue",
streetNumber: "Numéro",
floor: "Étage",
corridor: "Couloir",
steps: "Escalier",
flat: "Appartement",
buildingName: "Résidence",
extra: "Complément d'adresse",
distribution: "Cedex",
create_postal_code:
"Localité inconnue. Cliquez ici pour créer une nouvelle localité",
postalCode_name: "Nom",
postalCode_code: "Code postal",
date: "Date de la nouvelle adresse",
validFrom: "L'adresse est valable à partir du",
validTo: "L'adresse est valable jusqu'au",
back_to_the_list: "Retour à la liste",
loading: "chargement en cours...",
address_suggestions: "Suggestion d'adresses",
address_new_success: "La nouvelle adresse est enregistrée.",
address_edit_success: "L'adresse a été mise à jour.",
wait_redirection: " La page est redirigée.",
not_yet_address:
"Il n'y a pas encore d'adresse. Cliquez sur '+ Créer une adresse'",
use_this_address: "Utiliser cette adresse",
// household specific
move_date: 'Date du déménagement',
}
// household specific
move_date: "Date du déménagement",
},
};
Object.assign(addressMessages.fr, multiSelectMessages.fr);
export {
addressMessages
};
export { addressMessages };

View File

@@ -1,68 +1,67 @@
import { createApp } from 'vue';
import { _createI18n } from 'ChillMainAssets/vuejs/_js/i18n';
import { addressMessages } from './i18n';
import App from './App.vue';
import { createApp } from "vue";
import { _createI18n } from "ChillMainAssets/vuejs/_js/i18n";
import { addressMessages } from "./i18n";
import App from "./App.vue";
const i18n = _createI18n( addressMessages );
const i18n = _createI18n(addressMessages);
let containers = document.querySelectorAll('.address-container');
let containers = document.querySelectorAll(".address-container");
containers.forEach((container) => {
const app = createApp({
template: `<app v-bind:addAddress="this.addAddress" ></app>`,
data() {
return {
addAddress: {
context: {
target: {
name: container.dataset.targetName,
id: parseInt(container.dataset.targetId),
},
edit: container.dataset.mode === "edit", //boolean
addressId: parseInt(container.dataset.addressId) || null,
backUrl: container.dataset.backUrl || null,
defaults: JSON.parse(container.dataset.addressDefaults),
},
options: {
/// Options override default.
/// null value take default component value defined in AddAddress data()
button: {
text: {
create: container.dataset.buttonText || null,
edit: container.dataset.buttonText || null,
},
size: container.dataset.buttonSize || null,
displayText: container.dataset.buttonDisplayText !== "false", //boolean, default: true
},
const app = createApp({
template: `<app v-bind:addAddress="this.addAddress" ></app>`,
data() {
return {
addAddress: {
context: {
target: {
name: container.dataset.targetName,
id: parseInt(container.dataset.targetId)
},
edit: container.dataset.mode === 'edit', //boolean
addressId: parseInt(container.dataset.addressId) || null,
backUrl: container.dataset.backUrl || null,
defaults: JSON.parse(container.dataset.addressDefaults)
},
options: {
/// Options override default.
/// null value take default component value defined in AddAddress data()
button: {
text: {
create: container.dataset.buttonText || null,
edit: container.dataset.buttonText || null
},
size: container.dataset.buttonSize || null,
displayText: container.dataset.buttonDisplayText !== 'false' //boolean, default: true
},
/// Modal title text if create or edit address (trans chain, see i18n)
title: {
create: container.dataset.modalTitle || null,
edit: container.dataset.modalTitle || null,
},
/// Modal title text if create or edit address (trans chain, see i18n)
title: {
create: container.dataset.modalTitle || null,
edit: container.dataset.modalTitle || null
},
/// Display panes in Modal for step123
openPanesInModal: container.dataset.openPanesInModal !== "false", //boolean, default: true
/// Display panes in Modal for step123
openPanesInModal: container.dataset.openPanesInModal !== 'false', //boolean, default: true
/// Display actions buttons of panes in a sticky-form-button navbar
stickyActions: container.dataset.stickyActions === "true", //boolean, default: false
/// Display actions buttons of panes in a sticky-form-button navbar
stickyActions: container.dataset.stickyActions === 'true', //boolean, default: false
/// Use Date fields
useDate: {
validFrom: container.dataset.useValidFrom === "true", //boolean, default: false
validTo: container.dataset.useValidTo === "true", //boolean, default: false
},
/// Use Date fields
useDate: {
validFrom: container.dataset.useValidFrom === 'true', //boolean, default: false
validTo: container.dataset.useValidTo === 'true' //boolean, default: false
},
/// Don't display show renderbox Address: showPane display only a button
onlyButton: container.dataset.onlyButton === 'true' //boolean, default: false
}
}
}
}
})
/// Don't display show renderbox Address: showPane display only a button
onlyButton: container.dataset.onlyButton === "true", //boolean, default: false
},
},
};
},
})
.use(i18n)
.component('app', App)
.component("app", App)
.mount(container);
//console.log('container dataset', container.dataset);
//console.log('container dataset', container.dataset);
});

View File

@@ -1,93 +1,96 @@
import {createApp} from 'vue';
import {_createI18n} from 'ChillMainAssets/vuejs/_js/i18n';
import {addressMessages} from './i18n';
import App from './App.vue';
import { createApp } from "vue";
import { _createI18n } from "ChillMainAssets/vuejs/_js/i18n";
import { addressMessages } from "./i18n";
import App from "./App.vue";
const i18n = _createI18n(addressMessages);
const addAddressInput = (inputs) => {
console.log(inputs)
inputs.forEach(el => {
let
addressId = el.value,
uniqid = el.dataset.inputAddress,
container = el.parentNode.querySelector('div[data-input-address-container="' + uniqid + '"]'),
isEdit = addressId !== '',
addressIdInt = addressId !== '' ? parseInt(addressId) : null
;
if (container === null) {
throw Error("no container");
}
/* exported app */
const app = createApp({
template: `<app v-bind:addAddress="this.addAddress" @address-created="associateToInput"></app>`,
data() {
return {
addAddress: {
context: {
// for legacy ? can be remove ?
target: {
name: 'input-address',
id: addressIdInt,
},
edit: isEdit,
addressId: addressIdInt,
defaults: window.addaddress,
},
options: {
/// Options override default.
/// null value take default component value defined in AddAddress data()
button: {
text: {
create: el.dataset.buttonTextCreate || null,
edit: el.dataset.buttonTextUpdate || null,
},
size: null,
displayText: true
},
/// Modal title text if create or edit address (trans chain, see i18n)
title: {
create: null,
edit: null,
},
/// Display panes in Modal for step123
openPanesInModal: true,
/// Display actions buttons of panes in a sticky-form-button navbar
stickyActions: false,
showMessageWhenNoAddress: true,
/// Use Date fields
useDate: {
validFrom: el.dataset.useValidFrom === '1' || false, //boolean, default: false
validTo: el.dataset.useValidTo === '1' || false, //boolean, default: false
},
/// Don't display show renderbox Address: showPane display only a button
onlyButton: false,
}
}
}
console.log(inputs);
inputs.forEach((el) => {
let addressId = el.value,
uniqid = el.dataset.inputAddress,
container = el.parentNode.querySelector(
'div[data-input-address-container="' + uniqid + '"]',
),
isEdit = addressId !== "",
addressIdInt = addressId !== "" ? parseInt(addressId) : null;
if (container === null) {
throw Error("no container");
}
/* exported app */
const app = createApp({
template: `<app v-bind:addAddress="this.addAddress" @address-created="associateToInput"></app>`,
data() {
return {
addAddress: {
context: {
// for legacy ? can be remove ?
target: {
name: "input-address",
id: addressIdInt,
},
edit: isEdit,
addressId: addressIdInt,
defaults: window.addaddress,
},
methods: {
associateToInput(payload) {
el.value = payload.addressId;
}
}
})
.use(i18n)
.component('app', App)
.mount(container);
});
options: {
/// Options override default.
/// null value take default component value defined in AddAddress data()
button: {
text: {
create: el.dataset.buttonTextCreate || null,
edit: el.dataset.buttonTextUpdate || null,
},
size: null,
displayText: true,
},
/// Modal title text if create or edit address (trans chain, see i18n)
title: {
create: null,
edit: null,
},
/// Display panes in Modal for step123
openPanesInModal: true,
/// Display actions buttons of panes in a sticky-form-button navbar
stickyActions: false,
showMessageWhenNoAddress: true,
/// Use Date fields
useDate: {
validFrom: el.dataset.useValidFrom === "1" || false, //boolean, default: false
validTo: el.dataset.useValidTo === "1" || false, //boolean, default: false
},
/// Don't display show renderbox Address: showPane display only a button
onlyButton: false,
},
},
};
},
methods: {
associateToInput(payload) {
el.value = payload.addressId;
},
},
})
.use(i18n)
.component("app", App)
.mount(container);
});
};
document.addEventListener('DOMContentLoaded', (_e) =>
addAddressInput(document.querySelectorAll('input[type="hidden"][data-input-address]'))
document.addEventListener("DOMContentLoaded", (_e) =>
addAddressInput(
document.querySelectorAll('input[type="hidden"][data-input-address]'),
),
);
window.addEventListener('collection-add-entry', (e) =>
addAddressInput(e.detail.entry.querySelectorAll('input[type="hidden"][data-input-address]'))
window.addEventListener("collection-add-entry", (e) =>
addAddressInput(
e.detail.entry.querySelectorAll('input[type="hidden"][data-input-address]'),
),
);

View File

@@ -1,14 +1,14 @@
import 'es6-promise/auto';
import { createStore } from 'vuex';
import "es6-promise/auto";
import { createStore } from "vuex";
const debug = process.env.NODE_ENV !== 'production';
const debug = process.env.NODE_ENV !== "production";
const store = createStore({
strict: debug,
state: {},
getters: {},
mutations: {},
actions: {},
strict: debug,
state: {},
getters: {},
mutations: {},
actions: {},
});
export { store };

View File

@@ -1,36 +1,36 @@
<template>
<h2>{{ $t('main_title') }}</h2>
<h2>{{ $t("main_title") }}</h2>
<ul class="nav nav-tabs">
<li class="nav-item">
<a
class="nav-link"
:class="{'active': activeTab === 'MyCustoms'}"
@click="selectTab('MyCustoms')"
>
<i class="fa fa-dashboard" />
</a>
</li>
<li class="nav-item">
<a
class="nav-link"
:class="{'active': activeTab === 'MyNotifications'}"
@click="selectTab('MyNotifications')"
>
{{ $t('my_notifications.tab') }}
<tab-counter :count="state.notifications.count" />
</a>
</li>
<li class="nav-item">
<a
class="nav-link"
:class="{'active': activeTab === 'MyAccompanyingCourses'}"
@click="selectTab('MyAccompanyingCourses')"
>
{{ $t('my_accompanying_courses.tab') }}
</a>
</li>
<!-- <li class="nav-item">
<ul class="nav nav-tabs">
<li class="nav-item">
<a
class="nav-link"
:class="{ active: activeTab === 'MyCustoms' }"
@click="selectTab('MyCustoms')"
>
<i class="fa fa-dashboard" />
</a>
</li>
<li class="nav-item">
<a
class="nav-link"
:class="{ active: activeTab === 'MyNotifications' }"
@click="selectTab('MyNotifications')"
>
{{ $t("my_notifications.tab") }}
<tab-counter :count="state.notifications.count" />
</a>
</li>
<li class="nav-item">
<a
class="nav-link"
:class="{ active: activeTab === 'MyAccompanyingCourses' }"
@click="selectTab('MyAccompanyingCourses')"
>
{{ $t("my_accompanying_courses.tab") }}
</a>
</li>
<!-- <li class="nav-item">
<a class="nav-link"
:class="{'active': activeTab === 'MyWorks'}"
@click="selectTab('MyWorks')">
@@ -38,135 +38,122 @@
<tab-counter :count="state.works.count"></tab-counter>
</a>
</li> -->
<li class="nav-item">
<a
class="nav-link"
:class="{'active': activeTab === 'MyEvaluations'}"
@click="selectTab('MyEvaluations')"
>
{{ $t('my_evaluations.tab') }}
<tab-counter :count="state.evaluations.count" />
</a>
</li>
<li class="nav-item">
<a
class="nav-link"
:class="{'active': activeTab === 'MyTasks'}"
@click="selectTab('MyTasks')"
>
{{ $t('my_tasks.tab') }}
<tab-counter :count="state.tasks.warning.count + state.tasks.alert.count" />
</a>
</li>
<li class="nav-item">
<a
class="nav-link"
:class="{'active': activeTab === 'MyWorkflows'}"
@click="selectTab('MyWorkflows')"
>
{{ $t('my_workflows.tab') }}
<tab-counter :count="state.workflows.count + state.workflowsCc.count" />
</a>
</li>
<li
class="nav-item loading ms-auto py-2"
v-if="loading"
>
<i
class="fa fa-circle-o-notch fa-spin fa-lg text-chill-gray"
:title="$t('loading')"
/>
</li>
</ul>
<li class="nav-item">
<a
class="nav-link"
:class="{ active: activeTab === 'MyEvaluations' }"
@click="selectTab('MyEvaluations')"
>
{{ $t("my_evaluations.tab") }}
<tab-counter :count="state.evaluations.count" />
</a>
</li>
<li class="nav-item">
<a
class="nav-link"
:class="{ active: activeTab === 'MyTasks' }"
@click="selectTab('MyTasks')"
>
{{ $t("my_tasks.tab") }}
<tab-counter
:count="state.tasks.warning.count + state.tasks.alert.count"
/>
</a>
</li>
<li class="nav-item">
<a
class="nav-link"
:class="{ active: activeTab === 'MyWorkflows' }"
@click="selectTab('MyWorkflows')"
>
{{ $t("my_workflows.tab") }}
<tab-counter
:count="state.workflows.count + state.workflowsCc.count"
/>
</a>
</li>
<li class="nav-item loading ms-auto py-2" v-if="loading">
<i
class="fa fa-circle-o-notch fa-spin fa-lg text-chill-gray"
:title="$t('loading')"
/>
</li>
</ul>
<div class="my-4">
<my-customs
v-if="activeTab === 'MyCustoms'"
/>
<my-works
v-else-if="activeTab === 'MyWorks'"
/>
<my-evaluations
v-else-if="activeTab === 'MyEvaluations'"
/>
<my-tasks
v-else-if="activeTab === 'MyTasks'"
/>
<my-accompanying-courses
v-else-if="activeTab === 'MyAccompanyingCourses'"
/>
<my-notifications
v-else-if="activeTab === 'MyNotifications'"
/>
<my-workflows
v-else-if="activeTab === 'MyWorkflows'"
/>
</div>
<div class="my-4">
<my-customs v-if="activeTab === 'MyCustoms'" />
<my-works v-else-if="activeTab === 'MyWorks'" />
<my-evaluations v-else-if="activeTab === 'MyEvaluations'" />
<my-tasks v-else-if="activeTab === 'MyTasks'" />
<my-accompanying-courses
v-else-if="activeTab === 'MyAccompanyingCourses'"
/>
<my-notifications v-else-if="activeTab === 'MyNotifications'" />
<my-workflows v-else-if="activeTab === 'MyWorkflows'" />
</div>
</template>
<script>
import MyCustoms from './MyCustoms';
import MyWorks from './MyWorks';
import MyEvaluations from './MyEvaluations';
import MyTasks from './MyTasks';
import MyAccompanyingCourses from './MyAccompanyingCourses';
import MyNotifications from './MyNotifications';
import MyWorkflows from './MyWorkflows.vue';
import TabCounter from './TabCounter';
import MyCustoms from "./MyCustoms";
import MyWorks from "./MyWorks";
import MyEvaluations from "./MyEvaluations";
import MyTasks from "./MyTasks";
import MyAccompanyingCourses from "./MyAccompanyingCourses";
import MyNotifications from "./MyNotifications";
import MyWorkflows from "./MyWorkflows.vue";
import TabCounter from "./TabCounter";
import { mapState } from "vuex";
export default {
name: "App",
components: {
MyCustoms,
MyWorks,
MyEvaluations,
MyTasks,
MyWorkflows,
MyAccompanyingCourses,
MyNotifications,
TabCounter,
},
data() {
return {
activeTab: 'MyCustoms',
}
},
computed: {
...mapState([
'loading',
]),
// just to see all in devtool :
...mapState({
state: (state) => state,
}),
},
methods: {
selectTab(tab) {
if (tab !== 'MyCustoms') {
this.$store.dispatch('getByTab', { tab: tab });
}
this.activeTab = tab;
console.log(this.activeTab)
}
},
mounted() {
for (const m of [
'MyNotifications',
'MyAccompanyingCourses',
// 'MyWorks',
'MyEvaluations',
'MyTasks',
'MyWorkflows',
]) {
this.$store.dispatch('getByTab', { tab: m, param: "countOnly=1" });
}
}
}
name: "App",
components: {
MyCustoms,
MyWorks,
MyEvaluations,
MyTasks,
MyWorkflows,
MyAccompanyingCourses,
MyNotifications,
TabCounter,
},
data() {
return {
activeTab: "MyCustoms",
};
},
computed: {
...mapState(["loading"]),
// just to see all in devtool :
...mapState({
state: (state) => state,
}),
},
methods: {
selectTab(tab) {
if (tab !== "MyCustoms") {
this.$store.dispatch("getByTab", { tab: tab });
}
this.activeTab = tab;
console.log(this.activeTab);
},
},
mounted() {
for (const m of [
"MyNotifications",
"MyAccompanyingCourses",
// 'MyWorks',
"MyEvaluations",
"MyTasks",
"MyWorkflows",
]) {
this.$store.dispatch("getByTab", { tab: m, param: "countOnly=1" });
}
},
};
</script>
<style scoped>
a.nav-link {
cursor: pointer;
cursor: pointer;
}
</style>

View File

@@ -1,24 +1,25 @@
<template>
<div>
<h1>{{ $t('widget.news.title') }}</h1>
<h1>{{ $t("widget.news.title") }}</h1>
<ul v-if="newsItems.length > 0" class="scrollable">
<NewsItem v-for="item in newsItems" :item="item" :key="item.id" />
</ul>
<p v-if="newsItems.length === 0 " class="chill-no-data-statement">{{ $t('widget.news.none') }}</p>
<p v-if="newsItems.length === 0" class="chill-no-data-statement">
{{ $t("widget.news.none") }}
</p>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { fetchResults } from '../../../lib/api/apiMethods';
import { NewsItemType } from '../../../types';
import NewsItem from './NewsItem.vue';
import { onMounted, ref } from "vue";
import { fetchResults } from "../../../lib/api/apiMethods";
import { NewsItemType } from "../../../types";
import NewsItem from "./NewsItem.vue";
const newsItems = ref<NewsItemType[]>([])
const newsItems = ref<NewsItemType[]>([]);
onMounted(() => {
fetchResults<NewsItemType>('/api/1.0/main/news/current.json')
fetchResults<NewsItemType>("/api/1.0/main/news/current.json")
.then((news): Promise<void> => {
// console.log('news articles', response.results)
newsItems.value = news;
@@ -26,10 +27,9 @@ onMounted(() => {
return Promise.resolve();
})
.catch((error: string) => {
console.error('Error fetching news items', error);
})
})
console.error("Error fetching news items", error);
});
});
</script>
<style scoped>
@@ -41,6 +41,4 @@ ul {
h1 {
text-align: center;
}
</style>

View File

@@ -1,11 +1,18 @@
<template>
<li>
<h2>{{ props.item.title }}</h2>
<time class="createdBy" datetime="{{item.startDate.datetime}}">{{ $d(newsItemStartDate(), 'text') }}</time>
<time class="createdBy" datetime="{{item.startDate.datetime}}">{{
$d(newsItemStartDate(), "text")
}}</time>
<div class="content" v-if="shouldTruncate(item.content)">
<div v-html="prepareContent(item.content)"></div>
<div class="float-end">
<button class="btn btn-sm btn-show read-more" @click="() => openModal(item)">{{ $t('widget.news.readMore') }}</button>
<button
class="btn btn-sm btn-show read-more"
@click="() => openModal(item)"
>
{{ $t("widget.news.readMore") }}
</button>
</div>
</div>
<div class="content" v-else>
@@ -18,7 +25,11 @@
</template>
<template #body>
<p class="news-date">
<time class="createdBy" datetime="{{item.startDate.datetime}}">{{ $d(newsItemStartDate(), 'text') }}</time>
<time
class="createdBy"
datetime="{{item.startDate.datetime}}"
>{{ $d(newsItemStartDate(), "text") }}</time
>
</p>
<div v-html="convertMarkdownToHtml(item.content)"></div>
</template>
@@ -28,18 +39,17 @@
<script setup lang="ts">
import Modal from "ChillMainAssets/vuejs/_components/Modal.vue";
import { marked } from 'marked';
import DOMPurify from 'dompurify';
import { marked } from "marked";
import DOMPurify from "dompurify";
import { NewsItemType } from "../../../types";
import type { PropType } from 'vue'
import type { PropType } from "vue";
import { ref } from "vue";
import {ISOToDatetime} from '../../../chill/js/date';
import { ISOToDatetime } from "../../../chill/js/date";
const props = defineProps({
item: {
type: Object as PropType<NewsItemType>,
required: true
required: true,
},
maxLength: {
type: Number,
@@ -49,9 +59,9 @@ const props = defineProps({
maxLines: {
type: Number,
required: false,
default: 3
}
})
default: 3,
},
});
const selectedArticle = ref<NewsItemType | null>(null);
const showModal = ref(false);
@@ -67,7 +77,7 @@ const closeModal = () => {
};
const shouldTruncate = (content: string): boolean => {
const lines = content.split('\n');
const lines = content.split("\n");
// Check if any line exceeds the maximum length
const tooManyLines = lines.length > props.maxLines;
@@ -79,67 +89,74 @@ const truncateContent = (content: string): string => {
let truncatedContent = content.slice(0, props.maxLength);
let linkDepth = 0;
let linkStartIndex = -1;
const lines = content.split('\n');
const lines = content.split("\n");
// Truncate if amount of lines are too many
if (lines.length > props.maxLines && content.length < props.maxLength) {
const truncatedContent = lines.slice(0, props.maxLines).join('\n').trim();
return truncatedContent + '...';
const truncatedContent = lines
.slice(0, props.maxLines)
.join("\n")
.trim();
return truncatedContent + "...";
}
for (let i = 0; i < truncatedContent.length; i++) {
const char = truncatedContent[i];
if (char === '[') {
if (char === "[") {
linkDepth++;
if (linkDepth === 1) {
linkStartIndex = i;
}
} else if (char === ']') {
} else if (char === "]") {
linkDepth = Math.max(0, linkDepth - 1);
} else if (char === '(' && linkDepth === 0) {
} else if (char === "(" && linkDepth === 0) {
truncatedContent = truncatedContent.slice(0, i);
break;
}
}
while (linkDepth > 0) {
truncatedContent += ']';
truncatedContent += "]";
linkDepth--;
}
// If a link was found, append the URL inside the parentheses
if (linkStartIndex !== -1) {
const linkEndIndex = content.indexOf(')', linkStartIndex);
const linkEndIndex = content.indexOf(")", linkStartIndex);
const url = content.slice(linkStartIndex + 1, linkEndIndex);
truncatedContent = truncatedContent.slice(0, linkStartIndex) + `(${url})`;
truncatedContent =
truncatedContent.slice(0, linkStartIndex) + `(${url})`;
}
truncatedContent += '...';
truncatedContent += "...";
return truncatedContent;
};
const preprocess = (markdown: string): string => {
return markdown;
}
};
const postprocess = (html: string): string => {
DOMPurify.addHook('afterSanitizeAttributes', (node: any) => {
if ('target' in node) {
node.setAttribute('target', '_blank');
node.setAttribute('rel', 'noopener noreferrer');
DOMPurify.addHook("afterSanitizeAttributes", (node: any) => {
if ("target" in node) {
node.setAttribute("target", "_blank");
node.setAttribute("rel", "noopener noreferrer");
}
if (!node.hasAttribute('target') && (node.hasAttribute('xlink:href') || node.hasAttribute('href'))) {
node.setAttribute('xlink:show', 'new');
if (
!node.hasAttribute("target") &&
(node.hasAttribute("xlink:href") || node.hasAttribute("href"))
) {
node.setAttribute("xlink:show", "new");
}
})
});
return DOMPurify.sanitize(html);
}
};
const convertMarkdownToHtml = (markdown: string): string => {
marked.use({'hooks': {postprocess, preprocess}, 'async': false});
marked.use({ hooks: { postprocess, preprocess }, async: false });
const rawHtml = marked(markdown) as string;
return rawHtml;
};
@@ -149,18 +166,16 @@ const prepareContent = (content: string): string => {
return truncateContent(htmlContent);
};
const newsItemStartDate = (): null|Date => {
const newsItemStartDate = (): null | Date => {
return ISOToDatetime(props.item?.startDate.datetime);
}
};
</script>
<style scoped>
li {
margin-bottom: 20px;
overflow: hidden;
padding: .8rem;
padding: 0.8rem;
background-color: #fbfbfb;
border-radius: 4px;
}
@@ -172,12 +187,11 @@ h2 {
.content {
overflow: hidden;
font-size: .9rem;
font-size: 0.9rem;
position: relative;
}
.news-title {
font-weight: bold;
}
</style>

View File

@@ -1,116 +1,110 @@
<template>
<div class="alert alert-light">
{{ $t('my_accompanying_courses.description') }}
</div>
<span
v-if="noResults"
class="chill-no-data-statement"
>{{ $t('no_data') }}</span>
<tab-table v-else>
<template #thead>
<th scope="col">
{{ $t('opening_date') }}
</th>
<th scope="col">
{{ $t('social_issues') }}
</th>
<th scope="col">
{{ $t('concerned_persons') }}
</th>
<th scope="col" />
<th scope="col" />
</template>
<template #tbody>
<tr
v-for="(c, i) in accompanyingCourses.results"
:key="`course-${i}`"
>
<td>{{ $d(c.openingDate.datetime, 'short') }}</td>
<td>
<span
v-for="(i, index) in c.socialIssues"
:key="index"
class="chill-entity entity-social-issue"
>
<span class="badge bg-chill-l-gray text-dark">
{{ i.title.fr }}
</span>
</span>
</td>
<td>
<span
v-for="p in c.participations"
class="me-1"
:key="p.person.id"
>
<on-the-fly
:type="p.person.type"
:id="p.person.id"
:button-text="p.person.textAge"
:display-badge="'true' === 'true'"
action="show"
/>
</span>
</td>
<td>
<span
v-if="c.emergency"
class="badge rounded-pill bg-danger me-1"
>{{ $t('emergency') }}</span>
<span
v-if="c.confidential"
class="badge rounded-pill bg-danger"
>{{ $t('confidential') }}</span>
</td>
<td>
<a
class="btn btn-sm btn-show"
:href="getUrl(c)"
>
{{ $t('show_entity', { entity: $t('the_course') }) }}
</a>
</td>
</tr>
</template>
</tab-table>
<div class="alert alert-light">
{{ $t("my_accompanying_courses.description") }}
</div>
<span v-if="noResults" class="chill-no-data-statement">{{
$t("no_data")
}}</span>
<tab-table v-else>
<template #thead>
<th scope="col">
{{ $t("opening_date") }}
</th>
<th scope="col">
{{ $t("social_issues") }}
</th>
<th scope="col">
{{ $t("concerned_persons") }}
</th>
<th scope="col" />
<th scope="col" />
</template>
<template #tbody>
<tr
v-for="(c, i) in accompanyingCourses.results"
:key="`course-${i}`"
>
<td>{{ $d(c.openingDate.datetime, "short") }}</td>
<td>
<span
v-for="(i, index) in c.socialIssues"
:key="index"
class="chill-entity entity-social-issue"
>
<span class="badge bg-chill-l-gray text-dark">
{{ i.title.fr }}
</span>
</span>
</td>
<td>
<span
v-for="p in c.participations"
class="me-1"
:key="p.person.id"
>
<on-the-fly
:type="p.person.type"
:id="p.person.id"
:button-text="p.person.textAge"
:display-badge="'true' === 'true'"
action="show"
/>
</span>
</td>
<td>
<span
v-if="c.emergency"
class="badge rounded-pill bg-danger me-1"
>{{ $t("emergency") }}</span
>
<span
v-if="c.confidential"
class="badge rounded-pill bg-danger"
>{{ $t("confidential") }}</span
>
</td>
<td>
<a class="btn btn-sm btn-show" :href="getUrl(c)">
{{ $t("show_entity", { entity: $t("the_course") }) }}
</a>
</td>
</tr>
</template>
</tab-table>
</template>
<script>
import { mapState, mapGetters } from "vuex";
import TabTable from "./TabTable";
import OnTheFly from 'ChillMainAssets/vuejs/OnTheFly/components/OnTheFly';
import OnTheFly from "ChillMainAssets/vuejs/OnTheFly/components/OnTheFly";
export default {
name: "MyAccompanyingCourses",
components: {
TabTable,
OnTheFly,
},
computed: {
...mapState([
'accompanyingCourses',
]),
...mapGetters([
'isAccompanyingCoursesLoaded',
]),
noResults() {
if (!this.isAccompanyingCoursesLoaded) {
return false;
} else {
return this.accompanyingCourses.count === 0;
}
},
},
methods: {
getUrl(c) {
return `/fr/parcours/${c.id}`
}
}
}
name: "MyAccompanyingCourses",
components: {
TabTable,
OnTheFly,
},
computed: {
...mapState(["accompanyingCourses"]),
...mapGetters(["isAccompanyingCoursesLoaded"]),
noResults() {
if (!this.isAccompanyingCoursesLoaded) {
return false;
} else {
return this.accompanyingCourses.count === 0;
}
},
},
methods: {
getUrl(c) {
return `/fr/parcours/${c.id}`;
},
},
};
</script>
<style scoped>
span.badge.rounded-pill.bg-danger {
text-transform: uppercase;
text-transform: uppercase;
}
</style>

View File

@@ -1,158 +1,158 @@
<template>
<span
v-if="noResults"
class="chill-no-data-statement"
>{{ $t('no_dashboard') }}</span>
<div
v-else
id="dashboards"
class="container g-3"
>
<div class="row">
<div class="mbloc col-xs-12 col-sm-4">
<div class="custom1">
<ul class="list-unstyled">
<li v-if="counter.notifications > 0">
<i18n-t
keypath="counter.unread_notifications"
tag="span"
:class="counterClass"
:plural="counter.notifications"
>
<template #n>
<span>{{ counter.notifications }}</span>
</template>
</i18n-t>
</li>
<li v-if="counter.accompanyingCourses > 0">
<i18n-t
keypath="counter.assignated_courses"
tag="span"
:class="counterClass"
:plural="counter.accompanyingCourses"
>
<template #n>
<span>{{ counter.accompanyingCourses }}</span>
</template>
</i18n-t>
</li>
<li v-if="counter.works > 0">
<i18n-t
keypath="counter.assignated_actions"
tag="span"
:class="counterClass"
:plural="counter.works"
>
<template #n>
<span>{{ counter.works }}</span>
</template>
</i18n-t>
</li>
<li v-if="counter.evaluations > 0">
<i18n-t
keypath="counter.assignated_evaluations"
tag="span"
:class="counterClass"
:plural="counter.evaluations"
>
<template #n>
<span>{{ counter.evaluations }}</span>
</template>
</i18n-t>
</li>
<li v-if="counter.tasksAlert > 0">
<i18n-t
keypath="counter.alert_tasks"
tag="span"
:class="counterClass"
:plural="counter.tasksAlert"
>
<template #n>
<span>{{ counter.tasksAlert }}</span>
</template>
</i18n-t>
</li>
<li v-if="counter.tasksWarning > 0">
<i18n-t
keypath="counter.warning_tasks"
tag="span"
:class="counterClass"
:plural="counter.tasksWarning"
>
<template #n>
<span>{{ counter.tasksWarning }}</span>
</template>
</i18n-t>
</li>
</ul>
</div>
</div>
<span v-if="noResults" class="chill-no-data-statement">{{
$t("no_dashboard")
}}</span>
<div v-else id="dashboards" class="container g-3">
<div class="row">
<div class="mbloc col-xs-12 col-sm-4">
<div class="custom1">
<ul class="list-unstyled">
<li v-if="counter.notifications > 0">
<i18n-t
keypath="counter.unread_notifications"
tag="span"
:class="counterClass"
:plural="counter.notifications"
>
<template #n>
<span>{{ counter.notifications }}</span>
</template>
</i18n-t>
</li>
<li v-if="counter.accompanyingCourses > 0">
<i18n-t
keypath="counter.assignated_courses"
tag="span"
:class="counterClass"
:plural="counter.accompanyingCourses"
>
<template #n>
<span>{{
counter.accompanyingCourses
}}</span>
</template>
</i18n-t>
</li>
<li v-if="counter.works > 0">
<i18n-t
keypath="counter.assignated_actions"
tag="span"
:class="counterClass"
:plural="counter.works"
>
<template #n>
<span>{{ counter.works }}</span>
</template>
</i18n-t>
</li>
<li v-if="counter.evaluations > 0">
<i18n-t
keypath="counter.assignated_evaluations"
tag="span"
:class="counterClass"
:plural="counter.evaluations"
>
<template #n>
<span>{{ counter.evaluations }}</span>
</template>
</i18n-t>
</li>
<li v-if="counter.tasksAlert > 0">
<i18n-t
keypath="counter.alert_tasks"
tag="span"
:class="counterClass"
:plural="counter.tasksAlert"
>
<template #n>
<span>{{ counter.tasksAlert }}</span>
</template>
</i18n-t>
</li>
<li v-if="counter.tasksWarning > 0">
<i18n-t
keypath="counter.warning_tasks"
tag="span"
:class="counterClass"
:plural="counter.tasksWarning"
>
<template #n>
<span>{{ counter.tasksWarning }}</span>
</template>
</i18n-t>
</li>
</ul>
</div>
</div>
<template v-if="this.hasDashboardItems">
<template v-for="(dashboardItem, index) in this.dashboardItems" :key="index">
<div
class="mbloc col-xs-12 col-sm-8 news"
v-if="dashboardItem.type === 'news'"
>
<News />
</div>
</template>
</template>
<template v-if="this.hasDashboardItems">
<template
v-for="(dashboardItem, index) in this.dashboardItems"
:key="index"
>
<div
class="mbloc col-xs-12 col-sm-8 news"
v-if="dashboardItem.type === 'news'"
>
<News />
</div>
</template>
</template>
</div>
</div>
</div>
</template>
<script>
import { mapGetters } from "vuex";
import {makeFetch} from "ChillMainAssets/lib/api/apiMethods";
import News from './DashboardWidgets/News.vue';
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
import News from "./DashboardWidgets/News.vue";
export default {
name: "MyCustoms",
name: "MyCustoms",
components: {
News
News,
},
data() {
return {
counterClass: {
counter: true //hack to pass class 'counter' in i18n-t
},
dashboardItems: [],
masonry: null,
}
},
computed: {
...mapGetters(['counter']),
noResults() {
return false
},
hasDashboardItems() {
return this.dashboardItems.length > 0;
}
},
mounted() {
makeFetch('GET', '/api/1.0/main/dashboard-config-item.json')
.then((response) => {
this.dashboardItems = response;
})
.catch((error) => {
throw error
});
},
}
data() {
return {
counterClass: {
counter: true, //hack to pass class 'counter' in i18n-t
},
dashboardItems: [],
masonry: null,
};
},
computed: {
...mapGetters(["counter"]),
noResults() {
return false;
},
hasDashboardItems() {
return this.dashboardItems.length > 0;
},
},
mounted() {
makeFetch("GET", "/api/1.0/main/dashboard-config-item.json")
.then((response) => {
this.dashboardItems = response;
})
.catch((error) => {
throw error;
});
},
};
</script>
<style lang="scss" scoped>
div.custom4,
div.custom3,
div.custom2 {
font-style: italic;
color: var(--bs-chill-gray);
font-style: italic;
color: var(--bs-chill-gray);
}
span.counter {
& > span {
background-color: unset;
}
& > span {
background-color: unset;
}
}
div.news {

View File

@@ -1,130 +1,136 @@
<template>
<div class="accompanying-course-work">
<div class="alert alert-light">
{{ $t('my_evaluations.description') }}
<div class="accompanying-course-work">
<div class="alert alert-light">
{{ $t("my_evaluations.description") }}
</div>
<span v-if="noResults" class="chill-no-data-statement">{{
$t("no_data")
}}</span>
<tab-table v-else>
<template #thead>
<th scope="col">
{{ $t("max_date") }}
</th>
<th scope="col">
{{ $t("evaluation") }}
</th>
<th scope="col">
{{ $t("SocialAction") }}
</th>
<th scope="col" />
</template>
<template #tbody>
<tr
v-for="(e, i) in evaluations.results"
:key="`evaluation-${i}`"
>
<td>{{ $d(e.maxDate.datetime, "short") }}</td>
<td>
{{ e.evaluation.title.fr }}
</td>
<td>
<span class="chill-entity entity-social-issue">
<span class="badge bg-chill-l-gray text-dark">
{{
e.accompanyingPeriodWork.socialAction.issue
.text
}}
</span>
</span>
<h4 class="badge-title">
<span class="title_label" />
<span class="title_action">
{{ e.accompanyingPeriodWork.socialAction.text }}
</span>
</h4>
<span
v-for="person in e.accompanyingPeriodWork.persons"
class="me-1"
:key="person.id"
>
<on-the-fly
:type="person.type"
:id="person.id"
:button-text="person.textAge"
:display-badge="'true' === 'true'"
action="show"
/>
</span>
</td>
<td>
<div
class="btn-group-vertical"
role="group"
aria-label="Actions"
>
<a class="btn btn-sm btn-show" :href="getUrl(e)">
{{
$t("show_entity", {
entity: $t("the_evaluation"),
})
}}
</a>
<a
class="btn btn-sm btn-show"
:href="
getUrl(
e.accompanyingPeriodWork
.accompanyingPeriod,
)
"
>
{{
$t("show_entity", {
entity: $t("the_course"),
})
}}
</a>
</div>
</td>
</tr>
</template>
</tab-table>
</div>
<span
v-if="noResults"
class="chill-no-data-statement"
>{{ $t('no_data') }}</span>
<tab-table v-else>
<template #thead>
<th scope="col">
{{ $t('max_date') }}
</th>
<th scope="col">
{{ $t('evaluation') }}
</th>
<th scope="col">
{{ $t('SocialAction') }}
</th>
<th scope="col" />
</template>
<template #tbody>
<tr
v-for="(e, i) in evaluations.results"
:key="`evaluation-${i}`"
>
<td>{{ $d(e.maxDate.datetime, 'short') }}</td>
<td>
{{ e.evaluation.title.fr }}
</td>
<td>
<span class="chill-entity entity-social-issue">
<span class="badge bg-chill-l-gray text-dark">
{{ e.accompanyingPeriodWork.socialAction.issue.text }}
</span>
</span>
<h4 class="badge-title">
<span class="title_label" />
<span class="title_action">
{{ e.accompanyingPeriodWork.socialAction.text }}
</span>
</h4>
<span
v-for="person in e.accompanyingPeriodWork.persons"
class="me-1"
:key="person.id"
>
<on-the-fly
:type="person.type"
:id="person.id"
:button-text="person.textAge"
:display-badge="'true' === 'true'"
action="show"
/>
</span>
</td>
<td>
<div
class="btn-group-vertical"
role="group"
aria-label="Actions"
>
<a
class="btn btn-sm btn-show"
:href="getUrl(e)"
>
{{ $t('show_entity', { entity: $t('the_evaluation') }) }}
</a>
<a
class="btn btn-sm btn-show"
:href="getUrl(e.accompanyingPeriodWork.accompanyingPeriod)"
>
{{ $t('show_entity', { entity: $t('the_course') }) }}
</a>
</div>
</td>
</tr>
</template>
</tab-table>
</div>
</template>
<script>
import { mapState, mapGetters } from "vuex";
import TabTable from "./TabTable";
import OnTheFly from 'ChillMainAssets/vuejs/OnTheFly/components/OnTheFly';
import OnTheFly from "ChillMainAssets/vuejs/OnTheFly/components/OnTheFly";
export default {
name: "MyEvaluations",
components: {
TabTable,
OnTheFly,
},
computed: {
...mapState([
'evaluations',
]),
...mapGetters([
'isEvaluationsLoaded',
]),
noResults() {
if (!this.isEvaluationsLoaded) {
return false;
} else {
return this.evaluations.count === 0;
}
}
},
methods: {
getUrl(e) {
switch (e.type) {
case 'accompanying_period_work_evaluation':
let anchor = '#evaluations';
return `/fr/person/accompanying-period/work/${e.accompanyingPeriodWork.id}/edit${anchor}`;
case 'accompanying_period_work':
return `/fr/person/accompanying-period/work/${e.id}/edit`
case 'accompanying_period':
return `/fr/parcours/${e.id}`
default:
throw 'entity type unknown';
}
}
},
}
name: "MyEvaluations",
components: {
TabTable,
OnTheFly,
},
computed: {
...mapState(["evaluations"]),
...mapGetters(["isEvaluationsLoaded"]),
noResults() {
if (!this.isEvaluationsLoaded) {
return false;
} else {
return this.evaluations.count === 0;
}
},
},
methods: {
getUrl(e) {
switch (e.type) {
case "accompanying_period_work_evaluation":
let anchor = "#evaluations";
return `/fr/person/accompanying-period/work/${e.accompanyingPeriodWork.id}/edit${anchor}`;
case "accompanying_period_work":
return `/fr/person/accompanying-period/work/${e.id}/edit`;
case "accompanying_period":
return `/fr/parcours/${e.id}`;
default:
throw "entity type unknown";
}
},
},
};
</script>
<style scoped>
</style>
<style scoped></style>

View File

@@ -1,129 +1,117 @@
<template>
<div class="alert alert-light">
{{ $t('my_notifications.description') }}
</div>
<span
v-if="noResults"
class="chill-no-data-statement"
>{{ $t('no_data') }}</span>
<tab-table v-else>
<template #thead>
<th scope="col">
{{ $t('Date') }}
</th>
<th scope="col">
{{ $t('Subject') }}
</th>
<th scope="col">
{{ $t('From') }}
</th>
<th scope="col" />
</template>
<template #tbody>
<tr
v-for="(n, i) in notifications.results"
:key="`notify-${i}`"
>
<td>{{ $d(n.date.datetime, 'long') }}</td>
<td>
<span class="unread">
<i class="fa fa-envelope-o" />
<a :href="getNotificationUrl(n)">{{ n.title }}</a>
</span>
</td>
<td v-if="n.sender != null">
{{ n.sender.text }}
</td>
<td v-else>
{{ $t('automatic_notification') }}
</td>
<td>
<a
class="btn btn-sm btn-show"
:href="getEntityUrl(n)"
>
{{ $t('show_entity', { entity: getEntityName(n) }) }}
</a>
</td>
</tr>
</template>
</tab-table>
<div class="alert alert-light">
{{ $t("my_notifications.description") }}
</div>
<span v-if="noResults" class="chill-no-data-statement">{{
$t("no_data")
}}</span>
<tab-table v-else>
<template #thead>
<th scope="col">
{{ $t("Date") }}
</th>
<th scope="col">
{{ $t("Subject") }}
</th>
<th scope="col">
{{ $t("From") }}
</th>
<th scope="col" />
</template>
<template #tbody>
<tr v-for="(n, i) in notifications.results" :key="`notify-${i}`">
<td>{{ $d(n.date.datetime, "long") }}</td>
<td>
<span class="unread">
<i class="fa fa-envelope-o" />
<a :href="getNotificationUrl(n)">{{ n.title }}</a>
</span>
</td>
<td v-if="n.sender != null">
{{ n.sender.text }}
</td>
<td v-else>
{{ $t("automatic_notification") }}
</td>
<td>
<a class="btn btn-sm btn-show" :href="getEntityUrl(n)">
{{ $t("show_entity", { entity: getEntityName(n) }) }}
</a>
</td>
</tr>
</template>
</tab-table>
</template>
<script>
import { mapState, mapGetters } from "vuex";
import TabTable from "./TabTable";
import { appMessages } from 'ChillMainAssets/vuejs/HomepageWidget/js/i18n';
import { appMessages } from "ChillMainAssets/vuejs/HomepageWidget/js/i18n";
export default {
name: "MyNotifications",
components: {
TabTable
},
computed: {
...mapState([
'notifications',
]),
...mapGetters([
'isNotificationsLoaded',
]),
noResults() {
if (!this.isNotificationsLoaded) {
return false;
} else {
return this.notifications.count === 0;
}
}
},
methods: {
getNotificationUrl(n) {
return `/fr/notification/${n.id}/show`
},
getEntityName(n) {
switch (n.relatedEntityClass) {
case 'Chill\\ActivityBundle\\Entity\\Activity':
return appMessages.fr.the_activity;
case 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod':
return appMessages.fr.the_course;
case 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWork':
return appMessages.fr.the_action;
case 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWorkEvaluationDocument':
return appMessages.fr.the_evaluation_document;
case 'Chill\\MainBundle\\Entity\\Workflow\\EntityWorkflow':
return appMessages.fr.the_workflow;
default:
throw 'notification type unknown';
}
},
getEntityUrl(n) {
switch (n.relatedEntityClass) {
case 'Chill\\ActivityBundle\\Entity\\Activity':
return `/fr/activity/${n.relatedEntityId}/show`
case 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod':
return `/fr/parcours/${n.relatedEntityId}`
case 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWork':
return `/fr/person/accompanying-period/work/${n.relatedEntityId}/show`
case 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWorkEvaluationDocument':
return `/fr/person/accompanying-period/work/evaluation/document/${n.relatedEntityId}/show`
case 'Chill\\MainBundle\\Entity\\Workflow\\EntityWorkflow':
return `/fr/main/workflow/${n.relatedEntityId}/show`
default:
throw 'notification type unknown';
}
}
}
}
name: "MyNotifications",
components: {
TabTable,
},
computed: {
...mapState(["notifications"]),
...mapGetters(["isNotificationsLoaded"]),
noResults() {
if (!this.isNotificationsLoaded) {
return false;
} else {
return this.notifications.count === 0;
}
},
},
methods: {
getNotificationUrl(n) {
return `/fr/notification/${n.id}/show`;
},
getEntityName(n) {
switch (n.relatedEntityClass) {
case "Chill\\ActivityBundle\\Entity\\Activity":
return appMessages.fr.the_activity;
case "Chill\\PersonBundle\\Entity\\AccompanyingPeriod":
return appMessages.fr.the_course;
case "Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWork":
return appMessages.fr.the_action;
case "Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWorkEvaluationDocument":
return appMessages.fr.the_evaluation_document;
case "Chill\\MainBundle\\Entity\\Workflow\\EntityWorkflow":
return appMessages.fr.the_workflow;
default:
throw "notification type unknown";
}
},
getEntityUrl(n) {
switch (n.relatedEntityClass) {
case "Chill\\ActivityBundle\\Entity\\Activity":
return `/fr/activity/${n.relatedEntityId}/show`;
case "Chill\\PersonBundle\\Entity\\AccompanyingPeriod":
return `/fr/parcours/${n.relatedEntityId}`;
case "Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWork":
return `/fr/person/accompanying-period/work/${n.relatedEntityId}/show`;
case "Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWorkEvaluationDocument":
return `/fr/person/accompanying-period/work/evaluation/document/${n.relatedEntityId}/show`;
case "Chill\\MainBundle\\Entity\\Workflow\\EntityWorkflow":
return `/fr/main/workflow/${n.relatedEntityId}/show`;
default:
throw "notification type unknown";
}
},
},
};
</script>
<style lang="scss" scoped>
span.unread {
font-weight: bold;
i {
margin-right: 0.5em;
}
a {
text-decoration: unset;
}
font-weight: bold;
i {
margin-right: 0.5em;
}
a {
text-decoration: unset;
}
}
</style>
</style>

View File

@@ -1,90 +1,83 @@
<template>
<div class="alert alert-light">
{{ $t('my_tasks.description_warning') }}
</div>
<span
v-if="noResultsAlert"
class="chill-no-data-statement"
>{{ $t('no_data') }}</span>
<tab-table v-else>
<template #thead>
<th scope="col">
{{ $t('warning_date') }}
</th>
<th scope="col">
{{ $t('max_date') }}
</th>
<th scope="col">
{{ $t('task') }}
</th>
<th scope="col" />
</template>
<template #tbody>
<tr
v-for="(t, i) in tasks.alert.results"
:key="`task-alert-${i}`"
>
<td v-if="null !== t.warningDate">
{{ $d(t.warningDate.datetime, 'short') }}
</td>
<td v-else />
<td>
<span class="outdated">{{ $d(t.endDate.datetime, 'short') }}</span>
</td>
<td>{{ t.title }}</td>
<td>
<a
class="btn btn-sm btn-show"
:href="getUrl(t)"
>
{{ $t('show_entity', { entity: $t('the_task') }) }}
</a>
</td>
</tr>
</template>
</tab-table>
<div class="alert alert-light">
{{ $t("my_tasks.description_warning") }}
</div>
<span v-if="noResultsAlert" class="chill-no-data-statement">{{
$t("no_data")
}}</span>
<tab-table v-else>
<template #thead>
<th scope="col">
{{ $t("warning_date") }}
</th>
<th scope="col">
{{ $t("max_date") }}
</th>
<th scope="col">
{{ $t("task") }}
</th>
<th scope="col" />
</template>
<template #tbody>
<tr v-for="(t, i) in tasks.alert.results" :key="`task-alert-${i}`">
<td v-if="null !== t.warningDate">
{{ $d(t.warningDate.datetime, "short") }}
</td>
<td v-else />
<td>
<span class="outdated">{{
$d(t.endDate.datetime, "short")
}}</span>
</td>
<td>{{ t.title }}</td>
<td>
<a class="btn btn-sm btn-show" :href="getUrl(t)">
{{ $t("show_entity", { entity: $t("the_task") }) }}
</a>
</td>
</tr>
</template>
</tab-table>
<div class="alert alert-light">
{{ $t('my_tasks.description_alert') }}
</div>
<span
v-if="noResultsWarning"
class="chill-no-data-statement"
>{{ $t('no_data') }}</span>
<tab-table v-else>
<template #thead>
<th scope="col">
{{ $t('warning_date') }}
</th>
<th scope="col">
{{ $t('max_date') }}
</th>
<th scope="col">
{{ $t('task') }}
</th>
<th scope="col" />
</template>
<template #tbody>
<tr
v-for="(t, i) in tasks.warning.results"
:key="`task-warning-${i}`"
>
<td>
<span class="outdated">{{ $d(t.warningDate.datetime, 'short') }}</span>
</td>
<td>{{ $d(t.endDate.datetime, 'short') }}</td>
<td>{{ t.title }}</td>
<td>
<a
class="btn btn-sm btn-show"
:href="getUrl(t)"
>
{{ $t('show_entity', { entity: $t('the_task') }) }}
</a>
</td>
</tr>
</template>
</tab-table>
<div class="alert alert-light">
{{ $t("my_tasks.description_alert") }}
</div>
<span v-if="noResultsWarning" class="chill-no-data-statement">{{
$t("no_data")
}}</span>
<tab-table v-else>
<template #thead>
<th scope="col">
{{ $t("warning_date") }}
</th>
<th scope="col">
{{ $t("max_date") }}
</th>
<th scope="col">
{{ $t("task") }}
</th>
<th scope="col" />
</template>
<template #tbody>
<tr
v-for="(t, i) in tasks.warning.results"
:key="`task-warning-${i}`"
>
<td>
<span class="outdated">{{
$d(t.warningDate.datetime, "short")
}}</span>
</td>
<td>{{ $d(t.endDate.datetime, "short") }}</td>
<td>{{ t.title }}</td>
<td>
<a class="btn btn-sm btn-show" :href="getUrl(t)">
{{ $t("show_entity", { entity: $t("the_task") }) }}
</a>
</td>
</tr>
</template>
</tab-table>
</template>
<script>
@@ -92,44 +85,39 @@ import { mapState, mapGetters } from "vuex";
import TabTable from "./TabTable";
export default {
name: "MyTasks",
components: {
TabTable
},
computed: {
...mapState([
'tasks',
]),
...mapGetters([
'isTasksWarningLoaded',
'isTasksAlertLoaded',
]),
noResultsAlert() {
if (!this.isTasksAlertLoaded) {
return false;
} else {
return this.tasks.alert.count === 0;
}
},
noResultsWarning() {
if (!this.isTasksWarningLoaded) {
return false;
} else {
return this.tasks.warning.count === 0;
}
}
},
methods: {
getUrl(t) {
return `/fr/task/single-task/${t.id}/show`
}
},
}
name: "MyTasks",
components: {
TabTable,
},
computed: {
...mapState(["tasks"]),
...mapGetters(["isTasksWarningLoaded", "isTasksAlertLoaded"]),
noResultsAlert() {
if (!this.isTasksAlertLoaded) {
return false;
} else {
return this.tasks.alert.count === 0;
}
},
noResultsWarning() {
if (!this.isTasksWarningLoaded) {
return false;
} else {
return this.tasks.warning.count === 0;
}
},
},
methods: {
getUrl(t) {
return `/fr/task/single-task/${t.id}/show`;
},
},
};
</script>
<style scoped>
span.outdated {
font-weight: bold;
color: var(--bs-warning);
font-weight: bold;
color: var(--bs-warning);
}
</style>
</style>

View File

@@ -1,29 +1,26 @@
<template>
<div class="alert alert-light">
{{ $t('my_workflows.description') }}
</div>
<my-workflows-table :workflows="workflows" />
<div class="alert alert-light">
{{ $t("my_workflows.description") }}
</div>
<my-workflows-table :workflows="workflows" />
<div class="alert alert-light">
{{ $t('my_workflows.description_cc') }}
</div>
<my-workflows-table :workflows="workflowsCc" />
<div class="alert alert-light">
{{ $t("my_workflows.description_cc") }}
</div>
<my-workflows-table :workflows="workflowsCc" />
</template>
<script>
import { mapState } from "vuex";
import MyWorkflowsTable from './MyWorkflowsTable.vue';
import MyWorkflowsTable from "./MyWorkflowsTable.vue";
export default {
name: "MyWorkflows",
components: {
MyWorkflowsTable
MyWorkflowsTable,
},
computed: {
...mapState([
'workflows',
'workflowsCc',
]),
...mapState(["workflows", "workflowsCc"]),
},
}
</script>
};
</script>

View File

@@ -1,79 +1,68 @@
<template>
<span
v-if="hasNoResults(workflows)"
class="chill-no-data-statement"
>{{ $t('no_data') }}</span>
<tab-table v-else>
<template #thead>
<th scope="col">
{{ $t('Object_workflow') }}
</th>
<th scope="col">
{{ $t('Step') }}
</th>
<th scope="col">
{{ $t('concerned_users') }}
</th>
<th scope="col" />
</template>
<template #tbody>
<tr
v-for="(w, i) in workflows.results"
:key="`workflow-${i}`"
>
<td>{{ w.title }}</td>
<td>
<div class="workflow">
<div class="breadcrumb">
<i class="fa fa-circle me-1 text-chill-yellow mx-2" />
<span class="mx-2">{{ getStep(w) }}</span>
</div>
</div>
</td>
<td v-if="w.datas.persons !== null">
<span
v-for="p in w.datas.persons"
class="me-1"
:key="p.id"
>
<on-the-fly
:type="p.type"
:id="p.id"
:button-text="p.textAge"
:display-badge="'true' === 'true'"
action="show"
/>
</span>
</td>
<td>
<a
class="btn btn-sm btn-show"
:href="getUrl(w)"
>
{{ $t('show_entity', { entity: $t('the_workflow') }) }}
</a>
</td>
</tr>
</template>
</tab-table>
<span v-if="hasNoResults(workflows)" class="chill-no-data-statement">{{
$t("no_data")
}}</span>
<tab-table v-else>
<template #thead>
<th scope="col">
{{ $t("Object_workflow") }}
</th>
<th scope="col">
{{ $t("Step") }}
</th>
<th scope="col">
{{ $t("concerned_users") }}
</th>
<th scope="col" />
</template>
<template #tbody>
<tr v-for="(w, i) in workflows.results" :key="`workflow-${i}`">
<td>{{ w.title }}</td>
<td>
<div class="workflow">
<div class="breadcrumb">
<i
class="fa fa-circle me-1 text-chill-yellow mx-2"
/>
<span class="mx-2">{{ getStep(w) }}</span>
</div>
</div>
</td>
<td v-if="w.datas.persons !== null">
<span v-for="p in w.datas.persons" class="me-1" :key="p.id">
<on-the-fly
:type="p.type"
:id="p.id"
:button-text="p.textAge"
:display-badge="'true' === 'true'"
action="show"
/>
</span>
</td>
<td>
<a class="btn btn-sm btn-show" :href="getUrl(w)">
{{ $t("show_entity", { entity: $t("the_workflow") }) }}
</a>
</td>
</tr>
</template>
</tab-table>
</template>
<script>
import { mapGetters } from "vuex";
import TabTable from "./TabTable";
import OnTheFly from 'ChillMainAssets/vuejs/OnTheFly/components/OnTheFly';
import OnTheFly from "ChillMainAssets/vuejs/OnTheFly/components/OnTheFly";
export default {
name: "MyWorkflows",
components: {
TabTable,
OnTheFly
OnTheFly,
},
props: ['workflows'],
props: ["workflows"],
computed: {
...mapGetters([
'isWorkflowsLoaded',
]),
...mapGetters(["isWorkflowsLoaded"]),
},
methods: {
hasNoResults(workflows) {
@@ -87,11 +76,11 @@ export default {
return `/fr/main/workflow/${w.id}/show`;
},
getStep(w) {
const lastStep = w.steps.length - 1
const lastStep = w.steps.length - 1;
return w.steps[lastStep].currentStep.text;
}
},
},
}
};
</script>
<style scoped>

View File

@@ -1,127 +1,122 @@
// CURRENTLY NOT IN USE
<template>
<div class="accompanying-course-work">
<div class="alert alert-light">
{{ $t('my_works.description') }}
<div class="accompanying-course-work">
<div class="alert alert-light">
{{ $t("my_works.description") }}
</div>
<span v-if="noResults" class="chill-no-data-statement">{{
$t("no_data")
}}</span>
<tab-table v-else>
<template #thead>
<th scope="col">
{{ $t("StartDate") }}
</th>
<th scope="col">
{{ $t("SocialAction") }}
</th>
<th scope="col">
{{ $t("concerned_persons") }}
</th>
<th scope="col" />
</template>
<template #tbody>
<tr v-for="(w, i) in works.results" :key="`works-${i}`">
<td>{{ $d(w.startDate.datetime, "short") }}</td>
<td>
<span class="chill-entity entity-social-issue">
<span class="badge bg-chill-l-gray text-dark">
{{ w.socialAction.issue.text }}
</span>
</span>
<h4 class="badge-title">
<span class="title_label" />
<span class="title_action">
{{ w.socialAction.text }}
</span>
</h4>
</td>
<td>
<span
v-for="person in w.persons"
class="me-1"
:key="person.id"
>
<on-the-fly
:type="person.type"
:id="person.id"
:button-text="person.textAge"
:display-badge="'true' === 'true'"
action="show"
/>
</span>
</td>
<td>
<div
class="btn-group-vertical"
role="group"
aria-label="Actions"
>
<a class="btn btn-sm btn-update" :href="getUrl(w)">
{{
$t("show_entity", {
entity: $t("the_action"),
})
}}
</a>
<a
class="btn btn-sm btn-show"
:href="getUrl(w.accompanyingPeriod)"
>
{{
$t("show_entity", {
entity: $t("the_course"),
})
}}
</a>
</div>
</td>
</tr>
</template>
</tab-table>
</div>
<span
v-if="noResults"
class="chill-no-data-statement"
>{{ $t('no_data') }}</span>
<tab-table v-else>
<template #thead>
<th scope="col">
{{ $t('StartDate') }}
</th>
<th scope="col">
{{ $t('SocialAction') }}
</th>
<th scope="col">
{{ $t('concerned_persons') }}
</th>
<th scope="col" />
</template>
<template #tbody>
<tr
v-for="(w, i) in works.results"
:key="`works-${i}`"
>
<td>{{ $d(w.startDate.datetime, 'short') }}</td>
<td>
<span class="chill-entity entity-social-issue">
<span class="badge bg-chill-l-gray text-dark">
{{ w.socialAction.issue.text }}
</span>
</span>
<h4 class="badge-title">
<span class="title_label" />
<span class="title_action">
{{ w.socialAction.text }}
</span>
</h4>
</td>
<td>
<span
v-for="person in w.persons"
class="me-1"
:key="person.id"
>
<on-the-fly
:type="person.type"
:id="person.id"
:button-text="person.textAge"
:display-badge="'true' === 'true'"
action="show"
/>
</span>
</td>
<td>
<div
class="btn-group-vertical"
role="group"
aria-label="Actions"
>
<a
class="btn btn-sm btn-update"
:href="getUrl(w)"
>
{{ $t('show_entity', { entity: $t('the_action') }) }}
</a>
<a
class="btn btn-sm btn-show"
:href="getUrl(w.accompanyingPeriod)"
>
{{ $t('show_entity', { entity: $t('the_course') }) }}
</a>
</div>
</td>
</tr>
</template>
</tab-table>
</div>
</template>
<script>
import { mapState, mapGetters } from "vuex";
import TabTable from "./TabTable";
import OnTheFly from 'ChillMainAssets/vuejs/OnTheFly/components/OnTheFly';
import OnTheFly from "ChillMainAssets/vuejs/OnTheFly/components/OnTheFly";
export default {
name: "MyWorks",
components: {
TabTable,
OnTheFly,
},
computed: {
...mapState([
'works',
]),
...mapGetters([
'isWorksLoaded',
]),
noResults() {
if (!this.isWorksLoaded) {
return false;
} else {
return this.works.count === 0;
}
}
},
methods: {
getUrl(e) {
switch (e.type) {
case 'accompanying_period_work':
return `/fr/person/accompanying-period/work/${e.id}/edit`
case 'accompanying_period':
return `/fr/parcours/${e.id}`
default:
throw 'entity type unknown';
}
}
},
}
name: "MyWorks",
components: {
TabTable,
OnTheFly,
},
computed: {
...mapState(["works"]),
...mapGetters(["isWorksLoaded"]),
noResults() {
if (!this.isWorksLoaded) {
return false;
} else {
return this.works.count === 0;
}
},
},
methods: {
getUrl(e) {
switch (e.type) {
case "accompanying_period_work":
return `/fr/person/accompanying-period/work/${e.id}/edit`;
case "accompanying_period":
return `/fr/parcours/${e.id}`;
default:
throw "entity type unknown";
}
},
},
};
</script>
<style scoped>
</style>
<style scoped></style>

View File

@@ -1,20 +1,17 @@
<template>
<span
v-if="isCounterAvailable"
class="badge rounded-pill bg-danger"
>
{{ count }}
</span>
<span v-if="isCounterAvailable" class="badge rounded-pill bg-danger">
{{ count }}
</span>
</template>
<script>
export default {
name: "TabCounter",
props: ['count'],
computed: {
isCounterAvailable() {
return (typeof this.count !== 'undefined' && this.count > 0 )
}
}
}
</script>
name: "TabCounter",
props: ["count"],
computed: {
isCounterAvailable() {
return typeof this.count !== "undefined" && this.count > 0;
},
},
};
</script>

View File

@@ -1,21 +1,21 @@
<template>
<table class="table table-striped table-hover">
<thead>
<tr>
<slot name="thead" />
</tr>
</thead>
<tbody>
<slot name="tbody" />
</tbody>
</table>
<table class="table table-striped table-hover">
<thead>
<tr>
<slot name="thead" />
</tr>
</thead>
<tbody>
<slot name="tbody" />
</tbody>
</table>
</template>
<script>
export default {
name: "TabTable",
props: []
}
name: "TabTable",
props: [],
};
</script>
<style scoped></style>
<style scoped></style>

View File

@@ -1,82 +1,88 @@
const appMessages = {
fr: {
main_title: "Vue d'ensemble",
my_works: {
tab: "Mes actions",
description: "Liste des actions d'accompagnement dont je suis référent et qui arrivent à échéance.",
},
my_evaluations: {
tab: "Mes évaluations",
description: "Liste des évaluations dont je suis référent et qui arrivent à échéance.",
},
my_tasks: {
tab: "Mes tâches",
description_alert: "Liste des tâches auxquelles je suis assigné et dont la date de rappel est dépassée.",
description_warning: "Liste des tâches auxquelles je suis assigné et dont la date d'échéance est dépassée.",
},
my_accompanying_courses: {
tab: "Mes nouveaux parcours",
description: "Liste des parcours d'accompagnement que l'on vient de m'attribuer depuis moins de 15 jours.",
},
my_notifications: {
tab: "Mes nouvelles notifications",
description: "Liste des notifications reçues et non lues.",
},
my_workflows: {
tab: "Mes workflows",
description: "Liste des workflows en attente d'une action.",
description_cc: "Liste des workflows dont je suis en copie."
},
opening_date: "Date d'ouverture",
social_issues: "Problématiques sociales",
concerned_persons: "Usagers concernés",
max_date: "Date d'échéance",
warning_date: "Date de rappel",
evaluation: "Évaluation",
task: "Tâche",
Date: "Date",
From: "Expéditeur",
Subject: "Objet",
Entity: "Associé à",
Step: "Étape",
concerned_users: "Usagers concernés",
Object_workflow: "Objet du workflow",
show_entity: "Voir {entity}",
the_activity: "l'échange",
the_course: "le parcours",
the_action: "l'action",
the_evaluation: "l'évaluation",
the_evaluation_document: "le document",
the_task: "la tâche",
the_workflow: "le workflow",
StartDate: "Date d'ouverture",
SocialAction: "Action d'accompagnement",
no_data: "Aucun résultats",
no_dashboard: "Pas de tableaux de bord",
counter: {
unread_notifications: "{n} notification non lue | {n} notifications non lues",
assignated_courses: "{n} parcours récent assigné | {n} parcours récents assignés",
assignated_actions: "{n} action assignée | {n} actions assignées",
assignated_evaluations: "{n} évaluation assignée | {n} évaluations assignées",
alert_tasks: "{n} tâche en rappel | {n} tâches en rappel",
warning_tasks: "{n} tâche à échéance | {n} tâches à échéance",
},
emergency: "Urgent",
confidential: "Confidentiel",
automatic_notification: "Notification automatique",
widget: {
news: {
title: "Actualités",
readMore: "Lire la suite",
date: "Date",
none: "Aucune actualité"
}
}
}
fr: {
main_title: "Vue d'ensemble",
my_works: {
tab: "Mes actions",
description:
"Liste des actions d'accompagnement dont je suis référent et qui arrivent à échéance.",
},
my_evaluations: {
tab: "Mes évaluations",
description:
"Liste des évaluations dont je suis référent et qui arrivent à échéance.",
},
my_tasks: {
tab: "Mes tâches",
description_alert:
"Liste des tâches auxquelles je suis assigné et dont la date de rappel est dépassée.",
description_warning:
"Liste des tâches auxquelles je suis assigné et dont la date d'échéance est dépassée.",
},
my_accompanying_courses: {
tab: "Mes nouveaux parcours",
description:
"Liste des parcours d'accompagnement que l'on vient de m'attribuer depuis moins de 15 jours.",
},
my_notifications: {
tab: "Mes nouvelles notifications",
description: "Liste des notifications reçues et non lues.",
},
my_workflows: {
tab: "Mes workflows",
description: "Liste des workflows en attente d'une action.",
description_cc: "Liste des workflows dont je suis en copie.",
},
opening_date: "Date d'ouverture",
social_issues: "Problématiques sociales",
concerned_persons: "Usagers concernés",
max_date: "Date d'échéance",
warning_date: "Date de rappel",
evaluation: "Évaluation",
task: "Tâche",
Date: "Date",
From: "Expéditeur",
Subject: "Objet",
Entity: "Associé à",
Step: "Étape",
concerned_users: "Usagers concernés",
Object_workflow: "Objet du workflow",
show_entity: "Voir {entity}",
the_activity: "l'échange",
the_course: "le parcours",
the_action: "l'action",
the_evaluation: "l'évaluation",
the_evaluation_document: "le document",
the_task: "la tâche",
the_workflow: "le workflow",
StartDate: "Date d'ouverture",
SocialAction: "Action d'accompagnement",
no_data: "Aucun résultats",
no_dashboard: "Pas de tableaux de bord",
counter: {
unread_notifications:
"{n} notification non lue | {n} notifications non lues",
assignated_courses:
"{n} parcours récent assigné | {n} parcours récents assignés",
assignated_actions: "{n} action assignée | {n} actions assignées",
assignated_evaluations:
"{n} évaluation assignée | {n} évaluations assignées",
alert_tasks: "{n} tâche en rappel | {n} tâches en rappel",
warning_tasks: "{n} tâche à échéance | {n} tâches à échéance",
},
emergency: "Urgent",
confidential: "Confidentiel",
automatic_notification: "Notification automatique",
widget: {
news: {
title: "Actualités",
readMore: "Lire la suite",
date: "Date",
none: "Aucune actualité",
},
},
},
};
Object.assign(appMessages.fr);
export {
appMessages
};
export { appMessages };

View File

@@ -1,226 +1,222 @@
import 'es6-promise/auto';
import { createStore } from 'vuex';
import "es6-promise/auto";
import { createStore } from "vuex";
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
const debug = process.env.NODE_ENV !== 'production';
const debug = process.env.NODE_ENV !== "production";
const isEmpty = (obj) => {
return obj
&& Object.keys(obj).length <= 1
&& Object.getPrototypeOf(obj) === Object.prototype;
return (
obj &&
Object.keys(obj).length <= 1 &&
Object.getPrototypeOf(obj) === Object.prototype
);
};
const store = createStore({
strict: debug,
state: {
// works: {},
evaluations: {},
tasks: {
warning: {},
alert: {}
},
accompanyingCourses: {},
notifications: {},
workflows: {},
workflowsCc: {},
errorMsg: [],
loading: false
strict: debug,
state: {
// works: {},
evaluations: {},
tasks: {
warning: {},
alert: {},
},
getters: {
// isWorksLoaded(state) {
// return !isEmpty(state.works);
// },
isEvaluationsLoaded(state) {
return !isEmpty(state.evaluations);
},
isTasksWarningLoaded(state) {
return !isEmpty(state.tasks.warning);
},
isTasksAlertLoaded(state) {
return !isEmpty(state.tasks.alert);
},
isAccompanyingCoursesLoaded(state) {
return !isEmpty(state.accompanyingCourses);
},
isNotificationsLoaded(state) {
return !isEmpty(state.notifications);
},
isWorkflowsLoaded(state) {
return !isEmpty(state.workflows);
},
counter(state) {
return {
// works: state.works.count,
evaluations: state.evaluations.count,
tasksWarning: state.tasks.warning.count,
tasksAlert: state.tasks.alert.count,
accompanyingCourses: state.accompanyingCourses.count,
notifications: state.notifications.count,
workflows: state.workflows.count
}
}
accompanyingCourses: {},
notifications: {},
workflows: {},
workflowsCc: {},
errorMsg: [],
loading: false,
},
getters: {
// isWorksLoaded(state) {
// return !isEmpty(state.works);
// },
isEvaluationsLoaded(state) {
return !isEmpty(state.evaluations);
},
mutations: {
// addWorks(state, works) {
// //console.log('addWorks', works);
// state.works = works;
// },
addEvaluations(state, evaluations) {
//console.log('addEvaluations', evaluations);
state.evaluations = evaluations;
},
addTasksWarning(state, tasks) {
//console.log('addTasksWarning', tasks);
state.tasks.warning = tasks;
},
addTasksAlert(state, tasks) {
//console.log('addTasksAlert', tasks);
state.tasks.alert = tasks;
},
addCourses(state, courses) {
//console.log('addCourses', courses);
state.accompanyingCourses = courses;
},
addNotifications(state, notifications) {
//console.log('addNotifications', notifications);
state.notifications = notifications;
},
addWorkflows(state, workflows) {
state.workflows = workflows;
},
addWorkflowsCc(state, workflows) {
state.workflowsCc = workflows;
},
setLoading(state, bool) {
state.loading = bool;
},
catchError(state, error) {
state.errorMsg.push(error);
},
isTasksWarningLoaded(state) {
return !isEmpty(state.tasks.warning);
},
actions: {
getByTab({ commit, getters }, { tab, param }) {
switch (tab) {
// case 'MyWorks':
// if (!getters.isWorksLoaded) {
// commit('setLoading', true);
// const url = `/api/1.0/person/accompanying-period/work/my-near-end${'?'+ param}`;
// makeFetch('GET', url)
// .then((response) => {
// commit('addWorks', response);
// commit('setLoading', false);
// })
// .catch((error) => {
// commit('catchError', error);
// throw error;
// })
// ;
// }
// break;
case 'MyEvaluations':
if (!getters.isEvaluationsLoaded) {
commit('setLoading', true);
const url = `/api/1.0/person/accompanying-period/work/evaluation/my-near-end${'?'+ param}`;
makeFetch('GET', url)
.then((response) => {
commit('addEvaluations', response);
commit('setLoading', false);
})
.catch((error) => {
commit('catchError', error);
throw error;
})
;
}
break;
case 'MyTasks':
if (!(getters.isTasksWarningLoaded && getters.isTasksAlertLoaded)) {
commit('setLoading', true);
const
urlWarning = `/api/1.0/task/single-task/list/my?f[q]=&f[checkboxes][status][]=warning&f[checkboxes][states][]=new&f[checkboxes][states][]=in_progress${'&'+ param}`,
urlAlert = `/api/1.0/task/single-task/list/my?f[q]=&f[checkboxes][status][]=alert&f[checkboxes][states][]=new&f[checkboxes][states][]=in_progress${'&'+ param}`
;
makeFetch('GET', urlWarning)
.then((response) => {
commit('addTasksWarning', response);
commit('setLoading', false);
})
.catch((error) => {
commit('catchError', error);
throw error;
})
;
makeFetch('GET', urlAlert)
.then((response) => {
commit('addTasksAlert', response);
commit('setLoading', false);
})
.catch((error) => {
commit('catchError', error);
throw error;
})
;
}
break;
case 'MyAccompanyingCourses':
if (!getters.isAccompanyingCoursesLoaded) {
commit('setLoading', true);
const url = `/api/1.0/person/accompanying-course/list/by-recent-attributions${'?'+ param}`;
makeFetch('GET', url)
.then((response) => {
commit('addCourses', response);
commit('setLoading', false);
})
.catch((error) => {
commit('catchError', error);
throw error;
})
;
}
break;
case 'MyNotifications':
if (!getters.isNotificationsLoaded) {
commit('setLoading', true);
const url = `/api/1.0/main/notification/my/unread${'?'+ param}`;
makeFetch('GET', url)
.then((response) => {
console.log('notifications', response)
commit('addNotifications', response);
commit('setLoading', false);
})
.catch((error) => {
commit('catchError', error);
throw error;
});
}
break;
case 'MyWorkflows':
if (!getters.isWorflowsLoaded) {
commit('setLoading', true);
makeFetch('GET', '/api/1.0/main/workflow/my')
.then((response) => {
commit('addWorkflows', response);
makeFetch('GET', '/api/1.0/main/workflow/my-cc')
.then((response) => {
commit('addWorkflowsCc', response);
commit('setLoading', false);
})
.catch((error) => {
commit('catchError', error);
throw error;
});
})
.catch((error) => {
commit('catchError', error);
throw error;
});
}
break;
default:
throw 'tab '+ tab;
}
},
isTasksAlertLoaded(state) {
return !isEmpty(state.tasks.alert);
},
isAccompanyingCoursesLoaded(state) {
return !isEmpty(state.accompanyingCourses);
},
isNotificationsLoaded(state) {
return !isEmpty(state.notifications);
},
isWorkflowsLoaded(state) {
return !isEmpty(state.workflows);
},
counter(state) {
return {
// works: state.works.count,
evaluations: state.evaluations.count,
tasksWarning: state.tasks.warning.count,
tasksAlert: state.tasks.alert.count,
accompanyingCourses: state.accompanyingCourses.count,
notifications: state.notifications.count,
workflows: state.workflows.count,
};
},
},
mutations: {
// addWorks(state, works) {
// //console.log('addWorks', works);
// state.works = works;
// },
addEvaluations(state, evaluations) {
//console.log('addEvaluations', evaluations);
state.evaluations = evaluations;
},
addTasksWarning(state, tasks) {
//console.log('addTasksWarning', tasks);
state.tasks.warning = tasks;
},
addTasksAlert(state, tasks) {
//console.log('addTasksAlert', tasks);
state.tasks.alert = tasks;
},
addCourses(state, courses) {
//console.log('addCourses', courses);
state.accompanyingCourses = courses;
},
addNotifications(state, notifications) {
//console.log('addNotifications', notifications);
state.notifications = notifications;
},
addWorkflows(state, workflows) {
state.workflows = workflows;
},
addWorkflowsCc(state, workflows) {
state.workflowsCc = workflows;
},
setLoading(state, bool) {
state.loading = bool;
},
catchError(state, error) {
state.errorMsg.push(error);
},
},
actions: {
getByTab({ commit, getters }, { tab, param }) {
switch (tab) {
// case 'MyWorks':
// if (!getters.isWorksLoaded) {
// commit('setLoading', true);
// const url = `/api/1.0/person/accompanying-period/work/my-near-end${'?'+ param}`;
// makeFetch('GET', url)
// .then((response) => {
// commit('addWorks', response);
// commit('setLoading', false);
// })
// .catch((error) => {
// commit('catchError', error);
// throw error;
// })
// ;
// }
// break;
case "MyEvaluations":
if (!getters.isEvaluationsLoaded) {
commit("setLoading", true);
const url = `/api/1.0/person/accompanying-period/work/evaluation/my-near-end${"?" + param}`;
makeFetch("GET", url)
.then((response) => {
commit("addEvaluations", response);
commit("setLoading", false);
})
.catch((error) => {
commit("catchError", error);
throw error;
});
}
break;
case "MyTasks":
if (!(getters.isTasksWarningLoaded && getters.isTasksAlertLoaded)) {
commit("setLoading", true);
const urlWarning = `/api/1.0/task/single-task/list/my?f[q]=&f[checkboxes][status][]=warning&f[checkboxes][states][]=new&f[checkboxes][states][]=in_progress${"&" + param}`,
urlAlert = `/api/1.0/task/single-task/list/my?f[q]=&f[checkboxes][status][]=alert&f[checkboxes][states][]=new&f[checkboxes][states][]=in_progress${"&" + param}`;
makeFetch("GET", urlWarning)
.then((response) => {
commit("addTasksWarning", response);
commit("setLoading", false);
})
.catch((error) => {
commit("catchError", error);
throw error;
});
makeFetch("GET", urlAlert)
.then((response) => {
commit("addTasksAlert", response);
commit("setLoading", false);
})
.catch((error) => {
commit("catchError", error);
throw error;
});
}
break;
case "MyAccompanyingCourses":
if (!getters.isAccompanyingCoursesLoaded) {
commit("setLoading", true);
const url = `/api/1.0/person/accompanying-course/list/by-recent-attributions${"?" + param}`;
makeFetch("GET", url)
.then((response) => {
commit("addCourses", response);
commit("setLoading", false);
})
.catch((error) => {
commit("catchError", error);
throw error;
});
}
break;
case "MyNotifications":
if (!getters.isNotificationsLoaded) {
commit("setLoading", true);
const url = `/api/1.0/main/notification/my/unread${"?" + param}`;
makeFetch("GET", url)
.then((response) => {
console.log("notifications", response);
commit("addNotifications", response);
commit("setLoading", false);
})
.catch((error) => {
commit("catchError", error);
throw error;
});
}
break;
case "MyWorkflows":
if (!getters.isWorflowsLoaded) {
commit("setLoading", true);
makeFetch("GET", "/api/1.0/main/workflow/my")
.then((response) => {
commit("addWorkflows", response);
makeFetch("GET", "/api/1.0/main/workflow/my-cc")
.then((response) => {
commit("addWorkflowsCc", response);
commit("setLoading", false);
})
.catch((error) => {
commit("catchError", error);
throw error;
});
})
.catch((error) => {
commit("catchError", error);
throw error;
});
}
break;
default:
throw "tab " + tab;
}
},
},
});
export { store };

View File

@@ -1,32 +1,32 @@
<template>
<on-the-fly
:type="context.type"
:id="context.id"
:action="context.action"
:button-text="options.buttonText"
:display-badge="options.displayBadge === 'true'"
:is-dead="options.isDead"
:parent="options.parent"
@save-form-on-the-fly="saveFormOnTheFly"
/>
<on-the-fly
:type="context.type"
:id="context.id"
:action="context.action"
:button-text="options.buttonText"
:display-badge="options.displayBadge === 'true'"
:is-dead="options.isDead"
:parent="options.parent"
@save-form-on-the-fly="saveFormOnTheFly"
/>
</template>
<script>
import OnTheFly from './components/OnTheFly.vue';
import OnTheFly from "./components/OnTheFly.vue";
export default {
name: "App",
components: {
OnTheFly
OnTheFly,
},
props: ['onTheFly'],
props: ["onTheFly"],
computed: {
context() {
return this.onTheFly.context;
},
options() {
return this.onTheFly.options;
}
},
},
mounted() {
//console.log('OnTheFly mounted');
@@ -35,9 +35,8 @@ export default {
},
methods: {
saveFormOnTheFly(payload) {
console.log('saveFormOnTheFly', payload);
}
}
}
console.log("saveFormOnTheFly", payload);
},
},
};
</script>

View File

@@ -1,121 +1,113 @@
<template>
<ul class="nav nav-tabs">
<li
v-if="allowedTypes.includes('person')"
class="nav-item"
>
<a
class="nav-link"
:class="{ active: isActive('person') }"
>
<label for="person">
<input
type="radio"
name="person"
id="person"
v-model="radioType"
value="person"
>
{{ $t('onthefly.create.person') }}
</label>
</a>
</li>
<li
v-if="allowedTypes.includes('thirdparty')"
class="nav-item"
>
<a
class="nav-link"
:class="{ active: isActive('thirdparty') }"
>
<label for="thirdparty">
<input
type="radio"
name="thirdparty"
id="thirdparty"
v-model="radioType"
value="thirdparty"
>
{{ $t('onthefly.create.thirdparty') }}
</label>
</a>
</li>
</ul>
<ul class="nav nav-tabs">
<li v-if="allowedTypes.includes('person')" class="nav-item">
<a class="nav-link" :class="{ active: isActive('person') }">
<label for="person">
<input
type="radio"
name="person"
id="person"
v-model="radioType"
value="person"
/>
{{ $t("onthefly.create.person") }}
</label>
</a>
</li>
<li v-if="allowedTypes.includes('thirdparty')" class="nav-item">
<a class="nav-link" :class="{ active: isActive('thirdparty') }">
<label for="thirdparty">
<input
type="radio"
name="thirdparty"
id="thirdparty"
v-model="radioType"
value="thirdparty"
/>
{{ $t("onthefly.create.thirdparty") }}
</label>
</a>
</li>
</ul>
<div class="my-4">
<on-the-fly-person
v-if="type === 'person'"
:action="action"
:query="query"
ref="castPerson"
/>
<div class="my-4">
<on-the-fly-person
v-if="type === 'person'"
:action="action"
:query="query"
ref="castPerson"
/>
<on-the-fly-thirdparty
v-if="type === 'thirdparty'"
:action="action"
:query="query"
ref="castThirdparty"
/>
</div>
<on-the-fly-thirdparty
v-if="type === 'thirdparty'"
:action="action"
:query="query"
ref="castThirdparty"
/>
</div>
</template>
<script>
import OnTheFlyPerson from 'ChillPersonAssets/vuejs/_components/OnTheFly/Person.vue';
import OnTheFlyThirdparty from 'ChillThirdPartyAssets/vuejs/_components/OnTheFly/ThirdParty.vue';
import OnTheFlyPerson from "ChillPersonAssets/vuejs/_components/OnTheFly/Person.vue";
import OnTheFlyThirdparty from "ChillThirdPartyAssets/vuejs/_components/OnTheFly/ThirdParty.vue";
export default {
name: "OnTheFlyCreate",
props: ['action', 'allowedTypes', 'query'],
components: {
OnTheFlyPerson,
OnTheFlyThirdparty
},
data() {
return {
type: null
}
},
computed: {
radioType: {
set(type) {
this.type = type;
console.log('## type:', type, ', action:', this.action);
},
get() {
return this.type;
}
},
},
mounted() {
this.type = (this.allowedTypes.length === 1 && this.allowedTypes.includes('thirdparty')) ? 'thirdparty' : 'person'
},
methods: {
isActive(tab) {
return (this.type === tab) ? true : false;
},
castDataByType() {
switch (this.radioType) {
case 'person':
return this.$refs.castPerson.$data.person;
case 'thirdparty':
let data = this.$refs.castThirdparty.$data.thirdparty;
if (data.address !== undefined && data.address !== null) {
data.address = { id: data.address.address_id }
} else {
data.address = null;
}
name: "OnTheFlyCreate",
props: ["action", "allowedTypes", "query"],
components: {
OnTheFlyPerson,
OnTheFlyThirdparty,
},
data() {
return {
type: null,
};
},
computed: {
radioType: {
set(type) {
this.type = type;
console.log("## type:", type, ", action:", this.action);
},
get() {
return this.type;
},
},
},
mounted() {
this.type =
this.allowedTypes.length === 1 &&
this.allowedTypes.includes("thirdparty")
? "thirdparty"
: "person";
},
methods: {
isActive(tab) {
return this.type === tab ? true : false;
},
castDataByType() {
switch (this.radioType) {
case "person":
return this.$refs.castPerson.$data.person;
case "thirdparty":
let data = this.$refs.castThirdparty.$data.thirdparty;
if (data.address !== undefined && data.address !== null) {
data.address = { id: data.address.address_id };
} else {
data.address = null;
}
return data;
default:
throw Error('Invalid type of entity')
}
}
}
}
return data;
default:
throw Error("Invalid type of entity");
}
},
},
};
</script>
<style lang="css" scoped>
label {
cursor: pointer;
cursor: pointer;
}
</style>

View File

@@ -1,314 +1,328 @@
<template>
<a
v-if="isDisplayBadge"
@click="openModal"
>
<span
class="chill-entity"
:class="badgeType"
>
{{ buttonText }}<span v-if="isDead"> ()</span>
</span>
</a>
<a
v-else
class="btn btn-sm"
target="_blank"
:class="classAction"
:title="$t(titleAction)"
@click="openModal"
>
{{ buttonText }}<span v-if="isDead"> ()</span>
</a>
<teleport to="body">
<modal
v-if="modal.showModal"
:modal-dialog-class="modal.modalDialogClass"
@close="modal.showModal = false"
>
<template #header>
<h3
v-if="parent"
class="modal-title"
>
{{ $t(titleModal, {q: parent.text}) }}
</h3>
<h3
v-else
class="modal-title"
>
{{ $t(titleModal) }}
</h3>
</template>
<template
#body
v-if="type === 'person'"
>
<on-the-fly-person
:id="id"
:type="type"
:action="action"
ref="castPerson"
/>
<div v-if="hasResourceComment">
<h3>{{ $t('onthefly.resource_comment_title') }}</h3>
<blockquote class="chill-user-quote">
{{ parent.comment }}
</blockquote>
</div>
</template>
<template
#body
v-else-if="type === 'thirdparty'"
>
<on-the-fly-thirdparty
:id="id"
:type="type"
:action="action"
ref="castThirdparty"
/>
<div v-if="hasResourceComment">
<h3>{{ $t('onthefly.resource_comment_title') }}</h3>
<blockquote class="chill-user-quote">
{{ parent.comment }}
</blockquote>
</div>
</template>
<template
#body
v-else-if="parent"
>
<on-the-fly-thirdparty
:parent="parent"
:action="action"
type="thirdparty"
ref="castThirdparty"
/>
</template>
<template
#body
<a v-if="isDisplayBadge" @click="openModal">
<span class="chill-entity" :class="badgeType">
{{ buttonText }}<span v-if="isDead"> ()</span>
</span>
</a>
<a
v-else
>
<on-the-fly-create
:action="action"
:allowed-types="allowedTypes"
:query="query"
ref="castNew"
/>
</template>
class="btn btn-sm"
target="_blank"
:class="classAction"
:title="$t(titleAction)"
@click="openModal"
>
{{ buttonText }}<span v-if="isDead"> ()</span>
</a>
<template #footer>
<a
v-if="action === 'show'"
:href="buildLocation(id, type)"
:title="$t(titleMessage)"
class="btn btn-show"
>{{ $t(buttonMessage) }}
</a>
<a
v-else
class="btn btn-save"
@click="saveAction"
<teleport to="body">
<modal
v-if="modal.showModal"
:modal-dialog-class="modal.modalDialogClass"
@close="modal.showModal = false"
>
{{ $t('action.save') }}
</a>
</template>
</modal>
</teleport>
<template #header>
<h3 v-if="parent" class="modal-title">
{{ $t(titleModal, { q: parent.text }) }}
</h3>
<h3 v-else class="modal-title">
{{ $t(titleModal) }}
</h3>
</template>
<template #body v-if="type === 'person'">
<on-the-fly-person
:id="id"
:type="type"
:action="action"
ref="castPerson"
/>
<div v-if="hasResourceComment">
<h3>{{ $t("onthefly.resource_comment_title") }}</h3>
<blockquote class="chill-user-quote">
{{ parent.comment }}
</blockquote>
</div>
</template>
<template #body v-else-if="type === 'thirdparty'">
<on-the-fly-thirdparty
:id="id"
:type="type"
:action="action"
ref="castThirdparty"
/>
<div v-if="hasResourceComment">
<h3>{{ $t("onthefly.resource_comment_title") }}</h3>
<blockquote class="chill-user-quote">
{{ parent.comment }}
</blockquote>
</div>
</template>
<template #body v-else-if="parent">
<on-the-fly-thirdparty
:parent="parent"
:action="action"
type="thirdparty"
ref="castThirdparty"
/>
</template>
<template #body v-else>
<on-the-fly-create
:action="action"
:allowed-types="allowedTypes"
:query="query"
ref="castNew"
/>
</template>
<template #footer>
<a
v-if="action === 'show'"
:href="buildLocation(id, type)"
:title="$t(titleMessage)"
class="btn btn-show"
>{{ $t(buttonMessage) }}
</a>
<a v-else class="btn btn-save" @click="saveAction">
{{ $t("action.save") }}
</a>
</template>
</modal>
</teleport>
</template>
<script>
import Modal from 'ChillMainAssets/vuejs/_components/Modal.vue';
import OnTheFlyCreate from './Create.vue';
import OnTheFlyPerson from 'ChillPersonAssets/vuejs/_components/OnTheFly/Person.vue';
import OnTheFlyThirdparty from 'ChillThirdPartyAssets/vuejs/_components/OnTheFly/ThirdParty.vue';
import Modal from "ChillMainAssets/vuejs/_components/Modal.vue";
import OnTheFlyCreate from "./Create.vue";
import OnTheFlyPerson from "ChillPersonAssets/vuejs/_components/OnTheFly/Person.vue";
import OnTheFlyThirdparty from "ChillThirdPartyAssets/vuejs/_components/OnTheFly/ThirdParty.vue";
export default {
name: 'OnTheFly',
components: {
Modal,
OnTheFlyPerson,
OnTheFlyThirdparty,
OnTheFlyCreate
},
props: ['type', 'id', 'action', 'buttonText', 'displayBadge', 'isDead', 'parent', 'allowedTypes', 'query'],
emits: ['saveFormOnTheFly'],
data() {
return {
modal: {
showModal: false,
modalDialogClass: "modal-dialog-scrollable modal-xl"
}
}
},
computed: {
hasResourceComment() {
return (typeof this.parent !== 'undefined' && this.parent !== null)
&& this.action === 'show'
&& this.parent.type === 'accompanying_period_resource'
&& (this.parent.comment !== null && this.parent.comment !== '')
;
},
classAction() {
switch (this.action) {
case 'show':
return 'btn-show';
case 'edit':
return 'btn-update';
case 'create':
return 'btn-create';
case 'addContact':
return 'btn-tpchild';
default:
return '';
}
},
titleAction() {
switch (this.action) {
case 'show':
return 'action.show';
case 'edit':
return 'action.edit';
case 'create':
return 'action.create';
case 'addContact':
return 'action.addContact';
default:
return '';
}
},
titleCreate() {
if (typeof this.allowedTypes === 'undefined') {
return 'onthefly.create.title.default';
}
return this.allowedTypes.every(t => t === 'person')
? 'onthefly.create.title.person'
: this.allowedTypes.every(t => t === 'thirdparty')
? 'onthefly.create.title.thirdparty'
: 'onthefly.create.title.default'
},
titleModal() {
switch (this.action) {
case 'show':
return 'onthefly.show.' + this.type;
case 'edit':
return 'onthefly.edit.' + this.type;
case 'create':
return this.titleCreate;
case 'addContact':
return 'onthefly.addContact.title';
default:
return '';
}
},
titleMessage() {
switch (this.type){
case 'person':
return 'action.redirect.' + this.type;
case 'thirdparty':
return 'action.redirect.' + this.type;
default:
return '';
}
},
buttonMessage() {
switch (this.type){
case 'person':
return 'onthefly.show.file_' + this.type;
case 'thirdparty':
return 'onthefly.show.file_' + this.type;
}
},
isDisplayBadge() {
return (this.displayBadge === true && this.buttonText !== null);
},
badgeType() {
return 'entity-' + this.type + ' badge-' + this.type;
},
getReturnPath() {
return `?returnPath=${window.location.pathname}${window.location.search}${window.location.hash}`;
},
},
methods: {
closeModal() {
this.modal.showModal = false;
},
openModal() {
// console.log('## OPEN ON THE FLY MODAL');
// console.log('## type:', this.type, ', action:', this.action);
this.modal.showModal = true;
this.$nextTick(function() {
//this.$refs.search.focus();
})
},
changeActionTo(action) {
this.$data.action = action;
},
saveAction() {
// console.log('saveAction button: create/edit action with', this.type);
let
type = this.type,
data = {} ;
switch (type) {
case 'person':
data = this.$refs.castPerson.$data.person;
console.log('person data are', data);
break;
name: "OnTheFly",
components: {
Modal,
OnTheFlyPerson,
OnTheFlyThirdparty,
OnTheFlyCreate,
},
props: [
"type",
"id",
"action",
"buttonText",
"displayBadge",
"isDead",
"parent",
"allowedTypes",
"query",
],
emits: ["saveFormOnTheFly"],
data() {
return {
modal: {
showModal: false,
modalDialogClass: "modal-dialog-scrollable modal-xl",
},
};
},
computed: {
hasResourceComment() {
return (
typeof this.parent !== "undefined" &&
this.parent !== null &&
this.action === "show" &&
this.parent.type === "accompanying_period_resource" &&
this.parent.comment !== null &&
this.parent.comment !== ""
);
},
classAction() {
switch (this.action) {
case "show":
return "btn-show";
case "edit":
return "btn-update";
case "create":
return "btn-create";
case "addContact":
return "btn-tpchild";
default:
return "";
}
},
titleAction() {
switch (this.action) {
case "show":
return "action.show";
case "edit":
return "action.edit";
case "create":
return "action.create";
case "addContact":
return "action.addContact";
default:
return "";
}
},
titleCreate() {
if (typeof this.allowedTypes === "undefined") {
return "onthefly.create.title.default";
}
return this.allowedTypes.every((t) => t === "person")
? "onthefly.create.title.person"
: this.allowedTypes.every((t) => t === "thirdparty")
? "onthefly.create.title.thirdparty"
: "onthefly.create.title.default";
},
titleModal() {
switch (this.action) {
case "show":
return "onthefly.show." + this.type;
case "edit":
return "onthefly.edit." + this.type;
case "create":
return this.titleCreate;
case "addContact":
return "onthefly.addContact.title";
default:
return "";
}
},
titleMessage() {
switch (this.type) {
case "person":
return "action.redirect." + this.type;
case "thirdparty":
return "action.redirect." + this.type;
default:
return "";
}
},
buttonMessage() {
switch (this.type) {
case "person":
return "onthefly.show.file_" + this.type;
case "thirdparty":
return "onthefly.show.file_" + this.type;
}
},
isDisplayBadge() {
return this.displayBadge === true && this.buttonText !== null;
},
badgeType() {
return "entity-" + this.type + " badge-" + this.type;
},
getReturnPath() {
return `?returnPath=${window.location.pathname}${window.location.search}${window.location.hash}`;
},
},
methods: {
closeModal() {
this.modal.showModal = false;
},
openModal() {
// console.log('## OPEN ON THE FLY MODAL');
// console.log('## type:', this.type, ', action:', this.action);
this.modal.showModal = true;
this.$nextTick(function () {
//this.$refs.search.focus();
});
},
changeActionTo(action) {
this.$data.action = action;
},
saveAction() {
// console.log('saveAction button: create/edit action with', this.type);
let type = this.type,
data = {};
switch (type) {
case "person":
data = this.$refs.castPerson.$data.person;
console.log("person data are", data);
break;
case 'thirdparty':
data = this.$refs.castThirdparty.$data.thirdparty;
/* never executed ? */
break;
case "thirdparty":
data = this.$refs.castThirdparty.$data.thirdparty;
/* never executed ? */
break;
default:
if (typeof this.type === 'undefined') { // action=create or addContact
// console.log('will rewrite data');
if (this.action === 'addContact') {
type = 'thirdparty'
data = this.$refs.castThirdparty.$data.thirdparty;
// console.log('data original', data);
data.parent = {type: "thirdparty", id: this.parent.id};
data.civility = data.civility !== null ? {type: 'chill_main_civility', id: data.civility.id} : null;
data.profession = data.profession !== '' ? data.profession : '';
} else {
type = this.$refs.castNew.radioType;
data = this.$refs.castNew.castDataByType();
// console.log('type', type);
if (typeof data.civility !== 'undefined' && null !== data.civility) {
data.civility = data.civility !== null ? {type: 'chill_main_civility', id: data.civility.id} : null;
}
if (typeof data.profession !== 'undefined' && '' !== data.profession) {
data.profession = data.profession !== '' ? data.profession : '';
}
// console.log('onthefly data', data);
}
} else {
throw 'error with object type';
}
}
// pass datas to parent
this.$emit('saveFormOnTheFly', { type: type, data: data });
},
buildLocation(id, type) {
if (type === 'person') {
// TODO i18n
return encodeURI(`/fr/person/${id}/general${this.getReturnPath}`);
} else if (type === 'thirdparty') {
return encodeURI(`/fr/3party/3party/${id}/view${this.getReturnPath}`);
}
}
}
}
default:
if (typeof this.type === "undefined") {
// action=create or addContact
// console.log('will rewrite data');
if (this.action === "addContact") {
type = "thirdparty";
data = this.$refs.castThirdparty.$data.thirdparty;
// console.log('data original', data);
data.parent = {
type: "thirdparty",
id: this.parent.id,
};
data.civility =
data.civility !== null
? {
type: "chill_main_civility",
id: data.civility.id,
}
: null;
data.profession =
data.profession !== "" ? data.profession : "";
} else {
type = this.$refs.castNew.radioType;
data = this.$refs.castNew.castDataByType();
// console.log('type', type);
if (
typeof data.civility !== "undefined" &&
null !== data.civility
) {
data.civility =
data.civility !== null
? {
type: "chill_main_civility",
id: data.civility.id,
}
: null;
}
if (
typeof data.profession !== "undefined" &&
"" !== data.profession
) {
data.profession =
data.profession !== ""
? data.profession
: "";
}
// console.log('onthefly data', data);
}
} else {
throw "error with object type";
}
}
// pass datas to parent
this.$emit("saveFormOnTheFly", { type: type, data: data });
},
buildLocation(id, type) {
if (type === "person") {
// TODO i18n
return encodeURI(
`/fr/person/${id}/general${this.getReturnPath}`,
);
} else if (type === "thirdparty") {
return encodeURI(
`/fr/3party/3party/${id}/view${this.getReturnPath}`,
);
}
},
},
};
</script>
<style lang="css" scoped>
a {
cursor: pointer;
cursor: pointer;
}
/* .btn-add-contact {

View File

@@ -1,35 +1,35 @@
const ontheflyMessages = {
fr: {
onthefly: {
show: {
person: "Détails de l'usager",
thirdparty: "Détails du tiers",
file_person: "Ouvrir la fiche de l'usager",
file_thirdparty: "Voir le Tiers",
},
edit: {
person: "Modifier un usager",
thirdparty: "Modifier un tiers",
},
create: {
button: "Créer \"{q}\"",
title: {
default: "Création d'un nouvel usager ou d'un tiers professionnel",
person: "Création d'un nouvel usager",
thirdparty: "Création d'un nouveau tiers professionnel",
},
person: "un nouvel usager",
thirdparty: "un nouveau tiers professionnel"
},
addContact: {
title: "Créer un contact pour {q}"
},
resource_comment_title: "Un commentaire est associé à cet interlocuteur",
addContact: {
title: "Créer un contact pour {q}"
}
}
}
}
fr: {
onthefly: {
show: {
person: "Détails de l'usager",
thirdparty: "Détails du tiers",
file_person: "Ouvrir la fiche de l'usager",
file_thirdparty: "Voir le Tiers",
},
edit: {
person: "Modifier un usager",
thirdparty: "Modifier un tiers",
},
create: {
button: 'Créer "{q}"',
title: {
default: "Création d'un nouvel usager ou d'un tiers professionnel",
person: "Création d'un nouvel usager",
thirdparty: "Création d'un nouveau tiers professionnel",
},
person: "un nouvel usager",
thirdparty: "un nouveau tiers professionnel",
},
addContact: {
title: "Créer un contact pour {q}",
},
resource_comment_title: "Un commentaire est associé à cet interlocuteur",
addContact: {
title: "Créer un contact pour {q}",
},
},
},
};
export { ontheflyMessages };

View File

@@ -1,38 +1,40 @@
import { createApp } from "vue";
import { _createI18n } from 'ChillMainAssets/vuejs/_js/i18n';
import { ontheflyMessages } from './i18n.js';
import { _createI18n } from "ChillMainAssets/vuejs/_js/i18n";
import { ontheflyMessages } from "./i18n.js";
import App from "./App.vue";
const i18n = _createI18n( ontheflyMessages );
const i18n = _createI18n(ontheflyMessages);
let containers = document.querySelectorAll('.onthefly-container');
let containers = document.querySelectorAll(".onthefly-container");
containers.forEach((container) => {
/*exported app */
const app = createApp({
template: `<app :onTheFly="this.onTheFly" ></app>`,
data() {
return {
onTheFly: {
context: {
action: container.dataset.action,
type: container.dataset.targetName,
id: parseInt(container.dataset.targetId),
},
options: {
buttonText: container.dataset.buttonText || null,
displayBadge: container.dataset.displayBadge || false,
isDead: container.dataset.isDead || false,
parent: container.dataset.parent ? JSON.parse(container.dataset.parent) : null,
}
}
}
}
})
/*exported app */
const app = createApp({
template: `<app :onTheFly="this.onTheFly" ></app>`,
data() {
return {
onTheFly: {
context: {
action: container.dataset.action,
type: container.dataset.targetName,
id: parseInt(container.dataset.targetId),
},
options: {
buttonText: container.dataset.buttonText || null,
displayBadge: container.dataset.displayBadge || false,
isDead: container.dataset.isDead || false,
parent: container.dataset.parent
? JSON.parse(container.dataset.parent)
: null,
},
},
};
},
})
.use(i18n)
.component('app', App)
.component("app", App)
.mount(container);
//console.log('container dataset', container.dataset);
//console.log('data-parent', container.dataset.parent);
//console.log('container dataset', container.dataset);
//console.log('data-parent', container.dataset.parent);
});

View File

@@ -1,37 +1,26 @@
<template>
<ul
:class="listClasses"
v-if="picked.length && displayPicked"
>
<li
v-for="p in picked"
@click="removeEntity(p)"
:key="p.type+p.id"
>
<span class="chill_denomination">{{ p.text }}</span>
</li>
</ul>
<ul class="record_actions">
<li class="add-persons">
<add-persons
:options="addPersonsOptions"
:key="uniqid"
:button-title="translatedListOfTypes"
:modal-title="translatedListOfTypes"
ref="addPersons"
@add-new-persons="addNewEntity"
/>
</li>
</ul>
<ul class="list-suggest add-items inline">
<li
v-for="s in suggested"
:key="s.id"
@click="addNewSuggested(s)"
>
<span>{{ s.text }}</span>
</li>
</ul>
<ul :class="listClasses" v-if="picked.length && displayPicked">
<li v-for="p in picked" @click="removeEntity(p)" :key="p.type + p.id">
<span class="chill_denomination">{{ p.text }}</span>
</li>
</ul>
<ul class="record_actions">
<li class="add-persons">
<add-persons
:options="addPersonsOptions"
:key="uniqid"
:button-title="translatedListOfTypes"
:modal-title="translatedListOfTypes"
ref="addPersons"
@add-new-persons="addNewEntity"
/>
</li>
</ul>
<ul class="list-suggest add-items inline">
<li v-for="s in suggested" :key="s.id" @click="addNewSuggested(s)">
<span>{{ s.text }}</span>
</li>
</ul>
</template>
<script>
@@ -61,85 +50,91 @@ export default {
default: true,
},
displayPicked: {
// display picked entities.
type: Boolean,
default: true,
// display picked entities.
type: Boolean,
default: true,
},
suggested: {
type: Array,
default: []
type: Array,
default: [],
},
label: {
type: String,
required: false,
}
},
},
emits: ['addNewEntity', 'removeEntity'],
emits: ["addNewEntity", "removeEntity"],
components: {
AddPersons,
},
data() {
return {
key: ''
key: "",
};
},
computed: {
addPersonsOptions() {
return {
uniq: !this.multiple,
type: this.types,
priority: null,
button: {
size: 'btn-sm',
class: 'btn-submit',
},
};
},
translatedListOfTypes() {
if (this.label !== '') {
return this.label;
}
addPersonsOptions() {
return {
uniq: !this.multiple,
type: this.types,
priority: null,
button: {
size: "btn-sm",
class: "btn-submit",
},
};
},
translatedListOfTypes() {
if (this.label !== "") {
return this.label;
}
let trans = [];
this.types.forEach(t => {
if (this.$props.multiple) {
trans.push(appMessages.fr.pick_entity[t].toLowerCase());
} else {
trans.push(appMessages.fr.pick_entity[t + '_one'].toLowerCase());
}
})
let trans = [];
this.types.forEach((t) => {
if (this.$props.multiple) {
trans.push(appMessages.fr.pick_entity[t].toLowerCase());
} else {
trans.push(
appMessages.fr.pick_entity[t + "_one"].toLowerCase(),
);
}
});
if (this.$props.multiple) {
return appMessages.fr.pick_entity.modal_title + trans.join(', ');
} else {
return appMessages.fr.pick_entity.modal_title_one + trans.join(', ');
}
},
listClasses() {
return {
'list-suggest': true,
'remove-items': this.$props.removableIfSet,
};
},
if (this.$props.multiple) {
return (
appMessages.fr.pick_entity.modal_title + trans.join(", ")
);
} else {
return (
appMessages.fr.pick_entity.modal_title_one +
trans.join(", ")
);
}
},
listClasses() {
return {
"list-suggest": true,
"remove-items": this.$props.removableIfSet,
};
},
},
methods: {
addNewSuggested(entity) {
this.$emit('addNewEntity', {entity: entity});
},
addNewEntity({ selected, modal }) {
selected.forEach((item) => {
this.$emit('addNewEntity', { entity: item.result});
}, this
);
this.$refs.addPersons.resetSearch(); // to cast child method
modal.showModal = false;
},
removeEntity(entity) {
if (!this.$props.removableIfSet) {
return;
}
this.$emit('removeEntity',{ entity: entity });
}
addNewSuggested(entity) {
this.$emit("addNewEntity", { entity: entity });
},
addNewEntity({ selected, modal }) {
selected.forEach((item) => {
this.$emit("addNewEntity", { entity: item.result });
}, this);
this.$refs.addPersons.resetSearch(); // to cast child method
modal.showModal = false;
},
removeEntity(entity) {
if (!this.$props.removableIfSet) {
return;
}
this.$emit("removeEntity", { entity: entity });
},
},
}
};
</script>

View File

@@ -1,24 +1,30 @@
import { personMessages } from 'ChillPersonAssets/vuejs/_js/i18n';
import { thirdpartyMessages } from 'ChillThirdPartyAssets/vuejs/_js/i18n';
import { addressMessages } from 'ChillMainAssets/vuejs/Address/i18n';
import { ontheflyMessages } from 'ChillMainAssets/vuejs/OnTheFly/i18n';
import { personMessages } from "ChillPersonAssets/vuejs/_js/i18n";
import { thirdpartyMessages } from "ChillThirdPartyAssets/vuejs/_js/i18n";
import { addressMessages } from "ChillMainAssets/vuejs/Address/i18n";
import { ontheflyMessages } from "ChillMainAssets/vuejs/OnTheFly/i18n";
const appMessages = {
fr: {
pick_entity: {
add: 'Ajouter',
modal_title: 'Ajouter des ',
user: 'Utilisateurs',
person: 'Usagers',
thirdparty: 'Tiers',
modal_title_one: 'Indiquer un ',
user_one: 'Utilisateur',
thirdparty_one: 'Tiers',
person_one: 'Usager',
}
}
}
fr: {
pick_entity: {
add: "Ajouter",
modal_title: "Ajouter des ",
user: "Utilisateurs",
person: "Usagers",
thirdparty: "Tiers",
modal_title_one: "Indiquer un ",
user_one: "Utilisateur",
thirdparty_one: "Tiers",
person_one: "Usager",
},
},
};
Object.assign(appMessages.fr, personMessages.fr, thirdpartyMessages.fr, addressMessages.fr, ontheflyMessages.fr );
Object.assign(
appMessages.fr,
personMessages.fr,
thirdpartyMessages.fr,
addressMessages.fr,
ontheflyMessages.fr,
);
export { appMessages };

View File

@@ -1,107 +1,109 @@
<template>
<div class="PickPostalCode">
<vue-multiselect
id="citySelector"
@search-change="listenInputSearch"
ref="citySelector"
v-model="internalPicked"
@select="selectCity"
@remove="remove"
name=""
track-by="id"
label="value"
:custom-label="transName"
:placeholder="$t('select_city')"
:select-label="$t('multiselect.select_label')"
:deselect-label="$t('multiselect.deselect_label')"
:selected-label="$t('multiselect.selected_label')"
:taggable="true"
:multiple="false"
:internal-search="false"
:loading="isLoading"
:options="cities"
/>
</div>
<div class="PickPostalCode">
<vue-multiselect
id="citySelector"
@search-change="listenInputSearch"
ref="citySelector"
v-model="internalPicked"
@select="selectCity"
@remove="remove"
name=""
track-by="id"
label="value"
:custom-label="transName"
:placeholder="$t('select_city')"
:select-label="$t('multiselect.select_label')"
:deselect-label="$t('multiselect.deselect_label')"
:selected-label="$t('multiselect.selected_label')"
:taggable="true"
:multiple="false"
:internal-search="false"
:loading="isLoading"
:options="cities"
/>
</div>
</template>
<script lang="js">
import VueMultiselect from "vue-multiselect";
import { searchCities} from "./api";
import { searchCities } from "./api";
export default {
components: {
VueMultiselect,
},
data() {
return {
cities: [],
internalPicked: null,
isLoading: false,
abortControllers: [],
}
},
emits: ['pickCity', 'removeCity'],
props: {
picked: {
type: Object,
required: false,
default: null
components: {
VueMultiselect,
},
country: {
type: Object,
required: false,
default: null
}
},
mounted() {
if (this.picked !== null) {
this.internalPicked = this.picked;
this.cities.push(this.picked);
}
},
methods: {
transName(value) {
return (value.code && value.name) ? `${value.name} (${value.code})` : '';
data() {
return {
cities: [],
internalPicked: null,
isLoading: false,
abortControllers: [],
};
},
selectCity(city) {
this.$emit('selectCity', city);
emits: ["pickCity", "removeCity"],
props: {
picked: {
type: Object,
required: false,
default: null,
},
country: {
type: Object,
required: false,
default: null,
},
},
listenInputSearch(query) {
if (query.length <= 2) {
return;
}
let c = this.abortControllers.pop();
while (typeof c !== 'undefined') {
c.abort();
c = this.abortControllers.pop();
}
this.isLoading = true;
let controller = new AbortController();
this.abortControllers.push(controller);
searchCities(query, this.country, controller).then(
newCities => {
this.cities = this.cities.filter(city => city.id === this.picked);
newCities.forEach(item => {
this.cities.push(item);
})
this.isLoading = false;
return Promise.resolve();
})
.catch((error) => {
console.log(error); //TODO better error handling
this.isLoading = false;
});
mounted() {
if (this.picked !== null) {
this.internalPicked = this.picked;
this.cities.push(this.picked);
}
},
remove(item) {
this.$emit('removeCity', item);
}
},
}
methods: {
transName(value) {
return value.code && value.name
? `${value.name} (${value.code})`
: "";
},
selectCity(city) {
this.$emit("selectCity", city);
},
listenInputSearch(query) {
if (query.length <= 2) {
return;
}
let c = this.abortControllers.pop();
while (typeof c !== "undefined") {
c.abort();
c = this.abortControllers.pop();
}
this.isLoading = true;
let controller = new AbortController();
this.abortControllers.push(controller);
searchCities(query, this.country, controller)
.then((newCities) => {
this.cities = this.cities.filter(
(city) => city.id === this.picked,
);
newCities.forEach((item) => {
this.cities.push(item);
});
this.isLoading = false;
return Promise.resolve();
})
.catch((error) => {
console.log(error); //TODO better error handling
this.isLoading = false;
});
},
remove(item) {
this.$emit("removeCity", item);
},
},
};
</script>

View File

@@ -1,4 +1,4 @@
import {makeFetch} from 'ChillMainAssets/lib/api/apiMethods';
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
/**
* Endpoint chill_api_single_postal_code__index
@@ -7,14 +7,17 @@ import {makeFetch} from 'ChillMainAssets/lib/api/apiMethods';
* @returns {Promise} a promise containing all Postal Code objects filtered with country
*/
const fetchCities = (country) => {
// warning: do not use fetchResults (in apiMethods): we need only a **part** of the results in the db
const params = new URLSearchParams({item_per_page: 100});
// warning: do not use fetchResults (in apiMethods): we need only a **part** of the results in the db
const params = new URLSearchParams({ item_per_page: 100 });
if (country !== null) {
params.append('country', country.id);
}
if (country !== null) {
params.append("country", country.id);
}
return makeFetch('GET', `/api/1.0/main/postal-code.json?${params.toString()}`).then(r => Promise.resolve(r.results));
return makeFetch(
"GET",
`/api/1.0/main/postal-code.json?${params.toString()}`,
).then((r) => Promise.resolve(r.results));
};
/**
@@ -26,18 +29,16 @@ const fetchCities = (country) => {
* @returns {Promise} a promise containing all Postal Code objects filtered with country and a search string
*/
const searchCities = (search, country, controller) => {
const url = '/api/1.0/main/postal-code/search.json?';
const params = new URLSearchParams({q: search});
const url = "/api/1.0/main/postal-code/search.json?";
const params = new URLSearchParams({ q: search });
if (country !== null) {
Object.assign('country', country.id);
}
if (country !== null) {
Object.assign("country", country.id);
}
return makeFetch('GET', url + params, null, {signal: controller.signal})
.then(result => Promise.resolve(result.results));
return makeFetch("GET", url + params, null, {
signal: controller.signal,
}).then((result) => Promise.resolve(result.results));
};
export {
fetchCities,
searchCities,
};
export { fetchCities, searchCities };

View File

@@ -1,68 +1,80 @@
<template>
<span v-if="data.working_ref_status === 'to_review'" class="badge bg-danger address-details-button-warning">L'adresse de référence a été modifiée</span>
<a v-if="data.loading === false" @click.prevent="clickOrOpen" class="btn btn-sm address-details-button" title="Plus de détails">
<span class="fa fa-map-o"></span>
</a>
<span v-if="data.loading" class="fa fa-spin fa-spinner "></span>
<AddressModal :address="data.working_address" @update-address="onUpdateAddress" ref="address_modal"></AddressModal>
<span
v-if="data.working_ref_status === 'to_review'"
class="badge bg-danger address-details-button-warning"
>L'adresse de référence a été modifiée</span
>
<a
v-if="data.loading === false"
@click.prevent="clickOrOpen"
class="btn btn-sm address-details-button"
title="Plus de détails"
>
<span class="fa fa-map-o"></span>
</a>
<span v-if="data.loading" class="fa fa-spin fa-spinner"></span>
<AddressModal
:address="data.working_address"
@update-address="onUpdateAddress"
ref="address_modal"
></AddressModal>
</template>
<script lang="ts" setup>
import {Address, AddressRefStatus} from "../../../types";
import {onMounted, reactive, ref} from "vue";
import {getAddressById} from "../../../lib/api/address";
import { Address, AddressRefStatus } from "../../../types";
import { onMounted, reactive, ref } from "vue";
import { getAddressById } from "../../../lib/api/address";
import AddressModal from "./AddressModal.vue";
export interface AddressModalContentProps {
address_id: number;
address_ref_status: AddressRefStatus;
address_id: number;
address_ref_status: AddressRefStatus;
}
interface AddressModalData {
loading: boolean,
working_address: Address | null,
working_ref_status: AddressRefStatus | null,
loading: boolean;
working_address: Address | null;
working_ref_status: AddressRefStatus | null;
}
const data: AddressModalData = reactive({
loading: false,
working_address: null,
working_ref_status: null,
loading: false,
working_address: null,
working_ref_status: null,
} as AddressModalData);
const props = defineProps<AddressModalContentProps>();
const emit = defineEmits<(e: 'update-address', value: Address) => void>();
const emit = defineEmits<(e: "update-address", value: Address) => void>();
const address_modal = ref<InstanceType<typeof AddressModal> | null>(null);
onMounted(() => {
data.working_ref_status = props.address_ref_status;
data.working_ref_status = props.address_ref_status;
});
async function clickOrOpen(): Promise<void> {
if (data.working_address === null) {
data.loading = true;
data.working_address = await getAddressById(props.address_id);
data.working_ref_status = data.working_address.refStatus;
data.loading = false;
}
if (data.working_address === null) {
data.loading = true;
data.working_address = await getAddressById(props.address_id);
data.working_ref_status = data.working_address.refStatus;
data.loading = false;
}
// open the modal
address_modal.value?.open();
// open the modal
address_modal.value?.open();
}
const onUpdateAddress = (address: Address): void => {
data.working_address = address;
data.working_ref_status = address.refStatus;
emit('update-address', address);
}
data.working_address = address;
data.working_ref_status = address.refStatus;
emit("update-address", address);
};
</script>
<style scoped lang="scss">
.address-details-button-warning {
display: inline-block;
margin-right: 0.3rem;
display: inline-block;
margin-right: 0.3rem;
}
</style>

View File

@@ -1,31 +1,36 @@
<template>
<address-render-box :address="props.address" :show-button-details="false"></address-render-box>
<address-details-ref-matching :address="props.address" @update-address="onUpdateAddress"></address-details-ref-matching>
<address-details-map :address="props.address"></address-details-map>
<address-details-geographical-layers :address="props.address"></address-details-geographical-layers>
<address-render-box
:address="props.address"
:show-button-details="false"
></address-render-box>
<address-details-ref-matching
:address="props.address"
@update-address="onUpdateAddress"
></address-details-ref-matching>
<address-details-map :address="props.address"></address-details-map>
<address-details-geographical-layers
:address="props.address"
></address-details-geographical-layers>
</template>
<script lang="ts" setup>
import {Address} from "../../../types";
import { Address } from "../../../types";
import AddressDetailsMap from "./Parts/AddressDetailsMap.vue";
import AddressRenderBox from "../Entity/AddressRenderBox.vue";
import AddressDetailsGeographicalLayers from "./Parts/AddressDetailsGeographicalLayers.vue";
import AddressDetailsRefMatching from "./Parts/AddressDetailsRefMatching.vue";
interface AddressModalContentProps {
address: Address,
address: Address;
}
const props = defineProps<AddressModalContentProps>();
const emit = defineEmits<(e: 'update-address', value: Address) => void>();
const emit = defineEmits<(e: "update-address", value: Address) => void>();
const onUpdateAddress = (address: Address): void => {
emit('update-address', address);
}
emit("update-address", address);
};
</script>
<style scoped>
</style>
<style scoped></style>

View File

@@ -1,55 +1,55 @@
<template>
<teleport to="body">
<modal v-if="state.show_modal" @close="close">
<template v-slot:header>
<h2>Détails d'une adresse</h2>
</template>
<template v-slot:body>
<address-details-content :address="props.address" @update-address="onUpdateAddress"></address-details-content>
</template>
</modal>
</teleport>
<teleport to="body">
<modal v-if="state.show_modal" @close="close">
<template v-slot:header>
<h2>Détails d'une adresse</h2>
</template>
<template v-slot:body>
<address-details-content
:address="props.address"
@update-address="onUpdateAddress"
></address-details-content>
</template>
</modal>
</teleport>
</template>
<script lang="ts" setup>
import {reactive} from "vue";
import Modal from 'ChillMainAssets/vuejs/_components/Modal.vue';
import {Address} from "../../../types";
import { reactive } from "vue";
import Modal from "ChillMainAssets/vuejs/_components/Modal.vue";
import { Address } from "../../../types";
import AddressDetailsContent from "./AddressDetailsContent.vue";
interface AddressModalProps {
address: Address
address: Address;
}
interface AddressModalState {
show_modal: boolean,
show_modal: boolean;
}
const props = defineProps<AddressModalProps>();
const emit = defineEmits<(e: 'update-address', value: Address) => void>();
const emit = defineEmits<(e: "update-address", value: Address) => void>();
const state: AddressModalState = reactive({show_modal: false});
const state: AddressModalState = reactive({ show_modal: false });
const open = (): void => {
state.show_modal = true;
}
state.show_modal = true;
};
const close = (): void => {
state.show_modal = false;
}
state.show_modal = false;
};
const onUpdateAddress = (address: Address): void => {
emit('update-address', address);
}
emit("update-address", address);
};
defineExpose({
close,
open,
close,
open,
});
</script>
<style scoped>
</style>
<style scoped></style>

View File

@@ -1,55 +1,59 @@
<template>
<template v-for="(container, index) in data.containers" :key="index">
<h4>{{ container.layer.name.fr }}</h4>
<ul>
<li v-for="(unit, index) in container.units" :key="index">{{ unit.unitName }} ({{ unit.unitRefId }})</li>
</ul>
</template>
<template v-for="(container, index) in data.containers" :key="index">
<h4>{{ container.layer.name.fr }}</h4>
<ul>
<li v-for="(unit, index) in container.units" :key="index">
{{ unit.unitName }} ({{ unit.unitRefId }})
</li>
</ul>
</template>
</template>
<script lang="ts" setup>
import {Address, GeographicalUnitLayer, SimpleGeographicalUnit} from "../../../../types";
import {getGeographicalUnitsByAddress, getAllGeographicalUnitLayers} from "../../../../lib/api/address";
import {onMounted, reactive} from "vue";
import {
Address,
GeographicalUnitLayer,
SimpleGeographicalUnit,
} from "../../../../types";
import {
getGeographicalUnitsByAddress,
getAllGeographicalUnitLayers,
} from "../../../../lib/api/address";
import { onMounted, reactive } from "vue";
export interface AddressDetailsGeographicalLayersProp {
address: Address
};
address: Address;
}
interface GeographicalUnitContainer {
layer: GeographicalUnitLayer;
units: SimpleGeographicalUnit[];
layer: GeographicalUnitLayer;
units: SimpleGeographicalUnit[];
}
const props = defineProps<AddressDetailsGeographicalLayersProp>();
const data: {
containers: GeographicalUnitContainer[]
containers: GeographicalUnitContainer[];
} = reactive({
containers: []
containers: [],
});
onMounted(async () => {
const [units, layers] = await Promise.all([
getGeographicalUnitsByAddress(props.address),
getAllGeographicalUnitLayers()
]) as [SimpleGeographicalUnit[], GeographicalUnitLayer[]];
const [units, layers] = (await Promise.all([
getGeographicalUnitsByAddress(props.address),
getAllGeographicalUnitLayers(),
])) as [SimpleGeographicalUnit[], GeographicalUnitLayer[]];
for (let layer of layers) {
let us = units.filter((u) => u.layerId === layer.id);
if (us.length > 0) {
data.containers.push({
layer,
units: us
});
for (let layer of layers) {
let us = units.filter((u) => u.layerId === layer.id);
if (us.length > 0) {
data.containers.push({
layer,
units: us,
});
}
}
}
})
});
</script>
<style scoped>
</style>
<style scoped></style>

View File

@@ -1,90 +1,108 @@
<template>
<div v-if="props.address.isNoAddress" class="alert alert-info">
Cette adresse est incomplète. La position géographique est approximative.
</div>
<div v-if="props.address.point !== null" class="address_details_map" ref="map_div"></div>
<p>Voir sur <a :href="makeUrlGoogleMap(props.address)" target="_blank">Google Maps</a> <a :href="makeUrlOsm(props.address)" target="_blank">OSM</a></p>
<div v-if="props.address.isNoAddress" class="alert alert-info">
Cette adresse est incomplète. La position géographique est
approximative.
</div>
<div
v-if="props.address.point !== null"
class="address_details_map"
ref="map_div"
></div>
<p>
Voir sur
<a :href="makeUrlGoogleMap(props.address)" target="_blank"
>Google Maps</a
>
<a :href="makeUrlOsm(props.address)" target="_blank">OSM</a>
</p>
</template>
<script lang="ts" setup>
import {onMounted, ref} from "vue";
import 'leaflet/dist/leaflet.css';
import { onMounted, ref } from "vue";
import "leaflet/dist/leaflet.css";
import markerIconPng from "leaflet/dist/images/marker-icon.png";
import L, {LatLngExpression, LatLngTuple} from "leaflet";
import {Address, Point} from "../../../../types";
import L, { LatLngExpression, LatLngTuple } from "leaflet";
import { Address, Point } from "../../../../types";
const lonLatForLeaflet = (point: Point): LatLngTuple => {
return [point.coordinates[1], point.coordinates[0]];
}
return [point.coordinates[1], point.coordinates[0]];
};
export interface MapProps {
address: Address,
address: Address;
}
const props = defineProps<MapProps>();
const map_div = ref<HTMLDivElement | null>(null)
let map: L.Map|null = null;
let marker: L.Marker|null = null;
const map_div = ref<HTMLDivElement | null>(null);
let map: L.Map | null = null;
let marker: L.Marker | null = null;
onMounted(() => {
if (map_div.value === null) {
// there is no map div when the address does not have any Point
return;
}
if (map_div.value === null) {
// there is no map div when the address does not have any Point
return;
}
if (props.address.point !== null) {
map = L.map(map_div.value);
map.setView(lonLatForLeaflet(props.address.point), 18);
if (props.address.point !== null) {
map = L.map(map_div.value);
map.setView(lonLatForLeaflet(props.address.point), 18);
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
attribution:
'&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
}).addTo(map);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
const markerIcon = L.icon({
iconUrl: markerIconPng,
iconAnchor: [12, 41],
});
const markerIcon = L.icon({
iconUrl: markerIconPng,
iconAnchor: [12, 41],
});
marker = L.marker(lonLatForLeaflet(props.address.point), {icon: markerIcon});
marker.addTo(map);
}
marker = L.marker(lonLatForLeaflet(props.address.point), {
icon: markerIcon,
});
marker.addTo(map);
}
});
const makeUrlGoogleMap = (address: Address): string => {
const params = new URLSearchParams();
params.append('api', '1');
if (address.point !== null && address.addressReference !== null) {
params.append('query', `${address.point.coordinates[1]} ${address.point.coordinates[0]}`);
} else {
params.append('query', address.lines.join(', '));
}
const params = new URLSearchParams();
params.append("api", "1");
if (address.point !== null && address.addressReference !== null) {
params.append(
"query",
`${address.point.coordinates[1]} ${address.point.coordinates[0]}`,
);
} else {
params.append("query", address.lines.join(", "));
}
return `https://www.google.com/maps/search/?${params.toString()}`;
}
return `https://www.google.com/maps/search/?${params.toString()}`;
};
const makeUrlOsm = (address: Address): string => {
if (address.point !== null && address.addressReference !== null) {
if (address.point !== null && address.addressReference !== null) {
const params = new URLSearchParams();
params.append("mlat", `${address.point.coordinates[1]}`);
params.append("mlon", `${address.point.coordinates[0]}`);
const hashParams = new URLSearchParams();
hashParams.append(
"map",
`18/${address.point.coordinates[1]}/${address.point.coordinates[0]}`,
);
return `https://www.openstreetmap.org/?${params.toString()}#${hashParams.toString()}`;
}
const params = new URLSearchParams();
params.append('mlat', `${address.point.coordinates[1]}`);
params.append('mlon', `${address.point.coordinates[0]}`);
const hashParams = new URLSearchParams();
hashParams.append('map', `18/${address.point.coordinates[1]}/${address.point.coordinates[0]}`);
params.append("query", address.lines.join(", "));
return `https://www.openstreetmap.org/?${params.toString()}#${hashParams.toString()}`;
}
const params = new URLSearchParams();
params.append('query', address.lines.join(', '));
return `https://www.openstreetmap.org/search?${params.toString()}`;
}
return `https://www.openstreetmap.org/search?${params.toString()}`;
};
</script>
<style scoped>
div.address_details_map {
height: 250px;
height: 250px;
}
</style>

View File

@@ -1,94 +1,179 @@
<template>
<template v-if="props.address.refStatus !== 'match'">
<div v-if="props.address.refStatus === 'to_review' || props.address.refStatus === 'reviewed'" :class="{alert: true, 'alert-danger': props.address.refStatus === 'to_review', 'alert-warning': props.address.refStatus === 'reviewed'}">
<p v-if="props.address.refStatus === 'to_review'"><i class="fa fa-warning"></i> L'adresse de référence a été modifiée.</p>
<p v-if="props.address.refStatus === 'reviewed'">L'adresse est conservée, mais diffère de l'adresse de référence.</p>
<template v-if="props.address.refStatus !== 'match'">
<div
v-if="
props.address.refStatus === 'to_review' ||
props.address.refStatus === 'reviewed'
"
:class="{
alert: true,
'alert-danger': props.address.refStatus === 'to_review',
'alert-warning': props.address.refStatus === 'reviewed',
}"
>
<p v-if="props.address.refStatus === 'to_review'">
<i class="fa fa-warning"></i> L'adresse de référence a été
modifiée.
</p>
<p v-if="props.address.refStatus === 'reviewed'">
L'adresse est conservée, mais diffère de l'adresse de référence.
</p>
<template v-if="props.address.addressReference.street !== props.address.street || props.address.addressReference.streetNumber !== props.address.streetNumber">
<template v-if="props.address.country.code === 'BE'">
<div class="difference">
<span class="old">{{ props.address.street }} {{props.address.streetNumber}}</span>
<span class="new">{{ props.address.addressReference.street }} {{ props.address.addressReference.streetNumber }}</span>
</div>
</template>
<template v-else>
<div class="difference">
<span class="old">{{props.address.streetNumber}} {{ props.address.street }}</span>
<span class="new">{{ props.address.addressReference.streetNumber }} {{ props.address.addressReference.street }}</span>
</div>
</template>
</template>
<template
v-if="
props.address.addressReference.street !==
props.address.street ||
props.address.addressReference.streetNumber !==
props.address.streetNumber
"
>
<template v-if="props.address.country.code === 'BE'">
<div class="difference">
<span class="old"
>{{ props.address.street }}
{{ props.address.streetNumber }}</span
>
<span class="new"
>{{ props.address.addressReference.street }}
{{
props.address.addressReference.streetNumber
}}</span
>
</div>
</template>
<template v-else>
<div class="difference">
<span class="old"
>{{ props.address.streetNumber }}
{{ props.address.street }}</span
>
<span class="new"
>{{ props.address.addressReference.streetNumber }}
{{ props.address.addressReference.street }}</span
>
</div>
</template>
</template>
<template v-if="props.address.addressReference.postcode.id !== props.address.postcode.id">
<div class="difference">
<span class="old">{{ props.address.postcode.code }} {{props.address.postcode.name }}</span>
<span class="new">{{ props.address.addressReference.postcode.code }} {{ props.address.addressReference.postcode.name }}</span>
<template
v-if="
props.address.addressReference.postcode.id !==
props.address.postcode.id
"
>
<div class="difference">
<span class="old"
>{{ props.address.postcode.code }}
{{ props.address.postcode.name }}</span
>
<span class="new"
>{{ props.address.addressReference.postcode.code }}
{{ props.address.addressReference.postcode.name }}</span
>
</div>
</template>
<template
v-if="
props.address.point !== null &&
(props.address.point.coordinates[0] !==
props.address.addressReference.point.coordinates[0] ||
props.address.point.coordinates[1] !==
props.address.addressReference.point.coordinates[1])
"
>
<div class="difference">
<span class="old"
>{{ props.address.point.coordinates[0] }}
{{ props.address.point.coordinates[1] }}</span
>
<span class="new"
>{{
props.address.addressReference.point.coordinates[0]
}}
{{
props.address.addressReference.point.coordinates[1]
}}</span
>
</div>
</template>
<ul class="record_actions">
<li v-if="props.address.refStatus === 'to_review'">
<button class="btn btn-sm btn-update" @click="applyUpdate">
Appliquer les modifications
</button>
</li>
<li v-if="props.address.refStatus === 'to_review'">
<button
class="btn btn-sm btn-primary"
@click="keepCurrentAddress"
>
Conserver
</button>
</li>
<li v-if="props.address.refStatus === 'reviewed'">
<button
class="btn btn-sm btn-primary"
@click="backToReview"
>
Ré-examiner
</button>
</li>
</ul>
</div>
</template>
<template v-if="props.address.point !== null && (props.address.point.coordinates[0] !== props.address.addressReference.point.coordinates[0] || props.address.point.coordinates[1] !== props.address.addressReference.point.coordinates[1])">
<div class="difference">
<span class="old">{{ props.address.point.coordinates[0] }} {{ props.address.point.coordinates[1]}}</span>
<span class="new">{{ props.address.addressReference.point.coordinates[0] }} {{ props.address.addressReference.point.coordinates[1]}}</span>
</div>
</template>
<ul class="record_actions">
<li v-if="props.address.refStatus === 'to_review'"><button class="btn btn-sm btn-update" @click="applyUpdate">Appliquer les modifications</button></li>
<li v-if="props.address.refStatus === 'to_review'"><button class="btn btn-sm btn-primary" @click="keepCurrentAddress">Conserver</button></li>
<li v-if="props.address.refStatus === 'reviewed'"><button class="btn btn-sm btn-primary" @click="backToReview">-examiner</button></li>
</ul>
</div>
</template>
</template>
</template>
<script lang="ts" setup>
import {Address} from "../../../../types";
import {markAddressReviewed, markAddressToReview, syncAddressWithReference} from "../../../../lib/api/address";
import { Address } from "../../../../types";
import {
markAddressReviewed,
markAddressToReview,
syncAddressWithReference,
} from "../../../../lib/api/address";
export interface AddressDetailsRefMatchingProps {
address: Address;
address: Address;
}
const props = defineProps<AddressDetailsRefMatchingProps>();
const emit = defineEmits<(e: 'update-address', value: Address) => void>();
const emit = defineEmits<(e: "update-address", value: Address) => void>();
const applyUpdate = async () => {
const new_address = await syncAddressWithReference(props.address);
emit('update-address', new_address);
}
emit("update-address", new_address);
};
const keepCurrentAddress = async () => {
const new_address = await markAddressReviewed(props.address);
const new_address = await markAddressReviewed(props.address);
emit("update-address", new_address);
}
emit("update-address", new_address);
};
const backToReview = async () => {
const new_address = await markAddressToReview(props.address);
emit("update-address", new_address);
}
const new_address = await markAddressToReview(props.address);
emit("update-address", new_address);
};
</script>
<style scoped lang="scss">
.difference {
margin-bottom: 0.5rem;
margin-bottom: 0.5rem;
span {
display: block;
}
span {
display: block;
}
.old {
text-decoration: red line-through;
}
.new {
font-weight: bold;
color: green;
}
.old {
text-decoration: red line-through;
}
.new {
font-weight: bold;
color: green;
}
}
</style>

View File

@@ -1,58 +1,47 @@
<template>
<span
v-if="entity.type === 'person'"
class="badge rounded-pill bg-person"
>
{{ $t('person') }}
</span>
<span v-if="entity.type === 'person'" class="badge rounded-pill bg-person">
{{ $t("person") }}
</span>
<span
v-if="entity.type === 'thirdparty'"
class="badge rounded-pill bg-thirdparty"
>
<template v-if="options.displayLong !== true">
{{ $t('thirdparty.thirdparty') }}
</template>
<span
v-if="entity.type === 'thirdparty'"
class="badge rounded-pill bg-thirdparty"
>
<template v-if="options.displayLong !== true">
{{ $t("thirdparty.thirdparty") }}
</template>
<i
class="fa fa-fw fa-user"
v-if="entity.kind === 'child'"
/>
<i
class="fa fa-fw fa-hospital-o"
v-else-if="entity.kind === 'company'"
/>
<i
class="fa fa-fw fa-user-md"
v-else
/>
<i class="fa fa-fw fa-user" v-if="entity.kind === 'child'" />
<i
class="fa fa-fw fa-hospital-o"
v-else-if="entity.kind === 'company'"
/>
<i class="fa fa-fw fa-user-md" v-else />
<template v-if="options.displayLong === true">
<span v-if="entity.kind === 'child'">{{ $t('thirdparty.child') }}</span>
<span v-else-if="entity.kind === 'company'">{{ $t('thirdparty.company') }}</span>
<span v-else>{{ $t('thirdparty.contact') }}</span>
</template>
</span>
<template v-if="options.displayLong === true">
<span v-if="entity.kind === 'child'">{{
$t("thirdparty.child")
}}</span>
<span v-else-if="entity.kind === 'company'">{{
$t("thirdparty.company")
}}</span>
<span v-else>{{ $t("thirdparty.contact") }}</span>
</template>
</span>
<span
v-if="entity.type === 'user'"
class="badge rounded-pill bg-user"
>
{{ $t('user') }}
</span>
<span v-if="entity.type === 'user'" class="badge rounded-pill bg-user">
{{ $t("user") }}
</span>
<span
v-if="entity.type === 'household'"
class="badge rounded-pill bg-user"
>
{{ $t('household') }}
</span>
<span v-if="entity.type === 'household'" class="badge rounded-pill bg-user">
{{ $t("household") }}
</span>
</template>
<script>
export default {
name: "BadgeEntity",
props: ['options', 'entity'],
props: ["options", "entity"],
i18n: {
messages: {
fr: {
@@ -63,10 +52,10 @@ export default {
company: "Personne morale",
contact: "Personne physique",
},
user: 'TMS',
household: 'Ménage',
}
}
}
}
user: "TMS",
household: "Ménage",
},
},
},
};
</script>

View File

@@ -1,17 +1,17 @@
<template>
<div class="confidential">
<div :class="{ 'blur': isBlurred }">
<slot name="confidential-content" />
<div class="confidential">
<div :class="{ blur: isBlurred }">
<slot name="confidential-content" />
</div>
<div class="toggle-container">
<i
class="fa toggle"
:class="toggleIcon"
aria-hidden="true"
@click="toggleBlur"
/>
</div>
</div>
<div class="toggle-container">
<i
class="fa toggle"
:class="toggleIcon"
aria-hidden="true"
@click="toggleBlur"
/>
</div>
</div>
</template>
<script>
@@ -20,21 +20,21 @@ export default {
data() {
return {
isBlurred: true,
toggleIcon: 'fa-eye',
toggleIcon: "fa-eye",
};
},
methods : {
methods: {
toggleBlur() {
console.log(this.positionBtnFar);
this.isBlurred = !this.isBlurred;
this.toggleIcon = this.isBlurred ? 'fa-eye' : 'fa-eye-slash';
this.toggleIcon = this.isBlurred ? "fa-eye" : "fa-eye-slash";
},
},
}
};
</script>
<style scoped lang='scss'>
.confidential{
align-content: center;
}
<style scoped lang="scss">
.confidential {
align-content: center;
}
</style>

View File

@@ -1,150 +1,131 @@
<template>
<component
:is="component"
class="chill-entity entity-address"
>
<component
:is="component"
class="address"
:class="multiline"
>
<div v-if="isConfidential">
<confidential :position-btn-far="true">
<template #confidential-content>
<div v-if="isMultiline === true">
<p
v-for="(l, i) in address.lines"
:key="`line-${i}`"
>
{{ l }}
</p>
<p v-if="showButtonDetails">
<address-details-button
:address_id="address.address_id"
:address_ref_status="address.refStatus"
/>
</p>
<component :is="component" class="chill-entity entity-address">
<component :is="component" class="address" :class="multiline">
<div v-if="isConfidential">
<confidential :position-btn-far="true">
<template #confidential-content>
<div v-if="isMultiline === true">
<p
v-for="(l, i) in address.lines"
:key="`line-${i}`"
>
{{ l }}
</p>
<p v-if="showButtonDetails">
<address-details-button
:address_id="address.address_id"
:address_ref_status="address.refStatus"
/>
</p>
</div>
<div v-else>
<p v-if="'' !== address.text" class="street">
{{ address.text }}
</p>
<p
v-if="null !== address.postcode"
class="postcode"
>
{{ address.postcode.code }}
{{ address.postcode.name }}
</p>
<p v-if="null !== address.country" class="country">
{{ address.country.name.fr }}
</p>
</div>
</template>
</confidential>
</div>
<div v-else>
<p
v-if="'' !== address.text"
class="street"
>
{{ address.text }}
</p>
<p
v-if="null !== address.postcode"
class="postcode"
>
{{ address.postcode.code }} {{ address.postcode.name }}
</p>
<p
v-if="null !== address.country"
class="country"
>
{{ address.country.name.fr }}
</p>
</div>
</template>
</confidential>
</div>
<div v-if="!isConfidential">
<div v-if="isMultiline === true">
<p
v-for="(l, i) in address.lines"
:key="`line-${i}`"
>
{{ l }}
</p>
<p v-if="showButtonDetails">
<address-details-button
:address_id="address.address_id"
:address_ref_status="address.refStatus"
/>
</p>
<div v-if="!isConfidential">
<div v-if="isMultiline === true">
<p v-for="(l, i) in address.lines" :key="`line-${i}`">
{{ l }}
</p>
<p v-if="showButtonDetails">
<address-details-button
:address_id="address.address_id"
:address_ref_status="address.refStatus"
/>
</p>
</div>
<div v-else>
<p v-if="address.text" class="street">
{{ address.text }}
<template v-if="showButtonDetails">
<address-details-button
:address_id="address.address_id"
:address_ref_status="address.refStatus"
/>
</template>
</p>
</div>
</div>
</component>
<div v-if="useDatePane === true" class="address-more">
<div v-if="address.validFrom">
<span class="validFrom">
<b>{{ $t("validFrom") }}</b
>: {{ $d(address.validFrom.date) }}
</span>
</div>
<div v-if="address.validTo">
<span class="validTo">
<b>{{ $t("validTo") }}</b
>: {{ $d(address.validTo.date) }}
</span>
</div>
</div>
<div v-else>
<p
v-if="address.text"
class="street"
>
{{ address.text }} <template v-if="showButtonDetails">
<address-details-button
:address_id="address.address_id"
:address_ref_status="address.refStatus"
/>
</template>
</p>
</div>
</div>
</component>
<div
v-if="useDatePane === true"
class="address-more"
>
<div v-if="address.validFrom">
<span class="validFrom">
<b>{{ $t('validFrom') }}</b>: {{ $d(address.validFrom.date) }}
</span>
</div>
<div v-if="address.validTo">
<span class="validTo">
<b>{{ $t('validTo') }}</b>: {{ $d(address.validTo.date) }}
</span>
</div>
</div>
</component>
</template>
<script>
import Confidential from 'ChillMainAssets/vuejs/_components/Confidential.vue';
import Confidential from "ChillMainAssets/vuejs/_components/Confidential.vue";
import AddressDetailsButton from "ChillMainAssets/vuejs/_components/AddressDetails/AddressDetailsButton.vue";
export default {
name: 'AddressRenderBox',
components: {
Confidential,
AddressDetailsButton,
},
props: {
address: {
type: Object
},
isMultiline: {
default: true,
type: Boolean
},
useDatePane: {
default: false,
type: Boolean
},
showButtonDetails: {
default: true,
type: Boolean
}
},
computed: {
component() {
return this.isMultiline === true ? "div" : "span";
},
multiline() {
return this.isMultiline === true ? "multiline" : "";
},
isConfidential() {
return this.address.confidential;
}
}
name: "AddressRenderBox",
components: {
Confidential,
AddressDetailsButton,
},
props: {
address: {
type: Object,
},
isMultiline: {
default: true,
type: Boolean,
},
useDatePane: {
default: false,
type: Boolean,
},
showButtonDetails: {
default: true,
type: Boolean,
},
},
computed: {
component() {
return this.isMultiline === true ? "div" : "span";
},
multiline() {
return this.isMultiline === true ? "multiline" : "";
},
isConfidential() {
return this.address.confidential;
},
},
};
</script>
<style lang="scss" scoped>
p {
&:after {
content: " ";
margin-right: 0.3em;
}
&:after {
content: " ";
margin-right: 0.3em;
}
}
</style>

View File

@@ -1,11 +1,9 @@
<template>
<i :class="gender.icon" />
<i :class="gender.icon" />
</template>
<script setup>
const props = defineProps({
gender: Object
})
gender: Object,
});
</script>

View File

@@ -1,23 +1,24 @@
<template>
<span class="chill-entity entity-user">
{{ user.label }}
<span
class="user-job"
v-if="user.user_job !== null"
>({{ user.user_job.label.fr }})</span> <span
class="main-scope"
v-if="user.main_scope !== null"
>({{ user.main_scope.name.fr }})</span> <span
v-if="user.isAbsent"
class="badge bg-danger rounded-pill"
:title="Absent"
>A</span>
</span>
<span class="chill-entity entity-user">
{{ user.label }}
<span class="user-job" v-if="user.user_job !== null"
>({{ user.user_job.label.fr }})</span
>
<span class="main-scope" v-if="user.main_scope !== null"
>({{ user.main_scope.name.fr }})</span
>
<span
v-if="user.isAbsent"
class="badge bg-danger rounded-pill"
:title="Absent"
>A</span
>
</span>
</template>
<script>
export default {
name: "UserRenderBoxBadge",
props: ['user'],
}
props: ["user"],
};
</script>

View File

@@ -1,58 +1,61 @@
<template>
<div class="d-grid gap-2 my-3">
<button
class="btn btn-misc"
type="button"
v-if="!subscriberFinal"
@click="subscribeTo('subscribe', 'final')"
>
<i class="fa fa-check fa-fw" />
{{ $t('subscribe_final') }}
</button>
<button
class="btn btn-misc"
type="button"
v-if="subscriberFinal"
@click="subscribeTo('unsubscribe', 'final')"
>
<i class="fa fa-times fa-fw" />
{{ $t('unsubscribe_final') }}
</button>
<button
class="btn btn-misc"
type="button"
v-if="!subscriberStep"
@click="subscribeTo('subscribe', 'step')"
>
<i class="fa fa-check fa-fw" />
{{ $t('subscribe_all_steps') }}
</button>
<button
class="btn btn-misc"
type="button"
v-if="subscriberStep"
@click="subscribeTo('unsubscribe', 'step')"
>
<i class="fa fa-times fa-fw" />
{{ $t('unsubscribe_all_steps') }}
</button>
</div>
<div class="d-grid gap-2 my-3">
<button
class="btn btn-misc"
type="button"
v-if="!subscriberFinal"
@click="subscribeTo('subscribe', 'final')"
>
<i class="fa fa-check fa-fw" />
{{ $t("subscribe_final") }}
</button>
<button
class="btn btn-misc"
type="button"
v-if="subscriberFinal"
@click="subscribeTo('unsubscribe', 'final')"
>
<i class="fa fa-times fa-fw" />
{{ $t("unsubscribe_final") }}
</button>
<button
class="btn btn-misc"
type="button"
v-if="!subscriberStep"
@click="subscribeTo('subscribe', 'step')"
>
<i class="fa fa-check fa-fw" />
{{ $t("subscribe_all_steps") }}
</button>
<button
class="btn btn-misc"
type="button"
v-if="subscriberStep"
@click="subscribeTo('unsubscribe', 'step')"
>
<i class="fa fa-times fa-fw" />
{{ $t("unsubscribe_all_steps") }}
</button>
</div>
</template>
<script>
import {makeFetch} from 'ChillMainAssets/lib/api/apiMethods.ts';
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods.ts";
export default {
name: "EntityWorkflowVueSubscriber",
i18n: {
messages: {
fr: {
subscribe_final: "Recevoir une notification à l'étape finale",
unsubscribe_final: "Ne plus recevoir de notification à l'étape finale",
subscribe_all_steps: "Recevoir une notification à chaque étape du suivi",
unsubscribe_all_steps: "Ne plus recevoir de notification à chaque étape du suivi",
}
}
messages: {
fr: {
subscribe_final: "Recevoir une notification à l'étape finale",
unsubscribe_final:
"Ne plus recevoir de notification à l'étape finale",
subscribe_all_steps:
"Recevoir une notification à chaque étape du suivi",
unsubscribe_all_steps:
"Ne plus recevoir de notification à chaque étape du suivi",
},
},
},
props: {
entityWorkflowId: {
@@ -68,20 +71,22 @@ export default {
required: true,
},
},
emits: ['subscriptionUpdated'],
emits: ["subscriptionUpdated"],
methods: {
subscribeTo(step, to) {
let params = new URLSearchParams();
params.set('subscribe', to);
params.set("subscribe", to);
const url = `/api/1.0/main/workflow/${this.entityWorkflowId}/${step}?` + params.toString();
const url =
`/api/1.0/main/workflow/${this.entityWorkflowId}/${step}?` +
params.toString();
makeFetch('POST', url).then(response => {
this.$emit('subscriptionUpdated', response);
makeFetch("POST", url).then((response) => {
this.$emit("subscriptionUpdated", response);
});
}
},
},
}
};
/*
* ALTERNATIVES
*

View File

@@ -1,147 +1,149 @@
<template>
<div
class="flex-table workflow"
id="workflow-list"
>
<div
v-for="(w, i) in workflows"
:key="`workflow-${i}`"
class="item-bloc"
>
<div>
<div class="item-row col">
<h2>{{ w.title }}</h2>
<div class="flex-grow-1 ms-3 h3">
<div class="visually-hidden">
{{ w.relatedEntityClass }}
{{ w.relatedEntityId }}
<div class="flex-table workflow" id="workflow-list">
<div
v-for="(w, i) in workflows"
:key="`workflow-${i}`"
class="item-bloc"
>
<div>
<div class="item-row col">
<h2>{{ w.title }}</h2>
<div class="flex-grow-1 ms-3 h3">
<div class="visually-hidden">
{{ w.relatedEntityClass }}
{{ w.relatedEntityId }}
</div>
</div>
</div>
<div class="breadcrumb">
<template v-for="(step, j) in w.steps" :key="`step-${j}`">
<span
class="mx-2"
tabindex="0"
data-bs-trigger="focus hover"
data-bs-toggle="popover"
data-bs-placement="bottom"
data-bs-custom-class="workflow-transition"
:title="getPopTitle(step)"
:data-bs-content="getPopContent(step)"
>
<i
v-if="step.currentStep.name === 'initial'"
class="fa fa-circle me-1 text-chill-yellow"
/>
<i
v-if="step.isFreezed"
class="fa fa-snowflake-o fa-sm me-1"
/>
{{ step.currentStep.text }}
</span>
<span v-if="j !== Object.keys(w.steps).length - 1">
</span>
</template>
</div>
</div>
</div>
</div>
<div class="breadcrumb">
<template
v-for="(step, j) in w.steps"
:key="`step-${j}`"
>
<span
class="mx-2"
tabindex="0"
data-bs-trigger="focus hover"
data-bs-toggle="popover"
data-bs-placement="bottom"
data-bs-custom-class="workflow-transition"
:title="getPopTitle(step)"
:data-bs-content="getPopContent(step)"
>
<i
v-if="step.currentStep.name === 'initial'"
class="fa fa-circle me-1 text-chill-yellow"
/>
<i
v-if="step.isFreezed"
class="fa fa-snowflake-o fa-sm me-1"
/>
{{ step.currentStep.text }}
</span>
<span v-if="j !== Object.keys(w.steps).length - 1">
</span>
</template>
<div class="item-row">
<div class="item-col flex-grow-1">
<p v-if="isUserSubscribedToStep(w)">
<i class="fa fa-check fa-fw" />
{{ $t("you_subscribed_to_all_steps") }}
</p>
<p v-if="isUserSubscribedToFinal(w)">
<i class="fa fa-check fa-fw" />
{{ $t("you_subscribed_to_final_step") }}
</p>
</div>
<div class="item-col">
<ul class="record_actions">
<li>
<a
:href="goToUrl(w)"
class="btn btn-sm btn-show"
:title="$t('action.show')"
/>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="item-row">
<div class="item-col flex-grow-1">
<p v-if="isUserSubscribedToStep(w)">
<i class="fa fa-check fa-fw" />
{{ $t('you_subscribed_to_all_steps') }}
</p>
<p v-if="isUserSubscribedToFinal(w)">
<i class="fa fa-check fa-fw" />
{{ $t('you_subscribed_to_final_step') }}
</p>
</div>
<div class="item-col">
<ul class="record_actions">
<li>
<a
:href="goToUrl(w)"
class="btn btn-sm btn-show"
:title="$t('action.show')"
/>
</li>
</ul>
</div>
</div>
</div>
</div>
</template>
<script>
import Popover from 'bootstrap/js/src/popover';
import Popover from "bootstrap/js/src/popover";
const i18n = {
messages: {
fr: {
you_subscribed_to_all_steps: "Vous recevrez une notification à chaque étape",
you_subscribed_to_final_step: "Vous recevrez une notification à l'étape finale",
by: "Par",
at: "Le"
}
}
}
messages: {
fr: {
you_subscribed_to_all_steps:
"Vous recevrez une notification à chaque étape",
you_subscribed_to_final_step:
"Vous recevrez une notification à l'étape finale",
by: "Par",
at: "Le",
},
},
};
export default {
name: "ListWorkflow",
i18n: i18n,
props: {
workflows: {
type: Array,
required: true,
}
},
methods: {
goToUrl(w) {
return `/fr/main/workflow/${w.id}/show`;
},
getPopTitle(step) {
if (step.transitionPrevious != null) {
//console.log(step.transitionPrevious.text);
let freezed = step.isFreezed ? `<i class="fa fa-snowflake-o fa-sm me-1"></i>` : ``;
return `${freezed}${step.transitionPrevious.text}`;
}
},
getPopContent(step) {
if (step.transitionPrevious != null) {
return `<ul class="small_in_title">
name: "ListWorkflow",
i18n: i18n,
props: {
workflows: {
type: Array,
required: true,
},
},
methods: {
goToUrl(w) {
return `/fr/main/workflow/${w.id}/show`;
},
getPopTitle(step) {
if (step.transitionPrevious != null) {
//console.log(step.transitionPrevious.text);
let freezed = step.isFreezed
? `<i class="fa fa-snowflake-o fa-sm me-1"></i>`
: ``;
return `${freezed}${step.transitionPrevious.text}`;
}
},
getPopContent(step) {
if (step.transitionPrevious != null) {
return `<ul class="small_in_title">
<li><span class="item-key">${i18n.messages.fr.by} : </span><b>${step.transitionPreviousBy.text}</b></li>
<li><span class="item-key">${i18n.messages.fr.at} : </span><b>${this.formatDate(step.transitionPreviousAt.datetime)}</b></li>
</ul>`
;
}
},
formatDate(datetime) {
return datetime.split('T')[0] +' '+ datetime.split('T')[1].substring(0,5)
},
isUserSubscribedToStep() {
// todo
return false;
},
isUserSubscribedToFinal() {
// todo
return false;
},
},
mounted() {
const triggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'));
const popoverList = triggerList.map(function (el) {
//console.log('popover', el)
return new Popover(el, {
html: true,
});
});
}
}
</ul>`;
}
},
formatDate(datetime) {
return (
datetime.split("T")[0] +
" " +
datetime.split("T")[1].substring(0, 5)
);
},
isUserSubscribedToStep() {
// todo
return false;
},
isUserSubscribedToFinal() {
// todo
return false;
},
},
mounted() {
const triggerList = [].slice.call(
document.querySelectorAll('[data-bs-toggle="popover"]'),
);
const popoverList = triggerList.map(function (el) {
//console.log('popover', el)
return new Popover(el, {
html: true,
});
});
},
};
</script>

View File

@@ -1,140 +1,140 @@
<template>
<button
v-if="hasWorkflow"
class="btn btn-primary"
@click="openModal"
>
<b>{{ countWorkflows }}</b>
<template v-if="countWorkflows > 1">
{{ $t('workflows') }}
</template>
<template v-else>
{{ $t('workflow') }}
</template>
</button>
<button v-if="hasWorkflow" class="btn btn-primary" @click="openModal">
<b>{{ countWorkflows }}</b>
<template v-if="countWorkflows > 1">
{{ $t("workflows") }}
</template>
<template v-else>
{{ $t("workflow") }}
</template>
</button>
<pick-workflow
v-else-if="allowCreate"
:related-entity-class="this.relatedEntityClass"
:related-entity-id="this.relatedEntityId"
:workflows-availables="workflowsAvailables"
:prevent-default-move-to-generate="this.$props.preventDefaultMoveToGenerate"
:go-to-generate-workflow-payload="this.goToGenerateWorkflowPayload"
@go-to-generate-workflow="goToGenerateWorkflow"
/>
<pick-workflow
v-else-if="allowCreate"
:related-entity-class="this.relatedEntityClass"
:related-entity-id="this.relatedEntityId"
:workflows-availables="workflowsAvailables"
:prevent-default-move-to-generate="
this.$props.preventDefaultMoveToGenerate
"
:go-to-generate-workflow-payload="this.goToGenerateWorkflowPayload"
@go-to-generate-workflow="goToGenerateWorkflow"
/>
<teleport to="body">
<modal
v-if="modal.showModal"
:modal-dialog-class="modal.modalDialogClass"
@close="modal.showModal = false"
>
<template #header>
<h2 class="modal-title">
{{ $t('workflow_list') }}
</h2>
</template>
<teleport to="body">
<modal
v-if="modal.showModal"
:modal-dialog-class="modal.modalDialogClass"
@close="modal.showModal = false"
>
<template #header>
<h2 class="modal-title">
{{ $t("workflow_list") }}
</h2>
</template>
<template #body>
<list-workflow-vue
:workflows="workflows"
/>
</template>
<template #body>
<list-workflow-vue :workflows="workflows" />
</template>
<template #footer>
<pick-workflow
v-if="allowCreate"
:related-entity-class="this.relatedEntityClass"
:related-entity-id="this.relatedEntityId"
:workflows-availables="workflowsAvailables"
:prevent-default-move-to-generate="this.$props.preventDefaultMoveToGenerate"
:go-to-generate-workflow-payload="this.goToGenerateWorkflowPayload"
@go-to-generate-workflow="this.goToGenerateWorkflow"
/>
</template>
</modal>
</teleport>
<template #footer>
<pick-workflow
v-if="allowCreate"
:related-entity-class="this.relatedEntityClass"
:related-entity-id="this.relatedEntityId"
:workflows-availables="workflowsAvailables"
:prevent-default-move-to-generate="
this.$props.preventDefaultMoveToGenerate
"
:go-to-generate-workflow-payload="
this.goToGenerateWorkflowPayload
"
@go-to-generate-workflow="this.goToGenerateWorkflow"
/>
</template>
</modal>
</teleport>
</template>
<script>
import Modal from 'ChillMainAssets/vuejs/_components/Modal';
import PickWorkflow from 'ChillMainAssets/vuejs/_components/EntityWorkflow/PickWorkflow.vue';
import ListWorkflowVue from 'ChillMainAssets/vuejs/_components/EntityWorkflow/ListWorkflow.vue';
import Modal from "ChillMainAssets/vuejs/_components/Modal";
import PickWorkflow from "ChillMainAssets/vuejs/_components/EntityWorkflow/PickWorkflow.vue";
import ListWorkflowVue from "ChillMainAssets/vuejs/_components/EntityWorkflow/ListWorkflow.vue";
export default {
name: "ListWorkflowModal",
components: {
Modal,
PickWorkflow,
ListWorkflowVue
},
emits: ['goToGenerateWorkflow'],
props: {
workflows: {
type: Array,
required: true,
},
allowCreate: {
type: Boolean,
required: true,
},
relatedEntityClass: {
type: String,
required: true,
},
relatedEntityId: {
type: Number,
required: false,
},
workflowsAvailables: {
type: Array,
required: true,
},
preventDefaultMoveToGenerate: {
type: Boolean,
required: false,
default: false,
},
goToGenerateWorkflowPayload: {
required: false,
default: {}
},
},
name: "ListWorkflowModal",
components: {
Modal,
PickWorkflow,
ListWorkflowVue,
},
emits: ["goToGenerateWorkflow"],
props: {
workflows: {
type: Array,
required: true,
},
allowCreate: {
type: Boolean,
required: true,
},
relatedEntityClass: {
type: String,
required: true,
},
relatedEntityId: {
type: Number,
required: false,
},
workflowsAvailables: {
type: Array,
required: true,
},
preventDefaultMoveToGenerate: {
type: Boolean,
required: false,
default: false,
},
goToGenerateWorkflowPayload: {
required: false,
default: {},
},
},
data() {
return {
modal: {
showModal: false,
modalDialogClass: "modal-dialog-scrollable modal-xl"
},
}
},
computed: {
countWorkflows() {
return this.workflows.length;
},
hasWorkflow() {
return this.countWorkflows > 0;
}
},
methods: {
openModal() {
this.modal.showModal = true;
},
goToGenerateWorkflow(data) {
console.log('go to generate workflow intercepted', data);
this.$emit('goToGenerateWorkflow', data);
}
},
i18n: {
messages: {
fr: {
workflow_list: "Liste des workflows associés",
workflow: " workflow associé",
workflows: " workflows associés",
}
}
}
}
return {
modal: {
showModal: false,
modalDialogClass: "modal-dialog-scrollable modal-xl",
},
};
},
computed: {
countWorkflows() {
return this.workflows.length;
},
hasWorkflow() {
return this.countWorkflows > 0;
},
},
methods: {
openModal() {
this.modal.showModal = true;
},
goToGenerateWorkflow(data) {
console.log("go to generate workflow intercepted", data);
this.$emit("goToGenerateWorkflow", data);
},
},
i18n: {
messages: {
fr: {
workflow_list: "Liste des workflows associés",
workflow: " workflow associé",
workflows: " workflows associés",
},
},
},
};
</script>
<style scoped></style>

View File

@@ -1,37 +1,31 @@
<template>
<template v-if="workflowsAvailables.length >= 1">
<div class="dropdown d-grid gap-2">
<button
class="btn btn-primary dropdown-toggle"
type="button"
id="createWorkflowButton"
data-bs-toggle="dropdown"
aria-expanded="false"
>
Créer un workflow
</button>
<ul
class="dropdown-menu"
aria-labelledby="createWorkflowButton"
>
<li
v-for="w in workflowsAvailables"
:key="w.name"
>
<a
class="dropdown-item"
:href="makeLink(w.name)"
@click.prevent="goToGenerateWorkflow($event, w.name)"
>{{ w.text }}</a>
</li>
</ul>
</div>
</template>
<template v-if="workflowsAvailables.length >= 1">
<div class="dropdown d-grid gap-2">
<button
class="btn btn-primary dropdown-toggle"
type="button"
id="createWorkflowButton"
data-bs-toggle="dropdown"
aria-expanded="false"
>
Créer un workflow
</button>
<ul class="dropdown-menu" aria-labelledby="createWorkflowButton">
<li v-for="w in workflowsAvailables" :key="w.name">
<a
class="dropdown-item"
:href="makeLink(w.name)"
@click.prevent="goToGenerateWorkflow($event, w.name)"
>{{ w.text }}</a
>
</li>
</ul>
</div>
</template>
</template>
<script>
import {buildLinkCreate} from 'ChillMainAssets/lib/entity-workflow/api.js';
import { buildLinkCreate } from "ChillMainAssets/lib/entity-workflow/api.js";
export default {
name: "PickWorkflow",
@@ -55,28 +49,35 @@ export default {
},
goToGenerateWorkflowPayload: {
required: false,
default: {}
default: {},
},
},
emits: ['goToGenerateWorkflow'],
emits: ["goToGenerateWorkflow"],
methods: {
makeLink(workflowName) {
return buildLinkCreate(workflowName, this.relatedEntityClass, this.relatedEntityId);
return buildLinkCreate(
workflowName,
this.relatedEntityClass,
this.relatedEntityId,
);
},
goToGenerateWorkflow(event, workflowName) {
console.log('goToGenerateWorkflow', event, workflowName);
console.log("goToGenerateWorkflow", event, workflowName);
if (!this.$props.preventDefaultMoveToGenerate) {
console.log('to go generate');
console.log("to go generate");
window.location.assign(this.makeLink(workflowName));
}
this.$emit('goToGenerateWorkflow', {event, workflowName, link: this.makeLink(workflowName), payload: this.goToGenerateWorkflowPayload});
}
}
}
this.$emit("goToGenerateWorkflow", {
event,
workflowName,
link: this.makeLink(workflowName),
payload: this.goToGenerateWorkflowPayload,
});
},
},
};
</script>
<style scoped>
</style>
<style scoped></style>

View File

@@ -1,58 +1,46 @@
<template>
<transition name="modal">
<div class="modal-mask">
<!-- :: styles bootstrap :: -->
<div
class="modal fade show"
style="display: block"
aria-modal="true"
role="dialog"
>
<div
class="modal-dialog"
:class="modalDialogClass"
>
<div class="modal-content">
<div class="modal-header">
<slot name="header" />
<button
class="close btn"
@click="$emit('close')"
>
<i
class="fa fa-times"
aria-hidden="true"
/>
</button>
</div>
<div class="modal-body">
<div class="body-head">
<slot name="body-head" />
</div>
<slot name="body" />
</div>
<transition name="modal">
<div class="modal-mask">
<!-- :: styles bootstrap :: -->
<div
class="modal-footer"
v-if="!hideFooter"
class="modal fade show"
style="display: block"
aria-modal="true"
role="dialog"
>
<button
class="btn btn-cancel"
@click="$emit('close')"
>
{{ $t('action.close') }}
</button>
<slot name="footer" />
<div class="modal-dialog" :class="modalDialogClass">
<div class="modal-content">
<div class="modal-header">
<slot name="header" />
<button class="close btn" @click="$emit('close')">
<i class="fa fa-times" aria-hidden="true" />
</button>
</div>
<div class="modal-body">
<div class="body-head">
<slot name="body-head" />
</div>
<slot name="body" />
</div>
<div class="modal-footer" v-if="!hideFooter">
<button
class="btn btn-cancel"
@click="$emit('close')"
>
{{ $t("action.close") }}
</button>
<slot name="footer" />
</div>
</div>
</div>
</div>
</div>
<!-- :: end styles bootstrap :: -->
</div>
</div>
<!-- :: end styles bootstrap :: -->
</div>
</transition>
</transition>
</template>
<script lang="ts">
import {defineComponent} from "vue";
import { defineComponent } from "vue";
/*
* This Modal component is a mix between Vue3 modal implementation
* [+] with 'v-if:showModal' directive:parameter, html scope is added/removed not just shown/hidden
@@ -63,41 +51,41 @@ import {defineComponent} from "vue";
* [+] modal design can be configured using css classes (size, scroll)
*/
export default defineComponent({
name: 'Modal',
props: {
modalDialogClass: {
type: Object,
required: false,
default: {},
},
hideFooter: {
type: Boolean,
required: false,
default: false
}
},
emits: ['close']
name: "Modal",
props: {
modalDialogClass: {
type: Object,
required: false,
default: {},
},
hideFooter: {
type: Boolean,
required: false,
default: false,
},
},
emits: ["close"],
});
</script>
<style lang="scss">
/**
/**
* This is a mask behind the modal.
*/
.modal-mask {
position: fixed;
z-index: 9998;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.75);
transition: opacity 0.3s ease;
}
.modal-header .close {
border-top-right-radius: 0.3rem;
}
/*
.modal-mask {
position: fixed;
z-index: 9998;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.75);
transition: opacity 0.3s ease;
}
.modal-header .close {
border-top-right-radius: 0.3rem;
}
/*
* The following styles are auto-applied to elements with
* transition="modal" when their visibility is toggled
* by Vue.js.
@@ -105,25 +93,24 @@ export default defineComponent({
* You can easily play with the modal transition by editing
* these styles.
*/
.modal-enter {
opacity: 0;
}
.modal-leave-active {
opacity: 0;
}
.modal-enter
.modal-container,
.modal-leave-active .modal-container {
-webkit-transform: scale(1.1);
transform: scale(1.1);
}
h3.modal-title {
font-size: 1.5rem;
font-weight: bold;
}
div.modal-footer {
button:first-child {
margin-right: auto;
}
}
.modal-enter {
opacity: 0;
}
.modal-leave-active {
opacity: 0;
}
.modal-enter .modal-container,
.modal-leave-active .modal-container {
-webkit-transform: scale(1.1);
transform: scale(1.1);
}
h3.modal-title {
font-size: 1.5rem;
font-weight: bold;
}
div.modal-footer {
button:first-child {
margin-right: auto;
}
}
</style>

View File

@@ -1,17 +1,14 @@
<template>
<div>
<button v-if="idsMarkedAsRead.length === 0"
<button
v-if="idsMarkedAsRead.length === 0"
class="btn btn-primary"
type="button"
@click="markAllRead"
>
<i class="fa fa-sm fa-envelope-open-o"></i> Marquer tout comme lu
</button>
<button v-else
class="btn btn-primary"
type="button"
@click="undo"
>
<button v-else class="btn btn-primary" type="button" @click="undo">
<i class="fa fa-sm fa-envelope-open-o"></i> Annuler
</button>
</div>
@@ -22,29 +19,37 @@ import { makeFetch } from "../../../lib/api/apiMethods";
import { ref } from "vue";
const emit = defineEmits<{
(e: 'markAsRead', id: number): void,
(e: 'markAsUnRead', id: number): void,
(e: "markAsRead", id: number): void;
(e: "markAsUnRead", id: number): void;
}>();
const idsMarkedAsRead = ref([] as number[]);
async function markAllRead() {
const ids: number[] = await makeFetch("POST", `/api/1.0/main/notification/mark/allread`, null);
const ids: number[] = await makeFetch(
"POST",
`/api/1.0/main/notification/mark/allread`,
null,
);
for (let i of ids) {
idsMarkedAsRead.value.push(i);
emit('markAsRead', i);
emit("markAsRead", i);
}
}
async function undo() {
const touched: number[] = await makeFetch("POST", `/api/1.0/main/notification/mark/undoallread`, idsMarkedAsRead.value);
const touched: number[] = await makeFetch(
"POST",
`/api/1.0/main/notification/mark/undoallread`,
idsMarkedAsRead.value,
);
while (idsMarkedAsRead.value.length > 0) {
idsMarkedAsRead.value.pop();
}
for (let t of touched) {
emit('markAsUnRead', t);
emit("markAsUnRead", t);
}
};
}
</script>
<style lang="scss" scoped></style>

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