Merge remote-tracking branch 'origin/master' into ticket-app-master

This commit is contained in:
2025-06-20 12:53:20 +02:00
49 changed files with 7986 additions and 7490 deletions

View File

@@ -0,0 +1,7 @@
kind: Fixed
body: |
Allow null and cast as string to setContent method for NewsItem
time: 2025-06-19T17:01:42.125730402+02:00
custom:
Issue: "392"
SchemaChange: No schema change

View File

@@ -46,7 +46,7 @@ stages:
build: build:
stage: Composer install stage: Composer install
image: gitea.champs-libres.be/chill-project/chill-skeleton-basic/base-image:php82 image: chill/base-image:8.3-edge
before_script: before_script:
- composer config -g cache-dir "$(pwd)/.cache" - composer config -g cache-dir "$(pwd)/.cache"
script: script:
@@ -61,7 +61,7 @@ build:
code_style: code_style:
stage: Tests stage: Tests
image: gitea.champs-libres.be/chill-project/chill-skeleton-basic/base-image:php82 image: chill/base-image:8.3-edge
script: script:
- php-cs-fixer fix --dry-run -v --show-progress=none - php-cs-fixer fix --dry-run -v --show-progress=none
cache: cache:
@@ -74,7 +74,7 @@ code_style:
phpstan_tests: phpstan_tests:
stage: Tests stage: Tests
image: gitea.champs-libres.be/chill-project/chill-skeleton-basic/base-image:php82 image: chill/base-image:8.3-edge
variables: variables:
COMPOSER_MEMORY_LIMIT: 3G COMPOSER_MEMORY_LIMIT: 3G
before_script: before_script:
@@ -91,7 +91,7 @@ phpstan_tests:
rector_tests: rector_tests:
stage: Tests stage: Tests
image: gitea.champs-libres.be/chill-project/chill-skeleton-basic/base-image:php82 image: chill/base-image:8.3-edge
before_script: before_script:
- bin/console cache:clear --env=dev - bin/console cache:clear --env=dev
script: script:
@@ -132,7 +132,7 @@ lint:
unit_tests: unit_tests:
stage: Tests stage: Tests
image: gitea.champs-libres.be/chill-project/chill-skeleton-basic/base-image:php82 image: chill/base-image:8.3-edge
variables: variables:
COMPOSER_MEMORY_LIMIT: 3G COMPOSER_MEMORY_LIMIT: 3G
before_script: before_script:

View File

@@ -70,9 +70,9 @@ class NewsItem implements TrackCreationInterface, TrackUpdateInterface
return $this->content; return $this->content;
} }
public function setContent(string $content): void public function setContent(?string $content): void
{ {
$this->content = $content; $this->content = (string) $content;
} }
public function getStartDate(): ?\DateTimeImmutable public function getStartDate(): ?\DateTimeImmutable

View File

@@ -2,10 +2,10 @@
<span class="chill-entity entity-user"> <span class="chill-entity entity-user">
{{ user.label }} {{ user.label }}
<span class="user-job" v-if="user.user_job !== null" <span class="user-job" v-if="user.user_job !== null"
>({{ localizeString(user.user_job.label) }})</span > ({{ localizeString(user.user_job.label) }})</span
> >
<span class="main-scope" v-if="user.main_scope !== null" <span class="main-scope" v-if="user.main_scope !== null"
>({{ localizeString(user.main_scope.name) }})</span > ({{ localizeString(user.main_scope.name) }})</span
> >
<span <span
v-if="user.isAbsent" v-if="user.isAbsent"
@@ -22,8 +22,8 @@ import { localizeString } from "ChillMainAssets/lib/localizationHelper/localizat
export default { export default {
name: "UserRenderBoxBadge", name: "UserRenderBoxBadge",
methods: { methods: {
localizeString() { localizeString(label) {
return localizeString; return localizeString(label);
}, },
}, },
props: ["user"], props: ["user"],

View File

@@ -1,28 +1,28 @@
<template> <template>
<banner /> <banner />
<sticky-nav /> <sticky-nav />
<h1 v-if="accompanyingCourse.step === 'DRAFT'"> <h1 v-if="accompanyingCourse.step === 'DRAFT'">
{{ $t("course.title.draft") }} {{ $t("course.title.draft") }}
</h1> </h1>
<h1 v-else> <h1 v-else>
{{ $t("course.title.active") }} {{ $t("course.title.active") }}
</h1> </h1>
<persons-associated /> <persons-associated />
<course-location /> <course-location />
<origin-demand /> <origin-demand />
<admin-location /> <admin-location />
<requestor :is-anonymous="accompanyingCourse.requestorAnonymous" /> <requestor :is-anonymous="accompanyingCourse.requestorAnonymous" />
<social-issue /> <social-issue />
<scopes /> <scopes />
<referrer /> <referrer />
<resources /> <resources />
<start-date v-if="accompanyingCourse.step.startsWith('CONFIRMED')" /> <start-date v-if="accompanyingCourse.step.startsWith('CONFIRMED')" />
<comment v-if="accompanyingCourse.step === 'DRAFT'" /> <comment v-if="accompanyingCourse.step === 'DRAFT'" />
<confirm v-if="accompanyingCourse.step === 'DRAFT'" /> <confirm v-if="accompanyingCourse.step === 'DRAFT'" />
<!-- <div v-for="error in errorMsg" v-bind:key="error.id" class="vue-component errors alert alert-danger"> <!-- <div v-for="error in errorMsg" v-bind:key="error.id" class="vue-component errors alert alert-danger">
<p> <p>
<span>{{ error.sta }} {{ error.txt }}</span><br> <span>{{ error.sta }} {{ error.txt }}</span><br>
<span>{{ $t(error.msg) }}</span> <span>{{ $t(error.msg) }}</span>
@@ -47,26 +47,26 @@ import Confirm from "./components/Confirm.vue";
import StartDate from "./components/StartDate.vue"; import StartDate from "./components/StartDate.vue";
export default { export default {
name: "App", name: "App",
components: { components: {
Banner, Banner,
StickyNav, StickyNav,
OriginDemand, OriginDemand,
AdminLocation, AdminLocation,
PersonsAssociated, PersonsAssociated,
Requestor, Requestor,
SocialIssue, SocialIssue,
CourseLocation, CourseLocation,
Scopes, Scopes,
Referrer, Referrer,
Resources, Resources,
Comment, Comment,
Confirm, Confirm,
StartDate, StartDate,
}, },
computed: { computed: {
...mapState(["accompanyingCourse", "addressContext"]), ...mapState(["accompanyingCourse", "addressContext"]),
}, },
}; };
</script> </script>
@@ -75,62 +75,62 @@ export default {
$chill-accourse-context: #718596; $chill-accourse-context: #718596;
div#accompanying-course { div#accompanying-course {
div.vue-component { div.vue-component {
h2 { h2 {
margin: 1em 0.7em; margin: 1em 0.7em;
position: relative; position: relative;
&:before { &:before {
position: absolute; position: absolute;
content: "\f142"; //ellipsis-v content: "\f142"; //ellipsis-v
font-family: "ForkAwesome"; font-family: "ForkAwesome";
color: tint-color($chill-accourse-context, 10%); color: tint-color($chill-accourse-context, 10%);
left: -22px; left: -22px;
top: 4px; top: 4px;
} }
a[id^="section"] { a[id^="section"] {
position: absolute; position: absolute;
top: -2.5em; // reference for stickNav top: -2.5em; // reference for stickNav
} }
}
padding: 0em 0em;
margin: 1em 0;
border-radius: 5px;
border: 1px dotted tint-color($chill-accourse-context, 10%);
border-left: 1px dotted tint-color($chill-accourse-context, 10%);
border-right: 1px dotted tint-color($chill-accourse-context, 10%);
dd {
margin-left: 1em;
}
& > div {
margin: 1em 3em 0;
&.flex-table,
&.flex-bloc {
margin: 1em 0 0;
}
&.alert.to-confirm {
margin: 1em 0 0;
padding: 1em 3em;
}
}
div.flex-table {
div.item-row {
div.item-col:first-child {
flex-basis: 33%;
} }
} padding: 0em 0em;
} margin: 1em 0;
border-radius: 5px;
border: 1px dotted tint-color($chill-accourse-context, 10%);
border-left: 1px dotted tint-color($chill-accourse-context, 10%);
border-right: 1px dotted tint-color($chill-accourse-context, 10%);
dd {
margin-left: 1em;
}
& > div {
margin: 1em 3em 0;
&.errors { &.flex-table,
//display: flex; &.flex-bloc {
//position: sticky; margin: 1em 0 0;
//bottom: 0.3em; }
//z-index: 1000; &.alert.to-confirm {
margin: 1em 0; margin: 1em 0 0;
padding: 1em; padding: 1em 3em;
border-radius: 0; }
}
div.flex-table {
div.item-row {
div.item-col:first-child {
flex-basis: 33%;
}
}
}
&.errors {
//display: flex;
//position: sticky;
//bottom: 0.3em;
//z-index: 1000;
margin: 1em 0;
padding: 1em;
border-radius: 0;
}
} }
}
} }
</style> </style>

View File

@@ -1,35 +1,38 @@
<template> <template>
<div class="vue-component"> <div class="vue-component">
<h2><a id="section-40" />{{ $t("admin_location.title") }}</h2> <h2><a id="section-40" />{{ $t("admin_location.title") }}</h2>
<div class="mb-4"> <div class="mb-4">
<label for="selectAdminLocation"> <label for="selectAdminLocation">
{{ $t("admin_location.title") }} {{ $t("admin_location.title") }}
</label> </label>
<VueMultiselect <VueMultiselect
name="selectAdminLocation" name="selectAdminLocation"
label="text" label="text"
:custom-label="customLabel" :custom-label="customLabel"
track-by="id" track-by="id"
:multiple="false" :multiple="false"
:searchable="true" :searchable="true"
:placeholder="$t('admin_location.placeholder')" :placeholder="$t('admin_location.placeholder')"
v-model="value" v-model="value"
:options="options" :options="options"
group-values="locations" group-values="locations"
group-label="locationCategories" group-label="locationCategories"
:select-label="$t('multiselect.select_label')" :select-label="$t('multiselect.select_label')"
:deselect-label="$t('multiselect.deselect_label')" :deselect-label="$t('multiselect.deselect_label')"
:selected-label="$t('multiselect.selected_label')" :selected-label="$t('multiselect.selected_label')"
@select="updateAdminLocation" @select="updateAdminLocation"
/> />
</div>
<div
v-if="!isAdminLocationValid"
class="alert alert-warning to-confirm"
>
{{ $t("admin_location.not_valid") }}
</div>
</div> </div>
<div v-if="!isAdminLocationValid" class="alert alert-warning to-confirm">
{{ $t("admin_location.not_valid") }}
</div>
</div>
</template> </template>
<script> <script>
@@ -38,67 +41,72 @@ import { fetchResults } from "ChillMainAssets/lib/api/apiMethods";
import { mapState, mapGetters } from "vuex"; import { mapState, mapGetters } from "vuex";
export default { export default {
name: "AdminLocation", name: "AdminLocation",
components: { VueMultiselect }, components: { VueMultiselect },
data() { data() {
return { return {
options: [], options: [],
}; };
},
computed: {
...mapState({
value: (state) => state.accompanyingCourse.administrativeLocation,
}),
...mapGetters(["isAdminLocationValid"]),
},
mounted() {
this.getOptions();
},
methods: {
getOptions() {
fetchResults(`/api/1.0/main/location.json`).then((response) => {
let uniqueLocationTypeId = [
...new Set(response.map((o) => o.locationType.id)),
];
let results = [];
for (let id of uniqueLocationTypeId) {
results.push({
locationCategories: response.filter(
(o) => o.locationType.id === id,
)[0].locationType.title.fr,
locations: response.filter((o) => o.locationType.id === id),
});
}
this.options = results;
});
}, },
updateAdminLocation(value) { computed: {
this.$store ...mapState({
.dispatch("updateAdminLocation", value) value: (state) => state.accompanyingCourse.administrativeLocation,
.catch(({ name, violations }) => { }),
if (name === "ValidationException" || name === "AccessException") { ...mapGetters(["isAdminLocationValid"]),
violations.forEach((violation) =>
this.$toast.open({ message: violation }),
);
} else {
this.$toast.open({ message: "An error occurred" });
}
});
}, },
customLabel(value) { mounted() {
return value.locationType this.getOptions();
? value.name },
? `${value.name} (${value.locationType.title.fr})` methods: {
: value.locationType.title.fr getOptions() {
: ""; fetchResults(`/api/1.0/main/location.json`).then((response) => {
let uniqueLocationTypeId = [
...new Set(response.map((o) => o.locationType.id)),
];
let results = [];
for (let id of uniqueLocationTypeId) {
results.push({
locationCategories: response.filter(
(o) => o.locationType.id === id,
)[0].locationType.title.fr,
locations: response.filter(
(o) => o.locationType.id === id,
),
});
}
this.options = results;
});
},
updateAdminLocation(value) {
this.$store
.dispatch("updateAdminLocation", value)
.catch(({ name, violations }) => {
if (
name === "ValidationException" ||
name === "AccessException"
) {
violations.forEach((violation) =>
this.$toast.open({ message: violation }),
);
} else {
this.$toast.open({ message: "An error occurred" });
}
});
},
customLabel(value) {
return value.locationType
? value.name
? `${value.name} (${value.locationType.title.fr})`
: value.locationType.title.fr
: "";
},
}, },
},
}; };
</script> </script>
<style src="vue-multiselect/dist/vue-multiselect.css"></style> <style src="vue-multiselect/dist/vue-multiselect.css"></style>
<style lang="css" scoped> <style lang="css" scoped>
label { label {
display: none; display: none;
} }
</style> </style>

View File

@@ -1,106 +1,132 @@
<template> <template>
<teleport to="#header-accompanying_course-name #banner-flags"> <teleport to="#header-accompanying_course-name #banner-flags">
<toggle-flags /> <toggle-flags />
</teleport> </teleport>
<teleport to="#header-accompanying_course-name #banner-status"> <teleport to="#header-accompanying_course-name #banner-status">
<span <span
v-if="accompanyingCourse.step === 'DRAFT'" v-if="accompanyingCourse.step === 'DRAFT'"
class="text-md-end d-md-block" class="text-md-end d-md-block"
>
<span class="badge bg-secondary">
{{ $t("course.step.draft") }}
</span>
</span>
<span
v-else-if="
accompanyingCourse.step === 'CONFIRMED' ||
accompanyingCourse.step === 'CONFIRMED_INACTIVE_SHORT' ||
accompanyingCourse.step === 'CONFIRMED_INACTIVE_LONG'
"
class="text-md-end"
>
<span
v-if="accompanyingCourse.step === 'CONFIRMED'"
class="d-md-block mb-md-3"
>
<span class="badge bg-primary">
{{ $t("course.step.active") }}
</span>
</span>
<span
v-else-if="
accompanyingCourse.step === 'CONFIRMED_INACTIVE_SHORT'
"
class="d-md-block mb-md-3"
>
<span class="badge bg-chill-yellow text-primary">
{{ $t("course.step.inactive_short") }}
</span>
</span>
<span
v-else-if="
accompanyingCourse.step === 'CONFIRMED_INACTIVE_LONG'
"
class="d-md-block mb-md-3"
>
<span class="badge bg-chill-pink">
{{ $t("course.step.inactive_long") }}
</span>
</span>
<span class="d-md-block">
<span class="d-md-block ms-3 ms-md-0">
<i
>{{ $t("course.open_at")
}}{{
$d(accompanyingCourse.openingDate.datetime, "text")
}}</i
>
</span>
<span
v-if="accompanyingCourse.user"
class="d-md-block ms-3 ms-md-0"
>
<span class="item-key">{{ $t("course.referrer") }}:</span
>&nbsp;
<b>{{ accompanyingCourse.user.text }}</b>
<template v-if="accompanyingCourse.user.isAbsent">
&nbsp;
<span
class="badge bg-danger rounded-pill"
title="Absent"
>A</span
>
</template>
</span>
</span>
</span>
<span v-else class="text-md-end d-md-block">
<span class="badge bg-danger">
{{ $t("course.step.closed") }}
</span>
<span class="d-md-block">
<span class="d-md-block ms-3 ms-md-0">
<i
>{{
$d(accompanyingCourse.openingDate.datetime, "text")
}}
-
{{
$d(accompanyingCourse.closingDate.datetime, "text")
}}</i
>
</span>
<span
v-if="accompanyingCourse.user"
class="d-md-block ms-3 ms-md-0"
>
<span class="item-key">{{ $t("course.referrer") }}:</span>
<b>{{ accompanyingCourse.user.text }}</b>
</span>
</span>
</span>
</teleport>
<teleport
to="#header-accompanying_course-name #persons-associated-shortlist"
> >
<span class="badge bg-secondary"> <persons-associated
{{ $t("course.step.draft") }} :accompanyingCourse="accompanyingCourse"
</span> :shortlist="true"
</span> ></persons-associated>
<span </teleport>
v-else-if="
accompanyingCourse.step === 'CONFIRMED' || <teleport to="#header-accompanying_course-details #banner-social-issues">
accompanyingCourse.step === 'CONFIRMED_INACTIVE_SHORT' || <social-issue
accompanyingCourse.step === 'CONFIRMED_INACTIVE_LONG' v-for="issue in accompanyingCourse.socialIssues"
" :key="issue.id"
class="text-md-end" :issue="issue"
/>
</teleport>
<teleport
to="#header-accompanying_course-details #banner-persons-associated"
> >
<span <persons-associated
v-if="accompanyingCourse.step === 'CONFIRMED'" :accompanying-course="accompanyingCourse"
class="d-md-block mb-md-3" :shortlist="false"
> />
<span class="badge bg-primary"> </teleport>
{{ $t("course.step.active") }}
</span>
</span>
<span
v-else-if="accompanyingCourse.step === 'CONFIRMED_INACTIVE_SHORT'"
class="d-md-block mb-md-3"
>
<span class="badge bg-chill-yellow text-primary">
{{ $t("course.step.inactive_short") }}
</span>
</span>
<span
v-else-if="accompanyingCourse.step === 'CONFIRMED_INACTIVE_LONG'"
class="d-md-block mb-md-3"
>
<span class="badge bg-chill-pink">
{{ $t("course.step.inactive_long") }}
</span>
</span>
<span class="d-md-block">
<span class="d-md-block ms-3 ms-md-0">
<i
>{{ $t("course.open_at")
}}{{ $d(accompanyingCourse.openingDate.datetime, "text") }}</i
>
</span>
<span v-if="accompanyingCourse.user" class="d-md-block ms-3 ms-md-0">
<span class="item-key">{{ $t("course.referrer") }}:</span>&nbsp;
<b>{{ accompanyingCourse.user.text }}</b>
<template v-if="accompanyingCourse.user.isAbsent">
&nbsp;
<span class="badge bg-danger rounded-pill" title="Absent">A</span>
</template>
</span>
</span>
</span>
<span v-else class="text-md-end d-md-block">
<span class="badge bg-danger">
{{ $t("course.step.closed") }}
</span>
<span class="d-md-block">
<span class="d-md-block ms-3 ms-md-0">
<i
>{{ $d(accompanyingCourse.openingDate.datetime, "text") }} -
{{ $d(accompanyingCourse.closingDate.datetime, "text") }}</i
>
</span>
<span v-if="accompanyingCourse.user" class="d-md-block ms-3 ms-md-0">
<span class="item-key">{{ $t("course.referrer") }}:</span>
<b>{{ accompanyingCourse.user.text }}</b>
</span>
</span>
</span>
</teleport>
<teleport to="#header-accompanying_course-name #persons-associated-shortlist">
<persons-associated
:accompanyingCourse="accompanyingCourse"
:shortlist="true"
></persons-associated>
</teleport>
<teleport to="#header-accompanying_course-details #banner-social-issues">
<social-issue
v-for="issue in accompanyingCourse.socialIssues"
:key="issue.id"
:issue="issue"
/>
</teleport>
<teleport to="#header-accompanying_course-details #banner-persons-associated">
<persons-associated
:accompanying-course="accompanyingCourse"
:shortlist="false"
/>
</teleport>
</template> </template>
<script> <script>
@@ -109,30 +135,30 @@ import SocialIssue from "./Banner/SocialIssue.vue";
import PersonsAssociated from "./Banner/PersonsAssociated.vue"; import PersonsAssociated from "./Banner/PersonsAssociated.vue";
export default { export default {
name: "Banner", name: "Banner",
components: { components: {
ToggleFlags, ToggleFlags,
SocialIssue, SocialIssue,
PersonsAssociated, PersonsAssociated,
}, },
computed: { computed: {
accompanyingCourse() { accompanyingCourse() {
return this.$store.state.accompanyingCourse; return this.$store.state.accompanyingCourse;
},
}, },
},
}; };
</script> </script>
<style lang="scss"> <style lang="scss">
div#banner-flags, div#banner-flags,
div#banner-status { div#banner-status {
.badge { .badge {
text-transform: uppercase; text-transform: uppercase;
} }
} }
div#banner-status { div#banner-status {
span.badge { span.badge {
font-size: 90%; font-size: 90%;
} }
} }
</style> </style>

View File

