mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
Merge branch 'implement_eslint' into 'master'
Implement ESLint See merge request Chill-Projet/chill-bundles!752
This commit is contained in:
commit
e8962782ed
1986
.eslint-baseline.json
Normal file
1986
.eslint-baseline.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -5,6 +5,7 @@ cache:
|
||||
paths:
|
||||
- /vendor/
|
||||
- .cache
|
||||
- node_modules/
|
||||
|
||||
# Bring in any services we need http://docs.gitlab.com/ee/ci/docker/using_docker_images.html#what-is-a-service
|
||||
# See http://docs.gitlab.com/ee/ci/services/README.html for examples.
|
||||
@ -103,6 +104,32 @@ rector_tests:
|
||||
paths:
|
||||
- vendor/
|
||||
|
||||
lint:
|
||||
stage: Tests
|
||||
image: node:20-alpine
|
||||
before_script:
|
||||
- apk add --no-cache python3 make g++ py3-setuptools
|
||||
- export PYTHON="$(which python3)"
|
||||
- export PATH="./node_modules/.bin:$PATH"
|
||||
script:
|
||||
- yarn install --ignore-optional
|
||||
- npx eslint-baseline "**/*.{js,vue}"
|
||||
cache:
|
||||
paths:
|
||||
- node_modules/
|
||||
|
||||
# psalm_tests:
|
||||
# stage: Tests
|
||||
# image: gitea.champs-libres.be/chill-project/chill-skeleton-basic/base-image:php82
|
||||
# script:
|
||||
# - bin/psalm
|
||||
# allow_failure: true
|
||||
# artifacts:
|
||||
# expire_in: 30 min
|
||||
# paths:
|
||||
# - bin
|
||||
# - tests/app/vendor/
|
||||
|
||||
unit_tests:
|
||||
stage: Tests
|
||||
image: gitea.champs-libres.be/chill-project/chill-skeleton-basic/base-image:php82
|
||||
|
53
docs/source/development/es-lint.rst
Normal file
53
docs/source/development/es-lint.rst
Normal file
@ -0,0 +1,53 @@
|
||||
ESLint
|
||||
======
|
||||
|
||||
To improve the quality of our JS and VueJS code, ESLint and eslint-plugin-vue are implemented within the chill-bundles project.
|
||||
|
||||
Commands
|
||||
--------
|
||||
|
||||
To run ESLint, you can simply use the ``eslint`` command within the chill-bundles directory.
|
||||
|
||||
Interesting options are:
|
||||
|
||||
- ``--quiet`` to only get errors and silence the warnings
|
||||
- ``--fix`` to have ESLint fix what it can, automatically. This will not fix everything.
|
||||
|
||||
Rules
|
||||
-----
|
||||
|
||||
We use Vue 3, so the rules can be configured as follows within the ``eslint.config.mjs`` file:
|
||||
|
||||
- ``.configs["flat/base"]`` ... Settings and rules to enable correct ESLint parsing.
|
||||
|
||||
Configurations for using Vue.js 3.x:
|
||||
|
||||
- ``.configs["flat/essential"]`` : Base rules plus rules to prevent errors or unintended behavior.
|
||||
- ``.configs["flat/strongly-recommended"]`` ... Above, plus rules to considerably improve code readability and/or dev experience.
|
||||
- ``.configs["flat/recommended"]`` ... Above, plus rules to enforce subjective community defaults to ensure consistency.
|
||||
|
||||
Detailed information about which rules each set includes can be found here:
|
||||
`https://eslint.vuejs.org/rules/ <https://eslint.vuejs.org/rules/>`_
|
||||
|
||||
Manual Rule Configuration
|
||||
-------------------------
|
||||
|
||||
We can also manually configure certain rules or override rules that are part of the ruleset specified above.
|
||||
|
||||
For example, if we want to turn off a certain rule, we can do so as follows:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
rules: {
|
||||
'vue/multi-word-component': 'off'
|
||||
}
|
||||
|
||||
We could also change the severity of a certain rule from 'error' to 'warning', for example.
|
||||
|
||||
Within specific ``.js`` or ``.vue`` files, we can also override a certain rule only for that specific file by adding a comment:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
/* eslint multi-word-component: "off", no-child-content: "error"
|
||||
--------
|
||||
Here's a description about why this configuration is necessary. */
|
@ -31,6 +31,7 @@ As Chill relies on the `symfony <http://symfony.com>`_ framework, reading the fr
|
||||
Exports <exports.rst>
|
||||
Embeddable comments <embeddable-comments.rst>
|
||||
Run tests <run-tests.rst>
|
||||
ESLint <es-lint.rst>
|
||||
Useful snippets <useful-snippets.rst>
|
||||
manual/index.rst
|
||||
Assets <assets.rst>
|
||||
|
@ -1,39 +1,37 @@
|
||||
import { ShowHide } from 'ShowHide/show_hide.js';
|
||||
|
||||
var
|
||||
div_accompagnement = document.getElementById("form_accompagnement"),
|
||||
div_accompagnement_comment = document.getElementById("form_accompagnement_comment"),
|
||||
div_caf_id = document.getElementById("cafId"),
|
||||
div_caf_inscription_date = document.getElementById("cafInscriptionDate"),
|
||||
;
|
||||
import { ShowHide } from "ShowHide/show_hide.js";
|
||||
|
||||
var div_accompagnement = document.getElementById("form_accompagnement"),
|
||||
div_accompagnement_comment = document.getElementById(
|
||||
"form_accompagnement_comment",
|
||||
),
|
||||
div_caf_id = document.getElementById("cafId"),
|
||||
div_caf_inscription_date = document.getElementById("cafInscriptionDate");
|
||||
// let show/hide the div_accompagnement_comment if the input with value `'autre'` is checked
|
||||
new ShowHide({
|
||||
"froms": [div_accompagnement],
|
||||
"test": function(froms, event) {
|
||||
for (let el of froms.values()) {
|
||||
for (let input of el.querySelectorAll('input').values()) {
|
||||
if (input.value === 'autre') {
|
||||
return input.checked;
|
||||
}
|
||||
}
|
||||
froms: [div_accompagnement],
|
||||
test: function (froms, event) {
|
||||
for (let el of froms.values()) {
|
||||
for (let input of el.querySelectorAll("input").values()) {
|
||||
if (input.value === "autre") {
|
||||
return input.checked;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
"container": [div_accompagnement_comment]
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
container: [div_accompagnement_comment],
|
||||
});
|
||||
|
||||
// let show the date input only if the the id is filled
|
||||
// let show the date input only if the the id is filled
|
||||
new ShowHide({
|
||||
froms: [ div_caf_id ],
|
||||
test: function(froms, event) {
|
||||
for (let el of froms.values()) {
|
||||
return el.querySelector("input").value !== "";
|
||||
}
|
||||
},
|
||||
container: [ div_caf_inscription_date ],
|
||||
// using this option, we use the event `input` instead of `change`
|
||||
event_name: 'input'
|
||||
froms: [div_caf_id],
|
||||
test: function (froms, event) {
|
||||
for (let el of froms.values()) {
|
||||
return el.querySelector("input").value !== "";
|
||||
}
|
||||
},
|
||||
container: [div_caf_inscription_date],
|
||||
// using this option, we use the event `input` instead of `change`
|
||||
event_name: "input",
|
||||
});
|
||||
|
||||
|
35
eslint.config.mjs
Normal file
35
eslint.config.mjs
Normal file
@ -0,0 +1,35 @@
|
||||
import eslintPluginVue from "eslint-plugin-vue";
|
||||
import ts from "typescript-eslint";
|
||||
import eslintPluginPrettier from "eslint-plugin-prettier";
|
||||
|
||||
export default ts.config(
|
||||
...ts.configs.recommended,
|
||||
...ts.configs.stylistic,
|
||||
...eslintPluginVue.configs["flat/essential"],
|
||||
{
|
||||
files: ["**/*.vue"],
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
parser: "@typescript-eslint/parser",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ignores: [
|
||||
"**/vendor/*",
|
||||
"**/import-png.d.ts",
|
||||
"**/chill.webpack.config.js",
|
||||
],
|
||||
},
|
||||
{
|
||||
plugins: {
|
||||
prettier: eslintPluginPrettier,
|
||||
},
|
||||
rules: {
|
||||
"prettier/prettier": "error",
|
||||
// override/add rules settings here, such as:
|
||||
"vue/multi-word-component-names": "off",
|
||||
"@typescript-eslint/no-require-imports": "off",
|
||||
},
|
||||
},
|
||||
);
|
13
package.json
13
package.json
@ -13,26 +13,36 @@
|
||||
"@ckeditor/ckeditor5-markdown-gfm": "^41.4.2",
|
||||
"@ckeditor/ckeditor5-theme-lark": "^41.4.2",
|
||||
"@ckeditor/ckeditor5-vue": "^5.1.0",
|
||||
"@eslint/js": "^9.14.0",
|
||||
"@luminateone/eslint-baseline": "^1.0.9",
|
||||
"@symfony/webpack-encore": "^4.1.0",
|
||||
"@tsconfig/node14": "^1.0.1",
|
||||
"@types/dompurify": "^3.0.5",
|
||||
"@types/eslint__js": "^8.42.3",
|
||||
"@typescript-eslint/parser": "^8.12.2",
|
||||
"bindings": "^1.5.0",
|
||||
"bootstrap": "5.2.3",
|
||||
"chokidar": "^3.5.1",
|
||||
"dompurify": "^3.1.0",
|
||||
"eslint": "^9.14.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-prettier": "^5.2.1",
|
||||
"eslint-plugin-vue": "^9.30.0",
|
||||
"fork-awesome": "^1.1.7",
|
||||
"jquery": "^3.6.0",
|
||||
"marked": "^12.0.1",
|
||||
"node-sass": "^8.0.0",
|
||||
"popper.js": "^1.16.1",
|
||||
"postcss-loader": "^7.0.2",
|
||||
"prettier": "^3.3.3",
|
||||
"raw-loader": "^4.0.2",
|
||||
"sass-loader": "^14.0.0",
|
||||
"select2": "^4.0.13",
|
||||
"select2-bootstrap-theme": "0.1.0-beta.10",
|
||||
"style-loader": "^3.3.1",
|
||||
"ts-loader": "^9.3.1",
|
||||
"typescript": "^5.4.5",
|
||||
"typescript": "^5.6.3",
|
||||
"typescript-eslint": "^8.13.0",
|
||||
"vue-loader": "^17.0.0",
|
||||
"webpack": "^5.75.0",
|
||||
"webpack-cli": "^5.0.1"
|
||||
@ -68,6 +78,7 @@
|
||||
"scripts": {
|
||||
"dev-server": "encore dev-server",
|
||||
"dev": "encore dev",
|
||||
"prettier": "prettier --write \"**/*.{js,ts,vue}\"",
|
||||
"watch": "encore dev --watch",
|
||||
"build": "encore production --progress"
|
||||
},
|
||||
|
@ -1 +1 @@
|
||||
require('./chillactivity.scss');
|
||||
require("./chillactivity.scss");
|
||||
|
@ -1,21 +1,21 @@
|
||||
<template>
|
||||
<concerned-groups v-if="hasPerson"></concerned-groups>
|
||||
<social-issues-acc v-if="hasSocialIssues"></social-issues-acc>
|
||||
<location v-if="hasLocation"></location>
|
||||
<concerned-groups v-if="hasPerson" />
|
||||
<social-issues-acc v-if="hasSocialIssues" />
|
||||
<location v-if="hasLocation" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ConcernedGroups from './components/ConcernedGroups.vue';
|
||||
import SocialIssuesAcc from './components/SocialIssuesAcc.vue';
|
||||
import Location from './components/Location.vue';
|
||||
import ConcernedGroups from "./components/ConcernedGroups.vue";
|
||||
import SocialIssuesAcc from "./components/SocialIssuesAcc.vue";
|
||||
import Location from "./components/Location.vue";
|
||||
|
||||
export default {
|
||||
name: "App",
|
||||
props: ['hasSocialIssues', 'hasLocation', 'hasPerson'],
|
||||
components: {
|
||||
ConcernedGroups,
|
||||
SocialIssuesAcc,
|
||||
Location
|
||||
}
|
||||
}
|
||||
name: "App",
|
||||
props: ["hasSocialIssues", "hasLocation", "hasPerson"],
|
||||
components: {
|
||||
ConcernedGroups,
|
||||
SocialIssuesAcc,
|
||||
Location,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -1,38 +1,39 @@
|
||||
import { getSocialIssues } from 'ChillPersonAssets/vuejs/AccompanyingCourse/api.js';
|
||||
import { fetchResults } from 'ChillMainAssets/lib/api/apiMethods';
|
||||
import { getSocialIssues } from "ChillPersonAssets/vuejs/AccompanyingCourse/api.js";
|
||||
import { fetchResults } from "ChillMainAssets/lib/api/apiMethods";
|
||||
|
||||
/*
|
||||
* Load socialActions by socialIssue (id)
|
||||
*/
|
||||
* Load socialActions by socialIssue (id)
|
||||
*/
|
||||
const getSocialActionByIssue = (id) => {
|
||||
const url = `/api/1.0/person/social/social-action/by-social-issue/${id}.json`;
|
||||
return fetch(url)
|
||||
.then(response => {
|
||||
if (response.ok) { return response.json(); }
|
||||
throw Error('Error with request resource response');
|
||||
});
|
||||
const url = `/api/1.0/person/social/social-action/by-social-issue/${id}.json`;
|
||||
return fetch(url).then((response) => {
|
||||
if (response.ok) {
|
||||
return response.json();
|
||||
}
|
||||
throw Error("Error with request resource response");
|
||||
});
|
||||
};
|
||||
|
||||
const getLocations = () => fetchResults('/api/1.0/main/location.json');
|
||||
const getLocations = () => fetchResults("/api/1.0/main/location.json");
|
||||
|
||||
const getLocationTypes = () => fetchResults('/api/1.0/main/location-type.json');
|
||||
|
||||
const getUserCurrentLocation =
|
||||
() => fetch('/api/1.0/main/user-current-location.json')
|
||||
.then(response => {
|
||||
if (response.ok) { return response.json(); }
|
||||
throw Error('Error with request resource response');
|
||||
});
|
||||
const getLocationTypes = () => fetchResults("/api/1.0/main/location-type.json");
|
||||
|
||||
const getUserCurrentLocation = () =>
|
||||
fetch("/api/1.0/main/user-current-location.json").then((response) => {
|
||||
if (response.ok) {
|
||||
return response.json();
|
||||
}
|
||||
throw Error("Error with request resource response");
|
||||
});
|
||||
|
||||
/*
|
||||
* Load Location Type by defaultFor
|
||||
* @param {string} entity - can be "person" or "thirdparty"
|
||||
* Load Location Type by defaultFor
|
||||
* @param {string} entity - can be "person" or "thirdparty"
|
||||
*/
|
||||
const getLocationTypeByDefaultFor = (entity) => {
|
||||
return getLocationTypes().then(results =>
|
||||
results.filter(t => t.defaultFor === entity)[0]
|
||||
);
|
||||
return getLocationTypes().then(
|
||||
(results) => results.filter((t) => t.defaultFor === entity)[0],
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -43,26 +44,27 @@ const getLocationTypeByDefaultFor = (entity) => {
|
||||
* @returns {Promise<T>}
|
||||
*/
|
||||
const postLocation = (body) => {
|
||||
const url = `/api/1.0/main/location.json`;
|
||||
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');
|
||||
});
|
||||
const url = `/api/1.0/main/location.json`;
|
||||
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");
|
||||
});
|
||||
};
|
||||
|
||||
export {
|
||||
getSocialIssues,
|
||||
getSocialActionByIssue,
|
||||
getLocations,
|
||||
getLocationTypes,
|
||||
getLocationTypeByDefaultFor,
|
||||
postLocation,
|
||||
getUserCurrentLocation
|
||||
getSocialIssues,
|
||||
getSocialActionByIssue,
|
||||
getLocations,
|
||||
getLocationTypes,
|
||||
getLocationTypeByDefaultFor,
|
||||
postLocation,
|
||||
getUserCurrentLocation,
|
||||
};
|
||||
|
@ -1,219 +1,242 @@
|
||||
<template>
|
||||
<teleport to="#add-persons" v-if="isComponentVisible">
|
||||
<teleport to="#add-persons" v-if="isComponentVisible">
|
||||
<div class="flex-bloc concerned-groups" :class="getContext">
|
||||
<persons-bloc
|
||||
v-for="bloc in contextPersonsBlocs"
|
||||
:key="bloc.key"
|
||||
:bloc="bloc"
|
||||
:bloc-width="getBlocWidth"
|
||||
:set-persons-in-bloc="setPersonsInBloc"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-if="
|
||||
getContext === 'accompanyingCourse' &&
|
||||
suggestedEntities.length > 0
|
||||
"
|
||||
>
|
||||
<ul class="list-suggest add-items inline">
|
||||
<li
|
||||
v-for="(p, i) in suggestedEntities"
|
||||
@click="addSuggestedEntity(p)"
|
||||
:key="`suggestedEntities-${i}`"
|
||||
>
|
||||
<person-text v-if="p.type === 'person'" :person="p" />
|
||||
<span v-else>{{ p.text }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="flex-bloc concerned-groups" :class="getContext">
|
||||
<persons-bloc
|
||||
v-for="bloc in contextPersonsBlocs"
|
||||
v-bind:key="bloc.key"
|
||||
v-bind:bloc="bloc"
|
||||
v-bind:blocWidth="getBlocWidth"
|
||||
v-bind:setPersonsInBloc="setPersonsInBloc">
|
||||
</persons-bloc>
|
||||
</div>
|
||||
<div v-if="getContext === 'accompanyingCourse' && suggestedEntities.length > 0">
|
||||
<ul class="list-suggest add-items inline">
|
||||
<li v-for="(p, i) in suggestedEntities" @click="addSuggestedEntity(p)" :key="`suggestedEntities-${i}`">
|
||||
<person-text v-if="p.type === 'person'" :person="p"></person-text>
|
||||
<span v-else>{{ p.text }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<ul class="record_actions">
|
||||
<li class="add-persons">
|
||||
<add-persons
|
||||
buttonTitle="activity.add_persons"
|
||||
modalTitle="activity.add_persons"
|
||||
v-bind:key="addPersons.key"
|
||||
v-bind:options="addPersonsOptions"
|
||||
@addNewPersons="addNewPersons"
|
||||
ref="addPersons">
|
||||
</add-persons>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</teleport>
|
||||
<ul class="record_actions">
|
||||
<li class="add-persons">
|
||||
<add-persons
|
||||
button-title="activity.add_persons"
|
||||
modal-title="activity.add_persons"
|
||||
:key="addPersons.key"
|
||||
:options="addPersonsOptions"
|
||||
@add-new-persons="addNewPersons"
|
||||
ref="addPersons"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</teleport>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapGetters } from 'vuex';
|
||||
import AddPersons from 'ChillPersonAssets/vuejs/_components/AddPersons.vue';
|
||||
import PersonsBloc from './ConcernedGroups/PersonsBloc.vue';
|
||||
import PersonText from 'ChillPersonAssets/vuejs/_components/Entity/PersonText.vue';
|
||||
import { mapState, mapGetters } from "vuex";
|
||||
import AddPersons from "ChillPersonAssets/vuejs/_components/AddPersons.vue";
|
||||
import PersonsBloc from "./ConcernedGroups/PersonsBloc.vue";
|
||||
import PersonText from "ChillPersonAssets/vuejs/_components/Entity/PersonText.vue";
|
||||
|
||||
export default {
|
||||
name: "ConcernedGroups",
|
||||
components: {
|
||||
AddPersons,
|
||||
PersonsBloc,
|
||||
PersonText
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
personsBlocs: [
|
||||
{ key: 'persons',
|
||||
title: 'activity.bloc_persons',
|
||||
persons: [],
|
||||
included: false
|
||||
name: "ConcernedGroups",
|
||||
components: {
|
||||
AddPersons,
|
||||
PersonsBloc,
|
||||
PersonText,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
personsBlocs: [
|
||||
{
|
||||
key: "persons",
|
||||
title: "activity.bloc_persons",
|
||||
persons: [],
|
||||
included: false,
|
||||
},
|
||||
{
|
||||
key: "personsAssociated",
|
||||
title: "activity.bloc_persons_associated",
|
||||
persons: [],
|
||||
included: window.activity
|
||||
? window.activity.activityType.personsVisible !== 0
|
||||
: true,
|
||||
},
|
||||
{
|
||||
key: "personsNotAssociated",
|
||||
title: "activity.bloc_persons_not_associated",
|
||||
persons: [],
|
||||
included: window.activity
|
||||
? window.activity.activityType.personsVisible !== 0
|
||||
: true,
|
||||
},
|
||||
{
|
||||
key: "thirdparty",
|
||||
title: "activity.bloc_thirdparty",
|
||||
persons: [],
|
||||
included: window.activity
|
||||
? window.activity.activityType.thirdPartiesVisible !== 0
|
||||
: true,
|
||||
},
|
||||
{
|
||||
key: "users",
|
||||
title: "activity.bloc_users",
|
||||
persons: [],
|
||||
included: window.activity
|
||||
? window.activity.activityType.usersVisible !== 0
|
||||
: true,
|
||||
},
|
||||
],
|
||||
addPersons: {
|
||||
key: "activity",
|
||||
},
|
||||
{ key: 'personsAssociated',
|
||||
title: 'activity.bloc_persons_associated',
|
||||
persons: [],
|
||||
included: window.activity ? window.activity.activityType.personsVisible !== 0 : true
|
||||
},
|
||||
{ key: 'personsNotAssociated',
|
||||
title: 'activity.bloc_persons_not_associated',
|
||||
persons: [],
|
||||
included: window.activity ? window.activity.activityType.personsVisible !== 0 : true
|
||||
},
|
||||
{ key: 'thirdparty',
|
||||
title: 'activity.bloc_thirdparty',
|
||||
persons: [],
|
||||
included: window.activity ? window.activity.activityType.thirdPartiesVisible !== 0 : true
|
||||
},
|
||||
{ key: 'users',
|
||||
title: 'activity.bloc_users',
|
||||
persons: [],
|
||||
included: window.activity ? window.activity.activityType.usersVisible !== 0 : true
|
||||
},
|
||||
],
|
||||
addPersons: {
|
||||
key: 'activity'
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isComponentVisible() {
|
||||
return window.activity
|
||||
? (window.activity.activityType.personsVisible !== 0 || window.activity.activityType.thirdPartiesVisible !== 0 || window.activity.activityType.usersVisible !== 0)
|
||||
: true
|
||||
},
|
||||
...mapState({
|
||||
persons: state => state.activity.persons,
|
||||
thirdParties: state => state.activity.thirdParties,
|
||||
users: state => state.activity.users,
|
||||
accompanyingCourse: state => state.activity.accompanyingPeriod
|
||||
}),
|
||||
...mapGetters([
|
||||
'suggestedEntities'
|
||||
]),
|
||||
getContext() {
|
||||
return (this.accompanyingCourse) ? "accompanyingCourse" : "person";
|
||||
},
|
||||
contextPersonsBlocs() {
|
||||
return this.personsBlocs.filter(bloc => bloc.included !== false);
|
||||
},
|
||||
addPersonsOptions() {
|
||||
let optionsType = [];
|
||||
if (window.activity) {
|
||||
if (window.activity.activityType.personsVisible !== 0) {
|
||||
optionsType.push('person')
|
||||
}
|
||||
if (window.activity.activityType.thirdPartiesVisible !== 0) {
|
||||
optionsType.push('thirdparty')
|
||||
}
|
||||
if (window.activity.activityType.usersVisible !== 0) {
|
||||
optionsType.push('user')
|
||||
}
|
||||
} else {
|
||||
optionsType = ['person', 'thirdparty', 'user'];
|
||||
}
|
||||
return {
|
||||
type: optionsType,
|
||||
priority: null,
|
||||
uniq: false,
|
||||
button: {
|
||||
size: 'btn-sm'
|
||||
}
|
||||
}
|
||||
},
|
||||
getBlocWidth() {
|
||||
return Math.round(100/(this.contextPersonsBlocs.length)) + '%';
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.setPersonsInBloc();
|
||||
},
|
||||
methods: {
|
||||
setPersonsInBloc() {
|
||||
let groups;
|
||||
if (this.accompanyingCourse) {
|
||||
groups = this.splitPersonsInGroups();
|
||||
}
|
||||
this.personsBlocs.forEach(bloc => {
|
||||
if (this.accompanyingCourse) {
|
||||
switch (bloc.key) {
|
||||
case 'personsAssociated':
|
||||
bloc.persons = groups.personsAssociated;
|
||||
bloc.included = true;
|
||||
break;
|
||||
case 'personsNotAssociated':
|
||||
bloc.persons = groups.personsNotAssociated;
|
||||
bloc.included = true;
|
||||
break;
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isComponentVisible() {
|
||||
return window.activity
|
||||
? window.activity.activityType.personsVisible !== 0 ||
|
||||
window.activity.activityType.thirdPartiesVisible !== 0 ||
|
||||
window.activity.activityType.usersVisible !== 0
|
||||
: true;
|
||||
},
|
||||
...mapState({
|
||||
persons: (state) => state.activity.persons,
|
||||
thirdParties: (state) => state.activity.thirdParties,
|
||||
users: (state) => state.activity.users,
|
||||
accompanyingCourse: (state) => state.activity.accompanyingPeriod,
|
||||
}),
|
||||
...mapGetters(["suggestedEntities"]),
|
||||
getContext() {
|
||||
return this.accompanyingCourse ? "accompanyingCourse" : "person";
|
||||
},
|
||||
contextPersonsBlocs() {
|
||||
return this.personsBlocs.filter((bloc) => bloc.included !== false);
|
||||
},
|
||||
addPersonsOptions() {
|
||||
let optionsType = [];
|
||||
if (window.activity) {
|
||||
if (window.activity.activityType.personsVisible !== 0) {
|
||||
optionsType.push("person");
|
||||
}
|
||||
if (window.activity.activityType.thirdPartiesVisible !== 0) {
|
||||
optionsType.push("thirdparty");
|
||||
}
|
||||
if (window.activity.activityType.usersVisible !== 0) {
|
||||
optionsType.push("user");
|
||||
}
|
||||
} else {
|
||||
switch (bloc.key) {
|
||||
case 'persons':
|
||||
bloc.persons = this.persons;
|
||||
bloc.included = true;
|
||||
break;
|
||||
}
|
||||
optionsType = ["person", "thirdparty", "user"];
|
||||
}
|
||||
switch (bloc.key) {
|
||||
case 'thirdparty':
|
||||
bloc.persons = this.thirdParties;
|
||||
break;
|
||||
case 'users':
|
||||
bloc.persons = this.users;
|
||||
break;
|
||||
}
|
||||
}, groups);
|
||||
},
|
||||
splitPersonsInGroups() {
|
||||
let personsAssociated = [];
|
||||
let personsNotAssociated = this.persons;
|
||||
let participations = this.getCourseParticipations();
|
||||
this.persons.forEach(person => {
|
||||
participations.forEach(participation => {
|
||||
if (person.id === participation.id) {
|
||||
//console.log(person.id);
|
||||
personsAssociated.push(person);
|
||||
personsNotAssociated = personsNotAssociated.filter(p => p !== person);
|
||||
}
|
||||
});
|
||||
});
|
||||
return {
|
||||
'personsAssociated': personsAssociated,
|
||||
'personsNotAssociated': personsNotAssociated
|
||||
};
|
||||
},
|
||||
getCourseParticipations() {
|
||||
let participations = [];
|
||||
this.accompanyingCourse.participations.forEach(participation => {
|
||||
if (!participation.endDate) {
|
||||
participations.push(participation.person);
|
||||
}
|
||||
});
|
||||
return participations;
|
||||
},
|
||||
addNewPersons({ selected, modal }) {
|
||||
console.log('@@@ CLICK button addNewPersons', selected);
|
||||
selected.forEach((item) => {
|
||||
this.$store.dispatch('addPersonsInvolved', item);
|
||||
}, this
|
||||
);
|
||||
this.$refs.addPersons.resetSearch(); // to cast child method
|
||||
modal.showModal = false;
|
||||
this.setPersonsInBloc();
|
||||
},
|
||||
addSuggestedEntity(person) {
|
||||
this.$store.dispatch('addPersonsInvolved', { result: person, type: 'person' });
|
||||
return {
|
||||
type: optionsType,
|
||||
priority: null,
|
||||
uniq: false,
|
||||
button: {
|
||||
size: "btn-sm",
|
||||
},
|
||||
};
|
||||
},
|
||||
getBlocWidth() {
|
||||
return Math.round(100 / this.contextPersonsBlocs.length) + "%";
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.setPersonsInBloc();
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setPersonsInBloc() {
|
||||
let groups;
|
||||
if (this.accompanyingCourse) {
|
||||
groups = this.splitPersonsInGroups();
|
||||
}
|
||||
this.personsBlocs.forEach((bloc) => {
|
||||
if (this.accompanyingCourse) {
|
||||
switch (bloc.key) {
|
||||
case "personsAssociated":
|
||||
bloc.persons = groups.personsAssociated;
|
||||
bloc.included = true;
|
||||
break;
|
||||
case "personsNotAssociated":
|
||||
bloc.persons = groups.personsNotAssociated;
|
||||
bloc.included = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (bloc.key) {
|
||||
case "persons":
|
||||
bloc.persons = this.persons;
|
||||
bloc.included = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (bloc.key) {
|
||||
case "thirdparty":
|
||||
bloc.persons = this.thirdParties;
|
||||
break;
|
||||
case "users":
|
||||
bloc.persons = this.users;
|
||||
break;
|
||||
}
|
||||
}, groups);
|
||||
},
|
||||
splitPersonsInGroups() {
|
||||
let personsAssociated = [];
|
||||
let personsNotAssociated = this.persons;
|
||||
let participations = this.getCourseParticipations();
|
||||
this.persons.forEach((person) => {
|
||||
participations.forEach((participation) => {
|
||||
if (person.id === participation.id) {
|
||||
//console.log(person.id);
|
||||
personsAssociated.push(person);
|
||||
personsNotAssociated = personsNotAssociated.filter(
|
||||
(p) => p !== person,
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
return {
|
||||
personsAssociated: personsAssociated,
|
||||
personsNotAssociated: personsNotAssociated,
|
||||
};
|
||||
},
|
||||
getCourseParticipations() {
|
||||
let participations = [];
|
||||
this.accompanyingCourse.participations.forEach((participation) => {
|
||||
if (!participation.endDate) {
|
||||
participations.push(participation.person);
|
||||
}
|
||||
});
|
||||
return participations;
|
||||
},
|
||||
addNewPersons({ selected, modal }) {
|
||||
console.log("@@@ CLICK button addNewPersons", selected);
|
||||
selected.forEach((item) => {
|
||||
this.$store.dispatch("addPersonsInvolved", item);
|
||||
}, this);
|
||||
this.$refs.addPersons.resetSearch(); // to cast child method
|
||||
modal.showModal = false;
|
||||
this.setPersonsInBloc();
|
||||
},
|
||||
addSuggestedEntity(person) {
|
||||
this.$store.dispatch("addPersonsInvolved", {
|
||||
result: person,
|
||||
type: "person",
|
||||
});
|
||||
this.setPersonsInBloc();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
<style lang="scss" scoped></style>
|
||||
|
@ -1,32 +1,30 @@
|
||||
<template>
|
||||
<li>
|
||||
<span :title="person.text" @click.prevent="$emit('remove', person)">
|
||||
<span class="chill_denomination">
|
||||
<person-text :person="person" :isCut="true"></person-text>
|
||||
</span>
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span :title="person.text" @click.prevent="$emit('remove', person)">
|
||||
<span class="chill_denomination">
|
||||
<person-text :person="person" :is-cut="true" />
|
||||
</span>
|
||||
</span>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PersonText from 'ChillPersonAssets/vuejs/_components/Entity/PersonText.vue';
|
||||
|
||||
import PersonText from "ChillPersonAssets/vuejs/_components/Entity/PersonText.vue";
|
||||
|
||||
export default {
|
||||
name: "PersonBadge",
|
||||
props: ['person'],
|
||||
components: {
|
||||
PersonText
|
||||
},
|
||||
// computed: {
|
||||
// textCutted() {
|
||||
// let more = (this.person.text.length > 15) ?'…' : '';
|
||||
// return this.person.text.slice(0,15) + more;
|
||||
// }
|
||||
// },
|
||||
emits: ['remove'],
|
||||
}
|
||||
name: "PersonBadge",
|
||||
props: ["person"],
|
||||
components: {
|
||||
PersonText,
|
||||
},
|
||||
// computed: {
|
||||
// textCutted() {
|
||||
// let more = (this.person.text.length > 15) ?'…' : '';
|
||||
// return this.person.text.slice(0,15) + more;
|
||||
// }
|
||||
// },
|
||||
emits: ["remove"],
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
</style>
|
||||
<style lang="css" scoped></style>
|
||||
|
@ -1,41 +1,39 @@
|
||||
<template>
|
||||
<div class="item-bloc" :style="{ 'flex-basis': blocWidth }">
|
||||
<div class="item-row">
|
||||
<div class="item-col">
|
||||
<h4>{{ $t(bloc.title) }}</h4>
|
||||
</div>
|
||||
<div class="item-col">
|
||||
<ul class="list-suggest remove-items">
|
||||
<person-badge
|
||||
v-for="person in bloc.persons"
|
||||
v-bind:key="person.id"
|
||||
v-bind:person="person"
|
||||
@remove="removePerson">
|
||||
</person-badge>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-bloc" :style="{ 'flex-basis': blocWidth }">
|
||||
<div class="item-row">
|
||||
<div class="item-col">
|
||||
<h4>{{ $t(bloc.title) }}</h4>
|
||||
</div>
|
||||
<div class="item-col">
|
||||
<ul class="list-suggest remove-items">
|
||||
<person-badge
|
||||
v-for="person in bloc.persons"
|
||||
:key="person.id"
|
||||
:person="person"
|
||||
@remove="removePerson"
|
||||
/>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PersonBadge from './PersonBadge.vue';
|
||||
import PersonBadge from "./PersonBadge.vue";
|
||||
export default {
|
||||
name:"PersonsBloc",
|
||||
components: {
|
||||
PersonBadge
|
||||
},
|
||||
props: ['bloc', 'setPersonsInBloc', 'blocWidth'],
|
||||
methods: {
|
||||
removePerson(item) {
|
||||
console.log('@@ CLICK remove person: item', item);
|
||||
this.$store.dispatch('removePersonInvolved', item);
|
||||
this.setPersonsInBloc();
|
||||
}
|
||||
}
|
||||
}
|
||||
name: "PersonsBloc",
|
||||
components: {
|
||||
PersonBadge,
|
||||
},
|
||||
props: ["bloc", "setPersonsInBloc", "blocWidth"],
|
||||
methods: {
|
||||
removePerson(item) {
|
||||
console.log("@@ CLICK remove person: item", item);
|
||||
this.$store.dispatch("removePersonInvolved", item);
|
||||
this.setPersonsInBloc();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
</style>
|
||||
<style lang="scss"></style>
|
||||
|
@ -22,9 +22,8 @@
|
||||
group-values="locations"
|
||||
group-label="locationGroup"
|
||||
v-model="location"
|
||||
>
|
||||
</VueMultiselect>
|
||||
<new-location v-bind:availableLocations="availableLocations"></new-location>
|
||||
/>
|
||||
<new-location :available-locations="availableLocations" />
|
||||
</div>
|
||||
</div>
|
||||
</teleport>
|
||||
@ -43,9 +42,8 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
locationClassList:
|
||||
`col-form-label col-sm-4 ${document.querySelector('input#chill_activitybundle_activity_location').getAttribute('required') ? 'required' : ''}`,
|
||||
}
|
||||
locationClassList: `col-form-label col-sm-4 ${document.querySelector("input#chill_activitybundle_activity_location").getAttribute("required") ? "required" : ""}`,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(["activity", "availableLocations"]),
|
||||
@ -61,16 +59,16 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
labelAccompanyingCourseLocation(value) {
|
||||
return `${value.address.text} (${value.locationType.title.fr})`
|
||||
return `${value.address.text} (${value.locationType.title.fr})`;
|
||||
},
|
||||
customLabel(value) {
|
||||
return value.locationType
|
||||
? value.name
|
||||
? value.name === '__AccompanyingCourseLocation__'
|
||||
? value.name === "__AccompanyingCourseLocation__"
|
||||
? this.labelAccompanyingCourseLocation(value)
|
||||
: `${value.name} (${value.locationType.title.fr})`
|
||||
: value.locationType.title.fr
|
||||
: '';
|
||||
: "";
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -3,84 +3,129 @@
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<a class="btn btn-sm btn-create" @click="openModal">
|
||||
{{ $t('activity.create_new_location') }}
|
||||
{{ $t("activity.create_new_location") }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<teleport to="body">
|
||||
<modal v-if="modal.showModal"
|
||||
:modalDialogClass="modal.modalDialogClass"
|
||||
@close="modal.showModal = false">
|
||||
|
||||
<template v-slot:header>
|
||||
<h3 class="modal-title">{{ $t('activity.create_new_location') }}</h3>
|
||||
<modal
|
||||
v-if="modal.showModal"
|
||||
:modal-dialog-class="modal.modalDialogClass"
|
||||
@close="modal.showModal = false"
|
||||
>
|
||||
<template #header>
|
||||
<h3 class="modal-title">
|
||||
{{ $t("activity.create_new_location") }}
|
||||
</h3>
|
||||
</template>
|
||||
<template v-slot:body>
|
||||
<template #body>
|
||||
<form>
|
||||
<div class="alert alert-warning" v-if="errors.length">
|
||||
<ul>
|
||||
<li v-for="(e, i) in errors" :key="i">{{ e }}</li>
|
||||
<li v-for="(e, i) in errors" :key="i">
|
||||
{{ e }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="form-floating mb-3">
|
||||
<select class="form-select form-select-lg" id="type" required v-model="selectType">
|
||||
<option selected disabled value="">{{ $t('activity.choose_location_type') }}</option>
|
||||
<option v-for="t in locationTypes" :value="t" :key="t.id">
|
||||
<select
|
||||
class="form-select form-select-lg"
|
||||
id="type"
|
||||
required
|
||||
v-model="selectType"
|
||||
>
|
||||
<option selected disabled value="">
|
||||
{{ $t("activity.choose_location_type") }}
|
||||
</option>
|
||||
<option
|
||||
v-for="t in locationTypes"
|
||||
:value="t"
|
||||
:key="t.id"
|
||||
>
|
||||
{{ t.title.fr }}
|
||||
</option>
|
||||
</select>
|
||||
<label>{{ $t('activity.location_fields.type') }}</label>
|
||||
<label>{{
|
||||
$t("activity.location_fields.type")
|
||||
}}</label>
|
||||
</div>
|
||||
|
||||
<div class="form-floating mb-3">
|
||||
<input class="form-control form-control-lg" id="name" v-model="inputName" placeholder />
|
||||
<label for="name">{{ $t('activity.location_fields.name') }}</label>
|
||||
<input
|
||||
class="form-control form-control-lg"
|
||||
id="name"
|
||||
v-model="inputName"
|
||||
placeholder
|
||||
/>
|
||||
<label for="name">{{
|
||||
$t("activity.location_fields.name")
|
||||
}}</label>
|
||||
</div>
|
||||
|
||||
<add-address
|
||||
:context="addAddress.context"
|
||||
:options="addAddress.options"
|
||||
:addressChangedCallback="submitNewAddress"
|
||||
:address-changed-callback="submitNewAddress"
|
||||
v-if="showAddAddress"
|
||||
ref="addAddress">
|
||||
</add-address>
|
||||
ref="addAddress"
|
||||
/>
|
||||
|
||||
<div class="form-floating mb-3" v-if="showContactData">
|
||||
<input class="form-control form-control-lg" id="phonenumber1" v-model="inputPhonenumber1" placeholder />
|
||||
<label for="phonenumber1">{{ $t('activity.location_fields.phonenumber1') }}</label>
|
||||
<input
|
||||
class="form-control form-control-lg"
|
||||
id="phonenumber1"
|
||||
v-model="inputPhonenumber1"
|
||||
placeholder
|
||||
/>
|
||||
<label for="phonenumber1">{{
|
||||
$t("activity.location_fields.phonenumber1")
|
||||
}}</label>
|
||||
</div>
|
||||
<div class="form-floating mb-3" v-if="hasPhonenumber1">
|
||||
<input class="form-control form-control-lg" id="phonenumber2" v-model="inputPhonenumber2" placeholder />
|
||||
<label for="phonenumber2">{{ $t('activity.location_fields.phonenumber2') }}</label>
|
||||
<input
|
||||
class="form-control form-control-lg"
|
||||
id="phonenumber2"
|
||||
v-model="inputPhonenumber2"
|
||||
placeholder
|
||||
/>
|
||||
<label for="phonenumber2">{{
|
||||
$t("activity.location_fields.phonenumber2")
|
||||
}}</label>
|
||||
</div>
|
||||
<div class="form-floating mb-3" v-if="showContactData">
|
||||
<input class="form-control form-control-lg" id="email" v-model="inputEmail" placeholder />
|
||||
<label for="email">{{ $t('activity.location_fields.email') }}</label>
|
||||
<input
|
||||
class="form-control form-control-lg"
|
||||
id="email"
|
||||
v-model="inputEmail"
|
||||
placeholder
|
||||
/>
|
||||
<label for="email">{{
|
||||
$t("activity.location_fields.email")
|
||||
}}</label>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</template>
|
||||
<template v-slot:footer>
|
||||
<button class="btn btn-save"
|
||||
<template #footer>
|
||||
<button
|
||||
class="btn btn-save"
|
||||
@click.prevent="saveNewLocation"
|
||||
>
|
||||
{{ $t('action.save') }}
|
||||
{{ $t("action.save") }}
|
||||
</button>
|
||||
</template>
|
||||
|
||||
</modal>
|
||||
</teleport>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Modal from 'ChillMainAssets/vuejs/_components/Modal.vue';
|
||||
import Modal from "ChillMainAssets/vuejs/_components/Modal.vue";
|
||||
import AddAddress from "ChillMainAssets/vuejs/Address/components/AddAddress.vue";
|
||||
import { mapState } from "vuex";
|
||||
import { getLocationTypes } from "../../api";
|
||||
import { makeFetch } from 'ChillMainAssets/lib/api/apiMethods';
|
||||
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
|
||||
|
||||
export default {
|
||||
name: "NewLocation",
|
||||
@ -88,7 +133,7 @@ export default {
|
||||
Modal,
|
||||
AddAddress,
|
||||
},
|
||||
props: ['availableLocations'],
|
||||
props: ["availableLocations"],
|
||||
data() {
|
||||
return {
|
||||
errors: [],
|
||||
@ -103,35 +148,42 @@ export default {
|
||||
locationTypes: [],
|
||||
modal: {
|
||||
showModal: false,
|
||||
modalDialogClass: "modal-dialog-scrollable modal-xl"
|
||||
modalDialogClass: "modal-dialog-scrollable modal-xl",
|
||||
},
|
||||
addAddress: {
|
||||
options: {
|
||||
button: {
|
||||
text: { create: 'activity.create_address', edit: 'activity.edit_address' },
|
||||
size: 'btn-sm'
|
||||
text: {
|
||||
create: "activity.create_address",
|
||||
edit: "activity.edit_address",
|
||||
},
|
||||
size: "btn-sm",
|
||||
},
|
||||
title: {
|
||||
create: "activity.create_address",
|
||||
edit: "activity.edit_address",
|
||||
},
|
||||
title: { create: 'activity.create_address', edit: 'activity.edit_address' },
|
||||
},
|
||||
context: {
|
||||
target: { //name, id
|
||||
},
|
||||
target: {
|
||||
//name, id
|
||||
},
|
||||
edit: false,
|
||||
addressId: null,
|
||||
defaults: window.addaddress
|
||||
}
|
||||
}
|
||||
}
|
||||
defaults: window.addaddress,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(['activity']),
|
||||
...mapState(["activity"]),
|
||||
selectType: {
|
||||
get() {
|
||||
return this.selected.type;
|
||||
},
|
||||
set(value) {
|
||||
this.selected.type = value;
|
||||
}
|
||||
},
|
||||
},
|
||||
inputName: {
|
||||
get() {
|
||||
@ -139,7 +191,7 @@ export default {
|
||||
},
|
||||
set(value) {
|
||||
this.selected.name = value;
|
||||
}
|
||||
},
|
||||
},
|
||||
inputEmail: {
|
||||
get() {
|
||||
@ -147,7 +199,7 @@ export default {
|
||||
},
|
||||
set(value) {
|
||||
this.selected.email = value;
|
||||
}
|
||||
},
|
||||
},
|
||||
inputPhonenumber1: {
|
||||
get() {
|
||||
@ -155,7 +207,7 @@ export default {
|
||||
},
|
||||
set(value) {
|
||||
this.selected.phonenumber1 = value;
|
||||
}
|
||||
},
|
||||
},
|
||||
inputPhonenumber2: {
|
||||
get() {
|
||||
@ -163,15 +215,18 @@ export default {
|
||||
},
|
||||
set(value) {
|
||||
this.selected.phonenumber2 = value;
|
||||
}
|
||||
},
|
||||
},
|
||||
hasPhonenumber1() {
|
||||
return this.selected.phonenumber1 !== null && this.selected.phonenumber1 !== "";
|
||||
return (
|
||||
this.selected.phonenumber1 !== null &&
|
||||
this.selected.phonenumber1 !== ""
|
||||
);
|
||||
},
|
||||
showAddAddress() {
|
||||
let cond = false;
|
||||
if (this.selected.type) {
|
||||
if (this.selected.type.addressRequired !== 'never') {
|
||||
if (this.selected.type.addressRequired !== "never") {
|
||||
cond = true;
|
||||
}
|
||||
}
|
||||
@ -180,7 +235,7 @@ export default {
|
||||
showContactData() {
|
||||
let cond = false;
|
||||
if (this.selected.type) {
|
||||
if (this.selected.type.contactData !== 'never') {
|
||||
if (this.selected.type.contactData !== "never") {
|
||||
cond = true;
|
||||
}
|
||||
}
|
||||
@ -195,28 +250,39 @@ export default {
|
||||
let cond = true;
|
||||
this.errors = [];
|
||||
if (!this.selected.type) {
|
||||
this.errors.push('Type de localisation requis');
|
||||
this.errors.push("Type de localisation requis");
|
||||
cond = false;
|
||||
} else {
|
||||
if (this.selected.type.addressRequired === 'required' && !this.selected.addressId) {
|
||||
this.errors.push('Adresse requise');
|
||||
if (
|
||||
this.selected.type.addressRequired === "required" &&
|
||||
!this.selected.addressId
|
||||
) {
|
||||
this.errors.push("Adresse requise");
|
||||
cond = false;
|
||||
}
|
||||
if (this.selected.type.contactData === 'required' && !this.selected.phonenumber1) {
|
||||
this.errors.push('Numéro de téléphone requis');
|
||||
if (
|
||||
this.selected.type.contactData === "required" &&
|
||||
!this.selected.phonenumber1
|
||||
) {
|
||||
this.errors.push("Numéro de téléphone requis");
|
||||
cond = false;
|
||||
}
|
||||
if (this.selected.type.contactData === 'required' && !this.selected.email) {
|
||||
this.errors.push('Adresse email requise');
|
||||
if (
|
||||
this.selected.type.contactData === "required" &&
|
||||
!this.selected.email
|
||||
) {
|
||||
this.errors.push("Adresse email requise");
|
||||
cond = false;
|
||||
}
|
||||
}
|
||||
return cond;
|
||||
},
|
||||
getLocationTypesList() {
|
||||
getLocationTypes().then(results => {
|
||||
this.locationTypes = results.filter(t => t.availableForUsers === true);
|
||||
})
|
||||
getLocationTypes().then((results) => {
|
||||
this.locationTypes = results.filter(
|
||||
(t) => t.availableForUsers === true,
|
||||
);
|
||||
});
|
||||
},
|
||||
openModal() {
|
||||
this.modal.showModal = true;
|
||||
@ -224,11 +290,11 @@ export default {
|
||||
saveNewLocation() {
|
||||
if (this.checkForm()) {
|
||||
let body = {
|
||||
type: 'location',
|
||||
type: "location",
|
||||
name: this.selected.name,
|
||||
locationType: {
|
||||
id: this.selected.type.id,
|
||||
type: 'location-type'
|
||||
type: "location-type",
|
||||
},
|
||||
phonenumber1: this.selected.phonenumber1,
|
||||
phonenumber2: this.selected.phonenumber2,
|
||||
@ -237,36 +303,36 @@ export default {
|
||||
if (this.selected.addressId) {
|
||||
body = Object.assign(body, {
|
||||
address: {
|
||||
id: this.selected.addressId
|
||||
}
|
||||
id: this.selected.addressId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
makeFetch('POST', '/api/1.0/main/location.json', body)
|
||||
.then(response => {
|
||||
this.$store.dispatch('addAvailableLocationGroup', {
|
||||
locationGroup: 'Localisations nouvellement créées',
|
||||
locations: [response]
|
||||
makeFetch("POST", "/api/1.0/main/location.json", body)
|
||||
.then((response) => {
|
||||
this.$store.dispatch("addAvailableLocationGroup", {
|
||||
locationGroup: "Localisations nouvellement créées",
|
||||
locations: [response],
|
||||
});
|
||||
this.$store.dispatch('updateLocation', response);
|
||||
this.$store.dispatch("updateLocation", response);
|
||||
this.modal.showModal = false;
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error.name === 'ValidationException') {
|
||||
if (error.name === "ValidationException") {
|
||||
for (let v of error.violations) {
|
||||
this.errors.push(v);
|
||||
}
|
||||
} else {
|
||||
this.errors.push('An error occurred');
|
||||
this.errors.push("An error occurred");
|
||||
}
|
||||
})
|
||||
};
|
||||
});
|
||||
}
|
||||
},
|
||||
submitNewAddress(payload) {
|
||||
this.selected.addressId = payload.addressId;
|
||||
this.addAddress.context.addressId = payload.addressId;
|
||||
this.addAddress.context.edit = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -1,228 +1,243 @@
|
||||
<template>
|
||||
<teleport to="#social-issues-acc">
|
||||
|
||||
<div class="mb-3 row">
|
||||
<div class="col-4">
|
||||
<label :class="socialIssuesClassList">{{ $t('activity.social_issues') }}</label>
|
||||
</div>
|
||||
<div class="col-8">
|
||||
|
||||
<check-social-issue
|
||||
v-for="issue in socialIssuesList"
|
||||
:key="issue.id"
|
||||
:issue="issue"
|
||||
:selection="socialIssuesSelected"
|
||||
@updateSelected="updateIssuesSelected">
|
||||
</check-social-issue>
|
||||
|
||||
<div class="my-3">
|
||||
<VueMultiselect
|
||||
name="otherIssues"
|
||||
label="text"
|
||||
track-by="id"
|
||||
open-direction="bottom"
|
||||
:close-on-select="true"
|
||||
:preserve-search="false"
|
||||
:reset-after="true"
|
||||
:hide-selected="true"
|
||||
:taggable="false"
|
||||
:multiple="false"
|
||||
:searchable="true"
|
||||
:allow-empty="true"
|
||||
:show-labels="false"
|
||||
:loading="issueIsLoading"
|
||||
:placeholder="$t('activity.choose_other_social_issue')"
|
||||
:options="socialIssuesOther"
|
||||
@select="addIssueInList">
|
||||
</VueMultiselect>
|
||||
<teleport to="#social-issues-acc">
|
||||
<div class="mb-3 row">
|
||||
<div class="col-4">
|
||||
<label :class="socialIssuesClassList">{{
|
||||
$t("activity.social_issues")
|
||||
}}</label>
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<check-social-issue
|
||||
v-for="issue in socialIssuesList"
|
||||
:key="issue.id"
|
||||
:issue="issue"
|
||||
:selection="socialIssuesSelected"
|
||||
@update-selected="updateIssuesSelected"
|
||||
/>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 row">
|
||||
<div class="col-4">
|
||||
<label :class="socialActionsClassList">{{ $t('activity.social_actions') }}</label>
|
||||
</div>
|
||||
<div class="col-8">
|
||||
|
||||
<div v-if="actionIsLoading === true">
|
||||
<i class="chill-green fa fa-circle-o-notch fa-spin fa-lg"></i>
|
||||
<div class="my-3">
|
||||
<VueMultiselect
|
||||
name="otherIssues"
|
||||
label="text"
|
||||
track-by="id"
|
||||
open-direction="bottom"
|
||||
:close-on-select="true"
|
||||
:preserve-search="false"
|
||||
:reset-after="true"
|
||||
:hide-selected="true"
|
||||
:taggable="false"
|
||||
:multiple="false"
|
||||
:searchable="true"
|
||||
:allow-empty="true"
|
||||
:show-labels="false"
|
||||
:loading="issueIsLoading"
|
||||
:placeholder="$t('activity.choose_other_social_issue')"
|
||||
:options="socialIssuesOther"
|
||||
@select="addIssueInList"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span v-else-if="socialIssuesSelected.length === 0" class="inline-choice chill-no-data-statement mt-3">
|
||||
{{ $t('activity.select_first_a_social_issue') }}
|
||||
</span>
|
||||
<div class="mb-3 row">
|
||||
<div class="col-4">
|
||||
<label :class="socialActionsClassList">{{
|
||||
$t("activity.social_actions")
|
||||
}}</label>
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<div v-if="actionIsLoading === true">
|
||||
<i class="chill-green fa fa-circle-o-notch fa-spin fa-lg" />
|
||||
</div>
|
||||
|
||||
<template v-else-if="socialActionsList.length > 0">
|
||||
<check-social-action
|
||||
v-if="socialIssuesSelected.length || socialActionsSelected.length"
|
||||
v-for="action in socialActionsList"
|
||||
:key="action.id"
|
||||
:action="action"
|
||||
:selection="socialActionsSelected"
|
||||
@updateSelected="updateActionsSelected">
|
||||
</check-social-action>
|
||||
</template>
|
||||
<span
|
||||
v-else-if="socialIssuesSelected.length === 0"
|
||||
class="inline-choice chill-no-data-statement mt-3"
|
||||
>
|
||||
{{ $t("activity.select_first_a_social_issue") }}
|
||||
</span>
|
||||
|
||||
<span v-else-if="actionAreLoaded && socialActionsList.length === 0" class="inline-choice chill-no-data-statement mt-3">
|
||||
{{ $t('activity.social_action_list_empty') }}
|
||||
</span>
|
||||
<template v-else-if="socialActionsList.length > 0">
|
||||
<div
|
||||
v-if="
|
||||
socialIssuesSelected.length ||
|
||||
socialActionsSelected.length
|
||||
"
|
||||
>
|
||||
<check-social-action
|
||||
v-for="action in socialActionsList"
|
||||
:key="action.id"
|
||||
:action="action"
|
||||
:selection="socialActionsSelected"
|
||||
@update-selected="updateActionsSelected"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</teleport>
|
||||
<span
|
||||
v-else-if="
|
||||
actionAreLoaded && socialActionsList.length === 0
|
||||
"
|
||||
class="inline-choice chill-no-data-statement mt-3"
|
||||
>
|
||||
{{ $t("activity.social_action_list_empty") }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</teleport>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import VueMultiselect from 'vue-multiselect';
|
||||
import CheckSocialIssue from './SocialIssuesAcc/CheckSocialIssue.vue';
|
||||
import CheckSocialAction from './SocialIssuesAcc/CheckSocialAction.vue';
|
||||
import { getSocialIssues, getSocialActionByIssue } from '../api.js';
|
||||
import VueMultiselect from "vue-multiselect";
|
||||
import CheckSocialIssue from "./SocialIssuesAcc/CheckSocialIssue.vue";
|
||||
import CheckSocialAction from "./SocialIssuesAcc/CheckSocialAction.vue";
|
||||
import { getSocialIssues, getSocialActionByIssue } from "../api.js";
|
||||
|
||||
export default {
|
||||
name: "SocialIssuesAcc",
|
||||
components: {
|
||||
CheckSocialIssue,
|
||||
CheckSocialAction,
|
||||
VueMultiselect
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
issueIsLoading: false,
|
||||
actionIsLoading: false,
|
||||
actionAreLoaded: false,
|
||||
socialIssuesClassList:
|
||||
`col-form-label ${document.querySelector('input#chill_activitybundle_activity_socialIssues').getAttribute('required') ? 'required' : ''}`,
|
||||
socialActionsClassList:
|
||||
`col-form-label ${document.querySelector('input#chill_activitybundle_activity_socialActions').getAttribute('required') ? 'required' : ''}`,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
socialIssuesList() {
|
||||
return this.$store.state.activity.accompanyingPeriod.socialIssues;
|
||||
},
|
||||
socialIssuesSelected() {
|
||||
return this.$store.state.activity.socialIssues;
|
||||
},
|
||||
socialIssuesOther() {
|
||||
return this.$store.state.socialIssuesOther;
|
||||
},
|
||||
socialActionsList() {
|
||||
return this.$store.getters.socialActionsListSorted;
|
||||
},
|
||||
socialActionsSelected() {
|
||||
return this.$store.state.activity.socialActions;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
/* Load others issues in multiselect
|
||||
*/
|
||||
this.issueIsLoading = true;
|
||||
this.actionAreLoaded = false;
|
||||
getSocialIssues().then(response => new Promise((resolve, reject) => {
|
||||
this.$store.commit('updateIssuesOther', response.results);
|
||||
|
||||
/* Add in list the issues already associated (if not yet listed)
|
||||
name: "SocialIssuesAcc",
|
||||
components: {
|
||||
CheckSocialIssue,
|
||||
CheckSocialAction,
|
||||
VueMultiselect,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
issueIsLoading: false,
|
||||
actionIsLoading: false,
|
||||
actionAreLoaded: false,
|
||||
socialIssuesClassList: `col-form-label ${document.querySelector("input#chill_activitybundle_activity_socialIssues").getAttribute("required") ? "required" : ""}`,
|
||||
socialActionsClassList: `col-form-label ${document.querySelector("input#chill_activitybundle_activity_socialActions").getAttribute("required") ? "required" : ""}`,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
socialIssuesList() {
|
||||
return this.$store.state.activity.accompanyingPeriod.socialIssues;
|
||||
},
|
||||
socialIssuesSelected() {
|
||||
return this.$store.state.activity.socialIssues;
|
||||
},
|
||||
socialIssuesOther() {
|
||||
return this.$store.state.socialIssuesOther;
|
||||
},
|
||||
socialActionsList() {
|
||||
return this.$store.getters.socialActionsListSorted;
|
||||
},
|
||||
socialActionsSelected() {
|
||||
return this.$store.state.activity.socialActions;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
/* Load others issues in multiselect
|
||||
*/
|
||||
this.socialIssuesSelected.forEach(issue => {
|
||||
if (this.socialIssuesList.filter(i => i.id === issue.id).length !== 1) {
|
||||
this.$store.commit('addIssueInList', issue);
|
||||
}
|
||||
}, this);
|
||||
this.issueIsLoading = true;
|
||||
this.actionAreLoaded = false;
|
||||
getSocialIssues().then(
|
||||
(response) =>
|
||||
new Promise((resolve, reject) => {
|
||||
this.$store.commit("updateIssuesOther", response.results);
|
||||
|
||||
/* Remove from multiselect the issues that are not yet in checkbox list
|
||||
*/
|
||||
this.socialIssuesList.forEach(issue => {
|
||||
this.$store.commit('removeIssueInOther', issue);
|
||||
}, this);
|
||||
/* Add in list the issues already associated (if not yet listed)
|
||||
*/
|
||||
this.socialIssuesSelected.forEach((issue) => {
|
||||
if (
|
||||
this.socialIssuesList.filter(
|
||||
(i) => i.id === issue.id,
|
||||
).length !== 1
|
||||
) {
|
||||
this.$store.commit("addIssueInList", issue);
|
||||
}
|
||||
}, this);
|
||||
|
||||
/* Filter issues
|
||||
*/
|
||||
this.$store.commit('filterList', 'issues');
|
||||
/* Remove from multiselect the issues that are not yet in checkbox list
|
||||
*/
|
||||
this.socialIssuesList.forEach((issue) => {
|
||||
this.$store.commit("removeIssueInOther", issue);
|
||||
}, this);
|
||||
|
||||
/* Add in list the actions already associated (if not yet listed)
|
||||
*/
|
||||
this.socialActionsSelected.forEach(action => {
|
||||
this.$store.commit('addActionInList', action);
|
||||
}, this);
|
||||
/* Filter issues
|
||||
*/
|
||||
this.$store.commit("filterList", "issues");
|
||||
|
||||
/* Filter issues
|
||||
*/
|
||||
this.$store.commit('filterList', 'actions');
|
||||
/* Add in list the actions already associated (if not yet listed)
|
||||
*/
|
||||
this.socialActionsSelected.forEach((action) => {
|
||||
this.$store.commit("addActionInList", action);
|
||||
}, this);
|
||||
|
||||
this.issueIsLoading = false;
|
||||
this.actionAreLoaded = true;
|
||||
this.updateActionsList();
|
||||
resolve();
|
||||
}));
|
||||
},
|
||||
methods: {
|
||||
/* Filter issues
|
||||
*/
|
||||
this.$store.commit("filterList", "actions");
|
||||
|
||||
/* When choosing an issue in multiselect, add it in checkboxes (as selected),
|
||||
this.issueIsLoading = false;
|
||||
this.actionAreLoaded = true;
|
||||
this.updateActionsList();
|
||||
resolve();
|
||||
}),
|
||||
);
|
||||
},
|
||||
methods: {
|
||||
/* When choosing an issue in multiselect, add it in checkboxes (as selected),
|
||||
remove it from multiselect, and add socialActions concerned
|
||||
*/
|
||||
addIssueInList(value) {
|
||||
//console.log('addIssueInList', value);
|
||||
this.$store.commit('addIssueInList', value);
|
||||
this.$store.commit('removeIssueInOther', value);
|
||||
this.$store.dispatch('addIssueSelected', value);
|
||||
this.updateActionsList();
|
||||
},
|
||||
/* Update value for selected issues checkboxes
|
||||
*/
|
||||
updateIssuesSelected(issues) {
|
||||
//console.log('updateIssuesSelected', issues);
|
||||
this.$store.dispatch('updateIssuesSelected', issues);
|
||||
this.updateActionsList();
|
||||
},
|
||||
/* Update value for selected actions checkboxes
|
||||
*/
|
||||
updateActionsSelected(actions) {
|
||||
//console.log('updateActionsSelected', actions);
|
||||
this.$store.dispatch('updateActionsSelected', actions);
|
||||
},
|
||||
/* Add socialActions concerned: after reset, loop on each issue selected
|
||||
addIssueInList(value) {
|
||||
//console.log('addIssueInList', value);
|
||||
this.$store.commit("addIssueInList", value);
|
||||
this.$store.commit("removeIssueInOther", value);
|
||||
this.$store.dispatch("addIssueSelected", value);
|
||||
this.updateActionsList();
|
||||
},
|
||||
/* Update value for selected issues checkboxes
|
||||
*/
|
||||
updateIssuesSelected(issues) {
|
||||
//console.log('updateIssuesSelected', issues);
|
||||
this.$store.dispatch("updateIssuesSelected", issues);
|
||||
this.updateActionsList();
|
||||
},
|
||||
/* Update value for selected actions checkboxes
|
||||
*/
|
||||
updateActionsSelected(actions) {
|
||||
//console.log('updateActionsSelected', actions);
|
||||
this.$store.dispatch("updateActionsSelected", actions);
|
||||
},
|
||||
/* Add socialActions concerned: after reset, loop on each issue selected
|
||||
to get social actions concerned
|
||||
*/
|
||||
updateActionsList() {
|
||||
this.resetActionsList();
|
||||
this.socialIssuesSelected.forEach(item => {
|
||||
updateActionsList() {
|
||||
this.resetActionsList();
|
||||
this.socialIssuesSelected.forEach((item) => {
|
||||
this.actionIsLoading = true;
|
||||
getSocialActionByIssue(item.id).then(
|
||||
(actions) =>
|
||||
new Promise((resolve, reject) => {
|
||||
actions.results.forEach((action) => {
|
||||
this.$store.commit("addActionInList", action);
|
||||
}, this);
|
||||
|
||||
this.actionIsLoading = true;
|
||||
getSocialActionByIssue(item.id)
|
||||
.then(actions => new Promise((resolve, reject) => {
|
||||
this.$store.commit("filterList", "actions");
|
||||
|
||||
actions.results.forEach(action => {
|
||||
this.$store.commit('addActionInList', action);
|
||||
}, this);
|
||||
|
||||
this.$store.commit('filterList', 'actions');
|
||||
|
||||
this.actionIsLoading = false;
|
||||
this.actionAreLoaded = true;
|
||||
resolve();
|
||||
}));
|
||||
}, this);
|
||||
},
|
||||
/* Reset socialActions List: flush list and restore selected actions
|
||||
*/
|
||||
resetActionsList() {
|
||||
this.$store.commit('resetActionsList');
|
||||
this.actionAreLoaded = false;
|
||||
this.socialActionsSelected.forEach(item => {
|
||||
this.$store.commit('addActionInList', item);
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.actionIsLoading = false;
|
||||
this.actionAreLoaded = true;
|
||||
resolve();
|
||||
}),
|
||||
);
|
||||
}, this);
|
||||
},
|
||||
/* Reset socialActions List: flush list and restore selected actions
|
||||
*/
|
||||
resetActionsList() {
|
||||
this.$store.commit("resetActionsList");
|
||||
this.actionAreLoaded = false;
|
||||
this.socialActionsSelected.forEach((item) => {
|
||||
this.$store.commit("addActionInList", item);
|
||||
}, this);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style src="vue-multiselect/dist/vue-multiselect.css"></style>
|
||||
<style lang="scss" scoped>
|
||||
span.multiselect__single {
|
||||
display: none !important;
|
||||
}
|
||||
span.multiselect__single {
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,44 +1,43 @@
|
||||
<template>
|
||||
<span class="inline-choice">
|
||||
<div class="form-check">
|
||||
|
||||
<input class="form-check-input"
|
||||
type="checkbox"
|
||||
v-model="selected"
|
||||
name="action"
|
||||
v-bind:id="action.id"
|
||||
v-bind:value="action"
|
||||
<span class="inline-choice">
|
||||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
v-model="selected"
|
||||
name="action"
|
||||
:id="action.id"
|
||||
:value="action"
|
||||
/>
|
||||
<label class="form-check-label" v-bind:for="action.id">
|
||||
<span class="badge bg-light text-dark">{{ action.text }}</span>
|
||||
</label>
|
||||
|
||||
</div>
|
||||
</span>
|
||||
<label class="form-check-label" :for="action.id">
|
||||
<span class="badge bg-light text-dark">{{ action.text }}</span>
|
||||
</label>
|
||||
</div>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "CheckSocialAction",
|
||||
props: [ 'action', 'selection' ],
|
||||
emits: [ 'updateSelected' ],
|
||||
computed: {
|
||||
selected: {
|
||||
set(value) {
|
||||
this.$emit('updateSelected', value);
|
||||
},
|
||||
get() {
|
||||
return this.selection;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
name: "CheckSocialAction",
|
||||
props: ["action", "selection"],
|
||||
emits: ["updateSelected"],
|
||||
computed: {
|
||||
selected: {
|
||||
set(value) {
|
||||
this.$emit("updateSelected", value);
|
||||
},
|
||||
get() {
|
||||
return this.selection;
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import 'ChillMainAssets/module/bootstrap/shared';
|
||||
@import 'ChillPersonAssets/chill/scss/mixins';
|
||||
@import 'ChillMainAssets/chill/scss/chill_variables';
|
||||
@import "ChillMainAssets/module/bootstrap/shared";
|
||||
@import "ChillPersonAssets/chill/scss/mixins";
|
||||
@import "ChillMainAssets/chill/scss/chill_variables";
|
||||
span.badge {
|
||||
@include badge_social($social-action-color);
|
||||
font-size: 95%;
|
||||
|
@ -1,44 +1,45 @@
|
||||
<template>
|
||||
<span class="inline-choice">
|
||||
<div class="form-check">
|
||||
|
||||
<input class="form-check-input"
|
||||
type="checkbox"
|
||||
v-model="selected"
|
||||
name="issue"
|
||||
v-bind:id="issue.id"
|
||||
v-bind:value="issue"
|
||||
<span class="inline-choice">
|
||||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
v-model="selected"
|
||||
name="issue"
|
||||
:id="issue.id"
|
||||
:value="issue"
|
||||
/>
|
||||
<label class="form-check-label" v-bind:for="issue.id">
|
||||
<span class="badge bg-chill-l-gray text-dark">{{ issue.text }}</span>
|
||||
</label>
|
||||
|
||||
</div>
|
||||
</span>
|
||||
<label class="form-check-label" :for="issue.id">
|
||||
<span class="badge bg-chill-l-gray text-dark">{{
|
||||
issue.text
|
||||
}}</span>
|
||||
</label>
|
||||
</div>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "CheckSocialIssue",
|
||||
props: [ 'issue', 'selection' ],
|
||||
emits: [ 'updateSelected' ],
|
||||
computed: {
|
||||
selected: {
|
||||
set(value) {
|
||||
this.$emit('updateSelected', value);
|
||||
},
|
||||
get() {
|
||||
return this.selection;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
name: "CheckSocialIssue",
|
||||
props: ["issue", "selection"],
|
||||
emits: ["updateSelected"],
|
||||
computed: {
|
||||
selected: {
|
||||
set(value) {
|
||||
this.$emit("updateSelected", value);
|
||||
},
|
||||
get() {
|
||||
return this.selection;
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import 'ChillMainAssets/module/bootstrap/shared';
|
||||
@import 'ChillPersonAssets/chill/scss/mixins';
|
||||
@import 'ChillMainAssets/chill/scss/chill_variables';
|
||||
@import "ChillMainAssets/module/bootstrap/shared";
|
||||
@import "ChillPersonAssets/chill/scss/mixins";
|
||||
@import "ChillMainAssets/chill/scss/chill_variables";
|
||||
span.badge {
|
||||
@include badge_social($social-issue-color);
|
||||
font-size: 95%;
|
||||
|
@ -1,45 +1,44 @@
|
||||
import { personMessages } from 'ChillPersonAssets/vuejs/_js/i18n'
|
||||
import { multiSelectMessages } from 'ChillMainAssets/vuejs/_js/i18n'
|
||||
import { personMessages } from "ChillPersonAssets/vuejs/_js/i18n";
|
||||
import { multiSelectMessages } from "ChillMainAssets/vuejs/_js/i18n";
|
||||
|
||||
const activityMessages = {
|
||||
fr: {
|
||||
activity: {
|
||||
//
|
||||
errors: "Le formulaire contient des erreurs",
|
||||
social_issues: "Problématiques sociales",
|
||||
choose_other_social_issue: "Ajouter une autre problématique sociale...",
|
||||
social_actions: "Actions d'accompagnement",
|
||||
select_first_a_social_issue: "Sélectionnez d'abord une problématique sociale",
|
||||
social_action_list_empty: "Aucune action sociale disponible",
|
||||
fr: {
|
||||
activity: {
|
||||
//
|
||||
errors: "Le formulaire contient des erreurs",
|
||||
social_issues: "Problématiques sociales",
|
||||
choose_other_social_issue: "Ajouter une autre problématique sociale...",
|
||||
social_actions: "Actions d'accompagnement",
|
||||
select_first_a_social_issue:
|
||||
"Sélectionnez d'abord une problématique sociale",
|
||||
social_action_list_empty: "Aucune action sociale disponible",
|
||||
|
||||
//
|
||||
add_persons: "Ajouter des personnes concernées",
|
||||
bloc_persons: "Usagers",
|
||||
bloc_persons_associated: "Usagers du parcours",
|
||||
bloc_persons_not_associated: "Tiers non-pro.",
|
||||
bloc_thirdparty: "Tiers professionnels",
|
||||
bloc_users: "T(M)S",
|
||||
//
|
||||
add_persons: "Ajouter des personnes concernées",
|
||||
bloc_persons: "Usagers",
|
||||
bloc_persons_associated: "Usagers du parcours",
|
||||
bloc_persons_not_associated: "Tiers non-pro.",
|
||||
bloc_thirdparty: "Tiers professionnels",
|
||||
bloc_users: "T(M)S",
|
||||
|
||||
//
|
||||
location: "Localisation",
|
||||
choose_location: "Choisissez une localisation",
|
||||
choose_location_type: "Choisissez un type de localisation",
|
||||
create_new_location: "Créer une nouvelle localisation",
|
||||
location_fields: {
|
||||
name: "Nom",
|
||||
type: "Type",
|
||||
phonenumber1: "Téléphone",
|
||||
phonenumber2: "Autre téléphone",
|
||||
email: "Adresse courriel",
|
||||
},
|
||||
create_address: 'Créer une adresse',
|
||||
edit_address: "Modifier l'adresse"
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
location: "Localisation",
|
||||
choose_location: "Choisissez une localisation",
|
||||
choose_location_type: "Choisissez un type de localisation",
|
||||
create_new_location: "Créer une nouvelle localisation",
|
||||
location_fields: {
|
||||
name: "Nom",
|
||||
type: "Type",
|
||||
phonenumber1: "Téléphone",
|
||||
phonenumber2: "Autre téléphone",
|
||||
email: "Adresse courriel",
|
||||
},
|
||||
create_address: "Créer une adresse",
|
||||
edit_address: "Modifier l'adresse",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
Object.assign(activityMessages.fr, personMessages.fr, multiSelectMessages.fr);
|
||||
|
||||
export {
|
||||
activityMessages
|
||||
};
|
||||
export { activityMessages };
|
||||
|
@ -1,86 +1,86 @@
|
||||
import { createApp } from 'vue';
|
||||
import { _createI18n } from 'ChillMainAssets/vuejs/_js/i18n'
|
||||
import { activityMessages } from './i18n'
|
||||
import store from './store'
|
||||
import PickTemplate from 'ChillDocGeneratorAssets/vuejs/_components/PickTemplate.vue';
|
||||
import {fetchTemplates} from 'ChillDocGeneratorAssets/api/pickTemplate.js';
|
||||
import { createApp } from "vue";
|
||||
import { _createI18n } from "ChillMainAssets/vuejs/_js/i18n";
|
||||
import { activityMessages } from "./i18n";
|
||||
import store from "./store";
|
||||
import PickTemplate from "ChillDocGeneratorAssets/vuejs/_components/PickTemplate.vue";
|
||||
import { fetchTemplates } from "ChillDocGeneratorAssets/api/pickTemplate.js";
|
||||
|
||||
import App from './App.vue';
|
||||
import App from "./App.vue";
|
||||
|
||||
const i18n = _createI18n(activityMessages);
|
||||
|
||||
// app for activity
|
||||
|
||||
const hasSocialIssues = document.querySelector('#social-issues-acc') !== null;
|
||||
const hasLocation = document.querySelector('#location') !== null;
|
||||
const hasPerson = document.querySelector('#add-persons') !== null;
|
||||
const hasSocialIssues = document.querySelector("#social-issues-acc") !== null;
|
||||
const hasLocation = document.querySelector("#location") !== null;
|
||||
const hasPerson = document.querySelector("#add-persons") !== null;
|
||||
|
||||
const app = createApp({
|
||||
template: `<app
|
||||
template: `<app
|
||||
:hasSocialIssues="hasSocialIssues"
|
||||
:hasLocation="hasLocation"
|
||||
:hasPerson="hasPerson"
|
||||
></app>`,
|
||||
data() {
|
||||
return {
|
||||
hasSocialIssues,
|
||||
hasLocation,
|
||||
hasPerson,
|
||||
};
|
||||
}
|
||||
data() {
|
||||
return {
|
||||
hasSocialIssues,
|
||||
hasLocation,
|
||||
hasPerson,
|
||||
};
|
||||
},
|
||||
})
|
||||
.use(store)
|
||||
.use(i18n)
|
||||
.component('app', App)
|
||||
.mount('#activity');
|
||||
|
||||
.use(store)
|
||||
.use(i18n)
|
||||
.component("app", App)
|
||||
.mount("#activity");
|
||||
|
||||
// app for picking template
|
||||
|
||||
const i18nGendoc = _createI18n({});
|
||||
|
||||
document.querySelectorAll('div[data-docgen-template-picker]').forEach(el => {
|
||||
fetchTemplates(el.dataset.entityClass).then(templates => {
|
||||
const picker = {
|
||||
template:
|
||||
'<pick-template :templates="this.templates" :preventDefaultMoveToGenerate="true" ' +
|
||||
':entityClass="faked" @go-to-generate-document="generateDoc"></pick-template>',
|
||||
components: {
|
||||
PickTemplate,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
templates: templates,
|
||||
entityId: el.dataset.entityId,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
generateDoc({event, link, template}) {
|
||||
console.log('generateDoc');
|
||||
console.log('link', link);
|
||||
console.log('template', template);
|
||||
|
||||
let hiddenInput = document.querySelector("input[data-template-id]");
|
||||
|
||||
if (hiddenInput === null) {
|
||||
console.error('hidden input not found');
|
||||
return;
|
||||
}
|
||||
|
||||
hiddenInput.value = template;
|
||||
|
||||
let form = document.querySelector('form[name="chill_activitybundle_activity"');
|
||||
|
||||
if (form === null) {
|
||||
console.error('form not found');
|
||||
return;
|
||||
}
|
||||
|
||||
form.submit();
|
||||
}
|
||||
}
|
||||
document.querySelectorAll("div[data-docgen-template-picker]").forEach((el) => {
|
||||
fetchTemplates(el.dataset.entityClass).then((templates) => {
|
||||
const picker = {
|
||||
template:
|
||||
'<pick-template :templates="this.templates" :preventDefaultMoveToGenerate="true" ' +
|
||||
':entityClass="faked" @go-to-generate-document="generateDoc"></pick-template>',
|
||||
components: {
|
||||
PickTemplate,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
templates: templates,
|
||||
entityId: el.dataset.entityId,
|
||||
};
|
||||
createApp(picker).use(i18nGendoc).mount(el);
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
generateDoc({ event, link, template }) {
|
||||
console.log("generateDoc");
|
||||
console.log("link", link);
|
||||
console.log("template", template);
|
||||
|
||||
let hiddenInput = document.querySelector("input[data-template-id]");
|
||||
|
||||
if (hiddenInput === null) {
|
||||
console.error("hidden input not found");
|
||||
return;
|
||||
}
|
||||
|
||||
hiddenInput.value = template;
|
||||
|
||||
let form = document.querySelector(
|
||||
'form[name="chill_activitybundle_activity"',
|
||||
);
|
||||
|
||||
if (form === null) {
|
||||
console.error("form not found");
|
||||
return;
|
||||
}
|
||||
|
||||
form.submit();
|
||||
},
|
||||
},
|
||||
};
|
||||
createApp(picker).use(i18nGendoc).mount(el);
|
||||
});
|
||||
});
|
||||
|
@ -1,348 +1,332 @@
|
||||
import 'es6-promise/auto';
|
||||
import { createStore } from 'vuex';
|
||||
import { postLocation } from './api';
|
||||
import prepareLocations from './store.locations.js';
|
||||
import "es6-promise/auto";
|
||||
import { createStore } from "vuex";
|
||||
import { postLocation } from "./api";
|
||||
import prepareLocations from "./store.locations.js";
|
||||
|
||||
const debug = process.env.NODE_ENV !== 'production';
|
||||
const debug = process.env.NODE_ENV !== "production";
|
||||
//console.log('window.activity', window.activity);
|
||||
|
||||
const addIdToValue = (string, id) => {
|
||||
let array = string ? string.split(',') : [];
|
||||
array.push(id.toString());
|
||||
let str = array.join();
|
||||
return str;
|
||||
let array = string ? string.split(",") : [];
|
||||
array.push(id.toString());
|
||||
let str = array.join();
|
||||
return str;
|
||||
};
|
||||
|
||||
const removeIdFromValue = (string, id) => {
|
||||
let array = string.split(',');
|
||||
array = array.filter(el => el !== id.toString());
|
||||
let str = array.join();
|
||||
return str;
|
||||
let array = string.split(",");
|
||||
array = array.filter((el) => el !== id.toString());
|
||||
let str = array.join();
|
||||
return str;
|
||||
};
|
||||
|
||||
const store = createStore({
|
||||
strict: debug,
|
||||
state: {
|
||||
activity: window.activity,
|
||||
socialIssuesOther: [],
|
||||
socialActionsList: [],
|
||||
availableLocations: [],
|
||||
strict: debug,
|
||||
state: {
|
||||
activity: window.activity,
|
||||
socialIssuesOther: [],
|
||||
socialActionsList: [],
|
||||
availableLocations: [],
|
||||
},
|
||||
getters: {
|
||||
suggestedEntities(state) {
|
||||
if (
|
||||
typeof state.activity.accompanyingPeriod === "undefined" ||
|
||||
state.activity.accompanyingPeriod === null
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
const allEntities = [
|
||||
...store.getters.suggestedPersons,
|
||||
...store.getters.suggestedRequestor,
|
||||
...store.getters.suggestedUser,
|
||||
...store.getters.suggestedResources,
|
||||
];
|
||||
const uniqueIds = [
|
||||
...new Set(allEntities.map((i) => `${i.type}-${i.id}`)),
|
||||
];
|
||||
return Array.from(
|
||||
uniqueIds,
|
||||
(id) => allEntities.filter((r) => `${r.type}-${r.id}` === id)[0],
|
||||
);
|
||||
},
|
||||
getters: {
|
||||
suggestedEntities(state) {
|
||||
if (typeof state.activity.accompanyingPeriod === "undefined" || state.activity.accompanyingPeriod === null) {
|
||||
return [];
|
||||
}
|
||||
const allEntities = [
|
||||
...store.getters.suggestedPersons,
|
||||
...store.getters.suggestedRequestor,
|
||||
...store.getters.suggestedUser,
|
||||
...store.getters.suggestedResources,
|
||||
];
|
||||
const uniqueIds = [
|
||||
...new Set(allEntities.map((i) => `${i.type}-${i.id}`)),
|
||||
];
|
||||
return Array.from(
|
||||
uniqueIds,
|
||||
(id) => allEntities.filter((r) => `${r.type}-${r.id}` === id)[0]
|
||||
);
|
||||
},
|
||||
suggestedPersons(state) {
|
||||
const existingPersonIds = state.activity.persons.map((p) => p.id);
|
||||
return state.activity.activityType.personsVisible === 0
|
||||
? []
|
||||
: state.activity.accompanyingPeriod.participations
|
||||
.filter((p) => p.endDate === null)
|
||||
.map((p) => p.person)
|
||||
.filter((p) => !existingPersonIds.includes(p.id));
|
||||
},
|
||||
suggestedRequestor(state) {
|
||||
if (state.activity.accompanyingPeriod.requestor === null) {
|
||||
return [];
|
||||
}
|
||||
const existingPersonIds = state.activity.persons.map((p) => p.id);
|
||||
const existingThirdPartyIds = state.activity.thirdParties.map(
|
||||
(p) => p.id
|
||||
);
|
||||
|
||||
return [state.activity.accompanyingPeriod.requestor].filter(
|
||||
(r) =>
|
||||
(r.type === "person" &&
|
||||
!existingPersonIds.includes(r.id) &&
|
||||
state.activity.activityType.personsVisible !== 0) ||
|
||||
(r.type === "thirdparty" &&
|
||||
!existingThirdPartyIds.includes(r.id) &&
|
||||
state.activity.activityType.thirdPartiesVisible !== 0)
|
||||
);
|
||||
},
|
||||
suggestedUser(state) {
|
||||
const existingUserIds = state.activity.users.map((p) => p.id);
|
||||
return state.activity.activityType.usersVisible === 0
|
||||
? []
|
||||
: [state.activity.accompanyingPeriod.user].filter(
|
||||
(u) => u !== null && !existingUserIds.includes(u.id)
|
||||
);
|
||||
},
|
||||
suggestedResources(state) {
|
||||
const resources = state.activity.accompanyingPeriod.resources;
|
||||
const existingPersonIds = state.activity.persons.map((p) => p.id);
|
||||
const existingThirdPartyIds = state.activity.thirdParties.map(
|
||||
(p) => p.id
|
||||
);
|
||||
return state.activity.accompanyingPeriod.resources
|
||||
.map((r) => r.resource)
|
||||
.filter(
|
||||
(r) =>
|
||||
(r.type === "person" &&
|
||||
!existingPersonIds.includes(r.id) &&
|
||||
state.activity.activityType.personsVisible !== 0) ||
|
||||
(r.type === "thirdparty" &&
|
||||
!existingThirdPartyIds.includes(r.id) &&
|
||||
state.activity.activityType.thirdPartiesVisible !== 0)
|
||||
);
|
||||
},
|
||||
socialActionsListSorted(state) {
|
||||
return [ ...state.socialActionsList].sort((a, b) => a.ordering - b.ordering);
|
||||
},
|
||||
suggestedPersons(state) {
|
||||
const existingPersonIds = state.activity.persons.map((p) => p.id);
|
||||
return state.activity.activityType.personsVisible === 0
|
||||
? []
|
||||
: state.activity.accompanyingPeriod.participations
|
||||
.filter((p) => p.endDate === null)
|
||||
.map((p) => p.person)
|
||||
.filter((p) => !existingPersonIds.includes(p.id));
|
||||
},
|
||||
mutations: {
|
||||
// SocialIssueAcc
|
||||
addIssueInList(state, issue) {
|
||||
//console.log('add issue list', issue.id);
|
||||
state.activity.accompanyingPeriod.socialIssues.push(issue);
|
||||
},
|
||||
addIssueSelected(state, issue) {
|
||||
//console.log('add issue selected', issue.id);
|
||||
state.activity.socialIssues.push(issue);
|
||||
},
|
||||
updateIssuesSelected(state, issues) {
|
||||
//console.log('update issues selected', issues);
|
||||
state.activity.socialIssues = issues;
|
||||
},
|
||||
updateIssuesOther(state, payload) {
|
||||
//console.log('update issues other');
|
||||
state.socialIssuesOther = payload;
|
||||
},
|
||||
removeIssueInOther(state, issue) {
|
||||
//console.log('remove issue other', issue.id);
|
||||
state.socialIssuesOther = state.socialIssuesOther.filter(
|
||||
(i) => i.id !== issue.id
|
||||
);
|
||||
},
|
||||
resetActionsList(state) {
|
||||
//console.log('reset list actions');
|
||||
state.socialActionsList = [];
|
||||
},
|
||||
addActionInList(state, action) {
|
||||
state.socialActionsList.push(action);
|
||||
},
|
||||
updateActionsSelected(state, actions) {
|
||||
//console.log('update actions selected', actions);
|
||||
state.activity.socialActions = actions;
|
||||
},
|
||||
filterList(state, list) {
|
||||
const filterList = (list) => {
|
||||
// remove duplicates entries
|
||||
list = list.filter(
|
||||
(value, index) =>
|
||||
list.findIndex((array) => array.id === value.id) ===
|
||||
index
|
||||
);
|
||||
// alpha sort
|
||||
list.sort((a, b) =>
|
||||
a.text > b.text ? 1 : b.text > a.text ? -1 : 0
|
||||
);
|
||||
return list;
|
||||
};
|
||||
if (list === "issues") {
|
||||
state.activity.accompanyingPeriod.socialIssues = filterList(
|
||||
state.activity.accompanyingPeriod.socialIssues
|
||||
);
|
||||
}
|
||||
if (list === "actions") {
|
||||
state.socialActionsList = filterList(state.socialActionsList);
|
||||
}
|
||||
},
|
||||
suggestedRequestor(state) {
|
||||
if (state.activity.accompanyingPeriod.requestor === null) {
|
||||
return [];
|
||||
}
|
||||
const existingPersonIds = state.activity.persons.map((p) => p.id);
|
||||
const existingThirdPartyIds = state.activity.thirdParties.map(
|
||||
(p) => p.id,
|
||||
);
|
||||
|
||||
// ConcernedGroups
|
||||
addPersonsInvolved(state, payload) {
|
||||
console.log("### mutation addPersonsInvolved", payload);
|
||||
switch (payload.result.type) {
|
||||
case "person":
|
||||
state.activity.persons.push(payload.result);
|
||||
break;
|
||||
case "thirdparty":
|
||||
state.activity.thirdParties.push(payload.result);
|
||||
break;
|
||||
case "user":
|
||||
state.activity.users.push(payload.result);
|
||||
break;
|
||||
}
|
||||
},
|
||||
removePersonInvolved(state, payload) {
|
||||
//console.log('### mutation removePersonInvolved', payload.type);
|
||||
switch (payload.type) {
|
||||
case "person":
|
||||
state.activity.persons = state.activity.persons.filter(
|
||||
(person) => person !== payload
|
||||
);
|
||||
break;
|
||||
case "thirdparty":
|
||||
state.activity.thirdParties =
|
||||
state.activity.thirdParties.filter(
|
||||
(thirdparty) => thirdparty !== payload
|
||||
);
|
||||
break;
|
||||
case "user":
|
||||
state.activity.users = state.activity.users.filter(
|
||||
(user) => user !== payload
|
||||
);
|
||||
break;
|
||||
}
|
||||
},
|
||||
updateLocation(state, value) {
|
||||
console.log("### mutation: updateLocation", value);
|
||||
state.activity.location = value;
|
||||
},
|
||||
addAvailableLocationGroup(state, group) {
|
||||
state.availableLocations.push(group);
|
||||
return [state.activity.accompanyingPeriod.requestor].filter(
|
||||
(r) =>
|
||||
(r.type === "person" &&
|
||||
!existingPersonIds.includes(r.id) &&
|
||||
state.activity.activityType.personsVisible !== 0) ||
|
||||
(r.type === "thirdparty" &&
|
||||
!existingThirdPartyIds.includes(r.id) &&
|
||||
state.activity.activityType.thirdPartiesVisible !== 0),
|
||||
);
|
||||
},
|
||||
suggestedUser(state) {
|
||||
const existingUserIds = state.activity.users.map((p) => p.id);
|
||||
return state.activity.activityType.usersVisible === 0
|
||||
? []
|
||||
: [state.activity.accompanyingPeriod.user].filter(
|
||||
(u) => u !== null && !existingUserIds.includes(u.id),
|
||||
);
|
||||
},
|
||||
suggestedResources(state) {
|
||||
const resources = state.activity.accompanyingPeriod.resources;
|
||||
const existingPersonIds = state.activity.persons.map((p) => p.id);
|
||||
const existingThirdPartyIds = state.activity.thirdParties.map(
|
||||
(p) => p.id,
|
||||
);
|
||||
return state.activity.accompanyingPeriod.resources
|
||||
.map((r) => r.resource)
|
||||
.filter(
|
||||
(r) =>
|
||||
(r.type === "person" &&
|
||||
!existingPersonIds.includes(r.id) &&
|
||||
state.activity.activityType.personsVisible !== 0) ||
|
||||
(r.type === "thirdparty" &&
|
||||
!existingThirdPartyIds.includes(r.id) &&
|
||||
state.activity.activityType.thirdPartiesVisible !== 0),
|
||||
);
|
||||
},
|
||||
socialActionsListSorted(state) {
|
||||
return [...state.socialActionsList].sort(
|
||||
(a, b) => a.ordering - b.ordering,
|
||||
);
|
||||
},
|
||||
},
|
||||
mutations: {
|
||||
// SocialIssueAcc
|
||||
addIssueInList(state, issue) {
|
||||
//console.log('add issue list', issue.id);
|
||||
state.activity.accompanyingPeriod.socialIssues.push(issue);
|
||||
},
|
||||
addIssueSelected(state, issue) {
|
||||
//console.log('add issue selected', issue.id);
|
||||
state.activity.socialIssues.push(issue);
|
||||
},
|
||||
updateIssuesSelected(state, issues) {
|
||||
//console.log('update issues selected', issues);
|
||||
state.activity.socialIssues = issues;
|
||||
},
|
||||
updateIssuesOther(state, payload) {
|
||||
//console.log('update issues other');
|
||||
state.socialIssuesOther = payload;
|
||||
},
|
||||
removeIssueInOther(state, issue) {
|
||||
//console.log('remove issue other', issue.id);
|
||||
state.socialIssuesOther = state.socialIssuesOther.filter(
|
||||
(i) => i.id !== issue.id,
|
||||
);
|
||||
},
|
||||
resetActionsList(state) {
|
||||
//console.log('reset list actions');
|
||||
state.socialActionsList = [];
|
||||
},
|
||||
addActionInList(state, action) {
|
||||
state.socialActionsList.push(action);
|
||||
},
|
||||
updateActionsSelected(state, actions) {
|
||||
//console.log('update actions selected', actions);
|
||||
state.activity.socialActions = actions;
|
||||
},
|
||||
filterList(state, list) {
|
||||
const filterList = (list) => {
|
||||
// remove duplicates entries
|
||||
list = list.filter(
|
||||
(value, index) =>
|
||||
list.findIndex((array) => array.id === value.id) === index,
|
||||
);
|
||||
// alpha sort
|
||||
list.sort((a, b) => (a.text > b.text ? 1 : b.text > a.text ? -1 : 0));
|
||||
return list;
|
||||
};
|
||||
if (list === "issues") {
|
||||
state.activity.accompanyingPeriod.socialIssues = filterList(
|
||||
state.activity.accompanyingPeriod.socialIssues,
|
||||
);
|
||||
}
|
||||
if (list === "actions") {
|
||||
state.socialActionsList = filterList(state.socialActionsList);
|
||||
}
|
||||
},
|
||||
|
||||
// ConcernedGroups
|
||||
addPersonsInvolved(state, payload) {
|
||||
console.log("### mutation addPersonsInvolved", payload);
|
||||
switch (payload.result.type) {
|
||||
case "person":
|
||||
state.activity.persons.push(payload.result);
|
||||
break;
|
||||
case "thirdparty":
|
||||
state.activity.thirdParties.push(payload.result);
|
||||
break;
|
||||
case "user":
|
||||
state.activity.users.push(payload.result);
|
||||
break;
|
||||
}
|
||||
},
|
||||
removePersonInvolved(state, payload) {
|
||||
//console.log('### mutation removePersonInvolved', payload.type);
|
||||
switch (payload.type) {
|
||||
case "person":
|
||||
state.activity.persons = state.activity.persons.filter(
|
||||
(person) => person !== payload,
|
||||
);
|
||||
break;
|
||||
case "thirdparty":
|
||||
state.activity.thirdParties = state.activity.thirdParties.filter(
|
||||
(thirdparty) => thirdparty !== payload,
|
||||
);
|
||||
break;
|
||||
case "user":
|
||||
state.activity.users = state.activity.users.filter(
|
||||
(user) => user !== payload,
|
||||
);
|
||||
break;
|
||||
}
|
||||
},
|
||||
updateLocation(state, value) {
|
||||
console.log("### mutation: updateLocation", value);
|
||||
state.activity.location = value;
|
||||
},
|
||||
addAvailableLocationGroup(state, group) {
|
||||
state.availableLocations.push(group);
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
addIssueSelected({ commit }, issue) {
|
||||
let aSocialIssues = document.getElementById(
|
||||
"chill_activitybundle_activity_socialIssues",
|
||||
);
|
||||
aSocialIssues.value = addIdToValue(aSocialIssues.value, issue.id);
|
||||
commit("addIssueSelected", issue);
|
||||
},
|
||||
updateIssuesSelected({ commit }, payload) {
|
||||
let aSocialIssues = document.getElementById(
|
||||
"chill_activitybundle_activity_socialIssues",
|
||||
);
|
||||
aSocialIssues.value = "";
|
||||
payload.forEach((item) => {
|
||||
aSocialIssues.value = addIdToValue(aSocialIssues.value, item.id);
|
||||
});
|
||||
commit("updateIssuesSelected", payload);
|
||||
},
|
||||
updateActionsSelected({ commit }, payload) {
|
||||
let aSocialActions = document.getElementById(
|
||||
"chill_activitybundle_activity_socialActions",
|
||||
);
|
||||
aSocialActions.value = "";
|
||||
payload.forEach((item) => {
|
||||
aSocialActions.value = addIdToValue(aSocialActions.value, item.id);
|
||||
});
|
||||
commit("updateActionsSelected", payload);
|
||||
},
|
||||
addAvailableLocationGroup({ commit }, payload) {
|
||||
commit("addAvailableLocationGroup", payload);
|
||||
},
|
||||
addPersonsInvolved({ commit }, payload) {
|
||||
//console.log('### action addPersonsInvolved', payload.result.type);
|
||||
switch (payload.result.type) {
|
||||
case "person":
|
||||
let aPersons = document.getElementById(
|
||||
"chill_activitybundle_activity_persons",
|
||||
);
|
||||
aPersons.value = addIdToValue(aPersons.value, payload.result.id);
|
||||
break;
|
||||
case "thirdparty":
|
||||
let aThirdParties = document.getElementById(
|
||||
"chill_activitybundle_activity_thirdParties",
|
||||
);
|
||||
aThirdParties.value = addIdToValue(
|
||||
aThirdParties.value,
|
||||
payload.result.id,
|
||||
);
|
||||
break;
|
||||
case "user":
|
||||
let aUsers = document.getElementById(
|
||||
"chill_activitybundle_activity_users",
|
||||
);
|
||||
aUsers.value = addIdToValue(aUsers.value, payload.result.id);
|
||||
break;
|
||||
}
|
||||
commit("addPersonsInvolved", payload);
|
||||
},
|
||||
removePersonInvolved({ commit }, payload) {
|
||||
//console.log('### action removePersonInvolved', payload);
|
||||
switch (payload.type) {
|
||||
case "person":
|
||||
let aPersons = document.getElementById(
|
||||
"chill_activitybundle_activity_persons",
|
||||
);
|
||||
aPersons.value = removeIdFromValue(aPersons.value, payload.id);
|
||||
break;
|
||||
case "thirdparty":
|
||||
let aThirdParties = document.getElementById(
|
||||
"chill_activitybundle_activity_thirdParties",
|
||||
);
|
||||
aThirdParties.value = removeIdFromValue(
|
||||
aThirdParties.value,
|
||||
payload.id,
|
||||
);
|
||||
break;
|
||||
case "user":
|
||||
let aUsers = document.getElementById(
|
||||
"chill_activitybundle_activity_users",
|
||||
);
|
||||
aUsers.value = removeIdFromValue(aUsers.value, payload.id);
|
||||
break;
|
||||
}
|
||||
commit("removePersonInvolved", payload);
|
||||
},
|
||||
updateLocation({ commit }, value) {
|
||||
console.log("### action: updateLocation", value);
|
||||
let hiddenLocation = document.getElementById(
|
||||
"chill_activitybundle_activity_location",
|
||||
);
|
||||
if (value.onthefly) {
|
||||
const body = {
|
||||
type: "location",
|
||||
name:
|
||||
value.name === "__AccompanyingCourseLocation__" ? null : value.name,
|
||||
locationType: {
|
||||
id: value.locationType.id,
|
||||
type: "location-type",
|
||||
},
|
||||
};
|
||||
if (value.address.id) {
|
||||
Object.assign(body, {
|
||||
address: {
|
||||
id: value.address.id,
|
||||
},
|
||||
});
|
||||
}
|
||||
postLocation(body)
|
||||
.then((location) => (hiddenLocation.value = location.id))
|
||||
.catch((err) => {
|
||||
console.log(err.message);
|
||||
});
|
||||
} else {
|
||||
hiddenLocation.value = value.id;
|
||||
}
|
||||
commit("updateLocation", value);
|
||||
},
|
||||
actions: {
|
||||
addIssueSelected({ commit }, issue) {
|
||||
let aSocialIssues = document.getElementById(
|
||||
"chill_activitybundle_activity_socialIssues"
|
||||
);
|
||||
aSocialIssues.value = addIdToValue(aSocialIssues.value, issue.id);
|
||||
commit("addIssueSelected", issue);
|
||||
},
|
||||
updateIssuesSelected({ commit }, payload) {
|
||||
let aSocialIssues = document.getElementById(
|
||||
"chill_activitybundle_activity_socialIssues"
|
||||
);
|
||||
aSocialIssues.value = "";
|
||||
payload.forEach((item) => {
|
||||
aSocialIssues.value = addIdToValue(
|
||||
aSocialIssues.value,
|
||||
item.id
|
||||
);
|
||||
});
|
||||
commit("updateIssuesSelected", payload);
|
||||
},
|
||||
updateActionsSelected({ commit }, payload) {
|
||||
let aSocialActions = document.getElementById(
|
||||
"chill_activitybundle_activity_socialActions"
|
||||
);
|
||||
aSocialActions.value = "";
|
||||
payload.forEach((item) => {
|
||||
aSocialActions.value = addIdToValue(
|
||||
aSocialActions.value,
|
||||
item.id
|
||||
);
|
||||
});
|
||||
commit("updateActionsSelected", payload);
|
||||
},
|
||||
addAvailableLocationGroup({ commit }, payload) {
|
||||
commit("addAvailableLocationGroup", payload);
|
||||
},
|
||||
addPersonsInvolved({ commit }, payload) {
|
||||
//console.log('### action addPersonsInvolved', payload.result.type);
|
||||
switch (payload.result.type) {
|
||||
case "person":
|
||||
let aPersons = document.getElementById(
|
||||
"chill_activitybundle_activity_persons"
|
||||
);
|
||||
aPersons.value = addIdToValue(
|
||||
aPersons.value,
|
||||
payload.result.id
|
||||
);
|
||||
break;
|
||||
case "thirdparty":
|
||||
let aThirdParties = document.getElementById(
|
||||
"chill_activitybundle_activity_thirdParties"
|
||||
);
|
||||
aThirdParties.value = addIdToValue(
|
||||
aThirdParties.value,
|
||||
payload.result.id
|
||||
);
|
||||
break;
|
||||
case "user":
|
||||
let aUsers = document.getElementById(
|
||||
"chill_activitybundle_activity_users"
|
||||
);
|
||||
aUsers.value = addIdToValue(
|
||||
aUsers.value,
|
||||
payload.result.id
|
||||
);
|
||||
break;
|
||||
}
|
||||
commit("addPersonsInvolved", payload);
|
||||
},
|
||||
removePersonInvolved({ commit }, payload) {
|
||||
//console.log('### action removePersonInvolved', payload);
|
||||
switch (payload.type) {
|
||||
case "person":
|
||||
let aPersons = document.getElementById(
|
||||
"chill_activitybundle_activity_persons"
|
||||
);
|
||||
aPersons.value = removeIdFromValue(
|
||||
aPersons.value,
|
||||
payload.id
|
||||
);
|
||||
break;
|
||||
case "thirdparty":
|
||||
let aThirdParties = document.getElementById(
|
||||
"chill_activitybundle_activity_thirdParties"
|
||||
);
|
||||
aThirdParties.value = removeIdFromValue(
|
||||
aThirdParties.value,
|
||||
payload.id
|
||||
);
|
||||
break;
|
||||
case "user":
|
||||
let aUsers = document.getElementById(
|
||||
"chill_activitybundle_activity_users"
|
||||
);
|
||||
aUsers.value = removeIdFromValue(aUsers.value, payload.id);
|
||||
break;
|
||||
}
|
||||
commit("removePersonInvolved", payload);
|
||||
},
|
||||
updateLocation({ commit }, value) {
|
||||
console.log("### action: updateLocation", value);
|
||||
let hiddenLocation = document.getElementById(
|
||||
"chill_activitybundle_activity_location"
|
||||
);
|
||||
if (value.onthefly) {
|
||||
const body = {
|
||||
"type": "location",
|
||||
"name": value.name === '__AccompanyingCourseLocation__' ? null : value.name,
|
||||
"locationType": {
|
||||
"id": value.locationType.id,
|
||||
"type": "location-type"
|
||||
}
|
||||
};
|
||||
if (value.address.id) {
|
||||
Object.assign(body, {
|
||||
"address": {
|
||||
"id": value.address.id
|
||||
},
|
||||
})
|
||||
}
|
||||
postLocation(body)
|
||||
.then(
|
||||
location => hiddenLocation.value = location.id
|
||||
).catch(
|
||||
err => {
|
||||
console.log(err.message);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
hiddenLocation.value = value.id;
|
||||
}
|
||||
commit("updateLocation", value);
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
prepareLocations(store);
|
||||
|
@ -1,124 +1,132 @@
|
||||
import {getLocations, getLocationTypeByDefaultFor, getUserCurrentLocation} from "./api";
|
||||
import {
|
||||
getLocations,
|
||||
getLocationTypeByDefaultFor,
|
||||
getUserCurrentLocation,
|
||||
} from "./api";
|
||||
|
||||
const makeConcernedPersonsLocation = (locationType, store) => {
|
||||
let locations = [];
|
||||
store.getters.suggestedEntities.forEach(
|
||||
(e) => {
|
||||
if (e.type === 'person' && e.current_household_address !== null){
|
||||
locations.push({
|
||||
type: 'location',
|
||||
id: -store.getters.suggestedEntities.indexOf(e)*10,
|
||||
onthefly: true,
|
||||
name: e.text,
|
||||
address: {
|
||||
id: e.current_household_address.address_id,
|
||||
},
|
||||
locationType: locationType
|
||||
});
|
||||
}
|
||||
}
|
||||
)
|
||||
return locations;
|
||||
let locations = [];
|
||||
store.getters.suggestedEntities.forEach((e) => {
|
||||
if (e.type === "person" && e.current_household_address !== null) {
|
||||
locations.push({
|
||||
type: "location",
|
||||
id: -store.getters.suggestedEntities.indexOf(e) * 10,
|
||||
onthefly: true,
|
||||
name: e.text,
|
||||
address: {
|
||||
id: e.current_household_address.address_id,
|
||||
},
|
||||
locationType: locationType,
|
||||
});
|
||||
}
|
||||
});
|
||||
return locations;
|
||||
};
|
||||
const makeConcernedThirdPartiesLocation = (locationType, store) => {
|
||||
let locations = [];
|
||||
store.getters.suggestedEntities.forEach(
|
||||
(e) => {
|
||||
if (e.type === 'thirdparty' && e.address !== null){
|
||||
locations.push({
|
||||
type: 'location',
|
||||
id: -store.getters.suggestedEntities.indexOf(e)*10,
|
||||
onthefly: true,
|
||||
name: e.text,
|
||||
address: { id: e.address.address_id },
|
||||
locationType: locationType
|
||||
});
|
||||
}
|
||||
}
|
||||
)
|
||||
return locations;
|
||||
let locations = [];
|
||||
store.getters.suggestedEntities.forEach((e) => {
|
||||
if (e.type === "thirdparty" && e.address !== null) {
|
||||
locations.push({
|
||||
type: "location",
|
||||
id: -store.getters.suggestedEntities.indexOf(e) * 10,
|
||||
onthefly: true,
|
||||
name: e.text,
|
||||
address: { id: e.address.address_id },
|
||||
locationType: locationType,
|
||||
});
|
||||
}
|
||||
});
|
||||
return locations;
|
||||
};
|
||||
const makeAccompanyingPeriodLocation = (locationType, store) => {
|
||||
if (store.state.activity.accompanyingPeriod === null) {
|
||||
return {};
|
||||
}
|
||||
const accPeriodLocation = store.state.activity.accompanyingPeriod.location;
|
||||
return {
|
||||
type: 'location',
|
||||
id: -1,
|
||||
onthefly: true,
|
||||
name: '__AccompanyingCourseLocation__',
|
||||
address: {
|
||||
id: accPeriodLocation.address_id,
|
||||
text: `${accPeriodLocation.text} - ${accPeriodLocation.postcode.code} ${accPeriodLocation.postcode.name}`
|
||||
},
|
||||
locationType: locationType
|
||||
}
|
||||
if (store.state.activity.accompanyingPeriod === null) {
|
||||
return {};
|
||||
}
|
||||
const accPeriodLocation = store.state.activity.accompanyingPeriod.location;
|
||||
return {
|
||||
type: "location",
|
||||
id: -1,
|
||||
onthefly: true,
|
||||
name: "__AccompanyingCourseLocation__",
|
||||
address: {
|
||||
id: accPeriodLocation.address_id,
|
||||
text: `${accPeriodLocation.text} - ${accPeriodLocation.postcode.code} ${accPeriodLocation.postcode.name}`,
|
||||
},
|
||||
locationType: locationType,
|
||||
};
|
||||
};
|
||||
|
||||
export default function prepareLocations(store) {
|
||||
|
||||
// find the locations
|
||||
let allLocations = getLocations().then(
|
||||
(results) => {
|
||||
store.commit('addAvailableLocationGroup', {
|
||||
locationGroup: 'Autres localisations',
|
||||
locations: results
|
||||
let allLocations = getLocations().then((results) => {
|
||||
store.commit("addAvailableLocationGroup", {
|
||||
locationGroup: "Autres localisations",
|
||||
locations: results,
|
||||
});
|
||||
});
|
||||
|
||||
let currentLocation = getUserCurrentLocation().then((userCurrentLocation) => {
|
||||
if (null !== userCurrentLocation) {
|
||||
store.commit("addAvailableLocationGroup", {
|
||||
locationGroup: "Ma localisation",
|
||||
locations: [userCurrentLocation],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
let partiesLocations = [],
|
||||
partyPromise;
|
||||
["person", "thirdparty"].forEach((kind) => {
|
||||
partyPromise = getLocationTypeByDefaultFor(kind).then(
|
||||
(kindLocationType) => {
|
||||
if (kindLocationType) {
|
||||
let concernedKindLocations;
|
||||
if (kind === "person") {
|
||||
concernedKindLocations = makeConcernedPersonsLocation(
|
||||
kindLocationType,
|
||||
store,
|
||||
);
|
||||
// add location for the parcours into suggestions
|
||||
const personLocation = makeAccompanyingPeriodLocation(
|
||||
kindLocationType,
|
||||
store,
|
||||
);
|
||||
store.commit("addAvailableLocationGroup", {
|
||||
locationGroup: "Localisation du parcours",
|
||||
locations: [personLocation],
|
||||
});
|
||||
} else {
|
||||
concernedKindLocations = makeConcernedThirdPartiesLocation(
|
||||
kindLocationType,
|
||||
store,
|
||||
);
|
||||
}
|
||||
|
||||
store.commit("addAvailableLocationGroup", {
|
||||
locationGroup:
|
||||
kind === "person" ? "Usagers concernés" : "Tiers concernés",
|
||||
locations: concernedKindLocations,
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
partiesLocations.push(partyPromise);
|
||||
});
|
||||
|
||||
let currentLocation = getUserCurrentLocation().then(
|
||||
userCurrentLocation => {
|
||||
if (null !== userCurrentLocation) {
|
||||
store.commit('addAvailableLocationGroup', {
|
||||
locationGroup: 'Ma localisation',
|
||||
locations: [userCurrentLocation]
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
let partiesLocations = [], partyPromise;
|
||||
['person', 'thirdparty'].forEach(kind => {
|
||||
partyPromise = getLocationTypeByDefaultFor(kind).then(
|
||||
(kindLocationType) => {
|
||||
if (kindLocationType) {
|
||||
let concernedKindLocations;
|
||||
if (kind === 'person') {
|
||||
concernedKindLocations = makeConcernedPersonsLocation(kindLocationType, store);
|
||||
// add location for the parcours into suggestions
|
||||
const personLocation = makeAccompanyingPeriodLocation(kindLocationType, store);
|
||||
store.commit('addAvailableLocationGroup', {
|
||||
locationGroup: 'Localisation du parcours',
|
||||
locations: [personLocation]
|
||||
});
|
||||
} else {
|
||||
concernedKindLocations = makeConcernedThirdPartiesLocation(kindLocationType, store);
|
||||
}
|
||||
|
||||
store.commit('addAvailableLocationGroup', {
|
||||
locationGroup: kind === 'person' ? 'Usagers concernés' : 'Tiers concernés',
|
||||
locations: concernedKindLocations,
|
||||
});
|
||||
}
|
||||
}
|
||||
// when all location are loaded
|
||||
Promise.all([allLocations, currentLocation, ...partiesLocations]).then(() => {
|
||||
console.log("current location in activity", store.state.activity.location);
|
||||
console.log("default loation id", window.default_location_id);
|
||||
if (window.default_location_id) {
|
||||
for (let group of store.state.availableLocations) {
|
||||
let location = group.locations.find(
|
||||
(l) => l.id === window.default_location_id,
|
||||
);
|
||||
partiesLocations.push(partyPromise);
|
||||
});
|
||||
|
||||
// when all location are loaded
|
||||
Promise.all([allLocations, currentLocation, ...partiesLocations]).then(() => {
|
||||
console.log('current location in activity', store.state.activity.location);
|
||||
console.log('default loation id', window.default_location_id);
|
||||
if (window.default_location_id) {
|
||||
for (let group of store.state.availableLocations) {
|
||||
let location = group.locations.find((l) => l.id === window.default_location_id);
|
||||
if (location !== undefined && store.state.activity.location === null) {
|
||||
store.dispatch('updateLocation', location);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (location !== undefined && store.state.activity.location === null) {
|
||||
store.dispatch("updateLocation", location);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1,13 +1,18 @@
|
||||
// this file loads all assets from the Chill person bundle
|
||||
module.exports = function(encore, entries)
|
||||
{
|
||||
entries.push(__dirname + '/Resources/public/chill/index.js');
|
||||
module.exports = function (encore, entries) {
|
||||
entries.push(__dirname + "/Resources/public/chill/index.js");
|
||||
|
||||
encore.addAliases({
|
||||
ChillActivityAssets: __dirname + '/Resources/public'
|
||||
});
|
||||
encore.addAliases({
|
||||
ChillActivityAssets: __dirname + "/Resources/public",
|
||||
});
|
||||
|
||||
encore.addEntry('page_edit_activity', __dirname + '/Resources/public/page/edit_activity/index.scss');
|
||||
encore.addEntry(
|
||||
"page_edit_activity",
|
||||
__dirname + "/Resources/public/page/edit_activity/index.scss",
|
||||
);
|
||||
|
||||
encore.addEntry('vue_activity', __dirname + '/Resources/public/vuejs/Activity/index.js');
|
||||
encore.addEntry(
|
||||
"vue_activity",
|
||||
__dirname + "/Resources/public/vuejs/Activity/index.js",
|
||||
);
|
||||
};
|
||||
|
@ -1 +1 @@
|
||||
require('./chillbudget.scss');
|
||||
require("./chillbudget.scss");
|
||||
|
@ -1,9 +1,8 @@
|
||||
// this file loads all assets from the Chill budget bundle
|
||||
module.exports = function(encore, entries)
|
||||
{
|
||||
encore.addAliases({
|
||||
ChillBudgetAssets: __dirname + '/Resources/public'
|
||||
});
|
||||
module.exports = function (encore, entries) {
|
||||
encore.addAliases({
|
||||
ChillBudgetAssets: __dirname + "/Resources/public",
|
||||
});
|
||||
|
||||
encore.addEntry('page_budget', __dirname + '/Resources/public/page/index.js');
|
||||
encore.addEntry("page_budget", __dirname + "/Resources/public/page/index.js");
|
||||
};
|
||||
|
@ -1,2 +1,2 @@
|
||||
import './scss/badge.scss';
|
||||
import './scss/calendar-list.scss';
|
||||
import "./scss/badge.scss";
|
||||
import "./scss/calendar-list.scss";
|
||||
|
@ -1 +1 @@
|
||||
require('./scss/calendar.scss');
|
||||
require("./scss/calendar.scss");
|
||||
|
@ -1,14 +1,13 @@
|
||||
|
||||
import { createApp } from 'vue';
|
||||
import Answer from 'ChillCalendarAssets/vuejs/Invite/Answer';
|
||||
import { _createI18n } from 'ChillMainAssets/vuejs/_js/i18n';
|
||||
import { createApp } from "vue";
|
||||
import Answer from "ChillCalendarAssets/vuejs/Invite/Answer";
|
||||
import { _createI18n } from "ChillMainAssets/vuejs/_js/i18n";
|
||||
|
||||
const i18n = _createI18n({});
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function (e) {
|
||||
console.log('dom loaded answer');
|
||||
document.querySelectorAll('div[invite-answer]').forEach(function (el) {
|
||||
console.log('element found', el);
|
||||
document.addEventListener("DOMContentLoaded", function (e) {
|
||||
console.log("dom loaded answer");
|
||||
document.querySelectorAll("div[invite-answer]").forEach(function (el) {
|
||||
console.log("element found", el);
|
||||
|
||||
const app = createApp({
|
||||
components: {
|
||||
@ -18,14 +17,15 @@ document.addEventListener('DOMContentLoaded', function (e) {
|
||||
return {
|
||||
status: el.dataset.status,
|
||||
calendarId: Number.parseInt(el.dataset.calendarId),
|
||||
}
|
||||
};
|
||||
},
|
||||
template: '<answer :calendarId="calendarId" :status="status" @statusChanged="onStatusChanged"></answer>',
|
||||
template:
|
||||
'<answer :calendarId="calendarId" :status="status" @statusChanged="onStatusChanged"></answer>',
|
||||
methods: {
|
||||
onStatusChanged: function(newStatus) {
|
||||
onStatusChanged: function (newStatus) {
|
||||
this.$data.status = newStatus;
|
||||
},
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
app.use(i18n).mount(el);
|
||||
|
@ -1,67 +1,76 @@
|
||||
import {EventInput} from '@fullcalendar/core';
|
||||
import {DateTime, Location, User, UserAssociatedInterface} from '../../../ChillMainBundle/Resources/public/types' ;
|
||||
import {Person} from "../../../ChillPersonBundle/Resources/public/types";
|
||||
import { EventInput } from "@fullcalendar/core";
|
||||
import {
|
||||
DateTime,
|
||||
Location,
|
||||
User,
|
||||
UserAssociatedInterface,
|
||||
} from "../../../ChillMainBundle/Resources/public/types";
|
||||
import { Person } from "../../../ChillPersonBundle/Resources/public/types";
|
||||
|
||||
export interface CalendarRange {
|
||||
id: number;
|
||||
endDate: DateTime;
|
||||
startDate: DateTime;
|
||||
user: User;
|
||||
location: Location;
|
||||
createdAt: DateTime;
|
||||
createdBy: User;
|
||||
updatedAt: DateTime;
|
||||
updatedBy: User;
|
||||
id: number;
|
||||
endDate: DateTime;
|
||||
startDate: DateTime;
|
||||
user: User;
|
||||
location: Location;
|
||||
createdAt: DateTime;
|
||||
createdBy: User;
|
||||
updatedAt: DateTime;
|
||||
updatedBy: User;
|
||||
}
|
||||
|
||||
export interface CalendarRangeCreate {
|
||||
user: UserAssociatedInterface;
|
||||
startDate: DateTime;
|
||||
endDate: DateTime;
|
||||
location: Location;
|
||||
user: UserAssociatedInterface;
|
||||
startDate: DateTime;
|
||||
endDate: DateTime;
|
||||
location: Location;
|
||||
}
|
||||
|
||||
export interface CalendarRangeEdit {
|
||||
startDate?: DateTime,
|
||||
endDate?: DateTime
|
||||
location?: Location;
|
||||
startDate?: DateTime;
|
||||
endDate?: DateTime;
|
||||
location?: Location;
|
||||
}
|
||||
|
||||
export interface Calendar {
|
||||
id: number;
|
||||
id: number;
|
||||
}
|
||||
|
||||
export interface CalendarLight {
|
||||
id: number;
|
||||
endDate: DateTime;
|
||||
startDate: DateTime;
|
||||
mainUser: User;
|
||||
persons: Person[];
|
||||
status: "valid" | "moved" | "canceled";
|
||||
id: number;
|
||||
endDate: DateTime;
|
||||
startDate: DateTime;
|
||||
mainUser: User;
|
||||
persons: Person[];
|
||||
status: "valid" | "moved" | "canceled";
|
||||
}
|
||||
|
||||
export interface CalendarRemote {
|
||||
id: number;
|
||||
endDate: DateTime;
|
||||
startDate: DateTime;
|
||||
title: string;
|
||||
isAllDay: boolean;
|
||||
id: number;
|
||||
endDate: DateTime;
|
||||
startDate: DateTime;
|
||||
title: string;
|
||||
isAllDay: boolean;
|
||||
}
|
||||
|
||||
export type EventInputCalendarRange = EventInput & {
|
||||
id: string,
|
||||
userId: number,
|
||||
userLabel: string,
|
||||
calendarRangeId: number,
|
||||
locationId: number,
|
||||
locationName: string,
|
||||
start: string,
|
||||
end: string,
|
||||
is: "range"
|
||||
id: string;
|
||||
userId: number;
|
||||
userLabel: string;
|
||||
calendarRangeId: number;
|
||||
locationId: number;
|
||||
locationName: string;
|
||||
start: string;
|
||||
end: string;
|
||||
is: "range";
|
||||
};
|
||||
|
||||
export function isEventInputCalendarRange(toBeDetermined: EventInputCalendarRange | EventInput): toBeDetermined is EventInputCalendarRange {
|
||||
return typeof toBeDetermined.is === "string" && toBeDetermined.is === "range";
|
||||
export function isEventInputCalendarRange(
|
||||
toBeDetermined: EventInputCalendarRange | EventInput,
|
||||
): toBeDetermined is EventInputCalendarRange {
|
||||
return (
|
||||
typeof toBeDetermined.is === "string" && toBeDetermined.is === "range"
|
||||
);
|
||||
}
|
||||
|
||||
export {};
|
||||
|
@ -1,302 +1,393 @@
|
||||
<template>
|
||||
|
||||
<teleport to="#mainUser">
|
||||
|
||||
<h2 class="chill-red">Utilisateur principal</h2>
|
||||
<div>
|
||||
<div>
|
||||
<div v-if="null !== this.$store.getters.getMainUser">
|
||||
<calendar-active :user="this.$store.getters.getMainUser" ></calendar-active>
|
||||
<teleport to="#mainUser">
|
||||
<h2 class="chill-red">Utilisateur principal</h2>
|
||||
<div>
|
||||
<div>
|
||||
<div v-if="null !== this.$store.getters.getMainUser">
|
||||
<calendar-active :user="this.$store.getters.getMainUser" />
|
||||
</div>
|
||||
<pick-entity
|
||||
:multiple="false"
|
||||
:types="['user']"
|
||||
:uniqid="'main_user_calendar'"
|
||||
:picked="
|
||||
null !== this.$store.getters.getMainUser
|
||||
? [this.$store.getters.getMainUser]
|
||||
: []
|
||||
"
|
||||
:removable-if-set="false"
|
||||
:display-picked="false"
|
||||
:suggested="this.suggestedUsers"
|
||||
:label="'main_user'"
|
||||
@add-new-entity="setMainUser"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<pick-entity
|
||||
:multiple="false"
|
||||
:types="['user']"
|
||||
:uniqid="'main_user_calendar'"
|
||||
:picked="null !== this.$store.getters.getMainUser ? [this.$store.getters.getMainUser] : []"
|
||||
:removableIfSet="false"
|
||||
:displayPicked="false"
|
||||
:suggested="this.suggestedUsers"
|
||||
:label="'main_user'"
|
||||
@addNewEntity="setMainUser"
|
||||
></pick-entity>
|
||||
</div>
|
||||
</div>
|
||||
</teleport>
|
||||
</teleport>
|
||||
|
||||
<concerned-groups></concerned-groups>
|
||||
|
||||
<teleport to="#schedule">
|
||||
<div class="row mb-3" v-if="activity.startDate !== null">
|
||||
<label class="col-form-label col-sm-4">Date</label>
|
||||
<div class="col-sm-8">
|
||||
{{ $d(activity.startDate, 'long') }} - {{ $d(activity.endDate, 'hoursOnly') }}
|
||||
<span v-if="activity.calendarRange === null">(Pas de plage de disponibilité sélectionnée)</span>
|
||||
<span v-else>(Une plage de disponibilité sélectionnée)</span>
|
||||
</div>
|
||||
</div>
|
||||
</teleport>
|
||||
|
||||
<location></location>
|
||||
<concerned-groups />
|
||||
|
||||
<teleport to="#schedule">
|
||||
<div class="row mb-3" v-if="activity.startDate !== null">
|
||||
<label class="col-form-label col-sm-4">Date</label>
|
||||
<div class="col-sm-8">
|
||||
{{ $d(activity.startDate, "long") }} -
|
||||
{{ $d(activity.endDate, "hoursOnly") }}
|
||||
<span v-if="activity.calendarRange === null"
|
||||
>(Pas de plage de disponibilité sélectionnée)</span
|
||||
>
|
||||
<span v-else>(Une plage de disponibilité sélectionnée)</span>
|
||||
</div>
|
||||
</div>
|
||||
</teleport>
|
||||
|
||||
<location />
|
||||
|
||||
<teleport to="#fullCalendar">
|
||||
<div class="calendar-actives">
|
||||
<template class="" v-for="u in getActiveUsers" :key="u.id">
|
||||
<calendar-active :user="u" :invite="this.$store.getters.getInviteForUser(u)"></calendar-active>
|
||||
</template>
|
||||
</div>
|
||||
<div class="display-options row justify-content-between" style="margin-top: 1rem;">
|
||||
<div class="col-sm-9 col-xs-12">
|
||||
<div class="input-group mb-3">
|
||||
<label class="input-group-text" for="slotDuration">Durée des créneaux</label>
|
||||
<select v-model="slotDuration" id="slotDuration" class="form-select">
|
||||
<option value="00:05:00">5 minutes</option>
|
||||
<option value="00:10:00">10 minutes</option>
|
||||
<option value="00:15:00">15 minutes</option>
|
||||
<option value="00:30:00">30 minutes</option>
|
||||
</select>
|
||||
<label class="input-group-text" for="slotMinTime">De</label>
|
||||
<select v-model="slotMinTime" id="slotMinTime" class="form-select">
|
||||
<option value="00:00:00">0h</option>
|
||||
<option value="01:00:00">1h</option>
|
||||
<option value="02:00:00">2h</option>
|
||||
<option value="03:00:00">3h</option>
|
||||
<option value="04:00:00">4h</option>
|
||||
<option value="05:00:00">5h</option>
|
||||
<option value="06:00:00">6h</option>
|
||||
<option value="07:00:00">7h</option>
|
||||
<option value="08:00:00">8h</option>
|
||||
<option value="09:00:00">9h</option>
|
||||
<option value="10:00:00">10h</option>
|
||||
<option value="11:00:00">11h</option>
|
||||
<option value="12:00:00">12h</option>
|
||||
</select>
|
||||
<label class="input-group-text" for="slotMaxTime">À</label>
|
||||
<select v-model="slotMaxTime" id="slotMaxTime" class="form-select">
|
||||
<option value="12:00:00">12h</option>
|
||||
<option value="13:00:00">13h</option>
|
||||
<option value="14:00:00">14h</option>
|
||||
<option value="15:00:00">15h</option>
|
||||
<option value="16:00:00">16h</option>
|
||||
<option value="17:00:00">17h</option>
|
||||
<option value="18:00:00">18h</option>
|
||||
<option value="19:00:00">19h</option>
|
||||
<option value="20:00:00">20h</option>
|
||||
<option value="21:00:00">21h</option>
|
||||
<option value="22:00:00">22h</option>
|
||||
<option value="23:00:00">23h</option>
|
||||
<option value="23:59:59">24h</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="calendar-actives">
|
||||
<template v-for="u in getActiveUsers" :key="u.id">
|
||||
<calendar-active
|
||||
:user="u"
|
||||
:invite="this.$store.getters.getInviteForUser(u)"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12">
|
||||
<div class="float-end">
|
||||
<div class="form-check input-group">
|
||||
<span class="input-group-text">
|
||||
<input id="showHideWE" class="mt-0" type="checkbox" v-model="hideWeekends">
|
||||
</span>
|
||||
<label for="showHideWE" class="form-check-label input-group-text">Week-ends</label>
|
||||
<div
|
||||
class="display-options row justify-content-between"
|
||||
style="margin-top: 1rem"
|
||||
>
|
||||
<div class="col-sm-9 col-xs-12">
|
||||
<div class="input-group mb-3">
|
||||
<label class="input-group-text" for="slotDuration"
|
||||
>Durée des créneaux</label
|
||||
>
|
||||
<select
|
||||
v-model="slotDuration"
|
||||
id="slotDuration"
|
||||
class="form-select"
|
||||
>
|
||||
<option value="00:05:00">5 minutes</option>
|
||||
<option value="00:10:00">10 minutes</option>
|
||||
<option value="00:15:00">15 minutes</option>
|
||||
<option value="00:30:00">30 minutes</option>
|
||||
</select>
|
||||
<label class="input-group-text" for="slotMinTime">De</label>
|
||||
<select
|
||||
v-model="slotMinTime"
|
||||
id="slotMinTime"
|
||||
class="form-select"
|
||||
>
|
||||
<option value="00:00:00">0h</option>
|
||||
<option value="01:00:00">1h</option>
|
||||
<option value="02:00:00">2h</option>
|
||||
<option value="03:00:00">3h</option>
|
||||
<option value="04:00:00">4h</option>
|
||||
<option value="05:00:00">5h</option>
|
||||
<option value="06:00:00">6h</option>
|
||||
<option value="07:00:00">7h</option>
|
||||
<option value="08:00:00">8h</option>
|
||||
<option value="09:00:00">9h</option>
|
||||
<option value="10:00:00">10h</option>
|
||||
<option value="11:00:00">11h</option>
|
||||
<option value="12:00:00">12h</option>
|
||||
</select>
|
||||
<label class="input-group-text" for="slotMaxTime">À</label>
|
||||
<select
|
||||
v-model="slotMaxTime"
|
||||
id="slotMaxTime"
|
||||
class="form-select"
|
||||
>
|
||||
<option value="12:00:00">12h</option>
|
||||
<option value="13:00:00">13h</option>
|
||||
<option value="14:00:00">14h</option>
|
||||
<option value="15:00:00">15h</option>
|
||||
<option value="16:00:00">16h</option>
|
||||
<option value="17:00:00">17h</option>
|
||||
<option value="18:00:00">18h</option>
|
||||
<option value="19:00:00">19h</option>
|
||||
<option value="20:00:00">20h</option>
|
||||
<option value="21:00:00">21h</option>
|
||||
<option value="22:00:00">22h</option>
|
||||
<option value="23:00:00">23h</option>
|
||||
<option value="23:59:59">24h</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12">
|
||||
<div class="float-end">
|
||||
<div class="form-check input-group">
|
||||
<span class="input-group-text">
|
||||
<input
|
||||
id="showHideWE"
|
||||
class="mt-0"
|
||||
type="checkbox"
|
||||
v-model="hideWeekends"
|
||||
/>
|
||||
</span>
|
||||
<label
|
||||
for="showHideWE"
|
||||
class="form-check-label input-group-text"
|
||||
>Week-ends</label
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<FullCalendar ref="fullCalendar" :options="calendarOptions">
|
||||
<template v-slot:eventContent='arg'>
|
||||
<span>
|
||||
<b v-if="arg.event.extendedProps.is === 'remote'">{{ arg.event.title}}</b>
|
||||
<b v-else-if="arg.event.extendedProps.is === 'range'">{{ arg.timeText }} {{ arg.event.extendedProps.locationName }} <small>{{ arg.event.extendedProps.userLabel }}</small></b>
|
||||
<b v-else-if="arg.event.extendedProps.is === 'current'">{{ arg.timeText }} {{ $t('current_selected')}} </b>
|
||||
<b v-else-if="arg.event.extendedProps.is === 'local'">{{ arg.event.title}}</b>
|
||||
<b v-else>{{ arg.timeText }} {{ $t('current_selected')}} </b>
|
||||
</span>
|
||||
</template>
|
||||
</FullCalendar>
|
||||
<FullCalendar ref="fullCalendar" :options="calendarOptions">
|
||||
<template #eventContent="arg">
|
||||
<span>
|
||||
<b v-if="arg.event.extendedProps.is === 'remote'">{{
|
||||
arg.event.title
|
||||
}}</b>
|
||||
<b v-else-if="arg.event.extendedProps.is === 'range'"
|
||||
>{{ arg.timeText }}
|
||||
{{ arg.event.extendedProps.locationName }}
|
||||
<small>{{
|
||||
arg.event.extendedProps.userLabel
|
||||
}}</small></b
|
||||
>
|
||||
<b v-else-if="arg.event.extendedProps.is === 'current'"
|
||||
>{{ arg.timeText }} {{ $t("current_selected") }}
|
||||
</b>
|
||||
<b v-else-if="arg.event.extendedProps.is === 'local'">{{
|
||||
arg.event.title
|
||||
}}</b>
|
||||
<b v-else
|
||||
>{{ arg.timeText }} {{ $t("current_selected") }}
|
||||
</b>
|
||||
</span>
|
||||
</template>
|
||||
</FullCalendar>
|
||||
</teleport>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ConcernedGroups from 'ChillActivityAssets/vuejs/Activity/components/ConcernedGroups.vue';
|
||||
import Location from 'ChillActivityAssets/vuejs/Activity/components/Location.vue';
|
||||
import frLocale from '@fullcalendar/core/locales/fr';
|
||||
import FullCalendar from '@fullcalendar/vue3';
|
||||
import dayGridPlugin from '@fullcalendar/daygrid';
|
||||
import interactionPlugin from '@fullcalendar/interaction';
|
||||
import timeGridPlugin from '@fullcalendar/timegrid';
|
||||
import listPlugin from '@fullcalendar/list';
|
||||
import CalendarActive from './Components/CalendarActive';
|
||||
import PickEntity from 'ChillMainAssets/vuejs/PickEntity/PickEntity.vue';
|
||||
import {mapGetters, mapState} from "vuex";
|
||||
import ConcernedGroups from "ChillActivityAssets/vuejs/Activity/components/ConcernedGroups.vue";
|
||||
import Location from "ChillActivityAssets/vuejs/Activity/components/Location.vue";
|
||||
import frLocale from "@fullcalendar/core/locales/fr";
|
||||
import FullCalendar from "@fullcalendar/vue3";
|
||||
import dayGridPlugin from "@fullcalendar/daygrid";
|
||||
import interactionPlugin from "@fullcalendar/interaction";
|
||||
import timeGridPlugin from "@fullcalendar/timegrid";
|
||||
import listPlugin from "@fullcalendar/list";
|
||||
import CalendarActive from "./Components/CalendarActive";
|
||||
import PickEntity from "ChillMainAssets/vuejs/PickEntity/PickEntity.vue";
|
||||
import { mapGetters, mapState } from "vuex";
|
||||
|
||||
export default {
|
||||
name: "App",
|
||||
components: {
|
||||
ConcernedGroups,
|
||||
Location,
|
||||
FullCalendar,
|
||||
CalendarActive,
|
||||
PickEntity,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
errorMsg: [],
|
||||
showMyCalendar: false,
|
||||
slotDuration: '00:05:00',
|
||||
slotMinTime: '09:00:00',
|
||||
slotMaxTime: '18:00:00',
|
||||
hideWeekEnds: true,
|
||||
previousUser: [],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['getMainUser']),
|
||||
...mapState(['activity']),
|
||||
events() {
|
||||
return this.$store.getters.getEventSources;
|
||||
name: "App",
|
||||
components: {
|
||||
ConcernedGroups,
|
||||
Location,
|
||||
FullCalendar,
|
||||
CalendarActive,
|
||||
PickEntity,
|
||||
},
|
||||
calendarOptions() {
|
||||
return {
|
||||
locale: frLocale,
|
||||
plugins: [dayGridPlugin, interactionPlugin, timeGridPlugin, dayGridPlugin, listPlugin],
|
||||
initialView: 'timeGridWeek',
|
||||
initialDate: this.$store.getters.getInitialDate,
|
||||
eventSources: this.events,
|
||||
selectable: true,
|
||||
slotMinTime: this.slotMinTime,
|
||||
slotMaxTime: this.slotMaxTime,
|
||||
scrollTimeReset: false,
|
||||
datesSet: this.onDatesSet,
|
||||
select: this.onDateSelect,
|
||||
eventChange: this.onEventChange,
|
||||
eventClick: this.onEventClick,
|
||||
selectMirror: true,
|
||||
editable: true,
|
||||
weekends: !this.hideWeekEnds,
|
||||
headerToolbar: {
|
||||
left: 'prev,next today',
|
||||
center: 'title',
|
||||
right: 'timeGridWeek,timeGridDay,listWeek',
|
||||
data() {
|
||||
return {
|
||||
errorMsg: [],
|
||||
showMyCalendar: false,
|
||||
slotDuration: "00:05:00",
|
||||
slotMinTime: "09:00:00",
|
||||
slotMaxTime: "18:00:00",
|
||||
hideWeekEnds: true,
|
||||
previousUser: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(["getMainUser"]),
|
||||
...mapState(["activity"]),
|
||||
events() {
|
||||
return this.$store.getters.getEventSources;
|
||||
},
|
||||
views: {
|
||||
timeGrid: {
|
||||
slotEventOverlap: false,
|
||||
slotDuration: this.slotDuration,
|
||||
},
|
||||
calendarOptions() {
|
||||
return {
|
||||
locale: frLocale,
|
||||
plugins: [
|
||||
dayGridPlugin,
|
||||
interactionPlugin,
|
||||
timeGridPlugin,
|
||||
dayGridPlugin,
|
||||
listPlugin,
|
||||
],
|
||||
initialView: "timeGridWeek",
|
||||
initialDate: this.$store.getters.getInitialDate,
|
||||
eventSources: this.events,
|
||||
selectable: true,
|
||||
slotMinTime: this.slotMinTime,
|
||||
slotMaxTime: this.slotMaxTime,
|
||||
scrollTimeReset: false,
|
||||
datesSet: this.onDatesSet,
|
||||
select: this.onDateSelect,
|
||||
eventChange: this.onEventChange,
|
||||
eventClick: this.onEventClick,
|
||||
selectMirror: true,
|
||||
editable: true,
|
||||
weekends: !this.hideWeekEnds,
|
||||
headerToolbar: {
|
||||
left: "prev,next today",
|
||||
center: "title",
|
||||
right: "timeGridWeek,timeGridDay,listWeek",
|
||||
},
|
||||
views: {
|
||||
timeGrid: {
|
||||
slotEventOverlap: false,
|
||||
slotDuration: this.slotDuration,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
getActiveUsers() {
|
||||
const users = [];
|
||||
for (const id of this.$store.state.currentView.users.keys()) {
|
||||
users.push(this.$store.getters.getUserDataById(id).user);
|
||||
}
|
||||
return users;
|
||||
},
|
||||
suggestedUsers() {
|
||||
const suggested = [];
|
||||
|
||||
this.$data.previousUser.forEach((u) => {
|
||||
if (u.id !== this.$store.getters.getMainUser.id) {
|
||||
suggested.push(u);
|
||||
}
|
||||
});
|
||||
|
||||
return suggested;
|
||||
},
|
||||
};
|
||||
},
|
||||
getActiveUsers() {
|
||||
const users = [];
|
||||
for (const id of this.$store.state.currentView.users.keys()) {
|
||||
users.push(this.$store.getters.getUserDataById(id).user);
|
||||
}
|
||||
return users;
|
||||
methods: {
|
||||
setMainUser({ entity }) {
|
||||
const user = entity;
|
||||
console.log("setMainUser APP", entity);
|
||||
|
||||
if (
|
||||
user.id !== this.$store.getters.getMainUser &&
|
||||
(this.$store.state.activity.calendarRange !== null ||
|
||||
this.$store.state.activity.startDate !== null ||
|
||||
this.$store.state.activity.endDate !== null)
|
||||
) {
|
||||
if (
|
||||
!window.confirm(
|
||||
this.$t("change_main_user_will_reset_event_data"),
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// add the previous user, if any, in the previous user list (in use for suggestion)
|
||||
if (null !== this.$store.getters.getMainUser) {
|
||||
const suggestedUids = new Set(
|
||||
this.$data.previousUser.map((u) => u.id),
|
||||
);
|
||||
if (!suggestedUids.has(this.$store.getters.getMainUser.id)) {
|
||||
this.$data.previousUser.push(
|
||||
this.$store.getters.getMainUser,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
this.$store.dispatch("setMainUser", user);
|
||||
this.$store.commit("showUserOnCalendar", {
|
||||
user,
|
||||
ranges: true,
|
||||
remotes: true,
|
||||
});
|
||||
},
|
||||
removeMainUser(user) {
|
||||
console.log("removeMainUser APP", user);
|
||||
|
||||
window.alert(this.$t("main_user_is_mandatory"));
|
||||
return;
|
||||
},
|
||||
onDatesSet(event) {
|
||||
console.log("onDatesSet", event);
|
||||
this.$store.dispatch("setCurrentDatesView", {
|
||||
start: event.start,
|
||||
end: event.end,
|
||||
});
|
||||
},
|
||||
onDateSelect(payload) {
|
||||
console.log("onDateSelect", payload);
|
||||
|
||||
// show an alert if changing mainUser
|
||||
if (
|
||||
(this.$store.getters.getMainUser !== null &&
|
||||
this.$store.state.me.id !==
|
||||
this.$store.getters.getMainUser.id) ||
|
||||
this.$store.getters.getMainUser === null
|
||||
) {
|
||||
if (!window.confirm(this.$t("will_change_main_user_for_me"))) {
|
||||
return;
|
||||
} else {
|
||||
this.$store.commit("showUserOnCalendar", {
|
||||
user: this.$store.state.me,
|
||||
remotes: true,
|
||||
ranges: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.$store.dispatch("setEventTimes", {
|
||||
start: payload.start,
|
||||
end: payload.end,
|
||||
});
|
||||
},
|
||||
onEventChange(payload) {
|
||||
console.log("onEventChange", payload);
|
||||
if (this.$store.state.activity.calendarRange !== null) {
|
||||
throw new Error(
|
||||
"not allowed to edit a calendar associated with a calendar range",
|
||||
);
|
||||
}
|
||||
this.$store.dispatch("setEventTimes", {
|
||||
start: payload.event.start,
|
||||
end: payload.event.end,
|
||||
});
|
||||
},
|
||||
onEventClick(payload) {
|
||||
if (payload.event.extendedProps.is !== "range") {
|
||||
// do nothing when clicking on remote
|
||||
return;
|
||||
}
|
||||
|
||||
// show an alert if changing mainUser
|
||||
if (
|
||||
this.$store.getters.getMainUser !== null &&
|
||||
payload.event.extendedProps.userId !==
|
||||
this.$store.getters.getMainUser.id
|
||||
) {
|
||||
if (
|
||||
!window.confirm(
|
||||
this.$t("this_calendar_range_will_change_main_user"),
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.$store.dispatch("associateCalendarToRange", {
|
||||
range: payload.event,
|
||||
});
|
||||
},
|
||||
},
|
||||
suggestedUsers() {
|
||||
const suggested = [];
|
||||
|
||||
this.$data.previousUser.forEach(u => {
|
||||
if (u.id !== this.$store.getters.getMainUser.id) {
|
||||
suggested.push(u)
|
||||
}
|
||||
});
|
||||
|
||||
return suggested;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
setMainUser({entity}) {
|
||||
const user = entity;
|
||||
console.log('setMainUser APP', entity);
|
||||
|
||||
if (user.id !== this.$store.getters.getMainUser && (
|
||||
this.$store.state.activity.calendarRange !== null
|
||||
|| this.$store.state.activity.startDate !== null
|
||||
|| this.$store.state.activity.endDate !== null
|
||||
)
|
||||
) {
|
||||
if (!window.confirm(this.$t('change_main_user_will_reset_event_data'))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// add the previous user, if any, in the previous user list (in use for suggestion)
|
||||
if (null !== this.$store.getters.getMainUser) {
|
||||
const suggestedUids = new Set(this.$data.previousUser.map(u => u.id));
|
||||
if (!suggestedUids.has(this.$store.getters.getMainUser.id)){
|
||||
this.$data.previousUser.push(this.$store.getters.getMainUser);
|
||||
}
|
||||
}
|
||||
|
||||
this.$store.dispatch('setMainUser', user);
|
||||
this.$store.commit('showUserOnCalendar', {user, ranges: true, remotes: true});
|
||||
},
|
||||
removeMainUser(user) {
|
||||
console.log('removeMainUser APP', user);
|
||||
|
||||
window.alert(this.$t('main_user_is_mandatory'));
|
||||
return;
|
||||
},
|
||||
onDatesSet(event) {
|
||||
console.log('onDatesSet', event);
|
||||
this.$store.dispatch('setCurrentDatesView', {start: event.start, end: event.end});
|
||||
},
|
||||
onDateSelect(payload) {
|
||||
console.log('onDateSelect', payload);
|
||||
|
||||
// show an alert if changing mainUser
|
||||
if ((this.$store.getters.getMainUser !== null
|
||||
&& this.$store.state.me.id !== this.$store.getters.getMainUser.id)
|
||||
|| this.$store.getters.getMainUser === null) {
|
||||
if (!window.confirm(this.$t('will_change_main_user_for_me'))) {
|
||||
return;
|
||||
} else {
|
||||
this.$store.commit('showUserOnCalendar', {user: this.$store.state.me, remotes: true, ranges: true})
|
||||
}
|
||||
}
|
||||
|
||||
this.$store.dispatch('setEventTimes', {start: payload.start, end: payload.end});
|
||||
},
|
||||
onEventChange(payload) {
|
||||
console.log('onEventChange', payload);
|
||||
if (this.$store.state.activity.calendarRange !== null) {
|
||||
throw new Error("not allowed to edit a calendar associated with a calendar range");
|
||||
}
|
||||
this.$store.dispatch('setEventTimes', {start: payload.event.start, end: payload.event.end});
|
||||
},
|
||||
onEventClick(payload) {
|
||||
if (payload.event.extendedProps.is !== 'range') {
|
||||
// do nothing when clicking on remote
|
||||
return;
|
||||
}
|
||||
|
||||
// show an alert if changing mainUser
|
||||
if (this.$store.getters.getMainUser !== null
|
||||
&& payload.event.extendedProps.userId !== this.$store.getters.getMainUser.id) {
|
||||
if (!window.confirm(this.$t('this_calendar_range_will_change_main_user'))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.$store.dispatch('associateCalendarToRange', {range: payload.event});
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.calendar-actives {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.display-options {
|
||||
margin-top: 1rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
/* for events which are range */
|
||||
.fc-event.isrange {
|
||||
border-width: 3px;
|
||||
border-width: 3px;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,80 +1,119 @@
|
||||
<template>
|
||||
<div :style="style" class="calendar-active">
|
||||
<span class="badge-user">
|
||||
{{ user.text }}
|
||||
<template v-if="invite !== null">
|
||||
<i v-if="invite.status === 'accepted'" class="fa fa-check"></i>
|
||||
<i v-else-if="invite.status === 'declined'" class="fa fa-times"></i>
|
||||
<i v-else-if="invite.status === 'pending'" class="fa fa-question-o"></i>
|
||||
<i v-else-if="invite.status === 'tentative'" class="fa fa-question"></i>
|
||||
<span v-else="">{{ invite.status }}</span>
|
||||
</template>
|
||||
</span>
|
||||
<span class="form-check-inline form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="flexSwitchCheckDefault" v-model="rangeShow">
|
||||
<label class="form-check-label" for="flexSwitchCheckDefault" title="Disponibilités"><i class="fa fa-calendar-check-o"></i></label>
|
||||
</span>
|
||||
<span class="form-check-inline form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="flexSwitchCheckDefault" v-model="remoteShow">
|
||||
<label class="form-check-label" for="flexSwitchCheckDefault" title="Agenda"><i class="fa fa-calendar"></i></label>
|
||||
</span>
|
||||
</div>
|
||||
<div :style="style" class="calendar-active">
|
||||
<span class="badge-user">
|
||||
{{ user.text }}
|
||||
<template v-if="invite !== null">
|
||||
<i v-if="invite.status === 'accepted'" class="fa fa-check" />
|
||||
<i
|
||||
v-else-if="invite.status === 'declined'"
|
||||
class="fa fa-times"
|
||||
/>
|
||||
<i
|
||||
v-else-if="invite.status === 'pending'"
|
||||
class="fa fa-question-o"
|
||||
/>
|
||||
<i
|
||||
v-else-if="invite.status === 'tentative'"
|
||||
class="fa fa-question"
|
||||
/>
|
||||
<span v-else="">{{ invite.status }}</span>
|
||||
</template>
|
||||
</span>
|
||||
<span class="form-check-inline form-switch">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
id="flexSwitchCheckDefault"
|
||||
v-model="rangeShow"
|
||||
/>
|
||||
<label
|
||||
class="form-check-label"
|
||||
for="flexSwitchCheckDefault"
|
||||
title="Disponibilités"
|
||||
><i class="fa fa-calendar-check-o"
|
||||
/></label>
|
||||
</span>
|
||||
<span class="form-check-inline form-switch">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
id="flexSwitchCheckDefault"
|
||||
v-model="remoteShow"
|
||||
/>
|
||||
<label
|
||||
class="form-check-label"
|
||||
for="flexSwitchCheckDefault"
|
||||
title="Agenda"
|
||||
><i class="fa fa-calendar"
|
||||
/></label>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapGetters} from 'vuex';
|
||||
import { mapGetters } from "vuex";
|
||||
|
||||
export default {
|
||||
name: "CalendarActive",
|
||||
props: {
|
||||
user: {
|
||||
type: Object,
|
||||
required: true
|
||||
name: "CalendarActive",
|
||||
props: {
|
||||
user: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
invite: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
invite: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: null,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
style() {
|
||||
return {
|
||||
backgroundColor: this.$store.getters.getUserData(this.user).mainColor,
|
||||
};
|
||||
computed: {
|
||||
style() {
|
||||
return {
|
||||
backgroundColor: this.$store.getters.getUserData(this.user)
|
||||
.mainColor,
|
||||
};
|
||||
},
|
||||
rangeShow: {
|
||||
set(value) {
|
||||
this.$store.commit("showUserOnCalendar", {
|
||||
user: this.user,
|
||||
ranges: value,
|
||||
});
|
||||
},
|
||||
get() {
|
||||
return this.$store.getters.isRangeShownOnCalendarForUser(
|
||||
this.user,
|
||||
);
|
||||
},
|
||||
},
|
||||
remoteShow: {
|
||||
set(value) {
|
||||
this.$store.commit("showUserOnCalendar", {
|
||||
user: this.user,
|
||||
remotes: value,
|
||||
});
|
||||
},
|
||||
get() {
|
||||
return this.$store.getters.isRemoteShownOnCalendarForUser(
|
||||
this.user,
|
||||
);
|
||||
},
|
||||
},
|
||||
},
|
||||
rangeShow: {
|
||||
set (value) {
|
||||
this.$store.commit('showUserOnCalendar', {user: this.user, ranges: value});
|
||||
},
|
||||
get() {
|
||||
return this.$store.getters.isRangeShownOnCalendarForUser(this.user);
|
||||
}
|
||||
},
|
||||
remoteShow: {
|
||||
set (value) {
|
||||
this.$store.commit('showUserOnCalendar', {user: this.user, remotes: value});
|
||||
},
|
||||
get() {
|
||||
return this.$store.getters.isRemoteShownOnCalendarForUser(this.user);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
.calendar-active {
|
||||
margin: 0 0.25rem 0.25rem 0;
|
||||
padding: 0.5rem;
|
||||
margin: 0 0.25rem 0.25rem 0;
|
||||
padding: 0.5rem;
|
||||
|
||||
border-radius: 0.5rem;
|
||||
border-radius: 0.5rem;
|
||||
|
||||
color: var(--bs-blue);
|
||||
color: var(--bs-blue);
|
||||
|
||||
& > .badge-user {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
& > .badge-user {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -1,10 +1,10 @@
|
||||
import {fetchResults} from '../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods';
|
||||
import {datetimeToISO} from '../../../../../ChillMainBundle/Resources/public/chill/js/date';
|
||||
import {User} from '../../../../../ChillMainBundle/Resources/public/types';
|
||||
import {CalendarLight, CalendarRange, CalendarRemote} from '../../types';
|
||||
import { fetchResults } from "../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods";
|
||||
import { datetimeToISO } from "../../../../../ChillMainBundle/Resources/public/chill/js/date";
|
||||
import { User } from "../../../../../ChillMainBundle/Resources/public/types";
|
||||
import { CalendarLight, CalendarRange, CalendarRemote } from "../../types";
|
||||
|
||||
// re-export whoami
|
||||
export {whoami} from "../../../../../ChillMainBundle/Resources/public/lib/api/user";
|
||||
export { whoami } from "../../../../../ChillMainBundle/Resources/public/lib/api/user";
|
||||
|
||||
/**
|
||||
*
|
||||
@ -13,26 +13,38 @@ export {whoami} from "../../../../../ChillMainBundle/Resources/public/lib/api/us
|
||||
* @param Date end
|
||||
* @return Promise
|
||||
*/
|
||||
export const fetchCalendarRangeForUser = (user: User, start: Date, end: Date): Promise<CalendarRange[]> => {
|
||||
const uri = `/api/1.0/calendar/calendar-range-available/${user.id}.json`;
|
||||
const dateFrom = datetimeToISO(start);
|
||||
const dateTo = datetimeToISO(end);
|
||||
export const fetchCalendarRangeForUser = (
|
||||
user: User,
|
||||
start: Date,
|
||||
end: Date,
|
||||
): Promise<CalendarRange[]> => {
|
||||
const uri = `/api/1.0/calendar/calendar-range-available/${user.id}.json`;
|
||||
const dateFrom = datetimeToISO(start);
|
||||
const dateTo = datetimeToISO(end);
|
||||
|
||||
return fetchResults<CalendarRange>(uri, {dateFrom, dateTo});
|
||||
}
|
||||
return fetchResults<CalendarRange>(uri, { dateFrom, dateTo });
|
||||
};
|
||||
|
||||
export const fetchCalendarRemoteForUser = (user: User, start: Date, end: Date): Promise<CalendarRemote[]> => {
|
||||
const uri = `/api/1.0/calendar/proxy/calendar/by-user/${user.id}/events`;
|
||||
const dateFrom = datetimeToISO(start);
|
||||
const dateTo = datetimeToISO(end);
|
||||
export const fetchCalendarRemoteForUser = (
|
||||
user: User,
|
||||
start: Date,
|
||||
end: Date,
|
||||
): Promise<CalendarRemote[]> => {
|
||||
const uri = `/api/1.0/calendar/proxy/calendar/by-user/${user.id}/events`;
|
||||
const dateFrom = datetimeToISO(start);
|
||||
const dateTo = datetimeToISO(end);
|
||||
|
||||
return fetchResults<CalendarRemote>(uri, {dateFrom, dateTo});
|
||||
}
|
||||
return fetchResults<CalendarRemote>(uri, { dateFrom, dateTo });
|
||||
};
|
||||
|
||||
export const fetchCalendarLocalForUser = (user: User, start: Date, end: Date): Promise<CalendarLight[]> => {
|
||||
const uri = `/api/1.0/calendar/calendar/by-user/${user.id}.json`;
|
||||
const dateFrom = datetimeToISO(start);
|
||||
const dateTo = datetimeToISO(end);
|
||||
export const fetchCalendarLocalForUser = (
|
||||
user: User,
|
||||
start: Date,
|
||||
end: Date,
|
||||
): Promise<CalendarLight[]> => {
|
||||
const uri = `/api/1.0/calendar/calendar/by-user/${user.id}.json`;
|
||||
const dateFrom = datetimeToISO(start);
|
||||
const dateTo = datetimeToISO(end);
|
||||
|
||||
return fetchResults<CalendarLight>(uri, {dateFrom, dateTo});
|
||||
}
|
||||
return fetchResults<CalendarLight>(uri, { dateFrom, dateTo });
|
||||
};
|
||||
|
@ -1,19 +1,17 @@
|
||||
|
||||
const COLORS = [ /* from https://colorbrewer2.org/#type=qualitative&scheme=Set3&n=12 */
|
||||
'#8dd3c7',
|
||||
'#ffffb3',
|
||||
'#bebada',
|
||||
'#fb8072',
|
||||
'#80b1d3',
|
||||
'#fdb462',
|
||||
'#b3de69',
|
||||
'#fccde5',
|
||||
'#d9d9d9',
|
||||
'#bc80bd',
|
||||
'#ccebc5',
|
||||
'#ffed6f'
|
||||
const COLORS = [
|
||||
/* from https://colorbrewer2.org/#type=qualitative&scheme=Set3&n=12 */
|
||||
"#8dd3c7",
|
||||
"#ffffb3",
|
||||
"#bebada",
|
||||
"#fb8072",
|
||||
"#80b1d3",
|
||||
"#fdb462",
|
||||
"#b3de69",
|
||||
"#fccde5",
|
||||
"#d9d9d9",
|
||||
"#bc80bd",
|
||||
"#ccebc5",
|
||||
"#ffed6f",
|
||||
];
|
||||
|
||||
export {
|
||||
COLORS,
|
||||
};
|
||||
export { COLORS };
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {personMessages} from 'ChillPersonAssets/vuejs/_js/i18n'
|
||||
import {calendarUserSelectorMessages} from '../_components/CalendarUserSelector/js/i18n';
|
||||
import {activityMessages} from 'ChillActivityAssets/vuejs/Activity/i18n';
|
||||
import { personMessages } from "ChillPersonAssets/vuejs/_js/i18n";
|
||||
import { calendarUserSelectorMessages } from "../_components/CalendarUserSelector/js/i18n";
|
||||
import { activityMessages } from "ChillActivityAssets/vuejs/Activity/i18n";
|
||||
|
||||
const appMessages = {
|
||||
fr: {
|
||||
@ -13,20 +13,22 @@ const appMessages = {
|
||||
bloc_thirdparty: "Tiers professionnels",
|
||||
bloc_users: "T(M)S",
|
||||
},
|
||||
this_calendar_range_will_change_main_user: "Cette plage de disponibilité n'est pas celle de l'utilisateur principal. Si vous continuez, l'utilisateur principal sera adapté. Êtes-vous sûr·e ?",
|
||||
will_change_main_user_for_me: "Vous ne pouvez pas écrire dans le calendrier d'un autre utilisateur. Voulez-vous être l'utilisateur principal de ce rendez-vous ?",
|
||||
main_user_is_mandatory: "L'utilisateur principal est requis. Vous pouvez le modifier, mais pas le supprimer",
|
||||
change_main_user_will_reset_event_data: "Modifier l'utilisateur principal nécessite de choisir une autre plage de disponibilité ou un autre horaire. Ces informations seront perdues. Êtes-vous sûr·e de vouloir continuer ?",
|
||||
list_three_days: 'Liste 3 jours',
|
||||
current_selected: 'Rendez-vous fixé',
|
||||
this_calendar_range_will_change_main_user:
|
||||
"Cette plage de disponibilité n'est pas celle de l'utilisateur principal. Si vous continuez, l'utilisateur principal sera adapté. Êtes-vous sûr·e ?",
|
||||
will_change_main_user_for_me:
|
||||
"Vous ne pouvez pas écrire dans le calendrier d'un autre utilisateur. Voulez-vous être l'utilisateur principal de ce rendez-vous ?",
|
||||
main_user_is_mandatory:
|
||||
"L'utilisateur principal est requis. Vous pouvez le modifier, mais pas le supprimer",
|
||||
change_main_user_will_reset_event_data:
|
||||
"Modifier l'utilisateur principal nécessite de choisir une autre plage de disponibilité ou un autre horaire. Ces informations seront perdues. Êtes-vous sûr·e de vouloir continuer ?",
|
||||
list_three_days: "Liste 3 jours",
|
||||
current_selected: "Rendez-vous fixé",
|
||||
main_user: "Utilisateur principal",
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
Object.assign(appMessages.fr, personMessages.fr);
|
||||
Object.assign(appMessages.fr, calendarUserSelectorMessages.fr);
|
||||
Object.assign(appMessages.fr, activityMessages.fr);
|
||||
|
||||
export {
|
||||
appMessages
|
||||
};
|
||||
export { appMessages };
|
||||
|
@ -1,16 +1,16 @@
|
||||
import { createApp } from 'vue';
|
||||
import { _createI18n } from 'ChillMainAssets/vuejs/_js/i18n'
|
||||
import { appMessages } from './i18n'
|
||||
import store from './store'
|
||||
import { createApp } from "vue";
|
||||
import { _createI18n } from "ChillMainAssets/vuejs/_js/i18n";
|
||||
import { appMessages } from "./i18n";
|
||||
import store from "./store";
|
||||
|
||||
import App from './App.vue';
|
||||
import App from "./App.vue";
|
||||
|
||||
const i18n = _createI18n(appMessages);
|
||||
|
||||
const app = createApp({
|
||||
template: `<app></app>`,
|
||||
template: `<app></app>`,
|
||||
})
|
||||
.use(store)
|
||||
.use(i18n)
|
||||
.component('app', App)
|
||||
.mount('#calendar');
|
||||
.use(store)
|
||||
.use(i18n)
|
||||
.component("app", App)
|
||||
.mount("#calendar");
|
||||
|
@ -1,14 +1,11 @@
|
||||
import {
|
||||
addIdToValue,
|
||||
removeIdFromValue,
|
||||
} from './utils';
|
||||
import { addIdToValue, removeIdFromValue } from "./utils";
|
||||
import {
|
||||
fetchCalendarRangeForUser,
|
||||
fetchCalendarRemoteForUser,
|
||||
fetchCalendarLocalForUser,
|
||||
} from './../api';
|
||||
import {datetimeToISO} from 'ChillMainAssets/chill/js/date';
|
||||
import {postLocation} from 'ChillActivityAssets/vuejs/Activity/api';
|
||||
} from "./../api";
|
||||
import { datetimeToISO } from "ChillMainAssets/chill/js/date";
|
||||
import { postLocation } from "ChillActivityAssets/vuejs/Activity/api";
|
||||
|
||||
/**
|
||||
* This will store a unique key for each value, and prevent to launch the same
|
||||
@ -24,12 +21,12 @@ import {postLocation} from 'ChillActivityAssets/vuejs/Activity/api';
|
||||
const fetchings = new Set();
|
||||
|
||||
export default {
|
||||
setCurrentDatesView({commit, dispatch}, {start, end}) {
|
||||
commit('setCurrentDatesView', {start, end});
|
||||
setCurrentDatesView({ commit, dispatch }, { start, end }) {
|
||||
commit("setCurrentDatesView", { start, end });
|
||||
|
||||
return dispatch('fetchCalendarEvents');
|
||||
return dispatch("fetchCalendarEvents");
|
||||
},
|
||||
fetchCalendarEvents({state, getters, dispatch}) {
|
||||
fetchCalendarEvents({ state, getters, dispatch }) {
|
||||
if (state.currentView.start === null && state.currentView.end === null) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
@ -39,101 +36,124 @@ export default {
|
||||
let unique = `${uid}, ${state.currentView.start.toISOString()}, ${state.currentView.end.toISOString()}`;
|
||||
|
||||
if (fetchings.has(unique)) {
|
||||
console.log('prevent from fetching for a user', unique);
|
||||
console.log("prevent from fetching for a user", unique);
|
||||
continue;
|
||||
}
|
||||
|
||||
fetchings.add(unique);
|
||||
|
||||
promises.push(
|
||||
dispatch(
|
||||
'fetchCalendarRangeForUser',
|
||||
{user: state.usersData.get(uid).user, start: state.currentView.start, end: state.currentView.end}
|
||||
)
|
||||
dispatch("fetchCalendarRangeForUser", {
|
||||
user: state.usersData.get(uid).user,
|
||||
start: state.currentView.start,
|
||||
end: state.currentView.end,
|
||||
}),
|
||||
);
|
||||
promises.push(
|
||||
dispatch(
|
||||
'fetchCalendarRemotesForUser',
|
||||
{user: state.usersData.get(uid).user, start: state.currentView.start, end: state.currentView.end}
|
||||
)
|
||||
dispatch("fetchCalendarRemotesForUser", {
|
||||
user: state.usersData.get(uid).user,
|
||||
start: state.currentView.start,
|
||||
end: state.currentView.end,
|
||||
}),
|
||||
);
|
||||
promises.push(
|
||||
dispatch(
|
||||
'fetchCalendarLocalsForUser',
|
||||
{user: state.usersData.get(uid).user, start: state.currentView.start, end: state.currentView.end}
|
||||
)
|
||||
dispatch("fetchCalendarLocalsForUser", {
|
||||
user: state.usersData.get(uid).user,
|
||||
start: state.currentView.start,
|
||||
end: state.currentView.end,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return Promise.all(promises);
|
||||
},
|
||||
fetchCalendarRangeForUser({commit, getters}, {user, start, end}) {
|
||||
if (!getters.isCalendarRangeLoadedForUser({user, start, end})) {
|
||||
fetchCalendarRangeForUser({ commit, getters }, { user, start, end }) {
|
||||
if (!getters.isCalendarRangeLoadedForUser({ user, start, end })) {
|
||||
return fetchCalendarRangeForUser(user, start, end).then((ranges) => {
|
||||
commit('addCalendarRangesForUser', {user, ranges, start, end});
|
||||
commit("addCalendarRangesForUser", { user, ranges, start, end });
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
}
|
||||
},
|
||||
fetchCalendarRemotesForUser({commit, getters}, {user, start, end}) {
|
||||
if (!getters.isCalendarRemoteLoadedForUser({user, start, end})) {
|
||||
fetchCalendarRemotesForUser({ commit, getters }, { user, start, end }) {
|
||||
if (!getters.isCalendarRemoteLoadedForUser({ user, start, end })) {
|
||||
return fetchCalendarRemoteForUser(user, start, end).then((remotes) => {
|
||||
commit('addCalendarRemotesForUser', {user, remotes, start, end});
|
||||
commit("addCalendarRemotesForUser", { user, remotes, start, end });
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
}
|
||||
},
|
||||
fetchCalendarLocalsForUser({commit, getters}, {user, start, end}) {
|
||||
if (!getters.isCalendarRemoteLoadedForUser({user, start, end})) {
|
||||
fetchCalendarLocalsForUser({ commit, getters }, { user, start, end }) {
|
||||
if (!getters.isCalendarRemoteLoadedForUser({ user, start, end })) {
|
||||
return fetchCalendarLocalForUser(user, start, end).then((locals) => {
|
||||
commit('addCalendarLocalsForUser', {user, locals, start, end});
|
||||
commit("addCalendarLocalsForUser", { user, locals, start, end });
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
}
|
||||
},
|
||||
addPersonsInvolved({commit, dispatch}, payload) {
|
||||
console.log('### action addPersonsInvolved', payload.result.type);
|
||||
console.log('### action addPersonsInvolved payload result', payload.result);
|
||||
addPersonsInvolved({ commit, dispatch }, payload) {
|
||||
console.log("### action addPersonsInvolved", payload.result.type);
|
||||
console.log("### action addPersonsInvolved payload result", payload.result);
|
||||
switch (payload.result.type) {
|
||||
case 'person':
|
||||
let aPersons = document.getElementById("chill_activitybundle_activity_persons");
|
||||
case "person":
|
||||
let aPersons = document.getElementById(
|
||||
"chill_activitybundle_activity_persons",
|
||||
);
|
||||
aPersons.value = addIdToValue(aPersons.value, payload.result.id);
|
||||
break;
|
||||
case 'thirdparty':
|
||||
let aThirdParties = document.getElementById("chill_activitybundle_activity_professionals");
|
||||
aThirdParties.value = addIdToValue(aThirdParties.value, payload.result.id);
|
||||
case "thirdparty":
|
||||
let aThirdParties = document.getElementById(
|
||||
"chill_activitybundle_activity_professionals",
|
||||
);
|
||||
aThirdParties.value = addIdToValue(
|
||||
aThirdParties.value,
|
||||
payload.result.id,
|
||||
);
|
||||
break;
|
||||
case 'user':
|
||||
let aUsers = document.getElementById("chill_activitybundle_activity_users");
|
||||
case "user":
|
||||
let aUsers = document.getElementById(
|
||||
"chill_activitybundle_activity_users",
|
||||
);
|
||||
aUsers.value = addIdToValue(aUsers.value, payload.result.id);
|
||||
commit('showUserOnCalendar', {user: payload.result, ranges: false, remotes: true});
|
||||
dispatch('fetchCalendarEvents');
|
||||
commit("showUserOnCalendar", {
|
||||
user: payload.result,
|
||||
ranges: false,
|
||||
remotes: true,
|
||||
});
|
||||
dispatch("fetchCalendarEvents");
|
||||
break;
|
||||
}
|
||||
;
|
||||
commit('addPersonsInvolved', payload);
|
||||
commit("addPersonsInvolved", payload);
|
||||
},
|
||||
removePersonInvolved({commit}, payload) {
|
||||
removePersonInvolved({ commit }, payload) {
|
||||
//console.log('### action removePersonInvolved', payload);
|
||||
switch (payload.type) {
|
||||
case 'person':
|
||||
let aPersons = document.getElementById("chill_activitybundle_activity_persons");
|
||||
case "person":
|
||||
let aPersons = document.getElementById(
|
||||
"chill_activitybundle_activity_persons",
|
||||
);
|
||||
aPersons.value = removeIdFromValue(aPersons.value, payload.id);
|
||||
break;
|
||||
case 'thirdparty':
|
||||
let aThirdParties = document.getElementById("chill_activitybundle_activity_professionals");
|
||||
aThirdParties.value = removeIdFromValue(aThirdParties.value, payload.id);
|
||||
case "thirdparty":
|
||||
let aThirdParties = document.getElementById(
|
||||
"chill_activitybundle_activity_professionals",
|
||||
);
|
||||
aThirdParties.value = removeIdFromValue(
|
||||
aThirdParties.value,
|
||||
payload.id,
|
||||
);
|
||||
break;
|
||||
case 'user':
|
||||
let aUsers = document.getElementById("chill_activitybundle_activity_users");
|
||||
case "user":
|
||||
let aUsers = document.getElementById(
|
||||
"chill_activitybundle_activity_users",
|
||||
);
|
||||
aUsers.value = removeIdFromValue(aUsers.value, payload.id);
|
||||
break;
|
||||
}
|
||||
;
|
||||
commit('removePersonInvolved', payload);
|
||||
commit("removePersonInvolved", payload);
|
||||
},
|
||||
|
||||
// Calendar
|
||||
@ -148,31 +168,49 @@ export default {
|
||||
* @param start
|
||||
* @param end
|
||||
*/
|
||||
setEventTimes({commit, state, getters}, {start, end}) {
|
||||
console.log('### action createEvent', {start, end});
|
||||
let startDateInput = document.getElementById("chill_activitybundle_activity_startDate");
|
||||
startDateInput.value = null !== start ? datetimeToISO(start) : '';
|
||||
let endDateInput = document.getElementById("chill_activitybundle_activity_endDate");
|
||||
endDateInput.value = null !== end ? datetimeToISO(end) : '';
|
||||
let calendarRangeInput = document.getElementById("chill_activitybundle_activity_calendarRange");
|
||||
setEventTimes({ commit, state, getters }, { start, end }) {
|
||||
console.log("### action createEvent", { start, end });
|
||||
let startDateInput = document.getElementById(
|
||||
"chill_activitybundle_activity_startDate",
|
||||
);
|
||||
startDateInput.value = null !== start ? datetimeToISO(start) : "";
|
||||
let endDateInput = document.getElementById(
|
||||
"chill_activitybundle_activity_endDate",
|
||||
);
|
||||
endDateInput.value = null !== end ? datetimeToISO(end) : "";
|
||||
let calendarRangeInput = document.getElementById(
|
||||
"chill_activitybundle_activity_calendarRange",
|
||||
);
|
||||
calendarRangeInput.value = "";
|
||||
|
||||
if (getters.getMainUser === null || getters.getMainUser.id !== state.me.id) {
|
||||
let mainUserInput = document.getElementById("chill_activitybundle_activity_mainUser");
|
||||
if (
|
||||
getters.getMainUser === null ||
|
||||
getters.getMainUser.id !== state.me.id
|
||||
) {
|
||||
let mainUserInput = document.getElementById(
|
||||
"chill_activitybundle_activity_mainUser",
|
||||
);
|
||||
mainUserInput.value = state.me.id;
|
||||
commit('setMainUser', state.me);
|
||||
commit("setMainUser", state.me);
|
||||
}
|
||||
|
||||
commit('setEventTimes', {start, end});
|
||||
commit("setEventTimes", { start, end });
|
||||
},
|
||||
associateCalendarToRange({state, commit, dispatch, getters}, {range}) {
|
||||
console.log('### action associateCAlendarToRange', range);
|
||||
let startDateInput = document.getElementById("chill_activitybundle_activity_startDate");
|
||||
associateCalendarToRange({ state, commit, dispatch, getters }, { range }) {
|
||||
console.log("### action associateCAlendarToRange", range);
|
||||
let startDateInput = document.getElementById(
|
||||
"chill_activitybundle_activity_startDate",
|
||||
);
|
||||
startDateInput.value = null !== range ? datetimeToISO(range.start) : "";
|
||||
let endDateInput = document.getElementById("chill_activitybundle_activity_endDate");
|
||||
let endDateInput = document.getElementById(
|
||||
"chill_activitybundle_activity_endDate",
|
||||
);
|
||||
endDateInput.value = null !== range ? datetimeToISO(range.end) : "";
|
||||
let calendarRangeInput = document.getElementById("chill_activitybundle_activity_calendarRange");
|
||||
calendarRangeInput.value = null !== range ? Number(range.extendedProps.calendarRangeId) : "";
|
||||
let calendarRangeInput = document.getElementById(
|
||||
"chill_activitybundle_activity_calendarRange",
|
||||
);
|
||||
calendarRangeInput.value =
|
||||
null !== range ? Number(range.extendedProps.calendarRangeId) : "";
|
||||
|
||||
if (null !== range) {
|
||||
let location = getters.getLocationById(range.extendedProps.locationId);
|
||||
@ -181,63 +219,68 @@ export default {
|
||||
console.error("location not found!", range.extendedProps.locationId);
|
||||
}
|
||||
|
||||
dispatch('updateLocation', location);
|
||||
dispatch("updateLocation", location);
|
||||
|
||||
const userId = range.extendedProps.userId;
|
||||
if (state.activity.mainUser !== null && state.activity.mainUser.id !== userId) {
|
||||
dispatch('setMainUser', state.usersData.get(userId).user);
|
||||
if (
|
||||
state.activity.mainUser !== null &&
|
||||
state.activity.mainUser.id !== userId
|
||||
) {
|
||||
dispatch("setMainUser", state.usersData.get(userId).user);
|
||||
|
||||
// TODO: remove persons involved with this user
|
||||
}
|
||||
}
|
||||
|
||||
commit('associateCalendarToRange', {range});
|
||||
commit("associateCalendarToRange", { range });
|
||||
return Promise.resolve();
|
||||
},
|
||||
setMainUser({commit, dispatch, state}, mainUser) {
|
||||
console.log('setMainUser', mainUser);
|
||||
setMainUser({ commit, dispatch, state }, mainUser) {
|
||||
console.log("setMainUser", mainUser);
|
||||
|
||||
let mainUserInput = document.getElementById("chill_activitybundle_activity_mainUser");
|
||||
let mainUserInput = document.getElementById(
|
||||
"chill_activitybundle_activity_mainUser",
|
||||
);
|
||||
mainUserInput.value = Number(mainUser.id);
|
||||
|
||||
return dispatch('associateCalendarToRange', { range: null }).then(() => {
|
||||
commit('setMainUser', mainUser);
|
||||
return dispatch("associateCalendarToRange", { range: null }).then(() => {
|
||||
commit("setMainUser", mainUser);
|
||||
|
||||
return dispatch('fetchCalendarEvents');
|
||||
return dispatch("fetchCalendarEvents");
|
||||
});
|
||||
},
|
||||
|
||||
// Location
|
||||
updateLocation({commit}, value) {
|
||||
console.log('### action: updateLocation', value);
|
||||
let hiddenLocation = document.getElementById("chill_activitybundle_activity_location");
|
||||
updateLocation({ commit }, value) {
|
||||
console.log("### action: updateLocation", value);
|
||||
let hiddenLocation = document.getElementById(
|
||||
"chill_activitybundle_activity_location",
|
||||
);
|
||||
if (value.onthefly) {
|
||||
const body = {
|
||||
"type": "location",
|
||||
"name": value.name === '__AccompanyingCourseLocation__' ? null : value.name,
|
||||
"locationType": {
|
||||
"id": value.locationType.id,
|
||||
"type": "location-type"
|
||||
}
|
||||
type: "location",
|
||||
name:
|
||||
value.name === "__AccompanyingCourseLocation__" ? null : value.name,
|
||||
locationType: {
|
||||
id: value.locationType.id,
|
||||
type: "location-type",
|
||||
},
|
||||
};
|
||||
if (value.address.id) {
|
||||
Object.assign(body, {
|
||||
"address": {
|
||||
"id": value.address.id
|
||||
address: {
|
||||
id: value.address.id,
|
||||
},
|
||||
})
|
||||
});
|
||||
}
|
||||
postLocation(body)
|
||||
.then(
|
||||
location => hiddenLocation.value = location.id
|
||||
).catch(
|
||||
err => {
|
||||
.then((location) => (hiddenLocation.value = location.id))
|
||||
.catch((err) => {
|
||||
console.log(err.message);
|
||||
}
|
||||
);
|
||||
});
|
||||
} else {
|
||||
hiddenLocation.value = value.id;
|
||||
}
|
||||
commit("updateLocation", value);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
@ -18,7 +18,7 @@ export default {
|
||||
if (null === state.activity.start) {
|
||||
return new Date();
|
||||
}
|
||||
throw 'transform date to object ?';
|
||||
throw "transform date to object ?";
|
||||
},
|
||||
/**
|
||||
* Compute the event sources to show on the FullCalendar
|
||||
@ -33,7 +33,7 @@ export default {
|
||||
// current calendar
|
||||
if (state.activity.startDate !== null && state.activity.endDate !== null) {
|
||||
const s = {
|
||||
id: 'current',
|
||||
id: "current",
|
||||
events: [
|
||||
{
|
||||
title: "Rendez-vous",
|
||||
@ -41,8 +41,8 @@ export default {
|
||||
end: state.activity.endDate,
|
||||
allDay: false,
|
||||
is: "current",
|
||||
classNames: ['iscurrent'],
|
||||
}
|
||||
classNames: ["iscurrent"],
|
||||
},
|
||||
],
|
||||
editable: state.activity.calendarRange === null,
|
||||
};
|
||||
@ -52,7 +52,7 @@ export default {
|
||||
|
||||
for (const [userId, kinds] of state.currentView.users.entries()) {
|
||||
if (!state.usersData.has(userId)) {
|
||||
console.log('try to get events on a user which not exists', userId);
|
||||
console.log("try to get events on a user which not exists", userId);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -61,11 +61,16 @@ export default {
|
||||
if (kinds.ranges && userData.calendarRanges.length > 0) {
|
||||
const s = {
|
||||
id: `ranges_${userId}`,
|
||||
events: userData.calendarRanges.filter(r => state.activity.calendarRange === null || r.calendarRangeId !== state.activity.calendarRange.calendarRangeId),
|
||||
events: userData.calendarRanges.filter(
|
||||
(r) =>
|
||||
state.activity.calendarRange === null ||
|
||||
r.calendarRangeId !==
|
||||
state.activity.calendarRange.calendarRangeId,
|
||||
),
|
||||
color: userData.mainColor,
|
||||
classNames: ['isrange'],
|
||||
backgroundColor: 'white',
|
||||
textColor: 'black',
|
||||
classNames: ["isrange"],
|
||||
backgroundColor: "white",
|
||||
textColor: "black",
|
||||
editable: false,
|
||||
};
|
||||
|
||||
@ -74,10 +79,10 @@ export default {
|
||||
|
||||
if (kinds.remotes && userData.remotes.length > 0) {
|
||||
const s = {
|
||||
'id': `remote_${userId}`,
|
||||
id: `remote_${userId}`,
|
||||
events: userData.remotes,
|
||||
color: userData.mainColor,
|
||||
textColor: 'black',
|
||||
textColor: "black",
|
||||
editable: false,
|
||||
};
|
||||
|
||||
@ -87,10 +92,12 @@ export default {
|
||||
// if remotes is checked, we display also the locals calendars
|
||||
if (kinds.remotes && userData.locals.length > 0) {
|
||||
const s = {
|
||||
'id': `local_${userId}`,
|
||||
events: userData.locals.filter(l => l.originId !== state.activity.id),
|
||||
id: `local_${userId}`,
|
||||
events: userData.locals.filter(
|
||||
(l) => l.originId !== state.activity.id,
|
||||
),
|
||||
color: userData.mainColor,
|
||||
textColor: 'black',
|
||||
textColor: "black",
|
||||
editable: false,
|
||||
};
|
||||
|
||||
@ -104,7 +111,7 @@ export default {
|
||||
return state.activity.startDate;
|
||||
},
|
||||
getInviteForUser: (state) => (user) => {
|
||||
return state.activity.invites.find(i => i.user.id === user.id);
|
||||
return state.activity.invites.find((i) => i.user.id === user.id);
|
||||
},
|
||||
/**
|
||||
* get the user data for a specific user
|
||||
@ -138,19 +145,21 @@ export default {
|
||||
* @param getters
|
||||
* @returns {(function({user: *, start: *, end: *}): (boolean))|*}
|
||||
*/
|
||||
isCalendarRangeLoadedForUser: (state, getters) => ({user, start, end}) => {
|
||||
if (!getters.hasUserData(user)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let interval of getters.getUserData(user).calendarRangesLoaded) {
|
||||
if (start >= interval.start && end <= interval.end) {
|
||||
return true;
|
||||
isCalendarRangeLoadedForUser:
|
||||
(state, getters) =>
|
||||
({ user, start, end }) => {
|
||||
if (!getters.hasUserData(user)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
for (let interval of getters.getUserData(user).calendarRangesLoaded) {
|
||||
if (start >= interval.start && end <= interval.end) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
/**
|
||||
* return true if there was a fetch query for event between this date (start and end),
|
||||
* those date are included.
|
||||
@ -159,19 +168,21 @@ export default {
|
||||
* @param getters
|
||||
* @returns {(function({user: *, start: *, end: *}): (boolean))|*}
|
||||
*/
|
||||
isCalendarRemoteLoadedForUser: (state, getters) => ({user, start, end}) => {
|
||||
if (!getters.hasUserData(user)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let interval of getters.getUserData(user).remotesLoaded) {
|
||||
if (start >= interval.start && end <= interval.end) {
|
||||
return true;
|
||||
isCalendarRemoteLoadedForUser:
|
||||
(state, getters) =>
|
||||
({ user, start, end }) => {
|
||||
if (!getters.hasUserData(user)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
for (let interval of getters.getUserData(user).remotesLoaded) {
|
||||
if (start >= interval.start && end <= interval.end) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
/**
|
||||
* return true if the user ranges are shown on calendar
|
||||
*
|
||||
@ -180,8 +191,10 @@ export default {
|
||||
*/
|
||||
isRangeShownOnCalendarForUser: (state) => (user) => {
|
||||
const k = state.currentView.users.get(user.id);
|
||||
if (typeof k === 'undefined') {
|
||||
console.error('try to determinate if calendar range is shown and user is not in currentView');
|
||||
if (typeof k === "undefined") {
|
||||
console.error(
|
||||
"try to determinate if calendar range is shown and user is not in currentView",
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -195,8 +208,10 @@ export default {
|
||||
*/
|
||||
isRemoteShownOnCalendarForUser: (state) => (user) => {
|
||||
const k = state.currentView.users.get(user.id);
|
||||
if (typeof k === 'undefined') {
|
||||
console.error('try to determinate if calendar range is shown and user is not in currentView');
|
||||
if (typeof k === "undefined") {
|
||||
console.error(
|
||||
"try to determinate if calendar range is shown and user is not in currentView",
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -205,8 +220,8 @@ export default {
|
||||
|
||||
getLocationById: (state) => (id) => {
|
||||
for (let group of state.availableLocations) {
|
||||
console.log('group', group);
|
||||
const found = group.locations.find(l => l.id === id);
|
||||
console.log("group", group);
|
||||
const found = group.locations.find((l) => l.id === id);
|
||||
if (typeof found !== "undefined") {
|
||||
return found;
|
||||
}
|
||||
@ -216,57 +231,60 @@ export default {
|
||||
},
|
||||
|
||||
suggestedEntities(state, getters) {
|
||||
if (typeof (state.activity.accompanyingPeriod) === 'undefined') {
|
||||
if (typeof state.activity.accompanyingPeriod === "undefined") {
|
||||
return [];
|
||||
}
|
||||
const allEntities = [
|
||||
...getters.suggestedPersons,
|
||||
...getters.suggestedRequestor,
|
||||
...getters.suggestedUser,
|
||||
...getters.suggestedResources
|
||||
...getters.suggestedResources,
|
||||
];
|
||||
const uniqueIds = [...new Set(allEntities.map(i => `${i.type}-${i.id}`))];
|
||||
return Array.from(uniqueIds, id => allEntities.filter(r => `${r.type}-${r.id}` === id)[0]);
|
||||
const uniqueIds = [...new Set(allEntities.map((i) => `${i.type}-${i.id}`))];
|
||||
return Array.from(
|
||||
uniqueIds,
|
||||
(id) => allEntities.filter((r) => `${r.type}-${r.id}` === id)[0],
|
||||
);
|
||||
},
|
||||
suggestedPersons(state) {
|
||||
const existingPersonIds = state.activity.persons.map(p => p.id);
|
||||
const existingPersonIds = state.activity.persons.map((p) => p.id);
|
||||
return state.activity.accompanyingPeriod.participations
|
||||
.filter(p => p.endDate === null)
|
||||
.map(p => p.person)
|
||||
.filter(p => !existingPersonIds.includes(p.id))
|
||||
.filter((p) => p.endDate === null)
|
||||
.map((p) => p.person)
|
||||
.filter((p) => !existingPersonIds.includes(p.id));
|
||||
},
|
||||
suggestedRequestor(state) {
|
||||
if (state.activity.accompanyingPeriod.requestor === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const existingPersonIds = state.activity.persons.map(p => p.id);
|
||||
const existingThirdPartyIds = state.activity.thirdParties.map(p => p.id);
|
||||
return [state.activity.accompanyingPeriod.requestor]
|
||||
.filter(r =>
|
||||
(r.type === 'person' && !existingPersonIds.includes(r.id)) ||
|
||||
(r.type === 'thirdparty' && !existingThirdPartyIds.includes(r.id))
|
||||
);
|
||||
const existingPersonIds = state.activity.persons.map((p) => p.id);
|
||||
const existingThirdPartyIds = state.activity.thirdParties.map((p) => p.id);
|
||||
return [state.activity.accompanyingPeriod.requestor].filter(
|
||||
(r) =>
|
||||
(r.type === "person" && !existingPersonIds.includes(r.id)) ||
|
||||
(r.type === "thirdparty" && !existingThirdPartyIds.includes(r.id)),
|
||||
);
|
||||
},
|
||||
suggestedUser(state) {
|
||||
if (null === state.activity.users) {
|
||||
return [];
|
||||
}
|
||||
const existingUserIds = state.activity.users.map(p => p.id);
|
||||
return [state.activity.accompanyingPeriod.user]
|
||||
.filter(
|
||||
u => u !== null && !existingUserIds.includes(u.id)
|
||||
);
|
||||
const existingUserIds = state.activity.users.map((p) => p.id);
|
||||
return [state.activity.accompanyingPeriod.user].filter(
|
||||
(u) => u !== null && !existingUserIds.includes(u.id),
|
||||
);
|
||||
},
|
||||
suggestedResources(state) {
|
||||
const resources = state.activity.accompanyingPeriod.resources;
|
||||
const existingPersonIds = state.activity.persons.map(p => p.id);
|
||||
const existingThirdPartyIds = state.activity.thirdParties.map(p => p.id);
|
||||
const existingPersonIds = state.activity.persons.map((p) => p.id);
|
||||
const existingThirdPartyIds = state.activity.thirdParties.map((p) => p.id);
|
||||
return state.activity.accompanyingPeriod.resources
|
||||
.map(r => r.resource)
|
||||
.filter(r =>
|
||||
(r.type === 'person' && !existingPersonIds.includes(r.id)) ||
|
||||
(r.type === 'thirdparty' && !existingThirdPartyIds.includes(r.id))
|
||||
.map((r) => r.resource)
|
||||
.filter(
|
||||
(r) =>
|
||||
(r.type === "person" && !existingPersonIds.includes(r.id)) ||
|
||||
(r.type === "thirdparty" && !existingThirdPartyIds.includes(r.id)),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
@ -1,57 +1,65 @@
|
||||
import 'es6-promise/auto';
|
||||
import { createStore } from 'vuex';
|
||||
import { postLocation } from 'ChillActivityAssets/vuejs/Activity/api';
|
||||
import getters from './getters';
|
||||
import actions from './actions';
|
||||
import mutations from './mutations';
|
||||
import { mapEntity } from './utils';
|
||||
import { whoami } from '../api';
|
||||
import "es6-promise/auto";
|
||||
import { createStore } from "vuex";
|
||||
import { postLocation } from "ChillActivityAssets/vuejs/Activity/api";
|
||||
import getters from "./getters";
|
||||
import actions from "./actions";
|
||||
import mutations from "./mutations";
|
||||
import { mapEntity } from "./utils";
|
||||
import { whoami } from "../api";
|
||||
import prepareLocations from "ChillActivityAssets/vuejs/Activity/store.locations";
|
||||
|
||||
const debug = process.env.NODE_ENV !== 'production';
|
||||
const debug = process.env.NODE_ENV !== "production";
|
||||
|
||||
const store = createStore({
|
||||
strict: debug,
|
||||
state: {
|
||||
activity: mapEntity(window.entity), // activity is the calendar entity actually
|
||||
currentEvent: null,
|
||||
availableLocations: [],
|
||||
/**
|
||||
* the current user
|
||||
*/
|
||||
me: null,
|
||||
/**
|
||||
* store information about current view
|
||||
*/
|
||||
currentView: {
|
||||
start: null,
|
||||
end: null,
|
||||
users: new Map(),
|
||||
},
|
||||
/**
|
||||
* store a list of existing event, to avoid storing them twice
|
||||
*/
|
||||
existingEvents: new Set(),
|
||||
/**
|
||||
* store user data
|
||||
*/
|
||||
usersData: new Map(),
|
||||
},
|
||||
getters,
|
||||
mutations,
|
||||
actions,
|
||||
strict: debug,
|
||||
state: {
|
||||
activity: mapEntity(window.entity), // activity is the calendar entity actually
|
||||
currentEvent: null,
|
||||
availableLocations: [],
|
||||
/**
|
||||
* the current user
|
||||
*/
|
||||
me: null,
|
||||
/**
|
||||
* store information about current view
|
||||
*/
|
||||
currentView: {
|
||||
start: null,
|
||||
end: null,
|
||||
users: new Map(),
|
||||
},
|
||||
/**
|
||||
* store a list of existing event, to avoid storing them twice
|
||||
*/
|
||||
existingEvents: new Set(),
|
||||
/**
|
||||
* store user data
|
||||
*/
|
||||
usersData: new Map(),
|
||||
},
|
||||
getters,
|
||||
mutations,
|
||||
actions,
|
||||
});
|
||||
|
||||
whoami().then(me => {
|
||||
store.commit('setWhoAmiI', me);
|
||||
whoami().then((me) => {
|
||||
store.commit("setWhoAmiI", me);
|
||||
});
|
||||
|
||||
if (null !== store.getters.getMainUser) {
|
||||
store.commit('showUserOnCalendar', {ranges: true, remotes: true, user: store.getters.getMainUser});
|
||||
store.commit("showUserOnCalendar", {
|
||||
ranges: true,
|
||||
remotes: true,
|
||||
user: store.getters.getMainUser,
|
||||
});
|
||||
}
|
||||
|
||||
for (let u of store.state.activity.users) {
|
||||
store.commit('showUserOnCalendar', {ranges: false, remotes: false, user: u});
|
||||
store.commit("showUserOnCalendar", {
|
||||
ranges: false,
|
||||
remotes: false,
|
||||
user: u,
|
||||
});
|
||||
}
|
||||
|
||||
prepareLocations(store);
|
||||
|
@ -3,30 +3,27 @@ import {
|
||||
calendarRangeToFullCalendarEvent,
|
||||
remoteToFullCalendarEvent,
|
||||
localsToFullCalendarEvent,
|
||||
} from './utils';
|
||||
} from "./utils";
|
||||
|
||||
export default {
|
||||
setWhoAmiI(state, me) {
|
||||
state.me = me;
|
||||
},
|
||||
setCurrentDatesView(state, {start, end}) {
|
||||
setCurrentDatesView(state, { start, end }) {
|
||||
state.currentView.start = start;
|
||||
state.currentView.end = end;
|
||||
},
|
||||
showUserOnCalendar(state, {user, ranges, remotes}) {
|
||||
showUserOnCalendar(state, { user, ranges, remotes }) {
|
||||
if (!state.usersData.has(user.id)) {
|
||||
state.usersData.set(user.id, createUserData(user, state.usersData.size));
|
||||
}
|
||||
|
||||
const cur = state.currentView.users.get(user.id);
|
||||
|
||||
state.currentView.users.set(
|
||||
user.id,
|
||||
{
|
||||
ranges: typeof ranges !== 'undefined' ? ranges : cur.ranges,
|
||||
remotes: typeof remotes !== 'undefined' ? remotes : cur.remotes,
|
||||
}
|
||||
);
|
||||
state.currentView.users.set(user.id, {
|
||||
ranges: typeof ranges !== "undefined" ? ranges : cur.ranges,
|
||||
remotes: typeof remotes !== "undefined" ? remotes : cur.remotes,
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Set the event start and end to the given start and end,
|
||||
@ -36,7 +33,7 @@ export default {
|
||||
* @param Date start
|
||||
* @param Date end
|
||||
*/
|
||||
setEventTimes(state, {start, end}) {
|
||||
setEventTimes(state, { start, end }) {
|
||||
state.activity.startDate = start;
|
||||
state.activity.endDate = end;
|
||||
state.activity.calendarRange = null;
|
||||
@ -48,8 +45,8 @@ export default {
|
||||
* @param state
|
||||
* @param range
|
||||
*/
|
||||
associateCalendarToRange(state, {range}) {
|
||||
console.log('associateCalendarToRange', range);
|
||||
associateCalendarToRange(state, { range }) {
|
||||
console.log("associateCalendarToRange", range);
|
||||
|
||||
if (null === range) {
|
||||
state.activity.calendarRange = null;
|
||||
@ -59,22 +56,25 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('userId', range.extendedProps.userId);
|
||||
console.log("userId", range.extendedProps.userId);
|
||||
|
||||
const r = state.usersData.get(range.extendedProps.userId).calendarRanges
|
||||
.find(r => r.calendarRangeId === range.extendedProps.calendarRangeId);
|
||||
const r = state.usersData
|
||||
.get(range.extendedProps.userId)
|
||||
.calendarRanges.find(
|
||||
(r) => r.calendarRangeId === range.extendedProps.calendarRangeId,
|
||||
);
|
||||
|
||||
if (typeof r === 'undefined') {
|
||||
throw Error('Could not find managed calendar range');
|
||||
if (typeof r === "undefined") {
|
||||
throw Error("Could not find managed calendar range");
|
||||
}
|
||||
|
||||
console.log('range found', r);
|
||||
console.log("range found", r);
|
||||
|
||||
state.activity.startDate = range.start;
|
||||
state.activity.endDate = range.end;
|
||||
state.activity.calendarRange = r;
|
||||
|
||||
console.log('activity', state.activity);
|
||||
console.log("activity", state.activity);
|
||||
},
|
||||
|
||||
setMainUser(state, user) {
|
||||
@ -85,32 +85,36 @@ export default {
|
||||
addPersonsInvolved(state, payload) {
|
||||
//console.log('### mutation addPersonsInvolved', payload.result.type);
|
||||
switch (payload.result.type) {
|
||||
case 'person':
|
||||
case "person":
|
||||
state.activity.persons.push(payload.result);
|
||||
break;
|
||||
case 'thirdparty':
|
||||
case "thirdparty":
|
||||
state.activity.thirdParties.push(payload.result);
|
||||
break;
|
||||
case 'user':
|
||||
case "user":
|
||||
state.activity.users.push(payload.result);
|
||||
break;
|
||||
}
|
||||
;
|
||||
},
|
||||
removePersonInvolved(state, payload) {
|
||||
//console.log('### mutation removePersonInvolved', payload.type);
|
||||
switch (payload.type) {
|
||||
case 'person':
|
||||
state.activity.persons = state.activity.persons.filter(person => person !== payload);
|
||||
case "person":
|
||||
state.activity.persons = state.activity.persons.filter(
|
||||
(person) => person !== payload,
|
||||
);
|
||||
break;
|
||||
case 'thirdparty':
|
||||
state.activity.thirdParties = state.activity.thirdParties.filter(thirdparty => thirdparty !== payload);
|
||||
case "thirdparty":
|
||||
state.activity.thirdParties = state.activity.thirdParties.filter(
|
||||
(thirdparty) => thirdparty !== payload,
|
||||
);
|
||||
break;
|
||||
case 'user':
|
||||
state.activity.users = state.activity.users.filter(user => user !== payload);
|
||||
case "user":
|
||||
state.activity.users = state.activity.users.filter(
|
||||
(user) => user !== payload,
|
||||
);
|
||||
break;
|
||||
}
|
||||
;
|
||||
},
|
||||
/**
|
||||
* Add CalendarRange object for an user
|
||||
@ -121,7 +125,7 @@ export default {
|
||||
* @param start
|
||||
* @param end
|
||||
*/
|
||||
addCalendarRangesForUser(state, {user, ranges, start, end}) {
|
||||
addCalendarRangesForUser(state, { user, ranges, start, end }) {
|
||||
let userData;
|
||||
if (state.usersData.has(user.id)) {
|
||||
userData = state.usersData.get(user.id);
|
||||
@ -131,18 +135,18 @@ export default {
|
||||
}
|
||||
|
||||
const eventRanges = ranges
|
||||
.filter(r => !state.existingEvents.has(`range_${r.id}`))
|
||||
.map(r => {
|
||||
.filter((r) => !state.existingEvents.has(`range_${r.id}`))
|
||||
.map((r) => {
|
||||
// add to existing ids
|
||||
state.existingEvents.add(`range_${r.id}`);
|
||||
return r;
|
||||
})
|
||||
.map(r => calendarRangeToFullCalendarEvent(r));
|
||||
.map((r) => calendarRangeToFullCalendarEvent(r));
|
||||
|
||||
userData.calendarRanges = userData.calendarRanges.concat(eventRanges);
|
||||
userData.calendarRangesLoaded.push({start, end});
|
||||
userData.calendarRangesLoaded.push({ start, end });
|
||||
},
|
||||
addCalendarRemotesForUser(state, {user, remotes, start, end}) {
|
||||
addCalendarRemotesForUser(state, { user, remotes, start, end }) {
|
||||
let userData;
|
||||
if (state.usersData.has(user.id)) {
|
||||
userData = state.usersData.get(user.id);
|
||||
@ -152,18 +156,18 @@ export default {
|
||||
}
|
||||
|
||||
const eventRemotes = remotes
|
||||
.filter(r => !state.existingEvents.has(`remote_${r.id}`))
|
||||
.map(r => {
|
||||
.filter((r) => !state.existingEvents.has(`remote_${r.id}`))
|
||||
.map((r) => {
|
||||
// add to existing ids
|
||||
state.existingEvents.add(`remote_${r.id}`);
|
||||
return r;
|
||||
})
|
||||
.map(r => remoteToFullCalendarEvent(r));
|
||||
.map((r) => remoteToFullCalendarEvent(r));
|
||||
|
||||
userData.remotes = userData.remotes.concat(eventRemotes);
|
||||
userData.remotesLoaded.push({start, end});
|
||||
userData.remotesLoaded.push({ start, end });
|
||||
},
|
||||
addCalendarLocalsForUser(state, {user, locals, start, end}) {
|
||||
addCalendarLocalsForUser(state, { user, locals, start, end }) {
|
||||
let userData;
|
||||
if (state.usersData.has(user.id)) {
|
||||
userData = state.usersData.get(user.id);
|
||||
@ -173,20 +177,20 @@ export default {
|
||||
}
|
||||
|
||||
const eventRemotes = locals
|
||||
.filter(r => !state.existingEvents.has(`locals_${r.id}`))
|
||||
.map(r => {
|
||||
.filter((r) => !state.existingEvents.has(`locals_${r.id}`))
|
||||
.map((r) => {
|
||||
// add to existing ids
|
||||
state.existingEvents.add(`locals_${r.id}`);
|
||||
return r;
|
||||
})
|
||||
.map(r => localsToFullCalendarEvent(r));
|
||||
.map((r) => localsToFullCalendarEvent(r));
|
||||
|
||||
userData.locals = userData.locals.concat(eventRemotes);
|
||||
userData.localsLoaded.push({start, end});
|
||||
userData.localsLoaded.push({ start, end });
|
||||
},
|
||||
// Location
|
||||
updateLocation(state, value) {
|
||||
console.log('### mutation: updateLocation', value);
|
||||
console.log("### mutation: updateLocation", value);
|
||||
state.activity.location = value;
|
||||
},
|
||||
addAvailableLocationGroup(state, group) {
|
||||
|
@ -1,108 +1,117 @@
|
||||
import {COLORS} from '../const';
|
||||
import {ISOToDatetime} from '../../../../../../ChillMainBundle/Resources/public/chill/js/date';
|
||||
import {DateTime, User} from '../../../../../../ChillMainBundle/Resources/public/types';
|
||||
import {CalendarLight, CalendarRange, CalendarRemote} from '../../../types';
|
||||
import type {EventInputCalendarRange} from '../../../types';
|
||||
import {EventInput} from '@fullcalendar/core';
|
||||
import { COLORS } from "../const";
|
||||
import { ISOToDatetime } from "../../../../../../ChillMainBundle/Resources/public/chill/js/date";
|
||||
import {
|
||||
DateTime,
|
||||
User,
|
||||
} from "../../../../../../ChillMainBundle/Resources/public/types";
|
||||
import { CalendarLight, CalendarRange, CalendarRemote } from "../../../types";
|
||||
import type { EventInputCalendarRange } from "../../../types";
|
||||
import { EventInput } from "@fullcalendar/core";
|
||||
|
||||
export interface UserData {
|
||||
user: User,
|
||||
calendarRanges: CalendarRange[],
|
||||
calendarRangesLoaded: {}[],
|
||||
remotes: CalendarRemote[],
|
||||
remotesLoaded: {}[],
|
||||
locals: CalendarRemote[],
|
||||
localsLoaded: {}[],
|
||||
mainColor: string,
|
||||
user: User;
|
||||
calendarRanges: CalendarRange[];
|
||||
calendarRangesLoaded: {}[];
|
||||
remotes: CalendarRemote[];
|
||||
remotesLoaded: {}[];
|
||||
locals: CalendarRemote[];
|
||||
localsLoaded: {}[];
|
||||
mainColor: string;
|
||||
}
|
||||
|
||||
export const addIdToValue = (string: string, id: number): string => {
|
||||
let array = string ? string.split(',') : [];
|
||||
array.push(id.toString());
|
||||
let str = array.join();
|
||||
return str;
|
||||
const array = string ? string.split(",") : [];
|
||||
array.push(id.toString());
|
||||
const str = array.join();
|
||||
return str;
|
||||
};
|
||||
|
||||
export const removeIdFromValue = (string: string, id: number) => {
|
||||
let array = string.split(',');
|
||||
array = array.filter(el => el !== id.toString());
|
||||
let str = array.join();
|
||||
return str;
|
||||
let array = string.split(",");
|
||||
array = array.filter((el) => el !== id.toString());
|
||||
const str = array.join();
|
||||
return str;
|
||||
};
|
||||
|
||||
/*
|
||||
* Assign missing keys for the ConcernedGroups component
|
||||
*/
|
||||
* Assign missing keys for the ConcernedGroups component
|
||||
*/
|
||||
export const mapEntity = (entity: EventInput): EventInput => {
|
||||
let calendar = { ...entity};
|
||||
Object.assign(calendar, {thirdParties: entity.professionals});
|
||||
const calendar = { ...entity };
|
||||
Object.assign(calendar, { thirdParties: entity.professionals });
|
||||
|
||||
if (entity.startDate !== null ) {
|
||||
calendar.startDate = ISOToDatetime(entity.startDate.datetime);
|
||||
}
|
||||
if (entity.endDate !== null) {
|
||||
calendar.endDate = ISOToDatetime(entity.endDate.datetime);
|
||||
}
|
||||
if (entity.startDate !== null) {
|
||||
calendar.startDate = ISOToDatetime(entity.startDate.datetime);
|
||||
}
|
||||
if (entity.endDate !== null) {
|
||||
calendar.endDate = ISOToDatetime(entity.endDate.datetime);
|
||||
}
|
||||
|
||||
if (entity.calendarRange !== null) {
|
||||
calendar.calendarRange.calendarRangeId = entity.calendarRange.id;
|
||||
calendar.calendarRange.id = `range_${entity.calendarRange.id}`;
|
||||
}
|
||||
if (entity.calendarRange !== null) {
|
||||
calendar.calendarRange.calendarRangeId = entity.calendarRange.id;
|
||||
calendar.calendarRange.id = `range_${entity.calendarRange.id}`;
|
||||
}
|
||||
|
||||
return calendar;
|
||||
return calendar;
|
||||
};
|
||||
|
||||
export const createUserData = (user: User, colorIndex: number): UserData => {
|
||||
const colorId = colorIndex % COLORS.length;
|
||||
export const createUserData = (user: User, colorIndex: number): UserData => {
|
||||
const colorId = colorIndex % COLORS.length;
|
||||
|
||||
return {
|
||||
user: user,
|
||||
calendarRanges: [],
|
||||
calendarRangesLoaded: [],
|
||||
remotes: [],
|
||||
remotesLoaded: [],
|
||||
locals: [],
|
||||
localsLoaded: [],
|
||||
mainColor: COLORS[colorId],
|
||||
}
|
||||
}
|
||||
return {
|
||||
user: user,
|
||||
calendarRanges: [],
|
||||
calendarRangesLoaded: [],
|
||||
remotes: [],
|
||||
remotesLoaded: [],
|
||||
locals: [],
|
||||
localsLoaded: [],
|
||||
mainColor: COLORS[colorId],
|
||||
};
|
||||
};
|
||||
|
||||
// TODO move this function to a more global namespace, as it is also in use in MyCalendarRange app
|
||||
export const calendarRangeToFullCalendarEvent = (entity: CalendarRange): EventInputCalendarRange => {
|
||||
return {
|
||||
id: `range_${entity.id}`,
|
||||
title: "(" + entity.user.text + ")",
|
||||
start: entity.startDate.datetime8601,
|
||||
end: entity.endDate.datetime8601,
|
||||
allDay: false,
|
||||
userId: entity.user.id,
|
||||
userLabel: entity.user.label,
|
||||
calendarRangeId: entity.id,
|
||||
locationId: entity.location.id,
|
||||
locationName: entity.location.name,
|
||||
is: 'range',
|
||||
};
|
||||
}
|
||||
export const calendarRangeToFullCalendarEvent = (
|
||||
entity: CalendarRange,
|
||||
): EventInputCalendarRange => {
|
||||
return {
|
||||
id: `range_${entity.id}`,
|
||||
title: "(" + entity.user.text + ")",
|
||||
start: entity.startDate.datetime8601,
|
||||
end: entity.endDate.datetime8601,
|
||||
allDay: false,
|
||||
userId: entity.user.id,
|
||||
userLabel: entity.user.label,
|
||||
calendarRangeId: entity.id,
|
||||
locationId: entity.location.id,
|
||||
locationName: entity.location.name,
|
||||
is: "range",
|
||||
};
|
||||
};
|
||||
|
||||
export const remoteToFullCalendarEvent = (entity: CalendarRemote): EventInput & {id: string} => {
|
||||
return {
|
||||
id: `range_${entity.id}`,
|
||||
title: entity.title,
|
||||
start: entity.startDate.datetime8601,
|
||||
end: entity.endDate.datetime8601,
|
||||
allDay: entity.isAllDay,
|
||||
is: 'remote',
|
||||
};
|
||||
}
|
||||
export const remoteToFullCalendarEvent = (
|
||||
entity: CalendarRemote,
|
||||
): EventInput & { id: string } => {
|
||||
return {
|
||||
id: `range_${entity.id}`,
|
||||
title: entity.title,
|
||||
start: entity.startDate.datetime8601,
|
||||
end: entity.endDate.datetime8601,
|
||||
allDay: entity.isAllDay,
|
||||
is: "remote",
|
||||
};
|
||||
};
|
||||
|
||||
export const localsToFullCalendarEvent = (entity: CalendarLight): EventInput & {id: string; originId: number;} => {
|
||||
return {
|
||||
id: `local_${entity.id}`,
|
||||
title: entity.persons.map(p => p.text).join(', '),
|
||||
originId: entity.id,
|
||||
start: entity.startDate.datetime8601,
|
||||
end: entity.endDate.datetime8601,
|
||||
allDay: false,
|
||||
is: 'local',
|
||||
};
|
||||
}
|
||||
export const localsToFullCalendarEvent = (
|
||||
entity: CalendarLight,
|
||||
): EventInput & { id: string; originId: number } => {
|
||||
return {
|
||||
id: `local_${entity.id}`,
|
||||
title: entity.persons.map((p) => p.text).join(", "),
|
||||
originId: entity.id,
|
||||
start: entity.startDate.datetime8601,
|
||||
end: entity.endDate.datetime8601,
|
||||
allDay: false,
|
||||
is: "local",
|
||||
};
|
||||
};
|
||||
|
@ -1,95 +1,133 @@
|
||||
<template>
|
||||
<div class="btn-group" role="group">
|
||||
<button id="btnGroupDrop1" type="button" class="btn btn-misc dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<template v-if="status === Statuses.PENDING">
|
||||
<span class="fa fa-hourglass"></span> {{ $t('Give_an_answer')}}
|
||||
</template>
|
||||
<template v-else-if="status === Statuses.ACCEPTED">
|
||||
<span class="fa fa-check"></span> {{ $t('Accepted')}}
|
||||
</template>
|
||||
<template v-else-if="status === Statuses.DECLINED">
|
||||
<span class="fa fa-times"></span> {{ $t('Declined')}}
|
||||
</template>
|
||||
<template v-else-if="status === Statuses.TENTATIVELY_ACCEPTED">
|
||||
<span class="fa fa-question"></span> {{ $t('Tentative')}}
|
||||
</template>
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="btnGroupDrop1">
|
||||
<li v-if="status !== Statuses.ACCEPTED"><a class="dropdown-item" @click="changeStatus(Statuses.ACCEPTED)"><i class="fa fa-check" aria-hidden="true"></i> {{ $t('Accept') }}</a></li>
|
||||
<li v-if="status !== Statuses.DECLINED"><a class="dropdown-item" @click="changeStatus(Statuses.DECLINED)"><i class="fa fa-times" aria-hidden="true"></i> {{ $t('Decline') }}</a></li>
|
||||
<li v-if="status !== Statuses.TENTATIVELY_ACCEPTED"><a class="dropdown-item" @click="changeStatus(Statuses.TENTATIVELY_ACCEPTED)"><i class="fa fa-question"></i> {{ $t('Tentatively_accept') }}</a></li>
|
||||
<li v-if="status !== Statuses.PENDING"><a class="dropdown-item" @click="changeStatus(Statuses.PENDING)"><i class="fa fa-hourglass-o"></i> {{ $t('Set_pending') }}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="btn-group" role="group">
|
||||
<button
|
||||
id="btnGroupDrop1"
|
||||
type="button"
|
||||
class="btn btn-misc dropdown-toggle"
|
||||
data-bs-toggle="dropdown"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<template v-if="status === Statuses.PENDING">
|
||||
<span class="fa fa-hourglass"></span> {{ $t("Give_an_answer") }}
|
||||
</template>
|
||||
<template v-else-if="status === Statuses.ACCEPTED">
|
||||
<span class="fa fa-check"></span> {{ $t("Accepted") }}
|
||||
</template>
|
||||
<template v-else-if="status === Statuses.DECLINED">
|
||||
<span class="fa fa-times"></span> {{ $t("Declined") }}
|
||||
</template>
|
||||
<template v-else-if="status === Statuses.TENTATIVELY_ACCEPTED">
|
||||
<span class="fa fa-question"></span> {{ $t("Tentative") }}
|
||||
</template>
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="btnGroupDrop1">
|
||||
<li v-if="status !== Statuses.ACCEPTED">
|
||||
<a
|
||||
class="dropdown-item"
|
||||
@click="changeStatus(Statuses.ACCEPTED)"
|
||||
><i class="fa fa-check" aria-hidden="true"></i>
|
||||
{{ $t("Accept") }}</a
|
||||
>
|
||||
</li>
|
||||
<li v-if="status !== Statuses.DECLINED">
|
||||
<a
|
||||
class="dropdown-item"
|
||||
@click="changeStatus(Statuses.DECLINED)"
|
||||
><i class="fa fa-times" aria-hidden="true"></i>
|
||||
{{ $t("Decline") }}</a
|
||||
>
|
||||
</li>
|
||||
<li v-if="status !== Statuses.TENTATIVELY_ACCEPTED">
|
||||
<a
|
||||
class="dropdown-item"
|
||||
@click="changeStatus(Statuses.TENTATIVELY_ACCEPTED)"
|
||||
><i class="fa fa-question"></i>
|
||||
{{ $t("Tentatively_accept") }}</a
|
||||
>
|
||||
</li>
|
||||
<li v-if="status !== Statuses.PENDING">
|
||||
<a class="dropdown-item" @click="changeStatus(Statuses.PENDING)"
|
||||
><i class="fa fa-hourglass-o"></i>
|
||||
{{ $t("Set_pending") }}</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {defineComponent, PropType} from 'vue';
|
||||
import { defineComponent, PropType } from "vue";
|
||||
|
||||
const ACCEPTED = 'accepted';
|
||||
const DECLINED = 'declined';
|
||||
const PENDING = 'pending';
|
||||
const TENTATIVELY_ACCEPTED = 'tentative';
|
||||
const ACCEPTED = "accepted";
|
||||
const DECLINED = "declined";
|
||||
const PENDING = "pending";
|
||||
const TENTATIVELY_ACCEPTED = "tentative";
|
||||
|
||||
const i18n = {
|
||||
messages: {
|
||||
fr: {
|
||||
"Give_an_answer": "Répondre",
|
||||
"Accepted": "Accepté",
|
||||
"Declined": "Refusé",
|
||||
"Tentative": "Accepté provisoirement",
|
||||
"Accept": "Accepter",
|
||||
"Decline": "Refuser",
|
||||
"Tentatively_accept": "Accepter provisoirement",
|
||||
"Set_pending": "Ne pas répondre",
|
||||
}
|
||||
}
|
||||
messages: {
|
||||
fr: {
|
||||
Give_an_answer: "Répondre",
|
||||
Accepted: "Accepté",
|
||||
Declined: "Refusé",
|
||||
Tentative: "Accepté provisoirement",
|
||||
Accept: "Accepter",
|
||||
Decline: "Refuser",
|
||||
Tentatively_accept: "Accepter provisoirement",
|
||||
Set_pending: "Ne pas répondre",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: "Answer",
|
||||
i18n,
|
||||
props: {
|
||||
calendarId: { type: Number, required: true},
|
||||
status: {type: String as PropType<"accepted" | "declined" | "pending" | "tentative">, required: true},
|
||||
},
|
||||
emits: {
|
||||
statusChanged(payload: "accepted" | "declined" | "pending" | "tentative") {
|
||||
return true;
|
||||
name: "Answer",
|
||||
i18n,
|
||||
props: {
|
||||
calendarId: { type: Number, required: true },
|
||||
status: {
|
||||
type: String as PropType<
|
||||
"accepted" | "declined" | "pending" | "tentative"
|
||||
>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
Statuses: {
|
||||
ACCEPTED,
|
||||
DECLINED,
|
||||
PENDING,
|
||||
TENTATIVELY_ACCEPTED,
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
changeStatus: function (newStatus: "accepted" | "declined" | "pending" | "tentative") {
|
||||
console.log('changeStatus', newStatus);
|
||||
const url = `/api/1.0/calendar/calendar/${this.$props.calendarId}/answer/${newStatus}.json`;
|
||||
window.fetch(url, {
|
||||
method: 'POST',
|
||||
}).then((r: Response) => {
|
||||
if (!r.ok) {
|
||||
console.error('could not confirm answer', newStatus);
|
||||
return;
|
||||
}
|
||||
console.log('answer sent', newStatus);
|
||||
this.$emit('statusChanged', newStatus);
|
||||
});
|
||||
emits: {
|
||||
statusChanged(
|
||||
payload: "accepted" | "declined" | "pending" | "tentative",
|
||||
) {
|
||||
return true;
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
data() {
|
||||
return {
|
||||
Statuses: {
|
||||
ACCEPTED,
|
||||
DECLINED,
|
||||
PENDING,
|
||||
TENTATIVELY_ACCEPTED,
|
||||
},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
changeStatus: function (
|
||||
newStatus: "accepted" | "declined" | "pending" | "tentative",
|
||||
) {
|
||||
console.log("changeStatus", newStatus);
|
||||
const url = `/api/1.0/calendar/calendar/${this.$props.calendarId}/answer/${newStatus}.json`;
|
||||
window
|
||||
.fetch(url, {
|
||||
method: "POST",
|
||||
})
|
||||
.then((r: Response) => {
|
||||
if (!r.ok) {
|
||||
console.error("could not confirm answer", newStatus);
|
||||
return;
|
||||
}
|
||||
console.log("answer sent", newStatus);
|
||||
this.$emit("statusChanged", newStatus);
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
||||
<style scoped></style>
|
||||
|
@ -1,180 +1,225 @@
|
||||
<template>
|
||||
<div class="row">
|
||||
<div class="col-sm">
|
||||
<label class="form-label">{{ $t("created_availabilities") }}</label>
|
||||
<vue-multiselect
|
||||
v-model="pickedLocation"
|
||||
:options="locations"
|
||||
:label="'name'"
|
||||
:track-by="'id'"
|
||||
:selectLabel="'Presser \'Entrée\' pour choisir'"
|
||||
:selectedLabel="'Choisir'"
|
||||
:deselectLabel="'Presser \'Entrée\' pour enlever'"
|
||||
:placeholder="'Choisir'"
|
||||
></vue-multiselect>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="display-options row justify-content-between"
|
||||
style="margin-top: 1rem"
|
||||
>
|
||||
<div class="col-sm-9 col-xs-12">
|
||||
<div class="input-group mb-3">
|
||||
<label class="input-group-text" for="slotDuration"
|
||||
>Durée des créneaux</label
|
||||
>
|
||||
<select v-model="slotDuration" id="slotDuration" class="form-select">
|
||||
<option value="00:05:00">5 minutes</option>
|
||||
<option value="00:10:00">10 minutes</option>
|
||||
<option value="00:15:00">15 minutes</option>
|
||||
<option value="00:30:00">30 minutes</option>
|
||||
</select>
|
||||
<label class="input-group-text" for="slotMinTime">De</label>
|
||||
<select v-model="slotMinTime" id="slotMinTime" class="form-select">
|
||||
<option value="00:00:00">0h</option>
|
||||
<option value="01:00:00">1h</option>
|
||||
<option value="02:00:00">2h</option>
|
||||
<option value="03:00:00">3h</option>
|
||||
<option value="04:00:00">4h</option>
|
||||
<option value="05:00:00">5h</option>
|
||||
<option value="06:00:00">6h</option>
|
||||
<option value="07:00:00">7h</option>
|
||||
<option value="08:00:00">8h</option>
|
||||
<option value="09:00:00">9h</option>
|
||||
<option value="10:00:00">10h</option>
|
||||
<option value="11:00:00">11h</option>
|
||||
<option value="12:00:00">12h</option>
|
||||
</select>
|
||||
<label class="input-group-text" for="slotMaxTime">À</label>
|
||||
<select v-model="slotMaxTime" id="slotMaxTime" class="form-select">
|
||||
<option value="12:00:00">12h</option>
|
||||
<option value="13:00:00">13h</option>
|
||||
<option value="14:00:00">14h</option>
|
||||
<option value="15:00:00">15h</option>
|
||||
<option value="16:00:00">16h</option>
|
||||
<option value="17:00:00">17h</option>
|
||||
<option value="18:00:00">18h</option>
|
||||
<option value="19:00:00">19h</option>
|
||||
<option value="20:00:00">20h</option>
|
||||
<option value="21:00:00">21h</option>
|
||||
<option value="22:00:00">22h</option>
|
||||
<option value="23:00:00">23h</option>
|
||||
<option value="23:59:59">24h</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-3">
|
||||
<div class="float-end">
|
||||
<div class="form-check input-group">
|
||||
<span class="input-group-text">
|
||||
<input
|
||||
id="showHideWE"
|
||||
class="mt-0"
|
||||
type="checkbox"
|
||||
v-model="showWeekends"
|
||||
/>
|
||||
</span>
|
||||
<label for="showHideWE" class="form-check-label input-group-text"
|
||||
>Week-ends</label
|
||||
>
|
||||
<div class="row">
|
||||
<div class="col-sm">
|
||||
<label class="form-label">{{ $t("created_availabilities") }}</label>
|
||||
<vue-multiselect
|
||||
v-model="pickedLocation"
|
||||
:options="locations"
|
||||
:label="'name'"
|
||||
:track-by="'id'"
|
||||
:selectLabel="'Presser \'Entrée\' pour choisir'"
|
||||
:selectedLabel="'Choisir'"
|
||||
:deselectLabel="'Presser \'Entrée\' pour enlever'"
|
||||
:placeholder="'Choisir'"
|
||||
></vue-multiselect>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<FullCalendar :options="calendarOptions" ref="calendarRef">
|
||||
<template v-slot:eventContent="arg: EventApi">
|
||||
<span :class="eventClasses(arg.event)">
|
||||
<b v-if="arg.event.extendedProps.is === 'remote'">{{
|
||||
arg.event.title
|
||||
}}</b>
|
||||
<b v-else-if="arg.event.extendedProps.is === 'range'"
|
||||
>{{ arg.timeText }} - {{ arg.event.extendedProps.locationName }}</b
|
||||
>
|
||||
<b v-else-if="arg.event.extendedProps.is === 'local'">{{
|
||||
arg.event.title
|
||||
}}</b>
|
||||
<b v-else>no 'is'</b>
|
||||
<a
|
||||
v-if="arg.event.extendedProps.is === 'range'"
|
||||
class="fa fa-fw fa-times delete"
|
||||
@click.prevent="onClickDelete(arg.event)"
|
||||
>
|
||||
</a>
|
||||
</span>
|
||||
</template>
|
||||
</FullCalendar>
|
||||
|
||||
<div id="copy-widget">
|
||||
<div class="container mt-2 mb-2">
|
||||
|
||||
<div class="row justify-content-between align-items-center mb-4">
|
||||
<div class="col-xs-12 col-sm-3 col-md-2">
|
||||
<h6 class="chill-red">{{ $t("copy_range_from_to") }}</h6>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-9 col-md-2">
|
||||
<select v-model="dayOrWeek" id="dayOrWeek" class="form-select">
|
||||
<option value="day">{{ $t("from_day_to_day") }}</option>
|
||||
<option value="week">{{ $t("from_week_to_week") }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<template v-if="dayOrWeek === 'day'">
|
||||
<div class="col-xs-12 col-sm-3 col-md-3">
|
||||
<input class="form-control" type="date" v-model="copyFrom" />
|
||||
<div
|
||||
class="display-options row justify-content-between"
|
||||
style="margin-top: 1rem"
|
||||
>
|
||||
<div class="col-sm-9 col-xs-12">
|
||||
<div class="input-group mb-3">
|
||||
<label class="input-group-text" for="slotDuration"
|
||||
>Durée des créneaux</label
|
||||
>
|
||||
<select
|
||||
v-model="slotDuration"
|
||||
id="slotDuration"
|
||||
class="form-select"
|
||||
>
|
||||
<option value="00:05:00">5 minutes</option>
|
||||
<option value="00:10:00">10 minutes</option>
|
||||
<option value="00:15:00">15 minutes</option>
|
||||
<option value="00:30:00">30 minutes</option>
|
||||
</select>
|
||||
<label class="input-group-text" for="slotMinTime">De</label>
|
||||
<select
|
||||
v-model="slotMinTime"
|
||||
id="slotMinTime"
|
||||
class="form-select"
|
||||
>
|
||||
<option value="00:00:00">0h</option>
|
||||
<option value="01:00:00">1h</option>
|
||||
<option value="02:00:00">2h</option>
|
||||
<option value="03:00:00">3h</option>
|
||||
<option value="04:00:00">4h</option>
|
||||
<option value="05:00:00">5h</option>
|
||||
<option value="06:00:00">6h</option>
|
||||
<option value="07:00:00">7h</option>
|
||||
<option value="08:00:00">8h</option>
|
||||
<option value="09:00:00">9h</option>
|
||||
<option value="10:00:00">10h</option>
|
||||
<option value="11:00:00">11h</option>
|
||||
<option value="12:00:00">12h</option>
|
||||
</select>
|
||||
<label class="input-group-text" for="slotMaxTime">À</label>
|
||||
<select
|
||||
v-model="slotMaxTime"
|
||||
id="slotMaxTime"
|
||||
class="form-select"
|
||||
>
|
||||
<option value="12:00:00">12h</option>
|
||||
<option value="13:00:00">13h</option>
|
||||
<option value="14:00:00">14h</option>
|
||||
<option value="15:00:00">15h</option>
|
||||
<option value="16:00:00">16h</option>
|
||||
<option value="17:00:00">17h</option>
|
||||
<option value="18:00:00">18h</option>
|
||||
<option value="19:00:00">19h</option>
|
||||
<option value="20:00:00">20h</option>
|
||||
<option value="21:00:00">21h</option>
|
||||
<option value="22:00:00">22h</option>
|
||||
<option value="23:00:00">23h</option>
|
||||
<option value="23:59:59">24h</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-1 col-md-1 copy-chevron">
|
||||
<i class="fa fa-angle-double-right"></i>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-3 col-md-3">
|
||||
<input class="form-control" type="date" v-model="copyTo" />
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-5 col-md-1">
|
||||
<button class="btn btn-action float-end" @click="copyDay">
|
||||
{{ $t("copy_range") }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="col-xs-12 col-sm-3 col-md-3">
|
||||
<select
|
||||
v-model="copyFromWeek"
|
||||
id="copyFromWeek"
|
||||
class="form-select"
|
||||
>
|
||||
<option v-for="w in lastWeeks" :value="w.value" :key="w.value">
|
||||
{{ w.text }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-1 col-md-1 copy-chevron">
|
||||
<i class="fa fa-angle-double-right"></i>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-3 col-md-3">
|
||||
<select v-model="copyToWeek" id="copyToWeek" class="form-select">
|
||||
<option v-for="w in nextWeeks" :value="w.value" :key="w.value">
|
||||
{{ w.text }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-5 col-md-1">
|
||||
<button class="btn btn-action float-end" @click="copyWeek">
|
||||
{{ $t("copy_range") }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-3">
|
||||
<div class="float-end">
|
||||
<div class="form-check input-group">
|
||||
<span class="input-group-text">
|
||||
<input
|
||||
id="showHideWE"
|
||||
class="mt-0"
|
||||
type="checkbox"
|
||||
v-model="showWeekends"
|
||||
/>
|
||||
</span>
|
||||
<label
|
||||
for="showHideWE"
|
||||
class="form-check-label input-group-text"
|
||||
>Week-ends</label
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<FullCalendar :options="calendarOptions" ref="calendarRef">
|
||||
<template v-slot:eventContent="arg: EventApi">
|
||||
<span :class="eventClasses(arg.event)">
|
||||
<b v-if="arg.event.extendedProps.is === 'remote'">{{
|
||||
arg.event.title
|
||||
}}</b>
|
||||
<b v-else-if="arg.event.extendedProps.is === 'range'"
|
||||
>{{ arg.timeText }} -
|
||||
{{ arg.event.extendedProps.locationName }}</b
|
||||
>
|
||||
<b v-else-if="arg.event.extendedProps.is === 'local'">{{
|
||||
arg.event.title
|
||||
}}</b>
|
||||
<b v-else>no 'is'</b>
|
||||
<a
|
||||
v-if="arg.event.extendedProps.is === 'range'"
|
||||
class="fa fa-fw fa-times delete"
|
||||
@click.prevent="onClickDelete(arg.event)"
|
||||
>
|
||||
</a>
|
||||
</span>
|
||||
</template>
|
||||
</FullCalendar>
|
||||
|
||||
</div>
|
||||
<div id="copy-widget">
|
||||
<div class="container mt-2 mb-2">
|
||||
<div class="row justify-content-between align-items-center mb-4">
|
||||
<div class="col-xs-12 col-sm-3 col-md-2">
|
||||
<h6 class="chill-red">{{ $t("copy_range_from_to") }}</h6>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-9 col-md-2">
|
||||
<select
|
||||
v-model="dayOrWeek"
|
||||
id="dayOrWeek"
|
||||
class="form-select"
|
||||
>
|
||||
<option value="day">{{ $t("from_day_to_day") }}</option>
|
||||
<option value="week">
|
||||
{{ $t("from_week_to_week") }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<template v-if="dayOrWeek === 'day'">
|
||||
<div class="col-xs-12 col-sm-3 col-md-3">
|
||||
<input
|
||||
class="form-control"
|
||||
type="date"
|
||||
v-model="copyFrom"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-1 col-md-1 copy-chevron">
|
||||
<i class="fa fa-angle-double-right"></i>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-3 col-md-3">
|
||||
<input
|
||||
class="form-control"
|
||||
type="date"
|
||||
v-model="copyTo"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-5 col-md-1">
|
||||
<button
|
||||
class="btn btn-action float-end"
|
||||
@click="copyDay"
|
||||
>
|
||||
{{ $t("copy_range") }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="col-xs-12 col-sm-3 col-md-3">
|
||||
<select
|
||||
v-model="copyFromWeek"
|
||||
id="copyFromWeek"
|
||||
class="form-select"
|
||||
>
|
||||
<option
|
||||
v-for="w in lastWeeks"
|
||||
:value="w.value"
|
||||
:key="w.value"
|
||||
>
|
||||
{{ w.text }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-1 col-md-1 copy-chevron">
|
||||
<i class="fa fa-angle-double-right"></i>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-3 col-md-3">
|
||||
<select
|
||||
v-model="copyToWeek"
|
||||
id="copyToWeek"
|
||||
class="form-select"
|
||||
>
|
||||
<option
|
||||
v-for="w in nextWeeks"
|
||||
:value="w.value"
|
||||
:key="w.value"
|
||||
>
|
||||
{{ w.text }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-5 col-md-1">
|
||||
<button
|
||||
class="btn btn-action float-end"
|
||||
@click="copyWeek"
|
||||
>
|
||||
{{ $t("copy_range") }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- not directly seen, but include in a modal -->
|
||||
<edit-location ref="editLocation"></edit-location>
|
||||
<!-- not directly seen, but include in a modal -->
|
||||
<edit-location ref="editLocation"></edit-location>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type {
|
||||
CalendarOptions,
|
||||
DatesSetArg,
|
||||
EventInput,
|
||||
CalendarOptions,
|
||||
DatesSetArg,
|
||||
EventInput,
|
||||
} from "@fullcalendar/core";
|
||||
import { reactive, computed, ref, onMounted } from "vue";
|
||||
import { useStore } from "vuex";
|
||||
@ -182,19 +227,19 @@ import { key } from "./store";
|
||||
import FullCalendar from "@fullcalendar/vue3";
|
||||
import frLocale from "@fullcalendar/core/locales/fr";
|
||||
import interactionPlugin, {
|
||||
DropArg,
|
||||
EventResizeDoneArg,
|
||||
DropArg,
|
||||
EventResizeDoneArg,
|
||||
} from "@fullcalendar/interaction";
|
||||
import timeGridPlugin from "@fullcalendar/timegrid";
|
||||
import {
|
||||
EventApi,
|
||||
DateSelectArg,
|
||||
EventDropArg,
|
||||
EventClickArg,
|
||||
EventApi,
|
||||
DateSelectArg,
|
||||
EventDropArg,
|
||||
EventClickArg,
|
||||
} from "@fullcalendar/core";
|
||||
import {
|
||||
dateToISO,
|
||||
ISOToDate,
|
||||
dateToISO,
|
||||
ISOToDate,
|
||||
} from "../../../../../ChillMainBundle/Resources/public/chill/js/date";
|
||||
import VueMultiselect from "vue-multiselect";
|
||||
import { Location } from "../../../../../ChillMainBundle/Resources/public/types";
|
||||
@ -217,89 +262,91 @@ const copyFromWeek = ref<string | null>(null);
|
||||
const copyToWeek = ref<string | null>(null);
|
||||
|
||||
interface Weeks {
|
||||
value: string | null;
|
||||
text: string;
|
||||
value: string | null;
|
||||
text: string;
|
||||
}
|
||||
|
||||
const getMonday = (week: number): Date => {
|
||||
const lastMonday = new Date();
|
||||
lastMonday.setDate(
|
||||
lastMonday.getDate() - ((lastMonday.getDay() + 6) % 7) + week * 7
|
||||
);
|
||||
return lastMonday;
|
||||
const lastMonday = new Date();
|
||||
lastMonday.setDate(
|
||||
lastMonday.getDate() - ((lastMonday.getDay() + 6) % 7) + week * 7,
|
||||
);
|
||||
return lastMonday;
|
||||
};
|
||||
|
||||
const dateOptions: Intl.DateTimeFormatOptions = {
|
||||
weekday: "long",
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
weekday: "long",
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
};
|
||||
|
||||
const lastWeeks = computed((): Weeks[] =>
|
||||
Array.from(Array(30).keys()).map((w) => {
|
||||
const lastMonday = getMonday(15-w);
|
||||
return {
|
||||
value: dateToISO(lastMonday),
|
||||
text: `Semaine du ${lastMonday.toLocaleDateString("fr-FR", dateOptions)}`,
|
||||
};
|
||||
})
|
||||
Array.from(Array(30).keys()).map((w) => {
|
||||
const lastMonday = getMonday(15 - w);
|
||||
return {
|
||||
value: dateToISO(lastMonday),
|
||||
text: `Semaine du ${lastMonday.toLocaleDateString("fr-FR", dateOptions)}`,
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
const nextWeeks = computed((): Weeks[] =>
|
||||
Array.from(Array(52).keys()).map((w) => {
|
||||
const nextMonday = getMonday(w + 1);
|
||||
return {
|
||||
value: dateToISO(nextMonday),
|
||||
text: `Semaine du ${nextMonday.toLocaleDateString("fr-FR", dateOptions)}`,
|
||||
};
|
||||
})
|
||||
Array.from(Array(52).keys()).map((w) => {
|
||||
const nextMonday = getMonday(w + 1);
|
||||
return {
|
||||
value: dateToISO(nextMonday),
|
||||
text: `Semaine du ${nextMonday.toLocaleDateString("fr-FR", dateOptions)}`,
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
const baseOptions = ref<CalendarOptions>({
|
||||
locale: frLocale,
|
||||
plugins: [interactionPlugin, timeGridPlugin],
|
||||
initialView: "timeGridWeek",
|
||||
initialDate: new Date(),
|
||||
scrollTimeReset: false,
|
||||
selectable: true,
|
||||
// when the dates are changes in the fullcalendar view OR when new events are added
|
||||
datesSet: onDatesSet,
|
||||
// when a date is selected
|
||||
select: onDateSelect,
|
||||
// when a event is resized
|
||||
eventResize: onEventDropOrResize,
|
||||
// when an event is moved
|
||||
eventDrop: onEventDropOrResize,
|
||||
// when an event si clicked
|
||||
eventClick: onEventClick,
|
||||
selectMirror: false,
|
||||
editable: true,
|
||||
headerToolbar: {
|
||||
left: "prev,next today",
|
||||
center: "title",
|
||||
right: "timeGridWeek,timeGridDay",
|
||||
},
|
||||
locale: frLocale,
|
||||
plugins: [interactionPlugin, timeGridPlugin],
|
||||
initialView: "timeGridWeek",
|
||||
initialDate: new Date(),
|
||||
scrollTimeReset: false,
|
||||
selectable: true,
|
||||
// when the dates are changes in the fullcalendar view OR when new events are added
|
||||
datesSet: onDatesSet,
|
||||
// when a date is selected
|
||||
select: onDateSelect,
|
||||
// when a event is resized
|
||||
eventResize: onEventDropOrResize,
|
||||
// when an event is moved
|
||||
eventDrop: onEventDropOrResize,
|
||||
// when an event si clicked
|
||||
eventClick: onEventClick,
|
||||
selectMirror: false,
|
||||
editable: true,
|
||||
headerToolbar: {
|
||||
left: "prev,next today",
|
||||
center: "title",
|
||||
right: "timeGridWeek,timeGridDay",
|
||||
},
|
||||
});
|
||||
|
||||
const ranges = computed<EventInput[]>(() => {
|
||||
return store.state.calendarRanges.ranges;
|
||||
return store.state.calendarRanges.ranges;
|
||||
});
|
||||
|
||||
const locations = computed<Location[]>(() => {
|
||||
return store.state.locations.locations;
|
||||
return store.state.locations.locations;
|
||||
});
|
||||
|
||||
const pickedLocation = computed<Location | null>({
|
||||
get(): Location | null {
|
||||
return (
|
||||
store.state.locations.locationPicked ||
|
||||
store.state.locations.currentLocation
|
||||
);
|
||||
},
|
||||
set(newLocation: Location | null): void {
|
||||
store.commit("locations/setLocationPicked", newLocation, { root: true });
|
||||
},
|
||||
get(): Location | null {
|
||||
return (
|
||||
store.state.locations.locationPicked ||
|
||||
store.state.locations.currentLocation
|
||||
);
|
||||
},
|
||||
set(newLocation: Location | null): void {
|
||||
store.commit("locations/setLocationPicked", newLocation, {
|
||||
root: true,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
@ -307,7 +354,7 @@ const pickedLocation = computed<Location | null>({
|
||||
* @param arg
|
||||
*/
|
||||
const eventClasses = function (arg: EventApi): object {
|
||||
return { calendarRangeItems: true };
|
||||
return { calendarRangeItems: true };
|
||||
};
|
||||
|
||||
/*
|
||||
@ -331,117 +378,117 @@ const sources = computed<EventSourceInput[]>(() => {
|
||||
*/
|
||||
|
||||
const calendarOptions = computed((): CalendarOptions => {
|
||||
return {
|
||||
...baseOptions.value,
|
||||
weekends: showWeekends.value,
|
||||
slotDuration: slotDuration.value,
|
||||
events: ranges.value,
|
||||
slotMinTime: slotMinTime.value,
|
||||
slotMaxTime: slotMaxTime.value,
|
||||
};
|
||||
return {
|
||||
...baseOptions.value,
|
||||
weekends: showWeekends.value,
|
||||
slotDuration: slotDuration.value,
|
||||
events: ranges.value,
|
||||
slotMinTime: slotMinTime.value,
|
||||
slotMaxTime: slotMaxTime.value,
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* launched when the calendar range date change
|
||||
*/
|
||||
function onDatesSet(event: DatesSetArg): void {
|
||||
store.dispatch("fullCalendar/setCurrentDatesView", {
|
||||
start: event.start,
|
||||
end: event.end,
|
||||
});
|
||||
store.dispatch("fullCalendar/setCurrentDatesView", {
|
||||
start: event.start,
|
||||
end: event.end,
|
||||
});
|
||||
}
|
||||
|
||||
function onDateSelect(event: DateSelectArg): void {
|
||||
if (null === pickedLocation.value) {
|
||||
window.alert(
|
||||
"Indiquez une localisation avant de créer une période de disponibilité."
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (null === pickedLocation.value) {
|
||||
window.alert(
|
||||
"Indiquez une localisation avant de créer une période de disponibilité.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
store.dispatch("calendarRanges/createRange", {
|
||||
start: event.start,
|
||||
end: event.end,
|
||||
location: pickedLocation.value,
|
||||
});
|
||||
store.dispatch("calendarRanges/createRange", {
|
||||
start: event.start,
|
||||
end: event.end,
|
||||
location: pickedLocation.value,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* When a calendar range is deleted
|
||||
*/
|
||||
function onClickDelete(event: EventApi): void {
|
||||
if (event.extendedProps.is !== "range") {
|
||||
return;
|
||||
}
|
||||
if (event.extendedProps.is !== "range") {
|
||||
return;
|
||||
}
|
||||
|
||||
store.dispatch(
|
||||
"calendarRanges/deleteRange",
|
||||
event.extendedProps.calendarRangeId
|
||||
);
|
||||
store.dispatch(
|
||||
"calendarRanges/deleteRange",
|
||||
event.extendedProps.calendarRangeId,
|
||||
);
|
||||
}
|
||||
|
||||
function onEventDropOrResize(payload: EventDropArg | EventResizeDoneArg) {
|
||||
if (payload.event.extendedProps.is !== "range") {
|
||||
return;
|
||||
}
|
||||
const changedEvent = payload.event;
|
||||
if (payload.event.extendedProps.is !== "range") {
|
||||
return;
|
||||
}
|
||||
const changedEvent = payload.event;
|
||||
|
||||
store.dispatch("calendarRanges/patchRangeTime", {
|
||||
calendarRangeId: payload.event.extendedProps.calendarRangeId,
|
||||
start: payload.event.start,
|
||||
end: payload.event.end,
|
||||
});
|
||||
store.dispatch("calendarRanges/patchRangeTime", {
|
||||
calendarRangeId: payload.event.extendedProps.calendarRangeId,
|
||||
start: payload.event.start,
|
||||
end: payload.event.end,
|
||||
});
|
||||
}
|
||||
|
||||
function onEventClick(payload: EventClickArg): void {
|
||||
// @ts-ignore TS does not recognize the target. But it does exists.
|
||||
if (payload.jsEvent.target.classList.contains("delete")) {
|
||||
return;
|
||||
}
|
||||
if (payload.event.extendedProps.is !== "range") {
|
||||
return;
|
||||
}
|
||||
// @ts-ignore TS does not recognize the target. But it does exists.
|
||||
if (payload.jsEvent.target.classList.contains("delete")) {
|
||||
return;
|
||||
}
|
||||
if (payload.event.extendedProps.is !== "range") {
|
||||
return;
|
||||
}
|
||||
|
||||
editLocation.value?.startEdit(payload.event);
|
||||
editLocation.value?.startEdit(payload.event);
|
||||
}
|
||||
|
||||
function copyDay() {
|
||||
if (null === copyFrom.value || null === copyTo.value) {
|
||||
return;
|
||||
}
|
||||
store.dispatch("calendarRanges/copyFromDayToAnotherDay", {
|
||||
from: ISOToDate(copyFrom.value),
|
||||
to: ISOToDate(copyTo.value),
|
||||
});
|
||||
if (null === copyFrom.value || null === copyTo.value) {
|
||||
return;
|
||||
}
|
||||
store.dispatch("calendarRanges/copyFromDayToAnotherDay", {
|
||||
from: ISOToDate(copyFrom.value),
|
||||
to: ISOToDate(copyTo.value),
|
||||
});
|
||||
}
|
||||
|
||||
function copyWeek() {
|
||||
if (null === copyFromWeek.value || null === copyToWeek.value) {
|
||||
return;
|
||||
}
|
||||
store.dispatch("calendarRanges/copyFromWeekToAnotherWeek", {
|
||||
fromMonday: ISOToDate(copyFromWeek.value),
|
||||
toMonday: ISOToDate(copyToWeek.value),
|
||||
});
|
||||
if (null === copyFromWeek.value || null === copyToWeek.value) {
|
||||
return;
|
||||
}
|
||||
store.dispatch("calendarRanges/copyFromWeekToAnotherWeek", {
|
||||
fromMonday: ISOToDate(copyFromWeek.value),
|
||||
toMonday: ISOToDate(copyToWeek.value),
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
copyFromWeek.value = dateToISO(getMonday(0));
|
||||
copyToWeek.value = dateToISO(getMonday(1));
|
||||
copyFromWeek.value = dateToISO(getMonday(0));
|
||||
copyToWeek.value = dateToISO(getMonday(1));
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#copy-widget {
|
||||
position: sticky;
|
||||
bottom: 0px;
|
||||
background-color: white;
|
||||
z-index: 9999999999;
|
||||
padding: 0.25rem 0 0.25rem;
|
||||
position: sticky;
|
||||
bottom: 0px;
|
||||
background-color: white;
|
||||
z-index: 9999999999;
|
||||
padding: 0.25rem 0 0.25rem;
|
||||
}
|
||||
div.copy-chevron {
|
||||
text-align: center;
|
||||
font-size: x-large;
|
||||
width: 2rem;
|
||||
text-align: center;
|
||||
font-size: x-large;
|
||||
width: 2rem;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,51 +1,46 @@
|
||||
<template>
|
||||
<component :is="Teleport" to="body">
|
||||
<modal v-if="showModal"
|
||||
@close="closeModal">
|
||||
<component :is="Teleport" to="body">
|
||||
<modal v-if="showModal" @close="closeModal">
|
||||
<template v-slot:header>
|
||||
<h3>{{ "Modifier le lieu" }}</h3>
|
||||
</template>
|
||||
|
||||
<template v-slot:header>
|
||||
<h3>{{ 'Modifier le lieu' }}</h3>
|
||||
</template>
|
||||
<template v-slot:body>
|
||||
<div></div>
|
||||
<label>Localisation</label>
|
||||
<vue-multiselect
|
||||
v-model="location"
|
||||
:options="locations"
|
||||
:label="'name'"
|
||||
:track-by="'id'"
|
||||
></vue-multiselect>
|
||||
</template>
|
||||
|
||||
<template v-slot:body>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
<label>Localisation</label>
|
||||
<vue-multiselect v-model="location" :options="locations" :label="'name'" :track-by="'id'"></vue-multiselect>
|
||||
</template>
|
||||
|
||||
<template v-slot:footer>
|
||||
<button class="btn btn-save" @click="saveAndClose">{{ 'Enregistrer' }}</button>
|
||||
</template>
|
||||
|
||||
</modal>
|
||||
</component>
|
||||
<template v-slot:footer>
|
||||
<button class="btn btn-save" @click="saveAndClose">
|
||||
{{ "Enregistrer" }}
|
||||
</button>
|
||||
</template>
|
||||
</modal>
|
||||
</component>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Modal from "../../../../../../ChillMainBundle/Resources/public/vuejs/_components/Modal.vue";
|
||||
import {computed, ref} from "vue";
|
||||
import {EventApi} from "@fullcalendar/core";
|
||||
import {useStore} from "vuex";
|
||||
import {key} from "../store";
|
||||
import {Location} from "../../../../../../ChillMainBundle/Resources/public/types";
|
||||
import { computed, ref } from "vue";
|
||||
import { EventApi } from "@fullcalendar/core";
|
||||
import { useStore } from "vuex";
|
||||
import { key } from "../store";
|
||||
import { Location } from "../../../../../../ChillMainBundle/Resources/public/types";
|
||||
import VueMultiselect from "vue-multiselect";
|
||||
//import type {Teleport} from "vue";
|
||||
|
||||
// see https://github.com/vuejs/core/issues/2855
|
||||
import {
|
||||
Teleport as teleport_,
|
||||
TeleportProps,
|
||||
VNodeProps
|
||||
} from 'vue'
|
||||
|
||||
const Teleport = teleport_ as {
|
||||
new (): {
|
||||
$props: VNodeProps & TeleportProps
|
||||
}
|
||||
}
|
||||
import { Teleport as teleport_, TeleportProps, VNodeProps } from "vue";
|
||||
|
||||
const Teleport = teleport_ as new () => {
|
||||
$props: VNodeProps & TeleportProps;
|
||||
};
|
||||
|
||||
const store = useStore(key);
|
||||
|
||||
@ -55,34 +50,40 @@ const showModal = ref(false);
|
||||
//const tele = ref<InstanceType<typeof Teleport> | null>(null);
|
||||
|
||||
const locations = computed<Location[]>(() => {
|
||||
return store.state.locations.locations;
|
||||
return store.state.locations.locations;
|
||||
});
|
||||
|
||||
const startEdit = function(event: EventApi): void {
|
||||
console.log('startEditing', event);
|
||||
calendarRangeId.value = event.extendedProps.calendarRangeId;
|
||||
location.value = store.getters['locations/getLocationById'](event.extendedProps.locationId) || null;
|
||||
const startEdit = function (event: EventApi): void {
|
||||
console.log("startEditing", event);
|
||||
calendarRangeId.value = event.extendedProps.calendarRangeId;
|
||||
location.value =
|
||||
store.getters["locations/getLocationById"](
|
||||
event.extendedProps.locationId,
|
||||
) || null;
|
||||
|
||||
console.log('new location value', location.value);
|
||||
console.log('calendar range id', calendarRangeId.value);
|
||||
showModal.value = true;
|
||||
}
|
||||
console.log("new location value", location.value);
|
||||
console.log("calendar range id", calendarRangeId.value);
|
||||
showModal.value = true;
|
||||
};
|
||||
|
||||
const saveAndClose = function(e: Event): void {
|
||||
console.log('saveEditAndClose', e);
|
||||
const saveAndClose = function (e: Event): void {
|
||||
console.log("saveEditAndClose", e);
|
||||
|
||||
store.dispatch('calendarRanges/patchRangeLocation', {location: location.value, calendarRangeId: calendarRangeId.value})
|
||||
.then(_ => {showModal.value = false;})
|
||||
}
|
||||
store
|
||||
.dispatch("calendarRanges/patchRangeLocation", {
|
||||
location: location.value,
|
||||
calendarRangeId: calendarRangeId.value,
|
||||
})
|
||||
.then((_) => {
|
||||
showModal.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
const closeModal = function(_: any): void {
|
||||
showModal.value = false;
|
||||
}
|
||||
|
||||
defineExpose({startEdit});
|
||||
const closeModal = function (_: any): void {
|
||||
showModal.value = false;
|
||||
};
|
||||
|
||||
defineExpose({ startEdit });
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
<style scoped></style>
|
||||
|
@ -1,28 +1,27 @@
|
||||
const appMessages = {
|
||||
fr: {
|
||||
created_availabilities: "Lieu des plages de disponibilités créées",
|
||||
edit_your_calendar_range: "Planifiez vos plages de disponibilités",
|
||||
show_my_calendar: "Afficher mon calendrier",
|
||||
show_weekends: "Afficher les week-ends",
|
||||
copy_range: "Copier",
|
||||
copy_range_from_to: "Copier les plages",
|
||||
from_day_to_day: "d'un jour à l'autre",
|
||||
from_week_to_week: "d'une semaine à l'autre",
|
||||
copy_range_how_to: "Créez les plages de disponibilités durant une journée et copiez-les facilement au jour suivant avec ce bouton. Si les week-ends sont cachés, le jour suivant un vendredi sera le lundi.",
|
||||
new_range_to_save: "Nouvelles plages à enregistrer",
|
||||
update_range_to_save: "Plages à modifier",
|
||||
delete_range_to_save: "Plages à supprimer",
|
||||
by: "Par",
|
||||
main_user_concerned: "Utilisateur concerné",
|
||||
dateFrom: "De",
|
||||
dateTo: "à",
|
||||
day: "Jour",
|
||||
week: "Semaine",
|
||||
month: "Mois",
|
||||
today: "Aujourd'hui",
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
appMessages
|
||||
fr: {
|
||||
created_availabilities: "Lieu des plages de disponibilités créées",
|
||||
edit_your_calendar_range: "Planifiez vos plages de disponibilités",
|
||||
show_my_calendar: "Afficher mon calendrier",
|
||||
show_weekends: "Afficher les week-ends",
|
||||
copy_range: "Copier",
|
||||
copy_range_from_to: "Copier les plages",
|
||||
from_day_to_day: "d'un jour à l'autre",
|
||||
from_week_to_week: "d'une semaine à l'autre",
|
||||
copy_range_how_to:
|
||||
"Créez les plages de disponibilités durant une journée et copiez-les facilement au jour suivant avec ce bouton. Si les week-ends sont cachés, le jour suivant un vendredi sera le lundi.",
|
||||
new_range_to_save: "Nouvelles plages à enregistrer",
|
||||
update_range_to_save: "Plages à modifier",
|
||||
delete_range_to_save: "Plages à supprimer",
|
||||
by: "Par",
|
||||
main_user_concerned: "Utilisateur concerné",
|
||||
dateFrom: "De",
|
||||
dateTo: "à",
|
||||
day: "Jour",
|
||||
week: "Semaine",
|
||||
month: "Mois",
|
||||
today: "Aujourd'hui",
|
||||
},
|
||||
};
|
||||
|
||||
export { appMessages };
|
||||
|
@ -1,19 +1,19 @@
|
||||
import { createApp } from 'vue';
|
||||
import { _createI18n } from '../../../../../ChillMainBundle/Resources/public/vuejs/_js/i18n'
|
||||
import { appMessages } from './i18n'
|
||||
import futureStore, {key} from './store/index'
|
||||
import { createApp } from "vue";
|
||||
import { _createI18n } from "../../../../../ChillMainBundle/Resources/public/vuejs/_js/i18n";
|
||||
import { appMessages } from "./i18n";
|
||||
import futureStore, { key } from "./store/index";
|
||||
|
||||
import App2 from './App2.vue';
|
||||
import {useI18n} from "vue-i18n";
|
||||
import App2 from "./App2.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
futureStore().then((store) => {
|
||||
const i18n = _createI18n(appMessages, false);
|
||||
const i18n = _createI18n(appMessages, false);
|
||||
|
||||
const app = createApp({
|
||||
template: `<app></app>`,
|
||||
})
|
||||
.use(store, key)
|
||||
.use(i18n)
|
||||
.component('app', App2)
|
||||
.mount('#myCalendar');
|
||||
const app = createApp({
|
||||
template: `<app></app>`,
|
||||
})
|
||||
.use(store, key)
|
||||
.use(i18n)
|
||||
.component("app", App2)
|
||||
.mount("#myCalendar");
|
||||
});
|
||||
|
@ -1,50 +1,56 @@
|
||||
import 'es6-promise/auto';
|
||||
import {Store, createStore} from 'vuex';
|
||||
import {InjectionKey} from "vue";
|
||||
import me, {MeState} from './modules/me';
|
||||
import fullCalendar, {FullCalendarState} from './modules/fullcalendar';
|
||||
import calendarRanges, {CalendarRangesState} from './modules/calendarRanges';
|
||||
import calendarRemotes, {CalendarRemotesState} from './modules/calendarRemotes';
|
||||
import {whoami} from "../../../../../../ChillMainBundle/Resources/public/lib/api/user";
|
||||
import {User} from '../../../../../../ChillMainBundle/Resources/public/types';
|
||||
import locations, {LocationState} from "./modules/location";
|
||||
import calendarLocals, {CalendarLocalsState} from "./modules/calendarLocals";
|
||||
import "es6-promise/auto";
|
||||
import { Store, createStore } from "vuex";
|
||||
import { InjectionKey } from "vue";
|
||||
import me, { MeState } from "./modules/me";
|
||||
import fullCalendar, { FullCalendarState } from "./modules/fullcalendar";
|
||||
import calendarRanges, { CalendarRangesState } from "./modules/calendarRanges";
|
||||
import calendarRemotes, {
|
||||
CalendarRemotesState,
|
||||
} from "./modules/calendarRemotes";
|
||||
import { whoami } from "../../../../../../ChillMainBundle/Resources/public/lib/api/user";
|
||||
import { User } from "../../../../../../ChillMainBundle/Resources/public/types";
|
||||
import locations, { LocationState } from "./modules/location";
|
||||
import calendarLocals, { CalendarLocalsState } from "./modules/calendarLocals";
|
||||
|
||||
const debug = process.env.NODE_ENV !== 'production';
|
||||
const debug = process.env.NODE_ENV !== "production";
|
||||
|
||||
export interface State {
|
||||
calendarRanges: CalendarRangesState,
|
||||
calendarRemotes: CalendarRemotesState,
|
||||
calendarLocals: CalendarLocalsState,
|
||||
fullCalendar: FullCalendarState,
|
||||
me: MeState,
|
||||
locations: LocationState
|
||||
calendarRanges: CalendarRangesState;
|
||||
calendarRemotes: CalendarRemotesState;
|
||||
calendarLocals: CalendarLocalsState;
|
||||
fullCalendar: FullCalendarState;
|
||||
me: MeState;
|
||||
locations: LocationState;
|
||||
}
|
||||
|
||||
export const key: InjectionKey<Store<State>> = Symbol();
|
||||
|
||||
const futureStore = function(): Promise<Store<State>> {
|
||||
return whoami().then((user: User) => {
|
||||
const store = createStore<State>({
|
||||
strict: debug,
|
||||
modules: {
|
||||
me,
|
||||
fullCalendar,
|
||||
calendarRanges,
|
||||
calendarRemotes,
|
||||
calendarLocals,
|
||||
locations,
|
||||
},
|
||||
mutations: {}
|
||||
});
|
||||
const futureStore = function (): Promise<Store<State>> {
|
||||
return whoami().then((user: User) => {
|
||||
const store = createStore<State>({
|
||||
strict: debug,
|
||||
modules: {
|
||||
me,
|
||||
fullCalendar,
|
||||
calendarRanges,
|
||||
calendarRemotes,
|
||||
calendarLocals,
|
||||
locations,
|
||||
},
|
||||
mutations: {},
|
||||
});
|
||||
|
||||
store.commit('me/setWhoAmi', user, {root: true});
|
||||
store.dispatch('locations/getLocations', null, {root: true}).then(_ => {
|
||||
return store.dispatch('locations/getCurrentLocation', null, {root: true});
|
||||
});
|
||||
store.commit("me/setWhoAmi", user, { root: true });
|
||||
store
|
||||
.dispatch("locations/getLocations", null, { root: true })
|
||||
.then((_) => {
|
||||
return store.dispatch("locations/getCurrentLocation", null, {
|
||||
root: true,
|
||||
});
|
||||
});
|
||||
|
||||
return Promise.resolve(store);
|
||||
});
|
||||
}
|
||||
return Promise.resolve(store);
|
||||
});
|
||||
};
|
||||
|
||||
export default futureStore;
|
||||
|
@ -1,95 +1,116 @@
|
||||
import {State} from './../index';
|
||||
import {ActionContext, Module} from 'vuex';
|
||||
import {CalendarLight} from '../../../../types';
|
||||
import {fetchCalendarLocalForUser} from '../../../Calendar/api';
|
||||
import {EventInput} from '@fullcalendar/core';
|
||||
import {localsToFullCalendarEvent} from "../../../Calendar/store/utils";
|
||||
import {TransportExceptionInterface} from "../../../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods";
|
||||
import {COLORS} from "../../../Calendar/const";
|
||||
import { State } from "./../index";
|
||||
import { ActionContext, Module } from "vuex";
|
||||
import { CalendarLight } from "../../../../types";
|
||||
import { fetchCalendarLocalForUser } from "../../../Calendar/api";
|
||||
import { EventInput } from "@fullcalendar/core";
|
||||
import { localsToFullCalendarEvent } from "../../../Calendar/store/utils";
|
||||
import { TransportExceptionInterface } from "../../../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods";
|
||||
import { COLORS } from "../../../Calendar/const";
|
||||
|
||||
export interface CalendarLocalsState {
|
||||
locals: EventInput[],
|
||||
localsLoaded: {start: number, end: number}[],
|
||||
localsIndex: Set<string>,
|
||||
key: number
|
||||
locals: EventInput[];
|
||||
localsLoaded: { start: number; end: number }[];
|
||||
localsIndex: Set<string>;
|
||||
key: number;
|
||||
}
|
||||
|
||||
type Context = ActionContext<CalendarLocalsState, State>;
|
||||
|
||||
export default <Module<CalendarLocalsState, State>> {
|
||||
namespaced: true,
|
||||
state: (): CalendarLocalsState => ({
|
||||
locals: [],
|
||||
localsLoaded: [],
|
||||
localsIndex: new Set<string>(),
|
||||
key: 0
|
||||
}),
|
||||
getters: {
|
||||
isLocalsLoaded: (state: CalendarLocalsState) => ({start, end}: {start: Date, end: Date}): boolean => {
|
||||
for (let range of state.localsLoaded) {
|
||||
if (start.getTime() === range.start && end.getTime() === range.end) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: (): CalendarLocalsState => ({
|
||||
locals: [],
|
||||
localsLoaded: [],
|
||||
localsIndex: new Set<string>(),
|
||||
key: 0,
|
||||
}),
|
||||
getters: {
|
||||
isLocalsLoaded:
|
||||
(state: CalendarLocalsState) =>
|
||||
({ start, end }: { start: Date; end: Date }): boolean => {
|
||||
for (const range of state.localsLoaded) {
|
||||
if (
|
||||
start.getTime() === range.start &&
|
||||
end.getTime() === range.end
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return false;
|
||||
},
|
||||
},
|
||||
},
|
||||
mutations: {
|
||||
addLocals(state: CalendarLocalsState, ranges: CalendarLight[]) {
|
||||
console.log('addLocals', ranges);
|
||||
mutations: {
|
||||
addLocals(state: CalendarLocalsState, ranges: CalendarLight[]) {
|
||||
console.log("addLocals", ranges);
|
||||
|
||||
const toAdd = ranges
|
||||
.map(cr => localsToFullCalendarEvent(cr))
|
||||
.filter(r => !state.localsIndex.has(r.id));
|
||||
const toAdd = ranges
|
||||
.map((cr) => localsToFullCalendarEvent(cr))
|
||||
.filter((r) => !state.localsIndex.has(r.id));
|
||||
|
||||
toAdd.forEach((r) => {
|
||||
state.localsIndex.add(r.id);
|
||||
state.locals.push(r);
|
||||
});
|
||||
state.key = state.key + toAdd.length;
|
||||
toAdd.forEach((r) => {
|
||||
state.localsIndex.add(r.id);
|
||||
state.locals.push(r);
|
||||
});
|
||||
state.key = state.key + toAdd.length;
|
||||
},
|
||||
addLoaded(
|
||||
state: CalendarLocalsState,
|
||||
payload: { start: Date; end: Date },
|
||||
) {
|
||||
state.localsLoaded.push({
|
||||
start: payload.start.getTime(),
|
||||
end: payload.end.getTime(),
|
||||
});
|
||||
},
|
||||
},
|
||||
addLoaded(state: CalendarLocalsState, payload: {start: Date, end: Date}) {
|
||||
state.localsLoaded.push({start: payload.start.getTime(), end: payload.end.getTime()});
|
||||
actions: {
|
||||
fetchLocals(
|
||||
ctx: Context,
|
||||
payload: { start: Date; end: Date },
|
||||
): Promise<null> {
|
||||
const start = payload.start;
|
||||
const end = payload.end;
|
||||
|
||||
if (ctx.rootGetters["me/getMe"] === null) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
if (ctx.getters.isLocalsLoaded({ start, end })) {
|
||||
return Promise.resolve(ctx.getters.getRangeSource);
|
||||
}
|
||||
|
||||
ctx.commit("addLoaded", {
|
||||
start: start,
|
||||
end: end,
|
||||
});
|
||||
|
||||
return fetchCalendarLocalForUser(
|
||||
ctx.rootGetters["me/getMe"],
|
||||
start,
|
||||
end,
|
||||
)
|
||||
.then((remotes: CalendarLight[]) => {
|
||||
// to be add when reactivity problem will be solve ?
|
||||
//ctx.commit('addRemotes', remotes);
|
||||
const inputs = remotes
|
||||
.map((cr) => localsToFullCalendarEvent(cr))
|
||||
.map((cr) => ({
|
||||
...cr,
|
||||
backgroundColor: COLORS[0],
|
||||
textColor: "black",
|
||||
editable: false,
|
||||
}));
|
||||
ctx.commit("calendarRanges/addExternals", inputs, {
|
||||
root: true,
|
||||
});
|
||||
return Promise.resolve(null);
|
||||
})
|
||||
.catch((e: TransportExceptionInterface) => {
|
||||
console.error(e);
|
||||
|
||||
return Promise.resolve(null);
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
fetchLocals(ctx: Context, payload: {start: Date, end: Date}): Promise<null> {
|
||||
const start = payload.start;
|
||||
const end = payload.end;
|
||||
|
||||
if (ctx.rootGetters['me/getMe'] === null) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
if (ctx.getters.isLocalsLoaded({start, end})) {
|
||||
return Promise.resolve(ctx.getters.getRangeSource);
|
||||
}
|
||||
|
||||
ctx.commit('addLoaded', {
|
||||
start: start,
|
||||
end: end,
|
||||
});
|
||||
|
||||
return fetchCalendarLocalForUser(
|
||||
ctx.rootGetters['me/getMe'],
|
||||
start,
|
||||
end
|
||||
)
|
||||
.then((remotes: CalendarLight[]) => {
|
||||
// to be add when reactivity problem will be solve ?
|
||||
//ctx.commit('addRemotes', remotes);
|
||||
const inputs = remotes
|
||||
.map(cr => localsToFullCalendarEvent(cr))
|
||||
.map(cr => ({...cr, backgroundColor: COLORS[0], textColor: 'black', editable: false}))
|
||||
ctx.commit('calendarRanges/addExternals', inputs, {root: true});
|
||||
return Promise.resolve(null);
|
||||
})
|
||||
.catch((e: TransportExceptionInterface) => {
|
||||
console.error(e);
|
||||
|
||||
return Promise.resolve(null);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
} as Module<CalendarLocalsState, State>;
|
||||
|
@ -1,286 +1,377 @@
|
||||
import {State} from './../index';
|
||||
import {ActionContext, Module} from 'vuex';
|
||||
import {CalendarRange, CalendarRangeCreate, CalendarRangeEdit, isEventInputCalendarRange} from "../../../../types";
|
||||
import {Location} from "../../../../../../../ChillMainBundle/Resources/public/types";
|
||||
import {fetchCalendarRangeForUser} from '../../../Calendar/api';
|
||||
import {calendarRangeToFullCalendarEvent} from '../../../Calendar/store/utils';
|
||||
import {EventInput} from '@fullcalendar/core';
|
||||
import {makeFetch} from "../../../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods";
|
||||
import { State } from "./../index";
|
||||
import { ActionContext, Module } from "vuex";
|
||||
import {
|
||||
datetimeToISO,
|
||||
dateToISO,
|
||||
ISOToDatetime
|
||||
CalendarRange,
|
||||
CalendarRangeCreate,
|
||||
CalendarRangeEdit,
|
||||
isEventInputCalendarRange,
|
||||
} from "../../../../types";
|
||||
import { Location } from "../../../../../../../ChillMainBundle/Resources/public/types";
|
||||
import { fetchCalendarRangeForUser } from "../../../Calendar/api";
|
||||
import { calendarRangeToFullCalendarEvent } from "../../../Calendar/store/utils";
|
||||
import { EventInput } from "@fullcalendar/core";
|
||||
import { makeFetch } from "../../../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods";
|
||||
import {
|
||||
datetimeToISO,
|
||||
dateToISO,
|
||||
ISOToDatetime,
|
||||
} from "../../../../../../../ChillMainBundle/Resources/public/chill/js/date";
|
||||
import type {EventInputCalendarRange} from "../../../../types";
|
||||
import type { EventInputCalendarRange } from "../../../../types";
|
||||
|
||||
export interface CalendarRangesState {
|
||||
ranges: (EventInput | EventInputCalendarRange) [],
|
||||
rangesLoaded: { start: number, end: number }[],
|
||||
rangesIndex: Set<string>,
|
||||
key: number
|
||||
ranges: (EventInput | EventInputCalendarRange)[];
|
||||
rangesLoaded: { start: number; end: number }[];
|
||||
rangesIndex: Set<string>;
|
||||
key: number;
|
||||
}
|
||||
|
||||
type Context = ActionContext<CalendarRangesState, State>;
|
||||
|
||||
export default <Module<CalendarRangesState, State>>{
|
||||
namespaced: true,
|
||||
state: (): CalendarRangesState => ({
|
||||
ranges: [],
|
||||
rangesLoaded: [],
|
||||
rangesIndex: new Set<string>(),
|
||||
key: 0
|
||||
}),
|
||||
getters: {
|
||||
isRangeLoaded: (state: CalendarRangesState) => ({start, end}: { start: Date, end: Date }): boolean => {
|
||||
for (let range of state.rangesLoaded) {
|
||||
if (start.getTime() === range.start && end.getTime() === range.end) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: (): CalendarRangesState => ({
|
||||
ranges: [],
|
||||
rangesLoaded: [],
|
||||
rangesIndex: new Set<string>(),
|
||||
key: 0,
|
||||
}),
|
||||
getters: {
|
||||
isRangeLoaded:
|
||||
(state: CalendarRangesState) =>
|
||||
({ start, end }: { start: Date; end: Date }): boolean => {
|
||||
for (const range of state.rangesLoaded) {
|
||||
if (
|
||||
start.getTime() === range.start &&
|
||||
end.getTime() === range.end
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return false;
|
||||
},
|
||||
getRangesOnDate:
|
||||
(state: CalendarRangesState) =>
|
||||
(date: Date): EventInputCalendarRange[] => {
|
||||
const founds = [];
|
||||
const dateStr = dateToISO(date) as string;
|
||||
|
||||
for (const range of state.ranges) {
|
||||
if (
|
||||
isEventInputCalendarRange(range) &&
|
||||
range.start.startsWith(dateStr)
|
||||
) {
|
||||
founds.push(range);
|
||||
}
|
||||
}
|
||||
|
||||
return founds;
|
||||
},
|
||||
getRangesOnWeek:
|
||||
(state: CalendarRangesState) =>
|
||||
(mondayDate: Date): EventInputCalendarRange[] => {
|
||||
const founds = [];
|
||||
for (const d of Array.from(Array(7).keys())) {
|
||||
const dateOfWeek = new Date(mondayDate);
|
||||
dateOfWeek.setDate(mondayDate.getDate() + d);
|
||||
const dateStr = dateToISO(dateOfWeek) as string;
|
||||
for (const range of state.ranges) {
|
||||
if (
|
||||
isEventInputCalendarRange(range) &&
|
||||
range.start.startsWith(dateStr)
|
||||
) {
|
||||
founds.push(range);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return founds;
|
||||
},
|
||||
},
|
||||
getRangesOnDate: (state: CalendarRangesState) => (date: Date): EventInputCalendarRange[] => {
|
||||
const founds = [];
|
||||
const dateStr = <string>dateToISO(date);
|
||||
mutations: {
|
||||
addRanges(state: CalendarRangesState, ranges: CalendarRange[]) {
|
||||
const toAdd = ranges
|
||||
.map((cr) => calendarRangeToFullCalendarEvent(cr))
|
||||
.map((cr) => ({
|
||||
...cr,
|
||||
backgroundColor: "white",
|
||||
borderColor: "#3788d8",
|
||||
textColor: "black",
|
||||
}))
|
||||
.filter((r) => !state.rangesIndex.has(r.id));
|
||||
|
||||
for (let range of state.ranges) {
|
||||
if (isEventInputCalendarRange(range)
|
||||
&& range.start.startsWith(dateStr)
|
||||
toAdd.forEach((r) => {
|
||||
state.rangesIndex.add(r.id);
|
||||
state.ranges.push(r);
|
||||
});
|
||||
state.key = state.key + toAdd.length;
|
||||
},
|
||||
addExternals(state, externalEvents: (EventInput & { id: string })[]) {
|
||||
const toAdd = externalEvents.filter(
|
||||
(r) => !state.rangesIndex.has(r.id),
|
||||
);
|
||||
|
||||
toAdd.forEach((r) => {
|
||||
state.rangesIndex.add(r.id);
|
||||
state.ranges.push(r);
|
||||
});
|
||||
state.key = state.key + toAdd.length;
|
||||
},
|
||||
addLoaded(
|
||||
state: CalendarRangesState,
|
||||
payload: { start: Date; end: Date },
|
||||
) {
|
||||
founds.push(range);
|
||||
}
|
||||
}
|
||||
|
||||
return founds;
|
||||
},
|
||||
getRangesOnWeek: (state: CalendarRangesState) => (mondayDate: Date): EventInputCalendarRange[] => {
|
||||
const founds = [];
|
||||
for (let d of Array.from(Array(7).keys())) {
|
||||
const dateOfWeek = new Date(mondayDate);
|
||||
dateOfWeek.setDate(mondayDate.getDate() + d);
|
||||
const dateStr = <string>dateToISO(dateOfWeek);
|
||||
for (let range of state.ranges) {
|
||||
if (isEventInputCalendarRange(range)
|
||||
&& range.start.startsWith(dateStr)
|
||||
) {
|
||||
founds.push(range);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return founds;
|
||||
},
|
||||
},
|
||||
mutations: {
|
||||
addRanges(state: CalendarRangesState, ranges: CalendarRange[]) {
|
||||
const toAdd = ranges
|
||||
.map(cr => calendarRangeToFullCalendarEvent(cr))
|
||||
.map(cr => ({
|
||||
...cr, backgroundColor: 'white', borderColor: '#3788d8',
|
||||
textColor: 'black'
|
||||
}))
|
||||
.filter(r => !state.rangesIndex.has(r.id));
|
||||
|
||||
toAdd.forEach((r) => {
|
||||
state.rangesIndex.add(r.id);
|
||||
state.ranges.push(r);
|
||||
});
|
||||
state.key = state.key + toAdd.length;
|
||||
},
|
||||
addExternals(state, externalEvents: (EventInput & { id: string })[]) {
|
||||
const toAdd = externalEvents
|
||||
.filter(r => !state.rangesIndex.has(r.id));
|
||||
|
||||
toAdd.forEach((r) => {
|
||||
state.rangesIndex.add(r.id);
|
||||
state.ranges.push(r);
|
||||
});
|
||||
state.key = state.key + toAdd.length;
|
||||
},
|
||||
addLoaded(state: CalendarRangesState, payload: { start: Date, end: Date }) {
|
||||
state.rangesLoaded.push({start: payload.start.getTime(), end: payload.end.getTime()});
|
||||
},
|
||||
addRange(state: CalendarRangesState, payload: CalendarRange) {
|
||||
const asEvent = calendarRangeToFullCalendarEvent(payload);
|
||||
state.ranges.push({...asEvent, backgroundColor: 'white', borderColor: '#3788d8', textColor: 'black'});
|
||||
state.rangesIndex.add(asEvent.id);
|
||||
state.key = state.key + 1;
|
||||
},
|
||||
removeRange(state: CalendarRangesState, calendarRangeId: number) {
|
||||
const found = state.ranges.find(r => r.calendarRangeId === calendarRangeId && r.is === "range");
|
||||
|
||||
if (found !== undefined) {
|
||||
state.ranges = state.ranges.filter(
|
||||
(r) => !(r.calendarRangeId === calendarRangeId && r.is === "range")
|
||||
);
|
||||
|
||||
if (typeof found.id === "string") { // should always be true
|
||||
state.rangesIndex.delete(found.id);
|
||||
}
|
||||
|
||||
state.key = state.key + 1;
|
||||
}
|
||||
},
|
||||
updateRange(state, range: CalendarRange) {
|
||||
const found = state.ranges.find(r => r.calendarRangeId === range.id && r.is === "range");
|
||||
const newEvent = calendarRangeToFullCalendarEvent(range);
|
||||
|
||||
if (found !== undefined) {
|
||||
found.start = newEvent.start;
|
||||
found.end = newEvent.end;
|
||||
found.locationId = range.location.id;
|
||||
found.locationName = range.location.name;
|
||||
}
|
||||
|
||||
state.key = state.key + 1;
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
fetchRanges(ctx: Context, payload: { start: Date, end: Date }): Promise<null> {
|
||||
const start = payload.start;
|
||||
const end = payload.end;
|
||||
|
||||
if (ctx.rootGetters['me/getMe'] === null) {
|
||||
return Promise.resolve(ctx.getters.getRangeSource);
|
||||
}
|
||||
|
||||
if (ctx.getters.isRangeLoaded({start, end})) {
|
||||
return Promise.resolve(ctx.getters.getRangeSource);
|
||||
}
|
||||
|
||||
ctx.commit('addLoaded', {
|
||||
start: start,
|
||||
end: end,
|
||||
});
|
||||
|
||||
return fetchCalendarRangeForUser(
|
||||
ctx.rootGetters['me/getMe'],
|
||||
start,
|
||||
end
|
||||
)
|
||||
.then((ranges: CalendarRange[]) => {
|
||||
ctx.commit('addRanges', ranges);
|
||||
return Promise.resolve(null);
|
||||
});
|
||||
},
|
||||
createRange(ctx, {start, end, location}: { start: Date, end: Date, location: Location }): Promise<null> {
|
||||
const url = `/api/1.0/calendar/calendar-range.json?`;
|
||||
|
||||
if (ctx.rootState.me.me === null) {
|
||||
throw new Error('user is currently null');
|
||||
}
|
||||
|
||||
const body = {
|
||||
user: {
|
||||
id: ctx.rootState.me.me.id,
|
||||
type: "user"
|
||||
state.rangesLoaded.push({
|
||||
start: payload.start.getTime(),
|
||||
end: payload.end.getTime(),
|
||||
});
|
||||
},
|
||||
startDate: {
|
||||
datetime: datetimeToISO(start),
|
||||
addRange(state: CalendarRangesState, payload: CalendarRange) {
|
||||
const asEvent = calendarRangeToFullCalendarEvent(payload);
|
||||
state.ranges.push({
|
||||
...asEvent,
|
||||
backgroundColor: "white",
|
||||
borderColor: "#3788d8",
|
||||
textColor: "black",
|
||||
});
|
||||
state.rangesIndex.add(asEvent.id);
|
||||
state.key = state.key + 1;
|
||||
},
|
||||
endDate: {
|
||||
datetime: datetimeToISO(end)
|
||||
removeRange(state: CalendarRangesState, calendarRangeId: number) {
|
||||
const found = state.ranges.find(
|
||||
(r) =>
|
||||
r.calendarRangeId === calendarRangeId && r.is === "range",
|
||||
);
|
||||
|
||||
if (found !== undefined) {
|
||||
state.ranges = state.ranges.filter(
|
||||
(r) =>
|
||||
!(
|
||||
r.calendarRangeId === calendarRangeId &&
|
||||
r.is === "range"
|
||||
),
|
||||
);
|
||||
|
||||
if (typeof found.id === "string") {
|
||||
// should always be true
|
||||
state.rangesIndex.delete(found.id);
|
||||
}
|
||||
|
||||
state.key = state.key + 1;
|
||||
}
|
||||
},
|
||||
location: {
|
||||
id: location.id,
|
||||
type: "location"
|
||||
}
|
||||
} as CalendarRangeCreate;
|
||||
updateRange(state, range: CalendarRange) {
|
||||
const found = state.ranges.find(
|
||||
(r) => r.calendarRangeId === range.id && r.is === "range",
|
||||
);
|
||||
const newEvent = calendarRangeToFullCalendarEvent(range);
|
||||
|
||||
return makeFetch<CalendarRangeCreate, CalendarRange>('POST', url, body)
|
||||
.then((newRange) => {
|
||||
if (found !== undefined) {
|
||||
found.start = newEvent.start;
|
||||
found.end = newEvent.end;
|
||||
found.locationId = range.location.id;
|
||||
found.locationName = range.location.name;
|
||||
}
|
||||
|
||||
ctx.commit('addRange', newRange);
|
||||
|
||||
return Promise.resolve(null);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
|
||||
throw error;
|
||||
})
|
||||
},
|
||||
deleteRange(ctx, calendarRangeId: number) {
|
||||
const url = `/api/1.0/calendar/calendar-range/${calendarRangeId}.json`;
|
||||
|
||||
makeFetch<undefined, never>('DELETE', url)
|
||||
.then((_) => {
|
||||
ctx.commit('removeRange', calendarRangeId);
|
||||
});
|
||||
},
|
||||
patchRangeTime(ctx, {calendarRangeId, start, end}: {calendarRangeId: number, start: Date, end: Date}): Promise<null> {
|
||||
const url = `/api/1.0/calendar/calendar-range/${calendarRangeId}.json`;
|
||||
const body = {
|
||||
startDate: {
|
||||
datetime: datetimeToISO(start)
|
||||
state.key = state.key + 1;
|
||||
},
|
||||
endDate: {
|
||||
datetime: datetimeToISO(end)
|
||||
},
|
||||
actions: {
|
||||
fetchRanges(
|
||||
ctx: Context,
|
||||
payload: { start: Date; end: Date },
|
||||
): Promise<null> {
|
||||
const start = payload.start;
|
||||
const end = payload.end;
|
||||
|
||||
if (ctx.rootGetters["me/getMe"] === null) {
|
||||
return Promise.resolve(ctx.getters.getRangeSource);
|
||||
}
|
||||
|
||||
if (ctx.getters.isRangeLoaded({ start, end })) {
|
||||
return Promise.resolve(ctx.getters.getRangeSource);
|
||||
}
|
||||
|
||||
ctx.commit("addLoaded", {
|
||||
start: start,
|
||||
end: end,
|
||||
});
|
||||
|
||||
return fetchCalendarRangeForUser(
|
||||
ctx.rootGetters["me/getMe"],
|
||||
start,
|
||||
end,
|
||||
).then((ranges: CalendarRange[]) => {
|
||||
ctx.commit("addRanges", ranges);
|
||||
return Promise.resolve(null);
|
||||
});
|
||||
},
|
||||
} as CalendarRangeEdit;
|
||||
createRange(
|
||||
ctx,
|
||||
{
|
||||
start,
|
||||
end,
|
||||
location,
|
||||
}: { start: Date; end: Date; location: Location },
|
||||
): Promise<null> {
|
||||
const url = `/api/1.0/calendar/calendar-range.json?`;
|
||||
|
||||
return makeFetch<CalendarRangeEdit, CalendarRange>('PATCH', url, body)
|
||||
.then((range) => {
|
||||
ctx.commit('updateRange', range);
|
||||
return Promise.resolve(null);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
return Promise.resolve(null);
|
||||
})
|
||||
},
|
||||
patchRangeLocation(ctx, {location, calendarRangeId}: {location: Location, calendarRangeId: number}): Promise<null> {
|
||||
const url = `/api/1.0/calendar/calendar-range/${calendarRangeId}.json`;
|
||||
const body = {
|
||||
location: {
|
||||
id: location.id,
|
||||
type: "location"
|
||||
if (ctx.rootState.me.me === null) {
|
||||
throw new Error("user is currently null");
|
||||
}
|
||||
|
||||
const body = {
|
||||
user: {
|
||||
id: ctx.rootState.me.me.id,
|
||||
type: "user",
|
||||
},
|
||||
startDate: {
|
||||
datetime: datetimeToISO(start),
|
||||
},
|
||||
endDate: {
|
||||
datetime: datetimeToISO(end),
|
||||
},
|
||||
location: {
|
||||
id: location.id,
|
||||
type: "location",
|
||||
},
|
||||
} as CalendarRangeCreate;
|
||||
|
||||
return makeFetch<CalendarRangeCreate, CalendarRange>(
|
||||
"POST",
|
||||
url,
|
||||
body,
|
||||
)
|
||||
.then((newRange) => {
|
||||
ctx.commit("addRange", newRange);
|
||||
|
||||
return Promise.resolve(null);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
|
||||
throw error;
|
||||
});
|
||||
},
|
||||
} as CalendarRangeEdit;
|
||||
deleteRange(ctx, calendarRangeId: number) {
|
||||
const url = `/api/1.0/calendar/calendar-range/${calendarRangeId}.json`;
|
||||
|
||||
return makeFetch<CalendarRangeEdit, CalendarRange>('PATCH', url, body)
|
||||
.then((range) => {
|
||||
ctx.commit('updateRange', range);
|
||||
return Promise.resolve(null);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
return Promise.resolve(null);
|
||||
})
|
||||
makeFetch<undefined, never>("DELETE", url).then((_) => {
|
||||
ctx.commit("removeRange", calendarRangeId);
|
||||
});
|
||||
},
|
||||
patchRangeTime(
|
||||
ctx,
|
||||
{
|
||||
calendarRangeId,
|
||||
start,
|
||||
end,
|
||||
}: { calendarRangeId: number; start: Date; end: Date },
|
||||
): Promise<null> {
|
||||
const url = `/api/1.0/calendar/calendar-range/${calendarRangeId}.json`;
|
||||
const body = {
|
||||
startDate: {
|
||||
datetime: datetimeToISO(start),
|
||||
},
|
||||
endDate: {
|
||||
datetime: datetimeToISO(end),
|
||||
},
|
||||
} as CalendarRangeEdit;
|
||||
|
||||
return makeFetch<CalendarRangeEdit, CalendarRange>(
|
||||
"PATCH",
|
||||
url,
|
||||
body,
|
||||
)
|
||||
.then((range) => {
|
||||
ctx.commit("updateRange", range);
|
||||
return Promise.resolve(null);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
return Promise.resolve(null);
|
||||
});
|
||||
},
|
||||
patchRangeLocation(
|
||||
ctx,
|
||||
{
|
||||
location,
|
||||
calendarRangeId,
|
||||
}: { location: Location; calendarRangeId: number },
|
||||
): Promise<null> {
|
||||
const url = `/api/1.0/calendar/calendar-range/${calendarRangeId}.json`;
|
||||
const body = {
|
||||
location: {
|
||||
id: location.id,
|
||||
type: "location",
|
||||
},
|
||||
} as CalendarRangeEdit;
|
||||
|
||||
return makeFetch<CalendarRangeEdit, CalendarRange>(
|
||||
"PATCH",
|
||||
url,
|
||||
body,
|
||||
)
|
||||
.then((range) => {
|
||||
ctx.commit("updateRange", range);
|
||||
return Promise.resolve(null);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
return Promise.resolve(null);
|
||||
});
|
||||
},
|
||||
copyFromDayToAnotherDay(
|
||||
ctx,
|
||||
{ from, to }: { from: Date; to: Date },
|
||||
): Promise<null> {
|
||||
const rangesToCopy: EventInputCalendarRange[] =
|
||||
ctx.getters["getRangesOnDate"](from);
|
||||
const promises = [];
|
||||
|
||||
for (const r of rangesToCopy) {
|
||||
const start = new Date(ISOToDatetime(r.start) as Date);
|
||||
start.setFullYear(
|
||||
to.getFullYear(),
|
||||
to.getMonth(),
|
||||
to.getDate(),
|
||||
);
|
||||
const end = new Date(ISOToDatetime(r.end) as Date);
|
||||
end.setFullYear(to.getFullYear(), to.getMonth(), to.getDate());
|
||||
const location = ctx.rootGetters["locations/getLocationById"](
|
||||
r.locationId,
|
||||
);
|
||||
|
||||
promises.push(
|
||||
ctx.dispatch("createRange", { start, end, location }),
|
||||
);
|
||||
}
|
||||
|
||||
return Promise.all(promises).then((_) => Promise.resolve(null));
|
||||
},
|
||||
copyFromWeekToAnotherWeek(
|
||||
ctx,
|
||||
{ fromMonday, toMonday }: { fromMonday: Date; toMonday: Date },
|
||||
): Promise<null> {
|
||||
const rangesToCopy: EventInputCalendarRange[] =
|
||||
ctx.getters["getRangesOnWeek"](fromMonday);
|
||||
const promises = [];
|
||||
const diffTime = toMonday.getTime() - fromMonday.getTime();
|
||||
for (const r of rangesToCopy) {
|
||||
const start = new Date(ISOToDatetime(r.start) as Date);
|
||||
const end = new Date(ISOToDatetime(r.end) as Date);
|
||||
start.setTime(start.getTime() + diffTime);
|
||||
end.setTime(end.getTime() + diffTime);
|
||||
const location = ctx.rootGetters["locations/getLocationById"](
|
||||
r.locationId,
|
||||
);
|
||||
|
||||
promises.push(
|
||||
ctx.dispatch("createRange", { start, end, location }),
|
||||
);
|
||||
}
|
||||
|
||||
return Promise.all(promises).then((_) => Promise.resolve(null));
|
||||
},
|
||||
},
|
||||
copyFromDayToAnotherDay(ctx, {from, to}: {from: Date, to: Date}): Promise<null> {
|
||||
const rangesToCopy: EventInputCalendarRange[] = ctx.getters['getRangesOnDate'](from);
|
||||
const promises = [];
|
||||
|
||||
for (let r of rangesToCopy) {
|
||||
let start = new Date(<Date>ISOToDatetime(r.start));
|
||||
start.setFullYear(to.getFullYear(), to.getMonth(), to.getDate());
|
||||
let end = new Date(<Date>ISOToDatetime(r.end));
|
||||
end.setFullYear(to.getFullYear(), to.getMonth(), to.getDate());
|
||||
let location = ctx.rootGetters['locations/getLocationById'](r.locationId);
|
||||
|
||||
promises.push(ctx.dispatch('createRange', {start, end, location}));
|
||||
}
|
||||
|
||||
return Promise.all(promises).then(_ => Promise.resolve(null));
|
||||
},
|
||||
copyFromWeekToAnotherWeek(ctx, {fromMonday, toMonday}: {fromMonday: Date, toMonday: Date}): Promise<null> {
|
||||
|
||||
const rangesToCopy: EventInputCalendarRange[] = ctx.getters['getRangesOnWeek'](fromMonday);
|
||||
const promises = [];
|
||||
const diffTime = toMonday.getTime() - fromMonday.getTime();
|
||||
for (let r of rangesToCopy) {
|
||||
let start = new Date(<Date>ISOToDatetime(r.start));
|
||||
let end = new Date(<Date>ISOToDatetime(r.end));
|
||||
start.setTime(start.getTime() + diffTime);
|
||||
end.setTime(end.getTime() + diffTime);
|
||||
let location = ctx.rootGetters['locations/getLocationById'](r.locationId);
|
||||
|
||||
promises.push(ctx.dispatch('createRange', {start, end, location}));
|
||||
}
|
||||
|
||||
return Promise.all(promises).then(_ => Promise.resolve(null));
|
||||
}
|
||||
}
|
||||
};
|
||||
} as Module<CalendarRangesState, State>;
|
||||
|
@ -1,95 +1,116 @@
|
||||
import {State} from './../index';
|
||||
import {ActionContext, Module} from 'vuex';
|
||||
import {CalendarRemote} from '../../../../types';
|
||||
import {fetchCalendarRemoteForUser} from '../../../Calendar/api';
|
||||
import {EventInput} from '@fullcalendar/core';
|
||||
import {remoteToFullCalendarEvent} from "../../../Calendar/store/utils";
|
||||
import {TransportExceptionInterface} from "../../../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods";
|
||||
import {COLORS} from "../../../Calendar/const";
|
||||
import { State } from "./../index";
|
||||
import { ActionContext, Module } from "vuex";
|
||||
import { CalendarRemote } from "../../../../types";
|
||||
import { fetchCalendarRemoteForUser } from "../../../Calendar/api";
|
||||
import { EventInput } from "@fullcalendar/core";
|
||||
import { remoteToFullCalendarEvent } from "../../../Calendar/store/utils";
|
||||
import { TransportExceptionInterface } from "../../../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods";
|
||||
import { COLORS } from "../../../Calendar/const";
|
||||
|
||||
export interface CalendarRemotesState {
|
||||
remotes: EventInput[],
|
||||
remotesLoaded: {start: number, end: number}[],
|
||||
remotesIndex: Set<string>,
|
||||
key: number
|
||||
remotes: EventInput[];
|
||||
remotesLoaded: { start: number; end: number }[];
|
||||
remotesIndex: Set<string>;
|
||||
key: number;
|
||||
}
|
||||
|
||||
type Context = ActionContext<CalendarRemotesState, State>;
|
||||
|
||||
export default <Module<CalendarRemotesState, State>> {
|
||||
namespaced: true,
|
||||
state: (): CalendarRemotesState => ({
|
||||
remotes: [],
|
||||
remotesLoaded: [],
|
||||
remotesIndex: new Set<string>(),
|
||||
key: 0
|
||||
}),
|
||||
getters: {
|
||||
isRemotesLoaded: (state: CalendarRemotesState) => ({start, end}: {start: Date, end: Date}): boolean => {
|
||||
for (let range of state.remotesLoaded) {
|
||||
if (start.getTime() === range.start && end.getTime() === range.end) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: (): CalendarRemotesState => ({
|
||||
remotes: [],
|
||||
remotesLoaded: [],
|
||||
remotesIndex: new Set<string>(),
|
||||
key: 0,
|
||||
}),
|
||||
getters: {
|
||||
isRemotesLoaded:
|
||||
(state: CalendarRemotesState) =>
|
||||
({ start, end }: { start: Date; end: Date }): boolean => {
|
||||
for (const range of state.remotesLoaded) {
|
||||
if (
|
||||
start.getTime() === range.start &&
|
||||
end.getTime() === range.end
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return false;
|
||||
},
|
||||
},
|
||||
},
|
||||
mutations: {
|
||||
addRemotes(state: CalendarRemotesState, ranges: CalendarRemote[]) {
|
||||
console.log('addRemotes', ranges);
|
||||
mutations: {
|
||||
addRemotes(state: CalendarRemotesState, ranges: CalendarRemote[]) {
|
||||
console.log("addRemotes", ranges);
|
||||
|
||||
const toAdd = ranges
|
||||
.map(cr => remoteToFullCalendarEvent(cr))
|
||||
.filter(r => !state.remotesIndex.has(r.id));
|
||||
const toAdd = ranges
|
||||
.map((cr) => remoteToFullCalendarEvent(cr))
|
||||
.filter((r) => !state.remotesIndex.has(r.id));
|
||||
|
||||
toAdd.forEach((r) => {
|
||||
state.remotesIndex.add(r.id);
|
||||
state.remotes.push(r);
|
||||
});
|
||||
state.key = state.key + toAdd.length;
|
||||
toAdd.forEach((r) => {
|
||||
state.remotesIndex.add(r.id);
|
||||
state.remotes.push(r);
|
||||
});
|
||||
state.key = state.key + toAdd.length;
|
||||
},
|
||||
addLoaded(
|
||||
state: CalendarRemotesState,
|
||||
payload: { start: Date; end: Date },
|
||||
) {
|
||||
state.remotesLoaded.push({
|
||||
start: payload.start.getTime(),
|
||||
end: payload.end.getTime(),
|
||||
});
|
||||
},
|
||||
},
|
||||
addLoaded(state: CalendarRemotesState, payload: {start: Date, end: Date}) {
|
||||
state.remotesLoaded.push({start: payload.start.getTime(), end: payload.end.getTime()});
|
||||
actions: {
|
||||
fetchRemotes(
|
||||
ctx: Context,
|
||||
payload: { start: Date; end: Date },
|
||||
): Promise<null> {
|
||||
const start = payload.start;
|
||||
const end = payload.end;
|
||||
|
||||
if (ctx.rootGetters["me/getMe"] === null) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
if (ctx.getters.isRemotesLoaded({ start, end })) {
|
||||
return Promise.resolve(ctx.getters.getRangeSource);
|
||||
}
|
||||
|
||||
ctx.commit("addLoaded", {
|
||||
start: start,
|
||||
end: end,
|
||||
});
|
||||
|
||||
return fetchCalendarRemoteForUser(
|
||||
ctx.rootGetters["me/getMe"],
|
||||
start,
|
||||
end,
|
||||
)
|
||||
.then((remotes: CalendarRemote[]) => {
|
||||
// to be add when reactivity problem will be solve ?
|
||||
//ctx.commit('addRemotes', remotes);
|
||||
const inputs = remotes
|
||||
.map((cr) => remoteToFullCalendarEvent(cr))
|
||||
.map((cr) => ({
|
||||
...cr,
|
||||
backgroundColor: COLORS[0],
|
||||
textColor: "black",
|
||||
editable: false,
|
||||
}));
|
||||
ctx.commit("calendarRanges/addExternals", inputs, {
|
||||
root: true,
|
||||
});
|
||||
return Promise.resolve(null);
|
||||
})
|
||||
.catch((e: TransportExceptionInterface) => {
|
||||
console.error(e);
|
||||
|
||||
return Promise.resolve(null);
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
fetchRemotes(ctx: Context, payload: {start: Date, end: Date}): Promise<null> {
|
||||
const start = payload.start;
|
||||
const end = payload.end;
|
||||
|
||||
if (ctx.rootGetters['me/getMe'] === null) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
if (ctx.getters.isRemotesLoaded({start, end})) {
|
||||
return Promise.resolve(ctx.getters.getRangeSource);
|
||||
}
|
||||
|
||||
ctx.commit('addLoaded', {
|
||||
start: start,
|
||||
end: end,
|
||||
});
|
||||
|
||||
return fetchCalendarRemoteForUser(
|
||||
ctx.rootGetters['me/getMe'],
|
||||
start,
|
||||
end
|
||||
)
|
||||
.then((remotes: CalendarRemote[]) => {
|
||||
// to be add when reactivity problem will be solve ?
|
||||
//ctx.commit('addRemotes', remotes);
|
||||
const inputs = remotes
|
||||
.map(cr => remoteToFullCalendarEvent(cr))
|
||||
.map(cr => ({...cr, backgroundColor: COLORS[0], textColor: 'black', editable: false}))
|
||||
ctx.commit('calendarRanges/addExternals', inputs, {root: true});
|
||||
return Promise.resolve(null);
|
||||
})
|
||||
.catch((e: TransportExceptionInterface) => {
|
||||
console.error(e);
|
||||
|
||||
return Promise.resolve(null);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
} as Module<CalendarRemotesState, State>;
|
||||
|
@ -1,56 +1,78 @@
|
||||
import {State} from './../index';
|
||||
import {ActionContext} from 'vuex';
|
||||
import { State } from "./../index";
|
||||
import { ActionContext } from "vuex";
|
||||
|
||||
export interface FullCalendarState {
|
||||
currentView: {
|
||||
start: Date|null,
|
||||
end: Date|null,
|
||||
},
|
||||
key: number
|
||||
currentView: {
|
||||
start: Date | null;
|
||||
end: Date | null;
|
||||
};
|
||||
key: number;
|
||||
}
|
||||
|
||||
type Context = ActionContext<FullCalendarState, State>;
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: (): FullCalendarState => ({
|
||||
currentView: {
|
||||
start: null,
|
||||
end: null,
|
||||
namespaced: true,
|
||||
state: (): FullCalendarState => ({
|
||||
currentView: {
|
||||
start: null,
|
||||
end: null,
|
||||
},
|
||||
key: 0,
|
||||
}),
|
||||
mutations: {
|
||||
setCurrentDatesView: function (
|
||||
state: FullCalendarState,
|
||||
payload: { start: Date; end: Date },
|
||||
): void {
|
||||
state.currentView.start = payload.start;
|
||||
state.currentView.end = payload.end;
|
||||
},
|
||||
increaseKey: function (state: FullCalendarState): void {
|
||||
state.key = state.key + 1;
|
||||
},
|
||||
},
|
||||
key: 0,
|
||||
}),
|
||||
mutations: {
|
||||
setCurrentDatesView: function(state: FullCalendarState, payload: {start: Date, end: Date}): void {
|
||||
state.currentView.start = payload.start;
|
||||
state.currentView.end = payload.end;
|
||||
actions: {
|
||||
setCurrentDatesView(
|
||||
ctx: Context,
|
||||
{ start, end }: { start: Date | null; end: Date | null },
|
||||
): Promise<null> {
|
||||
console.log("dispatch setCurrentDatesView", { start, end });
|
||||
|
||||
if (
|
||||
ctx.state.currentView.start !== start ||
|
||||
ctx.state.currentView.end !== end
|
||||
) {
|
||||
ctx.commit("setCurrentDatesView", { start, end });
|
||||
}
|
||||
|
||||
if (start !== null && end !== null) {
|
||||
return Promise.all([
|
||||
ctx
|
||||
.dispatch(
|
||||
"calendarRanges/fetchRanges",
|
||||
{ start, end },
|
||||
{ root: true },
|
||||
)
|
||||
.then((_) => Promise.resolve(null)),
|
||||
ctx
|
||||
.dispatch(
|
||||
"calendarRemotes/fetchRemotes",
|
||||
{ start, end },
|
||||
{ root: true },
|
||||
)
|
||||
.then((_) => Promise.resolve(null)),
|
||||
ctx
|
||||
.dispatch(
|
||||
"calendarLocals/fetchLocals",
|
||||
{ start, end },
|
||||
{ root: true },
|
||||
)
|
||||
.then((_) => Promise.resolve(null)),
|
||||
]).then((_) => Promise.resolve(null));
|
||||
} else {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
},
|
||||
},
|
||||
increaseKey: function(state: FullCalendarState): void {
|
||||
state.key = state.key + 1;
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
setCurrentDatesView(ctx: Context, {start, end}: {start: Date|null, end: Date|null}): Promise<null> {
|
||||
console.log('dispatch setCurrentDatesView', {start, end});
|
||||
|
||||
if (ctx.state.currentView.start !== start || ctx.state.currentView.end !== end) {
|
||||
ctx.commit('setCurrentDatesView', {start, end});
|
||||
}
|
||||
|
||||
if (start !== null && end !== null) {
|
||||
|
||||
return Promise.all([
|
||||
ctx.dispatch('calendarRanges/fetchRanges', {start, end}, {root: true}).then(_ => Promise.resolve(null)),
|
||||
ctx.dispatch('calendarRemotes/fetchRemotes', {start, end}, {root: true}).then(_ => Promise.resolve(null)),
|
||||
ctx.dispatch('calendarLocals/fetchLocals', {start, end}, {root: true}).then(_ => Promise.resolve(null))
|
||||
]
|
||||
).then(_ => Promise.resolve(null));
|
||||
|
||||
} else {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
@ -1,62 +1,65 @@
|
||||
import {Location} from "../../../../../../../ChillMainBundle/Resources/public/types";
|
||||
import {State} from '../index';
|
||||
import {Module} from 'vuex';
|
||||
import {getLocations} from "../../../../../../../ChillMainBundle/Resources/public/lib/api/locations";
|
||||
import {whereami} from "../../../../../../../ChillMainBundle/Resources/public/lib/api/user";
|
||||
import { Location } from "../../../../../../../ChillMainBundle/Resources/public/types";
|
||||
import { State } from "../index";
|
||||
import { Module } from "vuex";
|
||||
import { getLocations } from "../../../../../../../ChillMainBundle/Resources/public/lib/api/locations";
|
||||
import { whereami } from "../../../../../../../ChillMainBundle/Resources/public/lib/api/user";
|
||||
|
||||
export interface LocationState {
|
||||
locations: Location[];
|
||||
locationPicked: Location | null;
|
||||
currentLocation: Location | null;
|
||||
locations: Location[];
|
||||
locationPicked: Location | null;
|
||||
currentLocation: Location | null;
|
||||
}
|
||||
|
||||
export default <Module<LocationState, State>>{
|
||||
namespaced: true,
|
||||
state: (): LocationState => {
|
||||
return {
|
||||
locations: [],
|
||||
locationPicked: null,
|
||||
currentLocation: null,
|
||||
}
|
||||
},
|
||||
getters: {
|
||||
getLocationById: (state) => (id: number): Location|undefined => {
|
||||
return state.locations.find(l => l.id === id);
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: (): LocationState => {
|
||||
return {
|
||||
locations: [],
|
||||
locationPicked: null,
|
||||
currentLocation: null,
|
||||
};
|
||||
},
|
||||
},
|
||||
mutations: {
|
||||
setLocations(state, locations): void {
|
||||
state.locations = locations;
|
||||
getters: {
|
||||
getLocationById:
|
||||
(state) =>
|
||||
(id: number): Location | undefined => {
|
||||
return state.locations.find((l) => l.id === id);
|
||||
},
|
||||
},
|
||||
setLocationPicked(state, location: Location | null): void {
|
||||
if (null === location) {
|
||||
state.locationPicked = null;
|
||||
return;
|
||||
}
|
||||
mutations: {
|
||||
setLocations(state, locations): void {
|
||||
state.locations = locations;
|
||||
},
|
||||
setLocationPicked(state, location: Location | null): void {
|
||||
if (null === location) {
|
||||
state.locationPicked = null;
|
||||
return;
|
||||
}
|
||||
|
||||
state.locationPicked = state.locations.find(l => l.id === location.id) || null;
|
||||
},
|
||||
setCurrentLocation(state, location: Location | null): void {
|
||||
if (null === location) {
|
||||
state.currentLocation = null;
|
||||
return;
|
||||
}
|
||||
state.locationPicked =
|
||||
state.locations.find((l) => l.id === location.id) || null;
|
||||
},
|
||||
setCurrentLocation(state, location: Location | null): void {
|
||||
if (null === location) {
|
||||
state.currentLocation = null;
|
||||
return;
|
||||
}
|
||||
|
||||
state.currentLocation = state.locations.find(l => l.id === location.id) || null;
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
getLocations(ctx): Promise<void> {
|
||||
return getLocations().then(locations => {
|
||||
ctx.commit('setLocations', locations);
|
||||
return Promise.resolve();
|
||||
});
|
||||
state.currentLocation =
|
||||
state.locations.find((l) => l.id === location.id) || null;
|
||||
},
|
||||
},
|
||||
getCurrentLocation(ctx): Promise<void> {
|
||||
return whereami().then(location => {
|
||||
ctx.commit('setCurrentLocation', location);
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
actions: {
|
||||
getLocations(ctx): Promise<void> {
|
||||
return getLocations().then((locations) => {
|
||||
ctx.commit("setLocations", locations);
|
||||
return Promise.resolve();
|
||||
});
|
||||
},
|
||||
getCurrentLocation(ctx): Promise<void> {
|
||||
return whereami().then((location) => {
|
||||
ctx.commit("setCurrentLocation", location);
|
||||
});
|
||||
},
|
||||
},
|
||||
} as Module<LocationState, State>;
|
||||
|
@ -1,29 +1,26 @@
|
||||
import {State} from './../index';
|
||||
import {User} from '../../../../../../../ChillMainBundle/Resources/public/types';
|
||||
import {ActionContext} from 'vuex';
|
||||
import { State } from "./../index";
|
||||
import { User } from "../../../../../../../ChillMainBundle/Resources/public/types";
|
||||
import { ActionContext } from "vuex";
|
||||
|
||||
export interface MeState {
|
||||
me: User|null,
|
||||
me: User | null;
|
||||
}
|
||||
|
||||
type Context = ActionContext<MeState, State>;
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: (): MeState => ({
|
||||
me: null,
|
||||
}),
|
||||
getters: {
|
||||
getMe: function(state: MeState): User|null {
|
||||
return state.me;
|
||||
namespaced: true,
|
||||
state: (): MeState => ({
|
||||
me: null,
|
||||
}),
|
||||
getters: {
|
||||
getMe: function (state: MeState): User | null {
|
||||
return state.me;
|
||||
},
|
||||
},
|
||||
},
|
||||
mutations: {
|
||||
setWhoAmi(state: MeState, me: User) {
|
||||
state.me = me;
|
||||
mutations: {
|
||||
setWhoAmi(state: MeState, me: User) {
|
||||
state.me = me;
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
@ -1,103 +1,111 @@
|
||||
/*
|
||||
* Endpoint chill_api_single_calendar_range
|
||||
* method GET, get Calendar ranges
|
||||
* @returns {Promise} a promise containing all Calendar ranges objects
|
||||
*/
|
||||
* Endpoint chill_api_single_calendar_range
|
||||
* method GET, get Calendar ranges
|
||||
* @returns {Promise} a promise containing all Calendar ranges objects
|
||||
*/
|
||||
const fetchCalendarRanges = () => {
|
||||
return Promise.resolve([]);
|
||||
const url = `/api/1.0/calendar/calendar-range-available.json`;
|
||||
return fetch(url)
|
||||
.then(response => {
|
||||
if (response.ok) { return response.json(); }
|
||||
throw Error('Error with request resource response');
|
||||
});
|
||||
const url = `/api/1.0/calendar/calendar-range-available.json`;
|
||||
return fetch(url).then((response) => {
|
||||
if (response.ok) {
|
||||
return response.json();
|
||||
}
|
||||
throw Error("Error with request resource response");
|
||||
});
|
||||
};
|
||||
|
||||
const fetchCalendarRangesByUser = (userId) => {
|
||||
return Promise.resolve([]);
|
||||
const url = `/api/1.0/calendar/calendar-range-available.json?user=${userId}`;
|
||||
return fetch(url)
|
||||
.then(response => {
|
||||
if (response.ok) { return response.json(); }
|
||||
throw Error('Error with request resource response');
|
||||
});
|
||||
const url = `/api/1.0/calendar/calendar-range-available.json?user=${userId}`;
|
||||
return fetch(url).then((response) => {
|
||||
if (response.ok) {
|
||||
return response.json();
|
||||
}
|
||||
throw Error("Error with request resource response");
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
* Endpoint chill_api_single_calendar
|
||||
* method GET, get Calendar events, can be filtered by mainUser
|
||||
* @returns {Promise} a promise containing all Calendar objects
|
||||
*/
|
||||
* Endpoint chill_api_single_calendar
|
||||
* method GET, get Calendar events, can be filtered by mainUser
|
||||
* @returns {Promise} a promise containing all Calendar objects
|
||||
*/
|
||||
const fetchCalendar = (mainUserId) => {
|
||||
return Promise.resolve([]);
|
||||
const url = `/api/1.0/calendar/calendar.json?main_user=${mainUserId}&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/calendar/calendar.json?main_user=${mainUserId}&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_calendar_range__entity_create
|
||||
* method POST, post CalendarRange entity
|
||||
*/
|
||||
* Endpoint chill_api_single_calendar_range__entity_create
|
||||
* method POST, post CalendarRange entity
|
||||
*/
|
||||
const postCalendarRange = (body) => {
|
||||
const url = `/api/1.0/calendar/calendar-range.json?`;
|
||||
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');
|
||||
});
|
||||
const url = `/api/1.0/calendar/calendar-range.json?`;
|
||||
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_calendar_range__entity
|
||||
* method PATCH, patch CalendarRange entity
|
||||
*/
|
||||
* Endpoint chill_api_single_calendar_range__entity
|
||||
* method PATCH, patch CalendarRange entity
|
||||
*/
|
||||
const patchCalendarRange = (id, body) => {
|
||||
console.log(body)
|
||||
const url = `/api/1.0/calendar/calendar-range/${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');
|
||||
});
|
||||
console.log(body);
|
||||
const url = `/api/1.0/calendar/calendar-range/${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_calendar_range__entity
|
||||
* method DELETE, delete CalendarRange entity
|
||||
*/
|
||||
* Endpoint chill_api_single_calendar_range__entity
|
||||
* method DELETE, delete CalendarRange entity
|
||||
*/
|
||||
const deleteCalendarRange = (id) => {
|
||||
const url = `/api/1.0/calendar/calendar-range/${id}.json`;
|
||||
return fetch(url, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json;charset=utf-8'
|
||||
},
|
||||
}).then(response => {
|
||||
if (response.ok) { return response.json(); }
|
||||
throw Error('Error with request resource response');
|
||||
});
|
||||
const url = `/api/1.0/calendar/calendar-range/${id}.json`;
|
||||
return fetch(url, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json;charset=utf-8",
|
||||
},
|
||||
}).then((response) => {
|
||||
if (response.ok) {
|
||||
return response.json();
|
||||
}
|
||||
throw Error("Error with request resource response");
|
||||
});
|
||||
};
|
||||
|
||||
export {
|
||||
fetchCalendarRanges,
|
||||
fetchCalendar,
|
||||
fetchCalendarRangesByUser,
|
||||
postCalendarRange,
|
||||
patchCalendarRange,
|
||||
deleteCalendarRange
|
||||
fetchCalendarRanges,
|
||||
fetchCalendar,
|
||||
fetchCalendarRangesByUser,
|
||||
postCalendarRange,
|
||||
patchCalendarRange,
|
||||
deleteCalendarRange,
|
||||
};
|
||||
|
@ -1,206 +1,259 @@
|
||||
<template>
|
||||
<div>
|
||||
<h2 class="chill-red">{{ $t('choose_your_calendar_user') }}</h2>
|
||||
<VueMultiselect
|
||||
name="field"
|
||||
id="calendarUserSelector"
|
||||
v-model="value"
|
||||
track-by="id"
|
||||
label="value"
|
||||
:custom-label="transName"
|
||||
:placeholder="$t('select_user')"
|
||||
:multiple="true"
|
||||
:close-on-select="false"
|
||||
:allow-empty="true"
|
||||
:model-value="value"
|
||||
:select-label="$t('multiselect.select_label')"
|
||||
:deselect-label="$t('multiselect.deselect_label')"
|
||||
:selected-label="$t('multiselect.selected_label')"
|
||||
@select="selectUsers"
|
||||
@remove="unSelectUsers"
|
||||
@close="coloriseSelectedValues"
|
||||
:options="options">
|
||||
</VueMultiselect>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input type="checkbox" id="myCalendar" class="form-check-input" v-model="showMyCalendarWidget" />
|
||||
<label class="form-check-label" for="myCalendar">{{ $t('show_my_calendar') }}</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input type="checkbox" id="weekends" class="form-check-input" @click="toggleWeekends" />
|
||||
<label class="form-check-label" for="weekends">{{ $t('show_weekends') }}</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 class="chill-red">
|
||||
{{ $t("choose_your_calendar_user") }}
|
||||
</h2>
|
||||
<VueMultiselect
|
||||
name="field"
|
||||
id="calendarUserSelector"
|
||||
v-model="value"
|
||||
track-by="id"
|
||||
label="value"
|
||||
:custom-label="transName"
|
||||
:placeholder="$t('select_user')"
|
||||
:multiple="true"
|
||||
:close-on-select="false"
|
||||
:allow-empty="true"
|
||||
:model-value="value"
|
||||
:select-label="$t('multiselect.select_label')"
|
||||
:deselect-label="$t('multiselect.deselect_label')"
|
||||
:selected-label="$t('multiselect.selected_label')"
|
||||
@select="selectUsers"
|
||||
@remove="unSelectUsers"
|
||||
@close="coloriseSelectedValues"
|
||||
:options="options"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="myCalendar"
|
||||
class="form-check-input"
|
||||
v-model="showMyCalendarWidget"
|
||||
/>
|
||||
<label class="form-check-label" for="myCalendar">{{
|
||||
$t("show_my_calendar")
|
||||
}}</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="weekends"
|
||||
class="form-check-input"
|
||||
@click="toggleWeekends"
|
||||
/>
|
||||
<label class="form-check-label" for="weekends">{{
|
||||
$t("show_weekends")
|
||||
}}</label>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { fetchCalendarRanges, fetchCalendar } from "../../_api/api";
|
||||
import VueMultiselect from "vue-multiselect";
|
||||
import { whoami } from "ChillPersonAssets/vuejs/AccompanyingCourse/api";
|
||||
|
||||
import { fetchCalendarRanges, fetchCalendar } from '../../_api/api'
|
||||
import VueMultiselect from 'vue-multiselect';
|
||||
import { whoami } from 'ChillPersonAssets/vuejs/AccompanyingCourse/api';
|
||||
|
||||
const COLORS = [ /* from https://colorbrewer2.org/#type=qualitative&scheme=Set3&n=12 */
|
||||
'#8dd3c7',
|
||||
'#ffffb3',
|
||||
'#bebada',
|
||||
'#fb8072',
|
||||
'#80b1d3',
|
||||
'#fdb462',
|
||||
'#b3de69',
|
||||
'#fccde5',
|
||||
'#d9d9d9',
|
||||
'#bc80bd',
|
||||
'#ccebc5',
|
||||
'#ffed6f'
|
||||
const COLORS = [
|
||||
/* from https://colorbrewer2.org/#type=qualitative&scheme=Set3&n=12 */
|
||||
"#8dd3c7",
|
||||
"#ffffb3",
|
||||
"#bebada",
|
||||
"#fb8072",
|
||||
"#80b1d3",
|
||||
"#fdb462",
|
||||
"#b3de69",
|
||||
"#fccde5",
|
||||
"#d9d9d9",
|
||||
"#bc80bd",
|
||||
"#ccebc5",
|
||||
"#ffed6f",
|
||||
];
|
||||
|
||||
export default {
|
||||
name: 'CalendarUserSelector',
|
||||
components: { VueMultiselect },
|
||||
props: ['users', 'updateEventsSource', 'calendarEvents', 'showMyCalendar', 'toggleMyCalendar', 'toggleWeekends'],
|
||||
data() {
|
||||
return {
|
||||
errorMsg: [],
|
||||
value: [],
|
||||
options: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
showMyCalendarWidget: {
|
||||
set(value) {
|
||||
this.toggleMyCalendar(value);
|
||||
this.updateEventsSource();
|
||||
},
|
||||
get() {
|
||||
return this.showMyCalendar;
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
this.fetchData()
|
||||
name: "CalendarUserSelector",
|
||||
components: { VueMultiselect },
|
||||
props: [
|
||||
"users",
|
||||
"updateEventsSource",
|
||||
"calendarEvents",
|
||||
"showMyCalendar",
|
||||
"toggleMyCalendar",
|
||||
"toggleWeekends",
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
errorMsg: [],
|
||||
value: [],
|
||||
options: [],
|
||||
};
|
||||
},
|
||||
fetchData() {
|
||||
fetchCalendarRanges().then(calendarRanges => new Promise((resolve, reject) => {
|
||||
let results = calendarRanges.results;
|
||||
|
||||
let users = [];
|
||||
|
||||
results.forEach(i => {
|
||||
if (!(users.some(j => i.user.id === j.id))){
|
||||
let ratio = Math.floor(users.length / COLORS.length);
|
||||
let colorIndex = users.length - ratio * COLORS.length;
|
||||
users.push({
|
||||
id: i.user.id,
|
||||
username: i.user.username,
|
||||
color: COLORS[colorIndex]
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
let calendarEvents = [];
|
||||
users.forEach(u => {
|
||||
let arr = results.filter(i => i.user.id === u.id).map(i =>
|
||||
({
|
||||
start: i.startDate.datetime,
|
||||
end: i.endDate.datetime,
|
||||
calendarRangeId: i.id,
|
||||
sourceColor: u.color
|
||||
//display: 'background' // can be an option for the disponibility
|
||||
})
|
||||
);
|
||||
calendarEvents.push({
|
||||
events: arr,
|
||||
color: u.color,
|
||||
textColor: '#444444',
|
||||
editable: false,
|
||||
id: u.id
|
||||
})
|
||||
})
|
||||
|
||||
this.users.loaded = users;
|
||||
this.options = users;
|
||||
|
||||
this.calendarEvents.loaded = calendarEvents;
|
||||
whoami().then(me => new Promise((resolve, reject) => {
|
||||
this.users.logged = me;
|
||||
let currentUser = users.find(u => u.id === me.id);
|
||||
this.value = currentUser;
|
||||
|
||||
fetchCalendar(currentUser.id).then(calendar => new Promise((resolve, reject) => {
|
||||
let results = calendar.results;
|
||||
let events = results.map(i =>
|
||||
({
|
||||
start: i.startDate.datetime,
|
||||
end: i.endDate.datetime,
|
||||
})
|
||||
);
|
||||
let calendarEventsCurrentUser = {
|
||||
events: events,
|
||||
color: 'darkblue',
|
||||
id: 1000,
|
||||
editable: false
|
||||
};
|
||||
this.calendarEvents.user = calendarEventsCurrentUser;
|
||||
|
||||
this.selectUsers(currentUser);
|
||||
|
||||
resolve();
|
||||
}));
|
||||
|
||||
resolve();
|
||||
}));
|
||||
|
||||
resolve();
|
||||
}))
|
||||
.catch((error) => {
|
||||
this.errorMsg.push(error.message);
|
||||
});
|
||||
computed: {
|
||||
showMyCalendarWidget: {
|
||||
set(value) {
|
||||
this.toggleMyCalendar(value);
|
||||
this.updateEventsSource();
|
||||
},
|
||||
get() {
|
||||
return this.showMyCalendar;
|
||||
},
|
||||
},
|
||||
},
|
||||
transName(value) {
|
||||
return `${value.username}`;
|
||||
},
|
||||
coloriseSelectedValues() {
|
||||
let tags = document.querySelectorAll('div.multiselect__tags-wrap')[0];
|
||||
methods: {
|
||||
init() {
|
||||
this.fetchData();
|
||||
},
|
||||
fetchData() {
|
||||
fetchCalendarRanges()
|
||||
.then(
|
||||
(calendarRanges) =>
|
||||
new Promise((resolve, reject) => {
|
||||
let results = calendarRanges.results;
|
||||
|
||||
if (tags.hasChildNodes()) {
|
||||
let children = tags.childNodes;
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
let child = children[i];
|
||||
if (child.nodeType === Node.ELEMENT_NODE) {
|
||||
this.users.selected.forEach(u => {
|
||||
if (child.hasChildNodes()) {
|
||||
if (child.firstChild.innerText == u.username) {
|
||||
child.style.background = u.color;
|
||||
child.firstChild.style.color = '#444444';
|
||||
}
|
||||
let users = [];
|
||||
|
||||
results.forEach((i) => {
|
||||
if (!users.some((j) => i.user.id === j.id)) {
|
||||
let ratio = Math.floor(
|
||||
users.length / COLORS.length,
|
||||
);
|
||||
let colorIndex =
|
||||
users.length - ratio * COLORS.length;
|
||||
users.push({
|
||||
id: i.user.id,
|
||||
username: i.user.username,
|
||||
color: COLORS[colorIndex],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
let calendarEvents = [];
|
||||
users.forEach((u) => {
|
||||
let arr = results
|
||||
.filter((i) => i.user.id === u.id)
|
||||
.map((i) => ({
|
||||
start: i.startDate.datetime,
|
||||
end: i.endDate.datetime,
|
||||
calendarRangeId: i.id,
|
||||
sourceColor: u.color,
|
||||
//display: 'background' // can be an option for the disponibility
|
||||
}));
|
||||
calendarEvents.push({
|
||||
events: arr,
|
||||
color: u.color,
|
||||
textColor: "#444444",
|
||||
editable: false,
|
||||
id: u.id,
|
||||
});
|
||||
});
|
||||
|
||||
this.users.loaded = users;
|
||||
this.options = users;
|
||||
|
||||
this.calendarEvents.loaded = calendarEvents;
|
||||
whoami().then(
|
||||
(me) =>
|
||||
new Promise((resolve, reject) => {
|
||||
this.users.logged = me;
|
||||
let currentUser = users.find(
|
||||
(u) => u.id === me.id,
|
||||
);
|
||||
this.value = currentUser;
|
||||
|
||||
fetchCalendar(currentUser.id).then(
|
||||
(calendar) =>
|
||||
new Promise(
|
||||
(resolve, reject) => {
|
||||
let results =
|
||||
calendar.results;
|
||||
let events =
|
||||
results.map(
|
||||
(i) => ({
|
||||
start: i
|
||||
.startDate
|
||||
.datetime,
|
||||
end: i
|
||||
.endDate
|
||||
.datetime,
|
||||
}),
|
||||
);
|
||||
let calendarEventsCurrentUser =
|
||||
{
|
||||
events: events,
|
||||
color: "darkblue",
|
||||
id: 1000,
|
||||
editable: false,
|
||||
};
|
||||
this.calendarEvents.user =
|
||||
calendarEventsCurrentUser;
|
||||
|
||||
this.selectUsers(
|
||||
currentUser,
|
||||
);
|
||||
|
||||
resolve();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
resolve();
|
||||
}),
|
||||
);
|
||||
|
||||
resolve();
|
||||
}),
|
||||
)
|
||||
.catch((error) => {
|
||||
this.errorMsg.push(error.message);
|
||||
});
|
||||
},
|
||||
transName(value) {
|
||||
return `${value.username}`;
|
||||
},
|
||||
coloriseSelectedValues() {
|
||||
let tags = document.querySelectorAll(
|
||||
"div.multiselect__tags-wrap",
|
||||
)[0];
|
||||
|
||||
if (tags.hasChildNodes()) {
|
||||
let children = tags.childNodes;
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
let child = children[i];
|
||||
if (child.nodeType === Node.ELEMENT_NODE) {
|
||||
this.users.selected.forEach((u) => {
|
||||
if (child.hasChildNodes()) {
|
||||
if (child.firstChild.innerText == u.username) {
|
||||
child.style.background = u.color;
|
||||
child.firstChild.style.color = "#444444";
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
selectEvents() {
|
||||
let selectedUsersId = this.users.selected.map((a) => a.id);
|
||||
this.calendarEvents.selected = this.calendarEvents.loaded.filter(
|
||||
(a) => selectedUsersId.includes(a.id),
|
||||
);
|
||||
},
|
||||
selectUsers(value) {
|
||||
this.users.selected.push(value);
|
||||
this.coloriseSelectedValues();
|
||||
this.selectEvents();
|
||||
this.updateEventsSource();
|
||||
},
|
||||
unSelectUsers(value) {
|
||||
this.users.selected = this.users.selected.filter(
|
||||
(a) => a.id != value.id,
|
||||
);
|
||||
this.selectEvents();
|
||||
this.updateEventsSource();
|
||||
},
|
||||
},
|
||||
selectEvents() {
|
||||
let selectedUsersId = this.users.selected.map(a => a.id);
|
||||
this.calendarEvents.selected = this.calendarEvents.loaded.filter(a => selectedUsersId.includes(a.id));
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
selectUsers(value) {
|
||||
this.users.selected.push(value);
|
||||
this.coloriseSelectedValues();
|
||||
this.selectEvents();
|
||||
this.updateEventsSource();
|
||||
},
|
||||
unSelectUsers(value) {
|
||||
this.users.selected = this.users.selected.filter(a => a.id != value.id);
|
||||
this.selectEvents();
|
||||
this.updateEventsSource();
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style src="vue-multiselect/dist/vue-multiselect.css"></style>
|
||||
|
||||
|
@ -1,17 +1,14 @@
|
||||
import { multiSelectMessages } from 'ChillMainAssets/vuejs/_js/i18n'
|
||||
import { multiSelectMessages } from "ChillMainAssets/vuejs/_js/i18n";
|
||||
|
||||
const calendarUserSelectorMessages = {
|
||||
fr: {
|
||||
choose_your_calendar_user: "Afficher les plages de disponibilités",
|
||||
select_user: "Sélectionnez des calendriers",
|
||||
show_my_calendar: "Afficher mon calendrier",
|
||||
show_weekends: "Afficher les week-ends"
|
||||
}
|
||||
show_weekends: "Afficher les week-ends",
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
Object.assign(calendarUserSelectorMessages.fr, multiSelectMessages.fr);
|
||||
|
||||
export {
|
||||
calendarUserSelectorMessages
|
||||
};
|
||||
|
||||
export { calendarUserSelectorMessages };
|
||||
|
@ -1,14 +1,25 @@
|
||||
// this file loads all assets from the Chill calendar bundle
|
||||
module.exports = function(encore, entries) {
|
||||
module.exports = function (encore, entries) {
|
||||
entries.push(__dirname + "/Resources/public/chill/chill.js");
|
||||
|
||||
entries.push(__dirname + '/Resources/public/chill/chill.js');
|
||||
encore.addAliases({
|
||||
ChillCalendarAssets: __dirname + "/Resources/public",
|
||||
});
|
||||
|
||||
encore.addAliases({
|
||||
ChillCalendarAssets: __dirname + '/Resources/public'
|
||||
});
|
||||
|
||||
encore.addEntry('vue_calendar', __dirname + '/Resources/public/vuejs/Calendar/index.js');
|
||||
encore.addEntry('vue_mycalendarrange', __dirname + '/Resources/public/vuejs/MyCalendarRange/index2.ts');
|
||||
encore.addEntry('page_calendar', __dirname + '/Resources/public/chill/index.js');
|
||||
encore.addEntry('mod_answer', __dirname + '/Resources/public/module/Invite/answer.js');
|
||||
encore.addEntry(
|
||||
"vue_calendar",
|
||||
__dirname + "/Resources/public/vuejs/Calendar/index.js",
|
||||
);
|
||||
encore.addEntry(
|
||||
"vue_mycalendarrange",
|
||||
__dirname + "/Resources/public/vuejs/MyCalendarRange/index2.ts",
|
||||
);
|
||||
encore.addEntry(
|
||||
"page_calendar",
|
||||
__dirname + "/Resources/public/chill/index.js",
|
||||
);
|
||||
encore.addEntry(
|
||||
"mod_answer",
|
||||
__dirname + "/Resources/public/module/Invite/answer.js",
|
||||
);
|
||||
};
|
||||
|
@ -1,10 +1,8 @@
|
||||
import { fetchResults } from "ChillMainAssets/lib/api/apiMethods.ts";
|
||||
|
||||
const fetchTemplates = (entityClass) => {
|
||||
let fqdnEntityClass = encodeURI(entityClass);
|
||||
return fetchResults(`/api/1.0/docgen/templates/by-entity/${fqdnEntityClass}`);
|
||||
}
|
||||
|
||||
export {
|
||||
fetchTemplates
|
||||
let fqdnEntityClass = encodeURI(entityClass);
|
||||
return fetchResults(`/api/1.0/docgen/templates/by-entity/${fqdnEntityClass}`);
|
||||
};
|
||||
|
||||
export { fetchTemplates };
|
||||
|
@ -1,13 +1,12 @@
|
||||
|
||||
const buildLink = function(templateId, entityId, entityClass) {
|
||||
const
|
||||
entityIdEncoded = encodeURI(entityId),
|
||||
returnPath = encodeURIComponent(window.location.pathname + window.location.search + window.location.hash),
|
||||
entityClassEncoded = encodeURI(entityClass),
|
||||
url = `/fr/doc/gen/generate/from/${templateId}/for/${entityClassEncoded}/${entityIdEncoded}?returnPath=${returnPath}`
|
||||
;
|
||||
console.log('computed Url');
|
||||
return url;
|
||||
const buildLink = function (templateId, entityId, entityClass) {
|
||||
const entityIdEncoded = encodeURI(entityId),
|
||||
returnPath = encodeURIComponent(
|
||||
window.location.pathname + window.location.search + window.location.hash,
|
||||
),
|
||||
entityClassEncoded = encodeURI(entityClass),
|
||||
url = `/fr/doc/gen/generate/from/${templateId}/for/${entityClassEncoded}/${entityIdEncoded}?returnPath=${returnPath}`;
|
||||
console.log("computed Url");
|
||||
return url;
|
||||
};
|
||||
|
||||
export {buildLink};
|
||||
export { buildLink };
|
||||
|
@ -1,27 +1,25 @@
|
||||
import {createApp} from 'vue';
|
||||
import PickTemplate from 'ChillDocGeneratorAssets/vuejs/_components/PickTemplate.vue';
|
||||
import {fetchTemplates} from 'ChillDocGeneratorAssets/api/pickTemplate.js';
|
||||
import {_createI18n} from 'ChillMainAssets/vuejs/_js/i18n';
|
||||
import { createApp } from "vue";
|
||||
import PickTemplate from "ChillDocGeneratorAssets/vuejs/_components/PickTemplate.vue";
|
||||
import { fetchTemplates } from "ChillDocGeneratorAssets/api/pickTemplate.js";
|
||||
import { _createI18n } from "ChillMainAssets/vuejs/_js/i18n";
|
||||
|
||||
const i18n = _createI18n({});
|
||||
|
||||
document.querySelectorAll('div[data-docgen-template-picker]').forEach(el => {
|
||||
fetchTemplates(el.dataset.entityClass).then(templates => {
|
||||
let
|
||||
picker = {
|
||||
template: '<pick-template :templates="this.templates" :entityId="this.entityId"></pick-template>',
|
||||
components: {
|
||||
PickTemplate,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
templates: templates,
|
||||
entityId: el.dataset.entityId,
|
||||
}
|
||||
},
|
||||
}
|
||||
;
|
||||
createApp(picker).use(i18n).mount(el);
|
||||
})
|
||||
|
||||
document.querySelectorAll("div[data-docgen-template-picker]").forEach((el) => {
|
||||
fetchTemplates(el.dataset.entityClass).then((templates) => {
|
||||
let picker = {
|
||||
template:
|
||||
'<pick-template :templates="this.templates" :entityId="this.entityId"></pick-template>',
|
||||
components: {
|
||||
PickTemplate,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
templates: templates,
|
||||
entityId: el.dataset.entityId,
|
||||
};
|
||||
},
|
||||
};
|
||||
createApp(picker).use(i18n).mount(el);
|
||||
});
|
||||
});
|
||||
|
@ -2,26 +2,44 @@
|
||||
<div>
|
||||
<template v-if="templates.length > 0">
|
||||
<slot name="title">
|
||||
<h2>{{ $t('generate_document')}}</h2>
|
||||
<h2>{{ $t("generate_document") }}</h2>
|
||||
</slot>
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<slot name="label">
|
||||
<label>{{ $t('select_a_template') }}</label>
|
||||
<label>{{ $t("select_a_template") }}</label>
|
||||
</slot>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="input-group mb-3">
|
||||
<select class="form-select" v-model="template">
|
||||
<option disabled selected value="">{{ $t('choose_a_template') }}</option>
|
||||
<option disabled selected value="">
|
||||
{{ $t("choose_a_template") }}
|
||||
</option>
|
||||
<template v-for="t in templates" :key="t.id">
|
||||
<option :value="t.id" >{{ t.name.fr || 'Aucun nom défini' }}</option>
|
||||
<option :value="t.id">
|
||||
{{ t.name.fr || "Aucun nom défini" }}
|
||||
</option>
|
||||
</template>
|
||||
</select>
|
||||
<a v-if="canGenerate" class="btn btn-update btn-sm change-icon" :href="buildUrlGenerate" @click.prevent="clickGenerate($event, buildUrlGenerate)"><i class="fa fa-fw fa-cog"></i></a>
|
||||
<a v-else class="btn btn-update btn-sm change-icon" href="#" disabled ><i class="fa fa-fw fa-cog"></i></a>
|
||||
<a
|
||||
v-if="canGenerate"
|
||||
class="btn btn-update btn-sm change-icon"
|
||||
:href="buildUrlGenerate"
|
||||
@click.prevent="
|
||||
clickGenerate($event, buildUrlGenerate)
|
||||
"
|
||||
><i class="fa fa-fw fa-cog"
|
||||
/></a>
|
||||
<a
|
||||
v-else
|
||||
class="btn btn-update btn-sm change-icon"
|
||||
href="#"
|
||||
disabled
|
||||
><i class="fa fa-fw fa-cog"
|
||||
/></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -30,16 +48,13 @@
|
||||
<p>{{ getDescription }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import {buildLink} from 'ChillDocGeneratorAssets/lib/document-generator';
|
||||
import { buildLink } from "ChillDocGeneratorAssets/lib/document-generator";
|
||||
|
||||
export default {
|
||||
name: "PickTemplate",
|
||||
@ -57,13 +72,13 @@ export default {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
}
|
||||
},
|
||||
},
|
||||
emits: ['goToGenerateDocument'],
|
||||
emits: ["goToGenerateDocument"],
|
||||
data() {
|
||||
return {
|
||||
template: null,
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
canGenerate() {
|
||||
@ -78,21 +93,21 @@ export default {
|
||||
},
|
||||
getDescription() {
|
||||
if (null === this.template) {
|
||||
return '';
|
||||
return "";
|
||||
}
|
||||
let desc = this.templates.find(t => t.id === this.template);
|
||||
let desc = this.templates.find((t) => t.id === this.template);
|
||||
if (null === desc) {
|
||||
return '';
|
||||
return "";
|
||||
}
|
||||
return desc.description || '';
|
||||
return desc.description || "";
|
||||
},
|
||||
buildUrlGenerate() {
|
||||
if (null === this.template) {
|
||||
return '#';
|
||||
return "#";
|
||||
}
|
||||
|
||||
return buildLink(this.template, this.entityId, this.entityClass);
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
clickGenerate(event, link) {
|
||||
@ -100,21 +115,23 @@ export default {
|
||||
window.location.assign(link);
|
||||
}
|
||||
|
||||
this.$emit('goToGenerateDocument', {event, link, template: this.template});
|
||||
this.$emit("goToGenerateDocument", {
|
||||
event,
|
||||
link,
|
||||
template: this.template,
|
||||
});
|
||||
},
|
||||
},
|
||||
i18n: {
|
||||
messages: {
|
||||
fr: {
|
||||
generate_document: 'Générer un document',
|
||||
select_a_template: 'Choisir un modèle',
|
||||
choose_a_template: 'Choisir',
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
generate_document: "Générer un document",
|
||||
select_a_template: "Choisir un modèle",
|
||||
choose_a_template: "Choisir",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
<style scoped></style>
|
||||
|
@ -1,8 +1,11 @@
|
||||
// this file loads all assets from the Chill DocGenerator bundle
|
||||
module.exports = function(encore, entries) {
|
||||
encore.addAliases({
|
||||
ChillDocGeneratorAssets: __dirname + '/Resources/public'
|
||||
});
|
||||
module.exports = function (encore, entries) {
|
||||
encore.addAliases({
|
||||
ChillDocGeneratorAssets: __dirname + "/Resources/public",
|
||||
});
|
||||
|
||||
encore.addEntry('mod_docgen_picktemplate', __dirname + '/Resources/public/module/PickTemplate/index.js');
|
||||
encore.addEntry(
|
||||
"mod_docgen_picktemplate",
|
||||
__dirname + "/Resources/public/module/PickTemplate/index.js",
|
||||
);
|
||||
};
|
||||
|
@ -1,96 +1,99 @@
|
||||
var mime = require('mime');
|
||||
var mime = require("mime");
|
||||
|
||||
var algo = 'AES-CBC';
|
||||
var algo = "AES-CBC";
|
||||
|
||||
var initializeButtons = (root) => {
|
||||
var
|
||||
buttons = root.querySelectorAll('a[data-download-button]');
|
||||
var buttons = root.querySelectorAll("a[data-download-button]");
|
||||
|
||||
for (let i = 0; i < buttons.length; i ++) {
|
||||
initialize(buttons[i]);
|
||||
}
|
||||
for (let i = 0; i < buttons.length; i++) {
|
||||
initialize(buttons[i]);
|
||||
}
|
||||
};
|
||||
|
||||
var initialize = (button) => {
|
||||
button.addEventListener('click', onClick);
|
||||
button.addEventListener("click", onClick);
|
||||
};
|
||||
|
||||
var onClick = e => download(e.target);
|
||||
var onClick = (e) => download(e.target);
|
||||
|
||||
var download = (button) => {
|
||||
var
|
||||
keyData = JSON.parse(button.dataset.key),
|
||||
ivData = JSON.parse(button.dataset.iv),
|
||||
iv = new Uint8Array(ivData),
|
||||
urlGenerator = button.dataset.tempUrlGetGenerator,
|
||||
hasFilename = 'filename' in button.dataset,
|
||||
filename = button.dataset.filename,
|
||||
labelPreparing = button.dataset.labelPreparing,
|
||||
labelReady = button.dataset.labelReady,
|
||||
mimeType = button.dataset.mimeType,
|
||||
extension = mime.getExtension(mimeType),
|
||||
decryptError = "Error while decrypting file",
|
||||
fetchError = "Error while fetching file",
|
||||
key, url
|
||||
;
|
||||
var keyData = JSON.parse(button.dataset.key),
|
||||
ivData = JSON.parse(button.dataset.iv),
|
||||
iv = new Uint8Array(ivData),
|
||||
urlGenerator = button.dataset.tempUrlGetGenerator,
|
||||
hasFilename = "filename" in button.dataset,
|
||||
filename = button.dataset.filename,
|
||||
labelPreparing = button.dataset.labelPreparing,
|
||||
labelReady = button.dataset.labelReady,
|
||||
mimeType = button.dataset.mimeType,
|
||||
extension = mime.getExtension(mimeType),
|
||||
decryptError = "Error while decrypting file",
|
||||
fetchError = "Error while fetching file",
|
||||
key,
|
||||
url;
|
||||
|
||||
button.textContent = labelPreparing;
|
||||
button.textContent = labelPreparing;
|
||||
|
||||
window.fetch(urlGenerator)
|
||||
.then((r) => {
|
||||
if (r.ok) {
|
||||
return r.json();
|
||||
} else {
|
||||
throw new Error("error while downloading url " + r.status + " " + r.statusText);
|
||||
}
|
||||
})
|
||||
.then(data => {
|
||||
return window.fetch(data.url);
|
||||
})
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
return response.arrayBuffer();
|
||||
}
|
||||
throw new Error(response.status + response.statusText);
|
||||
})
|
||||
.then(buffer => {
|
||||
if (keyData.alg !== undefined) {
|
||||
return window.crypto.subtle
|
||||
.importKey('jwk', keyData, { name: algo, iv: iv}, false, ['decrypt'])
|
||||
.then(key => {
|
||||
return window.crypto.subtle.decrypt({ name: algo, iv: iv }, key, buffer);
|
||||
});
|
||||
}
|
||||
return Promise.resolve(buffer);
|
||||
})
|
||||
.then(decrypted => {
|
||||
var
|
||||
blob = new Blob([decrypted], { type: mimeType }),
|
||||
url = window.URL.createObjectURL(blob)
|
||||
;
|
||||
button.href = url;
|
||||
button.target = '_blank';
|
||||
button.type = mimeType;
|
||||
button.textContent = labelReady;
|
||||
if (hasFilename) {
|
||||
|
||||
button.download = filename;
|
||||
if (extension !== false) {
|
||||
button.download = button.download + '.' + extension;
|
||||
}
|
||||
}
|
||||
button.removeEventListener('click', onClick);
|
||||
button.click();
|
||||
})
|
||||
.catch(error => {
|
||||
button.textContent = "";
|
||||
button.appendChild(document.createTextNode("error while handling decrypted file"));
|
||||
})
|
||||
;
|
||||
window
|
||||
.fetch(urlGenerator)
|
||||
.then((r) => {
|
||||
if (r.ok) {
|
||||
return r.json();
|
||||
} else {
|
||||
throw new Error(
|
||||
"error while downloading url " + r.status + " " + r.statusText,
|
||||
);
|
||||
}
|
||||
})
|
||||
.then((data) => {
|
||||
return window.fetch(data.url);
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.ok) {
|
||||
return response.arrayBuffer();
|
||||
}
|
||||
throw new Error(response.status + response.statusText);
|
||||
})
|
||||
.then((buffer) => {
|
||||
if (keyData.alg !== undefined) {
|
||||
return window.crypto.subtle
|
||||
.importKey("jwk", keyData, { name: algo, iv: iv }, false, ["decrypt"])
|
||||
.then((key) => {
|
||||
return window.crypto.subtle.decrypt(
|
||||
{ name: algo, iv: iv },
|
||||
key,
|
||||
buffer,
|
||||
);
|
||||
});
|
||||
}
|
||||
return Promise.resolve(buffer);
|
||||
})
|
||||
.then((decrypted) => {
|
||||
var blob = new Blob([decrypted], { type: mimeType }),
|
||||
url = window.URL.createObjectURL(blob);
|
||||
button.href = url;
|
||||
button.target = "_blank";
|
||||
button.type = mimeType;
|
||||
button.textContent = labelReady;
|
||||
if (hasFilename) {
|
||||
button.download = filename;
|
||||
if (extension !== false) {
|
||||
button.download = button.download + "." + extension;
|
||||
}
|
||||
}
|
||||
button.removeEventListener("click", onClick);
|
||||
button.click();
|
||||
})
|
||||
.catch((error) => {
|
||||
button.textContent = "";
|
||||
button.appendChild(
|
||||
document.createTextNode("error while handling decrypted file"),
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
window.addEventListener('load', function(e) {
|
||||
initializeButtons(e.target);
|
||||
window.addEventListener("load", function (e) {
|
||||
initializeButtons(e.target);
|
||||
});
|
||||
|
||||
export { initializeButtons, download };
|
||||
|
@ -1,2 +1,2 @@
|
||||
require('./uploader.js');
|
||||
require('./downloader.js');
|
||||
require("./uploader.js");
|
||||
require("./downloader.js");
|
||||
|
@ -1,7 +1,6 @@
|
||||
var algo = 'AES-CBC';
|
||||
import Dropzone from 'dropzone';
|
||||
import { initializeButtons } from './downloader.js';
|
||||
|
||||
var algo = "AES-CBC";
|
||||
import Dropzone from "dropzone";
|
||||
import { initializeButtons } from "./downloader.js";
|
||||
|
||||
/**
|
||||
*
|
||||
@ -23,351 +22,335 @@ import { initializeButtons } from './downloader.js';
|
||||
|
||||
// load css
|
||||
//require('dropzone/dist/basic.css');
|
||||
require('dropzone/dist/dropzone.css');
|
||||
require('./index.scss');
|
||||
require("dropzone/dist/dropzone.css");
|
||||
require("./index.scss");
|
||||
//
|
||||
|
||||
// disable dropzone autodiscover
|
||||
Dropzone.autoDiscover = false;
|
||||
|
||||
var keyDefinition = {
|
||||
name: algo,
|
||||
length: 256
|
||||
name: algo,
|
||||
length: 256,
|
||||
};
|
||||
|
||||
var searchForZones = function(root) {
|
||||
var zones = root.querySelectorAll('div[data-stored-object]');
|
||||
for(let i=0; i < zones.length; i++) {
|
||||
initialize(zones[i]);
|
||||
}
|
||||
var searchForZones = function (root) {
|
||||
var zones = root.querySelectorAll("div[data-stored-object]");
|
||||
for (let i = 0; i < zones.length; i++) {
|
||||
initialize(zones[i]);
|
||||
}
|
||||
};
|
||||
|
||||
var getUploadUrl = function(zoneData, files) {
|
||||
var
|
||||
generateTempUrlPost = zoneData.zone.querySelector('input[data-async-file-upload]').dataset.generateTempUrlPost,
|
||||
oReq = new XMLHttpRequest()
|
||||
;
|
||||
var getUploadUrl = function (zoneData, files) {
|
||||
var generateTempUrlPost = zoneData.zone.querySelector(
|
||||
"input[data-async-file-upload]",
|
||||
).dataset.generateTempUrlPost,
|
||||
oReq = new XMLHttpRequest();
|
||||
// arg, dropzone, you cannot handle async upload...
|
||||
oReq.open("GET", generateTempUrlPost, false);
|
||||
oReq.send();
|
||||
|
||||
// arg, dropzone, you cannot handle async upload...
|
||||
oReq.open("GET", generateTempUrlPost, false);
|
||||
oReq.send();
|
||||
if (oReq.readyState !== XMLHttpRequest.DONE) {
|
||||
throw new Error("Error while fetching url to upload");
|
||||
}
|
||||
|
||||
if (oReq.readyState !== XMLHttpRequest.DONE) {
|
||||
throw new Error("Error while fetching url to upload");
|
||||
}
|
||||
zoneData.params = JSON.parse(oReq.responseText);
|
||||
|
||||
zoneData.params = JSON.parse(oReq.responseText);
|
||||
|
||||
return zoneData.params.url;
|
||||
return zoneData.params.url;
|
||||
};
|
||||
|
||||
var encryptFile = function(originalFile, zoneData, done) {
|
||||
var
|
||||
iv = crypto.getRandomValues(new Uint8Array(16)),
|
||||
reader = new FileReader(),
|
||||
jsKey, rawKey
|
||||
;
|
||||
var encryptFile = function (originalFile, zoneData, done) {
|
||||
var iv = crypto.getRandomValues(new Uint8Array(16)),
|
||||
reader = new FileReader(),
|
||||
jsKey,
|
||||
rawKey;
|
||||
|
||||
zoneData.originalType = originalFile.type;
|
||||
zoneData.originalType = originalFile.type;
|
||||
|
||||
reader.onload = e => {
|
||||
window.crypto.subtle.generateKey(keyDefinition, true, [ "encrypt", "decrypt" ])
|
||||
.then(key => {
|
||||
jsKey = key;
|
||||
reader.onload = (e) => {
|
||||
window.crypto.subtle
|
||||
.generateKey(keyDefinition, true, ["encrypt", "decrypt"])
|
||||
.then((key) => {
|
||||
jsKey = key;
|
||||
|
||||
// we register the key somwhere
|
||||
return window.crypto.subtle.exportKey('jwk', key);
|
||||
}).then(exportedKey => {
|
||||
rawKey = exportedKey;
|
||||
// we register the key somwhere
|
||||
return window.crypto.subtle.exportKey("jwk", key);
|
||||
})
|
||||
.then((exportedKey) => {
|
||||
rawKey = exportedKey;
|
||||
|
||||
// we start encryption
|
||||
return window.crypto.subtle.encrypt({ name: algo, iv: iv}, jsKey, e.target.result);
|
||||
})
|
||||
.then(encrypted => {
|
||||
zoneData.crypto = {
|
||||
jsKey: jsKey,
|
||||
rawKey: rawKey,
|
||||
iv: iv
|
||||
};
|
||||
// we start encryption
|
||||
return window.crypto.subtle.encrypt(
|
||||
{ name: algo, iv: iv },
|
||||
jsKey,
|
||||
e.target.result,
|
||||
);
|
||||
})
|
||||
.then((encrypted) => {
|
||||
zoneData.crypto = {
|
||||
jsKey: jsKey,
|
||||
rawKey: rawKey,
|
||||
iv: iv,
|
||||
};
|
||||
|
||||
done(new File( [ encrypted ], zoneData.suffix));
|
||||
});
|
||||
};
|
||||
done(new File([encrypted], zoneData.suffix));
|
||||
});
|
||||
};
|
||||
|
||||
reader.readAsArrayBuffer(originalFile);
|
||||
reader.readAsArrayBuffer(originalFile);
|
||||
};
|
||||
|
||||
var addBelowButton = (btn, zone, zoneData) => {
|
||||
let
|
||||
belowZone = zone.querySelector('.chill-dropzone__below-zone');
|
||||
let belowZone = zone.querySelector(".chill-dropzone__below-zone");
|
||||
|
||||
if (belowZone === null) {
|
||||
belowZone = document.createElement('div');
|
||||
belowZone.classList.add('chill-dropzone__below-zone');
|
||||
zone.appendChild(belowZone);
|
||||
}
|
||||
if (belowZone === null) {
|
||||
belowZone = document.createElement("div");
|
||||
belowZone.classList.add("chill-dropzone__below-zone");
|
||||
zone.appendChild(belowZone);
|
||||
}
|
||||
|
||||
belowZone.appendChild(btn);
|
||||
belowZone.appendChild(btn);
|
||||
};
|
||||
|
||||
var createZone = (zone, zoneData) => {
|
||||
var
|
||||
created = document.createElement('div'),
|
||||
initMessage = document.createElement('div'),
|
||||
initContent = zone.dataset.labelInitMessage,
|
||||
dropzoneI;
|
||||
var created = document.createElement("div"),
|
||||
initMessage = document.createElement("div"),
|
||||
initContent = zone.dataset.labelInitMessage,
|
||||
dropzoneI;
|
||||
|
||||
created.classList.add('dropzone');
|
||||
initMessage.classList.add('dz-message');
|
||||
initMessage.appendChild(document.createTextNode(initContent));
|
||||
console.log(Dropzone);
|
||||
dropzoneI = new Dropzone(created, {
|
||||
url: function(files) {
|
||||
return getUploadUrl(zoneData, files);
|
||||
},
|
||||
dictDefaultMessage: zone.dataset.dictDefaultMessage,
|
||||
dictFileTooBig: zone.dataset.dictFileTooBig,
|
||||
dictRemoveFile: zone.dataset.dictRemoveFile,
|
||||
dictMaxFilesExceeded: zone.dataset.dictMaxFilesExceeded,
|
||||
dictCancelUpload: zone.dataset.dictCancelUpload,
|
||||
dictCancelUploadConfirm: zone.dataset.dictCancelUploadConfirm,
|
||||
dictUploadCanceled: zone.dataset.dictUploadCanceled,
|
||||
maxFiles: 1,
|
||||
addRemoveLinks: true,
|
||||
transformFile: function(file, done) {
|
||||
encryptFile(file, zoneData, done);
|
||||
},
|
||||
renameFile: function(file) {
|
||||
return zoneData.suffix;
|
||||
}
|
||||
});
|
||||
created.classList.add("dropzone");
|
||||
initMessage.classList.add("dz-message");
|
||||
initMessage.appendChild(document.createTextNode(initContent));
|
||||
console.log(Dropzone);
|
||||
dropzoneI = new Dropzone(created, {
|
||||
url: function (files) {
|
||||
return getUploadUrl(zoneData, files);
|
||||
},
|
||||
dictDefaultMessage: zone.dataset.dictDefaultMessage,
|
||||
dictFileTooBig: zone.dataset.dictFileTooBig,
|
||||
dictRemoveFile: zone.dataset.dictRemoveFile,
|
||||
dictMaxFilesExceeded: zone.dataset.dictMaxFilesExceeded,
|
||||
dictCancelUpload: zone.dataset.dictCancelUpload,
|
||||
dictCancelUploadConfirm: zone.dataset.dictCancelUploadConfirm,
|
||||
dictUploadCanceled: zone.dataset.dictUploadCanceled,
|
||||
maxFiles: 1,
|
||||
addRemoveLinks: true,
|
||||
transformFile: function (file, done) {
|
||||
encryptFile(file, zoneData, done);
|
||||
},
|
||||
renameFile: function (file) {
|
||||
return zoneData.suffix;
|
||||
},
|
||||
});
|
||||
|
||||
dropzoneI.on("sending", function(file, xhr, formData) {
|
||||
formData.append("redirect", zoneData.params.redirect);
|
||||
formData.append("max_file_size", zoneData.params.max_file_size);
|
||||
formData.append("max_file_count", zoneData.params.max_file_count);
|
||||
formData.append("expires", zoneData.params.expires);
|
||||
formData.append("signature", zoneData.params.signature);
|
||||
});
|
||||
dropzoneI.on("sending", function (file, xhr, formData) {
|
||||
formData.append("redirect", zoneData.params.redirect);
|
||||
formData.append("max_file_size", zoneData.params.max_file_size);
|
||||
formData.append("max_file_count", zoneData.params.max_file_count);
|
||||
formData.append("expires", zoneData.params.expires);
|
||||
formData.append("signature", zoneData.params.signature);
|
||||
});
|
||||
|
||||
dropzoneI.on("success", function(file, response) {
|
||||
zoneData.currentFile = file;
|
||||
storeDataInForm(zone, zoneData);
|
||||
});
|
||||
dropzoneI.on("success", function (file, response) {
|
||||
zoneData.currentFile = file;
|
||||
storeDataInForm(zone, zoneData);
|
||||
});
|
||||
|
||||
dropzoneI.on("addedfile", function(file) {
|
||||
if (zoneData.hasOwnProperty('currentFile')) {
|
||||
dropzoneI.removeFile(zoneData.currentFile);
|
||||
}
|
||||
});
|
||||
dropzoneI.on("addedfile", function (file) {
|
||||
if (zoneData.hasOwnProperty("currentFile")) {
|
||||
dropzoneI.removeFile(zoneData.currentFile);
|
||||
}
|
||||
});
|
||||
|
||||
dropzoneI.on("removedfile", function(file) {
|
||||
removeDataInForm(zone, zoneData);
|
||||
});
|
||||
dropzoneI.on("removedfile", function (file) {
|
||||
removeDataInForm(zone, zoneData);
|
||||
});
|
||||
|
||||
zone.insertBefore(created, zone.firstChild);
|
||||
zone.insertBefore(created, zone.firstChild);
|
||||
|
||||
let event = new CustomEvent("chill_dropzone_initialized", {
|
||||
detail: {
|
||||
dropzone: dropzoneI,
|
||||
zoneData: zoneData
|
||||
}
|
||||
});
|
||||
window.dispatchEvent(event);
|
||||
let event = new CustomEvent("chill_dropzone_initialized", {
|
||||
detail: {
|
||||
dropzone: dropzoneI,
|
||||
zoneData: zoneData,
|
||||
},
|
||||
});
|
||||
window.dispatchEvent(event);
|
||||
};
|
||||
|
||||
|
||||
var initialize = function(zone) {
|
||||
var
|
||||
allowRemove = zone.dataset.allowRemove,
|
||||
zoneData = { zone: zone, suffix: createFilename(), allowRemove: allowRemove, old: null }
|
||||
;
|
||||
|
||||
if (hasDataInForm(zone, zoneData)) {
|
||||
insertRemoveButton(zone, zoneData);
|
||||
insertDownloadButton(zone, zoneData);
|
||||
} else {
|
||||
createZone(zone, zoneData);
|
||||
}
|
||||
var initialize = function (zone) {
|
||||
var allowRemove = zone.dataset.allowRemove,
|
||||
zoneData = {
|
||||
zone: zone,
|
||||
suffix: createFilename(),
|
||||
allowRemove: allowRemove,
|
||||
old: null,
|
||||
};
|
||||
if (hasDataInForm(zone, zoneData)) {
|
||||
insertRemoveButton(zone, zoneData);
|
||||
insertDownloadButton(zone, zoneData);
|
||||
} else {
|
||||
createZone(zone, zoneData);
|
||||
}
|
||||
};
|
||||
|
||||
var createFilename = () => {
|
||||
var text = "";
|
||||
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
var text = "";
|
||||
var possible =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
|
||||
for (let i = 0; i < 7; i++) {
|
||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||
}
|
||||
for (let i = 0; i < 7; i++) {
|
||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||
}
|
||||
|
||||
return text;
|
||||
return text;
|
||||
};
|
||||
|
||||
var storeDataInForm = (zone, zoneData) => {
|
||||
var
|
||||
inputKey = zone.querySelector('input[data-stored-object-key]'),
|
||||
inputIv = zone.querySelector('input[data-stored-object-iv]'),
|
||||
inputObject = zone.querySelector('input[data-async-file-upload]'),
|
||||
inputType = zone.querySelector('input[data-async-file-type]')
|
||||
;
|
||||
var inputKey = zone.querySelector("input[data-stored-object-key]"),
|
||||
inputIv = zone.querySelector("input[data-stored-object-iv]"),
|
||||
inputObject = zone.querySelector("input[data-async-file-upload]"),
|
||||
inputType = zone.querySelector("input[data-async-file-type]");
|
||||
inputKey.value = JSON.stringify(zoneData.crypto.rawKey);
|
||||
inputIv.value = JSON.stringify(Array.from(zoneData.crypto.iv));
|
||||
inputType.value = zoneData.originalType;
|
||||
inputObject.value = zoneData.params.prefix + zoneData.suffix;
|
||||
|
||||
inputKey.value = JSON.stringify(zoneData.crypto.rawKey);
|
||||
inputIv.value = JSON.stringify(Array.from(zoneData.crypto.iv));
|
||||
inputType.value = zoneData.originalType;
|
||||
inputObject.value = zoneData.params.prefix + zoneData.suffix;
|
||||
|
||||
insertDownloadButton(zone);
|
||||
insertDownloadButton(zone);
|
||||
};
|
||||
|
||||
const restoreDataInForm = (zone, zoneData) => {
|
||||
var
|
||||
inputKey = zone.querySelector('input[data-stored-object-key]'),
|
||||
inputIv = zone.querySelector('input[data-stored-object-iv]'),
|
||||
inputObject = zone.querySelector('input[data-async-file-upload]'),
|
||||
inputType = zone.querySelector('input[data-async-file-type]')
|
||||
;
|
||||
var inputKey = zone.querySelector("input[data-stored-object-key]"),
|
||||
inputIv = zone.querySelector("input[data-stored-object-iv]"),
|
||||
inputObject = zone.querySelector("input[data-async-file-upload]"),
|
||||
inputType = zone.querySelector("input[data-async-file-type]");
|
||||
if (zoneData.old === null) {
|
||||
console.log("should not have restored data");
|
||||
return;
|
||||
}
|
||||
|
||||
if (zoneData.old === null) {
|
||||
console.log('should not have restored data');
|
||||
return;
|
||||
}
|
||||
inputKey.value = zoneData.old.key;
|
||||
inputIv.value = zoneData.old.iv;
|
||||
inputType.value = zoneData.old.type;
|
||||
inputObject.value = zoneData.old.obj;
|
||||
|
||||
inputKey.value = zoneData.old.key;
|
||||
inputIv.value = zoneData.old.iv;
|
||||
inputType.value = zoneData.old.type;
|
||||
inputObject.value = zoneData.old.obj;
|
||||
|
||||
insertDownloadButton(zone);
|
||||
insertDownloadButton(zone);
|
||||
};
|
||||
|
||||
const hasDataInForm = (zone, zoneData) => {
|
||||
var
|
||||
inputObject = zone.querySelector('input[data-async-file-upload]')
|
||||
;
|
||||
|
||||
return inputObject.value.length > 0;
|
||||
var inputObject = zone.querySelector("input[data-async-file-upload]");
|
||||
return inputObject.value.length > 0;
|
||||
};
|
||||
|
||||
var removeDataInForm = (zone, zoneData) => {
|
||||
var
|
||||
inputKey = zone.querySelector('input[data-stored-object-key]'),
|
||||
inputIv = zone.querySelector('input[data-stored-object-iv]'),
|
||||
inputObject = zone.querySelector('input[data-async-file-upload]'),
|
||||
inputType = zone.querySelector('input[data-async-file-type]')
|
||||
;
|
||||
var inputKey = zone.querySelector("input[data-stored-object-key]"),
|
||||
inputIv = zone.querySelector("input[data-stored-object-iv]"),
|
||||
inputObject = zone.querySelector("input[data-async-file-upload]"),
|
||||
inputType = zone.querySelector("input[data-async-file-type]");
|
||||
// store data for future usage
|
||||
zoneData.old = {
|
||||
key: inputKey.value,
|
||||
iv: inputIv.value,
|
||||
obj: inputObject.value,
|
||||
type: inputType.value,
|
||||
};
|
||||
// set blank values
|
||||
inputKey.value = "";
|
||||
inputIv.value = "";
|
||||
inputType.value = "";
|
||||
inputObject.value = "";
|
||||
|
||||
// store data for future usage
|
||||
zoneData.old = {
|
||||
key: inputKey.value,
|
||||
iv: inputIv.value,
|
||||
obj: inputObject.value,
|
||||
type: inputType.value
|
||||
};
|
||||
// set blank values
|
||||
inputKey.value = "";
|
||||
inputIv.value = "";
|
||||
inputType.value = "";
|
||||
inputObject.value = "";
|
||||
|
||||
insertDownloadButton(zone);
|
||||
insertDownloadButton(zone);
|
||||
};
|
||||
|
||||
var insertRemoveButton = (zone, zoneData) => {
|
||||
var
|
||||
removeButton = document.createElement('a'),
|
||||
cancelButton = document.createElement('a'),
|
||||
labelRemove = zone.dataset.dictRemove,
|
||||
labelCancel = 'Restaurer'
|
||||
;
|
||||
var removeButton = document.createElement("a"),
|
||||
cancelButton = document.createElement("a"),
|
||||
labelRemove = zone.dataset.dictRemove,
|
||||
labelCancel = "Restaurer";
|
||||
removeButton.classList.add("btn", "btn-delete");
|
||||
removeButton.textContent = labelRemove;
|
||||
|
||||
removeButton.classList.add('btn', 'btn-delete');
|
||||
removeButton.textContent = labelRemove;
|
||||
cancelButton.classList.add("btn", "btn-cancel");
|
||||
cancelButton.textContent = labelCancel;
|
||||
|
||||
cancelButton.classList.add('btn', 'btn-cancel');
|
||||
cancelButton.textContent = labelCancel;
|
||||
|
||||
removeButton.addEventListener('click', (e) => {
|
||||
removeButton.addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
if (zoneData.allowRemove === "true") {
|
||||
removeDataInForm(zone, zoneData);
|
||||
cancelButton.addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
if (zoneData.allowRemove === 'true') {
|
||||
removeDataInForm(zone, zoneData);
|
||||
cancelButton.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
restoreDataInForm(zone, zoneData);
|
||||
restoreDataInForm(zone, zoneData);
|
||||
|
||||
cancelButton.remove();
|
||||
zone.querySelector('.dropzone').remove();
|
||||
cancelButton.remove();
|
||||
zone.querySelector(".dropzone").remove();
|
||||
|
||||
initialize(zone);
|
||||
});
|
||||
}
|
||||
addBelowButton(cancelButton, zone, zoneData);
|
||||
//zone.appendChild(cancelButton);
|
||||
removeButton.remove();
|
||||
createZone(zone, zoneData);
|
||||
});
|
||||
initialize(zone);
|
||||
});
|
||||
}
|
||||
addBelowButton(cancelButton, zone, zoneData);
|
||||
//zone.appendChild(cancelButton);
|
||||
removeButton.remove();
|
||||
createZone(zone, zoneData);
|
||||
});
|
||||
|
||||
addBelowButton(removeButton, zone, zoneData);
|
||||
// zone.appendChild(removeButton);
|
||||
addBelowButton(removeButton, zone, zoneData);
|
||||
// zone.appendChild(removeButton);
|
||||
};
|
||||
|
||||
const removeDownloadButton = (zone, zoneData) => {
|
||||
var
|
||||
existingButtons = zone.querySelectorAll('a[data-download-button]')
|
||||
;
|
||||
|
||||
// remove existing
|
||||
existingButtons.forEach(function(b) {
|
||||
b.remove();
|
||||
});
|
||||
var existingButtons = zone.querySelectorAll("a[data-download-button]");
|
||||
// remove existing
|
||||
existingButtons.forEach(function (b) {
|
||||
b.remove();
|
||||
});
|
||||
};
|
||||
|
||||
var insertDownloadButton = (zone, zoneData) => {
|
||||
var
|
||||
existingButtons = zone.querySelectorAll('a[data-download-button]'),
|
||||
newButton = document.createElement('a'),
|
||||
inputKey = zone.querySelector('input[data-stored-object-key]'),
|
||||
inputIv = zone.querySelector('input[data-stored-object-iv]'),
|
||||
inputObject = zone.querySelector('input[data-async-file-upload]'),
|
||||
inputType = zone.querySelector('input[data-async-file-type]'),
|
||||
labelPreparing = zone.dataset.labelPreparing,
|
||||
labelQuietButton = zone.dataset.labelQuietButton,
|
||||
labelReady = zone.dataset.labelReady,
|
||||
tempUrlGenerator = zone.dataset.tempUrlGenerator,
|
||||
tempUrlGeneratorParams = new URLSearchParams()
|
||||
;
|
||||
var existingButtons = zone.querySelectorAll("a[data-download-button]"),
|
||||
newButton = document.createElement("a"),
|
||||
inputKey = zone.querySelector("input[data-stored-object-key]"),
|
||||
inputIv = zone.querySelector("input[data-stored-object-iv]"),
|
||||
inputObject = zone.querySelector("input[data-async-file-upload]"),
|
||||
inputType = zone.querySelector("input[data-async-file-type]"),
|
||||
labelPreparing = zone.dataset.labelPreparing,
|
||||
labelQuietButton = zone.dataset.labelQuietButton,
|
||||
labelReady = zone.dataset.labelReady,
|
||||
tempUrlGenerator = zone.dataset.tempUrlGenerator,
|
||||
tempUrlGeneratorParams = new URLSearchParams();
|
||||
// remove existing
|
||||
existingButtons.forEach(function (b) {
|
||||
b.remove();
|
||||
});
|
||||
|
||||
// remove existing
|
||||
existingButtons.forEach(function(b) {
|
||||
b.remove();
|
||||
});
|
||||
if (inputObject.value === "") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (inputObject.value === '') {
|
||||
return;
|
||||
}
|
||||
tempUrlGeneratorParams.append("object_name", inputObject.value);
|
||||
|
||||
tempUrlGeneratorParams.append('object_name', inputObject.value);
|
||||
newButton.dataset.downloadButton = true;
|
||||
newButton.dataset.key = inputKey.value;
|
||||
newButton.dataset.iv = inputIv.value;
|
||||
newButton.dataset.mimeType = inputType.value;
|
||||
newButton.dataset.labelPreparing = labelPreparing;
|
||||
newButton.dataset.labelReady = labelReady;
|
||||
newButton.dataset.tempUrlGetGenerator =
|
||||
tempUrlGenerator + "?" + tempUrlGeneratorParams.toString();
|
||||
newButton.classList.add("btn", "btn-download", "dz-bt-below-dropzone");
|
||||
newButton.textContent = labelQuietButton;
|
||||
|
||||
newButton.dataset.downloadButton = true;
|
||||
newButton.dataset.key = inputKey.value;
|
||||
newButton.dataset.iv = inputIv.value;
|
||||
newButton.dataset.mimeType = inputType.value;
|
||||
newButton.dataset.labelPreparing = labelPreparing;
|
||||
newButton.dataset.labelReady = labelReady;
|
||||
newButton.dataset.tempUrlGetGenerator = tempUrlGenerator + '?' + tempUrlGeneratorParams.toString();
|
||||
newButton.classList.add('btn', 'btn-download', 'dz-bt-below-dropzone');
|
||||
newButton.textContent = labelQuietButton;
|
||||
|
||||
addBelowButton(newButton, zone, zoneData);
|
||||
//zone.appendChild(newButton);
|
||||
initializeButtons(zone);
|
||||
addBelowButton(newButton, zone, zoneData);
|
||||
//zone.appendChild(newButton);
|
||||
initializeButtons(zone);
|
||||
};
|
||||
|
||||
window.addEventListener('load', function(e) {
|
||||
searchForZones(document);
|
||||
window.addEventListener("load", function (e) {
|
||||
searchForZones(document);
|
||||
});
|
||||
|
||||
window.addEventListener('collection-add-entry', function(e) {
|
||||
searchForZones(e.detail.entry);
|
||||
window.addEventListener("collection-add-entry", function (e) {
|
||||
searchForZones(e.detail.entry);
|
||||
});
|
||||
|
||||
export { searchForZones };
|
||||
|
@ -1,92 +1,137 @@
|
||||
<template>
|
||||
<div v-if="isButtonGroupDisplayable" class="btn-group">
|
||||
<button :class="Object.assign({'btn': true, 'btn-outline-primary': true, 'dropdown-toggle': true, 'btn-sm': props.small})" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Actions
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li v-if="isEditableOnline">
|
||||
<wopi-edit-button :stored-object="props.storedObject" :classes="{'dropdown-item': true}" :execute-before-leave="props.executeBeforeLeave"></wopi-edit-button>
|
||||
</li>
|
||||
<li v-if="isEditableOnDesktop">
|
||||
<desktop-edit-button :classes="{'dropdown-item': true}" :edit-link="props.davLink" :expiration-link="props.davLinkExpiration"></desktop-edit-button>
|
||||
</li>
|
||||
<li v-if="isConvertibleToPdf">
|
||||
<convert-button :stored-object="props.storedObject" :filename="filename" :classes="{'dropdown-item': true}"></convert-button>
|
||||
</li>
|
||||
<li v-if="isDownloadable">
|
||||
<download-button :stored-object="props.storedObject" :at-version="props.storedObject.currentVersion" :filename="filename" :classes="{'dropdown-item': true}" :display-action-string-in-button="true"></download-button>
|
||||
</li>
|
||||
<li v-if="isHistoryViewable">
|
||||
<history-button :stored-object="props.storedObject" :can-edit="canEdit && props.storedObject._permissions.canEdit"></history-button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div v-else-if="'pending' === props.storedObject.status">
|
||||
<div class="btn btn-outline-info">Génération en cours</div>
|
||||
</div>
|
||||
<div v-else-if="'failure' === props.storedObject.status">
|
||||
<div class="btn btn-outline-danger">La génération a échoué</div>
|
||||
</div>
|
||||
|
||||
<div v-if="isButtonGroupDisplayable" class="btn-group">
|
||||
<button
|
||||
:class="
|
||||
Object.assign({
|
||||
btn: true,
|
||||
'btn-outline-primary': true,
|
||||
'dropdown-toggle': true,
|
||||
'btn-sm': props.small,
|
||||
})
|
||||
"
|
||||
type="button"
|
||||
data-bs-toggle="dropdown"
|
||||
aria-expanded="false"
|
||||
>
|
||||
Actions
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li v-if="isEditableOnline">
|
||||
<wopi-edit-button
|
||||
:stored-object="props.storedObject"
|
||||
:classes="{ 'dropdown-item': true }"
|
||||
:execute-before-leave="props.executeBeforeLeave"
|
||||
></wopi-edit-button>
|
||||
</li>
|
||||
<li v-if="isEditableOnDesktop">
|
||||
<desktop-edit-button
|
||||
:classes="{ 'dropdown-item': true }"
|
||||
:edit-link="props.davLink"
|
||||
:expiration-link="props.davLinkExpiration"
|
||||
></desktop-edit-button>
|
||||
</li>
|
||||
<li v-if="isConvertibleToPdf">
|
||||
<convert-button
|
||||
:stored-object="props.storedObject"
|
||||
:filename="filename"
|
||||
:classes="{ 'dropdown-item': true }"
|
||||
></convert-button>
|
||||
</li>
|
||||
<li v-if="isDownloadable">
|
||||
<download-button
|
||||
:stored-object="props.storedObject"
|
||||
:at-version="props.storedObject.currentVersion"
|
||||
:filename="filename"
|
||||
:classes="{ 'dropdown-item': true }"
|
||||
:display-action-string-in-button="true"
|
||||
></download-button>
|
||||
</li>
|
||||
<li v-if="isHistoryViewable">
|
||||
<history-button
|
||||
:stored-object="props.storedObject"
|
||||
:can-edit="
|
||||
canEdit && props.storedObject._permissions.canEdit
|
||||
"
|
||||
></history-button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div v-else-if="'pending' === props.storedObject.status">
|
||||
<div class="btn btn-outline-info">Génération en cours</div>
|
||||
</div>
|
||||
<div v-else-if="'failure' === props.storedObject.status">
|
||||
<div class="btn btn-outline-danger">La génération a échoué</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
import {computed, onMounted} from "vue";
|
||||
import { computed, onMounted } from "vue";
|
||||
import ConvertButton from "./StoredObjectButton/ConvertButton.vue";
|
||||
import DownloadButton from "./StoredObjectButton/DownloadButton.vue";
|
||||
import WopiEditButton from "./StoredObjectButton/WopiEditButton.vue";
|
||||
import {is_extension_editable, is_extension_viewable, is_object_ready} from "./StoredObjectButton/helpers";
|
||||
import {
|
||||
is_extension_editable,
|
||||
is_extension_viewable,
|
||||
is_object_ready,
|
||||
} from "./StoredObjectButton/helpers";
|
||||
import {
|
||||
StoredObject,
|
||||
StoredObjectStatusChange, StoredObjectVersion,
|
||||
WopiEditButtonExecutableBeforeLeaveFunction
|
||||
StoredObjectStatusChange,
|
||||
StoredObjectVersion,
|
||||
WopiEditButtonExecutableBeforeLeaveFunction,
|
||||
} from "../types";
|
||||
import DesktopEditButton from "ChillDocStoreAssets/vuejs/StoredObjectButton/DesktopEditButton.vue";
|
||||
import HistoryButton from "ChillDocStoreAssets/vuejs/StoredObjectButton/HistoryButton.vue";
|
||||
|
||||
interface DocumentActionButtonsGroupConfig {
|
||||
storedObject: StoredObject,
|
||||
small?: boolean,
|
||||
canEdit?: boolean,
|
||||
canDownload?: boolean,
|
||||
canConvertPdf?: boolean,
|
||||
returnPath?: string,
|
||||
storedObject: StoredObject;
|
||||
small?: boolean;
|
||||
canEdit?: boolean;
|
||||
canDownload?: boolean;
|
||||
canConvertPdf?: boolean;
|
||||
returnPath?: string;
|
||||
|
||||
/**
|
||||
* Will be the filename displayed to the user when he·she download the document
|
||||
* (the document will be saved on his disk with this name)
|
||||
*
|
||||
* If not set, 'document' will be used.
|
||||
*/
|
||||
filename?: string,
|
||||
/**
|
||||
* Will be the filename displayed to the user when he·she download the document
|
||||
* (the document will be saved on his disk with this name)
|
||||
*
|
||||
* If not set, 'document' will be used.
|
||||
*/
|
||||
filename?: string;
|
||||
|
||||
/**
|
||||
* If set, will execute this function before leaving to the editor
|
||||
*/
|
||||
executeBeforeLeave?: WopiEditButtonExecutableBeforeLeaveFunction,
|
||||
/**
|
||||
* If set, will execute this function before leaving to the editor
|
||||
*/
|
||||
executeBeforeLeave?: WopiEditButtonExecutableBeforeLeaveFunction;
|
||||
|
||||
/**
|
||||
* a link to download and edit file using webdav
|
||||
*/
|
||||
davLink?: string,
|
||||
/**
|
||||
* a link to download and edit file using webdav
|
||||
*/
|
||||
davLink?: string;
|
||||
|
||||
/**
|
||||
* the expiration date of the download, as a unix timestamp
|
||||
*/
|
||||
davLinkExpiration?: number,
|
||||
/**
|
||||
* the expiration date of the download, as a unix timestamp
|
||||
*/
|
||||
davLinkExpiration?: number;
|
||||
}
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'onStoredObjectStatusChange', newStatus: StoredObjectStatusChange): void
|
||||
}>();
|
||||
const emit =
|
||||
defineEmits<
|
||||
(
|
||||
e: "onStoredObjectStatusChange",
|
||||
newStatus: StoredObjectStatusChange,
|
||||
) => void
|
||||
>();
|
||||
|
||||
const props = withDefaults(defineProps<DocumentActionButtonsGroupConfig>(), {
|
||||
small: false,
|
||||
canEdit: true,
|
||||
canDownload: true,
|
||||
canConvertPdf: true,
|
||||
returnPath: window.location.pathname + window.location.search + window.location.hash
|
||||
small: false,
|
||||
canEdit: true,
|
||||
canDownload: true,
|
||||
canConvertPdf: true,
|
||||
returnPath:
|
||||
window.location.pathname +
|
||||
window.location.search +
|
||||
window.location.hash,
|
||||
});
|
||||
|
||||
/**
|
||||
@ -100,22 +145,32 @@ let tryiesForReady = 0;
|
||||
const maxTryiesForReady = 120;
|
||||
|
||||
const isButtonGroupDisplayable = computed<boolean>(() => {
|
||||
return isDownloadable.value || isEditableOnline.value || isEditableOnDesktop.value || isConvertibleToPdf.value;
|
||||
return (
|
||||
isDownloadable.value ||
|
||||
isEditableOnline.value ||
|
||||
isEditableOnDesktop.value ||
|
||||
isConvertibleToPdf.value
|
||||
);
|
||||
});
|
||||
|
||||
const isDownloadable = computed<boolean>(() => {
|
||||
return props.storedObject.status === 'ready'
|
||||
return (
|
||||
props.storedObject.status === "ready" ||
|
||||
// happens when the stored object version is just added, but not persisted
|
||||
|| (props.storedObject.currentVersion !== null && props.storedObject.status === 'empty')
|
||||
(props.storedObject.currentVersion !== null &&
|
||||
props.storedObject.status === "empty")
|
||||
);
|
||||
});
|
||||
|
||||
const isEditableOnline = computed<boolean>(() => {
|
||||
return props.storedObject.status === 'ready'
|
||||
&& props.storedObject._permissions.canEdit
|
||||
&& props.canEdit
|
||||
&& props.storedObject.currentVersion !== null
|
||||
&& is_extension_editable(props.storedObject.currentVersion.type)
|
||||
&& props.storedObject.currentVersion.persisted !== false;
|
||||
return (
|
||||
props.storedObject.status === "ready" &&
|
||||
props.storedObject._permissions.canEdit &&
|
||||
props.canEdit &&
|
||||
props.storedObject.currentVersion !== null &&
|
||||
is_extension_editable(props.storedObject.currentVersion.type) &&
|
||||
props.storedObject.currentVersion.persisted !== false
|
||||
);
|
||||
});
|
||||
|
||||
const isEditableOnDesktop = computed<boolean>(() => {
|
||||
@ -123,62 +178,61 @@ const isEditableOnDesktop = computed<boolean>(() => {
|
||||
});
|
||||
|
||||
const isConvertibleToPdf = computed<boolean>(() => {
|
||||
return props.storedObject.status === 'ready'
|
||||
&& props.storedObject._permissions.canSee
|
||||
&& props.canConvertPdf
|
||||
&& props.storedObject.currentVersion !== null
|
||||
&& is_extension_viewable(props.storedObject.currentVersion.type)
|
||||
&& props.storedObject.currentVersion.type !== 'application/pdf'
|
||||
&& props.storedObject.currentVersion.persisted !== false;
|
||||
return (
|
||||
props.storedObject.status === "ready" &&
|
||||
props.storedObject._permissions.canSee &&
|
||||
props.canConvertPdf &&
|
||||
props.storedObject.currentVersion !== null &&
|
||||
is_extension_viewable(props.storedObject.currentVersion.type) &&
|
||||
props.storedObject.currentVersion.type !== "application/pdf" &&
|
||||
props.storedObject.currentVersion.persisted !== false
|
||||
);
|
||||
});
|
||||
|
||||
const isHistoryViewable = computed<boolean>(() => {
|
||||
return props.storedObject.status === 'ready';
|
||||
return props.storedObject.status === "ready";
|
||||
});
|
||||
|
||||
const checkForReady = function(): void {
|
||||
if (
|
||||
'ready' === props.storedObject.status
|
||||
|| 'empty' === props.storedObject.status
|
||||
|| 'failure' === props.storedObject.status
|
||||
// stop reloading if the page stays opened for a long time
|
||||
|| tryiesForReady > maxTryiesForReady
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const checkForReady = function (): void {
|
||||
if (
|
||||
"ready" === props.storedObject.status ||
|
||||
"empty" === props.storedObject.status ||
|
||||
"failure" === props.storedObject.status ||
|
||||
// stop reloading if the page stays opened for a long time
|
||||
tryiesForReady > maxTryiesForReady
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
tryiesForReady = tryiesForReady + 1;
|
||||
tryiesForReady = tryiesForReady + 1;
|
||||
|
||||
setTimeout(onObjectNewStatusCallback, 5000);
|
||||
setTimeout(onObjectNewStatusCallback, 5000);
|
||||
};
|
||||
|
||||
const onObjectNewStatusCallback = async function(): Promise<void> {
|
||||
const onObjectNewStatusCallback = async function (): Promise<void> {
|
||||
if (props.storedObject.status === "stored_object_created") {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const new_status = await is_object_ready(props.storedObject);
|
||||
if (props.storedObject.status !== new_status.status) {
|
||||
emit("onStoredObjectStatusChange", new_status);
|
||||
return Promise.resolve();
|
||||
} else if ("failure" === new_status.status) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
if ("ready" !== new_status.status) {
|
||||
// we check for new status, unless it is ready
|
||||
checkForReady();
|
||||
}
|
||||
|
||||
if (props.storedObject.status === 'stored_object_created') {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const new_status = await is_object_ready(props.storedObject);
|
||||
if (props.storedObject.status !== new_status.status) {
|
||||
emit('onStoredObjectStatusChange', new_status);
|
||||
return Promise.resolve();
|
||||
} else if ('failure' === new_status.status) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
if ('ready' !== new_status.status) {
|
||||
// we check for new status, unless it is ready
|
||||
checkForReady();
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
checkForReady();
|
||||
})
|
||||
checkForReady();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
<style scoped></style>
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,23 +1,35 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import {StoredObject, StoredObjectVersionCreated} from "../../types";
|
||||
import {encryptFile, fetchNewStoredObject, uploadVersion} from "../../js/async-upload/uploader";
|
||||
import {computed, ref, Ref} from "vue";
|
||||
import { StoredObject, StoredObjectVersionCreated } from "../../types";
|
||||
import {
|
||||
encryptFile,
|
||||
fetchNewStoredObject,
|
||||
uploadVersion,
|
||||
} from "../../js/async-upload/uploader";
|
||||
import { computed, ref, Ref } from "vue";
|
||||
import FileIcon from "ChillDocStoreAssets/vuejs/FileIcon.vue";
|
||||
|
||||
interface DropFileConfig {
|
||||
existingDoc?: StoredObject,
|
||||
existingDoc?: StoredObject;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<DropFileConfig>(), {existingDoc: null});
|
||||
const props = withDefaults(defineProps<DropFileConfig>(), {
|
||||
existingDoc: null,
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'addDocument', {stored_object_version: StoredObjectVersionCreated, stored_object: StoredObject}): void,
|
||||
}>();
|
||||
const emit =
|
||||
defineEmits<
|
||||
(
|
||||
e: "addDocument",
|
||||
{
|
||||
stored_object_version: StoredObjectVersionCreated,
|
||||
stored_object: StoredObject,
|
||||
},
|
||||
) => void
|
||||
>();
|
||||
|
||||
const is_dragging: Ref<boolean> = ref(false);
|
||||
const uploading: Ref<boolean> = ref(false);
|
||||
const display_filename: Ref<string|null> = ref(null);
|
||||
const display_filename: Ref<string | null> = ref(null);
|
||||
|
||||
const has_existing_doc = computed<boolean>(() => {
|
||||
return props.existingDoc !== undefined && props.existingDoc !== null;
|
||||
@ -27,13 +39,13 @@ const onDragOver = (e: Event) => {
|
||||
e.preventDefault();
|
||||
|
||||
is_dragging.value = true;
|
||||
}
|
||||
};
|
||||
|
||||
const onDragLeave = (e: Event) => {
|
||||
e.preventDefault();
|
||||
|
||||
is_dragging.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const onDrop = (e: DragEvent) => {
|
||||
e.preventDefault();
|
||||
@ -49,8 +61,8 @@ const onDrop = (e: DragEvent) => {
|
||||
return;
|
||||
}
|
||||
|
||||
handleFile(files[0])
|
||||
}
|
||||
handleFile(files[0]);
|
||||
};
|
||||
|
||||
const onZoneClick = (e: Event) => {
|
||||
e.stopPropagation();
|
||||
@ -61,21 +73,21 @@ const onZoneClick = (e: Event) => {
|
||||
input.addEventListener("change", onFileChange);
|
||||
|
||||
input.click();
|
||||
}
|
||||
};
|
||||
|
||||
const onFileChange = async (event: Event): Promise<void> => {
|
||||
const input = event.target as HTMLInputElement;
|
||||
|
||||
if (input.files && input.files[0]) {
|
||||
console.log('file added', input.files[0]);
|
||||
console.log("file added", input.files[0]);
|
||||
const file = input.files[0];
|
||||
await handleFile(file);
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
throw 'No file given';
|
||||
}
|
||||
throw "No file given";
|
||||
};
|
||||
|
||||
const handleFile = async (file: File): Promise<void> => {
|
||||
uploading.value = true;
|
||||
@ -100,25 +112,39 @@ const handleFile = async (file: File): Promise<void> => {
|
||||
keyInfos: jsonWebKey,
|
||||
type: type,
|
||||
persisted: false,
|
||||
}
|
||||
};
|
||||
|
||||
emit('addDocument', {stored_object, stored_object_version});
|
||||
emit("addDocument", { stored_object, stored_object_version });
|
||||
uploading.value = false;
|
||||
}
|
||||
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="drop-file">
|
||||
<div v-if="!uploading" :class="{ area: true, dragging: is_dragging}" @click="onZoneClick" @dragover="onDragOver" @dragleave="onDragLeave" @drop="onDrop">
|
||||
<div
|
||||
v-if="!uploading"
|
||||
:class="{ area: true, dragging: is_dragging }"
|
||||
@click="onZoneClick"
|
||||
@dragover="onDragOver"
|
||||
@dragleave="onDragLeave"
|
||||
@drop="onDrop"
|
||||
>
|
||||
<p v-if="has_existing_doc" class="file-icon">
|
||||
<file-icon :type="props.existingDoc?.type"></file-icon>
|
||||
</p>
|
||||
|
||||
<p v-if="display_filename !== null" class="display-filename">{{ display_filename }}</p>
|
||||
<p v-if="display_filename !== null" class="display-filename">
|
||||
{{ display_filename }}
|
||||
</p>
|
||||
<!-- todo i18n -->
|
||||
<p v-if="has_existing_doc">Déposez un document ou cliquez ici pour remplacer le document existant</p>
|
||||
<p v-else>Déposez un document ou cliquez ici pour ouvrir le navigateur de fichier</p>
|
||||
<p v-if="has_existing_doc">
|
||||
Déposez un document ou cliquez ici pour remplacer le document
|
||||
existant
|
||||
</p>
|
||||
<p v-else>
|
||||
Déposez un document ou cliquez ici pour ouvrir le navigateur de
|
||||
fichier
|
||||
</p>
|
||||
</div>
|
||||
<div v-else class="waiting">
|
||||
<i class="fa fa-cog fa-spin fa-3x fa-fw"></i>
|
||||
@ -140,7 +166,8 @@ const handleFile = async (file: File): Promise<void> => {
|
||||
font-weight: 200;
|
||||
}
|
||||
|
||||
& > .area, & > .waiting {
|
||||
& > .area,
|
||||
& > .waiting {
|
||||
width: 100%;
|
||||
height: 10rem;
|
||||
|
||||
@ -163,5 +190,4 @@ const handleFile = async (file: File): Promise<void> => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
@ -1,14 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import Modal from "ChillMainAssets/vuejs/_components/Modal.vue";
|
||||
import {StoredObject, StoredObjectVersion} from "../../types";
|
||||
import { StoredObject, StoredObjectVersion } from "../../types";
|
||||
import DropFileWidget from "ChillDocStoreAssets/vuejs/DropFileWidget/DropFileWidget.vue";
|
||||
import {computed, reactive} from "vue";
|
||||
import {useToast} from 'vue-toast-notification';
|
||||
import { computed, reactive } from "vue";
|
||||
import { useToast } from "vue-toast-notification";
|
||||
|
||||
interface DropFileConfig {
|
||||
allowRemove: boolean,
|
||||
existingDoc?: StoredObject,
|
||||
allowRemove: boolean;
|
||||
existingDoc?: StoredObject;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<DropFileConfig>(), {
|
||||
@ -16,33 +15,46 @@ const props = withDefaults(defineProps<DropFileConfig>(), {
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'addDocument', {stored_object: StoredObject, stored_object_version: StoredObjectVersion}): void,
|
||||
(e: 'removeDocument'): void
|
||||
(
|
||||
e: "addDocument",
|
||||
{
|
||||
stored_object: StoredObject,
|
||||
stored_object_version: StoredObjectVersion,
|
||||
},
|
||||
): void;
|
||||
(e: "removeDocument"): void;
|
||||
}>();
|
||||
|
||||
const $toast = useToast();
|
||||
|
||||
const state = reactive({showModal: false});
|
||||
const state = reactive({ showModal: false });
|
||||
|
||||
const modalClasses = {"modal-dialog-centered": true, "modal-md": true};
|
||||
const modalClasses = { "modal-dialog-centered": true, "modal-md": true };
|
||||
|
||||
const buttonState = computed<'add'|'replace'>(() => {
|
||||
const buttonState = computed<"add" | "replace">(() => {
|
||||
if (props.existingDoc === undefined || props.existingDoc === null) {
|
||||
return 'add';
|
||||
return "add";
|
||||
}
|
||||
|
||||
return 'replace';
|
||||
})
|
||||
return "replace";
|
||||
});
|
||||
|
||||
function onAddDocument({stored_object, stored_object_version}: {stored_object: StoredObject, stored_object_version: StoredObjectVersion}): void {
|
||||
const message = buttonState.value === 'add' ? "Document ajouté" : "Document remplacé";
|
||||
function onAddDocument({
|
||||
stored_object,
|
||||
stored_object_version,
|
||||
}: {
|
||||
stored_object: StoredObject;
|
||||
stored_object_version: StoredObjectVersion;
|
||||
}): void {
|
||||
const message =
|
||||
buttonState.value === "add" ? "Document ajouté" : "Document remplacé";
|
||||
$toast.success(message);
|
||||
emit('addDocument', {stored_object_version, stored_object});
|
||||
emit("addDocument", { stored_object_version, stored_object });
|
||||
state.showModal = false;
|
||||
}
|
||||
|
||||
function onRemoveDocument(): void {
|
||||
emit('removeDocument');
|
||||
emit("removeDocument");
|
||||
}
|
||||
|
||||
function openModal(): void {
|
||||
@ -55,15 +67,30 @@ function closeModal(): void {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button v-if="buttonState === 'add'" @click="openModal" class="btn btn-create">Ajouter un document</button>
|
||||
<button v-else @click="openModal" class="btn btn-edit">Remplacer le document</button>
|
||||
<modal v-if="state.showModal" :modal-dialog-class="modalClasses" @close="closeModal">
|
||||
<button
|
||||
v-if="buttonState === 'add'"
|
||||
@click="openModal"
|
||||
class="btn btn-create"
|
||||
>
|
||||
Ajouter un document
|
||||
</button>
|
||||
<button v-else @click="openModal" class="btn btn-edit">
|
||||
Remplacer le document
|
||||
</button>
|
||||
<modal
|
||||
v-if="state.showModal"
|
||||
:modal-dialog-class="modalClasses"
|
||||
@close="closeModal"
|
||||
>
|
||||
<template v-slot:body>
|
||||
<drop-file-widget :existing-doc="existingDoc" :allow-remove="allowRemove" @add-document="onAddDocument" @remove-document="onRemoveDocument" ></drop-file-widget>
|
||||
<drop-file-widget
|
||||
:existing-doc="existingDoc"
|
||||
:allow-remove="allowRemove"
|
||||
@add-document="onAddDocument"
|
||||
@remove-document="onRemoveDocument"
|
||||
></drop-file-widget>
|
||||
</template>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
||||
<style scoped lang="scss"></style>
|
||||
|
@ -1,13 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import {StoredObject, StoredObjectVersion} from "../../types";
|
||||
import {computed, ref, Ref} from "vue";
|
||||
import { StoredObject, StoredObjectVersion } from "../../types";
|
||||
import { computed, ref, Ref } from "vue";
|
||||
import DropFile from "ChillDocStoreAssets/vuejs/DropFileWidget/DropFile.vue";
|
||||
import DocumentActionButtonsGroup from "ChillDocStoreAssets/vuejs/DocumentActionButtonsGroup.vue";
|
||||
|
||||
interface DropFileConfig {
|
||||
allowRemove: boolean,
|
||||
existingDoc?: StoredObject,
|
||||
allowRemove: boolean;
|
||||
existingDoc?: StoredObject;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<DropFileConfig>(), {
|
||||
@ -15,51 +14,65 @@ const props = withDefaults(defineProps<DropFileConfig>(), {
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'addDocument', {stored_object: StoredObject, stored_object_version: StoredObjectVersion}): void,
|
||||
(e: 'removeDocument'): void
|
||||
(
|
||||
e: "addDocument",
|
||||
{
|
||||
stored_object: StoredObject,
|
||||
stored_object_version: StoredObjectVersion,
|
||||
},
|
||||
): void;
|
||||
(e: "removeDocument"): void;
|
||||
}>();
|
||||
|
||||
const has_existing_doc = computed<boolean>(() => {
|
||||
return props.existingDoc !== undefined && props.existingDoc !== null;
|
||||
});
|
||||
|
||||
const dav_link_expiration = computed<number|undefined>(() => {
|
||||
const dav_link_expiration = computed<number | undefined>(() => {
|
||||
if (props.existingDoc === undefined || props.existingDoc === null) {
|
||||
return undefined;
|
||||
}
|
||||
if (props.existingDoc.status !== 'ready') {
|
||||
if (props.existingDoc.status !== "ready") {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return props.existingDoc._links?.dav_link?.expiration;
|
||||
});
|
||||
|
||||
const dav_link_href = computed<string|undefined>(() => {
|
||||
const dav_link_href = computed<string | undefined>(() => {
|
||||
if (props.existingDoc === undefined || props.existingDoc === null) {
|
||||
return undefined;
|
||||
}
|
||||
if (props.existingDoc.status !== 'ready') {
|
||||
if (props.existingDoc.status !== "ready") {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return props.existingDoc._links?.dav_link?.href;
|
||||
})
|
||||
});
|
||||
|
||||
const onAddDocument = ({stored_object, stored_object_version}: {stored_object: StoredObject, stored_object_version: StoredObjectVersion}): void => {
|
||||
emit('addDocument', {stored_object, stored_object_version});
|
||||
}
|
||||
const onAddDocument = ({
|
||||
stored_object,
|
||||
stored_object_version,
|
||||
}: {
|
||||
stored_object: StoredObject;
|
||||
stored_object_version: StoredObjectVersion;
|
||||
}): void => {
|
||||
emit("addDocument", { stored_object, stored_object_version });
|
||||
};
|
||||
|
||||
const onRemoveDocument = (e: Event): void => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
emit('removeDocument');
|
||||
}
|
||||
|
||||
emit("removeDocument");
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<drop-file :existingDoc="props.existingDoc" @addDocument="onAddDocument"></drop-file>
|
||||
<drop-file
|
||||
:existingDoc="props.existingDoc"
|
||||
@addDocument="onAddDocument"
|
||||
></drop-file>
|
||||
|
||||
<ul class="record_actions">
|
||||
<li v-if="has_existing_doc">
|
||||
@ -72,12 +85,14 @@ const onRemoveDocument = (e: Event): void => {
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<button v-if="allowRemove" class="btn btn-delete" @click="onRemoveDocument($event)" ></button>
|
||||
<button
|
||||
v-if="allowRemove"
|
||||
class="btn btn-delete"
|
||||
@click="onRemoveDocument($event)"
|
||||
></button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
||||
<style scoped lang="scss"></style>
|
||||
|
@ -4,22 +4,43 @@ interface FileIconConfig {
|
||||
}
|
||||
|
||||
const props = defineProps<FileIconConfig>();
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<i class="fa fa-file-pdf-o" v-if="props.type === 'application/pdf'"></i>
|
||||
<i class="fa fa-file-word-o" v-else-if="props.type === 'application/vnd.oasis.opendocument.text'"></i>
|
||||
<i class="fa fa-file-word-o" v-else-if="props.type === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'"></i>
|
||||
<i class="fa fa-file-word-o" v-else-if="props.type === 'application/msword'"></i>
|
||||
<i class="fa fa-file-excel-o" v-else-if="props.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'"></i>
|
||||
<i class="fa fa-file-excel-o" v-else-if="props.type === 'application/vnd.ms-excel'"></i>
|
||||
<i
|
||||
class="fa fa-file-word-o"
|
||||
v-else-if="props.type === 'application/vnd.oasis.opendocument.text'"
|
||||
></i>
|
||||
<i
|
||||
class="fa fa-file-word-o"
|
||||
v-else-if="
|
||||
props.type ===
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
|
||||
"
|
||||
></i>
|
||||
<i
|
||||
class="fa fa-file-word-o"
|
||||
v-else-if="props.type === 'application/msword'"
|
||||
></i>
|
||||
<i
|
||||
class="fa fa-file-excel-o"
|
||||
v-else-if="
|
||||
props.type ===
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
"
|
||||
></i>
|
||||
<i
|
||||
class="fa fa-file-excel-o"
|
||||
v-else-if="props.type === 'application/vnd.ms-excel'"
|
||||
></i>
|
||||
<i class="fa fa-file-image-o" v-else-if="props.type === 'image/jpeg'"></i>
|
||||
<i class="fa fa-file-image-o" v-else-if="props.type === 'image/png'"></i>
|
||||
<i class="fa fa-file-archive-o" v-else-if="props.type === 'application/x-zip-compressed'"></i>
|
||||
<i class="fa fa-file-code-o" v-else ></i>
|
||||
<i
|
||||
class="fa fa-file-archive-o"
|
||||
v-else-if="props.type === 'application/x-zip-compressed'"
|
||||
></i>
|
||||
<i class="fa fa-file-code-o" v-else></i>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
||||
<style scoped lang="scss"></style>
|
||||
|
@ -1,61 +1,65 @@
|
||||
<template>
|
||||
<a :class="props.classes" @click="download_and_open($event)" ref="btn">
|
||||
<i class="fa fa-file-pdf-o"></i>
|
||||
Télécharger en pdf
|
||||
</a>
|
||||
<a :class="props.classes" @click="download_and_open($event)" ref="btn">
|
||||
<i class="fa fa-file-pdf-o"></i>
|
||||
Télécharger en pdf
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
import {build_convert_link, download_and_decrypt_doc, download_doc} from "./helpers";
|
||||
import {
|
||||
build_convert_link,
|
||||
download_and_decrypt_doc,
|
||||
download_doc,
|
||||
} from "./helpers";
|
||||
import mime from "mime";
|
||||
import {reactive, ref} from "vue";
|
||||
import {StoredObject} from "../../types";
|
||||
import { reactive, ref } from "vue";
|
||||
import { StoredObject } from "../../types";
|
||||
|
||||
interface ConvertButtonConfig {
|
||||
storedObject: StoredObject,
|
||||
classes: { [key: string]: boolean},
|
||||
filename?: string,
|
||||
};
|
||||
storedObject: StoredObject;
|
||||
classes: Record<string, boolean>;
|
||||
filename?: string;
|
||||
}
|
||||
|
||||
interface DownloadButtonState {
|
||||
content: null|string
|
||||
content: null | string;
|
||||
}
|
||||
|
||||
const props = defineProps<ConvertButtonConfig>();
|
||||
const state: DownloadButtonState = reactive({content: null});
|
||||
const state: DownloadButtonState = reactive({ content: null });
|
||||
const btn = ref<HTMLAnchorElement | null>(null);
|
||||
|
||||
async function download_and_open(event: Event): Promise<void> {
|
||||
const button = event.target as HTMLAnchorElement;
|
||||
const button = event.target as HTMLAnchorElement;
|
||||
|
||||
if (null === state.content) {
|
||||
event.preventDefault();
|
||||
if (null === state.content) {
|
||||
event.preventDefault();
|
||||
|
||||
const raw = await download_doc(build_convert_link(props.storedObject.uuid));
|
||||
state.content = window.URL.createObjectURL(raw);
|
||||
const raw = await download_doc(
|
||||
build_convert_link(props.storedObject.uuid),
|
||||
);
|
||||
state.content = window.URL.createObjectURL(raw);
|
||||
|
||||
button.href = window.URL.createObjectURL(raw);
|
||||
button.type = 'application/pdf';
|
||||
button.href = window.URL.createObjectURL(raw);
|
||||
button.type = "application/pdf";
|
||||
|
||||
button.download = (props.filename + '.pdf') || 'document.pdf';
|
||||
}
|
||||
button.download = props.filename + ".pdf" || "document.pdf";
|
||||
}
|
||||
|
||||
button.click();
|
||||
const reset_pending = setTimeout(reset_state, 45000);
|
||||
button.click();
|
||||
const reset_pending = setTimeout(reset_state, 45000);
|
||||
}
|
||||
|
||||
function reset_state(): void {
|
||||
state.content = null;
|
||||
btn.value?.removeAttribute('download');
|
||||
btn.value?.removeAttribute('href');
|
||||
btn.value?.removeAttribute('type');
|
||||
btn.value?.removeAttribute("download");
|
||||
btn.value?.removeAttribute("href");
|
||||
btn.value?.removeAttribute("type");
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
i.fa::before {
|
||||
color: var(--bs-dropdown-link-hover-color);
|
||||
color: var(--bs-dropdown-link-hover-color);
|
||||
}
|
||||
</style>
|
||||
|
@ -1,23 +1,24 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import Modal from "ChillMainAssets/vuejs/_components/Modal.vue";
|
||||
import {computed, reactive} from "vue";
|
||||
import { computed, reactive } from "vue";
|
||||
|
||||
export interface DesktopEditButtonConfig {
|
||||
editLink: null,
|
||||
classes: { [k: string]: boolean },
|
||||
expirationLink: number|Date,
|
||||
editLink: null;
|
||||
classes: Record<string, boolean>;
|
||||
expirationLink: number | Date;
|
||||
}
|
||||
|
||||
interface DesktopEditButtonState {
|
||||
modalOpened: boolean
|
||||
};
|
||||
modalOpened: boolean;
|
||||
}
|
||||
|
||||
const state: DesktopEditButtonState = reactive({modalOpened: false});
|
||||
const state: DesktopEditButtonState = reactive({ modalOpened: false });
|
||||
|
||||
const props = defineProps<DesktopEditButtonConfig>();
|
||||
|
||||
const buildCommand = computed<string>(() => 'vnd.libreoffice.command:ofe|u|' + props.editLink);
|
||||
const buildCommand = computed<string>(
|
||||
() => "vnd.libreoffice.command:ofe|u|" + props.editLink,
|
||||
);
|
||||
|
||||
const editionUntilFormatted = computed<string>(() => {
|
||||
let d;
|
||||
@ -29,26 +30,52 @@ const editionUntilFormatted = computed<string>(() => {
|
||||
}
|
||||
console.log(props.expirationLink);
|
||||
|
||||
return (new Intl.DateTimeFormat(undefined, {'dateStyle': 'long', 'timeStyle': 'medium'})).format(d);
|
||||
return new Intl.DateTimeFormat(undefined, {
|
||||
dateStyle: "long",
|
||||
timeStyle: "medium",
|
||||
}).format(d);
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<teleport to="body">
|
||||
<modal v-if="state.modalOpened" @close="state.modalOpened=false">
|
||||
<modal v-if="state.modalOpened" @close="state.modalOpened = false">
|
||||
<template v-slot:body>
|
||||
<div class="desktop-edit">
|
||||
<p class="center">Veuillez enregistrer vos modifications avant le</p>
|
||||
<p><strong>{{ editionUntilFormatted }}</strong></p>
|
||||
<p class="center">
|
||||
Veuillez enregistrer vos modifications avant le
|
||||
</p>
|
||||
<p>
|
||||
<strong>{{ editionUntilFormatted }}</strong>
|
||||
</p>
|
||||
|
||||
<p><a class="btn btn-primary" :href="buildCommand">Ouvrir le document pour édition</a></p>
|
||||
<p>
|
||||
<a class="btn btn-primary" :href="buildCommand"
|
||||
>Ouvrir le document pour édition</a
|
||||
>
|
||||
</p>
|
||||
|
||||
<p><small>Le document peut être édité uniquement en utilisant Libre Office.</small></p>
|
||||
<p>
|
||||
<small
|
||||
>Le document peut être édité uniquement en utilisant
|
||||
Libre Office.</small
|
||||
>
|
||||
</p>
|
||||
|
||||
<p><small>En cas d'échec lors de l'enregistrement, sauver le document sur le poste de travail avant de le déposer à nouveau ici.</small></p>
|
||||
<p>
|
||||
<small
|
||||
>En cas d'échec lors de l'enregistrement, sauver le
|
||||
document sur le poste de travail avant de le déposer
|
||||
à nouveau ici.</small
|
||||
>
|
||||
</p>
|
||||
|
||||
<p><small>Vous pouvez naviguez sur d'autres pages pendant l'édition.</small></p>
|
||||
<p>
|
||||
<small
|
||||
>Vous pouvez naviguez sur d'autres pages pendant
|
||||
l'édition.</small
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
</modal>
|
||||
|
@ -1,58 +1,79 @@
|
||||
<template>
|
||||
<a v-if="!state.is_ready" :class="props.classes" @click="download_and_open()" title="Télécharger">
|
||||
<a
|
||||
v-if="!state.is_ready"
|
||||
:class="props.classes"
|
||||
@click="download_and_open()"
|
||||
title="Télécharger"
|
||||
>
|
||||
<i class="fa fa-download"></i>
|
||||
<template v-if="displayActionStringInButton">Télécharger</template>
|
||||
</a>
|
||||
<a v-else :class="props.classes" target="_blank" :type="props.atVersion.type" :download="buildDocumentName()" :href="state.href_url" ref="open_button" title="Ouvrir">
|
||||
<a
|
||||
v-else
|
||||
:class="props.classes"
|
||||
target="_blank"
|
||||
:type="props.atVersion.type"
|
||||
:download="buildDocumentName()"
|
||||
:href="state.href_url"
|
||||
ref="open_button"
|
||||
title="Ouvrir"
|
||||
>
|
||||
<i class="fa fa-external-link"></i>
|
||||
<template v-if="displayActionStringInButton">Ouvrir</template>
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {reactive, ref, nextTick, onMounted} from "vue";
|
||||
import {download_and_decrypt_doc} from "./helpers";
|
||||
import { reactive, ref, nextTick, onMounted } from "vue";
|
||||
import { download_and_decrypt_doc } from "./helpers";
|
||||
import mime from "mime";
|
||||
import {StoredObject, StoredObjectVersion} from "../../types";
|
||||
import { StoredObject, StoredObjectVersion } from "../../types";
|
||||
|
||||
interface DownloadButtonConfig {
|
||||
storedObject: StoredObject,
|
||||
atVersion: StoredObjectVersion,
|
||||
classes: { [k: string]: boolean },
|
||||
filename?: string,
|
||||
storedObject: StoredObject;
|
||||
atVersion: StoredObjectVersion;
|
||||
classes: Record<string, boolean>;
|
||||
filename?: string;
|
||||
/**
|
||||
* if true, display the action string into the button. If false, displays only
|
||||
* the icon
|
||||
*/
|
||||
displayActionStringInButton?: boolean,
|
||||
displayActionStringInButton?: boolean;
|
||||
/**
|
||||
* if true, will download directly the file on load
|
||||
*/
|
||||
directDownload?: boolean,
|
||||
directDownload?: boolean;
|
||||
}
|
||||
|
||||
interface DownloadButtonState {
|
||||
is_ready: boolean,
|
||||
is_running: boolean,
|
||||
href_url: string,
|
||||
is_ready: boolean;
|
||||
is_running: boolean;
|
||||
href_url: string;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<DownloadButtonConfig>(), {displayActionStringInButton: true, directDownload: false});
|
||||
const state: DownloadButtonState = reactive({is_ready: false, is_running: false, href_url: "#"});
|
||||
const props = withDefaults(defineProps<DownloadButtonConfig>(), {
|
||||
displayActionStringInButton: true,
|
||||
directDownload: false,
|
||||
});
|
||||
const state: DownloadButtonState = reactive({
|
||||
is_ready: false,
|
||||
is_running: false,
|
||||
href_url: "#",
|
||||
});
|
||||
|
||||
const open_button = ref<HTMLAnchorElement | null>(null);
|
||||
|
||||
function buildDocumentName(): string {
|
||||
let document_name = props.filename ?? props.storedObject.title;
|
||||
|
||||
if ('' === document_name) {
|
||||
document_name = 'document';
|
||||
if ("" === document_name) {
|
||||
document_name = "document";
|
||||
}
|
||||
|
||||
const ext = mime.getExtension(props.atVersion.type);
|
||||
|
||||
if (null !== ext) {
|
||||
return document_name + '.' + ext;
|
||||
return document_name + "." + ext;
|
||||
}
|
||||
|
||||
return document_name;
|
||||
@ -60,21 +81,24 @@ function buildDocumentName(): string {
|
||||
|
||||
async function download_and_open(): Promise<void> {
|
||||
if (state.is_running) {
|
||||
console.log('state is running, aborting');
|
||||
console.log("state is running, aborting");
|
||||
return;
|
||||
}
|
||||
|
||||
state.is_running = true;
|
||||
|
||||
if (state.is_ready) {
|
||||
console.log('state is ready. This should not happens');
|
||||
console.log("state is ready. This should not happens");
|
||||
return;
|
||||
}
|
||||
|
||||
let raw;
|
||||
|
||||
try {
|
||||
raw = await download_and_decrypt_doc(props.storedObject, props.atVersion);
|
||||
raw = await download_and_decrypt_doc(
|
||||
props.storedObject,
|
||||
props.atVersion,
|
||||
);
|
||||
} catch (e) {
|
||||
console.error("error while downloading and decrypting document");
|
||||
console.error(e);
|
||||
@ -89,13 +113,13 @@ async function download_and_open(): Promise<void> {
|
||||
await nextTick();
|
||||
open_button.value?.click();
|
||||
|
||||
console.log('open button should have been clicked');
|
||||
console.log("open button should have been clicked");
|
||||
setTimeout(reset_state, 45000);
|
||||
}
|
||||
}
|
||||
|
||||
function reset_state(): void {
|
||||
state.href_url = '#';
|
||||
state.href_url = "#";
|
||||
state.is_ready = false;
|
||||
state.is_running = false;
|
||||
}
|
||||
@ -109,7 +133,7 @@ onMounted(() => {
|
||||
|
||||
<style scoped lang="scss">
|
||||
i.fa::before {
|
||||
color: var(--bs-dropdown-link-hover-color);
|
||||
color: var(--bs-dropdown-link-hover-color);
|
||||
}
|
||||
i.fa {
|
||||
margin-right: 0.5rem;
|
||||
|
@ -1,9 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import HistoryButtonModal from "ChillDocStoreAssets/vuejs/StoredObjectButton/HistoryButton/HistoryButtonModal.vue";
|
||||
import {StoredObject, StoredObjectVersionWithPointInTime} from "./../../types";
|
||||
import {computed, reactive, ref, useTemplateRef} from "vue";
|
||||
import {get_versions} from "./HistoryButton/api";
|
||||
import {
|
||||
StoredObject,
|
||||
StoredObjectVersionWithPointInTime,
|
||||
} from "./../../types";
|
||||
import { computed, reactive, ref, useTemplateRef } from "vue";
|
||||
import { get_versions } from "./HistoryButton/api";
|
||||
|
||||
interface HistoryButtonConfig {
|
||||
storedObject: StoredObject;
|
||||
@ -16,8 +18,8 @@ interface HistoryButtonState {
|
||||
}
|
||||
|
||||
const props = defineProps<HistoryButtonConfig>();
|
||||
const state = reactive<HistoryButtonState>({versions: [], loaded: false});
|
||||
const modal = useTemplateRef<typeof HistoryButtonModal>('modal');
|
||||
const state = reactive<HistoryButtonState>({ versions: [], loaded: false });
|
||||
const modal = useTemplateRef<typeof HistoryButtonModal>("modal");
|
||||
|
||||
const download_version_and_open_modal = async function (): Promise<void> {
|
||||
if (null !== modal.value) {
|
||||
@ -34,20 +36,29 @@ const download_version_and_open_modal = async function (): Promise<void> {
|
||||
}
|
||||
state.loaded = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onRestoreVersion = ({newVersion}: {newVersion: StoredObjectVersionWithPointInTime}) => {
|
||||
const onRestoreVersion = ({
|
||||
newVersion,
|
||||
}: {
|
||||
newVersion: StoredObjectVersionWithPointInTime;
|
||||
}) => {
|
||||
state.versions.unshift(newVersion);
|
||||
}
|
||||
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a @click="download_version_and_open_modal" class="dropdown-item">
|
||||
<history-button-modal ref="modal" :versions="state.versions" :stored-object="storedObject" :can-edit="canEdit" @restore-version="onRestoreVersion"></history-button-modal>
|
||||
<i class="fa fa-history"></i>
|
||||
Historique
|
||||
</a>
|
||||
<a @click="download_version_and_open_modal" class="dropdown-item">
|
||||
<history-button-modal
|
||||
ref="modal"
|
||||
:versions="state.versions"
|
||||
:stored-object="storedObject"
|
||||
:can-edit="canEdit"
|
||||
@restore-version="onRestoreVersion"
|
||||
></history-button-modal>
|
||||
<i class="fa fa-history"></i>
|
||||
Historique
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
@ -1,7 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import {StoredObject, StoredObjectVersionWithPointInTime} from "./../../../types";
|
||||
import {
|
||||
StoredObject,
|
||||
StoredObjectVersionWithPointInTime,
|
||||
} from "./../../../types";
|
||||
import HistoryButtonListItem from "ChillDocStoreAssets/vuejs/StoredObjectButton/HistoryButton/HistoryButtonListItem.vue";
|
||||
import {computed, reactive} from "vue";
|
||||
import { computed, reactive } from "vue";
|
||||
|
||||
interface HistoryButtonListConfig {
|
||||
versions: StoredObjectVersionWithPointInTime[];
|
||||
@ -10,8 +13,8 @@ interface HistoryButtonListConfig {
|
||||
}
|
||||
|
||||
const emit = defineEmits<{
|
||||
restoreVersion: [newVersion: StoredObjectVersionWithPointInTime]
|
||||
}>()
|
||||
restoreVersion: [newVersion: StoredObjectVersionWithPointInTime];
|
||||
}>();
|
||||
|
||||
interface HistoryButtonListState {
|
||||
/**
|
||||
@ -22,12 +25,14 @@ interface HistoryButtonListState {
|
||||
|
||||
const props = defineProps<HistoryButtonListConfig>();
|
||||
|
||||
const state = reactive<HistoryButtonListState>({restored: -1})
|
||||
const state = reactive<HistoryButtonListState>({ restored: -1 });
|
||||
|
||||
const higher_version = computed<number>(() => props.versions.reduce(
|
||||
(accumulator: number, version: StoredObjectVersionWithPointInTime) => Math.max(accumulator, version.version),
|
||||
-1
|
||||
)
|
||||
const higher_version = computed<number>(() =>
|
||||
props.versions.reduce(
|
||||
(accumulator: number, version: StoredObjectVersionWithPointInTime) =>
|
||||
Math.max(accumulator, version.version),
|
||||
-1,
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
@ -35,11 +40,14 @@ const higher_version = computed<number>(() => props.versions.reduce(
|
||||
*
|
||||
* internally, keep track of the newly restored version
|
||||
*/
|
||||
const onRestored = ({newVersion}: {newVersion: StoredObjectVersionWithPointInTime}) => {
|
||||
const onRestored = ({
|
||||
newVersion,
|
||||
}: {
|
||||
newVersion: StoredObjectVersionWithPointInTime;
|
||||
}) => {
|
||||
state.restored = newVersion.version;
|
||||
emit('restoreVersion', {newVersion});
|
||||
}
|
||||
|
||||
emit("restoreVersion", { newVersion });
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -59,9 +67,6 @@ const onRestored = ({newVersion}: {newVersion: StoredObjectVersionWithPointInTim
|
||||
<template v-else>
|
||||
<p>Chargement des versions</p>
|
||||
</template>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
||||
<style scoped lang="scss"></style>
|
||||
|
@ -1,11 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import {StoredObject, StoredObjectPointInTime, StoredObjectVersionWithPointInTime} from "./../../../types";
|
||||
import {
|
||||
StoredObject,
|
||||
StoredObjectPointInTime,
|
||||
StoredObjectVersionWithPointInTime,
|
||||
} from "./../../../types";
|
||||
import UserRenderBoxBadge from "ChillMainAssets/vuejs/_components/Entity/UserRenderBoxBadge.vue";
|
||||
import {ISOToDatetime} from "./../../../../../../ChillMainBundle/Resources/public/chill/js/date";
|
||||
import { ISOToDatetime } from "./../../../../../../ChillMainBundle/Resources/public/chill/js/date";
|
||||
import FileIcon from "ChillDocStoreAssets/vuejs/FileIcon.vue";
|
||||
import RestoreVersionButton from "ChillDocStoreAssets/vuejs/StoredObjectButton/HistoryButton/RestoreVersionButton.vue";
|
||||
import DownloadButton from "ChillDocStoreAssets/vuejs/StoredObjectButton/DownloadButton.vue";
|
||||
import {computed} from "vue";
|
||||
import { computed } from "vue";
|
||||
|
||||
interface HistoryButtonListItemConfig {
|
||||
version: StoredObjectVersionWithPointInTime;
|
||||
@ -15,51 +19,124 @@ interface HistoryButtonListItemConfig {
|
||||
}
|
||||
|
||||
const emit = defineEmits<{
|
||||
restoreVersion: [newVersion: StoredObjectVersionWithPointInTime]
|
||||
}>()
|
||||
restoreVersion: [newVersion: StoredObjectVersionWithPointInTime];
|
||||
}>();
|
||||
|
||||
const props = defineProps<HistoryButtonListItemConfig>();
|
||||
|
||||
const onRestore = ({newVersion}: {newVersion: StoredObjectVersionWithPointInTime}) => {
|
||||
emit('restoreVersion', {newVersion});
|
||||
}
|
||||
const onRestore = ({
|
||||
newVersion,
|
||||
}: {
|
||||
newVersion: StoredObjectVersionWithPointInTime;
|
||||
}) => {
|
||||
emit("restoreVersion", { newVersion });
|
||||
};
|
||||
|
||||
const isKeptBeforeConversion = computed<boolean>(() => props.version["point-in-times"].reduce(
|
||||
(accumulator: boolean, pit: StoredObjectPointInTime) => accumulator || "keep-before-conversion" === pit.reason,
|
||||
false
|
||||
const isKeptBeforeConversion = computed<boolean>(() =>
|
||||
props.version["point-in-times"].reduce(
|
||||
(accumulator: boolean, pit: StoredObjectPointInTime) =>
|
||||
accumulator || "keep-before-conversion" === pit.reason,
|
||||
false,
|
||||
),
|
||||
);
|
||||
|
||||
const isRestored = computed<boolean>(() => props.version.version > 0 && null !== props.version["from-restored"]);
|
||||
const isRestored = computed<boolean>(
|
||||
() => props.version.version > 0 && null !== props.version["from-restored"],
|
||||
);
|
||||
|
||||
const isDuplicated = computed<boolean>(() => props.version.version === 0 && null !== props.version["from-restored"]);
|
||||
|
||||
const classes = computed<{row: true, 'row-hover': true, 'blinking-1': boolean, 'blinking-2': boolean}>(() => ({row: true, 'row-hover': true, 'blinking-1': props.isRestored && 0 === props.version.version % 2, 'blinking-2': props.isRestored && 1 === props.version.version % 2}));
|
||||
const isDuplicated = computed<boolean>(
|
||||
() =>
|
||||
props.version.version === 0 && null !== props.version["from-restored"],
|
||||
);
|
||||
|
||||
const classes = computed<{
|
||||
row: true;
|
||||
"row-hover": true;
|
||||
"blinking-1": boolean;
|
||||
"blinking-2": boolean;
|
||||
}>(() => ({
|
||||
row: true,
|
||||
"row-hover": true,
|
||||
"blinking-1": props.isRestored && 0 === props.version.version % 2,
|
||||
"blinking-2": props.isRestored && 1 === props.version.version % 2,
|
||||
}));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="classes">
|
||||
<div class="col-12 tags" v-if="isCurrent || isKeptBeforeConversion || isRestored || isDuplicated">
|
||||
<span class="badge bg-success" v-if="isCurrent">Version actuelle</span>
|
||||
<span class="badge bg-info" v-if="isKeptBeforeConversion">Conservée avant conversion dans un autre format</span>
|
||||
<span class="badge bg-info" v-if="isRestored">Restaurée depuis la version {{ version["from-restored"]?.version + 1 }}</span>
|
||||
<span class="badge bg-info" v-if="isDuplicated">Dupliqué depuis un autre document</span>
|
||||
<div :class="classes">
|
||||
<div
|
||||
class="col-12 tags"
|
||||
v-if="
|
||||
isCurrent ||
|
||||
isKeptBeforeConversion ||
|
||||
isRestored ||
|
||||
isDuplicated
|
||||
"
|
||||
>
|
||||
<span class="badge bg-success" v-if="isCurrent"
|
||||
>Version actuelle</span
|
||||
>
|
||||
<span class="badge bg-info" v-if="isKeptBeforeConversion"
|
||||
>Conservée avant conversion dans un autre format</span
|
||||
>
|
||||
<span class="badge bg-info" v-if="isRestored"
|
||||
>Restaurée depuis la version
|
||||
{{ version["from-restored"]?.version + 1 }}</span
|
||||
>
|
||||
<span class="badge bg-info" v-if="isDuplicated"
|
||||
>Dupliqué depuis un autre document</span
|
||||
>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<file-icon :type="version.type"></file-icon>
|
||||
<span
|
||||
><strong>#{{ version.version + 1 }}</strong></span
|
||||
>
|
||||
<template
|
||||
v-if="version.createdBy !== null && version.createdAt !== null"
|
||||
><strong v-if="version.version == 0">Créé par</strong
|
||||
><strong v-else>modifié par</strong>
|
||||
<span class="badge-user"
|
||||
><UserRenderBoxBadge
|
||||
:user="version.createdBy"
|
||||
></UserRenderBoxBadge
|
||||
></span>
|
||||
<strong>à</strong>
|
||||
{{
|
||||
$d(ISOToDatetime(version.createdAt.datetime8601), "long")
|
||||
}}</template
|
||||
><template
|
||||
v-if="version.createdBy === null && version.createdAt !== null"
|
||||
><strong v-if="version.version == 0">Créé le</strong
|
||||
><strong v-else>modifié le</strong>
|
||||
{{
|
||||
$d(ISOToDatetime(version.createdAt.datetime8601), "long")
|
||||
}}</template
|
||||
>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<ul class="record_actions small slim on-version-actions">
|
||||
<li v-if="canEdit && !isCurrent">
|
||||
<restore-version-button
|
||||
:stored-object-version="props.version"
|
||||
@restore-version="onRestore"
|
||||
></restore-version-button>
|
||||
</li>
|
||||
<li>
|
||||
<download-button
|
||||
:stored-object="storedObject"
|
||||
:at-version="version"
|
||||
:classes="{
|
||||
btn: true,
|
||||
'btn-outline-primary': true,
|
||||
'btn-sm': true,
|
||||
}"
|
||||
:display-action-string-in-button="false"
|
||||
></download-button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<file-icon :type="version.type"></file-icon> <span><strong>#{{ version.version + 1 }}</strong></span> <template v-if="version.createdBy !== null && version.createdAt !== null"><strong v-if="version.version == 0">Créé par</strong><strong v-else>modifié par</strong> <span class="badge-user"><UserRenderBoxBadge :user="version.createdBy"></UserRenderBoxBadge></span> <strong>à</strong> {{ $d(ISOToDatetime(version.createdAt.datetime8601), 'long') }}</template><template v-if="version.createdBy === null && version.createdAt !== null"><strong v-if="version.version == 0">Créé le</strong><strong v-else>modifié le</strong> {{ $d(ISOToDatetime(version.createdAt.datetime8601), 'long') }}</template>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<ul class="record_actions small slim on-version-actions">
|
||||
<li v-if="canEdit && !isCurrent">
|
||||
<restore-version-button :stored-object-version="props.version" @restore-version="onRestore"></restore-version-button>
|
||||
</li>
|
||||
<li>
|
||||
<download-button :stored-object="storedObject" :at-version="version" :classes="{btn: true, 'btn-outline-primary': true, 'btn-sm': true}" :display-action-string-in-button="false"></download-button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
@ -1,8 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import Modal from "ChillMainAssets/vuejs/_components/Modal.vue";
|
||||
import {reactive} from "vue";
|
||||
import { reactive } from "vue";
|
||||
import HistoryButtonList from "ChillDocStoreAssets/vuejs/StoredObjectButton/HistoryButton/HistoryButtonList.vue";
|
||||
import {StoredObject, StoredObjectVersionWithPointInTime} from "./../../../types";
|
||||
import {
|
||||
StoredObject,
|
||||
StoredObjectVersionWithPointInTime,
|
||||
} from "./../../../types";
|
||||
|
||||
interface HistoryButtonListConfig {
|
||||
versions: StoredObjectVersionWithPointInTime[];
|
||||
@ -11,22 +14,21 @@ interface HistoryButtonListConfig {
|
||||
}
|
||||
|
||||
const emit = defineEmits<{
|
||||
restoreVersion: [newVersion: StoredObjectVersionWithPointInTime]
|
||||
}>()
|
||||
restoreVersion: [newVersion: StoredObjectVersionWithPointInTime];
|
||||
}>();
|
||||
|
||||
interface HistoryButtonModalState {
|
||||
opened: boolean;
|
||||
}
|
||||
|
||||
const props = defineProps<HistoryButtonListConfig>();
|
||||
const state = reactive<HistoryButtonModalState>({opened: false});
|
||||
const state = reactive<HistoryButtonModalState>({ opened: false });
|
||||
|
||||
const open = () => {
|
||||
state.opened = true;
|
||||
}
|
||||
|
||||
defineExpose({open});
|
||||
};
|
||||
|
||||
defineExpose({ open });
|
||||
</script>
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
@ -36,12 +38,17 @@ defineExpose({open});
|
||||
</template>
|
||||
<template v-slot:body>
|
||||
<p>Les versions sont conservées pendant 90 jours.</p>
|
||||
<history-button-list :versions="props.versions" :can-edit="canEdit" :stored-object="storedObject" @restore-version="(payload) => emit('restoreVersion', payload)"></history-button-list>
|
||||
<history-button-list
|
||||
:versions="props.versions"
|
||||
:can-edit="canEdit"
|
||||
:stored-object="storedObject"
|
||||
@restore-version="
|
||||
(payload) => emit('restoreVersion', payload)
|
||||
"
|
||||
></history-button-list>
|
||||
</template>
|
||||
</modal>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
||||
<style scoped lang="scss"></style>
|
||||
|
@ -1,17 +1,20 @@
|
||||
<script setup lang="ts">
|
||||
import {StoredObjectVersionPersisted, StoredObjectVersionWithPointInTime} from "../../../types";
|
||||
import {useToast} from "vue-toast-notification";
|
||||
import {restore_version} from "./api";
|
||||
import {
|
||||
StoredObjectVersionPersisted,
|
||||
StoredObjectVersionWithPointInTime,
|
||||
} from "../../../types";
|
||||
import { useToast } from "vue-toast-notification";
|
||||
import { restore_version } from "./api";
|
||||
|
||||
interface RestoreVersionButtonProps {
|
||||
storedObjectVersion: StoredObjectVersionPersisted,
|
||||
storedObjectVersion: StoredObjectVersionPersisted;
|
||||
}
|
||||
|
||||
const emit = defineEmits<{
|
||||
restoreVersion: [newVersion: StoredObjectVersionWithPointInTime]
|
||||
}>()
|
||||
restoreVersion: [newVersion: StoredObjectVersionWithPointInTime];
|
||||
}>();
|
||||
|
||||
const props = defineProps<RestoreVersionButtonProps>()
|
||||
const props = defineProps<RestoreVersionButtonProps>();
|
||||
|
||||
const $toast = useToast();
|
||||
|
||||
@ -19,14 +22,18 @@ const restore_version_fn = async () => {
|
||||
const newVersion = await restore_version(props.storedObjectVersion);
|
||||
|
||||
$toast.success("Version restaurée");
|
||||
emit('restoreVersion', {newVersion});
|
||||
}
|
||||
emit("restoreVersion", { newVersion });
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button class="btn btn-outline-action" @click="restore_version_fn" title="Restaurer"><i class="fa fa-rotate-left"></i> Restaurer</button>
|
||||
<button
|
||||
class="btn btn-outline-action"
|
||||
@click="restore_version_fn"
|
||||
title="Restaurer"
|
||||
>
|
||||
<i class="fa fa-rotate-left"></i> Restaurer
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
||||
<style scoped lang="scss"></style>
|
||||
|
@ -1,20 +1,29 @@
|
||||
<template>
|
||||
<a :class="Object.assign(props.classes, {'btn': true})" @click="beforeLeave($event)" :href="build_wopi_editor_link(props.storedObject.uuid, props.returnPath)">
|
||||
<i class="fa fa-paragraph"></i>
|
||||
Editer en ligne
|
||||
</a>
|
||||
<a
|
||||
:class="Object.assign(props.classes, { btn: true })"
|
||||
@click="beforeLeave($event)"
|
||||
:href="
|
||||
build_wopi_editor_link(props.storedObject.uuid, props.returnPath)
|
||||
"
|
||||
>
|
||||
<i class="fa fa-paragraph"></i>
|
||||
Editer en ligne
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import WopiEditButton from "./WopiEditButton.vue";
|
||||
import {build_wopi_editor_link} from "./helpers";
|
||||
import {StoredObject, WopiEditButtonExecutableBeforeLeaveFunction} from "../../types";
|
||||
import { build_wopi_editor_link } from "./helpers";
|
||||
import {
|
||||
StoredObject,
|
||||
WopiEditButtonExecutableBeforeLeaveFunction,
|
||||
} from "../../types";
|
||||
|
||||
interface WopiEditButtonConfig {
|
||||
storedObject: StoredObject,
|
||||
returnPath?: string,
|
||||
classes: {[k: string] : boolean},
|
||||
executeBeforeLeave?: WopiEditButtonExecutableBeforeLeaveFunction,
|
||||
storedObject: StoredObject;
|
||||
returnPath?: string;
|
||||
classes: Record<string, boolean>;
|
||||
executeBeforeLeave?: WopiEditButtonExecutableBeforeLeaveFunction;
|
||||
}
|
||||
|
||||
const props = defineProps<WopiEditButtonConfig>();
|
||||
@ -22,25 +31,24 @@ const props = defineProps<WopiEditButtonConfig>();
|
||||
let executed = false;
|
||||
|
||||
async function beforeLeave(event: Event): Promise<true> {
|
||||
if (props.executeBeforeLeave === undefined || executed === true) {
|
||||
if (props.executeBeforeLeave === undefined || executed === true) {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
await props.executeBeforeLeave();
|
||||
executed = true;
|
||||
|
||||
const link = event.target as HTMLAnchorElement;
|
||||
link.click();
|
||||
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
await props.executeBeforeLeave();
|
||||
executed = true;
|
||||
|
||||
const link = event.target as HTMLAnchorElement;
|
||||
link.click();
|
||||
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
i.fa::before {
|
||||
color: var(--bs-dropdown-link-hover-color);
|
||||
color: var(--bs-dropdown-link-hover-color);
|
||||
}
|
||||
</style>
|
||||
|
||||
|
@ -1 +1 @@
|
||||
require('./chillevent.scss');
|
||||
require("./chillevent.scss");
|
||||
|
@ -1,4 +1,3 @@
|
||||
module.exports = function(encore, entries)
|
||||
{
|
||||
entries.push(__dirname + '/Resources/public/chill/index.js');
|
||||
module.exports = function (encore, entries) {
|
||||
entries.push(__dirname + "/Resources/public/chill/index.js");
|
||||
};
|
||||
|
@ -1 +1 @@
|
||||
require('./footer_bandeau.jpg');
|
||||
require("./footer_bandeau.jpg");
|
||||
|
@ -1,43 +1,37 @@
|
||||
import { ShowHide } from 'ShowHide/show_hide.js';
|
||||
import { ShowHide } from "ShowHide/show_hide.js";
|
||||
// listen to adding of formation and register a show hide
|
||||
|
||||
var make_show_hide = function(entry) {
|
||||
let
|
||||
obtained = entry.querySelector('[data-diploma-obtained]'),
|
||||
reconnue = entry.querySelector('[data-diploma-reconnue]')
|
||||
;
|
||||
var a = new ShowHide({
|
||||
load_event: null,
|
||||
froms: [ obtained ],
|
||||
container: [ reconnue ],
|
||||
test: function(froms, event) {
|
||||
for (let f of froms.values()) {
|
||||
for (let input of f.querySelectorAll('input').values()) {
|
||||
if (input.value === 'non-fr') {
|
||||
return input.checked;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
var make_show_hide = function (entry) {
|
||||
let obtained = entry.querySelector("[data-diploma-obtained]"),
|
||||
reconnue = entry.querySelector("[data-diploma-reconnue]");
|
||||
var a = new ShowHide({
|
||||
load_event: null,
|
||||
froms: [obtained],
|
||||
container: [reconnue],
|
||||
test: function (froms, event) {
|
||||
for (let f of froms.values()) {
|
||||
for (let input of f.querySelectorAll("input").values()) {
|
||||
if (input.value === "non-fr") {
|
||||
return input.checked;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
window.addEventListener('collection-add-entry', function(e) {
|
||||
if (e.detail.collection.dataset.collectionName === 'formations') {
|
||||
make_show_hide(e.detail.entry);
|
||||
}
|
||||
window.addEventListener("collection-add-entry", function (e) {
|
||||
if (e.detail.collection.dataset.collectionName === "formations") {
|
||||
make_show_hide(e.detail.entry);
|
||||
}
|
||||
});
|
||||
|
||||
// starting the formation on load
|
||||
window.addEventListener('load', function(_e) {
|
||||
let
|
||||
formations = document.querySelectorAll('[data-formation-entry]')
|
||||
;
|
||||
|
||||
for (let f of formations.values()) {
|
||||
make_show_hide(f);
|
||||
}
|
||||
window.addEventListener("load", function (_e) {
|
||||
let formations = document.querySelectorAll("[data-formation-entry]");
|
||||
for (let f of formations.values()) {
|
||||
make_show_hide(f);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1,72 +1,71 @@
|
||||
import { ShowHide } from 'ShowHide/show_hide.js';
|
||||
import { ShowHide } from "ShowHide/show_hide.js";
|
||||
|
||||
var
|
||||
div_accompagnement = document.getElementById("form_accompagnement"),
|
||||
div_accompagnement_rqth = document.getElementById("form_accompagnement_rqth"),
|
||||
div_accompagnement_comment = document.getElementById("form_accompagnement_comment"),
|
||||
div_caf_id = document.getElementById("cafId"),
|
||||
div_caf_inscription_date = document.getElementById("cafInscriptionDate"),
|
||||
div_neet_eligibilite = document.getElementById("neetEligibilite"),
|
||||
div_neet_commission_date = document.getElementById("neetCommissionDate")
|
||||
;
|
||||
|
||||
var div_accompagnement = document.getElementById("form_accompagnement"),
|
||||
div_accompagnement_rqth = document.getElementById("form_accompagnement_rqth"),
|
||||
div_accompagnement_comment = document.getElementById(
|
||||
"form_accompagnement_comment",
|
||||
),
|
||||
div_caf_id = document.getElementById("cafId"),
|
||||
div_caf_inscription_date = document.getElementById("cafInscriptionDate"),
|
||||
div_neet_eligibilite = document.getElementById("neetEligibilite"),
|
||||
div_neet_commission_date = document.getElementById("neetCommissionDate");
|
||||
// faire apparaitre / disparaitre RQTH si RQTH coché
|
||||
new ShowHide({
|
||||
"froms": [div_accompagnement],
|
||||
"test": function(froms, event) {
|
||||
for (let el of froms.values()) {
|
||||
for (let input of el.querySelectorAll('input').values()) {
|
||||
if (input.value === 'rqth') {
|
||||
return input.checked;
|
||||
}
|
||||
}
|
||||
froms: [div_accompagnement],
|
||||
test: function (froms, event) {
|
||||
for (let el of froms.values()) {
|
||||
for (let input of el.querySelectorAll("input").values()) {
|
||||
if (input.value === "rqth") {
|
||||
return input.checked;
|
||||
}
|
||||
},
|
||||
"container": [div_accompagnement_rqth]
|
||||
}
|
||||
}
|
||||
},
|
||||
container: [div_accompagnement_rqth],
|
||||
});
|
||||
|
||||
// faire apparaitre / disparaitre commetnaire si coché
|
||||
new ShowHide({
|
||||
"froms": [div_accompagnement],
|
||||
"test": function(froms, event) {
|
||||
for (let el of froms.values()) {
|
||||
for (let input of el.querySelectorAll('input').values()) {
|
||||
if (input.value === 'autre') {
|
||||
return input.checked;
|
||||
}
|
||||
}
|
||||
froms: [div_accompagnement],
|
||||
test: function (froms, event) {
|
||||
for (let el of froms.values()) {
|
||||
for (let input of el.querySelectorAll("input").values()) {
|
||||
if (input.value === "autre") {
|
||||
return input.checked;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
"container": [div_accompagnement_comment]
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
container: [div_accompagnement_comment],
|
||||
});
|
||||
|
||||
// faire apparaitre cafInscriptionDate seulement si cafID est rempli
|
||||
new ShowHide({
|
||||
froms: [ div_caf_id ],
|
||||
test: function(froms, event) {
|
||||
for (let el of froms.values()) {
|
||||
return el.querySelector("input").value !== "";
|
||||
}
|
||||
},
|
||||
container: [ div_caf_inscription_date ],
|
||||
event_name: 'input'
|
||||
froms: [div_caf_id],
|
||||
test: function (froms, event) {
|
||||
for (let el of froms.values()) {
|
||||
return el.querySelector("input").value !== "";
|
||||
}
|
||||
},
|
||||
container: [div_caf_inscription_date],
|
||||
event_name: "input",
|
||||
});
|
||||
|
||||
// faire apparaitre date de commission neet seulement si eligible
|
||||
new ShowHide({
|
||||
froms: [ div_neet_eligibilite ],
|
||||
test: function(froms, event) {
|
||||
for (let el of froms.values()) {
|
||||
for (let input of el.querySelectorAll('input').values()) {
|
||||
if (input.value === "oui") {
|
||||
return input.checked;
|
||||
}
|
||||
}
|
||||
froms: [div_neet_eligibilite],
|
||||
test: function (froms, event) {
|
||||
for (let el of froms.values()) {
|
||||
for (let input of el.querySelectorAll("input").values()) {
|
||||
if (input.value === "oui") {
|
||||
return input.checked;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
container: [ div_neet_commission_date ]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
container: [div_neet_commission_date],
|
||||
});
|
||||
|
@ -1,20 +1,17 @@
|
||||
import { ShowHide } from 'ShowHide/show_hide.js';
|
||||
import { ShowHide } from "ShowHide/show_hide.js";
|
||||
|
||||
var
|
||||
div_objectifs = document.getElementById("objectifs"),
|
||||
div_objectifs_autre = document.getElementById("objectifsAutre")
|
||||
;
|
||||
|
||||
var div_objectifs = document.getElementById("objectifs"),
|
||||
div_objectifs_autre = document.getElementById("objectifsAutre");
|
||||
new ShowHide({
|
||||
froms: [div_objectifs],
|
||||
container: [div_objectifs_autre],
|
||||
test: function(froms, event) {
|
||||
for (let el of froms.values()) {
|
||||
for (let input of el.querySelectorAll('input').values()) {
|
||||
if (input.value === 'autre') {
|
||||
return input.checked;
|
||||
}
|
||||
}
|
||||
froms: [div_objectifs],
|
||||
container: [div_objectifs_autre],
|
||||
test: function (froms, event) {
|
||||
for (let el of froms.values()) {
|
||||
for (let input of el.querySelectorAll("input").values()) {
|
||||
if (input.value === "autre") {
|
||||
return input.checked;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
@ -1,103 +1,102 @@
|
||||
import { ShowHide } from 'ShowHide/show_hide.js';
|
||||
var
|
||||
ressources = document.getElementById("ressources"),
|
||||
ressources_comment = document.getElementById("ressourcesComment"),
|
||||
handicap_is = document.getElementById('handicap_is'),
|
||||
handicap_if = document.getElementById('handicap_if'),
|
||||
situation_prof = document.getElementById('situation_prof'),
|
||||
type_contrat = document.getElementById('type_contrat'),
|
||||
type_contrat_aide = document.getElementById('type_contrat_aide'),
|
||||
situation_logement = document.getElementById('situation_logement'),
|
||||
situation_logement_precision = document.getElementById('situation_logement_precision')
|
||||
;
|
||||
|
||||
import { ShowHide } from "ShowHide/show_hide.js";
|
||||
var ressources = document.getElementById("ressources"),
|
||||
ressources_comment = document.getElementById("ressourcesComment"),
|
||||
handicap_is = document.getElementById("handicap_is"),
|
||||
handicap_if = document.getElementById("handicap_if"),
|
||||
situation_prof = document.getElementById("situation_prof"),
|
||||
type_contrat = document.getElementById("type_contrat"),
|
||||
type_contrat_aide = document.getElementById("type_contrat_aide"),
|
||||
situation_logement = document.getElementById("situation_logement"),
|
||||
situation_logement_precision = document.getElementById(
|
||||
"situation_logement_precision",
|
||||
);
|
||||
new ShowHide({
|
||||
froms: [ressources],
|
||||
container: [ressources_comment],
|
||||
test: function(froms) {
|
||||
for (let f of froms.values()) {
|
||||
for (let input of f.querySelectorAll('input').values()) {
|
||||
if (input.value === 'autre') {
|
||||
return input.checked;
|
||||
}
|
||||
}
|
||||
froms: [ressources],
|
||||
container: [ressources_comment],
|
||||
test: function (froms) {
|
||||
for (let f of froms.values()) {
|
||||
for (let input of f.querySelectorAll("input").values()) {
|
||||
if (input.value === "autre") {
|
||||
return input.checked;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
new ShowHide({
|
||||
froms: [handicap_is],
|
||||
container: [handicap_if],
|
||||
test: function(froms) {
|
||||
for (let f of froms.values()) {
|
||||
for (let input of f.querySelectorAll('input').values()) {
|
||||
if (input.value === '1') {
|
||||
return input.checked;
|
||||
}
|
||||
}
|
||||
froms: [handicap_is],
|
||||
container: [handicap_if],
|
||||
test: function (froms) {
|
||||
for (let f of froms.values()) {
|
||||
for (let input of f.querySelectorAll("input").values()) {
|
||||
if (input.value === "1") {
|
||||
return input.checked;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
});
|
||||
|
||||
var show_hide_contrat_aide = new ShowHide({
|
||||
froms: [type_contrat],
|
||||
container: [type_contrat_aide],
|
||||
test: function(froms) {
|
||||
for (let f of froms.values()) {
|
||||
for (let input of f.querySelectorAll('input').values()) {
|
||||
if (input.value === 'contrat_aide') {
|
||||
return input.checked;
|
||||
}
|
||||
}
|
||||
froms: [type_contrat],
|
||||
container: [type_contrat_aide],
|
||||
test: function (froms) {
|
||||
for (let f of froms.values()) {
|
||||
for (let input of f.querySelectorAll("input").values()) {
|
||||
if (input.value === "contrat_aide") {
|
||||
return input.checked;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
});
|
||||
|
||||
new ShowHide({
|
||||
id: 'situation_prof_type_contrat',
|
||||
froms: [situation_prof],
|
||||
container: [type_contrat],
|
||||
test: function(froms) {
|
||||
for (let f of froms.values()) {
|
||||
for (let input of f.querySelectorAll('input').values()) {
|
||||
if (input.value === 'en_activite') {
|
||||
return input.checked;
|
||||
}
|
||||
}
|
||||
id: "situation_prof_type_contrat",
|
||||
froms: [situation_prof],
|
||||
container: [type_contrat],
|
||||
test: function (froms) {
|
||||
for (let f of froms.values()) {
|
||||
for (let input of f.querySelectorAll("input").values()) {
|
||||
if (input.value === "en_activite") {
|
||||
return input.checked;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
});
|
||||
|
||||
window.addEventListener('show-hide-hide', function (e) {
|
||||
if (e.detail.id = 'situation_prof_type_contrat') {
|
||||
show_hide_contrat_aide.forceHide();
|
||||
}
|
||||
window.addEventListener("show-hide-hide", function (e) {
|
||||
if ((e.detail.id = "situation_prof_type_contrat")) {
|
||||
show_hide_contrat_aide.forceHide();
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('show-hide-show', function (e) {
|
||||
if (e.detail.id = 'situation_prof_type_contrat') {
|
||||
show_hide_contrat_aide.forceCompute();
|
||||
}
|
||||
window.addEventListener("show-hide-show", function (e) {
|
||||
if ((e.detail.id = "situation_prof_type_contrat")) {
|
||||
show_hide_contrat_aide.forceCompute();
|
||||
}
|
||||
});
|
||||
|
||||
new ShowHide({
|
||||
froms: [situation_logement],
|
||||
container: [situation_logement_precision],
|
||||
test: function(froms) {
|
||||
for (let f of froms.values()) {
|
||||
for (let input of f.querySelectorAll('input')) {
|
||||
if (input.value === 'heberge_chez_tiers') {
|
||||
return input.checked;
|
||||
}
|
||||
}
|
||||
froms: [situation_logement],
|
||||
container: [situation_logement_precision],
|
||||
test: function (froms) {
|
||||
for (let f of froms.values()) {
|
||||
for (let input of f.querySelectorAll("input")) {
|
||||
if (input.value === "heberge_chez_tiers") {
|
||||
return input.checked;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
},
|
||||
});
|
||||
|
@ -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');
|
||||
|
||||
|
@ -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 };
|
||||
|
@ -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 };
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
let
|
||||
[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;
|
||||
}
|
||||
|
||||
let
|
||||
[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("");
|
||||
|
||||
let 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;
|
||||
}
|
||||
};
|
||||
|
@ -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`,
|
||||
);
|
||||
};
|
||||
|
@ -1,62 +1,61 @@
|
||||
import {Scope} from '../../types';
|
||||
import { Scope } from "../../types";
|
||||
|
||||
export type body = {[key: string]: boolean|string|number|null};
|
||||
export type fetchOption = {[key: string]: boolean|string|number|null};
|
||||
export type body = Record<string, boolean | string | number | null>;
|
||||
export type fetchOption = Record<string, boolean | string | number | null>;
|
||||
|
||||
export interface Params {
|
||||
[key: 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 interface FetchParams {
|
||||
[K: 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[];
|
||||
}
|
||||
|
||||
@ -64,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);
|
||||
});
|
||||
}
|
||||
|
||||
@ -105,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>> {
|
||||
const item_per_page: number = 50;
|
||||
function _fetchAction<T>(
|
||||
page: number,
|
||||
uri: string,
|
||||
params?: FetchParams,
|
||||
): Promise<PaginationResponse<T>> {
|
||||
const item_per_page = 50;
|
||||
|
||||
let searchParams = new URLSearchParams();
|
||||
searchParams.append('item_per_page', item_per_page.toString());
|
||||
searchParams.append('page', page.toString());
|
||||
const searchParams = new URLSearchParams();
|
||||
searchParams.append("item_per_page", item_per_page.toString());
|
||||
searchParams.append("page", page.toString());
|
||||
|
||||
if (params !== undefined) {
|
||||
Object.keys(params).forEach(key => {
|
||||
let 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, "");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let 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;
|
||||
let 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));
|
||||
|
||||
@ -185,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;
|
||||
};
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
// const _fetchAction = (page, uri, params) => {
|
||||
// const item_per_page = 50;
|
||||
// if (params === undefined) {
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -1,3 +1 @@
|
||||
require("./layout.scss");
|
||||
|
||||
|
||||
|
@ -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);
|
||||
});
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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 };
|
||||
|
@ -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,
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user