Merge branch 'master' into calendar/docgen-add-generation

This commit is contained in:
2022-10-20 13:34:21 +02:00
56 changed files with 1728 additions and 100 deletions

View File

@@ -0,0 +1,60 @@
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);
}
function loadDynamicPickers(element) {
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);
}
});
}
document.addEventListener('DOMContentLoaded', function(e) {
loadDynamicPickers(document)
})

View File

@@ -0,0 +1,29 @@
# Pickpostalcode
Allow to pick a postal code.
In use with module `mod_pick_postal_code`, associated with `PickPostalCodeType` in php.
## Usage
`<pick-postal-code @select-city="onCitySelected" @removeCity="onCityRemoved" :picked="city"></pick-postal-code>`
## Props
* `picked`: the city picked. A javascript object (a city). Null if empty.
* `country`: country to restraint search on picked. May be null.
## Emits
### `selectCity`
When a city is onCitySelected.
Argument: a js object, representing a city
### `removeCity`
When a city is removed.
Argument: a js object, representing a city

View File

@@ -0,0 +1,107 @@
<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"></vue-multiselect>
</div>
</template>
<script lang="js">
import VueMultiselect from "vue-multiselect";
import {reactive, defineProps, onMounted} from "vue";
import {fetchCities, 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
},
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})` : '';
},
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

@@ -0,0 +1,3 @@
.PickPostalCode {
}

View File

@@ -0,0 +1,43 @@
import {makeFetch, fetchResults} from 'ChillMainAssets/lib/api/apiMethods';
/**
* 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) => {
// 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);
}
return makeFetch('GET', `/api/1.0/main/postal-code.json?${params.toString()}`).then(r => Promise.resolve(r.results));
};
/**
* Endpoint chill_main_postalcodeapi_search
* method GET, get Cities Object
* @params {string} search a search string
* @params {object} country a country object
* @params {AbortController} an abort controller
* @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});
if (country !== null) {
Object.assign('country', country.id);
}
return makeFetch('GET', url + params, null, {signal: controller.signal})
.then(result => Promise.resolve(result.results));
};
export {
fetchCities,
searchCities,
};

View File

@@ -251,3 +251,9 @@
<input type="hidden" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value|escape('html_attr') }}" {% endif %} data-input-uniqid="{{ form.vars['uniqid'] }}"/>
<div data-module="pick-dynamic" data-types="{{ form.vars['types']|json_encode }}" data-multiple="{{ form.vars['multiple'] }}" data-uniqid="{{ form.vars['uniqid'] }}"></div>
{% endblock %}
{% block pick_postal_code_widget %}
{{ form_help(form)}}
<input type="hidden" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value }}" {% endif %} data-input-uniqid="{{ form.vars['uniqid'] }}"/>
<div data-module="pick-postal-code" data-uniqid="{{ form.vars['uniqid'] }}"></div>
{% endblock %}

View File

@@ -95,6 +95,15 @@
</ul>
{% endif %}
{% if entity_workflow.currentStep.destEmail|length > 0 %}
<p><b>{{ 'workflow.An access key was also sent to those addresses'|trans }}&nbsp;:</b></p>
<ul>
{% for e in entity_workflow.currentStep.destEmail -%}
<li><a href="mailto:{{ e|escape('html_attr') }}">{{ e }}</a></li>
{%- endfor %}
</ul>
{% endif %}
{% if entity_workflow.currentStep.destUserByAccessKey|length > 0 %}
<p><b>{{ 'workflow.Those users are also granted to apply a transition by using an access key'|trans }}&nbsp;:</b></p>
<ul>
@@ -103,6 +112,21 @@
{% endfor %}
</ul>
{% endif %}
{% if is_granted('CHILL_MAIN_WORKFLOW_LINK_SHOW', entity_workflow) %}
<p><b>{{ 'workflow.This link grant any user to apply a transition'|trans }}&nbsp;:</b></p>
{% set link = absolute_url(path('chill_main_workflow_grant_access_by_key', {'id': entity_workflow.currentStep.id, 'accessKey': entity_workflow.currentStep.accessKey})) %}
<div class="input-group mb-3">
<input type="text" readonly value="{{ link|e('html_attr') }}" class="form-control">
<button class="btn btn-secondary" type="button" id="button-copy" onclick="navigator.clipboard.writeText('{{ link|e('html_attr') }}').then(() => { window.alert({{ ('"' ~ 'workflow.Access link copied'|trans ~ ' !"') |e('html_attr') }})});"><i class="fa fa-files-o"></i></button>
<a class="btn btn-secondary" type="button" id="button-email"
href="mailto:?body={{ ((('workflow.The workflow may be accssed through this link'|trans)~':')|e('url')) ~ '%0D%0A%0D%0A' ~ link|e('url') }}"><i class="fa fa-envelope"></i></a>
</div>
{% endif %}
{% endif %}
</div>

View File

@@ -81,6 +81,15 @@
</ul>
{% endif %}
{% if entity_workflow.currentStep.destEmail|length > 0 %}
<p><b>{{ 'workflow.An access key was also sent to those addresses'|trans }}&nbsp;:</b></p>
<ul>
{% for e in entity_workflow.currentStep.destEmail -%}
<li><a href="mailto:{{ e|escape('html_attr') }}">{{ e }}</a></li>
{%- endfor %}
</ul>
{% endif %}
{% if step.destUserByAccessKey|length > 0 %}
<p><b>{{ 'workflow.Those users are also granted to apply a transition by using an access key'|trans }}&nbsp;:</b></p>
<ul>