@@ -1,123 +1,129 @@
<template> <template>
<span v-if="shortlist"> <span v-if="shortlist">
<span v-for="person in firstPersons" class="me-1" :key="person.id"> <span v-for="person in firstPersons" class="me-1" :key="person.id">
<on-the-fly <on-the-fly
:type="person.type" :type="person.type"
:id="person.id" :id="person.id"
:buttonText="person.textAge" :buttonText="person.textAge"
:displayBadge="'true' === 'true'" :displayBadge="'true' === 'true'"
action="show" action="show"
></on-the-fly> ></on-the-fly>
</span>
<span v-if="hasMoreThanShortListPerson">
<a
class="showMore carousel-control"
role="button"
data-bs-target="#ACHeaderSlider"
data-bs-slide="next"
>{{ $t("more_x", { x: countMoreThanShortListPerson }) }}</a
>
</span>
</span> </span>
<span v-if="hasMoreThanShortListPerson"> <span v-else>
<a <span
class="showMore carousel-control" v-for="([pk, persons], h) in personsByHousehold"
role="button" :class="{ household: pk > -1, 'no-household': pk === -1 }"
data-bs-target="#ACHeaderSlider" :key="h.id"
data-bs-slide="next" >
>{{ $t("more_x", { x: countMoreThanShortListPerson }) }}</a <a v-if="pk !== -1" :href="householdLink(pk)">
> <i
class="fa fa-home fa-fw text-light"
:title="
$t('persons_associated.show_household_number', {
id: pk,
})
"
></i>
</a>
<span v-for="person in persons" class="me-1" :key="person.id">
<on-the-fly
:type="person.type"
:id="person.id"
:buttonText="person.textAge"
:displayBadge="true"
action="show"
></on-the-fly>
</span>
</span>
</span> </span>
</span>
<span v-else>
<span
v-for="([pk, persons], h) in personsByHousehold"
:class="{ household: pk > -1, 'no-household': pk === -1 }"
:key="h.id"
>
<a v-if="pk !== -1" :href="householdLink(pk)">
<i
class="fa fa-home fa-fw text-light"
:title="$t('persons_associated.show_household_number', { id: pk })"
></i>
</a>
<span v-for="person in persons" class="me-1" :key="person.id">
<on-the-fly
:type="person.type"
:id="person.id"
:buttonText="person.textAge"
:displayBadge="true"
action="show"
></on-the-fly>
</span>
</span>
</span>
</template> </template>
<script> <script>
import OnTheFly from "ChillMainAssets/vuejs/OnTheFly/components/OnTheFly"; import OnTheFly from "ChillMainAssets/vuejs/OnTheFly/components/OnTheFly";
export default { export default {
name: "PersonsAssociated", name: "PersonsAssociated",
components: { components: {
OnTheFly, OnTheFly,
},
props: ["accompanyingCourse", "shortlist"],
data() {
return {
showAllPersons: false,
maxTotalPersons: 2,
nbShortList: 3,
};
},
computed: {
participations() {
return this.accompanyingCourse.participations.filter(
(p) => p.endDate === null,
);
}, },
persons() { props: ["accompanyingCourse", "shortlist"],
return this.participations.map((p) => p.person); data() {
return {
showAllPersons: false,
maxTotalPersons: 2,
nbShortList: 3,
};
}, },
firstPersons() { computed: {
return this.participations.slice(0, 3).map((p) => p.person); participations() {
}, return this.accompanyingCourse.participations.filter(
hasMoreThanShortListPerson() { (p) => p.endDate === null,
return this.participations.length > 3; );
}, },
countMoreThanShortListPerson() { persons() {
return this.participations.length - 3; return this.participations.map((p) => p.person);
}, },
resources() { firstPersons() {
return this.accompanyingCourse.resources; return this.participations.slice(0, 3).map((p) => p.person);
}, },
requestor() { hasMoreThanShortListPerson() {
return this.accompanyingCourse.requestor; return this.participations.length > 3;
}, },
personsByHousehold() { countMoreThanShortListPerson() {
const households = new Map(); return this.participations.length - 3;
this.accompanyingCourse.participations },
.filter((part) => part.endDate === null) resources() {
.map((part) => part.person) return this.accompanyingCourse.resources;
.forEach((person) => { },
if (!households.has(person.current_household_id || -1)) { requestor() {
households.set(person.current_household_id || -1, []); return this.accompanyingCourse.requestor;
} },
households.get(person.current_household_id || -1).push(person); personsByHousehold() {
}); const households = new Map();
this.accompanyingCourse.participations
.filter((part) => part.endDate === null)
.map((part) => part.person)
.forEach((person) => {
if (!households.has(person.current_household_id || -1)) {
households.set(person.current_household_id || -1, []);
}
households
.get(person.current_household_id || -1)
.push(person);
});
return households; return households;
},
}, },
}, methods: {
methods: { householdLink(id) {
householdLink(id) { return `/fr/person/household/${id}/summary`;
return `/fr/person/household/${id}/summary`; },
}, },
},
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.showMore { .showMore {
cursor: pointer; cursor: pointer;
color: white; color: white;
} }
span.household { span.household {
display: inline-block; display: inline-block;
border-top: 1px solid rgba(255, 255, 255, 0.3); border-top: 1px solid rgba(255, 255, 255, 0.3);
background-color: rgba(255, 255, 255, 0.1); background-color: rgba(255, 255, 255, 0.1);
border-radius: 10px; border-radius: 10px;
margin-right: 0.3em; margin-right: 0.3em;
padding: 5px; padding: 5px;
} }
</style> </style>

View File

@@ -1,11 +1,11 @@
<template> <template>
<span class="badge bg-chill-l-gray text-dark">{{ issue.text }}</span> <span class="badge bg-chill-l-gray text-dark">{{ issue.text }}</span>
</template> </template>
<script> <script>
export default { export default {
name: "SocialIssues", name: "SocialIssues",
props: ["issue"], props: ["issue"],
}; };
</script> </script>
@@ -14,9 +14,9 @@ export default {
@import "ChillPersonAssets/chill/scss/mixins"; @import "ChillPersonAssets/chill/scss/mixins";
@import "ChillMainAssets/chill/scss/chill_variables"; @import "ChillMainAssets/chill/scss/chill_variables";
span.badge { span.badge {
@include badge_social($social-issue-color); @include badge_social($social-issue-color);
font-size: 95%; font-size: 95%;
margin-bottom: 5px; margin-bottom: 5px;
margin-right: 1em; margin-right: 1em;
} }
</style> </style>

View File

@@ -1,142 +1,158 @@
<template> <template>
<div class="text-md-end"> <div class="text-md-end">
<span class="d-block d-sm-inline-block mb-md-2"> <span class="d-block d-sm-inline-block mb-md-2">
<a @click="toggleIntensity" class="flag-toggle"> <a @click="toggleIntensity" class="flag-toggle">
<span :class="{ on: !isRegular }">{{ $t("course.occasional") }}</span> <span :class="{ on: !isRegular }">{{
<i $t("course.occasional")
class="fa" }}</span>
:class="{ <i
'fa-toggle-on': isRegular, class="fa"
'fa-toggle-on fa-flip-horizontal': !isRegular, :class="{
}" 'fa-toggle-on': isRegular,
/> 'fa-toggle-on fa-flip-horizontal': !isRegular,
<span :class="{ on: isRegular }">{{ $t("course.regular") }}</span> }"
</a> />
</span> <span :class="{ on: isRegular }">{{
$t("course.regular")
}}</span>
</a>
</span>
<span class="d-block d-sm-inline-block ms-sm-3 ms-md-0"> <span class="d-block d-sm-inline-block ms-sm-3 ms-md-0">
<button <button
class="badge rounded-pill me-1" class="badge rounded-pill me-1"
:class="{ 'bg-danger': isEmergency, 'bg-secondary': !isEmergency }" :class="{
@click="toggleEmergency" 'bg-danger': isEmergency,
> 'bg-secondary': !isEmergency,
{{ $t("course.emergency") }} }"
</button> @click="toggleEmergency"
<button >
class="badge rounded-pill" {{ $t("course.emergency") }}
:class="{ </button>
'bg-danger': isConfidential, <button
'bg-secondary': !isConfidential, class="badge rounded-pill"
}" :class="{
@click="toggleConfidential" 'bg-danger': isConfidential,
> 'bg-secondary': !isConfidential,
{{ $t("course.confidential") }} }"
</button> @click="toggleConfidential"
</span> >
</div> {{ $t("course.confidential") }}
</button>
</span>
</div>
</template> </template>
<script> <script>
import { mapState } from "vuex"; import { mapState } from "vuex";
export default { export default {
name: "ToggleFlags", name: "ToggleFlags",
computed: { computed: {
...mapState({ ...mapState({
intensity: (state) => state.accompanyingCourse.intensity, intensity: (state) => state.accompanyingCourse.intensity,
emergency: (state) => state.accompanyingCourse.emergency, emergency: (state) => state.accompanyingCourse.emergency,
confidential: (state) => state.accompanyingCourse.confidential, confidential: (state) => state.accompanyingCourse.confidential,
permissions: (state) => state.permissions, permissions: (state) => state.permissions,
}), }),
isRegular() { isRegular() {
return this.intensity === "regular" ? true : false; return this.intensity === "regular" ? true : false;
},
isEmergency() {
return this.emergency ? true : false;
},
isConfidential() {
return this.confidential ? true : false;
},
}, },
isEmergency() { methods: {
return this.emergency ? true : false; toggleIntensity() {
let value;
switch (this.intensity) {
case "occasional":
value = "regular";
break;
case "regular":
value = "occasional";
break;
default:
//temporaire (modif backend)
value = "occasional";
}
this.$store.dispatch("toggleIntensity", value).catch(({ name }) => {
if (
name === "ValidationException" ||
name === "AccessException"
) {
this.$toast.open({
message: this.$t(
"Only the referrer can toggle the intensity of an accompanying course",
),
});
} else {
this.$toast.open({ message: "An error occurred" });
}
});
},
toggleEmergency() {
this.$store
.dispatch("toggleEmergency", !this.isEmergency)
.catch(({ name, violations }) => {
if (
name === "ValidationException" ||
name === "AccessException"
) {
violations.forEach((violation) =>
this.$toast.open({ message: violation }),
);
} else {
this.$toast.open({ message: "An error occurred" });
}
});
},
toggleConfidential() {
this.$store.dispatch("toggleConfidential").catch(({ name }) => {
console.log(name);
if (
name === "ValidationException" ||
name === "AccessException"
) {
this.$toast.open({
message: this.$t(
"Only the referrer can toggle the confidentiality of an accompanying course",
),
});
} else {
this.$toast.open({ message: "An error occurred" });
}
});
},
}, },
isConfidential() {
return this.confidential ? true : false;
},
},
methods: {
toggleIntensity() {
let value;
switch (this.intensity) {
case "occasional":
value = "regular";
break;
case "regular":
value = "occasional";
break;
default:
//temporaire (modif backend)
value = "occasional";
}
this.$store.dispatch("toggleIntensity", value).catch(({ name }) => {
if (name === "ValidationException" || name === "AccessException") {
this.$toast.open({
message: this.$t(
"Only the referrer can toggle the intensity of an accompanying course",
),
});
} else {
this.$toast.open({ message: "An error occurred" });
}
});
},
toggleEmergency() {
this.$store
.dispatch("toggleEmergency", !this.isEmergency)
.catch(({ name, violations }) => {
if (name === "ValidationException" || name === "AccessException") {
violations.forEach((violation) =>
this.$toast.open({ message: violation }),
);
} else {
this.$toast.open({ message: "An error occurred" });
}
});
},
toggleConfidential() {
this.$store.dispatch("toggleConfidential").catch(({ name }) => {
console.log(name);
if (name === "ValidationException" || name === "AccessException") {
this.$toast.open({
message: this.$t(
"Only the referrer can toggle the confidentiality of an accompanying course",
),
});
} else {
this.$toast.open({ message: "An error occurred" });
}
});
},
},
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
a.flag-toggle { a.flag-toggle {
color: white;
cursor: pointer;
&:hover {
color: white; color: white;
text-decoration: underline; cursor: pointer;
border-radius: 20px; &:hover {
} color: white;
i { text-decoration: underline;
margin: auto 0.4em; border-radius: 20px;
} }
span.on { i {
font-weight: bolder; margin: auto 0.4em;
} }
span.on {
font-weight: bolder;
}
} }
button.badge { button.badge {
&.bg-secondary { &.bg-secondary {
opacity: 0.5; opacity: 0.5;
&:hover { &:hover {
opacity: 0.7; opacity: 0.7;
}
} }
}
} }
</style> </style>

View File

@@ -1,36 +1,38 @@
<template> <template>
<li> <li>
<button <button
class="btn btn-sm btn-secondary" class="btn btn-sm btn-secondary"
@click="modal.showModal = true" @click="modal.showModal = true"
:title="$t('courselocation.assign_course_address')" :title="$t('courselocation.assign_course_address')"
> >
<i class="fa fa-map-marker" /> <i class="fa fa-map-marker" />
</button>
</li>
<teleport to="body">
<modal
v-if="modal.showModal"
:modal-dialog-class="modal.modalDialogClass"
@close="modal.showModal = false"
>
<template #header>
<h2 class="modal-title">
{{ $t("courselocation.sure") }}
</h2>
</template>
<template #body>
<address-render-box :address="person.current_household_address" />
<p>{{ $t("courselocation.sure_description") }}</p>
</template>
<template #footer>
<button class="btn btn-danger" @click="assignAddress">
{{ $t("courselocation.ok") }}
</button> </button>
</template> </li>
</modal>
</teleport> <teleport to="body">
<modal
v-if="modal.showModal"
:modal-dialog-class="modal.modalDialogClass"
@close="modal.showModal = false"
>
<template #header>
<h2 class="modal-title">
{{ $t("courselocation.sure") }}
</h2>
</template>
<template #body>
<address-render-box
:address="person.current_household_address"
/>
<p>{{ $t("courselocation.sure_description") }}</p>
</template>
<template #footer>
<button class="btn btn-danger" @click="assignAddress">
{{ $t("courselocation.ok") }}
</button>
</template>
</modal>
</teleport>
</template> </template>
<script> <script>
@@ -39,49 +41,52 @@ import Modal from "ChillMainAssets/vuejs/_components/Modal";
import AddressRenderBox from "ChillMainAssets/vuejs/_components/Entity/AddressRenderBox.vue"; import AddressRenderBox from "ChillMainAssets/vuejs/_components/Entity/AddressRenderBox.vue";
export default { export default {
name: "ButtonLocation", name: "ButtonLocation",
components: { components: {
AddressRenderBox, AddressRenderBox,
Modal, Modal,
}, },
props: ["person"], props: ["person"],
data() { data() {
return { return {
modal: { modal: {
showModal: false, showModal: false,
modalDialogClass: "modal-dialog-centered modal-md", modalDialogClass: "modal-dialog-centered modal-md",
}, },
}; };
}, },
computed: { computed: {
...mapState({ ...mapState({
context: (state) => state.addressContext, context: (state) => state.addressContext,
}), }),
}, },
methods: { methods: {
assignAddress() { assignAddress() {
//console.log('assignAddress id', this.person.current_household_address); //console.log('assignAddress id', this.person.current_household_address);
let payload = { let payload = {
target: this.context.target.name, target: this.context.target.name,
targetId: this.context.target.id, targetId: this.context.target.id,
locationStatusTo: "person", locationStatusTo: "person",
personId: this.person.id, personId: this.person.id,
}; };
this.$store this.$store
.dispatch("updateLocation", payload) .dispatch("updateLocation", payload)
.catch(({ name, violations }) => { .catch(({ name, violations }) => {
if (name === "ValidationException" || name === "AccessException") { if (
violations.forEach((violation) => name === "ValidationException" ||
this.$toast.open({ message: violation }), name === "AccessException"
); ) {
} else { violations.forEach((violation) =>
this.$toast.open({ message: "An error occurred" }); this.$toast.open({ message: violation }),
} );
}); } else {
this.$toast.open({ message: "An error occurred" });
window.location.assign("#section-20"); }
this.modal.showModal = false; });
window.location.assign("#section-20");
this.modal.showModal = false;
},
}, },
},
}; };
</script> </script>

View File

@@ -1,56 +1,56 @@
<template> <template>
<div class="vue-component"> <div class="vue-component">
<h2><a id="section-100" />{{ $t("comment.title") }}</h2> <h2><a id="section-100" />{{ $t("comment.title") }}</h2>
<!--div class="error flash_message" v-if="errors.length > 0"> <!--div class="error flash_message" v-if="errors.length > 0">
{{ errors[0] }} {{ errors[0] }}
TODO fix errors flashbag for app component TODO fix errors flashbag for app component
</div--> </div-->
<div>
<form @submit.prevent="submitform">
<label class="col-form-label" for="content">{{
$t("comment.label")
}}</label>
<comment-editor v-model="content" />
<div class="sub-comment">
<div
v-if="
pinnedComment !== null &&
typeof pinnedComment.creator !== 'undefined'
"
class="metadata"
>
{{
$t("comment.created_by", [
pinnedComment.creator.text,
$d(pinnedComment.updatedAt.datetime, "long"),
])
}}
</div>
<div class="loading">
<i
v-if="loading"
class="fa fa-circle-o-notch fa-spin"
:title="$t('loading')"
/>
</div>
</div>
<div> <div>
<ul class="record_actions"> <form @submit.prevent="submitform">
<li v-if="pinnedComment !== null"> <label class="col-form-label" for="content">{{
<a class="btn btn-delete" @click="removeComment"> $t("comment.label")
{{ $t("action.delete") }} }}</label>
</a>
</li> <comment-editor v-model="content" />
</ul>
<div class="sub-comment">
<div
v-if="
pinnedComment !== null &&
typeof pinnedComment.creator !== 'undefined'
"
class="metadata"
>
{{
$t("comment.created_by", [
pinnedComment.creator.text,
$d(pinnedComment.updatedAt.datetime, "long"),
])
}}
</div>
<div class="loading">
<i
v-if="loading"
class="fa fa-circle-o-notch fa-spin"
:title="$t('loading')"
/>
</div>
</div>
<div>
<ul class="record_actions">
<li v-if="pinnedComment !== null">
<a class="btn btn-delete" @click="removeComment">
{{ $t("action.delete") }}
</a>
</li>
</ul>
</div>
</form>
</div> </div>
</form>
</div> </div>
</div>
</template> </template>
<script> <script>
@@ -58,113 +58,127 @@ import CommentEditor from "ChillMainAssets/vuejs/_components/CommentEditor/Comme
import { mapState } from "vuex"; import { mapState } from "vuex";
export default { export default {
name: "Comment", name: "Comment",
components: { components: {
CommentEditor, CommentEditor,
}, },
data() { data() {
return { return {
loading: false, loading: false,
lastRecordedContent: null, lastRecordedContent: null,
}; };
}, },
computed: { computed: {
...mapState({ ...mapState({
pinnedComment: (state) => state.accompanyingCourse.pinnedComment, pinnedComment: (state) => state.accompanyingCourse.pinnedComment,
}), }),
classicEditor: () => ClassicEditor, classicEditor: () => ClassicEditor,
editorConfig: () => classicEditorConfig, editorConfig: () => classicEditorConfig,
content: { content: {
set(value) { set(value) {
console.log("new comment value", value); console.log("new comment value", value);
console.log("previous value", this.lastRecordedContent); console.log("previous value", this.lastRecordedContent);
this.lastRecordedContent = value; this.lastRecordedContent = value;
setTimeout(() => { setTimeout(() => {
console.log("performing test on ", value); console.log("performing test on ", value);
if (this.lastRecordedContent === value) { if (this.lastRecordedContent === value) {
this.loading = true; this.loading = true;
if (value !== "") { if (value !== "") {
this.$store this.$store
.dispatch("updatePinnedComment", value) .dispatch("updatePinnedComment", value)
.then(() => { .then(() => {
this.loading = false; this.loading = false;
}) })
.catch(({ name, violations }) => { .catch(({ name, violations }) => {
if ( if (
name === "ValidationException" || name === "ValidationException" ||
name === "AccessException" name === "AccessException"
) { ) {
violations.forEach((violation) => violations.forEach((violation) =>
this.$toast.open({ message: violation }), this.$toast.open({
); message: violation,
} else { }),
this.$toast.open({ message: "An error occurred" }); );
} } else {
}); this.$toast.open({
} else { message: "An error occurred",
if (this.$store.state.accompanyingCourse.pinnedComment !== null) { });
this.$store }
.dispatch("removePinnedComment", { });
id: this.pinnedComment.id, } else {
}) if (
.then(() => { this.$store.state.accompanyingCourse
this.loading = false; .pinnedComment !== null
this.lastRecoredContent = null; ) {
}) this.$store
.catch(({ name, violations }) => { .dispatch("removePinnedComment", {
if ( id: this.pinnedComment.id,
name === "ValidationException" || })
name === "AccessException" .then(() => {
) { this.loading = false;
violations.forEach((violation) => this.lastRecoredContent = null;
this.$toast.open({ message: violation }), })
); .catch(({ name, violations }) => {
} else { if (
this.$toast.open({ message: "An error occurred" }); name === "ValidationException" ||
name === "AccessException"
) {
violations.forEach((violation) =>
this.$toast.open({
message: violation,
}),
);
} else {
this.$toast.open({
message: "An error occurred",
});
}
});
}
}
} }
}); }, 3000);
} },
} get() {
} return this.pinnedComment ? this.pinnedComment.content : "";
}, 3000); },
}, },
get() { errors() {
return this.pinnedComment ? this.pinnedComment.content : ""; return this.$store.state.errorMsg;
}, },
}, },
errors() { methods: {
return this.$store.state.errorMsg; removeComment() {
this.$store
.dispatch("removePinnedComment", { id: this.pinnedComment.id })
.catch(({ name, violations }) => {
if (
name === "ValidationException" ||
name === "AccessException"
) {
violations.forEach((violation) =>
this.$toast.open({ message: violation }),
);
} else {
this.$toast.open({ message: "An error occurred" });
}
});
},
}, },
},
methods: {
removeComment() {
this.$store
.dispatch("removePinnedComment", { id: this.pinnedComment.id })
.catch(({ name, violations }) => {
if (name === "ValidationException" || name === "AccessException") {
violations.forEach((violation) =>
this.$toast.open({ message: violation }),
);
} else {
this.$toast.open({ message: "An error occurred" });
}
});
},
},
}; };
</script> </script>
<style lang="scss"> <style lang="scss">
div.ck-editor.ck-reset { div.ck-editor.ck-reset {
margin: 0.6em 0; margin: 0.6em 0;
} }
div.sub-comment { div.sub-comment {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
div.loading { div.loading {
margin-right: 6px; margin-right: 6px;
margin-left: 6px; margin-left: 6px;
} }
} }
</style> </style>

View File

@@ -1,120 +1,127 @@
<template> <template>
<div class="vue-component"> <div class="vue-component">
<h2> <h2>
<a id="section-110" /> <a id="section-110" />
{{ $t("confirm.title") }} {{ $t("confirm.title") }}
</h2> </h2>
<div> <div>
<p v-html="$t('confirm.text_draft', [$t('course.step.draft')])" /> <p v-html="$t('confirm.text_draft', [$t('course.step.draft')])" />
<div v-if="!isValidToBeConfirmed"> <div v-if="!isValidToBeConfirmed">
<div class="alert alert-warning"> <div class="alert alert-warning">
{{ $t("confirm.alert_validation") }} {{ $t("confirm.alert_validation") }}
<ul class="mt-2"> <ul class="mt-2">
<li v-for="k in validationKeys" :key="k"> <li v-for="k in validationKeys" :key="k">
{{ $t(notValidMessages[k].msg) }} {{ $t(notValidMessages[k].msg) }}
<a :href="notValidMessages[k].anchor"> <a :href="notValidMessages[k].anchor">
<i class="fa fa-level-up fa-fw" /> <i class="fa fa-level-up fa-fw" />
</a> </a>
</li> </li>
</ul> </ul>
</div>
<ul class="record_actions">
<li>
<button class="btn btn-save" disabled>
{{ $t("confirm.ok") }}
</button>
</li>
<li>
<a class="btn btn-delete" :href="deleteLink">
{{ $t("confirm.delete") }}
</a>
</li>
</ul>
</div>
<div v-else>
<p
v-html="
$t('confirm.text_active', [$t('course.step.active')])
"
/>
<ul class="record_actions">
<li>
<button
class="btn btn-save"
@click="modal.showModal = true"
>
{{ $t("confirm.ok") }}
</button>
</li>
<li>
<a class="btn btn-delete" :href="deleteLink">
{{ $t("confirm.delete") }}
</a>
</li>
</ul>
</div>
</div> </div>
<ul class="record_actions">
<li>
<button class="btn btn-save" disabled>
{{ $t("confirm.ok") }}
</button>
</li>
<li>
<a class="btn btn-delete" :href="deleteLink">
{{ $t("confirm.delete") }}
</a>
</li>
</ul>
</div>
<div v-else> <teleport to="body">
<p v-html="$t('confirm.text_active', [$t('course.step.active')])" /> <modal
<ul class="record_actions"> v-if="modal.showModal"
<li> :modal-dialog-class="modal.modalDialogClass"
<button class="btn btn-save" @click="modal.showModal = true"> @close="modal.showModal = false"
{{ $t("confirm.ok") }}
</button>
</li>
<li>
<a class="btn btn-delete" :href="deleteLink">
{{ $t("confirm.delete") }}
</a>
</li>
</ul>
</div>
</div>
<teleport to="body">
<modal
v-if="modal.showModal"
:modal-dialog-class="modal.modalDialogClass"
@close="modal.showModal = false"
>
<template #header>
<h2 class="modal-title">
{{ $t("confirm.sure") }}
</h2>
</template>
<template #body>
<p>{{ $t("confirm.sure_description") }}</p>
<div v-if="accompanyingCourse.user === null">
<div v-if="usersSuggestedFilteredByJob.length === 0">
<p class="alert alert-warning">
{{ $t("confirm.no_suggested_referrer") }}
</p>
</div>
<div
v-if="usersSuggestedFilteredByJob.length === 1"
class="alert alert-info"
> >
<p>{{ $t("confirm.one_suggested_referrer") }}:</p> <template #header>
<ul class="list-suggest add-items inline"> <h2 class="modal-title">
<li> {{ $t("confirm.sure") }}
<user-render-box-badge </h2>
:user="usersSuggestedFilteredByJob[0]" </template>
/> <template #body>
</li> <p>{{ $t("confirm.sure_description") }}</p>
</ul> <div v-if="accompanyingCourse.user === null">
<p>{{ $t("confirm.choose_suggested_referrer") }}</p> <div v-if="usersSuggestedFilteredByJob.length === 0">
<ul class="record_actions"> <p class="alert alert-warning">
<li> {{ $t("confirm.no_suggested_referrer") }}
<button </p>
class="btn btn-save mr-5" </div>
@click="chooseSuggestedReferrer" <div
> v-if="usersSuggestedFilteredByJob.length === 1"
{{ $t("confirm.choose_button") }} class="alert alert-info"
</button> >
</li> <p>{{ $t("confirm.one_suggested_referrer") }}:</p>
<li> <ul class="list-suggest add-items inline">
<button <li>
class="btn btn-secondary" <user-render-box-badge
@click="doNotChooseSuggestedReferrer" :user="usersSuggestedFilteredByJob[0]"
> />
{{ $t("confirm.do_not_choose_button") }} </li>
</button> </ul>
</li> <p>{{ $t("confirm.choose_suggested_referrer") }}</p>
</ul> <ul class="record_actions">
</div> <li>
</div> <button
</template> class="btn btn-save mr-5"
<template #footer> @click="chooseSuggestedReferrer"
<button >
class="btn btn-danger" {{ $t("confirm.choose_button") }}
:disabled="disableConfirm" </button>
@click="confirmCourse" </li>
> <li>
{{ $t("confirm.ok") }} <button
</button> class="btn btn-secondary"
</template> @click="doNotChooseSuggestedReferrer"
</modal> >
</teleport> {{ $t("confirm.do_not_choose_button") }}
</div> </button>
</li>
</ul>
</div>
</div>
</template>
<template #footer>
<button
class="btn btn-danger"
:disabled="disableConfirm"
@click="confirmCourse"
>
{{ $t("confirm.ok") }}
</button>
</template>
</modal>
</teleport>
</div>
</template> </template>
<script> <script>
@@ -123,106 +130,112 @@ import Modal from "ChillMainAssets/vuejs/_components/Modal";
import UserRenderBoxBadge from "ChillMainAssets/vuejs/_components/Entity/UserRenderBoxBadge"; import UserRenderBoxBadge from "ChillMainAssets/vuejs/_components/Entity/UserRenderBoxBadge";
export default { export default {
name: "Confirm", name: "Confirm",
components: { components: {
Modal, Modal,
UserRenderBoxBadge, UserRenderBoxBadge,
},
data() {
return {
modal: {
showModal: false,
modalDialogClass: "modal-dialog-centered modal-md",
},
notValidMessages: {
participation: {
msg: "confirm.participation_not_valid",
anchor: "#section-10",
},
location: {
msg: "confirm.location_not_valid",
anchor: "#section-20",
},
origin: {
msg: "confirm.origin_not_valid",
anchor: "#section-30",
},
adminLocation: {
msg: "confirm.adminLocation_not_valid",
anchor: "#section-40",
},
socialIssue: {
msg: "confirm.socialIssue_not_valid",
anchor: "#section-60",
},
scopes: {
msg: "confirm.set_a_scope",
anchor: "#section-70",
},
job: {
msg: "confirm.job_not_valid",
anchor: "#section-80",
},
},
clickedDoNotChooseReferrer: false,
};
},
computed: {
...mapState({
accompanyingCourse: (state) => state.accompanyingCourse,
}),
...mapGetters([
"isParticipationValid",
"isSocialIssueValid",
"isOriginValid",
"isAdminLocationValid",
"isLocationValid",
"isJobValid",
"validationKeys",
"isValidToBeConfirmed",
"usersSuggestedFilteredByJob",
]),
deleteLink() {
return `/fr/parcours/${this.accompanyingCourse.id}/delete`; //TODO locale
}, },
disableConfirm() { data() {
return ( return {
this.accompanyingCourse.user === null && modal: {
this.usersSuggestedFilteredByJob.length === 1 && showModal: false,
this.clickedDoNotChooseReferrer === false modalDialogClass: "modal-dialog-centered modal-md",
); },
notValidMessages: {
participation: {
msg: "confirm.participation_not_valid",
anchor: "#section-10",
},
location: {
msg: "confirm.location_not_valid",
anchor: "#section-20",
},
origin: {
msg: "confirm.origin_not_valid",
anchor: "#section-30",
},
adminLocation: {
msg: "confirm.adminLocation_not_valid",
anchor: "#section-40",
},
socialIssue: {
msg: "confirm.socialIssue_not_valid",
anchor: "#section-60",
},
scopes: {
msg: "confirm.set_a_scope",
anchor: "#section-70",
},
job: {
msg: "confirm.job_not_valid",
anchor: "#section-80",
},
},
clickedDoNotChooseReferrer: false,
};
}, },
}, computed: {
methods: { ...mapState({
confirmCourse() { accompanyingCourse: (state) => state.accompanyingCourse,
this.$store }),
.dispatch("confirmAccompanyingCourse") ...mapGetters([
.catch(({ name, violations }) => { "isParticipationValid",
if (name === "ValidationException" || name === "AccessException") { "isSocialIssueValid",
violations.forEach((violation) => "isOriginValid",
this.$toast.open({ message: violation }), "isAdminLocationValid",
"isLocationValid",
"isJobValid",
"validationKeys",
"isValidToBeConfirmed",
"usersSuggestedFilteredByJob",
]),
deleteLink() {
return `/fr/parcours/${this.accompanyingCourse.id}/delete`; //TODO locale
},
disableConfirm() {
return (
this.accompanyingCourse.user === null &&
this.usersSuggestedFilteredByJob.length === 1 &&
this.clickedDoNotChooseReferrer === false
); );
} else { },
this.$toast.open({ message: "An error occurred" });
}
});
}, },
chooseSuggestedReferrer() { methods: {
this.$store confirmCourse() {
.dispatch("updateReferrer", this.usersSuggestedFilteredByJob[0]) this.$store
.catch(({ name, violations }) => { .dispatch("confirmAccompanyingCourse")
if (name === "ValidationException" || name === "AccessException") { .catch(({ name, violations }) => {
violations.forEach((violation) => if (
this.$toast.open({ message: violation }), name === "ValidationException" ||
); name === "AccessException"
} else { ) {
this.$toast.open({ message: "An error occurred" }); violations.forEach((violation) =>
} this.$toast.open({ message: violation }),
}); );
} else {
this.$toast.open({ message: "An error occurred" });
}
});
},
chooseSuggestedReferrer() {
this.$store
.dispatch("updateReferrer", this.usersSuggestedFilteredByJob[0])
.catch(({ name, violations }) => {
if (
name === "ValidationException" ||
name === "AccessException"
) {
violations.forEach((violation) =>
this.$toast.open({ message: violation }),
);
} else {
this.$toast.open({ message: "An error occurred" });
}
});
},
doNotChooseSuggestedReferrer() {
this.clickedDoNotChooseReferrer = true;
},
}, },
doNotChooseSuggestedReferrer() {
this.clickedDoNotChooseReferrer = true;
},
},
}; };
</script> </script>

View File

@@ -1,85 +1,95 @@
<template> <template>
<div class="vue-component"> <div class="vue-component">
<h2> <h2>
<a id="section-20" /> <a id="section-20" />
{{ $t("courselocation.title") }} {{ $t("courselocation.title") }}
</h2> </h2>
<div <div
v-for="error in displayErrors" v-for="error in displayErrors"
class="alert alert-danger my-2" class="alert alert-danger my-2"
:key="error" :key="error"
> >
{{ error }} {{ error }}
</div>
<div v-if="hasNoLocation">
<label class="chill-no-data-statement">
{{ $t("courselocation.no_address") }}
</label>
</div>
<div class="flex-table" v-if="accompanyingCourse.location">
<div class="item-bloc">
<address-render-box :address="accompanyingCourse.location" />
<div v-if="isPersonLocation" class="alert alert-secondary separator">
<label class="col-form-label">
{{
$t("courselocation.person_locator", [
accompanyingCourse.personLocation.text,
])
}}
</label>
</div> </div>
<div v-if="isTemporaryAddress" class="alert alert-warning separator"> <div v-if="hasNoLocation">
<p> <label class="chill-no-data-statement">
{{ $t("courselocation.temporary_address_must_be_changed") }} {{ $t("courselocation.no_address") }}
<i class="fa fa-fw fa-map-marker" /> </label>
</p>
</div> </div>
</div>
</div>
<div <div class="flex-table" v-if="accompanyingCourse.location">
v-if="hasNoPersonLocation" <div class="item-bloc">
class="alert alert-danger no-person-location" <address-render-box :address="accompanyingCourse.location" />
>
<i class="fa fa-warning fa-2x" />
<div>
<p>
{{
$t(
"courselocation.associate_at_least_one_person_with_one_household_with_address",
)
}}
<a href="#section-10">
<i class="fa fa-level-up fa-fw" />
</a>
</p>
</div>
</div>
<div> <div
<ul class="record_actions"> v-if="isPersonLocation"
<li> class="alert alert-secondary separator"
<add-address >
v-if="!isPersonLocation" <label class="col-form-label">
:key="key" {{
:context="context" $t("courselocation.person_locator", [
:options="options" accompanyingCourse.personLocation.text,
:address-changed-callback="submitTemporaryAddress" ])
ref="addAddress" }}
/> </label>
</li> </div>
</ul>
</div>
<div v-if="!isLocationValid" class="alert alert-warning to-confirm"> <div
{{ $t("courselocation.not_valid") }} v-if="isTemporaryAddress"
class="alert alert-warning separator"
>
<p>
{{
$t(
"courselocation.temporary_address_must_be_changed",
)
}}
<i class="fa fa-fw fa-map-marker" />
</p>
</div>
</div>
</div>
<div
v-if="hasNoPersonLocation"
class="alert alert-danger no-person-location"
>
<i class="fa fa-warning fa-2x" />
<div>
<p>
{{
$t(
"courselocation.associate_at_least_one_person_with_one_household_with_address",
)
}}
<a href="#section-10">
<i class="fa fa-level-up fa-fw" />
</a>
</p>
</div>
</div>
<div>
<ul class="record_actions">
<li>
<add-address
v-if="!isPersonLocation"
:key="key"
:context="context"
:options="options"
:address-changed-callback="submitTemporaryAddress"
ref="addAddress"
/>
</li>
</ul>
</div>
<div v-if="!isLocationValid" class="alert alert-warning to-confirm">
{{ $t("courselocation.not_valid") }}
</div>
</div> </div>
</div>
</template> </template>
<script> <script>
@@ -88,171 +98,177 @@ import AddAddress from "ChillMainAssets/vuejs/Address/components/AddAddress.vue"
import AddressRenderBox from "ChillMainAssets/vuejs/_components/Entity/AddressRenderBox.vue"; import AddressRenderBox from "ChillMainAssets/vuejs/_components/Entity/AddressRenderBox.vue";
export default { export default {
name: "CourseLocation", name: "CourseLocation",
components: { components: {
AddAddress, AddAddress,
AddressRenderBox, AddressRenderBox,
}, },
data() { data() {
return { return {
addAddress: { addAddress: {
options: { options: {
button: { button: {
text: { text: {
create: "courselocation.add_temporary_address", create: "courselocation.add_temporary_address",
edit: "courselocation.edit_temporary_address", edit: "courselocation.edit_temporary_address",
},
},
title: {
create: "courselocation.add_temporary_address",
edit: "courselocation.edit_temporary_address",
},
onlyButton: true,
},
}, },
}, };
title: { },
create: "courselocation.add_temporary_address", computed: {
edit: "courselocation.edit_temporary_address", ...mapState({
}, accompanyingCourse: (state) => state.accompanyingCourse,
onlyButton: true, context: (state) => state.addressContext,
}),
...mapGetters(["isLocationValid"]),
options() {
return this.addAddress.options;
}, },
}, key() {
}; return this.context.edit
}, ? "address_" + this.context.addressId
computed: { : this.accompanyingCourse.type +
...mapState({ "_" +
accompanyingCourse: (state) => state.accompanyingCourse, this.accompanyingCourse.id;
context: (state) => state.addressContext,
}),
...mapGetters(["isLocationValid"]),
options() {
return this.addAddress.options;
},
key() {
return this.context.edit
? "address_" + this.context.addressId
: this.accompanyingCourse.type + "_" + this.accompanyingCourse.id;
},
isTemporaryAddress() {
return this.accompanyingCourse.locationStatus === "address";
},
isPersonLocation() {
return this.accompanyingCourse.locationStatus === "person";
},
hasNoLocation() {
return this.accompanyingCourse.locationStatus === "none";
},
currentParticipations() {
return this.accompanyingCourse.participations.filter(
(p) => p.enddate !== null,
);
},
hasNoPersonLocation() {
let addressInParticipations_ = [];
this.currentParticipations.forEach((p) => {
addressInParticipations_.push(
this.checkHouseholdAddressForParticipation(p),
);
});
const booleanReducer = (previousValue, currentValue) =>
previousValue || currentValue;
let addressInParticipations =
addressInParticipations_.length > 0
? addressInParticipations_.reduce(booleanReducer)
: false;
//console.log(addressInParticipations_, addressInParticipations);
return (
this.accompanyingCourse.step !== "DRAFT" &&
this.isTemporaryAddress &&
!addressInParticipations
);
},
isContextEdit() {
return this.context.edit;
},
},
methods: {
checkHouseholdAddressForParticipation(participation) {
if (participation.person.current_household_id === null) {
return false;
}
return participation.person.current_household_address !== null;
},
initAddressContext() {
let context = {
target: {
name: this.accompanyingCourse.type,
id: this.accompanyingCourse.id,
}, },
edit: false, isTemporaryAddress() {
addressId: null, return this.accompanyingCourse.locationStatus === "address";
defaults: window.addaddress, },
}; isPersonLocation() {
if (this.accompanyingCourse.location) { return this.accompanyingCourse.locationStatus === "person";
context["edit"] = true; },
context["addressId"] = this.accompanyingCourse.location.address_id; hasNoLocation() {
} return this.accompanyingCourse.locationStatus === "none";
this.$store.commit("setAddressContext", context); },
}, currentParticipations() {
displayErrors() { return this.accompanyingCourse.participations.filter(
return this.$refs.addAddress.errorMsg; (p) => p.enddate !== null,
},
submitTemporaryAddress(payload) {
//console.log('@@@ click on Submit Temporary Address Button', payload);
payload["locationStatusTo"] = "address"; // <== temporary, not none, not person
this.$store
.dispatch("updateLocation", payload)
.catch(({ name, violations }) => {
if (name === "ValidationException" || name === "AccessException") {
violations.forEach((violation) =>
this.$toast.open({ message: violation }),
); );
} else { },
this.$toast.open({ message: "An error occurred" }); hasNoPersonLocation() {
} let addressInParticipations_ = [];
}); this.currentParticipations.forEach((p) => {
addressInParticipations_.push(
this.checkHouseholdAddressForParticipation(p),
);
});
this.$store.commit("setEditContextTrue", payload); const booleanReducer = (previousValue, currentValue) =>
previousValue || currentValue;
let addressInParticipations =
addressInParticipations_.length > 0
? addressInParticipations_.reduce(booleanReducer)
: false;
//console.log(addressInParticipations_, addressInParticipations);
return (
this.accompanyingCourse.step !== "DRAFT" &&
this.isTemporaryAddress &&
!addressInParticipations
);
},
isContextEdit() {
return this.context.edit;
},
}, },
}, methods: {
created() { checkHouseholdAddressForParticipation(participation) {
this.initAddressContext(); if (participation.person.current_household_id === null) {
return false;
}
return participation.person.current_household_address !== null;
},
initAddressContext() {
let context = {
target: {
name: this.accompanyingCourse.type,
id: this.accompanyingCourse.id,
},
edit: false,
addressId: null,
defaults: window.addaddress,
};
if (this.accompanyingCourse.location) {
context["edit"] = true;
context["addressId"] =
this.accompanyingCourse.location.address_id;
}
this.$store.commit("setAddressContext", context);
},
displayErrors() {
return this.$refs.addAddress.errorMsg;
},
submitTemporaryAddress(payload) {
//console.log('@@@ click on Submit Temporary Address Button', payload);
payload["locationStatusTo"] = "address"; // <== temporary, not none, not person
this.$store
.dispatch("updateLocation", payload)
.catch(({ name, violations }) => {
if (
name === "ValidationException" ||
name === "AccessException"
) {
violations.forEach((violation) =>
this.$toast.open({ message: violation }),
);
} else {
this.$toast.open({ message: "An error occurred" });
}
});
//console.log('ac.locationStatus', this.accompanyingCourse.locationStatus); this.$store.commit("setEditContextTrue", payload);
//console.log('ac.location (temporary location)', this.accompanyingCourse.location); },
//console.log('ac.personLocation', this.accompanyingCourse.personLocation); },
}, created() {
this.initAddressContext();
//console.log('ac.locationStatus', this.accompanyingCourse.locationStatus);
//console.log('ac.location (temporary location)', this.accompanyingCourse.location);
//console.log('ac.personLocation', this.accompanyingCourse.personLocation);
},
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
div#accompanying-course { div#accompanying-course {
div.vue-component { div.vue-component {
& > div.alert.no-person-location { & > div.alert.no-person-location {
margin: 1px 0 0; margin: 1px 0 0;
}
div.no-person-location {
padding-top: 1.5em;
display: flex;
flex-direction: row;
& > i {
flex-basis: 1.5em;
flex-grow: 0;
flex-shrink: 0;
padding-top: 0.2em;
opacity: 0.75;
}
& > div {
flex-basis: auto;
div.action {
button.btn-update {
margin-right: 2em;
}
} }
} div.no-person-location {
} padding-top: 1.5em;
div.flex-table { display: flex;
div.item-bloc { flex-direction: row;
div.alert { & > i {
margin: 0 -0.9em -1em; flex-basis: 1.5em;
flex-grow: 0;
flex-shrink: 0;
padding-top: 0.2em;
opacity: 0.75;
}
& > div {
flex-basis: auto;
div.action {
button.btn-update {
margin-right: 2em;
}
}
}
}
div.flex-table {
div.item-bloc {
div.alert {
margin: 0 -0.9em -1em;
}
}
} }
}
} }
}
} }
</style> </style>

View File

@@ -1,33 +1,33 @@
<template> <template>
<div class="vue-component"> <div class="vue-component">
<h2><a id="section-30" />{{ $t("origin.title") }}</h2> <h2><a id="section-30" />{{ $t("origin.title") }}</h2>
<div class="mb-4"> <div class="mb-4">
<label for="selectOrigin"> <label for="selectOrigin">
{{ $t("origin.title") }} {{ $t("origin.title") }}
</label> </label>
<VueMultiselect <VueMultiselect
name="selectOrigin" name="selectOrigin"
label="text" label="text"
:custom-label="transText" :custom-label="transText"
track-by="id" track-by="id"
:multiple="false" :multiple="false"
:searchable="true" :searchable="true"
:placeholder="$t('origin.placeholder')" :placeholder="$t('origin.placeholder')"
v-model="value" v-model="value"
:options="options" :options="options"
:select-label="$t('multiselect.select_label')" :select-label="$t('multiselect.select_label')"
:deselect-label="$t('multiselect.deselect_label')" :deselect-label="$t('multiselect.deselect_label')"
:selected-label="$t('multiselect.selected_label')" :selected-label="$t('multiselect.selected_label')"
@select="updateOrigin" @select="updateOrigin"
/> />
</div>
<div v-if="!isOriginValid" class="alert alert-warning to-confirm">
{{ $t("origin.not_valid") }}
</div>
</div> </div>
<div v-if="!isOriginValid" class="alert alert-warning to-confirm">
{{ $t("origin.not_valid") }}
</div>
</div>
</template> </template>
<script> <script>
@@ -36,58 +36,61 @@ import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
import { mapState, mapGetters } from "vuex"; import { mapState, mapGetters } from "vuex";
export default { export default {
name: "OriginDemand", name: "OriginDemand",
components: { VueMultiselect }, components: { VueMultiselect },
data() { data() {
return { return {
options: [], options: [],
}; };
},
computed: {
...mapState({
value: (state) => state.accompanyingCourse.origin,
}),
...mapGetters(["isOriginValid"]),
},
mounted() {
this.getOptions();
},
methods: {
getOptions() {
const url = `/api/1.0/person/accompanying-period/origin.json`;
makeFetch("GET", url)
.then((response) => {
this.options = response.results;
return response;
})
.catch((error) => {
commit("catchError", error);
this.$toast.open({ message: error.txt });
});
}, },
updateOrigin(value) { computed: {
this.$store ...mapState({
.dispatch("updateOrigin", value) value: (state) => state.accompanyingCourse.origin,
.catch(({ name, violations }) => { }),
if (name === "ValidationException" || name === "AccessException") { ...mapGetters(["isOriginValid"]),
violations.forEach((violation) =>
this.$toast.open({ message: violation }),
);
} else {
this.$toast.open({ message: "An error occurred" });
}
});
}, },
transText({ text }) { mounted() {
return text.fr; this.getOptions();
},
methods: {
getOptions() {
const url = `/api/1.0/person/accompanying-period/origin.json`;
makeFetch("GET", url)
.then((response) => {
this.options = response.results;
return response;
})
.catch((error) => {
commit("catchError", error);
this.$toast.open({ message: error.txt });
});
},
updateOrigin(value) {
this.$store
.dispatch("updateOrigin", value)
.catch(({ name, violations }) => {
if (
name === "ValidationException" ||
name === "AccessException"
) {
violations.forEach((violation) =>
this.$toast.open({ message: violation }),
);
} else {
this.$toast.open({ message: "An error occurred" });
}
});
},
transText({ text }) {
return text.fr;
},
}, },
},
}; };
</script> </script>
<style src="vue-multiselect/dist/vue-multiselect.css"></style> <style src="vue-multiselect/dist/vue-multiselect.css"></style>
<style lang="css" scoped> <style lang="css" scoped>
label { label {
display: none; display: none;
} }
</style> </style>

View File

@@ -1,106 +1,121 @@
<template> <template>
<div class="vue-component"> <div class="vue-component">
<h2><a id="section-10" />{{ $t("persons_associated.title") }}</h2> <h2><a id="section-10" />{{ $t("persons_associated.title") }}</h2>
<div v-if="currentParticipations.length > 0"> <div v-if="currentParticipations.length > 0">
<label class="col-form-label">{{ <label class="col-form-label">{{
$tc("persons_associated.counter", counter) $tc("persons_associated.counter", counter)
}}</label> }}</label>
</div> </div>
<div v-else> <div v-else>
<label class="chill-no-data-statement">{{ <label class="chill-no-data-statement">{{
$tc("persons_associated.counter", counter) $tc("persons_associated.counter", counter)
}}</label> }}</label>
</div>
<div
v-if="participationWithoutHousehold.length > 0"
class="alert alert-warning no-household"
>
<i class="fa fa-warning fa-2x" />
<form method="GET" action="/fr/person/household/members/editor">
<div class="float-button bottom">
<div class="box">
<div class="action">
<button class="btn btn-update" type="submit">
{{ $t("persons_associated.update_household") }}
</button>
</div>
<p class="mb-3">
{{ $t("persons_associated.person_without_household_warning") }}
</p>
<div
class="form-check"
v-for="p in participationWithoutHousehold"
:key="p.id"
>
<input
type="checkbox"
class="form-check-input"
name="persons[]"
checked="checked"
:id="p.person.id"
:value="p.person.id"
/>
<label class="form-check-label">
<person-text :person="p.person" />
</label>
</div>
<input type="hidden" name="expand_suggestions" value="true" />
<input type="hidden" name="returnPath" :value="getReturnPath" />
<input
type="hidden"
name="accompanying_period_id"
:value="courseId"
/>
</div>
</div> </div>
</form>
</div>
<div class="flex-table mb-3"> <div
<participation-item v-if="participationWithoutHousehold.length > 0"
v-for="participation in currentParticipations" class="alert alert-warning no-household"
:participation="participation"
:key="participation.id"
@remove="removeParticipation"
@close="closeParticipation"
/>
</div>
<div v-if="suggestedPersons.length > 0">
<ul class="list-suggest add-items inline">
<li
v-for="p in suggestedPersons"
:key="p.id"
@click="addSuggestedPerson(p)"
> >
<person-text :person="p" /> <i class="fa fa-warning fa-2x" />
</li> <form method="GET" action="/fr/person/household/members/editor">
</ul> <div class="float-button bottom">
</div> <div class="box">
<div class="action">
<button class="btn btn-update" type="submit">
{{ $t("persons_associated.update_household") }}
</button>
</div>
<p class="mb-3">
{{
$t(
"persons_associated.person_without_household_warning",
)
}}
</p>
<div
class="form-check"
v-for="p in participationWithoutHousehold"
:key="p.id"
>
<input
type="checkbox"
class="form-check-input"
name="persons[]"
checked="checked"
:id="p.person.id"
:value="p.person.id"
/>
<label class="form-check-label">
<person-text :person="p.person" />
</label>
</div>
<input
type="hidden"
name="expand_suggestions"
value="true"
/>
<input
type="hidden"
name="returnPath"
:value="getReturnPath"
/>
<input
type="hidden"
name="accompanying_period_id"
:value="courseId"
/>
</div>
</div>
</form>
</div>
<div> <div class="flex-table mb-3">
<ul class="record_actions"> <participation-item
<li class="add-persons"> v-for="participation in currentParticipations"
<add-persons :participation="participation"
button-title="persons_associated.add_persons" :key="participation.id"
modal-title="add_persons.title" @remove="removeParticipation"
:key="addPersons.key" @close="closeParticipation"
:options="addPersons.options" />
@add-new-persons="addNewPersons" </div>
ref="addPersons"
>
<!-- to cast child method -->
</add-persons>
</li>
</ul>
</div>
<div v-if="!isParticipationValid" class="alert alert-warning to-confirm"> <div v-if="suggestedPersons.length > 0">
{{ $t("persons_associated.participation_not_valid") }} <ul class="list-suggest add-items inline">
<li
v-for="p in suggestedPersons"
:key="p.id"
@click="addSuggestedPerson(p)"
>
<person-text :person="p" />
</li>
</ul>
</div>
<div>
<ul class="record_actions">
<li class="add-persons">
<add-persons
button-title="persons_associated.add_persons"
modal-title="add_persons.title"
:key="addPersons.key"
:options="addPersons.options"
@add-new-persons="addNewPersons"
ref="addPersons"
>
<!-- to cast child method -->
</add-persons>
</li>
</ul>
</div>
<div
v-if="!isParticipationValid"
class="alert alert-warning to-confirm"
>
{{ $t("persons_associated.participation_not_valid") }}
</div>
</div> </div>
</div>
</template> </template>
<script> <script>
@@ -110,157 +125,176 @@ import AddPersons from "ChillPersonAssets/vuejs/_components/AddPersons.vue";
import PersonText from "ChillPersonAssets/vuejs/_components/Entity/PersonText.vue"; import PersonText from "ChillPersonAssets/vuejs/_components/Entity/PersonText.vue";
export default { export default {
name: "PersonsAssociated", name: "PersonsAssociated",
components: { components: {
ParticipationItem, ParticipationItem,
AddPersons, AddPersons,
PersonText, PersonText,
}, },
data() { data() {
return { return {
addPersons: { addPersons: {
key: "persons_associated", key: "persons_associated",
options: { options: {
type: ["person"], type: ["person"],
priority: null, priority: null,
uniq: false, uniq: false,
}, },
}, },
}; };
}, },
computed: { computed: {
...mapState({ ...mapState({
courseId: (state) => state.accompanyingCourse.id, courseId: (state) => state.accompanyingCourse.id,
participations: (state) => state.accompanyingCourse.participations, participations: (state) => state.accompanyingCourse.participations,
suggestedPersons: (state) => suggestedPersons: (state) =>
[ [
state.accompanyingCourse.requestor, state.accompanyingCourse.requestor,
...state.accompanyingCourse.resources.map((r) => r.resource), ...state.accompanyingCourse.resources.map(
] (r) => r.resource,
.filter((e) => e !== null) ),
.filter((e) => e.type === "person") ]
.filter( .filter((e) => e !== null)
(p) => .filter((e) => e.type === "person")
!state.accompanyingCourse.participations .filter(
.filter((pa) => pa.endDate === null) (p) =>
.map((pa) => pa.person.id) !state.accompanyingCourse.participations
.includes(p.id), .filter((pa) => pa.endDate === null)
) .map((pa) => pa.person.id)
// filter persons appearing twice in requestor and resources .includes(p.id),
.filter((e, index, suggested) => { )
for (let i = 0; i < suggested.length; i = i + 1) { // filter persons appearing twice in requestor and resources
if (i < index && e.id === suggested[i].id) { .filter((e, index, suggested) => {
return false; for (let i = 0; i < suggested.length; i = i + 1) {
} if (i < index && e.id === suggested[i].id) {
} return false;
}
}
return true; return true;
}), }),
}), }),
...mapGetters(["isParticipationValid"]), ...mapGetters(["isParticipationValid"]),
currentParticipations() { currentParticipations() {
return this.participations.filter((p) => p.endDate === null); return this.participations.filter((p) => p.endDate === null);
}, },
counter() { counter() {
return this.currentParticipations.length; return this.currentParticipations.length;
}, },
participationWithoutHousehold() { participationWithoutHousehold() {
return this.currentParticipations.filter( return this.currentParticipations.filter(
(p) => p.person.current_household_id === null, (p) => p.person.current_household_id === null,
);
},
getReturnPath() {
return (
window.location.pathname + window.location.search + window.location.hash
);
},
},
methods: {
removeParticipation(item) {
this.$store
.dispatch("removeParticipation", item)
.catch(({ name, violations }) => {
if (name === "ValidationException" || name === "AccessException") {
violations.forEach((violation) =>
this.$toast.open({ message: violation }),
); );
} else { },
this.$toast.open({ message: "An error occurred" }); getReturnPath() {
} return (
}); window.location.pathname +
}, window.location.search +
closeParticipation(item) { window.location.hash
this.$store
.dispatch("closeParticipation", item)
.catch(({ name, violations }) => {
if (name === "ValidationException" || name === "AccessException") {
violations.forEach((violation) =>
this.$toast.open({ message: violation }),
); );
} else { },
this.$toast.open({ message: "An error occurred" });
}
});
}, },
addNewPersons({ selected, modal }) { methods: {
selected.forEach(function (item) { removeParticipation(item) {
this.$store this.$store
.dispatch("addParticipation", item) .dispatch("removeParticipation", item)
.catch(({ name, violations }) => { .catch(({ name, violations }) => {
if (name === "ValidationException" || name === "AccessException") { if (
violations.forEach((violation) => name === "ValidationException" ||
this.$toast.open({ message: violation }), name === "AccessException"
); ) {
} else { violations.forEach((violation) =>
this.$toast.open({ message: "An error occurred" }); this.$toast.open({ message: violation }),
} );
}); } else {
}, this); this.$toast.open({ message: "An error occurred" });
this.$refs.addPersons.resetSearch(); // to cast child method }
modal.showModal = false; });
},
closeParticipation(item) {
this.$store
.dispatch("closeParticipation", item)
.catch(({ name, violations }) => {
if (
name === "ValidationException" ||
name === "AccessException"
) {
violations.forEach((violation) =>
this.$toast.open({ message: violation }),
);
} else {
this.$toast.open({ message: "An error occurred" });
}
});
},
addNewPersons({ selected, modal }) {
selected.forEach(function (item) {
this.$store
.dispatch("addParticipation", item)
.catch(({ name, violations }) => {
if (
name === "ValidationException" ||
name === "AccessException"
) {
violations.forEach((violation) =>
this.$toast.open({ message: violation }),
);
} else {
this.$toast.open({ message: "An error occurred" });
}
});
}, this);
this.$refs.addPersons.resetSearch(); // to cast child method
modal.showModal = false;
},
addSuggestedPerson(person) {
this.$store
.dispatch("addParticipation", {
result: person,
type: "person",
})
.catch(({ name, violations }) => {
if (
name === "ValidationException" ||
name === "AccessException"
) {
violations.forEach((violation) =>
this.$toast.open({ message: violation }),
);
} else {
this.$toast.open({ message: "An error occurred" });
}
});
},
}, },
addSuggestedPerson(person) {
this.$store
.dispatch("addParticipation", { result: person, type: "person" })
.catch(({ name, violations }) => {
if (name === "ValidationException" || name === "AccessException") {
violations.forEach((violation) =>
this.$toast.open({ message: violation }),
);
} else {
this.$toast.open({ message: "An error occurred" });
}
});
},
},
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
div#accompanying-course { div#accompanying-course {
div.vue-component { div.vue-component {
& > div.alert.no-household { & > div.alert.no-household {
margin: 0 0 -1em; margin: 0 0 -1em;
} }
div.no-household { div.no-household {
padding-bottom: 1.5em; padding-bottom: 1.5em;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
& > i { & > i {
flex-basis: 1.5em; flex-basis: 1.5em;
flex-grow: 0; flex-grow: 0;
flex-shrink: 0; flex-shrink: 0;
padding-top: 0.2em; padding-top: 0.2em;
opacity: 0.75; opacity: 0.75;
} }
& > form { & > form {
flex-basis: auto; flex-basis: auto;
div.action { div.action {
button.btn-update { button.btn-update {
margin-right: 2em; margin-right: 2em;
} }
}
}
} }
}
} }
}
} }
</style> </style>

View File

@@ -1,97 +1,98 @@
<template> <template>
<person-render-box <person-render-box
render="bloc" render="bloc"
:options="{ :options="{
addInfo: true, addInfo: true,
addId: false, addId: false,
addEntity: false, addEntity: false,
addLink: false, addLink: false,
addHouseholdLink: false, addHouseholdLink: false,
addAltNames: true, addAltNames: true,
addAge: true, addAge: true,
hLevel: 3, hLevel: 3,
isConfidential: false, isConfidential: false,
isMultiline: true, isMultiline: true,
}" }"
:person="participation.person" :person="participation.person"
:return-path="getAccompanyingCourseReturnPath" :return-path="getAccompanyingCourseReturnPath"
>
<template #end-bloc>
<div class="item-row separator">
<ul class="record_actions">
<button-location
v-if="
hasCurrentHouseholdAddress &&
!isPersonLocatingCourse(participation.person)
"
:person="participation.person"
/>
<li v-if="participation.person.current_household_id">
<a
class="btn btn-sm btn-chill-beige"
:href="getCurrentHouseholdUrl"
:title="
$t('persons_associated.show_household_number', {
id: participation.person.current_household_id,
})
"
>
<i class="fa fa-fw fa-home" />
</a>
</li>
<li>
<on-the-fly
:type="participation.person.type"
:id="participation.person.id"
action="show"
/>
</li>
<li>
<on-the-fly
:type="participation.person.type"
:id="participation.person.id"
action="edit"
@save-form-on-the-fly="saveFormOnTheFly"
ref="onTheFly"
/>
</li>
<li>
<button
v-if="!participation.endDate"
class="btn btn-sm btn-remove"
:title="$t('persons_associated.leave_course')"
@click="modal.showModal = true"
/>
</li>
</ul>
</div>
</template>
</person-render-box>
<teleport to="body">
<modal
v-if="modal.showModal"
:modal-dialog-class="modal.modalDialogClass"
@close="modal.showModal = false"
> >
<template #header> <template #end-bloc>
<h2 class="modal-title"> <div class="item-row separator">
{{ $t("persons_associated.sure") }} <ul class="record_actions">
</h2> <button-location
</template> v-if="
<template #body> hasCurrentHouseholdAddress &&
<p>{{ $t("persons_associated.sure_description") }}</p> !isPersonLocatingCourse(participation.person)
</template> "
<template #footer> :person="participation.person"
<button />
class="btn btn-danger" <li v-if="participation.person.current_household_id">
@click.prevent="$emit('close', participation)" <a
class="btn btn-sm btn-chill-beige"
:href="getCurrentHouseholdUrl"
:title="
$t('persons_associated.show_household_number', {
id: participation.person
.current_household_id,
})
"
>
<i class="fa fa-fw fa-home" />
</a>
</li>
<li>
<on-the-fly
:type="participation.person.type"
:id="participation.person.id"
action="show"
/>
</li>
<li>
<on-the-fly
:type="participation.person.type"
:id="participation.person.id"
action="edit"
@save-form-on-the-fly="saveFormOnTheFly"
ref="onTheFly"
/>
</li>
<li>
<button
v-if="!participation.endDate"
class="btn btn-sm btn-remove"
:title="$t('persons_associated.leave_course')"
@click="modal.showModal = true"
/>
</li>
</ul>
</div>
</template>
</person-render-box>
<teleport to="body">
<modal
v-if="modal.showModal"
:modal-dialog-class="modal.modalDialogClass"
@close="modal.showModal = false"
> >
{{ $t("persons_associated.ok") }} <template #header>
</button> <h2 class="modal-title">
</template> {{ $t("persons_associated.sure") }}
</modal> </h2>
</teleport> </template>
<template #body>
<p>{{ $t("persons_associated.sure_description") }}</p>
</template>
<template #footer>
<button
class="btn btn-danger"
@click.prevent="$emit('close', participation)"
>
{{ $t("persons_associated.ok") }}
</button>
</template>
</modal>
</teleport>
</template> </template>
<script> <script>
@@ -102,135 +103,135 @@ import Modal from "ChillMainAssets/vuejs/_components/Modal";
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods"; import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
export default { export default {
name: "ParticipationItem", name: "ParticipationItem",
components: { components: {
OnTheFly, OnTheFly,
ButtonLocation, ButtonLocation,
PersonRenderBox, PersonRenderBox,
Modal, Modal,
},
props: ["participation"],
emits: ["remove", "close"],
data() {
return {
modal: {
showModal: false,
modalDialogClass: "modal-dialog-centered modal-md",
},
PersonRenderBox: {
participation: "participation",
options: {
addInfo: false,
addId: true,
addAge: false,
hLevel: 1,
},
},
};
},
computed: {
hasCurrentHouseholdAddress() {
if (
!this.participation.endDate &&
this.participation.person.current_household_address !== null
) {
return true;
}
return false;
}, },
getAccompanyingCourseReturnPath() { props: ["participation"],
return `fr/parcours/${this.$store.state.accompanyingCourse.id}/edit#section-10`; emits: ["remove", "close"],
}, data() {
getCurrentHouseholdUrl() { return {
return `/fr/person/household/${this.participation.person.current_household_id}/summary?returnPath=${this.getAccompanyingCourseReturnPath}`; modal: {
}, showModal: false,
}, modalDialogClass: "modal-dialog-centered modal-md",
methods: { },
isPersonLocatingCourse(person) { PersonRenderBox: {
return this.$store.getters.isPersonLocatingCourse(person); participation: "participation",
}, options: {
saveFormOnTheFly(payload) { addInfo: false,
console.log( addId: true,
"saveFormOnTheFly: type", addAge: false,
payload.type, hLevel: 1,
", data", },
payload.data, },
);
payload.target = "participation";
let body = { type: payload.type };
if (payload.type === "person") {
body.firstName = payload.data.firstName;
body.lastName = payload.data.lastName;
if (payload.data.birthdate !== null) {
body.birthdate = payload.data.birthdate;
}
body.phonenumber = payload.data.phonenumber;
body.mobilenumber = payload.data.mobilenumber;
body.email = payload.data.email;
body.altNames = payload.data.altNames;
body.gender = {
id: payload.data.gender.id,
type: payload.data.gender.type,
}; };
if (payload.data.civility !== null) {
body.civility = {
id: payload.data.civility.id,
type: payload.data.civility.type,
};
}
makeFetch(
"PATCH",
`/api/1.0/person/person/${payload.data.id}.json`,
body,
)
.then((response) => {
this.$store.dispatch("addPerson", {
target: payload.target,
body: response,
});
this.$refs.onTheFly.closeModal();
})
.catch((error) => {
if (error.name === "ValidationException") {
for (let v of error.violations) {
this.$toast.open({ message: v });
}
} else {
this.$toast.open({ message: "An error occurred" });
}
});
} else if (payload.type === "thirdparty") {
body.name = payload.data.text;
body.email = payload.data.email;
body.telephone = payload.data.telephone;
body.telephone2 = payload.data.telephone2;
body.address = { id: payload.data.address.address_id };
makeFetch(
"PATCH",
`/api/1.0/thirdparty/thirdparty/${payload.data.id}.json`,
body,
)
.then((response) => {
this.$store.dispatch("addThirdparty", {
target: payload.target,
body: response,
});
this.$refs.onTheFly.closeModal();
})
.catch((error) => {
if (error.name === "ValidationException") {
for (let v of error.violations) {
this.$toast.open({ message: v });
}
} else {
this.$toast.open({ message: "An error occurred" });
}
});
}
}, },
}, computed: {
hasCurrentHouseholdAddress() {
if (
!this.participation.endDate &&
this.participation.person.current_household_address !== null
) {
return true;
}
return false;
},
getAccompanyingCourseReturnPath() {
return `fr/parcours/${this.$store.state.accompanyingCourse.id}/edit#section-10`;
},
getCurrentHouseholdUrl() {
return `/fr/person/household/${this.participation.person.current_household_id}/summary?returnPath=${this.getAccompanyingCourseReturnPath}`;
},
},
methods: {
isPersonLocatingCourse(person) {
return this.$store.getters.isPersonLocatingCourse(person);
},
saveFormOnTheFly(payload) {
console.log(
"saveFormOnTheFly: type",
payload.type,
", data",
payload.data,
);
payload.target = "participation";
let body = { type: payload.type };
if (payload.type === "person") {
body.firstName = payload.data.firstName;
body.lastName = payload.data.lastName;
if (payload.data.birthdate !== null) {
body.birthdate = payload.data.birthdate;
}
body.phonenumber = payload.data.phonenumber;
body.mobilenumber = payload.data.mobilenumber;
body.email = payload.data.email;
body.altNames = payload.data.altNames;
body.gender = {
id: payload.data.gender.id,
type: payload.data.gender.type,
};
if (payload.data.civility !== null) {
body.civility = {
id: payload.data.civility.id,
type: payload.data.civility.type,
};
}
makeFetch(
"PATCH",
`/api/1.0/person/person/${payload.data.id}.json`,
body,
)
.then((response) => {
this.$store.dispatch("addPerson", {
target: payload.target,
body: response,
});
this.$refs.onTheFly.closeModal();
})
.catch((error) => {
if (error.name === "ValidationException") {
for (let v of error.violations) {
this.$toast.open({ message: v });
}
} else {
this.$toast.open({ message: "An error occurred" });
}
});
} else if (payload.type === "thirdparty") {
body.name = payload.data.text;
body.email = payload.data.email;
body.telephone = payload.data.telephone;
body.telephone2 = payload.data.telephone2;
body.address = { id: payload.data.address.address_id };
makeFetch(
"PATCH",
`/api/1.0/thirdparty/thirdparty/${payload.data.id}.json`,
body,
)
.then((response) => {
this.$store.dispatch("addThirdparty", {
target: payload.target,
body: response,
});
this.$refs.onTheFly.closeModal();
})
.catch((error) => {
if (error.name === "ValidationException") {
for (let v of error.violations) {
this.$toast.open({ message: v });
}
} else {
this.$toast.open({ message: "An error occurred" });
}
});
}
},
},
}; };
</script> </script>

View File

@@ -1,111 +1,116 @@
<template> <template>
<div class="vue-component"> <div class="vue-component">
<h2><a id="section-80" />{{ $t("referrer.title") }}</h2> <h2><a id="section-80" />{{ $t("referrer.title") }}</h2>
<teleport to="body"> <teleport to="body">
<modal <modal
v-if="modal.showModal" v-if="modal.showModal"
:modal-dialog-class="modal.modalDialogClass" :modal-dialog-class="modal.modalDialogClass"
@close="cancelChange" @close="cancelChange"
> >
<template #header> <template #header>
<h3 class="modal-title"> <h3 class="modal-title">
{{ $t("confirm.title") }} {{ $t("confirm.title") }}
</h3> </h3>
</template> </template>
<template #body-head> <template #body-head>
<div class="modal-body"> <div class="modal-body">
<p <p
v-html=" v-html="
$t('confirm.sure_referrer', { referrer: this.value.text }) $t('confirm.sure_referrer', {
" referrer: this.value.text,
})
"
/>
</div>
</template>
<template #footer>
<button
class="btn btn-save"
@click.prevent="this.confirmReferrer"
>
{{ $t("confirm.ok_referrer") }}
</button>
</template>
</modal>
</teleport>
<div>
<label class="col-form-label" for="selectJob">
{{ $t("job.label") }}
</label>
<VueMultiselect
name="selectJob"
label="text"
:custom-label="customJobLabel"
track-by="id"
:multiple="false"
:searchable="true"
:placeholder="$t('job.placeholder')"
v-model="valueJob"
:options="jobs"
:select-label="$t('multiselect.select_label')"
:deselect-label="$t('multiselect.deselect_label')"
:selected-label="$t('multiselect.selected_label')"
/> />
</div>
</template>
<template #footer> <label class="col-form-label" for="selectReferrer">
<button class="btn btn-save" @click.prevent="this.confirmReferrer"> {{ $t("referrer.label") }}
{{ $t("confirm.ok_referrer") }} </label>
</button>
</template>
</modal>
</teleport>
<div> <VueMultiselect
<label class="col-form-label" for="selectJob"> name="selectReferrer"
{{ $t("job.label") }} label="text"
</label> track-by="id"
:multiple="false"
:searchable="true"
:placeholder="$t('referrer.placeholder')"
v-model="value"
@select="updateReferrer"
@remove="removeReferrer"
:options="users"
:select-label="$t('multiselect.select_label')"
:deselect-label="$t('multiselect.deselect_label')"
:selected-label="$t('multiselect.selected_label')"
/>
<VueMultiselect <template v-if="usersSuggestedFilteredByJob.length > 0">
name="selectJob" <ul class="list-suggest add-items inline">
label="text" <li
:custom-label="customJobLabel" v-for="(u, i) in usersSuggestedFilteredByJob"
track-by="id" @click="updateReferrer(u)"
:multiple="false" :key="`referrer-${i}`"
:searchable="true" >
:placeholder="$t('job.placeholder')" <span>
v-model="valueJob" <user-render-box-badge :user="u" />
:options="jobs" </span>
:select-label="$t('multiselect.select_label')" </li>
:deselect-label="$t('multiselect.deselect_label')" </ul>
:selected-label="$t('multiselect.selected_label')" </template>
/> </div>
<label class="col-form-label" for="selectReferrer"> <div>
{{ $t("referrer.label") }} <ul class="record_actions">
</label> <li>
<button
class="btn btn-create"
type="button"
name="button"
@click="assignMe"
>
{{ $t("referrer.assign_me") }}
</button>
</li>
</ul>
</div>
<VueMultiselect <div v-if="!isJobValid" class="alert alert-warning to-confirm">
name="selectReferrer" {{ $t("job.not_valid") }}
label="text" </div>
track-by="id"
:multiple="false"
:searchable="true"
:placeholder="$t('referrer.placeholder')"
v-model="value"
@select="updateReferrer"
@remove="removeReferrer"
:options="users"
:select-label="$t('multiselect.select_label')"
:deselect-label="$t('multiselect.deselect_label')"
:selected-label="$t('multiselect.selected_label')"
/>
<template v-if="usersSuggestedFilteredByJob.length > 0">
<ul class="list-suggest add-items inline">
<li
v-for="(u, i) in usersSuggestedFilteredByJob"
@click="updateReferrer(u)"
:key="`referrer-${i}`"
>
<span>
<user-render-box-badge :user="u" />
</span>
</li>
</ul>
</template>
</div> </div>
<div>
<ul class="record_actions">
<li>
<button
class="btn btn-create"
type="button"
name="button"
@click="assignMe"
>
{{ $t("referrer.assign_me") }}
</button>
</li>
</ul>
</div>
<div v-if="!isJobValid" class="alert alert-warning to-confirm">
{{ $t("job.not_valid") }}
</div>
</div>
</template> </template>
<script> <script>
@@ -116,124 +121,134 @@ import UserRenderBoxBadge from "ChillMainAssets/vuejs/_components/Entity/UserRen
import Modal from "ChillMainAssets/vuejs/_components/Modal"; import Modal from "ChillMainAssets/vuejs/_components/Modal";
export default { export default {
name: "Referrer", name: "Referrer",
components: { components: {
UserRenderBoxBadge, UserRenderBoxBadge,
VueMultiselect, VueMultiselect,
Modal, Modal,
},
data() {
return {
jobs: [],
modal: {
showModal: false,
modalDialogClass: "modal-dialog-scrollable modal-xl",
},
value: this.$store.state.accompanyingCourse.user,
confirmed: false,
};
},
computed: {
...mapState({
valueJob: (state) => state.accompanyingCourse.job,
}),
...mapGetters(["isJobValid", "usersSuggestedFilteredByJob"]),
users: function () {
let users = this.$store.getters.usersFilteredByJob;
// ensure that the selected user is in the list. add it if necessary
if (
this.$store.state.accompanyingCourse.user !== null &&
users.find(
(u) => this.$store.state.accompanyingCourse.user.id === u.id,
) === undefined
) {
users.push(this.$store.state.accompanyingCourse.user);
}
return users;
}, },
valueJob: { data() {
get() { return {
return this.$store.state.accompanyingCourse.job; jobs: [],
}, modal: {
set(value) { showModal: false,
this.$store modalDialogClass: "modal-dialog-scrollable modal-xl",
.dispatch("updateJob", value) },
.catch(({ name, violations }) => { value: this.$store.state.accompanyingCourse.user,
if (name === "ValidationException" || name === "AccessException") { confirmed: false,
violations.forEach((violation) => };
this.$toast.open({ message: violation }), },
); computed: {
} else { ...mapState({
this.$toast.open({ message: "An error occurred" }); valueJob: (state) => state.accompanyingCourse.job,
}),
...mapGetters(["isJobValid", "usersSuggestedFilteredByJob"]),
users: function () {
let users = this.$store.getters.usersFilteredByJob;
// ensure that the selected user is in the list. add it if necessary
if (
this.$store.state.accompanyingCourse.user !== null &&
users.find(
(u) =>
this.$store.state.accompanyingCourse.user.id === u.id,
) === undefined
) {
users.push(this.$store.state.accompanyingCourse.user);
} }
}); return users;
}, },
valueJob: {
get() {
return this.$store.state.accompanyingCourse.job;
},
set(value) {
this.$store
.dispatch("updateJob", value)
.catch(({ name, violations }) => {
if (
name === "ValidationException" ||
name === "AccessException"
) {
violations.forEach((violation) =>
this.$toast.open({ message: violation }),
);
} else {
this.$toast.open({ message: "An error occurred" });
}
});
},
},
}, },
}, mounted() {
mounted() { this.getJobs();
this.getJobs();
},
methods: {
updateReferrer(value) {
this.value = value;
this.toggleModal();
}, },
getJobs() { methods: {
const url = "/api/1.0/main/user-job.json"; updateReferrer(value) {
makeFetch("GET", url) this.value = value;
.then((response) => { this.toggleModal();
this.jobs = response.results; },
}) getJobs() {
.catch((error) => { const url = "/api/1.0/main/user-job.json";
this.$toast.open({ message: error.txt }); makeFetch("GET", url)
}); .then((response) => {
this.jobs = response.results;
})
.catch((error) => {
this.$toast.open({ message: error.txt });
});
},
customJobLabel(value) {
return value.label.fr;
},
assignMe() {
const url = `/api/1.0/main/whoami.json`;
makeFetch("GET", url).then((user) => {
// this.value = user
this.updateReferrer(user);
});
},
toggleModal() {
this.modal.showModal = !this.modal.showModal;
},
confirmReferrer() {
this.$store
.dispatch("updateReferrer", this.value)
.catch(({ name, violations }) => {
if (
name === "ValidationException" ||
name === "AccessException"
) {
violations.forEach((violation) =>
this.$toast.open({ message: violation }),
);
} else {
this.$toast.open({ message: "An error occurred" });
}
});
this.toggleModal();
},
removeReferrer() {
console.log("remove option");
this.$store
.dispatch("updateReferrer", null)
.catch(({ name, violations }) => {
if (
name === "ValidationException" ||
name === "AccessException"
) {
violations.forEach((violation) =>
this.$toast.open({ message: violation }),
);
} else {
this.$toast.open({ message: "An error occurred" });
}
});
},
cancelChange() {
this.value = this.$store.state.accompanyingCourse.user;
this.toggleModal();
},
}, },
customJobLabel(value) {
return value.label.fr;
},
assignMe() {
const url = `/api/1.0/main/whoami.json`;
makeFetch("GET", url).then((user) => {
// this.value = user
this.updateReferrer(user);
});
},
toggleModal() {
this.modal.showModal = !this.modal.showModal;
},
confirmReferrer() {
this.$store
.dispatch("updateReferrer", this.value)
.catch(({ name, violations }) => {
if (name === "ValidationException" || name === "AccessException") {
violations.forEach((violation) =>
this.$toast.open({ message: violation }),
);
} else {
this.$toast.open({ message: "An error occurred" });
}
});
this.toggleModal();
},
removeReferrer() {
console.log("remove option");
this.$store
.dispatch("updateReferrer", null)
.catch(({ name, violations }) => {
if (name === "ValidationException" || name === "AccessException") {
violations.forEach((violation) =>
this.$toast.open({ message: violation }),
);
} else {
this.$toast.open({ message: "An error occurred" });
}
});
},
cancelChange() {
this.value = this.$store.state.accompanyingCourse.user;
this.toggleModal();
},
},
}; };
</script> </script>

View File

@@ -1,239 +1,263 @@
<template> <template>
<div class="vue-component"> <div class="vue-component">
<h2><a id="section-50" />{{ $t("requestor.title") }}</h2> <h2><a id="section-50" />{{ $t("requestor.title") }}</h2>
<div v-if="accompanyingCourse.requestor && isAnonymous" class="flex-table"> <div
<label> v-if="accompanyingCourse.requestor && isAnonymous"
<input type="checkbox" v-model="requestorIsAnonymous" class="me-2" /> class="flex-table"
{{ $t("requestor.is_anonymous") }}
</label>
<confidential v-if="accompanyingCourse.requestor.type === 'thirdparty'">
<template #confidential-content>
<third-party-render-box
:thirdparty="accompanyingCourse.requestor"
:options="{
addLink: false,
addId: false,
addEntity: true,
addInfo: false,
hLevel: 3,
isMultiline: true,
isConfidential: true,
}"
>
<template #record-actions>
<ul class="record_actions">
<li>
<on-the-fly
:type="accompanyingCourse.requestor.type"
:id="accompanyingCourse.requestor.id"
action="show"
/>
</li>
<li>
<on-the-fly
:type="accompanyingCourse.requestor.type"
:id="accompanyingCourse.requestor.id"
action="edit"
@save-form-on-the-fly="saveFormOnTheFly"
ref="onTheFly"
/>
</li>
</ul>
</template>
</third-party-render-box>
</template>
</confidential>
<confidential v-else-if="accompanyingCourse.requestor.type === 'person'">
<template #confidential-content>
<person-render-box
render="bloc"
:person="accompanyingCourse.requestor"
:options="{
addLink: false,
addId: false,
addAltNames: false,
addEntity: true,
addInfo: true,
hLevel: 3,
isMultiline: true,
isConfidential: false,
addAge: true,
}"
>
<template #record-actions>
<ul class="record_actions">
<li>
<on-the-fly
:type="accompanyingCourse.requestor.type"
:id="accompanyingCourse.requestor.id"
action="show"
/>
</li>
<li>
<on-the-fly
:type="accompanyingCourse.requestor.type"
:id="accompanyingCourse.requestor.id"
action="edit"
@save-form-on-the-fly="saveFormOnTheFly"
ref="onTheFly"
/>
</li>
</ul>
</template>
</person-render-box>
</template>
</confidential>
<ul class="record_actions">
<li>
<button
class="btn btn-remove"
:title="$t('action.remove')"
@click="removeRequestor"
>
{{ $t("action.remove") }}
</button>
</li>
</ul>
</div>
<div
v-else-if="accompanyingCourse.requestor && !isAnonymous"
class="flex-table"
>
<label>
<input type="checkbox" v-model="requestorIsAnonymous" class="me-2" />
{{ $t("requestor.is_anonymous") }}
</label>
<third-party-render-box
v-if="accompanyingCourse.requestor.type === 'thirdparty'"
:thirdparty="accompanyingCourse.requestor"
:options="{
addLink: false,
addId: false,
addEntity: true,
addInfo: false,
hLevel: 3,
isMultiline: true,
isConfidential: true,
}"
>
<template #record-actions>
<ul class="record_actions">
<li>
<on-the-fly
:type="accompanyingCourse.requestor.type"
:id="accompanyingCourse.requestor.id"
action="show"
/>
</li>
<li>
<on-the-fly
:type="accompanyingCourse.requestor.type"
:id="accompanyingCourse.requestor.id"
action="edit"
@save-form-on-the-fly="saveFormOnTheFly"
ref="onTheFly"
/>
</li>
</ul>
</template>
</third-party-render-box>
<person-render-box
render="bloc"
v-if="accompanyingCourse.requestor.type === 'person'"
:person="accompanyingCourse.requestor"
:options="{
addLink: false,
addId: false,
addAltNames: false,
addEntity: true,
addInfo: true,
hLevel: 3,
isMultiline: true,
isConfidential: false,
}"
>
<template #record-actions>
<ul class="record_actions">
<li>
<on-the-fly
:type="accompanyingCourse.requestor.type"
:id="accompanyingCourse.requestor.id"
action="show"
/>
</li>
<li>
<on-the-fly
:type="accompanyingCourse.requestor.type"
:id="accompanyingCourse.requestor.id"
action="edit"
@save-form-on-the-fly="saveFormOnTheFly"
ref="onTheFly"
/>
</li>
</ul>
</template>
</person-render-box>
<ul class="record_actions">
<li>
<button
class="btn btn-remove"
:title="$t('action.remove')"
@click="removeRequestor"
>
{{ $t("action.remove") }}
</button>
</li>
</ul>
</div>
<div v-else>
<label class="chill-no-data-statement">{{
$t("requestor.counter")
}}</label>
</div>
<div
v-if="
accompanyingCourse.requestor === null && suggestedEntities.length > 0
"
>
<ul class="list-suggest add-items inline">
<li
v-for="p in suggestedEntities"
:key="uniqueId(p)"
@click="addSuggestedEntity(p)"
> >
<person-text v-if="p.type === 'person'" :person="p" /> <label>
<span v-else>{{ p.text }}</span> <input
</li> type="checkbox"
</ul> v-model="requestorIsAnonymous"
</div> class="me-2"
/>
{{ $t("requestor.is_anonymous") }}
</label>
<confidential
v-if="accompanyingCourse.requestor.type === 'thirdparty'"
>
<template #confidential-content>
<third-party-render-box
:thirdparty="accompanyingCourse.requestor"
:options="{
addLink: false,
addId: false,
addEntity: true,
addInfo: false,
hLevel: 3,
isMultiline: true,
isConfidential: true,
}"
>
<template #record-actions>
<ul class="record_actions">
<li>
<on-the-fly
:type="
accompanyingCourse.requestor.type
"
:id="accompanyingCourse.requestor.id"
action="show"
/>
</li>
<li>
<on-the-fly
:type="
accompanyingCourse.requestor.type
"
:id="accompanyingCourse.requestor.id"
action="edit"
@save-form-on-the-fly="saveFormOnTheFly"
ref="onTheFly"
/>
</li>
</ul>
</template>
</third-party-render-box>
</template>
</confidential>
<div> <confidential
<ul class="record_actions"> v-else-if="accompanyingCourse.requestor.type === 'person'"
<li class="add-persons"> >
<add-persons <template #confidential-content>
v-if="accompanyingCourse.requestor === null" <person-render-box
button-title="requestor.add_requestor" render="bloc"
modal-title="requestor.add_requestor" :person="accompanyingCourse.requestor"
:key="addPersons.key" :options="{
:options="addPersons.options" addLink: false,
@add-new-persons="addNewPersons" addId: false,
ref="addPersons" addAltNames: false,
> addEntity: true,
<!-- to cast child method --> addInfo: true,
</add-persons> hLevel: 3,
</li> isMultiline: true,
</ul> isConfidential: false,
addAge: true,
}"
>
<template #record-actions>
<ul class="record_actions">
<li>
<on-the-fly
:type="
accompanyingCourse.requestor.type
"
:id="accompanyingCourse.requestor.id"
action="show"
/>
</li>
<li>
<on-the-fly
:type="
accompanyingCourse.requestor.type
"
:id="accompanyingCourse.requestor.id"
action="edit"
@save-form-on-the-fly="saveFormOnTheFly"
ref="onTheFly"
/>
</li>
</ul>
</template>
</person-render-box>
</template>
</confidential>
<ul class="record_actions">
<li>
<button
class="btn btn-remove"
:title="$t('action.remove')"
@click="removeRequestor"
>
{{ $t("action.remove") }}
</button>
</li>
</ul>
</div>
<div
v-else-if="accompanyingCourse.requestor && !isAnonymous"
class="flex-table"
>
<label>
<input
type="checkbox"
v-model="requestorIsAnonymous"
class="me-2"
/>
{{ $t("requestor.is_anonymous") }}
</label>
<third-party-render-box
v-if="accompanyingCourse.requestor.type === 'thirdparty'"
:thirdparty="accompanyingCourse.requestor"
:options="{
addLink: false,
addId: false,
addEntity: true,
addInfo: false,
hLevel: 3,
isMultiline: true,
isConfidential: true,
}"
>
<template #record-actions>
<ul class="record_actions">
<li>
<on-the-fly
:type="accompanyingCourse.requestor.type"
:id="accompanyingCourse.requestor.id"
action="show"
/>
</li>
<li>
<on-the-fly
:type="accompanyingCourse.requestor.type"
:id="accompanyingCourse.requestor.id"
action="edit"
@save-form-on-the-fly="saveFormOnTheFly"
ref="onTheFly"
/>
</li>
</ul>
</template>
</third-party-render-box>
<person-render-box
render="bloc"
v-if="accompanyingCourse.requestor.type === 'person'"
:person="accompanyingCourse.requestor"
:options="{
addLink: false,
addId: false,
addAltNames: false,
addEntity: true,
addInfo: true,
hLevel: 3,
isMultiline: true,
isConfidential: false,
}"
>
<template #record-actions>
<ul class="record_actions">
<li>
<on-the-fly
:type="accompanyingCourse.requestor.type"
:id="accompanyingCourse.requestor.id"
action="show"
/>
</li>
<li>
<on-the-fly
:type="accompanyingCourse.requestor.type"
:id="accompanyingCourse.requestor.id"
action="edit"
@save-form-on-the-fly="saveFormOnTheFly"
ref="onTheFly"
/>
</li>
</ul>
</template>
</person-render-box>
<ul class="record_actions">
<li>
<button
class="btn btn-remove"
:title="$t('action.remove')"
@click="removeRequestor"
>
{{ $t("action.remove") }}
</button>
</li>
</ul>
</div>
<div v-else>
<label class="chill-no-data-statement">{{
$t("requestor.counter")
}}</label>
</div>
<div
v-if="
accompanyingCourse.requestor === null &&
suggestedEntities.length > 0
"
>
<ul class="list-suggest add-items inline">
<li
v-for="p in suggestedEntities"
:key="uniqueId(p)"
@click="addSuggestedEntity(p)"
>
<person-text v-if="p.type === 'person'" :person="p" />
<span v-else>{{ p.text }}</span>
</li>
</ul>
</div>
<div>
<ul class="record_actions">
<li class="add-persons">
<add-persons
v-if="accompanyingCourse.requestor === null"
button-title="requestor.add_requestor"
modal-title="requestor.add_requestor"
:key="addPersons.key"
:options="addPersons.options"
@add-new-persons="addNewPersons"
ref="addPersons"
>
<!-- to cast child method -->
</add-persons>
</li>
</ul>
</div>
</div> </div>
</div>
</template> </template>
<script> <script>
@@ -247,208 +271,221 @@ import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
import PersonText from "ChillPersonAssets/vuejs/_components/Entity/PersonText.vue"; import PersonText from "ChillPersonAssets/vuejs/_components/Entity/PersonText.vue";
export default { export default {
name: "Requestor", name: "Requestor",
components: { components: {
AddPersons, AddPersons,
OnTheFly, OnTheFly,
PersonRenderBox, PersonRenderBox,
ThirdPartyRenderBox, ThirdPartyRenderBox,
Confidential, Confidential,
PersonText, PersonText,
}, },
props: ["isAnonymous"], props: ["isAnonymous"],
data() { data() {
return { return {
addPersons: { addPersons: {
key: "requestor", key: "requestor",
options: { options: {
type: ["person", "thirdparty"], type: ["person", "thirdparty"],
priority: null, priority: null,
uniq: true, uniq: true,
},
},
};
},
computed: {
...mapState({
suggestedEntities: (state) => {
return (
[
...state.accompanyingCourse.participations
.filter((p) => p.endDate === null)
.map((p) => p.person),
...state.accompanyingCourse.resources.map(
(r) => r.resource,
),
]
.filter((e) => e !== null)
// filter for same entity appearing twice
.filter((e, index, suggested) => {
for (let i = 0; i < suggested.length; i = i + 1) {
if (
i < index &&
e.id === suggested[i].id &&
e.type === suggested[i].type
) {
return false;
}
}
return true;
})
);
},
}),
accompanyingCourse() {
return this.$store.state.accompanyingCourse;
}, },
}, requestorIsAnonymous: {
}; set(value) {
}, this.$store.dispatch("requestorIsAnonymous", value);
computed: { },
...mapState({ get() {
suggestedEntities: (state) => { return this.$store.state.accompanyingCourse.requestorAnonymous;
return ( },
[ },
...state.accompanyingCourse.participations },
.filter((p) => p.endDate === null) methods: {
.map((p) => p.person), removeRequestor() {
...state.accompanyingCourse.resources.map((r) => r.resource), //console.log('@@ CLICK remove requestor: item');
] this.$store
.filter((e) => e !== null) .dispatch("removeRequestor")
// filter for same entity appearing twice .catch(({ name, violations }) => {
.filter((e, index, suggested) => { if (
for (let i = 0; i < suggested.length; i = i + 1) { name === "ValidationException" ||
if ( name === "AccessException"
i < index && ) {
e.id === suggested[i].id && violations.forEach((violation) =>
e.type === suggested[i].type this.$toast.open({ message: violation }),
) { );
return false; } else {
this.$toast.open({ message: "An error occurred" });
}
});
},
addNewPersons({ selected, modal }) {
//console.log('@@@ CLICK button addNewPersons', selected);
this.$store
.dispatch("addRequestor", selected.shift())
.catch(({ name, violations }) => {
if (
name === "ValidationException" ||
name === "AccessException"
) {
violations.forEach((violation) =>
this.$toast.open({ message: violation }),
);
} else {
this.$toast.open({ message: "An error occurred" });
}
});
this.$refs.addPersons.resetSearch(); // to cast child method
modal.showModal = false;
},
saveFormOnTheFly(payload) {
console.log(
"saveFormOnTheFly: type",
payload.type,
", data",
payload.data,
);
payload.target = "requestor";
let body = { type: payload.type };
if (payload.type === "person") {
body.firstName = payload.data.firstName;
body.lastName = payload.data.lastName;
if (payload.data.birthdate !== null) {
body.birthdate = payload.data.birthdate;
} }
} body.phonenumber = payload.data.phonenumber;
body.mobilenumber = payload.data.mobilenumber;
body.email = payload.data.email;
body.altNames = payload.data.altNames;
body.gender = payload.data.gender;
return true; makeFetch(
}) "PATCH",
); `/api/1.0/person/person/${payload.data.id}.json`,
}, body,
}), )
accompanyingCourse() { .then((response) => {
return this.$store.state.accompanyingCourse; this.$store.dispatch("addPerson", {
}, target: payload.target,
requestorIsAnonymous: { body: response,
set(value) { });
this.$store.dispatch("requestorIsAnonymous", value); this.$refs.onTheFly.closeModal();
}, })
get() { .catch((error) => {
return this.$store.state.accompanyingCourse.requestorAnonymous; if (error.name === "ValidationException") {
}, for (let v of error.violations) {
}, this.$toast.open({ message: v });
}, }
methods: { } else {
removeRequestor() { this.$toast.open({ message: "An error occurred" });
//console.log('@@ CLICK remove requestor: item'); }
this.$store.dispatch("removeRequestor").catch(({ name, violations }) => { });
if (name === "ValidationException" || name === "AccessException") { } else if (payload.type === "thirdparty") {
violations.forEach((violation) => body.name = payload.data.text;
this.$toast.open({ message: violation }), body.email = payload.data.email;
); body.telephone = payload.data.telephone;
} else { body.telephone2 = payload.data.telephone2;
this.$toast.open({ message: "An error occurred" }); if (payload.data.address) {
} body.address = { id: payload.data.address.address_id };
}); }
},
addNewPersons({ selected, modal }) {
//console.log('@@@ CLICK button addNewPersons', selected);
this.$store
.dispatch("addRequestor", selected.shift())
.catch(({ name, violations }) => {
if (name === "ValidationException" || name === "AccessException") {
violations.forEach((violation) =>
this.$toast.open({ message: violation }),
);
} else {
this.$toast.open({ message: "An error occurred" });
}
});
this.$refs.addPersons.resetSearch(); // to cast child method makeFetch(
modal.showModal = false; "PATCH",
}, `/api/1.0/thirdparty/thirdparty/${payload.data.id}.json`,
saveFormOnTheFly(payload) { body,
console.log( )
"saveFormOnTheFly: type", .then((response) => {
payload.type, this.$store.dispatch("addThirdparty", {
", data", target: payload.target,
payload.data, body: response,
); });
payload.target = "requestor"; this.$refs.onTheFly.closeModal();
})
let body = { type: payload.type }; .catch((error) => {
if (payload.type === "person") { if (error.name === "ValidationException") {
body.firstName = payload.data.firstName; for (let v of error.violations) {
body.lastName = payload.data.lastName; this.$toast.open({ message: v });
if (payload.data.birthdate !== null) { }
body.birthdate = payload.data.birthdate; } else {
} this.$toast.open({ message: "An error occurred" });
body.phonenumber = payload.data.phonenumber; }
body.mobilenumber = payload.data.mobilenumber; });
body.email = payload.data.email;
body.altNames = payload.data.altNames;
body.gender = payload.data.gender;
makeFetch(
"PATCH",
`/api/1.0/person/person/${payload.data.id}.json`,
body,
)
.then((response) => {
this.$store.dispatch("addPerson", {
target: payload.target,
body: response,
});
this.$refs.onTheFly.closeModal();
})
.catch((error) => {
if (error.name === "ValidationException") {
for (let v of error.violations) {
this.$toast.open({ message: v });
}
} else {
this.$toast.open({ message: "An error occurred" });
} }
}); },
} else if (payload.type === "thirdparty") { addSuggestedEntity(e) {
body.name = payload.data.text; this.$store
body.email = payload.data.email; .dispatch("addRequestor", { result: e, type: e.type })
body.telephone = payload.data.telephone; .catch(({ name, violations }) => {
body.telephone2 = payload.data.telephone2; if (
if (payload.data.address) { name === "ValidationException" ||
body.address = { id: payload.data.address.address_id }; name === "AccessException"
} ) {
violations.forEach((violation) =>
makeFetch( this.$toast.open({ message: violation }),
"PATCH", );
`/api/1.0/thirdparty/thirdparty/${payload.data.id}.json`, } else {
body, this.$toast.open({ message: "An error occurred" });
) }
.then((response) => { });
this.$store.dispatch("addThirdparty", { },
target: payload.target, uniqueId(e) {
body: response, return `${e.type}-${e.id}`;
}); },
this.$refs.onTheFly.closeModal();
})
.catch((error) => {
if (error.name === "ValidationException") {
for (let v of error.violations) {
this.$toast.open({ message: v });
}
} else {
this.$toast.open({ message: "An error occurred" });
}
});
}
}, },
addSuggestedEntity(e) {
this.$store
.dispatch("addRequestor", { result: e, type: e.type })
.catch(({ name, violations }) => {
if (name === "ValidationException" || name === "AccessException") {
violations.forEach((violation) =>
this.$toast.open({ message: violation }),
);
} else {
this.$toast.open({ message: "An error occurred" });
}
});
},
uniqueId(e) {
return `${e.type}-${e.id}`;
},
},
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
div.flex-table { div.flex-table {
margin: 1em 0 0 !important; margin: 1em 0 0 !important;
& > label, & > label,
& > ul.record_actions { & > ul.record_actions {
margin: 1em 3em 0 !important; margin: 1em 3em 0 !important;
} }
div.item-bloc { div.item-bloc {
background-color: white !important; background-color: white !important;
margin-top: 1em; margin-top: 1em;
} }
} }
.confidential { .confidential {
display: block; display: block;
margin-right: 0px !important; margin-right: 0px !important;
} }
</style> </style>

View File

@@ -1,57 +1,57 @@
<template> <template>
<div class="vue-component"> <div class="vue-component">
<h2><a id="section-90" />{{ $t("resources.title") }}</h2> <h2><a id="section-90" />{{ $t("resources.title") }}</h2>
<div v-if="resources.length > 0"> <div v-if="resources.length > 0">
<label class="col-form-label">{{ <label class="col-form-label">{{
$tc("resources.counter", counter) $tc("resources.counter", counter)
}}</label> }}</label>
</div> </div>
<div v-else> <div v-else>
<label class="chill-no-data-statement">{{ <label class="chill-no-data-statement">{{
$tc("resources.counter", counter) $tc("resources.counter", counter)
}}</label> }}</label>
</div> </div>
<div class="flex-table mb-3"> <div class="flex-table mb-3">
<resource-item <resource-item
v-for="resource in resources" v-for="resource in resources"
:resource="resource" :resource="resource"
:key="resource.id" :key="resource.id"
@remove="removeResource" @remove="removeResource"
/> />
</div> </div>
<div v-if="suggestedEntities.length > 0"> <div v-if="suggestedEntities.length > 0">
<ul class="list-suggest add-items inline"> <ul class="list-suggest add-items inline">
<li <li
v-for="p in suggestedEntities" v-for="p in suggestedEntities"
:key="uniqueId(p)" :key="uniqueId(p)"
@click="addSuggestedEntity(p)" @click="addSuggestedEntity(p)"
> >
<person-text v-if="p.type === 'person'" :person="p" /> <person-text v-if="p.type === 'person'" :person="p" />
<span v-else>{{ p.text }}</span> <span v-else>{{ p.text }}</span>
</li> </li>
</ul> </ul>
</div> </div>
<div> <div>
<ul class="record_actions"> <ul class="record_actions">
<li class="add-persons"> <li class="add-persons">
<add-persons <add-persons
button-title="resources.add_resources" button-title="resources.add_resources"
modal-title="resources.add_resources" modal-title="resources.add_resources"
:key="addPersons.key" :key="addPersons.key"
:options="addPersons.options" :options="addPersons.options"
@add-new-persons="addNewPersons" @add-new-persons="addNewPersons"
ref="addPersons" ref="addPersons"
> >
<!-- to cast child method --> <!-- to cast child method -->
</add-persons> </add-persons>
</li> </li>
</ul> </ul>
</div>
</div> </div>
</div>
</template> </template>
<script> <script>
@@ -61,117 +61,126 @@ import ResourceItem from "./Resources/ResourceItem.vue";
import PersonText from "ChillPersonAssets/vuejs/_components/Entity/PersonText.vue"; import PersonText from "ChillPersonAssets/vuejs/_components/Entity/PersonText.vue";
export default { export default {
name: "Resources", name: "Resources",
components: { components: {
AddPersons, AddPersons,
ResourceItem, ResourceItem,
PersonText, PersonText,
}, },
data() { data() {
return { return {
addPersons: { addPersons: {
key: "resources", key: "resources",
options: { options: {
type: ["person", "thirdparty"], type: ["person", "thirdparty"],
priority: null, priority: null,
uniq: false, uniq: false,
}, },
}, },
}; };
}, },
computed: mapState({ computed: mapState({
resources: (state) => state.accompanyingCourse.resources, resources: (state) => state.accompanyingCourse.resources,
counter: (state) => state.accompanyingCourse.resources.length, counter: (state) => state.accompanyingCourse.resources.length,
suggestedEntities: (state) => suggestedEntities: (state) =>
[ [
state.accompanyingCourse.requestor, state.accompanyingCourse.requestor,
...state.accompanyingCourse.participations ...state.accompanyingCourse.participations
.filter((p) => p.endDate === null) .filter((p) => p.endDate === null)
.map((p) => p.person), .map((p) => p.person),
] ]
.filter((e) => e !== null) .filter((e) => e !== null)
.filter((e) => { .filter((e) => {
if (e.type === "person") { if (e.type === "person") {
return !state.accompanyingCourse.resources return !state.accompanyingCourse.resources
.filter((r) => r.resource.type === "person") .filter((r) => r.resource.type === "person")
.map((r) => r.resource.id) .map((r) => r.resource.id)
.includes(e.id); .includes(e.id);
} }
if (e.type === "thirdparty") { if (e.type === "thirdparty") {
return !state.accompanyingCourse.resources return !state.accompanyingCourse.resources
.filter((r) => r.resource.type === "thirdparty") .filter((r) => r.resource.type === "thirdparty")
.map((r) => r.resource.id) .map((r) => r.resource.id)
.includes(e.id); .includes(e.id);
} }
}) })
// filter persons appearing twice in requestor and resources // filter persons appearing twice in requestor and resources
.filter((e, index, suggested) => { .filter((e, index, suggested) => {
for (let i = 0; i < suggested.length; i = i + 1) { for (let i = 0; i < suggested.length; i = i + 1) {
if (i < index && e.id === suggested[i].id) { if (i < index && e.id === suggested[i].id) {
return false; return false;
} }
} }
return true; return true;
}), }),
}), }),
methods: { methods: {
removeResource(item) { removeResource(item) {
//console.log('@@ CLICK remove resource: item', item); //console.log('@@ CLICK remove resource: item', item);
this.$store this.$store
.dispatch("removeResource", item) .dispatch("removeResource", item)
.catch(({ name, violations }) => { .catch(({ name, violations }) => {
if (name === "ValidationException" || name === "AccessException") { if (
violations.forEach((violation) => name === "ValidationException" ||
this.$toast.open({ message: violation }), name === "AccessException"
); ) {
} else { violations.forEach((violation) =>
this.$toast.open({ message: "An error occurred" }); this.$toast.open({ message: violation }),
} );
}); } else {
this.$toast.open({ message: "An error occurred" });
}
});
},
addNewPersons({ selected, modal }) {
//console.log('@@@ CLICK button addNewPersons', selected);
selected.forEach(function (item) {
this.$store
.dispatch("addResource", item)
.catch(({ name, violations }) => {
if (
name === "ValidationException" ||
name === "AccessException"
) {
violations.forEach((violation) =>
this.$toast.open({ message: violation }),
);
} else {
this.$toast.open({ message: violations });
}
});
}, this);
this.$refs.addPersons.resetSearch(); // to cast child method
modal.showModal = false;
},
addSuggestedEntity(e) {
this.$store
.dispatch("addResource", { result: e, type: e.type })
.catch(({ name, violations }) => {
if (
name === "ValidationException" ||
name === "AccessException"
) {
violations.forEach((violation) =>
this.$toast.open({ message: violation }),
);
} else {
this.$toast.open({ message: "An error occurred" });
}
});
},
uniqueId(e) {
return `${e.type}-${e.id}`;
},
}, },
addNewPersons({ selected, modal }) {
//console.log('@@@ CLICK button addNewPersons', selected);
selected.forEach(function (item) {
this.$store
.dispatch("addResource", item)
.catch(({ name, violations }) => {
if (name === "ValidationException" || name === "AccessException") {
violations.forEach((violation) =>
this.$toast.open({ message: violation }),
);
} else {
this.$toast.open({ message: violations });
}
});
}, this);
this.$refs.addPersons.resetSearch(); // to cast child method
modal.showModal = false;
},
addSuggestedEntity(e) {
this.$store
.dispatch("addResource", { result: e, type: e.type })
.catch(({ name, violations }) => {
if (name === "ValidationException" || name === "AccessException") {
violations.forEach((violation) =>
this.$toast.open({ message: violation }),
);
} else {
this.$toast.open({ message: "An error occurred" });
}
});
},
uniqueId(e) {
return `${e.type}-${e.id}`;
},
},
}; };
</script> </script>
<style lang="scss"> <style lang="scss">
div.flex-bloc { div.flex-bloc {
div.item-bloc { div.item-bloc {
flex-basis: 50%; flex-basis: 50%;
} }
} }
</style> </style>

View File

@@ -1,107 +1,107 @@
<template> <template>
<person-render-box <person-render-box
render="bloc" render="bloc"
v-if="resource.resource.type === 'person'" v-if="resource.resource.type === 'person'"
:person="resource.resource" :person="resource.resource"
:options="{ :options="{
addInfo: true, addInfo: true,
addId: false, addId: false,
addEntity: true, addEntity: true,
addLink: false, addLink: false,
addAltNames: true, addAltNames: true,
addAge: false, addAge: false,
hLevel: 3, hLevel: 3,
isConfidential: true, isConfidential: true,
}" }"
> >
<template #end-bloc> <template #end-bloc>
<div class="item-row separator"> <div class="item-row separator">
<ul class="record_actions"> <ul class="record_actions">
<li> <li>
<write-comment <write-comment
:resource="resource" :resource="resource"
@update-comment="updateComment" @update-comment="updateComment"
/> />
</li> </li>
<li> <li>
<on-the-fly <on-the-fly
:parent="parent" :parent="parent"
:type="resource.resource.type" :type="resource.resource.type"
:id="resource.resource.id" :id="resource.resource.id"
action="show" action="show"
/> />
</li> </li>
<li> <li>
<on-the-fly <on-the-fly
:parent="parent" :parent="parent"
:type="resource.resource.type" :type="resource.resource.type"
:id="resource.resource.id" :id="resource.resource.id"
action="edit" action="edit"
@save-form-on-the-fly="saveFormOnTheFly" @save-form-on-the-fly="saveFormOnTheFly"
ref="onTheFly" ref="onTheFly"
/> />
</li> </li>
<li> <li>
<button <button
class="btn btn-sm btn-remove" class="btn btn-sm btn-remove"
:title="$t('action.remove')" :title="$t('action.remove')"
@click.prevent="$emit('remove', resource)" @click.prevent="$emit('remove', resource)"
/> />
</li> </li>
</ul> </ul>
</div> </div>
</template> </template>
</person-render-box> </person-render-box>
<third-party-render-box <third-party-render-box
v-if="resource.resource.type === 'thirdparty'" v-if="resource.resource.type === 'thirdparty'"
:thirdparty="resource.resource" :thirdparty="resource.resource"
:options="{ :options="{
addLink: false, addLink: false,
addId: false, addId: false,
addEntity: true, addEntity: true,
addInfo: false, addInfo: false,
hLevel: 3, hLevel: 3,
}" }"
> >
<template #end-bloc> <template #end-bloc>
<div class="item-row separator"> <div class="item-row separator">
<ul class="record_actions"> <ul class="record_actions">
<li> <li>
<write-comment <write-comment
:resource="resource" :resource="resource"
@update-comment="updateComment" @update-comment="updateComment"
/> />
</li> </li>
<li> <li>
<on-the-fly <on-the-fly
:parent="parent" :parent="parent"
:type="resource.resource.type" :type="resource.resource.type"
:id="resource.resource.id" :id="resource.resource.id"
action="show" action="show"
/> />
</li> </li>
<li> <li>
<on-the-fly <on-the-fly
:parent="parent" :parent="parent"
:type="resource.resource.type" :type="resource.resource.type"
:id="resource.resource.id" :id="resource.resource.id"
action="edit" action="edit"
@save-form-on-the-fly="saveFormOnTheFly" @save-form-on-the-fly="saveFormOnTheFly"
ref="onTheFly" ref="onTheFly"
/> />
</li> </li>
<li> <li>
<button <button
class="btn btn-sm btn-remove" class="btn btn-sm btn-remove"
:title="$t('action.remove')" :title="$t('action.remove')"
@click.prevent="$emit('remove', resource)" @click.prevent="$emit('remove', resource)"
/> />
</li> </li>
</ul> </ul>
</div> </div>
</template> </template>
</third-party-render-box> </third-party-render-box>
</template> </template>
<script> <script>
@@ -112,130 +112,130 @@ import WriteComment from "./WriteComment";
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods"; import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
export default { export default {
name: "ResourceItem", name: "ResourceItem",
components: { components: {
OnTheFly, OnTheFly,
PersonRenderBox, PersonRenderBox,
ThirdPartyRenderBox, ThirdPartyRenderBox,
WriteComment, WriteComment,
}, },
props: ["resource"], props: ["resource"],
emits: ["remove"], emits: ["remove"],
computed: { computed: {
parent() { parent() {
return { return {
type: this.resource.type, type: this.resource.type,
id: this.resource.id, id: this.resource.id,
comment: this.resource.comment, comment: this.resource.comment,
parent: { parent: {
type: this.$store.state.accompanyingCourse.type, type: this.$store.state.accompanyingCourse.type,
id: this.$store.state.accompanyingCourse.id, id: this.$store.state.accompanyingCourse.id,
},
};
}, },
}; hasCurrentHouseholdAddress() {
}, if (this.resource.resource.current_household_address !== null) {
hasCurrentHouseholdAddress() { return true;
if (this.resource.resource.current_household_address !== null) {
return true;
}
return false;
},
},
methods: {
saveFormOnTheFly(payload) {
// console.log('saveFormOnTheFly: type', payload.type, ', data', payload.data);
payload.target = "resource";
let body = { type: payload.type };
if (payload.type === "person") {
body.firstName = payload.data.firstName;
body.lastName = payload.data.lastName;
if (payload.data.birthdate !== null) {
body.birthdate = payload.data.birthdate;
}
body.phonenumber = payload.data.phonenumber;
body.mobilenumber = payload.data.mobilenumber;
body.email = payload.data.email;
body.altNames = payload.data.altNames;
body.gender = {
id: payload.data.gender.id,
type: payload.data.gender.type,
};
if (payload.data.civility !== null) {
body.civility = {
id: payload.data.civility.id,
type: payload.data.civility.type,
};
}
makeFetch(
"PATCH",
`/api/1.0/person/person/${payload.data.id}.json`,
body,
)
.then((response) => {
this.$store.dispatch("addPerson", {
target: payload.target,
body: response,
});
this.$refs.onTheFly.closeModal();
})
.catch((error) => {
if (error.name === "ValidationException") {
for (let v of error.violations) {
this.$toast.open({ message: v });
}
} else {
this.$toast.open({ message: "An error occurred" });
} }
}); return false;
} else if (payload.type === "thirdparty") { },
// console.log('data', payload.data) },
body.firstname = payload.data.firstname; methods: {
body.name = payload.data.name; saveFormOnTheFly(payload) {
body.email = payload.data.email; // console.log('saveFormOnTheFly: type', payload.type, ', data', payload.data);
body.telephone = payload.data.telephone; payload.target = "resource";
body.telephone2 = payload.data.telephone2;
body.address = payload.data.address
? { id: payload.data.address.address_id }
: null;
if (null !== payload.data.civility) {
body.civility = {
type: "chill_main_civility",
id: payload.data.civility.id,
};
}
if (null !== payload.data.profession) {
body.profession = payload.data.profession;
}
// console.log('body', body);
makeFetch( let body = { type: payload.type };
"PATCH", if (payload.type === "person") {
`/api/1.0/thirdparty/thirdparty/${payload.data.id}.json`, body.firstName = payload.data.firstName;
body, body.lastName = payload.data.lastName;
) if (payload.data.birthdate !== null) {
.then((response) => { body.birthdate = payload.data.birthdate;
this.$store.dispatch("addThirdparty", { }
target: payload.target, body.phonenumber = payload.data.phonenumber;
body: response, body.mobilenumber = payload.data.mobilenumber;
}); body.email = payload.data.email;
this.$refs.onTheFly.closeModal(); body.altNames = payload.data.altNames;
}) body.gender = {
.catch((error) => { id: payload.data.gender.id,
if (error.name === "ValidationException") { type: payload.data.gender.type,
for (let v of error.violations) { };
this.$toast.open({ message: v }); if (payload.data.civility !== null) {
} body.civility = {
} else { id: payload.data.civility.id,
this.$toast.open({ message: "An error occurred" }); type: payload.data.civility.type,
};
}
makeFetch(
"PATCH",
`/api/1.0/person/person/${payload.data.id}.json`,
body,
)
.then((response) => {
this.$store.dispatch("addPerson", {
target: payload.target,
body: response,
});
this.$refs.onTheFly.closeModal();
})
.catch((error) => {
if (error.name === "ValidationException") {
for (let v of error.violations) {
this.$toast.open({ message: v });
}
} else {
this.$toast.open({ message: "An error occurred" });
}
});
} else if (payload.type === "thirdparty") {
// console.log('data', payload.data)
body.firstname = payload.data.firstname;
body.name = payload.data.name;
body.email = payload.data.email;
body.telephone = payload.data.telephone;
body.telephone2 = payload.data.telephone2;
body.address = payload.data.address
? { id: payload.data.address.address_id }
: null;
if (null !== payload.data.civility) {
body.civility = {
type: "chill_main_civility",
id: payload.data.civility.id,
};
}
if (null !== payload.data.profession) {
body.profession = payload.data.profession;
}
// console.log('body', body);
makeFetch(
"PATCH",
`/api/1.0/thirdparty/thirdparty/${payload.data.id}.json`,
body,
)
.then((response) => {
this.$store.dispatch("addThirdparty", {
target: payload.target,
body: response,
});
this.$refs.onTheFly.closeModal();
})
.catch((error) => {
if (error.name === "ValidationException") {
for (let v of error.violations) {
this.$toast.open({ message: v });
}
} else {
this.$toast.open({ message: "An error occurred" });
}
});
} }
}); },
} updateComment(resource) {
console.log("updateComment", resource);
this.$store.commit("updateResource", resource);
},
}, },
updateComment(resource) {
console.log("updateComment", resource);
this.$store.commit("updateResource", resource);
},
},
}; };
</script> </script>

View File

@@ -1,32 +1,34 @@
<template> <template>
<a <a
class="btn btn-sm btn-misc change-icon" class="btn btn-sm btn-misc change-icon"
:title="$t('write_comment')" :title="$t('write_comment')"
@click="openModal" @click="openModal"
><i class="fa fa-pencil-square-o" /> ><i class="fa fa-pencil-square-o" />
</a> </a>
<teleport to="body"> <teleport to="body">
<modal <modal
v-if="modal.showModal" v-if="modal.showModal"
:modal-dialog-class="modal.modalDialogClass" :modal-dialog-class="modal.modalDialogClass"
@close="modal.showModal = false" @close="modal.showModal = false"
> >
<template #header> <template #header>
<h3 class="modal-title"> <h3 class="modal-title">
{{ $t("write_comment_about", { r: resource.resource.text }) }} {{
</h3> $t("write_comment_about", { r: resource.resource.text })
</template> }}
<template #body> </h3>
<comment-editor v-model="content" /> </template>
</template> <template #body>
<template #footer> <comment-editor v-model="content" />
<a class="btn btn-save" @click="saveAction"> </template>
{{ $t("action.save") }} <template #footer>
</a> <a class="btn btn-save" @click="saveAction">
</template> {{ $t("action.save") }}
</modal> </a>
</teleport> </template>
</modal>
</teleport>
</template> </template>
<script> <script>
@@ -35,71 +37,71 @@ import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
import CommentEditor from "ChillMainAssets/vuejs/_components/CommentEditor/CommentEditor.vue"; import CommentEditor from "ChillMainAssets/vuejs/_components/CommentEditor/CommentEditor.vue";
export default { export default {
name: "WriteComment", name: "WriteComment",
components: { components: {
Modal, Modal,
CommentEditor, CommentEditor,
},
props: ["resource"],
emits: ["updateComment"],
data() {
return {
modal: {
showModal: false,
modalDialogClass: "modal-dialog-scrollable modal-xl",
},
formdata: {
content: this.resource.comment,
},
};
},
i18n: {
messages: {
fr: {
write_comment: "Écrire un commentaire",
write_comment_about: "Écrire un commentaire à propos de {r}",
comment_placeholder: "Commencez à écrire...",
},
}, },
}, props: ["resource"],
computed: { emits: ["updateComment"],
editor: () => ClassicEditor, data() {
editorConfig: () => classicEditorConfig, return {
content: { modal: {
set(value) { showModal: false,
this.formdata.content = value; modalDialogClass: "modal-dialog-scrollable modal-xl",
}, },
get() { formdata: {
return this.formdata.content; content: this.resource.comment,
}, },
},
},
methods: {
openModal() {
//console.log('write comment for', this.resource.resource.type, this.resource.resource.id);
this.modal.showModal = true;
},
saveAction() {
//console.log('save comment', this.resource.id, this.formdata.content);
this.patchResource(this.resource.id, this.formdata.content);
},
patchResource(id, comment) {
let url = `/api/1.0/person/accompanying-period/resource/${id}.json`,
body = {
type: "accompanying_period_resource",
comment: comment,
}; };
makeFetch("PATCH", url, body).then((r) => {
let resource = {
type: "accompanying_period_resource",
id: r.id,
comment: r.comment,
resource: r.resource,
};
this.$emit("updateComment", resource);
this.modal.showModal = false;
});
}, },
}, i18n: {
messages: {
fr: {
write_comment: "Écrire un commentaire",
write_comment_about: "Écrire un commentaire à propos de {r}",
comment_placeholder: "Commencez à écrire...",
},
},
},
computed: {
editor: () => ClassicEditor,
editorConfig: () => classicEditorConfig,
content: {
set(value) {
this.formdata.content = value;
},
get() {
return this.formdata.content;
},
},
},
methods: {
openModal() {
//console.log('write comment for', this.resource.resource.type, this.resource.resource.id);
this.modal.showModal = true;
},
saveAction() {
//console.log('save comment', this.resource.id, this.formdata.content);
this.patchResource(this.resource.id, this.formdata.content);
},
patchResource(id, comment) {
let url = `/api/1.0/person/accompanying-period/resource/${id}.json`,
body = {
type: "accompanying_period_resource",
comment: comment,
};
makeFetch("PATCH", url, body).then((r) => {
let resource = {
type: "accompanying_period_resource",
id: r.id,
comment: r.comment,
resource: r.resource,
};
this.$emit("updateComment", resource);
this.modal.showModal = false;
});
},
},
}; };
</script> </script>

View File

@@ -1,25 +1,25 @@
<template> <template>
<div class="vue-component"> <div class="vue-component">
<h2><a id="section-70" />{{ $t("scopes.title") }}</h2> <h2><a id="section-70" />{{ $t("scopes.title") }}</h2>
<div class="mb-4"> <div class="mb-4">
<div class="form-check" v-for="s in scopes" :key="s.id"> <div class="form-check" v-for="s in scopes" :key="s.id">
<input <input
class="form-check-input" class="form-check-input"
type="checkbox" type="checkbox"
v-model="checkedScopes" v-model="checkedScopes"
:value="s" :value="s"
/> />
<label class="form-check-label"> <label class="form-check-label">
{{ localizeString(s.name) }} {{ localizeString(s.name) }}
</label> </label>
</div> </div>
</div> </div>
<div v-if="!isScopeValid" class="alert alert-warning to-confirm"> <div v-if="!isScopeValid" class="alert alert-warning to-confirm">
{{ $t("scopes.add_at_least_one") }} {{ $t("scopes.add_at_least_one") }}
</div>
</div> </div>
</div>
</template> </template>
<script> <script>
@@ -27,33 +27,38 @@ import { mapState, mapGetters } from "vuex";
import { localizeString } from "ChillMainAssets/lib/localizationHelper/localizationHelper"; import { localizeString } from "ChillMainAssets/lib/localizationHelper/localizationHelper";
export default { export default {
name: "Scopes", name: "Scopes",
computed: { computed: {
...mapState(["scopes", "scopesAtStart"]), ...mapState(["scopes", "scopesAtStart"]),
...mapGetters(["isScopeValid"]), ...mapGetters(["isScopeValid"]),
checkedScopes: { checkedScopes: {
get: function () { get: function () {
return this.$store.state.accompanyingCourse.scopes; return this.$store.state.accompanyingCourse.scopes;
}, },
set: function (v) { set: function (v) {
this.$store.dispatch("setScopes", v).catch(({ name, violations }) => { this.$store
if (name === "ValidationException" || name === "AccessException") { .dispatch("setScopes", v)
violations.forEach((violation) => .catch(({ name, violations }) => {
this.$toast.open({ message: violation }), if (
); name === "ValidationException" ||
} else { name === "AccessException"
this.$toast.open({ message: "An error occurred" }); ) {
} violations.forEach((violation) =>
}); this.$toast.open({ message: violation }),
}, );
} else {
this.$toast.open({ message: "An error occurred" });
}
});
},
},
}, },
}, methods: {
methods: { localizeString,
localizeString, restore() {
restore() { console.log("restore");
console.log("restore"); },
}, },
},
}; };
</script> </script>

View File

@@ -1,30 +1,30 @@
<template> <template>
<div class="vue-component"> <div class="vue-component">
<h2><a id="section-60" />{{ $t("social_issue.title") }}</h2> <h2><a id="section-60" />{{ $t("social_issue.title") }}</h2>
<div class="my-4"> <div class="my-4">
<!--label for="field">{{ $t('social_issue.label') }}</label <!--label for="field">{{ $t('social_issue.label') }}</label
--> -->
<VueMultiselect <VueMultiselect
name="field" name="field"
:close-on-select="true" :close-on-select="true"
:allow-empty="true" :allow-empty="true"
:show-labels="false" :show-labels="false"
track-by="id" track-by="id"
label="text" label="text"
:multiple="true" :multiple="true"
:searchable="true" :searchable="true"
:placeholder="$t('social_issue.label')" :placeholder="$t('social_issue.label')"
@update:model-value="updateSocialIssues" @update:model-value="updateSocialIssues"
:model-value="value" :model-value="value"
:options="options" :options="options"
/> />
</div> </div>
<div v-if="!isSocialIssueValid" class="alert alert-warning to-confirm"> <div v-if="!isSocialIssueValid" class="alert alert-warning to-confirm">
{{ $t("social_issue.not_valid") }} {{ $t("social_issue.not_valid") }}
</div>
</div> </div>
</div>
</template> </template>
<script> <script>
@@ -33,54 +33,59 @@ import { fetchResults } from "ChillMainAssets/lib/api/apiMethods";
import { mapGetters, mapState } from "vuex"; import { mapGetters, mapState } from "vuex";
export default { export default {
name: "SocialIssue", name: "SocialIssue",
components: { VueMultiselect }, components: { VueMultiselect },
data() { data() {
return { return {
options: [], options: [],
}; };
},
computed: {
...mapState({
value: (state) => state.accompanyingCourse.socialIssues,
}),
...mapGetters(["isSocialIssueValid"]),
},
mounted() {
this.getOptions();
},
methods: {
getOptions() {
fetchResults(`/api/1.0/person/social-work/social-issue.json`).then(
(response) => {
this.options = response;
},
);
}, },
updateSocialIssues(value) { computed: {
this.$store ...mapState({
.dispatch("updateSocialIssues", this.transformValue(value)) value: (state) => state.accompanyingCourse.socialIssues,
.catch(({ name, violations }) => { }),
if (name === "ValidationException" || name === "AccessException") { ...mapGetters(["isSocialIssueValid"]),
violations.forEach((violation) => },
this.$toast.open({ message: violation }), mounted() {
this.getOptions();
},
methods: {
getOptions() {
fetchResults(`/api/1.0/person/social-work/social-issue.json`).then(
(response) => {
this.options = response;
},
); );
} else { },
this.$toast.open({ message: "An error occurred" }); updateSocialIssues(value) {
} this.$store
}); .dispatch("updateSocialIssues", this.transformValue(value))
.catch(({ name, violations }) => {
if (
name === "ValidationException" ||
name === "AccessException"
) {
violations.forEach((violation) =>
this.$toast.open({ message: violation }),
);
} else {
this.$toast.open({ message: "An error occurred" });
}
});
},
transformValue(updated) {
let stored = this.value;
let added = updated.filter((x) => stored.indexOf(x) === -1).shift();
let removed = stored
.filter((x) => updated.indexOf(x) === -1)
.shift();
let method = typeof removed === "undefined" ? "POST" : "DELETE";
let changed = typeof removed === "undefined" ? added : removed;
let body = { type: "social_issue", id: changed.id };
let payload = updated;
return { payload, body, method };
},
}, },
transformValue(updated) {
let stored = this.value;
let added = updated.filter((x) => stored.indexOf(x) === -1).shift();
let removed = stored.filter((x) => updated.indexOf(x) === -1).shift();
let method = typeof removed === "undefined" ? "POST" : "DELETE";
let changed = typeof removed === "undefined" ? added : removed;
let body = { type: "social_issue", id: changed.id };
let payload = updated;
return { payload, body, method };
},
},
}; };
</script> </script>
@@ -91,20 +96,20 @@ export default {
@import "ChillPersonAssets/chill/scss/mixins"; @import "ChillPersonAssets/chill/scss/mixins";
@import "ChillMainAssets/chill/scss/chill_variables"; @import "ChillMainAssets/chill/scss/chill_variables";
div#accompanying-course { div#accompanying-course {
span.multiselect__tag { span.multiselect__tag {
@include badge_social($social-issue-color); @include badge_social($social-issue-color);
background: $chill-l-gray; background: $chill-l-gray;
color: $dark; color: $dark;
}
span.multiselect__option--highlight {
&::after {
background: $green;
} }
&.multiselect__option--selected { span.multiselect__option--highlight {
&::after { &::after {
background: $red; background: $green;
} }
&.multiselect__option--selected {
&::after {
background: $red;
}
}
} }
}
} }
</style> </style>

View File

@@ -1,22 +1,22 @@
<template> <template>
<div class="vue-component"> <div class="vue-component">
<h2> <h2>
<a id="section-110" /> <a id="section-110" />
{{ $t("startdate.change") }} {{ $t("startdate.change") }}
</h2> </h2>
<div> <div>
<div class="mb-3 row"> <div class="mb-3 row">
<div class="col-sm-12 date-update"> <div class="col-sm-12 date-update">
<input <input
class="form-control" class="form-control"
type="date" type="date"
id="startDate" id="startDate"
v-model="startDateInput" v-model="startDateInput"
/> />
</div>
</div>
</div> </div>
</div>
</div> </div>
</div>
</template> </template>
<script> <script>
@@ -24,56 +24,69 @@ import { dateToISO, ISOToDatetime } from "ChillMainAssets/chill/js/date";
import { mapState } from "vuex"; import { mapState } from "vuex";
export default { export default {
name: "StartDate", name: "StartDate",
data() { data() {
return { return {
lastRecordedDate: null, lastRecordedDate: null,
}; };
}, },
computed: { computed: {
...mapState({ ...mapState({
startDate: (state) => startDate: (state) =>
dateToISO(ISOToDatetime(state.accompanyingCourse.openingDate.datetime)), dateToISO(
}), ISOToDatetime(
startDateInput: { state.accompanyingCourse.openingDate.datetime,
get() { ),
return this.startDate; ),
}, }),
set(value) { startDateInput: {
this.lastRecordedDate = value; get() {
return this.startDate;
setTimeout(() => { },
console.log("timeout finished"); set(value) {
if (this.lastRecordedDate === value) { this.lastRecordedDate = value;
console.log("last recorded", this.lastRecordedDate, "value", value);
this.$store setTimeout(() => {
.dispatch("updateStartDate", value) console.log("timeout finished");
.catch(({ name, violations }) => { if (this.lastRecordedDate === value) {
if ( console.log(
name === "ValidationException" || "last recorded",
name === "AccessException" this.lastRecordedDate,
) { "value",
violations.forEach((violation) => value,
this.$toast.open({ message: violation }), );
); this.$store
} else { .dispatch("updateStartDate", value)
this.$toast.open({ message: "An error occurred" }); .catch(({ name, violations }) => {
} if (
}); name === "ValidationException" ||
} name === "AccessException"
}, 3000); ) {
}, violations.forEach((violation) =>
this.$toast.open({
message: violation,
}),
);
} else {
this.$toast.open({
message: "An error occurred",
});
}
});
}
}, 3000);
},
},
}, },
},
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.date-update { .date-update {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
&-btn { &-btn {
margin-left: 1rem; margin-left: 1rem;
} }
} }
</style> </style>

View File

@@ -1,195 +1,207 @@
<template> <template>
<teleport to="#content"> <teleport to="#content">
<div id="navmap"> <div id="navmap">
<nav> <nav>
<a class="top" href="#top"> <a class="top" href="#top">
<i class="fa fa-fw fa-square" /> <i class="fa fa-fw fa-square" />
<span>{{ $t("nav.top") }}</span> <span>{{ $t("nav.top") }}</span>
</a> </a>
<item v-for="item of items" :key="item.key" :item="item" :step="step" /> <item
</nav> v-for="item of items"
</div> :key="item.key"
</teleport> :item="item"
:step="step"
/>
</nav>
</div>
</teleport>
</template> </template>
<script> <script>
import Item from "./StickyNav/Item.vue"; import Item from "./StickyNav/Item.vue";
export default { export default {
name: "StickyNav", name: "StickyNav",
components: { components: {
Item, Item,
},
data() {
return {
header: document.querySelector("header nav.navbar"),
bannerName: document.querySelector("#header-accompanying_course-name"),
bannerDetails: document.querySelector(
"#header-accompanying_course-details",
),
container: null,
heightSum: null,
stickyNav: null,
limit: 25,
anchors: null,
items: [],
};
},
computed: {
accompanyingCourse() {
return this.$store.state.accompanyingCourse;
}, },
step() { data() {
return this.accompanyingCourse.step; return {
header: document.querySelector("header nav.navbar"),
bannerName: document.querySelector(
"#header-accompanying_course-name",
),
bannerDetails: document.querySelector(
"#header-accompanying_course-details",
),
container: null,
heightSum: null,
stickyNav: null,
limit: 25,
anchors: null,
items: [],
};
}, },
top() { computed: {
return parseInt( accompanyingCourse() {
window return this.$store.state.accompanyingCourse;
.getComputedStyle(this.stickyNav) },
.getPropertyValue("top") step() {
.slice(0, -2), return this.accompanyingCourse.step;
); },
top() {
return parseInt(
window
.getComputedStyle(this.stickyNav)
.getPropertyValue("top")
.slice(0, -2),
);
},
}, },
}, mounted() {
mounted() { this.ready();
this.ready(); window.addEventListener("scroll", this.handleScroll);
window.addEventListener("scroll", this.handleScroll); },
}, unmounted() {
unmounted() { window.removeEventListener("scroll", this.handleScroll);
window.removeEventListener("scroll", this.handleScroll); },
}, methods: {
methods: { ready() {
ready() { // load datas DOM when mounted ready
// load datas DOM when mounted ready this.container = document.querySelector("#content");
this.container = document.querySelector("#content"); this.stickyNav = document.querySelector("#navmap");
this.stickyNav = document.querySelector("#navmap"); this.anchors = document.querySelectorAll("h2 a[id^='section']");
this.anchors = document.querySelectorAll("h2 a[id^='section']"); this.initItemsMap();
this.initItemsMap();
// TODO resizeObserver not supports IE ! // TODO resizeObserver not supports IE !
// Listen when elements change size, then recalculate heightSum and initItemsMap // Listen when elements change size, then recalculate heightSum and initItemsMap
const resizeObserver = new ResizeObserver(() => { const resizeObserver = new ResizeObserver(() => {
this.refreshPos(); this.refreshPos();
}); });
resizeObserver.observe(this.header); resizeObserver.observe(this.header);
resizeObserver.observe(this.bannerName); resizeObserver.observe(this.bannerName);
resizeObserver.observe(this.bannerDetails); resizeObserver.observe(this.bannerDetails);
resizeObserver.observe(this.container); resizeObserver.observe(this.container);
},
initItemsMap() {
this.anchors.forEach((anchor) => {
this.items.push({
pos: null,
active: false,
key: parseInt(anchor.id.slice(8).slice(0, -1)),
id: "#" + anchor.id,
});
});
},
refreshPos() {
//console.log('refreshPos');
this.heightSum =
this.header.offsetHeight +
this.bannerName.offsetHeight +
this.bannerDetails.offsetHeight;
this.anchors.forEach((anchor, i) => {
this.items[i].pos = this.findPos(anchor)["y"];
});
},
findPos(element) {
let posX = 0,
posY = 0;
do {
posX += element.offsetLeft;
posY += element.offsetTop;
element = element.offsetParent;
} while (element != null);
let pos = [];
pos["x"] = posX;
pos["y"] = posY;
return pos;
},
handleScroll(event) {
let pos = this.findPos(this.stickyNav);
let top = this.heightSum + this.top - window.scrollY;
//console.log(window.scrollY);
if (top > this.limit) {
this.stickyNav.style.position = "absolute";
this.stickyNav.style.left = "10px";
} else {
this.stickyNav.style.position = "fixed";
this.stickyNav.style.left = pos["x"] + "px";
}
this.switchActive();
},
switchActive() {
this.items.forEach((item, i) => {
let next = this.items[i + 1] ? this.items[i + 1].pos : "100000";
item.active =
(window.scrollY >= item.pos) & (window.scrollY < next)
? true
: false;
}, this);
// last item never switch active because scroll reach bottom of page
if (
document.body.scrollHeight ==
window.scrollY + window.innerHeight
) {
this.items[this.items.length - 1].active = true;
this.items[this.items.length - 2].active = false;
} else {
this.items[this.items.length - 1].active = false;
}
},
}, },
initItemsMap() {
this.anchors.forEach((anchor) => {
this.items.push({
pos: null,
active: false,
key: parseInt(anchor.id.slice(8).slice(0, -1)),
id: "#" + anchor.id,
});
});
},
refreshPos() {
//console.log('refreshPos');
this.heightSum =
this.header.offsetHeight +
this.bannerName.offsetHeight +
this.bannerDetails.offsetHeight;
this.anchors.forEach((anchor, i) => {
this.items[i].pos = this.findPos(anchor)["y"];
});
},
findPos(element) {
let posX = 0,
posY = 0;
do {
posX += element.offsetLeft;
posY += element.offsetTop;
element = element.offsetParent;
} while (element != null);
let pos = [];
pos["x"] = posX;
pos["y"] = posY;
return pos;
},
handleScroll(event) {
let pos = this.findPos(this.stickyNav);
let top = this.heightSum + this.top - window.scrollY;
//console.log(window.scrollY);
if (top > this.limit) {
this.stickyNav.style.position = "absolute";
this.stickyNav.style.left = "10px";
} else {
this.stickyNav.style.position = "fixed";
this.stickyNav.style.left = pos["x"] + "px";
}
this.switchActive();
},
switchActive() {
this.items.forEach((item, i) => {
let next = this.items[i + 1] ? this.items[i + 1].pos : "100000";
item.active =
(window.scrollY >= item.pos) & (window.scrollY < next) ? true : false;
}, this);
// last item never switch active because scroll reach bottom of page
if (document.body.scrollHeight == window.scrollY + window.innerHeight) {
this.items[this.items.length - 1].active = true;
this.items[this.items.length - 2].active = false;
} else {
this.items[this.items.length - 1].active = false;
}
},
},
}; };
</script> </script>
<style lang="scss"> <style lang="scss">
div#content { div#content {
position: relative; position: relative;
div#navmap { div#navmap {
position: absolute; position: absolute;
top: 30px; top: 30px;
left: 10px; //-10%; left: 10px; //-10%;
nav { nav {
font-size: small; font-size: small;
a { a {
display: block; display: block;
box-sizing: border-box; box-sizing: border-box;
margin-bottom: -3px; margin-bottom: -3px;
color: #71859669; color: #71859669;
text-decoration: none; text-decoration: none;
&.top { &.top {
color: #718596; color: #718596;
}
span {
display: none;
}
&:hover,
&.active {
span {
display: inline;
padding-left: 8px;
}
}
&:hover {
color: #718596b5;
}
&.active {
color: #e2793d; //orange
//color: #eec84a; //jaune
}
}
} }
span {
display: none;
}
&:hover,
&.active {
span {
display: inline;
padding-left: 8px;
}
}
&:hover {
color: #718596b5;
}
&.active {
color: #e2793d; //orange
//color: #eec84a; //jaune
}
}
} }
}
} }
@media only screen and (max-width: 768px) { @media only screen and (max-width: 768px) {
div#navmap { div#navmap {
display: none; display: none;
} }
} }
</style> </style>

View File

@@ -1,22 +1,26 @@
<template> <template>
<a v-if="item.key <= 8" :href="item.id" :class="{ active: isActive }"> <a v-if="item.key <= 8" :href="item.id" :class="{ active: isActive }">
<i class="fa fa-fw fa-square" /> <i class="fa fa-fw fa-square" />
<span>{{ item.key }}</span> <span>{{ item.key }}</span>
</a> </a>
<a v-else-if="step === 'DRAFT'" :href="item.id" :class="{ active: isActive }"> <a
<i class="fa fa-fw fa-square" /> v-else-if="step === 'DRAFT'"
<span>{{ item.key }}</span> :href="item.id"
</a> :class="{ active: isActive }"
>
<i class="fa fa-fw fa-square" />
<span>{{ item.key }}</span>
</a>
</template> </template>
<script> <script>
export default { export default {
name: "Item", name: "Item",
props: ["item", "step"], props: ["item", "step"],
computed: { computed: {
isActive() { isActive() {
return this.item.active; return this.item.active;
},
}, },
},
}; };
</script> </script>

View File

@@ -1,146 +1,155 @@
<template> <template>
<div class="vue-component"> <div class="vue-component">
<h2>Tests</h2> <h2>Tests</h2>
<!-- Modal --> <!-- Modal -->
<ul class="record_actions"> <ul class="record_actions">
<li> <li>
<button class="btn btn-create" @click="modal1.showModal = true"> <button class="btn btn-create" @click="modal1.showModal = true">
{{ $t("action.show_modal") }} {{ $t("action.show_modal") }}
</button> </button>
</li> </li>
<li> <li>
<button class="btn btn-create" @click="modal2.showModal = true"> <button class="btn btn-create" @click="modal2.showModal = true">
Ouvrir une seconde modale Ouvrir une seconde modale
</button> </button>
</li> </li>
</ul> </ul>
<teleport to="body"> <teleport to="body">
<modal <modal
v-if="modal1.showModal" v-if="modal1.showModal"
:modal-dialog-class="modal1.modalDialogClass" :modal-dialog-class="modal1.modalDialogClass"
@close="modal1.showModal = false" @close="modal1.showModal = false"
> >
<template #header> <template #header>
<h2 class="modal-title">Le titre de ma modale</h2> <h2 class="modal-title">Le titre de ma modale</h2>
</template> </template>
<template #body> <template #body>
<p> <p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus Lorem ipsum dolor sit amet, consectetur adipiscing elit.
luctus facilisis suscipit. Cras pulvinar, purus sagittis pulvinar Phasellus luctus facilisis suscipit. Cras pulvinar,
porta, enim ex posuere lacus, in pulvinar lectus magna in odio. purus sagittis pulvinar porta, enim ex posuere lacus, in
Nullam iaculis congue lorem ac suscipit. Proin ut rutrum augue. Ut pulvinar lectus magna in odio. Nullam iaculis congue
vehicula risus nec hendrerit ullamcorper. Ut volutpat eu mi eget lorem ac suscipit. Proin ut rutrum augue. Ut vehicula
viverra. Morbi dictum placerat suscipit. risus nec hendrerit ullamcorper. Ut volutpat eu mi eget
</p> viverra. Morbi dictum placerat suscipit.
<p> </p>
Quisque non erat tincidunt, lacinia justo ut, pulvinar nisl. Nunc id <p>
enim ut sem pretium interdum consectetur eu quam. Vestibulum ante Quisque non erat tincidunt, lacinia justo ut, pulvinar
ipsum primis in faucibus orci luctus et ultrices posuere cubilia nisl. Nunc id enim ut sem pretium interdum consectetur
curae; Etiam posuere erat eget augue finibus luctus. Maecenas eu quam. Vestibulum ante ipsum primis in faucibus orci
auctor, tortor non luctus ultrices, neque neque porttitor ex, nec luctus et ultrices posuere cubilia curae; Etiam posuere
lacinia lorem ligula et elit. Sed tempor nulla vitae lorem erat eget augue finibus luctus. Maecenas auctor, tortor
sollicitudin dictum. Vestibulum nec arcu eget elit pulvinar pretium. non luctus ultrices, neque neque porttitor ex, nec
Phasellus facilisis metus sed diam luctus, feugiat scelerisque velit lacinia lorem ligula et elit. Sed tempor nulla vitae
dignissim. lorem sollicitudin dictum. Vestibulum nec arcu eget elit
</p> pulvinar pretium. Phasellus facilisis metus sed diam
<p> luctus, feugiat scelerisque velit dignissim.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus </p>
luctus facilisis suscipit. Cras pulvinar, purus sagittis pulvinar <p>
porta, enim ex posuere lacus, in pulvinar lectus magna in odio. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Nullam iaculis congue lorem ac suscipit. Proin ut rutrum augue. Ut Phasellus luctus facilisis suscipit. Cras pulvinar,
vehicula risus nec hendrerit ullamcorper. Ut volutpat eu mi eget purus sagittis pulvinar porta, enim ex posuere lacus, in
viverra. Morbi dictum placerat suscipit. pulvinar lectus magna in odio. Nullam iaculis congue
</p> lorem ac suscipit. Proin ut rutrum augue. Ut vehicula
<p> risus nec hendrerit ullamcorper. Ut volutpat eu mi eget
Quisque non erat tincidunt, lacinia justo ut, pulvinar nisl. Nunc id viverra. Morbi dictum placerat suscipit.
enim ut sem pretium interdum consectetur eu quam. Vestibulum ante </p>
ipsum primis in faucibus orci luctus et ultrices posuere cubilia <p>
curae; Etiam posuere erat eget augue finibus luctus. Maecenas Quisque non erat tincidunt, lacinia justo ut, pulvinar
auctor, tortor non luctus ultrices, neque neque porttitor ex, nec nisl. Nunc id enim ut sem pretium interdum consectetur
lacinia lorem ligula et elit. Sed tempor nulla vitae lorem eu quam. Vestibulum ante ipsum primis in faucibus orci
sollicitudin dictum. Vestibulum nec arcu eget elit pulvinar pretium. luctus et ultrices posuere cubilia curae; Etiam posuere
Phasellus facilisis metus sed diam luctus, feugiat scelerisque velit erat eget augue finibus luctus. Maecenas auctor, tortor
dignissim. non luctus ultrices, neque neque porttitor ex, nec
</p> lacinia lorem ligula et elit. Sed tempor nulla vitae
<p> lorem sollicitudin dictum. Vestibulum nec arcu eget elit
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus pulvinar pretium. Phasellus facilisis metus sed diam
luctus facilisis suscipit. Cras pulvinar, purus sagittis pulvinar luctus, feugiat scelerisque velit dignissim.
porta, enim ex posuere lacus, in pulvinar lectus magna in odio. </p>
Nullam iaculis congue lorem ac suscipit. Proin ut rutrum augue. Ut <p>
vehicula risus nec hendrerit ullamcorper. Ut volutpat eu mi eget Lorem ipsum dolor sit amet, consectetur adipiscing elit.
viverra. Morbi dictum placerat suscipit. Phasellus luctus facilisis suscipit. Cras pulvinar,
</p> purus sagittis pulvinar porta, enim ex posuere lacus, in
<p> pulvinar lectus magna in odio. Nullam iaculis congue
Quisque non erat tincidunt, lacinia justo ut, pulvinar nisl. Nunc id lorem ac suscipit. Proin ut rutrum augue. Ut vehicula
enim ut sem pretium interdum consectetur eu quam. Vestibulum ante risus nec hendrerit ullamcorper. Ut volutpat eu mi eget
ipsum primis in faucibus orci luctus et ultrices posuere cubilia viverra. Morbi dictum placerat suscipit.
curae; Etiam posuere erat eget augue finibus luctus. Maecenas </p>
auctor, tortor non luctus ultrices, neque neque porttitor ex, nec <p>
lacinia lorem ligula et elit. Sed tempor nulla vitae lorem Quisque non erat tincidunt, lacinia justo ut, pulvinar
sollicitudin dictum. Vestibulum nec arcu eget elit pulvinar pretium. nisl. Nunc id enim ut sem pretium interdum consectetur
Phasellus facilisis metus sed diam luctus, feugiat scelerisque velit eu quam. Vestibulum ante ipsum primis in faucibus orci
dignissim. luctus et ultrices posuere cubilia curae; Etiam posuere
</p> erat eget augue finibus luctus. Maecenas auctor, tortor
</template> non luctus ultrices, neque neque porttitor ex, nec
<template #footer> lacinia lorem ligula et elit. Sed tempor nulla vitae
<button lorem sollicitudin dictum. Vestibulum nec arcu eget elit
class="btn btn-create" pulvinar pretium. Phasellus facilisis metus sed diam
@click=" luctus, feugiat scelerisque velit dignissim.
modal1.showModal = false; </p>
modal2.showModal = true; </template>
" <template #footer>
> <button
{{ $t("action.next") }} class="btn btn-create"
</button> @click="
</template> modal1.showModal = false;
</modal> modal2.showModal = true;
</teleport> "
>
{{ $t("action.next") }}
</button>
</template>
</modal>
</teleport>
<teleport to="body"> <teleport to="body">
<modal <modal
v-if="modal2.showModal" v-if="modal2.showModal"
:modal-dialog-class="modal2.modalDialogClass" :modal-dialog-class="modal2.modalDialogClass"
@close="modal2.showModal = false" @close="modal2.showModal = false"
> >
<template #header> <template #header>
<h2 class="modal-title">Une autre modale</h2> <h2 class="modal-title">Une autre modale</h2>
</template> </template>
<template #body> <template #body>
<p>modal 2</p> <p>modal 2</p>
</template> </template>
<template #footer> <template #footer>
<button class="btn btn-create" @click="modal2.showModal = false"> <button
{{ $t("action.save") }} class="btn btn-create"
</button> @click="modal2.showModal = false"
</template> >
</modal> {{ $t("action.save") }}
</teleport> </button>
<!-- END Modal --> </template>
</div> </modal>
</teleport>
<!-- END Modal -->
</div>
</template> </template>
<script> <script>
import Modal from "ChillMainAssets/vuejs/_components/Modal"; import Modal from "ChillMainAssets/vuejs/_components/Modal";
export default { export default {
name: "Test", name: "Test",
components: { components: {
Modal, Modal,
}, },
data() { data() {
return { return {
modal1: { modal1: {
showModal: false, showModal: false,
modalDialogClass: "modal-dialog-scrollable modal-xl", // modal-lg modal-md modal-sm modalDialogClass: "modal-dialog-scrollable modal-xl", // modal-lg modal-md modal-sm
}, },
modal2: { modal2: {
showModal: false, showModal: false,
modalDialogClass: "modal-dialog-centered modal-sm", // modal-lg modal-md modal-sm modalDialogClass: "modal-dialog-centered modal-sm", // modal-lg modal-md modal-sm
}, },
}; };
}, },
computed: {}, computed: {},
}; };
</script> </script>

View File

@@ -1,135 +1,152 @@
<template> <template>
<h2>{{ $t("pick_social_issue") }}</h2> <h2>{{ $t("pick_social_issue") }}</h2>
<div id="awc_create_form"> <div id="awc_create_form">
<div id="picking" class=""> <div id="picking" class="">
<p>{{ $t("pick_social_issue_linked_with_action") }}</p> <p>{{ $t("pick_social_issue_linked_with_action") }}</p>
<div v-for="si in socialIssues" :key="si.id"> <div v-for="si in socialIssues" :key="si.id">
<input <input
type="radio" type="radio"
:value="si.id" :value="si.id"
name="socialIssue" name="socialIssue"
v-model="socialIssuePicked" v-model="socialIssuePicked"
/><span class="badge bg-chill-l-gray text-dark">{{ si.text }}</span> /><span class="badge bg-chill-l-gray text-dark">{{
</div> si.text
<div class="my-3"> }}</span>
<div class="col-11">
<vue-multiselect
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('choose_other_social_issue')"
:options="socialIssuesOther"
@select="addIssueInList"
/>
</div>
</div>
<div v-if="hasSocialIssuePicked" class="mb-3">
<h2>{{ $t("pick_an_action") }}</h2>
<div class="col-11">
<vue-multiselect
v-model="socialActionPicked"
label="text"
:options="socialActionsReachables"
:searchable="true"
:close-on-select="true"
:show-labels="true"
track-by="id"
/>
</div>
</div>
<div v-if="isLoadingSocialActions">
<i class="fa fa-circle-o-notch fa-spin fa-fw" />
</div>
<div v-if="hasSocialActionPicked" id="persons" class="mb-5">
<h2>{{ $t("persons_involved") }}</h2>
<ul>
<li v-for="p in personsReachables" :key="p.id">
<div class="form-check">
<input
type="checkbox"
:value="p.id"
v-model="personsPicked"
class="form-check-input"
:id="'person_check' + p.id"
/>
<label class="form-check-label" :for="'person_check' + p.id">
<person-text :person="p" />
</label>
</div> </div>
</li> <div class="my-3">
</ul> <div class="col-11">
</div> <vue-multiselect
</div> name="otherIssues"
<!-- <div v-if="hasSocialActionPicked" id="start_date"> 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('choose_other_social_issue')"
:options="socialIssuesOther"
@select="addIssueInList"
/>
</div>
</div>
<div v-if="hasSocialIssuePicked" class="mb-3">
<h2>{{ $t("pick_an_action") }}</h2>
<div class="col-11">
<vue-multiselect
v-model="socialActionPicked"
label="text"
:options="socialActionsReachables"
:searchable="true"
:close-on-select="true"
:show-labels="true"
track-by="id"
/>
</div>
</div>
<div v-if="isLoadingSocialActions">
<i class="fa fa-circle-o-notch fa-spin fa-fw" />
</div>
<div v-if="hasSocialActionPicked" id="persons" class="mb-5">
<h2>{{ $t("persons_involved") }}</h2>
<ul>
<li v-for="p in personsReachables" :key="p.id">
<div class="form-check">
<input
type="checkbox"
:value="p.id"
v-model="personsPicked"
class="form-check-input"
:id="'person_check' + p.id"
/>
<label
class="form-check-label"
:for="'person_check' + p.id"
>
<person-text :person="p" />
</label>
</div>
</li>
</ul>
</div>
</div>
<!-- <div v-if="hasSocialActionPicked" id="start_date">
<p><label>{{ $t('startDate') }}</label> <input type="date" v-model="startDate" /></p> <p><label>{{ $t('startDate') }}</label> <input type="date" v-model="startDate" /></p>
</div> --> </div> -->
<div class="row"> <div class="row">
<div v-if="hasSocialActionPicked" id="start_date" class="mb-3 row"> <div v-if="hasSocialActionPicked" id="start_date" class="mb-3 row">
<label class="col-form-label col-sm-4">{{ $t("startDate") }}</label> <label class="col-form-label col-sm-4">{{
<div class="col-sm-8"> $t("startDate")
<input class="form-control" type="date" v-model="startDate" /> }}</label>
</div> <div class="col-sm-8">
</div> <input
class="form-control"
type="date"
v-model="startDate"
/>
</div>
</div>
<!-- <div v-if="hasSocialActionPicked" id="end_date"> <!-- <div v-if="hasSocialActionPicked" id="end_date">
<p><label>{{ $t('endDate') }}</label> <input type="date" v-model="endDate" /></p> <p><label>{{ $t('endDate') }}</label> <input type="date" v-model="endDate" /></p>
</div> --> </div> -->
<div v-if="hasSocialActionPicked" id="end_date" class="mb-3 row"> <div v-if="hasSocialActionPicked" id="end_date" class="mb-3 row">
<label class="col-form-label col-sm-4">{{ $t("endDate") }}</label> <label class="col-form-label col-sm-4">{{
<div class="col-sm-8"> $t("endDate")
<input class="form-control" type="date" v-model="endDate" /> }}</label>
<div class="col-sm-8">
<input class="form-control" type="date" v-model="endDate" />
</div>
</div>
</div> </div>
</div> <div id="confirm">
</div> <div v-if="hasErrors">
<div id="confirm"> <p>{{ $t("form_has_errors") }}</p>
<div v-if="hasErrors">
<p>{{ $t("form_has_errors") }}</p>
<ul> <ul>
<li v-for="e in errors" :key="e.id"> <li v-for="e in errors" :key="e.id">
{{ e }} {{ e }}
</li> </li>
</ul> </ul>
</div> </div>
<div> <div>
<ul class="record_actions"> <ul class="record_actions">
<li class="cancel"> <li class="cancel">
<button class="btn btn-cancel" @click="goToPrevious"> <button class="btn btn-cancel" @click="goToPrevious">
{{ $t("action.cancel") }} {{ $t("action.cancel") }}
</button> </button>
</li> </li>
<li v-if="hasSocialActionPicked"> <li v-if="hasSocialActionPicked">
<button <button
class="btn btn-save" class="btn btn-save"
v-show="!isPostingWork" v-show="!isPostingWork"
@click="submit" @click="submit"
> >
{{ $t("action.save") }} {{ $t("action.save") }}
</button> </button>
<button class="btn btn-save" v-show="isPostingWork" disabled> <button
{{ $t("action.save") }} class="btn btn-save"
</button> v-show="isPostingWork"
</li> disabled
</ul> >
</div> {{ $t("action.save") }}
</button>
</li>
</ul>
</div>
</div>
</div> </div>
</div>
</template> </template>
<script> <script>
@@ -138,127 +155,131 @@ import VueMultiselect from "vue-multiselect";
import PersonText from "ChillPersonAssets/vuejs/_components/Entity/PersonText.vue"; import PersonText from "ChillPersonAssets/vuejs/_components/Entity/PersonText.vue";
const i18n = { const i18n = {
messages: { messages: {
fr: { fr: {
startDate: "Date de début", startDate: "Date de début",
endDate: "Date de fin", endDate: "Date de fin",
form_has_errors: "Le formulaire comporte des erreurs", form_has_errors: "Le formulaire comporte des erreurs",
pick_social_issue: "Choisir une problématique sociale", pick_social_issue: "Choisir une problématique sociale",
pick_other_social_issue: "Veuillez choisir un autre problématique", pick_other_social_issue: "Veuillez choisir un autre problématique",
pick_an_action: "Choisir une action d'accompagnement", pick_an_action: "Choisir une action d'accompagnement",
pick_social_issue_linked_with_action: pick_social_issue_linked_with_action:
"Indiquez la problématique sociale liée à l'action d'accompagnement", "Indiquez la problématique sociale liée à l'action d'accompagnement",
persons_involved: "Usagers concernés", persons_involved: "Usagers concernés",
choose_other_social_issue: "Veuillez choisir un autre problématique", choose_other_social_issue:
"Veuillez choisir un autre problématique",
},
}, },
},
}; };
export default { export default {
name: "App", name: "App",
components: { components: {
VueMultiselect, VueMultiselect,
PersonText, PersonText,
},
methods: {
submit() {
this.$store.dispatch("submit").catch(({ name, violations }) => {
if (name === "ValidationException" || name === "AccessException") {
violations.forEach((violation) =>
this.$toast.open({ message: violation }),
);
} else {
this.$toast.open({ message: "An error occurred" });
}
});
}, },
addIssueInList(value) { methods: {
this.$store.commit("addIssueInList", value); submit() {
this.$store.commit("removeIssueInOther", value); this.$store.dispatch("submit").catch(({ name, violations }) => {
this.$store.dispatch("pickSocialIssue", value.id); if (
name === "ValidationException" ||
name === "AccessException"
) {
violations.forEach((violation) =>
this.$toast.open({ message: violation }),
);
} else {
this.$toast.open({ message: "An error occurred" });
}
});
},
addIssueInList(value) {
this.$store.commit("addIssueInList", value);
this.$store.commit("removeIssueInOther", value);
this.$store.dispatch("pickSocialIssue", value.id);
},
goToPrevious() {
let params = new URLSearchParams(window.location.search);
if (params.has("returnPath")) {
window.location.replace(params.get("returnPath"));
} else {
return;
}
},
}, },
goToPrevious() { i18n,
let params = new URLSearchParams(window.location.search); computed: {
if (params.has("returnPath")) { ...mapState([
window.location.replace(params.get("returnPath")); "socialIssues",
} else { "socialIssuesOther",
return; "socialActionsReachables",
} "errors",
}, "personsReachables",
}, ]),
i18n, ...mapGetters([
computed: { "hasSocialIssuePicked",
...mapState([ "hasSocialActionPicked",
"socialIssues", "isLoadingSocialActions",
"socialIssuesOther", "isPostingWork",
"socialActionsReachables", "hasErrors",
"errors", ]),
"personsReachables", personsPicked: {
]), get() {
...mapGetters([ let s = this.$store.state.personsPicked.map((p) => p.id);
"hasSocialIssuePicked",
"hasSocialActionPicked",
"isLoadingSocialActions",
"isPostingWork",
"hasErrors",
]),
personsPicked: {
get() {
let s = this.$store.state.personsPicked.map((p) => p.id);
return s; return s;
}, },
set(v) { set(v) {
this.$store.commit("setPersonsPickedIds", v); this.$store.commit("setPersonsPickedIds", v);
}, },
}, },
socialIssuePicked: { socialIssuePicked: {
get() { get() {
let s = this.$store.state.socialIssuePicked; let s = this.$store.state.socialIssuePicked;
if (s === null) { if (s === null) {
return null; return null;
} }
return s.id; return s.id;
}, },
set(value) { set(value) {
this.$store.dispatch("pickSocialIssue", value); this.$store.dispatch("pickSocialIssue", value);
}, },
},
socialActionPicked: {
get() {
return this.$store.state.socialActionPicked;
},
set(value) {
this.$store.commit("setSocialAction", value);
},
},
startDate: {
get() {
return this.$store.state.startDate;
},
set(value) {
this.$store.commit("setStartDate", value);
},
},
endDate: {
get() {
return this.$store.state.endDate;
},
set(value) {
this.$store.commit("setEndDate", value);
},
},
setSocialIssue: {
set() {
this.$store.dispatch(
"setSocialIssue",
socialIssues[socialIssues.length - 1],
);
},
},
}, },
socialActionPicked: {
get() {
return this.$store.state.socialActionPicked;
},
set(value) {
this.$store.commit("setSocialAction", value);
},
},
startDate: {
get() {
return this.$store.state.startDate;
},
set(value) {
this.$store.commit("setStartDate", value);
},
},
endDate: {
get() {
return this.$store.state.endDate;
},
set(value) {
this.$store.commit("setEndDate", value);
},
},
setSocialIssue: {
set() {
this.$store.dispatch(
"setSocialIssue",
socialIssues[socialIssues.length - 1],
);
},
},
},
}; };
</script> </script>
@@ -267,46 +288,46 @@ export default {
@import "ChillPersonAssets/chill/scss/mixins"; @import "ChillPersonAssets/chill/scss/mixins";
@import "ChillMainAssets/chill/scss/chill_variables"; @import "ChillMainAssets/chill/scss/chill_variables";
span.badge { span.badge {
@include badge_social($social-issue-color); @include badge_social($social-issue-color);
font-size: 95%; font-size: 95%;
margin-bottom: 5px; margin-bottom: 5px;
margin-right: 1em; margin-right: 1em;
margin-left: 1em; margin-left: 1em;
} }
</style> </style>
<style lang="scss"> <style lang="scss">
#awc_create_form { #awc_create_form {
display: grid; display: grid;
grid-template-areas: grid-template-areas:
"picking picking" "picking picking"
"start_date end_date" "start_date end_date"
"confirm confirm"; "confirm confirm";
grid-template-columns: 50% 50%; grid-template-columns: 50% 50%;
column-gap: 1.5rem; column-gap: 1.5rem;
#picking { #picking {
grid-area: picking; grid-area: picking;
#persons { #persons {
ul { ul {
padding: 0; padding: 0;
list-style-type: none; list-style-type: none;
} }
}
} }
}
#start_date { #start_date {
grid-area: start_date; grid-area: start_date;
} }
#end_date { #end_date {
grid-area: end_date; grid-area: end_date;
} }
#confirm { #confirm {
grid-area: confirm; grid-area: confirm;
} }
} }
</style> </style>

View File

@@ -1,67 +1,70 @@
<template> <template>
<div>
<a id="evaluations"></a>
<div class="item-title" :title="evaluation.id || 'no id yet'">
<span>{{ localizeString(evaluation.evaluation.title) }}</span>
</div>
<div class="item-url mt-3 mb-4" v-if="evaluation.evaluation.url">
<i class="fa fa-link fa-lg"></i>
<a :href="evaluation.evaluation.url" target="_blank">{{
evaluation.evaluation.url
}}</a>
</div>
<div> <div>
<form-evaluation <a id="evaluations"></a>
ref="FormEvaluation" <div class="item-title" :title="evaluation.id || 'no id yet'">
:key="evaluation.key" <span>{{ localizeString(evaluation.evaluation.title) }}</span>
:evaluation="evaluation" </div>
:docAnchorId="docAnchorId"
></form-evaluation>
<ul class="record_actions"> <div class="item-url mt-3 mb-4" v-if="evaluation.evaluation.url">
<li v-if="evaluation.workflows_availables.length > 0"> <i class="fa fa-link fa-lg"></i>
<list-workflow-modal <a :href="evaluation.evaluation.url" target="_blank">{{
:workflows="evaluation.workflows" evaluation.evaluation.url
:allowCreate="true" }}</a>
relatedEntityClass="Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation" </div>
:relatedEntityId="evaluation.id"
:workflowsAvailables="evaluation.workflows_availables" <div>
@go-to-generate-workflow="goToGenerateWorkflow" <form-evaluation
></list-workflow-modal> ref="FormEvaluation"
</li> :key="evaluation.key"
<li v-if="canDelete"> :evaluation="evaluation"
<a :docAnchorId="docAnchorId"
class="btn btn-delete" ></form-evaluation>
@click="modal.showModal = true"
:title="$t('action.delete')" <ul class="record_actions">
>{{ $t("delete_evaluation") }}</a <li v-if="evaluation.workflows_availables.length > 0">
> <list-workflow-modal
</li> :workflows="evaluation.workflows"
</ul> :allowCreate="true"
relatedEntityClass="Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation"
:relatedEntityId="evaluation.id"
:workflowsAvailables="evaluation.workflows_availables"
@go-to-generate-workflow="goToGenerateWorkflow"
></list-workflow-modal>
</li>
<li v-if="canDelete">
<a
class="btn btn-delete"
@click="modal.showModal = true"
:title="$t('action.delete')"
>{{ $t("delete_evaluation") }}</a
>
</li>
</ul>
</div>
<teleport to="body">
<modal
v-if="modal.showModal"
:modalDialogClass="modal.modalDialogClass"
@close="modal.showModal = false"
>
<template v-slot:header>
<h2 class="modal-title">{{ $t("delete.sure") }}</h2>
</template>
<template v-slot:body>
<p>{{ $t("delete.sure_description") }}</p>
</template>
<template v-slot:footer>
<button
class="btn btn-danger"
@click="removeEvaluation(evaluation)"
>
{{ $t("delete.ok") }}
</button>
</template>
</modal>
</teleport>
</div> </div>
<teleport to="body">
<modal
v-if="modal.showModal"
:modalDialogClass="modal.modalDialogClass"
@close="modal.showModal = false"
>
<template v-slot:header>
<h2 class="modal-title">{{ $t("delete.sure") }}</h2>
</template>
<template v-slot:body>
<p>{{ $t("delete.sure_description") }}</p>
</template>
<template v-slot:footer>
<button class="btn btn-danger" @click="removeEvaluation(evaluation)">
{{ $t("delete.ok") }}
</button>
</template>
</modal>
</teleport>
</div>
</template> </template>
<script> <script>
@@ -72,112 +75,114 @@ import { buildLinkCreate } from "ChillMainAssets/lib/entity-workflow/api";
import { localizeString } from "ChillMainAssets/lib/localizationHelper/localizationHelper"; import { localizeString } from "ChillMainAssets/lib/localizationHelper/localizationHelper";
const i18n = { const i18n = {
messages: { messages: {
fr: { fr: {
no_evaluation_associated: "Aucune évaluation associée", no_evaluation_associated: "Aucune évaluation associée",
add_an_evaluation: "Évaluations disponibles", add_an_evaluation: "Évaluations disponibles",
evaluation_has_no_evaluation: "Aucune évaluation disponible", evaluation_has_no_evaluation: "Aucune évaluation disponible",
startDate: "Date d'ouverture", startDate: "Date d'ouverture",
endDate: "Date de fin", endDate: "Date de fin",
maxDate: "Date d'échéance", maxDate: "Date d'échéance",
warningInterval: "Rappel (jours)", warningInterval: "Rappel (jours)",
comment: "Note publique", comment: "Note publique",
documents: "Documents", documents: "Documents",
delete: { delete: {
sure: "Êtes-vous sûr?", sure: "Êtes-vous sûr?",
sure_description: sure_description:
"Cette évaluation sera supprimée de cette action d'accompagnement", "Cette évaluation sera supprimée de cette action d'accompagnement",
ok: "Supprimer", ok: "Supprimer",
}, },
delete_evaluation: "Supprimer l'évaluation", delete_evaluation: "Supprimer l'évaluation",
},
}, },
},
}; };
export default { export default {
name: "AddEvaluation", name: "AddEvaluation",
components: { components: {
FormEvaluation, FormEvaluation,
Modal, Modal,
ListWorkflowModal, ListWorkflowModal,
},
props: ["evaluation", "docAnchorId"],
i18n,
data() {
return {
modal: {
showModal: false,
modalDialogClass: "modal-dialog-centered modal-md",
},
};
},
computed: {
pickedEvaluations() {
return this.$store.state.evaluationsPicked;
}, },
canDelete() { props: ["evaluation", "docAnchorId"],
if (this.evaluation.workflows.length > 0) { i18n,
return false; data() {
} return {
modal: {
showModal: false,
modalDialogClass: "modal-dialog-centered modal-md",
},
};
},
computed: {
pickedEvaluations() {
return this.$store.state.evaluationsPicked;
},
canDelete() {
if (this.evaluation.workflows.length > 0) {
return false;
}
for (let doc of this.evaluation.documents) { for (let doc of this.evaluation.documents) {
if (doc.workflows.length > 0) { if (doc.workflows.length > 0) {
return false; return false;
} }
} }
return true; return true;
},
}, },
}, methods: {
methods: { localizeString,
localizeString, removeEvaluation(e) {
removeEvaluation(e) { this.$store.commit("removeEvaluation", e);
this.$store.commit("removeEvaluation", e); return;
return; },
}, toggleEditEvaluation() {
toggleEditEvaluation() { this.$store.commit("toggleEvaluationEdit", {
this.$store.commit("toggleEvaluationEdit", { key: this.evaluation.key }); key: this.evaluation.key,
}, });
submitForm() { },
this.toggleEditEvaluation(); submitForm() {
}, this.toggleEditEvaluation();
goToGenerateWorkflow({ workflowName }) { },
const callback = (data) => { goToGenerateWorkflow({ workflowName }) {
let evaluationId = data.accompanyingPeriodWorkEvaluations.find( const callback = (data) => {
(e) => e.key === this.evaluation.key, let evaluationId = data.accompanyingPeriodWorkEvaluations.find(
).id; (e) => e.key === this.evaluation.key,
window.location.assign( ).id;
buildLinkCreate( window.location.assign(
workflowName, buildLinkCreate(
"Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWorkEvaluation", workflowName,
evaluationId, "Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWorkEvaluation",
), evaluationId,
); ),
}; );
};
return this.$store.dispatch("submit", callback).catch((e) => { return this.$store.dispatch("submit", callback).catch((e) => {
console.log(e); console.log(e);
throw e; throw e;
}); });
},
}, },
},
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
div.item-title { div.item-title {
.evaluation-title { .evaluation-title {
cursor: default; cursor: default;
&::before { &::before {
content: ""; content: "";
}
} }
}
} }
div.item-url { div.item-url {
i { i {
color: unset !important; color: unset !important;
margin-left: 1rem; margin-left: 1rem;
margin-right: 0.5rem; margin-right: 0.5rem;
} }
} }
</style> </style>

View File

@@ -1,167 +1,179 @@
<template> <template>
<div v-if="hasResult" class="addResult"> <div v-if="hasResult" class="addResult">
<p v-if="pickedResults.length === 0" class="chill-no-data-statement"> <p v-if="pickedResults.length === 0" class="chill-no-data-statement">
Aucun résultat associé Aucun résultat associé
</p> </p>
<ul class="list-suggest remove-items"> <ul class="list-suggest remove-items">
<li v-for="r in pickedResults" @click="removeResult(r)" :key="r.id"> <li v-for="r in pickedResults" @click="removeResult(r)" :key="r.id">
<span> <span>
{{ localizeString(r.title) }} {{ localizeString(r.title) }}
</span> </span>
</li> </li>
</ul> </ul>
<div class="accordion" id="expandedSuggestions"> <div class="accordion" id="expandedSuggestions">
<div class="accordion-item"> <div class="accordion-item">
<h2 class="accordion-header" id="heading_expanded_suggestions"> <h2 class="accordion-header" id="heading_expanded_suggestions">
<button <button
v-if="isExpanded" v-if="isExpanded"
class="accordion-button" class="accordion-button"
type="button" type="button"
data-bs-toggle="collapse" data-bs-toggle="collapse"
aria-expanded="true" aria-expanded="true"
@click="toggleSelect" @click="toggleSelect"
> >
Masquer Masquer
</button> </button>
<button <button
v-else v-else
class="accordion-button collapsed" class="accordion-button collapsed"
type="button" type="button"
data-bs-toggle="collapse" data-bs-toggle="collapse"
aria-expanded="false" aria-expanded="false"
@click="toggleSelect" @click="toggleSelect"
> >
Résultats et orientations disponibles Résultats et orientations disponibles
</button> </button>
</h2> </h2>
<div <div
class="accordion-collapse" class="accordion-collapse"
id="collapse_expanded_suggestions" id="collapse_expanded_suggestions"
aria-labelledby="heading_expanded_suggestions" aria-labelledby="heading_expanded_suggestions"
data-bs-parent="#expandedSuggestions" data-bs-parent="#expandedSuggestions"
> >
<template v-if="isExpanded"> <template v-if="isExpanded">
<ul class="list-suggest add-items"> <ul class="list-suggest add-items">
<li <li
v-for="r in availableForCheckResults" v-for="r in availableForCheckResults"
@click="addResult(r)" @click="addResult(r)"
:key="r.id" :key="r.id"
> >
<span>{{ localizeString(r.title) }}</span> <span>{{ localizeString(r.title) }}</span>
</li> </li>
</ul> </ul>
</template> </template>
</div>
</div>
</div> </div>
</div>
</div> </div>
</div> <div v-if="!hasResult" class="noResult">
<div v-if="!hasResult" class="noResult"> <div class="chill-no-data-statement">
<div class="chill-no-data-statement"> {{ $t("goal_has_no_result") }}
{{ $t("goal_has_no_result") }} </div>
</div> </div>
</div>
</template> </template>
<script> <script>
import { localizeString } from "ChillMainAssets/lib/localizationHelper/localizationHelper"; import { localizeString } from "ChillMainAssets/lib/localizationHelper/localizationHelper";
const i18n = { const i18n = {
messages: { messages: {
fr: { fr: {
add_a_result: "Résultat - orientation disponibles", add_a_result: "Résultat - orientation disponibles",
goal_has_no_result: "Aucun résultat - orientation disponible", goal_has_no_result: "Aucun résultat - orientation disponible",
},
}, },
},
}; };
export default { export default {
name: "AddResult", name: "AddResult",
props: ["destination", "goal", "availableResults"], props: ["destination", "goal", "availableResults"],
i18n, i18n,
data() { data() {
return { return {
isExpanded: false, isExpanded: false,
}; };
}, },
computed: { computed: {
hasResult() { hasResult() {
if (this.destination === "action") { if (this.destination === "action") {
return this.$store.state.resultsForAction.length > 0; return this.$store.state.resultsForAction.length > 0;
} else if (this.destination === "goal") { } else if (this.destination === "goal") {
return this.$store.getters.resultsForGoal(this.goal).length > 0; return this.$store.getters.resultsForGoal(this.goal).length > 0;
} }
throw Error(`this.destination is not implemented: ${this.destination}`); throw Error(
}, `this.destination is not implemented: ${this.destination}`,
pickedResults() { );
if (this.destination === "action") { },
return this.$store.state.resultsPicked; pickedResults() {
} else if (this.destination === "goal") { if (this.destination === "action") {
return this.$store.getters.resultsPickedForGoal(this.goal); return this.$store.state.resultsPicked;
} } else if (this.destination === "goal") {
return this.$store.getters.resultsPickedForGoal(this.goal);
}
throw Error(`this.destination is not implemented: ${this.destination}`); throw Error(
}, `this.destination is not implemented: ${this.destination}`,
availableForCheckResults() { );
if (this.destination === "action") { },
let pickedIds = this.$store.state.resultsPicked.map((r) => r.id); availableForCheckResults() {
if (this.destination === "action") {
let pickedIds = this.$store.state.resultsPicked.map(
(r) => r.id,
);
return this.$store.state.resultsForAction.filter( return this.$store.state.resultsForAction.filter(
(r) => !pickedIds.includes(r.id), (r) => !pickedIds.includes(r.id),
); );
} else if (this.destination === "goal") { } else if (this.destination === "goal") {
let pickedIds = this.$store.getters let pickedIds = this.$store.getters
.resultsPickedForGoal(this.goal) .resultsPickedForGoal(this.goal)
.map((r) => r.id); .map((r) => r.id);
return this.$store.getters return this.$store.getters
.resultsForGoal(this.goal) .resultsForGoal(this.goal)
.filter((r) => !pickedIds.includes(r.id)); .filter((r) => !pickedIds.includes(r.id));
} }
throw Error(`this.destination is not implemented: ${this.destination}`); throw Error(
`this.destination is not implemented: ${this.destination}`,
);
},
}, },
}, methods: {
methods: { localizeString,
localizeString, toggleSelect() {
toggleSelect() { this.isExpanded = !this.isExpanded;
this.isExpanded = !this.isExpanded; },
addResult(r) {
if (this.destination === "action") {
this.$store.commit("addResultPicked", r);
return;
} else if (this.destination === "goal") {
this.$store.commit("addResultForGoalPicked", {
goal: this.goal,
result: r,
});
return;
}
throw Error(
`this.destination is not implemented: ${this.destination}`,
);
},
removeResult(r) {
if (this.destination === "action") {
this.$store.commit("removeResultPicked", r);
return;
} else if (this.destination === "goal") {
this.$store.commit("removeResultForGoalPicked", {
goal: this.goal,
result: r,
});
return;
}
throw Error(
`this.destination is not implemented: ${this.destination}`,
);
},
}, },
addResult(r) {
if (this.destination === "action") {
this.$store.commit("addResultPicked", r);
return;
} else if (this.destination === "goal") {
this.$store.commit("addResultForGoalPicked", {
goal: this.goal,
result: r,
});
return;
}
throw Error(`this.destination is not implemented: ${this.destination}`);
},
removeResult(r) {
if (this.destination === "action") {
this.$store.commit("removeResultPicked", r);
return;
} else if (this.destination === "goal") {
this.$store.commit("removeResultForGoalPicked", {
goal: this.goal,
result: r,
});
return;
}
throw Error(`this.destination is not implemented: ${this.destination}`);
},
},
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.accordion-button { .accordion-button {
padding: 0.25rem; padding: 0.25rem;
} }
</style> </style>

View File

@@ -1,447 +1,445 @@
<template> <template>
<fieldset class="mb-3" id="actionType"> <fieldset class="mb-3" id="actionType">
<div class="row"> <div class="row">
<legend class="col-sm-4 col-form-label"> <legend class="col-sm-4 col-form-label">
{{ $t("action.label") }} {{ $t("action.label") }}
</legend> </legend>
<div class="col-sm-8"> <div class="col-sm-8">
<VueMultiselect <VueMultiselect
v-model="action" v-model="action"
:options="actions.options" :options="actions.options"
@select="selectAction" @select="selectAction"
@remove="unselectAction" @remove="unselectAction"
:multiple="true" :multiple="true"
:close-on-select="false" :close-on-select="false"
:placeholder="$t('action.placeholder')" :placeholder="$t('action.placeholder')"
:custom-label="formatSocialAction" :custom-label="formatSocialAction"
track-by="id" track-by="id"
:searchable="true" :searchable="true"
/> />
</div> </div>
</div> </div>
</fieldset> </fieldset>
<fieldset class="mb-3" id="goal"> <fieldset class="mb-3" id="goal">
<div class="row"> <div class="row">
<legend class="col-sm-4 col-form-label"> <legend class="col-sm-4 col-form-label">
{{ $t("goal.label") }} {{ $t("goal.label") }}
</legend> </legend>
<div class="col-sm-8"> <div class="col-sm-8">
<VueMultiselect <VueMultiselect
v-model="goal" v-model="goal"
:options="goals.options" :options="goals.options"
@select="selectGoal" @select="selectGoal"
@remove="unselectGoal" @remove="unselectGoal"
:multiple="true" :multiple="true"
:close-on-select="false" :close-on-select="false"
:placeholder="$t('goal.placeholder')" :placeholder="$t('goal.placeholder')"
label="title" label="title"
:custom-label="transTitle" :custom-label="transTitle"
track-by="id" track-by="id"
:searchable="true" :searchable="true"
/> />
</div> </div>
</div> </div>
</fieldset> </fieldset>
<fieldset class="mb-3" id="result"> <fieldset class="mb-3" id="result">
<div class="row"> <div class="row">
<legend class="col-sm-4 col-form-label"> <legend class="col-sm-4 col-form-label">
{{ $t("result.label") }} {{ $t("result.label") }}
</legend> </legend>
<div class="col-sm-8"> <div class="col-sm-8">
<VueMultiselect <VueMultiselect
v-model="result" v-model="result"
:options="results.options" :options="results.options"
@select="selectResult" @select="selectResult"
@remove="unselectResult" @remove="unselectResult"
:multiple="true" :multiple="true"
:close-on-select="false" :close-on-select="false"
:placeholder="$t('result.placeholder')" :placeholder="$t('result.placeholder')"
label="title" label="title"
:custom-label="transTitle" :custom-label="transTitle"
track-by="id" track-by="id"
:searchable="true" :searchable="true"
/> />
</div> </div>
</div> </div>
</fieldset> </fieldset>
</template> </template>
<script> <script>
import VueMultiselect from "vue-multiselect"; import VueMultiselect from "vue-multiselect";
import { import {
getSocialActions, getSocialActions,
getGoalByAction, getGoalByAction,
getResultByAction, getResultByAction,
getResultByGoal, getResultByGoal,
} from "./api"; } from "./api";
export default { export default {
name: "App", name: "App",
components: { components: {
VueMultiselect, VueMultiselect,
}, },
i18n: { i18n: {
messages: { messages: {
fr: { fr: {
action: {
label: "Types d'actions",
placeholder: "Choisissez une ou plusieurs actions",
},
goal: {
label: "Objectifs",
placeholder: "Choisissez un ou plusieurs objectifs",
},
result: {
label: "Résultats",
placeholder: "Choisissez un ou plusieurs résultats",
},
},
},
},
data() {
return {
actions: {
options: [], // array with multiselect options
value: [], // array with selected values
hiddenField: document.getElementById(
"export_filters_social_work_type_filter_form_actionType",
),
},
goals: {
options: [],
value: [],
hiddenField: document.getElementById(
"export_filters_social_work_type_filter_form_goal",
),
},
results: {
options: [],
value: [],
hiddenField: document.getElementById(
"export_filters_social_work_type_filter_form_result",
),
},
};
},
computed: {
action: { action: {
label: "Types d'actions", get() {
placeholder: "Choisissez une ou plusieurs actions", return this.actions.value;
},
set(value) {
this.actions.value = value;
this.rebuildHiddenFieldValues("actions");
},
}, },
goal: { goal: {
label: "Objectifs", get() {
placeholder: "Choisissez un ou plusieurs objectifs", return this.goals.value;
},
set(value) {
this.goals.value = value;
this.rebuildHiddenFieldValues("goals");
},
}, },
result: { result: {
label: "Résultats", get() {
placeholder: "Choisissez un ou plusieurs résultats", return this.results.value;
},
set(value) {
this.results.value = value;
this.rebuildHiddenFieldValues("results");
},
}, },
},
}, },
}, async mounted() {
data() { await this.getSocialActionsList();
return {
actions: {
options: [], // array with multiselect options
value: [], // array with selected values
hiddenField: document.getElementById(
"export_filters_social_work_type_filter_form_actionType",
),
},
goals: {
options: [],
value: [],
hiddenField: document.getElementById(
"export_filters_social_work_type_filter_form_goal",
),
},
results: {
options: [],
value: [],
hiddenField: document.getElementById(
"export_filters_social_work_type_filter_form_result",
),
},
};
},
computed: {
action: {
get() {
return this.actions.value;
},
set(value) {
this.actions.value = value;
this.rebuildHiddenFieldValues("actions");
},
},
goal: {
get() {
return this.goals.value;
},
set(value) {
this.goals.value = value;
this.rebuildHiddenFieldValues("goals");
},
},
result: {
get() {
return this.results.value;
},
set(value) {
this.results.value = value;
this.rebuildHiddenFieldValues("results");
},
},
},
async mounted() {
await this.getSocialActionsList();
if ("" !== this.actions.hiddenField.value) { if ("" !== this.actions.hiddenField.value) {
const actionIds = this.actions.hiddenField.value.split(","); const actionIds = this.actions.hiddenField.value.split(",");
for (const aid of actionIds) { for (const aid of actionIds) {
let action = this.actions.options.find( let action = this.actions.options.find(
(a) => Number.parseInt(aid) === a.id, (a) => Number.parseInt(aid) === a.id,
); );
if (undefined !== action) { if (undefined !== action) {
this.action.push(action); this.action.push(action);
await this.selectAction(action); await this.selectAction(action);
} }
} }
}
if ("" !== this.goals.hiddenField.value) {
const goalsIds = this.goals.hiddenField.value
.split(",")
.map((s) => Number.parseInt(s));
for (const gid of goalsIds) {
let goal = this.goals.options.find((g) => gid === g.id);
if (undefined !== goal) {
this.goal.push(goal);
await this.selectGoal(goal);
}
}
}
if ("" !== this.results.hiddenField.value) {
const resultsIds = this.results.hiddenField.value
.split(",")
.map((s) => Number.parseInt(s));
for (const rid of resultsIds) {
let result = this.results.options.find((r) => rid === r.id);
if (undefined !== result) {
this.result.push(result);
}
}
}
},
methods: {
async getSocialActionsList() {
let actions = await getSocialActions();
this.actions.options = actions.toSorted(function (a, b) {
if (a.issue.ordering === b.issue.ordering) {
if (a.ordering === b.ordering) {
return 0;
}
if (a.ordering < b.ordering) {
return -1;
}
return 1;
} }
if (a.issue.ordering < b.issue.ordering) { if ("" !== this.goals.hiddenField.value) {
return -1; const goalsIds = this.goals.hiddenField.value
.split(",")
.map((s) => Number.parseInt(s));
for (const gid of goalsIds) {
let goal = this.goals.options.find((g) => gid === g.id);
if (undefined !== goal) {
this.goal.push(goal);
await this.selectGoal(goal);
}
}
} }
return 1; if ("" !== this.results.hiddenField.value) {
}); const resultsIds = this.results.hiddenField.value
.split(",")
return Promise.resolve(); .map((s) => Number.parseInt(s));
for (const rid of resultsIds) {
let result = this.results.options.find((r) => rid === r.id);
if (undefined !== result) {
this.result.push(result);
}
}
}
}, },
methods: {
async getSocialActionsList() {
let actions = await getSocialActions();
this.actions.options = actions.toSorted(function (a, b) {
if (a.issue.ordering === b.issue.ordering) {
if (a.ordering === b.ordering) {
return 0;
}
if (a.ordering < b.ordering) {
return -1;
}
return 1;
}
formatSocialAction({ text, issue }) { if (a.issue.ordering < b.issue.ordering) {
return text + " (" + issue.text + ")"; return -1;
}, }
/** return 1;
* Select/unselect in Action Multiselect });
* @param value
*/
async selectAction(value) {
//console.log('----'); console.log('select action', value.id);
let children = this.getChildrensFromParent(value);
this.addSelectedElement("actions", children);
let parentAndChildren = [...[value], ...children];
const promises = [];
parentAndChildren.forEach((elem) => {
promises.push(
getGoalByAction(elem.id).then((goals) => {
this.addElementInData("goals", goals);
return Promise.resolve(); return Promise.resolve();
}), },
);
promises.push( formatSocialAction({ text, issue }) {
getResultByAction(elem.id).then((results) => { return text + " (" + issue.text + ")";
this.addElementInData("results", results); },
/**
* Select/unselect in Action Multiselect
* @param value
*/
async selectAction(value) {
//console.log('----'); console.log('select action', value.id);
let children = this.getChildrensFromParent(value);
this.addSelectedElement("actions", children);
let parentAndChildren = [...[value], ...children];
const promises = [];
parentAndChildren.forEach((elem) => {
promises.push(
getGoalByAction(elem.id).then((goals) => {
this.addElementInData("goals", goals);
return Promise.resolve();
}),
);
promises.push(
getResultByAction(elem.id).then((results) => {
this.addElementInData("results", results);
return Promise.resolve();
}),
);
});
await Promise.all(promises);
return Promise.resolve(); return Promise.resolve();
}), },
);
});
await Promise.all(promises); unselectAction(value) {
return Promise.resolve(); getGoalByAction(value.id).then((goals) => {
[this.results.options, this.results.value] =
this.removeElementInData("goals", goals);
});
getResultByAction(value.id).then((results) => {
[this.results.options, this.results.value] =
this.removeElementInData("results", results);
});
},
/**
* Select/unselect in Goal Multiselect
* @param value
*/
async selectGoal(value) {
return getResultByGoal(value.id).then((results) => {
this.addElementInData("results", results);
});
},
unselectGoal(value) {
getResultByGoal(value.id).then(
(results) =>
([this.results.options, this.results.value] =
this.removeElementInData("results", results)),
).catch;
},
// selectResult(value) {
//console.log('----'); console.log('select result', value.id);
// },
// unselectResult(value) {
//console.log('----'); console.log('unselect result', value.id);
// },
/**
* Choose parent action will involve retaining the "children" actions.
* @param value
* @return array
*/
getChildrensFromParent(value) {
if (null === value.parent) {
let excludeParent = this.actions.options.filter(
(o) => o.parent !== null,
);
let children = excludeParent.filter(
(o) => o.parent.id === value.id,
);
//console.log("get childrens", children.map(e => e.id));
return children;
}
return [];
},
/**
* Add response elements in data target
* @param target string -> 'actions', 'goals' or 'results'
* @param response array of objects with fetch results
*/
addElementInData(target, response) {
let data = this[target];
let dump = [];
response.forEach((elem) => {
let found = data.options.some((e) => e.id === elem.id);
if (!found) {
data.options.push(elem);
dump.push(elem.id);
}
});
if (dump.length > 0) {
//console.log('push ' + dump.length + ' elems in', target, dump);
}
data.options.sort();
},
/**
* Remove response elements from data target
* @param target string -> 'actions', 'goals' or 'results'
* @param response array of objects with fetch results
* @returns data.<target>.options
*/
removeElementInData(target, response) {
let data = this[target];
let dump = [];
response.forEach((elem) => {
let found = data.options.some((e) => e.id === elem.id);
if (found) {
data.options = data.options.filter((e) => e.id !== elem.id);
dump.push(elem.id);
this.removeSelectedElement(target, elem);
}
});
if (dump.length > 0) {
//console.log('remove ' + dump.length + ' elems from ' + target + ' options', dump);
}
return [data.options, data.value];
},
/**
*
* @param target
* @param elements
*/
addSelectedElement(target, elements) {
let data = this[target];
let dump = [];
elements.forEach((elem) => {
let selected = data.value.some((e) => e.id === elem.id);
if (!selected) {
data.value.push(elem);
dump.push(elem.id);
// add in hiddenField
this.rebuildHiddenFieldValues(target);
}
});
if (dump.length > 0) {
//console.log('add ' + dump.length + ' selected elems in', target, dump);
}
},
/**
* Remove element from selected and from hiddenField
* @param target
* @param elem
*/
removeSelectedElement(target, elem) {
let data = this[target];
let selected = data.value.some((e) => e.id === elem.id);
if (selected) {
// remove from selected
data.value = data.value.filter((e) => e.id !== elem.id);
//console.log('remove ' + elem.id + ' from selected ' + target);
// remove from hiddenField
this.rebuildHiddenFieldValues(target);
// in any cases, remove should be recursive
this.unselectToNextField(target, elem);
}
},
/**
* When unselect Action, it could remove elements in goals multiselect.
* In that case, we have to unselect Goal to remove elements in results too.
* @param target
* @param elem
*/
unselectToNextField(target, elem) {
if (target === "goals") {
//console.log('!!!! target is goal: unselect goal', elem.id);
this.unselectGoal(elem);
//console.log('!!!! done');
}
},
/**
* Rebuild values serie (string) in target HiddenField
* @param target
*/
rebuildHiddenFieldValues(target) {
let data = this[target];
//console.log('rebuild hiddenFields ' + target + ' values :');
data.hiddenField.value = ""; // reset
data.value.forEach((elem) => {
data.hiddenField.value = this.addIdToValue(
data.hiddenField.value,
elem.id,
);
});
//console.log(data.hiddenField);
},
addIdToValue(string, id) {
let array = string ? string.split(",") : [];
array.push(id.toString());
let str = array.join();
return str;
},
transTitle({ title }) {
return title.fr; //TODO multilang
},
}, },
unselectAction(value) {
getGoalByAction(value.id).then((goals) => {
[this.results.options, this.results.value] = this.removeElementInData(
"goals",
goals,
);
});
getResultByAction(value.id).then((results) => {
[this.results.options, this.results.value] = this.removeElementInData(
"results",
results,
);
});
},
/**
* Select/unselect in Goal Multiselect
* @param value
*/
async selectGoal(value) {
return getResultByGoal(value.id).then((results) => {
this.addElementInData("results", results);
});
},
unselectGoal(value) {
getResultByGoal(value.id).then(
(results) =>
([this.results.options, this.results.value] =
this.removeElementInData("results", results)),
).catch;
},
// selectResult(value) {
//console.log('----'); console.log('select result', value.id);
// },
// unselectResult(value) {
//console.log('----'); console.log('unselect result', value.id);
// },
/**
* Choose parent action will involve retaining the "children" actions.
* @param value
* @return array
*/
getChildrensFromParent(value) {
if (null === value.parent) {
let excludeParent = this.actions.options.filter(
(o) => o.parent !== null,
);
let children = excludeParent.filter((o) => o.parent.id === value.id);
//console.log("get childrens", children.map(e => e.id));
return children;
}
return [];
},
/**
* Add response elements in data target
* @param target string -> 'actions', 'goals' or 'results'
* @param response array of objects with fetch results
*/
addElementInData(target, response) {
let data = this[target];
let dump = [];
response.forEach((elem) => {
let found = data.options.some((e) => e.id === elem.id);
if (!found) {
data.options.push(elem);
dump.push(elem.id);
}
});
if (dump.length > 0) {
//console.log('push ' + dump.length + ' elems in', target, dump);
}
data.options.sort();
},
/**
* Remove response elements from data target
* @param target string -> 'actions', 'goals' or 'results'
* @param response array of objects with fetch results
* @returns data.<target>.options
*/
removeElementInData(target, response) {
let data = this[target];
let dump = [];
response.forEach((elem) => {
let found = data.options.some((e) => e.id === elem.id);
if (found) {
data.options = data.options.filter((e) => e.id !== elem.id);
dump.push(elem.id);
this.removeSelectedElement(target, elem);
}
});
if (dump.length > 0) {
//console.log('remove ' + dump.length + ' elems from ' + target + ' options', dump);
}
return [data.options, data.value];
},
/**
*
* @param target
* @param elements
*/
addSelectedElement(target, elements) {
let data = this[target];
let dump = [];
elements.forEach((elem) => {
let selected = data.value.some((e) => e.id === elem.id);
if (!selected) {
data.value.push(elem);
dump.push(elem.id);
// add in hiddenField
this.rebuildHiddenFieldValues(target);
}
});
if (dump.length > 0) {
//console.log('add ' + dump.length + ' selected elems in', target, dump);
}
},
/**
* Remove element from selected and from hiddenField
* @param target
* @param elem
*/
removeSelectedElement(target, elem) {
let data = this[target];
let selected = data.value.some((e) => e.id === elem.id);
if (selected) {
// remove from selected
data.value = data.value.filter((e) => e.id !== elem.id);
//console.log('remove ' + elem.id + ' from selected ' + target);
// remove from hiddenField
this.rebuildHiddenFieldValues(target);
// in any cases, remove should be recursive
this.unselectToNextField(target, elem);
}
},
/**
* When unselect Action, it could remove elements in goals multiselect.
* In that case, we have to unselect Goal to remove elements in results too.
* @param target
* @param elem
*/
unselectToNextField(target, elem) {
if (target === "goals") {
//console.log('!!!! target is goal: unselect goal', elem.id);
this.unselectGoal(elem);
//console.log('!!!! done');
}
},
/**
* Rebuild values serie (string) in target HiddenField
* @param target
*/
rebuildHiddenFieldValues(target) {
let data = this[target];
//console.log('rebuild hiddenFields ' + target + ' values :');
data.hiddenField.value = ""; // reset
data.value.forEach((elem) => {
data.hiddenField.value = this.addIdToValue(
data.hiddenField.value,
elem.id,
);
});
//console.log(data.hiddenField);
},
addIdToValue(string, id) {
let array = string ? string.split(",") : [];
array.push(id.toString());
let str = array.join();
return str;
},
transTitle({ title }) {
return title.fr; //TODO multilang
},
},
}; };
</script> </script>

View File

@@ -1,53 +1,53 @@
<template> <template>
<ol class="breadcrumb"> <ol class="breadcrumb">
<li <li
v-for="s in steps" v-for="s in steps"
:key="s" :key="s"
class="breadcrumb-item" class="breadcrumb-item"
:class="{ active: step === s }" :class="{ active: step === s }"
> >
{{ $t("household_members_editor.app.steps." + s) }} {{ $t("household_members_editor.app.steps." + s) }}
</li> </li>
</ol> </ol>
<concerned v-if="step === 'concerned'" /> <concerned v-if="step === 'concerned'" />
<household v-if="step === 'household'" @ready-to-go="goToNext" /> <household v-if="step === 'household'" @ready-to-go="goToNext" />
<household-address v-if="step === 'household_address'" /> <household-address v-if="step === 'household_address'" />
<positioning v-if="step === 'positioning'" /> <positioning v-if="step === 'positioning'" />
<dates v-if="step === 'confirm'" /> <dates v-if="step === 'confirm'" />
<confirmation v-if="step === 'confirm'" /> <confirmation v-if="step === 'confirm'" />
<ul class="record_actions sticky-form-buttons"> <ul class="record_actions sticky-form-buttons">
<li class="cancel" v-if="step !== 'concerned'"> <li class="cancel" v-if="step !== 'concerned'">
<button class="btn btn-cancel" @click="goToPrevious"> <button class="btn btn-cancel" @click="goToPrevious">
{{ $t("household_members_editor.app.previous") }} {{ $t("household_members_editor.app.previous") }}
</button> </button>
</li> </li>
<li class="cancel" v-else-if="hasReturnPath"> <li class="cancel" v-else-if="hasReturnPath">
<button class="btn btn-cancel" @click="goToPrevious"> <button class="btn btn-cancel" @click="goToPrevious">
{{ $t("household_members_editor.app.cancel") }} {{ $t("household_members_editor.app.cancel") }}
</button> </button>
</li> </li>
<li v-if="step !== 'confirm'"> <li v-if="step !== 'confirm'">
<button <button
class="btn btn-action" class="btn btn-action"
@click="goToNext" @click="goToNext"
:disabled="!isNextAllowed" :disabled="!isNextAllowed"
> >
{{ $t("household_members_editor.app.next") }}&nbsp;<i {{ $t("household_members_editor.app.next") }}&nbsp;<i
class="fa fa-arrow-right" class="fa fa-arrow-right"
/> />
</button> </button>
</li> </li>
<li v-else> <li v-else>
<button <button
class="btn btn-save" class="btn btn-save"
@click="confirm" @click="confirm"
:disabled="hasWarnings || !lastStepIsSaveAllowed" :disabled="hasWarnings || !lastStepIsSaveAllowed"
> >
{{ $t("household_members_editor.app.save") }} {{ $t("household_members_editor.app.save") }}
</button> </button>
</li> </li>
</ul> </ul>
</template> </template>
<script> <script>
@@ -60,123 +60,123 @@ import Confirmation from "./components/Confirmation.vue";
import Positioning from "./components/Positioning"; import Positioning from "./components/Positioning";
export default { export default {
name: "App", name: "App",
components: { components: {
Positioning, Positioning,
Concerned, Concerned,
Household, Household,
HouseholdAddress, HouseholdAddress,
Dates, Dates,
Confirmation, Confirmation,
},
data() {
return {
step: "concerned",
};
},
computed: {
...mapState({
hasWarnings: (state) =>
state.warnings.length > 0 || state.errors.length > 0,
}),
steps() {
let s = ["concerned", "household"];
if (this.$store.getters.isHouseholdNew) {
s.push("household_address");
}
if (!this.$store.getters.isModeLeave) {
s.push("positioning");
}
s.push("confirm");
return s;
}, },
hasReturnPath() { data() {
let params = new URLSearchParams(window.location.search); return {
step: "concerned",
};
},
computed: {
...mapState({
hasWarnings: (state) =>
state.warnings.length > 0 || state.errors.length > 0,
}),
steps() {
let s = ["concerned", "household"];
return params.has("returnPath"); if (this.$store.getters.isHouseholdNew) {
}, s.push("household_address");
// return true if the next step is allowed }
isNextAllowed() {
switch (this.$data.step) {
case "concerned":
return this.$store.state.concerned.length > 0;
case "household":
return this.$store.state.mode !== null;
case "household_address":
return (
this.$store.getters.hasHouseholdAddress ||
this.$store.getters.isHouseholdForceNoAddress
);
case "positioning":
return (
this.$store.getters.hasHouseholdOrLeave &&
this.$store.getters.hasPersonsWellPositionnated
);
}
return false; if (!this.$store.getters.isModeLeave) {
}, s.push("positioning");
lastStepIsSaveAllowed() { }
let r =
!this.$store.getters.isHouseholdNew ||
(this.$store.state.numberOfChildren !== null &&
this.$store.state.householdCompositionType !== null);
console.log("is saved allowed ?", r);
return r; s.push("confirm");
},
},
methods: {
goToNext() {
console.log("go to next");
switch (this.$data.step) {
case "concerned":
this.$data.step = "household";
break;
case "household":
if (this.$store.getters.isHouseholdNew) {
this.$data.step = "household_address";
break;
} else if (this.$store.getters.isModeLeave) {
this.$data.step = "confirm";
break;
} else {
this.$data.step = "positioning";
break;
}
case "household_address":
this.$data.step = "positioning";
break;
case "positioning":
this.$data.step = "confirm";
break;
}
},
goToPrevious() {
if (this.$data.step === "concerned") {
let params = new URLSearchParams(window.location.search);
if (params.has("returnPath")) {
window.location.replace(params.get("returnPath"));
} else {
return;
}
}
let s = this.steps; return s;
let index = s.indexOf(this.$data.step); },
if (s[index - 1] === undefined) { hasReturnPath() {
throw Error("step not found"); let params = new URLSearchParams(window.location.search);
}
this.$data.step = s[index - 1]; return params.has("returnPath");
},
// return true if the next step is allowed
isNextAllowed() {
switch (this.$data.step) {
case "concerned":
return this.$store.state.concerned.length > 0;
case "household":
return this.$store.state.mode !== null;
case "household_address":
return (
this.$store.getters.hasHouseholdAddress ||
this.$store.getters.isHouseholdForceNoAddress
);
case "positioning":
return (
this.$store.getters.hasHouseholdOrLeave &&
this.$store.getters.hasPersonsWellPositionnated
);
}
return false;
},
lastStepIsSaveAllowed() {
let r =
!this.$store.getters.isHouseholdNew ||
(this.$store.state.numberOfChildren !== null &&
this.$store.state.householdCompositionType !== null);
console.log("is saved allowed ?", r);
return r;
},
}, },
confirm() { methods: {
this.$store.dispatch("confirm"); goToNext() {
console.log("go to next");
switch (this.$data.step) {
case "concerned":
this.$data.step = "household";
break;
case "household":
if (this.$store.getters.isHouseholdNew) {
this.$data.step = "household_address";
break;
} else if (this.$store.getters.isModeLeave) {
this.$data.step = "confirm";
break;
} else {
this.$data.step = "positioning";
break;
}
case "household_address":
this.$data.step = "positioning";
break;
case "positioning":
this.$data.step = "confirm";
break;
}
},
goToPrevious() {
if (this.$data.step === "concerned") {
let params = new URLSearchParams(window.location.search);
if (params.has("returnPath")) {
window.location.replace(params.get("returnPath"));
} else {
return;
}
}
let s = this.steps;
let index = s.indexOf(this.$data.step);
if (s[index - 1] === undefined) {
throw Error("step not found");
}
this.$data.step = s[index - 1];
},
confirm() {
this.$store.dispatch("confirm");
},
}, },
},
}; };
</script> </script>

View File

@@ -1,74 +1,88 @@
<template> <template>
<h2 class="mt-4"> <h2 class="mt-4">
{{ $t("household_members_editor.concerned.title") }} {{ $t("household_members_editor.concerned.title") }}
</h2> </h2>
<div v-if="noPerson"> <div v-if="noPerson">
<div class="alert alert-info"> <div class="alert alert-info">
{{ $t("household_members_editor.concerned.add_at_least_onePerson") }} {{
$t("household_members_editor.concerned.add_at_least_onePerson")
}}
</div>
</div> </div>
</div> <div v-else>
<div v-else> <p>
<p> {{
{{ $t("household_members_editor.concerned.persons_will_be_moved")
$t("household_members_editor.concerned.persons_will_be_moved") }}&nbsp;:
}}&nbsp;: </p>
</p>
<ul class="list-suggest remove-items inline"> <ul class="list-suggest remove-items inline">
<li v-for="c in concerned" :key="c.person.id" @click="removeConcerned(c)"> <li
<span><person-text :person="c.person" /></span> v-for="c in concerned"
</li> :key="c.person.id"
</ul> @click="removeConcerned(c)"
>
<span><person-text :person="c.person" /></span>
</li>
</ul>
<div <div
class="alert alert-info" class="alert alert-info"
v-if="concernedPersonsWithHouseholds.length > 0" v-if="concernedPersonsWithHouseholds.length > 0"
> >
<p> <p>
{{ $t("household_members_editor.concerned.persons_with_household") }} {{
</p> $t(
<ul v-for="c in concernedPersonsWithHouseholds" :key="c.person.id"> "household_members_editor.concerned.persons_with_household",
<li> )
{{ c.person.text }} }}
{{ </p>
$t( <ul v-for="c in concernedPersonsWithHouseholds" :key="c.person.id">
"household_members_editor.concerned.already_belongs_to_household", <li>
) {{ c.person.text }}
}} {{
<a $t(
target="_blank" "household_members_editor.concerned.already_belongs_to_household",
:href="this.makeHouseholdLink(c.person.current_household_id)" )
>{{ c.person.current_household_id }}</a }}
>. <a
target="_blank"
:href="
this.makeHouseholdLink(
c.person.current_household_id,
)
"
>{{ c.person.current_household_id }}</a
>.
</li>
</ul>
</div>
</div>
<ul class="record_actions">
<li class="add-persons">
<add-persons
button-title="household_members_editor.concerned.add_persons"
modal-title="household_members_editor.concerned.search"
:key="addPersons.key"
:options="addPersons.options"
@add-new-persons="addNewPersons"
ref="addPersons"
>
<!-- to cast child method -->
</add-persons>
</li> </li>
</ul> </ul>
</div>
</div>
<ul class="record_actions">
<li class="add-persons">
<add-persons
button-title="household_members_editor.concerned.add_persons"
modal-title="household_members_editor.concerned.search"
:key="addPersons.key"
:options="addPersons.options"
@add-new-persons="addNewPersons"
ref="addPersons"
>
<!-- to cast child method -->
</add-persons>
</li>
</ul>
</template> </template>
<style lang="scss"> <style lang="scss">
.move_to { .move_to {
.move_hint { .move_hint {
text-align: center; text-align: center;
display: inline-block; display: inline-block;
padding: 0.4rem 0.5rem; padding: 0.4rem 0.5rem;
} }
} }
</style> </style>
@@ -78,61 +92,62 @@ import AddPersons from "ChillPersonAssets/vuejs/_components/AddPersons.vue";
import PersonText from "ChillPersonAssets/vuejs/_components/Entity/PersonText.vue"; import PersonText from "ChillPersonAssets/vuejs/_components/Entity/PersonText.vue";
export default { export default {
name: "Concerned", name: "Concerned",
components: { components: {
AddPersons, AddPersons,
PersonText, PersonText,
},
computed: {
...mapState(["concerned", "household"]),
...mapGetters(["persons"]),
noPerson() {
return this.$store.getters.persons.length === 0;
}, },
concernedPersonsWithHouseholds() { computed: {
if (this.$store.state.household) { ...mapState(["concerned", "household"]),
return this.$store.state.concerned.filter( ...mapGetters(["persons"]),
(c) => noPerson() {
c.person.current_household_id !== null && return this.$store.getters.persons.length === 0;
c.person.current_household_id !== this.$store.state.household.id, },
); concernedPersonsWithHouseholds() {
} else { if (this.$store.state.household) {
return []; return this.$store.state.concerned.filter(
} (c) =>
}, c.person.current_household_id !== null &&
}, c.person.current_household_id !==
data() { this.$store.state.household.id,
return { );
addPersons: { } else {
key: "household_members_editor_concerned", return [];
options: { }
type: ["person"],
priority: null,
uniq: false,
}, },
},
};
},
methods: {
addNewPersons({ selected, modal }) {
selected.forEach(function (item) {
this.$store.dispatch("addConcerned", item.result);
}, this);
this.$refs.addPersons.resetSearch(); // to cast child method
modal.showModal = false;
}, },
removeConcerned(concerned) { data() {
console.log("removedconcerned", concerned); return {
addPersons: {
key: "household_members_editor_concerned",
options: {
type: ["person"],
priority: null,
uniq: false,
},
},
};
},
methods: {
addNewPersons({ selected, modal }) {
selected.forEach(function (item) {
this.$store.dispatch("addConcerned", item.result);
}, this);
this.$refs.addPersons.resetSearch(); // to cast child method
modal.showModal = false;
},
removeConcerned(concerned) {
console.log("removedconcerned", concerned);
if (!concerned.allowRemove) { if (!concerned.allowRemove) {
return; return;
} }
this.$store.dispatch("removePerson", concerned.person); this.$store.dispatch("removePerson", concerned.person);
},
makeHouseholdLink(id) {
return `/fr/person/household/${id}/summary`;
},
}, },
makeHouseholdLink(id) {
return `/fr/person/household/${id}/summary`;
},
},
}; };
</script> </script>

View File

@@ -1,20 +1,20 @@
<template> <template>
<div v-if="hasWarning" class="alert alert-warning"> <div v-if="hasWarning" class="alert alert-warning">
{{ $t("household_members_editor.confirmation.there_are_warnings") }} {{ $t("household_members_editor.confirmation.there_are_warnings") }}
</div> </div>
<p v-if="hasWarning"> <p v-if="hasWarning">
{{ $t("household_members_editor.confirmation.check_those_items") }} {{ $t("household_members_editor.confirmation.check_those_items") }}
</p> </p>
<ul> <ul>
<li v-for="(msg, i) in warnings" class="warning" :key="i"> <li v-for="(msg, i) in warnings" class="warning" :key="i">
{{ $t(msg.m, msg.a) }} {{ $t(msg.m, msg.a) }}
</li> </li>
<li v-for="(msg, i) in errors" class="error" :key="i"> <li v-for="(msg, i) in errors" class="error" :key="i">
{{ msg }} {{ msg }}
</li> </li>
</ul> </ul>
</template> </template>
<style scoped lang="scss"></style> <style scoped lang="scss"></style>
@@ -23,14 +23,14 @@
import { mapState } from "vuex"; import { mapState } from "vuex";
export default { export default {
name: "Confirmation", name: "Confirmation",
computed: { computed: {
...mapState({ ...mapState({
hasWarnings: (state) => hasWarnings: (state) =>
state.warnings.length > 0 || state.errors.length > 0, state.warnings.length > 0 || state.errors.length > 0,
warnings: (state) => state.warnings, warnings: (state) => state.warnings,
errors: (state) => state.errors, errors: (state) => state.errors,
}), }),
}, },
}; };
</script> </script>

View File

@@ -1,35 +1,37 @@
<template> <template>
<div class="flex-table mb-5" v-if="hasHousehold"> <div class="flex-table mb-5" v-if="hasHousehold">
<div class="item-bloc"> <div class="item-bloc">
<household-render-box :household="fakeHouseholdWithConcerned" /> <household-render-box :household="fakeHouseholdWithConcerned" />
</div> </div>
</div> </div>
<div class="flex-table" v-if="isModeLeave"> <div class="flex-table" v-if="isModeLeave">
<div class="item-bloc"> <div class="item-bloc">
<section> <section>
<div class="item-row"> <div class="item-row">
<div class="item-col"> <div class="item-col">
<div class="h4"> <div class="h4">
<span class="fa-stack fa-lg"> <span class="fa-stack fa-lg">
<i class="fa fa-home fa-stack-1x" /> <i class="fa fa-home fa-stack-1x" />
<i class="fa fa-ban fa-stack-2x text-danger" /> <i class="fa fa-ban fa-stack-2x text-danger" />
</span> </span>
{{ {{
$t("household_members_editor.household.leave_without_household") $t(
}} "household_members_editor.household.leave_without_household",
</div> )
</div> }}
</div>
</div>
</div>
<div class="item-row">
{{
$t(
"household_members_editor.household.will_leave_any_household_explanation",
)
}}
</div>
</section>
</div> </div>
<div class="item-row">
{{
$t(
"household_members_editor.household.will_leave_any_household_explanation",
)
}}
</div>
</section>
</div> </div>
</div>
</template> </template>
<script> <script>
@@ -37,17 +39,17 @@ import { mapGetters } from "vuex";
import HouseholdRenderBox from "ChillPersonAssets/vuejs/_components/Entity/HouseholdRenderBox.vue"; import HouseholdRenderBox from "ChillPersonAssets/vuejs/_components/Entity/HouseholdRenderBox.vue";
export default { export default {
name: "CurrentHousehold", name: "CurrentHousehold",
components: { components: {
HouseholdRenderBox, HouseholdRenderBox,
}, },
computed: { computed: {
...mapGetters([ ...mapGetters([
"hasHousehold", "hasHousehold",
"fakeHouseholdWithConcerned", "fakeHouseholdWithConcerned",
"isModeLeave", "isModeLeave",
]), ]),
}, },
}; };
</script> </script>

View File

@@ -1,83 +1,83 @@
<template> <template>
<current-household /> <current-household />
<h2>{{ $t("household_members_editor.dates.dates_title") }}</h2> <h2>{{ $t("household_members_editor.dates.dates_title") }}</h2>
<div class="mb-3 row">
<label for="start_date" class="col-form-label col-sm-4 required">
{{ $t("household_members_editor.dates.start_date") }}
</label>
<div class="col-sm-8">
<input type="date" v-model="startDate" class="form-control" />
</div>
</div>
<div v-if="this.isHouseholdNew">
<h2>{{ $t("household_members_editor.composition.composition") }}</h2>
<div class="mb-3 row"> <div class="mb-3 row">
<label class="col-form-label col-sm-4 required">{{ <label for="start_date" class="col-form-label col-sm-4 required">
$t("household_members_editor.composition.household_composition") {{ $t("household_members_editor.dates.start_date") }}
}}</label> </label>
<div class="col-sm-8"> <div class="col-sm-8">
<select <input type="date" v-model="startDate" class="form-control" />
v-model="householdCompositionType" </div>
class="form-select form-control"
>
<option
v-for="t in householdCompositionTypes"
:key="t.id"
:value="t.id"
>
{{ localizeString(t.label) }}
</option>
</select>
</div>
</div> </div>
<div class="mb-3 row">
<label class="col-form-label col-sm-4 required">{{ <div v-if="this.isHouseholdNew">
$t("household_members_editor.composition.number_of_children") <h2>{{ $t("household_members_editor.composition.composition") }}</h2>
}}</label> <div class="mb-3 row">
<div class="col-sm-8"> <label class="col-form-label col-sm-4 required">{{
<input $t("household_members_editor.composition.household_composition")
type="number" }}</label>
v-model="numberOfChildren" <div class="col-sm-8">
min="0" <select
max="30" v-model="householdCompositionType"
class="form-control" class="form-select form-control"
/> >
</div> <option
v-for="t in householdCompositionTypes"
:key="t.id"
:value="t.id"
>
{{ localizeString(t.label) }}
</option>
</select>
</div>
</div>
<div class="mb-3 row">
<label class="col-form-label col-sm-4 required">{{
$t("household_members_editor.composition.number_of_children")
}}</label>
<div class="col-sm-8">
<input
type="number"
v-model="numberOfChildren"
min="0"
max="30"
class="form-control"
/>
</div>
</div>
<div v-if="this.displayDependents" class="mb-3 row">
<label class="col-form-label col-sm-4 required">{{
$t("household_members_editor.composition.number_of_dependents")
}}</label>
<div class="col-sm-8">
<input
type="number"
v-model="numberOfDependents"
min="0"
max="30"
class="form-control"
/>
</div>
</div>
<div v-if="this.displayDependents" class="mb-3 row">
<label class="col-form-label col-sm-4 required">{{
$t(
"household_members_editor.composition.number_of_dependents_with_disabilities",
)
}}</label>
<div class="col-sm-8">
<input
type="number"
v-model="numberOfDependentsWithDisabilities"
min="0"
max="30"
class="form-control"
/>
</div>
</div>
</div> </div>
<div v-if="this.displayDependents" class="mb-3 row">
<label class="col-form-label col-sm-4 required">{{
$t("household_members_editor.composition.number_of_dependents")
}}</label>
<div class="col-sm-8">
<input
type="number"
v-model="numberOfDependents"
min="0"
max="30"
class="form-control"
/>
</div>
</div>
<div v-if="this.displayDependents" class="mb-3 row">
<label class="col-form-label col-sm-4 required">{{
$t(
"household_members_editor.composition.number_of_dependents_with_disabilities",
)
}}</label>
<div class="col-sm-8">
<input
type="number"
v-model="numberOfDependentsWithDisabilities"
min="0"
max="30"
class="form-control"
/>
</div>
</div>
</div>
</template> </template>
<script> <script>
@@ -86,71 +86,74 @@ import { mapGetters, mapState } from "vuex";
import { localizeString } from "ChillMainAssets/lib/localizationHelper/localizationHelper"; import { localizeString } from "ChillMainAssets/lib/localizationHelper/localizationHelper";
export default { export default {
name: "Dates", name: "Dates",
methods: { localizeString }, methods: { localizeString },
components: { components: {
CurrentHousehold, CurrentHousehold,
},
computed: {
...mapState(["householdCompositionTypes"]),
...mapGetters(["isHouseholdNew"]),
displayDependents: {
get() {
return window.household_members_editor_data.displayDependents;
},
}, },
householdCompositionType: { computed: {
get() { ...mapState(["householdCompositionTypes"]),
if (this.$store.state.householdCompositionType !== null) { ...mapGetters(["isHouseholdNew"]),
return this.$store.state.householdCompositionType.id; displayDependents: {
} get() {
return null; return window.household_members_editor_data.displayDependents;
}, },
set(value) { },
this.$store.dispatch("setHouseholdCompositionType", value); householdCompositionType: {
}, get() {
}, if (this.$store.state.householdCompositionType !== null) {
numberOfChildren: { return this.$store.state.householdCompositionType.id;
get() { }
return this.$store.state.numberOfChildren; return null;
}, },
set(value) { set(value) {
this.$store.commit("setNumberOfChildren", value); this.$store.dispatch("setHouseholdCompositionType", value);
}, },
}, },
numberOfDependents: { numberOfChildren: {
get() { get() {
return this.$store.state.numberOfDependents; return this.$store.state.numberOfChildren;
}, },
set(value) { set(value) {
this.$store.commit("setNumberOfDependents", value); this.$store.commit("setNumberOfChildren", value);
}, },
}, },
numberOfDependentsWithDisabilities: { numberOfDependents: {
get() { get() {
return this.$store.state.numberOfDependentsWithDisabilities; return this.$store.state.numberOfDependents;
}, },
set(value) { set(value) {
this.$store.commit("setNumberOfDependentsWithDisabilities", value); this.$store.commit("setNumberOfDependents", value);
}, },
}, },
startDate: { numberOfDependentsWithDisabilities: {
get() { get() {
return this.$store.state.startDate; return this.$store.state.numberOfDependentsWithDisabilities;
// return [ },
// this.$store.state.startDate.getFullYear(), set(value) {
// (this.$store.state.startDate.getMonth() + 1).toString().padStart(2, '0'), this.$store.commit(
// this.$store.state.startDate.getDate().toString().padStart(2, '0') "setNumberOfDependentsWithDisabilities",
// ].join('-'); value,
}, );
set(value) { },
// let },
// [year, month, day] = value.split('-'), startDate: {
// dValue = new Date(year, month-1, day); get() {
return this.$store.state.startDate;
// return [
// this.$store.state.startDate.getFullYear(),
// (this.$store.state.startDate.getMonth() + 1).toString().padStart(2, '0'),
// this.$store.state.startDate.getDate().toString().padStart(2, '0')
// ].join('-');
},
set(value) {
// let
// [year, month, day] = value.split('-'),
// dValue = new Date(year, month-1, day);
this.$store.dispatch("setStartDate", value); this.$store.dispatch("setStartDate", value);
}, },
},
}, },
},
}; };
</script> </script>

View File

@@ -1,116 +1,130 @@
<template> <template>
<h2 class="mt-4"> <h2 class="mt-4">
{{ $t("household_members_editor.household_part") }} {{ $t("household_members_editor.household_part") }}
</h2> </h2>
<div class="alert alert-info" v-if="!hasHousehold"> <div class="alert alert-info" v-if="!hasHousehold">
{{ $t("household_members_editor.household.no_household_choose_one") }} {{ $t("household_members_editor.household.no_household_choose_one") }}
</div>
<template v-else>
<current-household />
</template>
<div v-if="hasHouseholdSuggestion" class="householdSuggestions my-5">
<h4 class="mb-3">
{{ $t("household_members_editor.household.household_suggested") }}
</h4>
<p>
{{
$t("household_members_editor.household.household_suggested_explanation")
}}
</p>
<div class="accordion" id="householdSuggestions">
<div class="accordion-item">
<h2 class="accordion-header" id="heading_household_suggestions">
<button
v-if="!showHouseholdSuggestion"
class="accordion-button collapsed"
type="button"
data-bs-toggle="collapse"
aria-expanded="false"
@click="toggleHouseholdSuggestion"
>
{{
$tc(
"household_members_editor.show_household_suggestion",
countHouseholdSuggestion,
)
}}
</button>
<button
v-if="showHouseholdSuggestion"
class="accordion-button"
type="button"
data-bs-toggle="collapse"
aria-expanded="true"
@click="toggleHouseholdSuggestion"
>
{{ $t("household_members_editor.hide_household_suggestion") }}
</button>
<!-- disabled bootstrap behaviour: data-bs-target="#collapse_household_suggestions" aria-controls="collapse_household_suggestions" -->
</h2>
<div
class="accordion-collapse"
id="collapse_household_suggestions"
aria-labelledby="heading_household_suggestions"
data-bs-parent="#householdSuggestions"
>
<div v-if="showHouseholdSuggestion">
<div class="flex-table householdSuggestionList">
<div
v-for="(s, i) in getSuggestions"
class="item-bloc"
:key="`householdSuggestions-${i}`"
>
<household-render-box :household="s.household" />
<ul class="record_actions">
<li>
<button
class="btn btn-sm btn-choose"
@click="selectHousehold(s.household)"
>
{{ $t("household_members_editor.select_household") }}
</button>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div> </div>
</div> <template v-else>
<current-household />
</template>
<ul class="record_actions"> <div v-if="hasHouseholdSuggestion" class="householdSuggestions my-5">
<li v-if="hasHousehold"> <h4 class="mb-3">
<button @click="resetMode" class="btn btn-sm btn-misc"> {{ $t("household_members_editor.household.household_suggested") }}
{{ $t("household_members_editor.household.reset_mode") }} </h4>
</button> <p>
</li> {{
<li v-if="!hasHousehold" class="add-persons"> $t(
<add-persons "household_members_editor.household.household_suggested_explanation",
modal-title="Chercher un ménage existant" )
button-title="Chercher un ménage existant" }}
:key="addPersons.key" </p>
:options="addPersons.options" <div class="accordion" id="householdSuggestions">
@add-new-persons="pickHouseholdFound" <div class="accordion-item">
ref="pickHousehold" <h2 class="accordion-header" id="heading_household_suggestions">
> <button
<!-- to cast child method --> v-if="!showHouseholdSuggestion"
</add-persons> class="accordion-button collapsed"
</li> type="button"
<li v-if="!hasHousehold"> data-bs-toggle="collapse"
<button @click="setModeNew" class="btn btn-sm btn-create"> aria-expanded="false"
{{ $t("household_members_editor.household.create_household") }} @click="toggleHouseholdSuggestion"
</button> >
</li> {{
<li v-if="isModeLeaveAllowed && !hasHousehold"> $tc(
<button @click="setModeLeave" class="btn btn-sm btn-misc"> "household_members_editor.show_household_suggestion",
<i class="fa fa-sign-out" /> countHouseholdSuggestion,
{{ $t("household_members_editor.household.leave") }} )
</button> }}
</li> </button>
</ul> <button
v-if="showHouseholdSuggestion"
class="accordion-button"
type="button"
data-bs-toggle="collapse"
aria-expanded="true"
@click="toggleHouseholdSuggestion"
>
{{
$t(
"household_members_editor.hide_household_suggestion",
)
}}
</button>
<!-- disabled bootstrap behaviour: data-bs-target="#collapse_household_suggestions" aria-controls="collapse_household_suggestions" -->
</h2>
<div
class="accordion-collapse"
id="collapse_household_suggestions"
aria-labelledby="heading_household_suggestions"
data-bs-parent="#householdSuggestions"
>
<div v-if="showHouseholdSuggestion">
<div class="flex-table householdSuggestionList">
<div
v-for="(s, i) in getSuggestions"
class="item-bloc"
:key="`householdSuggestions-${i}`"
>
<household-render-box
:household="s.household"
/>
<ul class="record_actions">
<li>
<button
class="btn btn-sm btn-choose"
@click="
selectHousehold(s.household)
"
>
{{
$t(
"household_members_editor.select_household",
)
}}
</button>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<ul class="record_actions">
<li v-if="hasHousehold">
<button @click="resetMode" class="btn btn-sm btn-misc">
{{ $t("household_members_editor.household.reset_mode") }}
</button>
</li>
<li v-if="!hasHousehold" class="add-persons">
<add-persons
modal-title="Chercher un ménage existant"
button-title="Chercher un ménage existant"
:key="addPersons.key"
:options="addPersons.options"
@add-new-persons="pickHouseholdFound"
ref="pickHousehold"
>
<!-- to cast child method -->
</add-persons>
</li>
<li v-if="!hasHousehold">
<button @click="setModeNew" class="btn btn-sm btn-create">
{{ $t("household_members_editor.household.create_household") }}
</button>
</li>
<li v-if="isModeLeaveAllowed && !hasHousehold">
<button @click="setModeLeave" class="btn btn-sm btn-misc">
<i class="fa fa-sign-out" />
{{ $t("household_members_editor.household.leave") }}
</button>
</li>
</ul>
</template> </template>
<script> <script>
@@ -120,154 +134,156 @@ import CurrentHousehold from "./CurrentHousehold";
import AddPersons from "ChillPersonAssets/vuejs/_components/AddPersons"; import AddPersons from "ChillPersonAssets/vuejs/_components/AddPersons";
export default { export default {
name: "Household", name: "Household",
components: { components: {
AddPersons, AddPersons,
CurrentHousehold, CurrentHousehold,
HouseholdRenderBox, HouseholdRenderBox,
}, },
emits: ["readyToGo"], emits: ["readyToGo"],
data() { data() {
return { return {
addPersons: { addPersons: {
key: "household_find", key: "household_find",
options: { options: {
type: ["household"], type: ["household"],
priority: null, priority: null,
uniq: true, uniq: true,
button: { button: {
size: "btn-sm", size: "btn-sm",
type: "btn-search", type: "btn-search",
}, },
}, },
},
addAddress: {
key: "household_new",
options: {
useDate: {
validFrom: false,
validTo: false,
},
onlyButton: true,
button: {
text: {
create: "household_members_editor.household.set_address",
edit: "household_members_editor.household.update_address",
}, },
}, addAddress: {
title: { key: "household_new",
create: "household_members_editor.household.create_new_address", options: {
edit: "household_members_editor.household.update_address_title", useDate: {
}, validFrom: false,
validTo: false,
},
onlyButton: true,
button: {
text: {
create: "household_members_editor.household.set_address",
edit: "household_members_editor.household.update_address",
},
},
title: {
create: "household_members_editor.household.create_new_address",
edit: "household_members_editor.household.update_address_title",
},
},
},
};
},
computed: {
...mapGetters([
"isModeNewAllowed",
"isModeLeaveAllowed",
"getSuggestions",
"hasHousehold",
"isHouseholdNew",
"hasHouseholdSuggestion",
"countHouseholdSuggestion",
"filterHouseholdSuggestionByAccompanyingPeriod",
"hasAddressSuggestion",
"countAddressSuggestion",
"filterAddressesSuggestion",
"hasHouseholdAddress",
"isModeLeave",
"getAddressContext",
]),
...mapState([
"household",
"showHouseholdSuggestion",
"showAddressSuggestion",
"mode",
]),
household() {
return this.$store.state.household;
},
allowHouseholdSearch() {
return false;
return (
this.$store.state.allowHouseholdSearch &&
!this.$store.getters.hasHousehold
);
},
isHouseholdNewDesactivated() {
return (
this.$store.state.mode !== null &&
!this.$store.getters.isHouseholdNew
);
},
isHouseholdLeaveDesactivated() {
return (
this.$store.state.mode !== null &&
this.$store.state.mode !== "leave"
);
}, },
},
};
},
computed: {
...mapGetters([
"isModeNewAllowed",
"isModeLeaveAllowed",
"getSuggestions",
"hasHousehold",
"isHouseholdNew",
"hasHouseholdSuggestion",
"countHouseholdSuggestion",
"filterHouseholdSuggestionByAccompanyingPeriod",
"hasAddressSuggestion",
"countAddressSuggestion",
"filterAddressesSuggestion",
"hasHouseholdAddress",
"isModeLeave",
"getAddressContext",
]),
...mapState([
"household",
"showHouseholdSuggestion",
"showAddressSuggestion",
"mode",
]),
household() {
return this.$store.state.household;
}, },
allowHouseholdSearch() { methods: {
return false; setModeNew() {
return ( this.$store.dispatch("createHousehold");
this.$store.state.allowHouseholdSearch && this.$emit("readyToGo");
!this.$store.getters.hasHousehold },
); setModeLeave() {
this.$store.dispatch("forceLeaveWithoutHousehold");
this.$emit("readyToGo");
},
resetMode() {
this.$store.commit("resetMode");
},
addressChanged(payload) {
console.log("addressChanged", payload);
this.$store.dispatch("setHouseholdNewAddress", payload.address);
},
selectHousehold(h) {
this.$store.dispatch("selectHousehold", h);
this.$emit("readyToGo");
},
pickHouseholdFound({ selected, modal }) {
selected.forEach(function (item) {
this.selectHousehold(item.result);
}, this);
this.$refs.pickHousehold.resetSearch(); // to cast child method
modal.showModal = false;
},
removeHouseholdAddress() {
this.$store.commit("removeHouseholdAddress");
},
toggleHouseholdSuggestion() {
this.$store.commit("toggleHouseholdSuggestion");
},
}, },
isHouseholdNewDesactivated() {
return (
this.$store.state.mode !== null && !this.$store.getters.isHouseholdNew
);
},
isHouseholdLeaveDesactivated() {
return (
this.$store.state.mode !== null && this.$store.state.mode !== "leave"
);
},
},
methods: {
setModeNew() {
this.$store.dispatch("createHousehold");
this.$emit("readyToGo");
},
setModeLeave() {
this.$store.dispatch("forceLeaveWithoutHousehold");
this.$emit("readyToGo");
},
resetMode() {
this.$store.commit("resetMode");
},
addressChanged(payload) {
console.log("addressChanged", payload);
this.$store.dispatch("setHouseholdNewAddress", payload.address);
},
selectHousehold(h) {
this.$store.dispatch("selectHousehold", h);
this.$emit("readyToGo");
},
pickHouseholdFound({ selected, modal }) {
selected.forEach(function (item) {
this.selectHousehold(item.result);
}, this);
this.$refs.pickHousehold.resetSearch(); // to cast child method
modal.showModal = false;
},
removeHouseholdAddress() {
this.$store.commit("removeHouseholdAddress");
},
toggleHouseholdSuggestion() {
this.$store.commit("toggleHouseholdSuggestion");
},
},
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.filtered { .filtered {
filter: grayscale(1) opacity(0.6); filter: grayscale(1) opacity(0.6);
} }
.filteredButActive { .filteredButActive {
filter: grayscale(1) opacity(0.6); filter: grayscale(1) opacity(0.6);
&:hover { &:hover {
filter: unset; filter: unset;
} }
} }
div#household_members_editor div, div#household_members_editor div,
div.householdSuggestionList { div.householdSuggestionList {
&.flex-table { &.flex-table {
margin: 0; margin: 0;
div.item-bloc div.item-row div.item-col { div.item-bloc div.item-row div.item-col {
&:first-child { &:first-child {
width: 25%; width: 25%;
} }
&:last-child { &:last-child {
display: initial; display: initial;
} }
}
} }
}
} }
</style> </style>

View File

@@ -1,26 +1,30 @@
<template> <template>
<current-household /> <current-household />
<ul class="record_actions"> <ul class="record_actions">
<!-- <li v-if="!hasHouseholdAddress && !isHouseholdForceAddress"> <!-- <li v-if="!hasHouseholdAddress && !isHouseholdForceAddress">
<button class="btn btn-misc" @click="markNoAddress"> <button class="btn btn-misc" @click="markNoAddress">
{{ $t('household_members_editor.household_address.mark_no_address') }} {{ $t('household_members_editor.household_address.mark_no_address') }}
</button> </button>
</li> --> </li> -->
<li v-if="!hasHouseholdAddress"> <li v-if="!hasHouseholdAddress">
<add-address <add-address
:context="getAddressContext" :context="getAddressContext"
:key="addAddress.key" :key="addAddress.key"
:options="addAddress.options" :options="addAddress.options"
:address-changed-callback="addressChanged" :address-changed-callback="addressChanged"
/> />
</li> </li>
<li v-if="hasHouseholdAddress"> <li v-if="hasHouseholdAddress">
<button class="btn btn-remove" @click="removeHouseholdAddress"> <button class="btn btn-remove" @click="removeHouseholdAddress">
{{ $t("household_members_editor.household_address.remove_address") }} {{
</button> $t(
</li> "household_members_editor.household_address.remove_address",
</ul> )
}}
</button>
</li>
</ul>
</template> </template>
<script> <script>
@@ -29,56 +33,55 @@ import CurrentHousehold from "./CurrentHousehold";
import { mapGetters } from "vuex"; import { mapGetters } from "vuex";
export default { export default {
name: "HouseholdAddress.vue", name: "HouseholdAddress.vue",
components: { components: {
CurrentHousehold, CurrentHousehold,
AddAddress, AddAddress,
}, },
data() { data() {
return { return {
addAddress: { addAddress: {
key: "household_new", key: "household_new",
options: { options: {
useDate: { useDate: {
validFrom: false, validFrom: false,
validTo: false, validTo: false,
}, },
onlyButton: true, onlyButton: true,
button: { button: {
text: { text: {
create: "household_members_editor.household_address.set_address", create: "household_members_editor.household_address.set_address",
edit: "household_members_editor.household_address.update_address", edit: "household_members_editor.household_address.update_address",
},
},
title: {
create: "household_members_editor.household_address.create_new_address",
edit: "household_members_editor.household_address.update_address_title",
},
},
}, },
}, };
title: { },
create: computed: {
"household_members_editor.household_address.create_new_address", ...mapGetters([
edit: "household_members_editor.household_address.update_address_title", "isHouseholdNew",
}, "hasHouseholdAddress",
"getAddressContext",
"isHouseholdForceNoAddress",
]),
},
methods: {
addressChanged(payload) {
console.log("addressChanged", payload);
this.$store.dispatch("setHouseholdNewAddress", payload.address);
},
markNoAddress() {
this.$store.commit("markHouseholdNoAddress");
},
removeHouseholdAddress() {
this.$store.commit("removeHouseholdAddress");
}, },
},
};
},
computed: {
...mapGetters([
"isHouseholdNew",
"hasHouseholdAddress",
"getAddressContext",
"isHouseholdForceNoAddress",
]),
},
methods: {
addressChanged(payload) {
console.log("addressChanged", payload);
this.$store.dispatch("setHouseholdNewAddress", payload.address);
}, },
markNoAddress() {
this.$store.commit("markHouseholdNoAddress");
},
removeHouseholdAddress() {
this.$store.commit("removeHouseholdAddress");
},
},
}; };
</script> </script>

View File

@@ -1,97 +1,104 @@
<template> <template>
<div class="item-bloc"> <div class="item-bloc">
<div class="item-row"> <div class="item-row">
<div class="item-col"> <div class="item-col">
<div> <div>
<person-render-box <person-render-box
render="badge" render="badge"
:options="{}" :options="{}"
:person="conc.person" :person="conc.person"
/> />
<span v-if="isHolder" class="badge bg-primary holder"> <span v-if="isHolder" class="badge bg-primary holder">
{{ $t("household_members_editor.holder") }} {{ $t("household_members_editor.holder") }}
</span> </span>
</div>
<div v-if="conc.person.birthdate !== null">
{{
$t("person.born", {
gender: conc.person.gender.genderTranslation,
})
}}
</div>
</div>
<div class="item-col">
<ul class="list-content fa-ul">
<li>
<i class="fa fa-li fa-map-marker" />
<span class="chill-no-data-statement"
>Sans adresse</span
>
</li>
</ul>
</div>
</div> </div>
<div v-if="conc.person.birthdate !== null">
{{ <div class="item-row comment">
$t("person.born", { gender: conc.person.gender.genderTranslation }) <comment-editor v-model="comment" />
}} </div>
<div class="item-row participation-details">
<div v-if="conc.position.allowHolder" class="action">
<button
class="btn"
:class="{
'btn-primary': isHolder,
'btn-secondary': !isHolder,
}"
@click="toggleHolder"
>
{{
$t(
isHolder
? "household_members_editor.is_holder"
: "household_members_editor.is_not_holder",
)
}}
</button>
</div>
<div>
<button @click="removePosition" class="btn btn-outline-primary">
{{
$t("household_members_editor.remove_position", {
position: conc.position.label.fr,
})
}}
</button>
</div>
<div>
<button
v-if="conc.allowRemove"
@click="removeConcerned"
class="btn btn-primary"
>
{{ $t("household_members_editor.remove_concerned") }}
</button>
</div>
</div> </div>
</div>
<div class="item-col">
<ul class="list-content fa-ul">
<li>
<i class="fa fa-li fa-map-marker" />
<span class="chill-no-data-statement">Sans adresse</span>
</li>
</ul>
</div>
</div> </div>
<div class="item-row comment">
<comment-editor v-model="comment" />
</div>
<div class="item-row participation-details">
<div v-if="conc.position.allowHolder" class="action">
<button
class="btn"
:class="{ 'btn-primary': isHolder, 'btn-secondary': !isHolder }"
@click="toggleHolder"
>
{{
$t(
isHolder
? "household_members_editor.is_holder"
: "household_members_editor.is_not_holder",
)
}}
</button>
</div>
<div>
<button @click="removePosition" class="btn btn-outline-primary">
{{
$t("household_members_editor.remove_position", {
position: conc.position.label.fr,
})
}}
</button>
</div>
<div>
<button
v-if="conc.allowRemove"
@click="removeConcerned"
class="btn btn-primary"
>
{{ $t("household_members_editor.remove_concerned") }}
</button>
</div>
</div>
</div>
</template> </template>
<style scoped lang="scss"> <style scoped lang="scss">
.drag-icon { .drag-icon {
height: 1.1em; height: 1.1em;
margin-right: 0.5em; margin-right: 0.5em;
} }
div.participation-details { div.participation-details {
display: flex; display: flex;
flex-direction: row !important; flex-direction: row !important;
justify-content: flex-end; justify-content: flex-end;
.action { .action {
align-self: flex-start; align-self: flex-start;
margin-right: auto; margin-right: auto;
} }
} }
.holder { .holder {
display: inline; display: inline;
vertical-align: super; vertical-align: super;
font-size: 0.6em; font-size: 0.6em;
} }
</style> </style>
@@ -101,41 +108,44 @@ import PersonRenderBox from "ChillPersonAssets/vuejs/_components/Entity/PersonRe
import CommentEditor from "ChillMainAssets/vuejs/_components/CommentEditor/CommentEditor.vue"; import CommentEditor from "ChillMainAssets/vuejs/_components/CommentEditor/CommentEditor.vue";
export default { export default {
name: "MemberDetails", name: "MemberDetails",
components: { components: {
PersonRenderBox, PersonRenderBox,
CommentEditor, CommentEditor,
},
props: ["conc"],
computed: {
...mapGetters(["concByPersonId"]),
classicEditor: () => ClassicEditor,
editorConfig: () => classicEditorConfig,
isHolder() {
return this.conc.holder;
}, },
comment: { props: ["conc"],
get() { computed: {
return this.conc.comment; ...mapGetters(["concByPersonId"]),
}, classicEditor: () => ClassicEditor,
set(text) { editorConfig: () => classicEditorConfig,
console.log("set comment"); isHolder() {
console.log("comment", text); return this.conc.holder;
},
comment: {
get() {
return this.conc.comment;
},
set(text) {
console.log("set comment");
console.log("comment", text);
this.$store.dispatch("setComment", { conc: this.conc, comment: text }); this.$store.dispatch("setComment", {
}, conc: this.conc,
comment: text,
});
},
},
}, },
}, methods: {
methods: { toggleHolder() {
toggleHolder() { this.$store.dispatch("toggleHolder", this.conc);
this.$store.dispatch("toggleHolder", this.conc); },
removePosition() {
this.$store.dispatch("removePosition", this.conc);
},
removeConcerned() {
this.$store.dispatch("removeConcerned", this.conc);
},
}, },
removePosition() {
this.$store.dispatch("removePosition", this.conc);
},
removeConcerned() {
this.$store.dispatch("removeConcerned", this.conc);
},
},
}; };
</script> </script>

View File

@@ -1,30 +1,30 @@
<template> <template>
<comment-editor v-model="content" /> <comment-editor v-model="content" />
</template> </template>
<script> <script>
import CommentEditor from "ChillMainAssets/vuejs/_components/CommentEditor/CommentEditor.vue"; import CommentEditor from "ChillMainAssets/vuejs/_components/CommentEditor/CommentEditor.vue";
export default { export default {
name: "PersonComment.vue", name: "PersonComment.vue",
components: { components: {
CommentEditor, CommentEditor,
}, },
props: ["conc"], props: ["conc"],
computed: { computed: {
content: { content: {
get() { get() {
return this.$props.conc.comment || ""; return this.$props.conc.comment || "";
}, },
set(value) { set(value) {
console.log("set content", value); console.log("set content", value);
this.$store.commit("setComment", { this.$store.commit("setComment", {
conc: this.$props.conc, conc: this.$props.conc,
comment: value, comment: value,
}); });
}, },
},
}, },
},
}; };
</script> </script>

View File

@@ -1,55 +1,57 @@
<template> <template>
<current-household /> <current-household />
<h2> <h2>
{{ $t("household_members_editor.positioning.persons_to_positionnate") }} {{ $t("household_members_editor.positioning.persons_to_positionnate") }}
</h2> </h2>
<div class="list-household-members flex-table"> <div class="list-household-members flex-table">
<div v-for="conc in concerned" class="item-bloc" :key="conc.person.id"> <div v-for="conc in concerned" class="item-bloc" :key="conc.person.id">
<div class="pick-position item-row"> <div class="pick-position item-row">
<div class="person"> <div class="person">
<!-- <h3>{{ conc.person.text }}</h3> --> <!-- <h3>{{ conc.person.text }}</h3> -->
<h3><person-text :person="conc.person" /></h3> <h3><person-text :person="conc.person" /></h3>
</div>
<div class="holder">
<button
class="btn"
:disabled="!allowHolderForConcerned(conc)"
:class="{
'btn-outline-chill-green': !conc.holder,
'btn-chill-green': conc.holder,
}"
@click="toggleHolder(conc)"
>
{{ $t("household_members_editor.positioning.holder") }}
</button>
</div>
<div
v-for="(position, i) in positions"
:key="`position-${i}`"
class="position"
>
<button
class="btn"
:class="{
'btn-primary': conc.position === position,
'btn-outline-primary': conc.position !== position,
}"
@click="moveToPosition(conc.person.id, position.id)"
>
{{ localizeString(position.label) }}
</button>
</div>
</div>
<div class="item-row">
<div class="col-12">
<h6>
{{ $t("household_members_editor.positioning.comment") }}
</h6>
<person-comment :conc="conc" />
</div>
</div>
</div> </div>
<div class="holder">
<button
class="btn"
:disabled="!allowHolderForConcerned(conc)"
:class="{
'btn-outline-chill-green': !conc.holder,
'btn-chill-green': conc.holder,
}"
@click="toggleHolder(conc)"
>
{{ $t("household_members_editor.positioning.holder") }}
</button>
</div>
<div
v-for="(position, i) in positions"
:key="`position-${i}`"
class="position"
>
<button
class="btn"
:class="{
'btn-primary': conc.position === position,
'btn-outline-primary': conc.position !== position,
}"
@click="moveToPosition(conc.person.id, position.id)"
>
{{ localizeString(position.label) }}
</button>
</div>
</div>
<div class="item-row">
<div class="col-12">
<h6>{{ $t("household_members_editor.positioning.comment") }}</h6>
<person-comment :conc="conc" />
</div>
</div>
</div> </div>
</div>
</template> </template>
<script> <script>
@@ -60,61 +62,61 @@ import PersonText from "../../_components/Entity/PersonText.vue";
import { localizeString } from "ChillMainAssets/lib/localizationHelper/localizationHelper"; import { localizeString } from "ChillMainAssets/lib/localizationHelper/localizationHelper";
export default { export default {
name: "Positioning", name: "Positioning",
components: { components: {
CurrentHousehold, CurrentHousehold,
PersonComment, PersonComment,
PersonText, PersonText,
},
computed: {
...mapState(["concerned"]),
...mapGetters([
"persons",
"concUnpositionned",
"positions",
"concByPosition",
]),
allPersonsPositionnated() {
return (
this.$store.getters.persons.length > 0 &&
this.$store.getters.concUnpositionned.length === 0
);
}, },
allowHolderForConcerned: () => (conc) => { computed: {
console.log("allow holder for concerned", conc); ...mapState(["concerned"]),
if (conc.position === null) { ...mapGetters([
return false; "persons",
} "concUnpositionned",
"positions",
"concByPosition",
]),
allPersonsPositionnated() {
return (
this.$store.getters.persons.length > 0 &&
this.$store.getters.concUnpositionned.length === 0
);
},
allowHolderForConcerned: () => (conc) => {
console.log("allow holder for concerned", conc);
if (conc.position === null) {
return false;
}
return conc.position.allowHolder; return conc.position.allowHolder;
},
}, },
}, methods: {
methods: { localizeString,
localizeString, moveToPosition(person_id, position_id) {
moveToPosition(person_id, position_id) { this.$store.dispatch("markPosition", { person_id, position_id });
this.$store.dispatch("markPosition", { person_id, position_id }); },
toggleHolder(conc) {
console.log("toggle holder", conc);
this.$store.dispatch("toggleHolder", conc);
},
}, },
toggleHolder(conc) {
console.log("toggle holder", conc);
this.$store.dispatch("toggleHolder", conc);
},
},
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.pick-position { .pick-position {
margin: 0; margin: 0;
padding: 0; padding: 0;
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
align-items: center; align-items: center;
.person { .person {
margin-right: auto; margin-right: auto;
} }
.holder { .holder {
margin-right: 1.2rem; margin-right: 1.2rem;
} }
} }
</style> </style>

View File

@@ -2,10 +2,10 @@ import { AccompanyingPeriodWorkEvaluationDocument } from "../../types";
import { makeFetch } from "../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods"; import { makeFetch } from "../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods";
export const duplicate = async ( export const duplicate = async (
id: number, id: number,
): Promise<AccompanyingPeriodWorkEvaluationDocument> => { ): Promise<AccompanyingPeriodWorkEvaluationDocument> => {
return makeFetch<null, AccompanyingPeriodWorkEvaluationDocument>( return makeFetch<null, AccompanyingPeriodWorkEvaluationDocument>(
"POST", "POST",
`/api/1.0/person/accompanying-course-work-evaluation-document/${id}/duplicate`, `/api/1.0/person/accompanying-course-work-evaluation-document/${id}/duplicate`,
); );
}; };

View File

@@ -1,26 +1,26 @@
<template> <template>
<a <a
class="btn" class="btn"
:class="getClassButton" :class="getClassButton"
:title="buttonTitle" :title="buttonTitle"
@click="openModal" @click="openModal"
>
<span v-if="displayTextButton">{{ buttonTitle }}</span>
</a>
<teleport to="body">
<modal
v-if="showModal"
@close="closeModal"
:modal-dialog-class="modalDialogClass"
:show="showModal"
:hide-footer="false"
> >
<template #header> <span v-if="displayTextButton">{{ buttonTitle }}</span>
<h3 class="modal-title"> </a>
{{ modalTitle }}
</h3> <teleport to="body">
</template> <modal
v-if="showModal"
@close="closeModal"
:modal-dialog-class="modalDialogClass"
:show="showModal"
:hide-footer="false"
>
<template #header>
<h3 class="modal-title">
{{ modalTitle }}
</h3>
</template>
<template #body-head> <template #body-head>
<div class="modal-body"> <div class="modal-body">
@@ -437,9 +437,9 @@ defineExpose({
<style lang="scss"> <style lang="scss">
li.add-persons { li.add-persons {
a { a {
cursor: pointer; cursor: pointer;
} }
} }
div.body-head { div.body-head {
overflow-y: unset; overflow-y: unset;
@@ -480,7 +480,7 @@ div.body-head {
} }
} }
.create-button > a { .create-button > a {
margin-top: 0.5em; margin-top: 0.5em;
margin-left: 2.6em; margin-left: 2.6em;
} }
</style> </style>

View File

@@ -1,63 +1,64 @@
const personMessages = { const personMessages = {
fr: { fr: {
add_persons: { add_persons: {
title: "Ajouter des usagers", title: "Ajouter des usagers",
suggested_counter: "Pas de résultats | 1 résultat | {count} résultats", suggested_counter:
selected_counter: " 1 sélectionné | {count} sélectionnés", "Pas de résultats | 1 résultat | {count} résultats",
search_some_persons: "Rechercher des personnes..", selected_counter: " 1 sélectionné | {count} sélectionnés",
search_some_persons: "Rechercher des personnes..",
},
item: {
type_person: "Usager",
type_user: "TMS",
type_thirdparty: "Tiers professionnel",
type_household: "Ménage",
},
person: {
firstname: "Prénom",
lastname: "Nom",
born: (ctx: { gender: "man" | "woman" | "neutral" }) => {
if (ctx.gender === "man") {
return "Né le";
} else if (ctx.gender === "woman") {
return "Née le";
} else {
return "Né·e le";
}
},
center_id: "Identifiant du centre",
center_type: "Type de centre",
center_name: "Territoire", // vendée
phonenumber: "Téléphone",
mobilenumber: "Mobile",
altnames: "Autres noms",
email: "Courriel",
gender: {
title: "Genre",
placeholder: "Choisissez le genre de l'usager",
woman: "Féminin",
man: "Masculin",
neutral: "Neutre, non binaire",
unknown: "Non renseigné",
undefined: "Non renseigné",
},
civility: {
title: "Civilité",
placeholder: "Choisissez la civilité",
},
address: {
create_address: "Ajouter une adresse",
show_address_form:
"Ajouter une adresse pour un usager non suivi et seul dans un ménage",
warning:
"Un nouveau ménage va être créé. L'usager sera membre de ce ménage.",
},
center: {
placeholder: "Choisissez un centre",
title: "Centre",
},
},
error_only_one_person: "Une seule personne peut être sélectionnée !",
}, },
item: {
type_person: "Usager",
type_user: "TMS",
type_thirdparty: "Tiers professionnel",
type_household: "Ménage",
},
person: {
firstname: "Prénom",
lastname: "Nom",
born: (ctx: { gender: "man" | "woman" | "neutral" }) => {
if (ctx.gender === "man") {
return "Né le";
} else if (ctx.gender === "woman") {
return "Née le";
} else {
return "Né·e le";
}
},
center_id: "Identifiant du centre",
center_type: "Type de centre",
center_name: "Territoire", // vendée
phonenumber: "Téléphone",
mobilenumber: "Mobile",
altnames: "Autres noms",
email: "Courriel",
gender: {
title: "Genre",
placeholder: "Choisissez le genre de l'usager",
woman: "Féminin",
man: "Masculin",
neutral: "Neutre, non binaire",
unknown: "Non renseigné",
undefined: "Non renseigné",
},
civility: {
title: "Civilité",
placeholder: "Choisissez la civilité",
},
address: {
create_address: "Ajouter une adresse",
show_address_form:
"Ajouter une adresse pour un usager non suivi et seul dans un ménage",
warning:
"Un nouveau ménage va être créé. L'usager sera membre de ce ménage.",
},
center: {
placeholder: "Choisissez un centre",
title: "Centre",
},
},
error_only_one_person: "Une seule personne peut être sélectionnée !",
},
}; };
export { personMessages }; export { personMessages };

View File

@@ -1,40 +1,40 @@
import { is_object_ready } from "../../../../../../ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/helpers"; import { is_object_ready } from "../../../../../../ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/helpers";
import { import {
StoredObject, StoredObject,
StoredObjectStatus, StoredObjectStatus,
StoredObjectStatusChange, StoredObjectStatusChange,
} from "../../../../../../ChillDocStoreBundle/Resources/public/types"; } from "../../../../../../ChillDocStoreBundle/Resources/public/types";
async function reload_if_needed( async function reload_if_needed(
stored_object: StoredObject, stored_object: StoredObject,
i: number, i: number,
): Promise<void> { ): Promise<void> {
const current_status = await is_object_ready(stored_object); const current_status = await is_object_ready(stored_object);
if (stored_object.status !== current_status.status) { if (stored_object.status !== current_status.status) {
window.location.reload(); window.location.reload();
} }
wait_before_reload(stored_object, i + 1); wait_before_reload(stored_object, i + 1);
return Promise.resolve(); return Promise.resolve();
} }
function wait_before_reload(stored_object: StoredObject, i: number): void { function wait_before_reload(stored_object: StoredObject, i: number): void {
/** /**
* value of the timeout. Set to 5 sec during the first 10 minutes, then every 1 minute * value of the timeout. Set to 5 sec during the first 10 minutes, then every 1 minute
*/ */
const timeout = i < 1200 ? 5000 : 60000; const timeout = i < 1200 ? 5000 : 60000;
setTimeout(reload_if_needed, timeout, stored_object, i); setTimeout(reload_if_needed, timeout, stored_object, i);
} }
window.addEventListener("DOMContentLoaded", async function (e) { window.addEventListener("DOMContentLoaded", async function (e) {
if (undefined === (window as any).stored_object) { if (undefined === (window as any).stored_object) {
console.error("window.stored_object is undefined"); console.error("window.stored_object is undefined");
throw Error("window.stored_object is undefined"); throw Error("window.stored_object is undefined");
} }
const stored_object = JSON.parse( const stored_object = JSON.parse(
(window as any).stored_object, (window as any).stored_object,
) as StoredObject; ) as StoredObject;
reload_if_needed(stored_object, 0); reload_if_needed(stored_object, 0);
}); });