958 lines
26 KiB
Vue

<template>
<div id="workEditor" class="my-4">
<div id="title" class="action-row">
<div>
<p class="wl-item social-issues">
<span class="chill-entity entity-social-issue">
<span class="badge bg-chill-l-gray text-dark">{{
work.socialAction.issue.text
}}</span>
</span>
</p>
</div>
<h2 class="badge-title">
<span class="title_label"></span>
<span class="title_action">{{ work.socialAction.text }}</span>
</h2>
</div>
<div id="startDate" class="action-row">
<label class="col-form-label">{{ $t("startDate") }}</label>
<input
v-model="startDate"
type="date"
required="true"
class="form-control"
v-once
/>
</div>
<div id="endDate" class="action-row">
<label class="col-form-label">{{ $t("endDate") }}</label>
<input v-model="endDate" type="date" class="form-control" />
</div>
<div id="privateComment" class="action-row">
<label class="col-form-label">{{ $t("private_comment") }}</label>
<ckeditor
v-model="privateComment"
:editor="editor"
tag-name="textarea"
></ckeditor>
</div>
<div id="comment" class="action-row">
<label class="col-form-label">{{ $t("comments") }}</label>
<ckeditor v-model="note" :editor="editor" tag-name="textarea"></ckeditor>
</div>
<div id="objectives" class="action-row">
<div aria="hidden" class="title">
<div>
<h3>{{ $t("goals_title") }}</h3>
</div>
<div>
<h3>{{ $t("results_title") }}</h3>
</div>
</div>
<!-- results which are not attached to an objective -->
<div v-if="hasResultsForAction">
<div class="results_without_objective">
{{ $t("results_without_objective") }}
</div>
<div>
<add-result
:availableResults="resultsForAction"
destination="action"
></add-result>
</div>
</div>
<!-- results which **are** attached to an objective -->
<div v-for="g in goalsPicked" :key="g.goal.id">
<div class="item-title" @click="removeGoal(g)">
<span class="removable">{{ g.goal.title.fr }}</span>
</div>
<div>
<add-result :goal="g.goal" destination="goal"></add-result>
</div>
</div>
<div class="accordion" id="expandedSuggestions">
<div v-if="availableForCheckGoal.length > 0" class="accordion-item">
<h2 class="accordion-header" id="heading_expanded_suggestions">
<button
v-if="isExpanded"
class="accordion-button"
type="button"
data-bs-toggle="collapse"
aria-expanded="true"
@click="toggleSelect"
>
Masquer
</button>
<button
v-else
class="accordion-button collapsed"
type="button"
data-bs-toggle="collapse"
aria-expanded="false"
@click="toggleSelect"
>
Motifs, objectifs et dispositfs disponibles
</button>
</h2>
<div
class="accordion-collapse"
id="collapse_expanded_suggestions"
aria-labelledby="heading_expanded_suggestions"
data-bs-parent="#expandedSuggestions"
>
<template v-if="isExpanded">
<ul class="list-suggest add-items">
<li
v-for="g in availableForCheckGoal"
@click="addGoal(g)"
:key="g.id"
>
<span>{{ g.title.fr }}</span>
</li>
</ul>
</template>
</div>
<p v-if="goalsPicked.length === 0" class="chill-no-data-statement">
Aucun objectif associé
</p>
</div>
<div v-else>
<span class="chill-no-data-statement">{{
$t("no_goals_available")
}}</span>
</div>
</div>
</div>
<div id="evaluations" class="action-row">
<div aria="hidden" class="title">
<div>
<h3>
{{ $t("Evaluations") }} - {{ $t("Forms") }} - {{ $t("Post") }}
</h3>
</div>
</div>
<!-- list evaluations -->
<add-evaluation
v-for="e in pickedEvaluations"
v-bind:key="e.key"
v-bind:evaluation="e"
v-bind:docAnchorId="this.docAnchorId"
>
</add-evaluation>
<!-- box to add new evaluation -->
<div class="add_evaluation">
<div v-if="showAddEvaluation">
<p>{{ $t("available_evaluations_text") }}</p>
<ul class="list-suggest add-items">
<li
v-for="e in evaluationsForAction"
@click="addEvaluation(e)"
:key="e.id"
>
<span>{{ e.title.fr }}</span>
</li>
</ul>
</div>
<ul class="record_actions" v-if="evaluationsForAction.length > 0">
<li>
<button
:title="$t('add_an_evaluation')"
class="btn btn-create"
@click="toggleAddEvaluation"
>
{{ $t("add_an_evaluation") }}
</button>
</li>
</ul>
<div v-else>
<span class="chill-no-data-statement">{{
$t("no_evaluations_available")
}}</span>
</div>
</div>
</div>
<div id="persons" class="action-row">
<h3 class="mb-3">{{ $t("persons_involved") }}</h3>
<ul class="list-unstyled">
<li v-for="p in personsReachables" :key="p.id">
<div class="form-check">
<input
v-model="personsPicked"
:value="p.id"
type="checkbox"
class="me-2 form-check-input"
:id="'person_check' + p.id"
/>
<label :for="'person_check' + p.id" class="form-check-label">
<person-text :person="p"></person-text>
</label>
</div>
</li>
</ul>
</div>
<div id="referrers" class="action-row">
<h3>{{ $t("referrers") }}</h3>
<div v-if="!hasReferrers">
<p class="chill-no-data-statement">{{ $t("no_referrers") }}</p>
</div>
<div v-else>
<ul class="list-suggest remove-items inline">
<li
v-for="u in referrers"
:key="u.id"
:title="$t('remove_referrer')"
@click="removeReferrer(u)"
>
<span>
{{ u.text }}
</span>
</li>
</ul>
</div>
<ul class="record_actions">
<li class="add-persons">
<add-persons
ref="referrerPicker"
:key="referrerPicker.key"
:buttonTitle="$t('add_referrers')"
:modalTitle="$t('choose_referrers')"
:options="referrerPicker.options"
@addNewPersons="addReferrers"
>
</add-persons>
</li>
</ul>
</div>
<div id="handlingThirdParty" class="action-row">
<h3>{{ $t("handling_thirdparty") }}</h3>
<div v-if="!hasHandlingThirdParty">
<p class="chill-no-data-statement">
{{ $t("no_handling_thirdparty") }}
</p>
<ul class="record_actions">
<li class="add-persons">
<add-persons
ref="handlingThirdPartyPicker"
v-bind:key="handlingThirdPartyPicker.key"
v-bind:buttonTitle="$t('precise_handling_thirdparty')"
v-bind:modalTitle="$t('choose_a_thirdparty')"
v-bind:options="handlingThirdPartyPicker.options"
@addNewPersons="setHandlingThirdParty"
>
<!-- to cast child method -->
</add-persons>
</li>
</ul>
</div>
<div v-else class="flex-table">
<third-party-render-box
:thirdparty="handlingThirdParty"
:options="{
addLink: false,
addId: false,
addEntity: true,
addInfo: false,
hLevel: 3,
isMultiline: true,
isConfidential: false,
}"
></third-party-render-box>
<ul class="record_actions">
<li>
<button
:title="$t('remove_handling_thirdparty')"
class="btn btn-remove"
@click="removeHandlingThirdParty"
/>
</li>
</ul>
</div>
</div>
<div id="thirdParties" class="action-row">
<h3>{{ $t("thirdparty_intervener") }}</h3>
<div v-if="!hasThirdParties">
<p class="chill-no-data-statement">
{{ $t("no_thirdparty_intervener") }}
</p>
</div>
<div v-else>
<div class="flex-bloc mb-3">
<third-party-render-box
v-for="thirdparty in thirdParties"
:key="thirdparty.id"
:thirdparty="thirdparty"
:options="{
addLink: false,
addId: false,
addEntity: true,
addInfo: false,
hLevel: 3,
}"
>
<template v-slot:record-actions>
<ul class="record_actions">
<li>
<on-the-fly
:type="thirdparty.type"
:id="thirdparty.id"
action="show"
></on-the-fly>
</li>
<li>
<on-the-fly
:type="thirdparty.type"
:id="thirdparty.id"
action="edit"
@saveFormOnTheFly="saveFormOnTheFly"
ref="onTheFly"
></on-the-fly>
</li>
<li>
<button
:title="$t('remove_thirdparty')"
class="btn btn-sm btn-remove"
@click="removeThirdParty(thirdparty)"
/>
</li>
</ul>
</template>
</third-party-render-box>
</div>
</div>
<ul class="record_actions">
<li class="add-persons">
<add-persons
ref="thirdPartyPicker"
v-bind:key="thirdPartyPicker.key"
v-bind:buttonTitle="$t('add_thirdparties')"
v-bind:modalTitle="$t('choose_thirdparties')"
v-bind:options="thirdPartyPicker.options"
@addNewPersons="addThirdParties"
>
<!-- to cast child method -->
</add-persons>
</li>
</ul>
</div>
<div
v-if="errors.length > 0"
id="errors"
class="alert alert-danger flashbag"
>
<p>{{ $t("fix_these_errors") }}</p>
<ul>
<li v-for="e in errors" :key="e.id">{{ e }}</li>
</ul>
</div>
</div>
<ul class="record_actions sticky-form-buttons">
<li>
<list-workflow-modal
:workflows="this.work.workflows"
:allowCreate="true"
relatedEntityClass="Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork"
:relatedEntityId="this.work.id"
:workflowsAvailables="this.work.workflows_availables"
:preventDefaultMoveToGenerate="true"
@go-to-generate-workflow="goToGenerateWorkflow"
></list-workflow-modal>
</li>
<li>
<button
v-if="AmIRefferer"
class="btn btn-notify"
@click="goToGenerateNotification(false)"
></button>
<template v-else>
<button
id="btnGroupNotifyButtons"
type="button"
class="btn btn-notify dropdown-toggle"
:title="$t('notification_send')"
data-bs-toggle="dropdown"
aria-expanded="false"
>
&nbsp;
</button>
<ul class="dropdown-menu" aria-labelledby="btnGroupNotifyButtons">
<li>
<a class="dropdown-item" @click="goToGenerateNotification(true)">{{
$t("notification_notify_referrer")
}}</a>
</li>
<li>
<a class="dropdown-item" @click="goToGenerateNotification(false)">{{
$t("notification_notify_any")
}}</a>
</li>
</ul>
</template>
</li>
<li v-if="!isPosting">
<button class="btn btn-save" @click="submit">
{{ $t("action.save") }}
</button>
</li>
<li v-if="isPosting">
<button class="btn btn-save" disabled>
{{ $t("action.save") }}
</button>
</li>
</ul>
</template>
<script>
import { mapState, mapGetters } from "vuex";
import {
dateToISO,
ISOToDate,
ISOToDatetime,
} from "ChillMainAssets/chill/js/date";
import { Ckeditor } from "@ckeditor/ckeditor5-vue";
import ClassicEditor from "ChillMainAssets/module/ckeditor5/editor_config";
import AddResult from "./components/AddResult.vue";
import AddEvaluation from "./components/AddEvaluation.vue";
import AddPersons from "ChillPersonAssets/vuejs/_components/AddPersons.vue";
import AddressRenderBox from "ChillMainAssets/vuejs/_components/Entity/AddressRenderBox.vue";
import ThirdPartyRenderBox from "ChillThirdPartyAssets/vuejs/_components/Entity/ThirdPartyRenderBox.vue";
import PickTemplate from "ChillDocGeneratorAssets/vuejs/_components/PickTemplate.vue";
import OnTheFly from "ChillMainAssets/vuejs/OnTheFly/components/OnTheFly.vue";
import ListWorkflowModal from "ChillMainAssets/vuejs/_components/EntityWorkflow/ListWorkflowModal.vue";
import PersonText from "ChillPersonAssets/vuejs/_components/Entity/PersonText.vue";
import { buildLinkCreate } from "ChillMainAssets/lib/entity-workflow/api";
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
const i18n = {
messages: {
fr: {
action: {
save: "Enregistrer",
},
conflict_on_save:
"Désolé, cette action d'accompagnement a été modifiée dans une autre fenêtre ou par un autre utilisateur. Rechargez la page pour voir ses changements.",
action_title: "Action d'accompagnement",
comments: "Commentaire",
startDate: "Date de début",
endDate: "Date de fin",
goals_title: "Motifs - objectifs - dispositifs",
available_goals_text:
"Motifs, objectifs et dispositifs disponibles pour ajout :",
results_title: "Orientations - résultats",
results_without_objective: "Résultats - orientations sans objectifs",
add_objectif: "Ajouter un motif - objectif - dispositif",
add_an_objective: "Ajouter un objectif",
Evaluations: "Évaluations",
Forms: "Formulaires",
Post: "Courriers",
add_an_evaluation: "Ajouter une évaluation",
persons_involved: "Usagers concernés",
handling_thirdparty: "Tiers traitant",
no_handling_thirdparty: "Aucun tiers traitant",
precise_handling_thirdparty: "Indiquer un tiers traitant",
choose_a_thirdparty: "Choisir un tiers",
remove_thirdparty: "Enlever le tiers",
remove_handling_thirdparty: "Enlever le tiers traitant",
thirdparty_intervener: "Tiers intervenants",
no_thirdparty_intervener: "Aucun tiers intervenant",
add_thirdparties: "Ajouter des tiers",
choose_thirdparties: "Choisir des tiers",
fix_these_errors: "Veuillez corriger les erreurs suivantes :",
available_evaluations_text: "Documents disponibles pour ajout :",
no_evaluations_available: "Aucune évaluation disponible",
no_goals_available: "Aucun objectif disponible",
referrers: "Agents traitants",
add_referrers: "Ajouter des agents traitants",
no_referrers: "Aucun agent traitant",
choose_referrers: "Choisir des agents traitants",
remove_referrer: "Enlever l'agent",
private_comment: "Commentaire privé",
notification_notify_referrer: "Notifier le référent",
notification_notify_any: "Notifier d'autres utilisateurs",
notification_send: "Envoyer une notification",
},
},
};
export default {
name: "App",
components: {
ckeditor: Ckeditor,
AddResult,
AddEvaluation,
AddPersons,
AddressRenderBox,
ThirdPartyRenderBox,
PickTemplate,
ListWorkflowModal,
OnTheFly,
PersonText,
},
i18n,
data() {
return {
docAnchorId: null,
isExpanded: false,
editor: ClassicEditor,
showAddObjective: false,
showAddEvaluation: false,
handlingThirdPartyPicker: {
key: "handling-third-party",
options: {
type: ["thirdparty"],
priority: null,
uniq: true,
button: {
display: false,
},
},
},
thirdPartyPicker: {
key: "third-party",
options: {
type: ["thirdparty"],
priority: null,
uniq: false,
button: {
display: false,
},
},
},
referrerPicker: {
key: "referrer",
options: {
type: ["user"],
priority: null,
uniq: false,
button: {
display: false,
},
},
},
};
},
beforeMount() {
const urlParams = new URLSearchParams(window.location.search);
this.docAnchorId = urlParams.get("doc_id");
},
mounted() {
this.scrollToElement(this.docAnchorId);
},
computed: {
...mapState([
"work",
"resultsForAction",
"evaluationsForAction",
"goalsPicked",
"personsReachables",
"handlingThirdParty",
"thirdParties",
"referrers",
"isPosting",
"errors",
"templatesAvailablesForAction",
"me",
"version",
]),
...mapGetters([
"hasResultsForAction",
"hasHandlingThirdParty",
"hasThirdParties",
"hasReferrers",
]),
startDate: {
get() {
return this.$store.state.startDate;
},
set(v) {
this.$store.commit("setStartDate", v);
},
},
endDate: {
get() {
return this.$store.state.endDate;
},
set(v) {
this.$store.commit("setEndDate", v);
},
},
note: {
get() {
return this.$store.state.note;
},
set(v) {
this.$store.commit("setNote", v);
},
},
privateComment: {
get() {
return this.$store.state.privateComment;
},
set(v) {
this.$store.commit("setPrivateComment", v);
},
},
availableForCheckGoal() {
let pickedIds = this.$store.state.goalsPicked.map((g) => g.goal.id);
return this.$store.state.goalsForAction.filter(
(g) => !pickedIds.includes(g.id),
);
},
pickedEvaluations() {
return this.$store.state.evaluationsPicked;
},
personsPicked: {
get() {
let s = this.$store.state.personsPicked.map((p) => p.id);
return s;
},
set(v) {
this.$store.commit("setPersonsPickedIds", v);
},
},
AmIRefferer() {
return !(
this.work.accompanyingPeriod.user &&
this.me &&
this.work.accompanyingPeriod.user.id !== this.me.id
);
},
},
methods: {
toggleSelect() {
this.isExpanded = !this.isExpanded;
},
addGoal(g) {
this.$store.commit("addGoal", g);
},
removeGoal(g) {
this.$store.commit("removeGoal", g);
},
addEvaluation(e) {
this.$store.dispatch("addEvaluation", e);
},
toggleAddEvaluation() {
this.showAddEvaluation = !this.showAddEvaluation;
},
setHandlingThirdParty({ selected, modal }) {
this.$store.commit("setHandlingThirdParty", selected.shift().result);
this.$refs.handlingThirdPartyPicker.resetSearch();
modal.showModal = false;
},
removeHandlingThirdParty() {
this.$store.commit("setHandlingThirdParty", null);
},
addThirdParties({ selected, modal }) {
this.$store.commit(
"addThirdParties",
selected.map((r) => r.result),
);
this.$refs.thirdPartyPicker.resetSearch();
modal.showModal = false;
},
removeThirdParty(t) {
this.$store.commit("removeThirdParty", t);
},
addReferrers({ selected, modal }) {
this.$store.commit(
"addReferrers",
selected.map((r) => r.result),
);
this.$refs.referrerPicker.resetSearch();
modal.showModal = false;
},
removeReferrer(u) {
this.$store.commit("removeReferrer", u);
},
goToGenerateWorkflow({ link }) {
// console.log('save before leave to generate workflow')
const callback = (data) => {
window.location.assign(link);
};
return this.$store.dispatch("submit", callback).catch((e) => {
console.log(e);
throw e;
});
},
goToGenerateNotification(tos) {
console.log("save before leave to notification");
const callback = (data) => {
if (tos === true) {
window.location.assign(
`/fr/notification/create?entityClass=Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWork&entityId=${this.work.id}&tos[0]=${this.work.accompanyingPeriod.user.id}&returnPath=/fr/person/accompanying-period/${this.work.accompanyingPeriod.id}/work`,
);
} else {
window.location.assign(
`/fr/notification/create?entityClass=Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWork&entityId=${this.work.id}&returnPath=/fr/person/accompanying-period/${this.work.accompanyingPeriod.id}/work`,
);
}
};
return this.$store.dispatch("submit", callback).catch((e) => {
console.log(e);
throw e;
});
},
submit() {
this.$store.dispatch("submit").catch((error) => {
if (
error.name === "ValidationException" ||
error.name === "AccessException"
) {
error.violations.forEach((violation) =>
this.$toast.open({ message: violation }),
);
} else if (error.name === "ConflictHttpException") {
this.$toast.open({ message: this.$t("conflict_on_save") });
} else {
this.$toast.open({ message: "An error occurred" });
throw error;
}
});
},
saveFormOnTheFly(payload) {
// console.log('saveFormOnTheFly: type', payload.type, ', data', payload.data);
let body = { type: payload.type };
body.name = payload.data.text;
body.email = payload.data.email;
body.telephone = payload.data.phonenumber;
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("updateThirdParty", response);
this.$refs.onTheFly.closeModal();
})
.catch((error) => {
if (error.name === "ValidationException") {
for (let v of error.violations) {
this.$toast.open({ message: v });
}
} else if (error.name === "ConflictHttpException") {
this.$toast.open({ message: this.$t("conflict_on_save") });
} else {
this.$toast.open({ message: "An error occurred" });
}
});
},
scrollToElement(docAnchorId) {
const documentEl = document.getElementById(`document_${docAnchorId}`);
if (documentEl) {
documentEl.scrollIntoView({ behavior: "smooth" });
}
},
},
};
</script>
<style lang="scss">
@import "~ChillMainAssets/module/bootstrap/shared";
@import "~ChillMainAssets/chill/scss/mixins";
div#workEditor {
display: grid;
grid-template-columns: 50%;
column-gap: 0rem;
grid-template-areas:
"title title"
"startDate endDate"
"comment comment"
"privateComment privateComment"
"objectives objectives"
"evaluations evaluations"
"persons persons"
"referrers referrers"
"handling handling"
"tparties tparties"
"errors errors";
#title {
grid-area: title;
}
#startDate {
grid-area: startDate;
}
#endDate {
grid-area: endDate;
}
#comment {
grid-area: comment;
}
#privateComment {
grid-area: privateComment;
}
#objectives {
grid-area: objectives;
}
#evaluations {
grid-area: evaluations;
}
#persons {
grid-area: persons;
}
#handlingThirdParty {
grid-area: handling;
}
#thirdParties {
grid-area: tparties;
}
#referrers {
grid-area: referrers;
}
#errors {
grid-area: errors;
}
div.action-row {
@include border-collapse;
padding: 1em;
&#title {
label {
margin-bottom: 0;
}
p {
margin-top: 0;
font-weight: bold;
font-size: 1rem;
}
}
&#objectives {
& > div {
display: grid;
grid-template-columns: 50%;
column-gap: 0rem;
grid-template-areas: "obj res";
& > div {
@include border-collapse;
padding: 1em;
&:nth-child(1) {
grid-area: obj;
}
&:nth-child(2) {
grid-area: res;
}
}
& > div.results_without_objective {
background: repeating-linear-gradient(
45deg,
$gray-200,
$gray-200 10px,
$gray-100 10px,
$gray-100 20px
);
text-align: center;
font-weight: 700;
padding-top: 1.5rem;
}
}
}
&#evaluations {
& > div {
@include border-collapse;
padding: 1em;
}
}
&#objectives,
&#evaluations {
padding: 0;
& > div.title {
background-color: $gray-200;
color: $gray-700;
h3 {
text-align: center;
}
}
.item-title {
font-weight: bold;
}
.item-details {
margin: 1em 2em;
font-size: 85%;
}
i.fa {
padding: 0.25rem;
color: $white;
&.fa-times {
color: $red;
}
}
}
&#persons {
margin-top: 1.5em;
}
ul.record_actions {
margin-bottom: 0;
}
}
div#errors {
&.alert {
margin-top: 2em;
}
}
}
.accordion-item:first-of-type,
.accordion-item:last-of-type {
border-radius: 0rem;
border: 0px;
.accordion-button {
padding: 0.25rem;
border: 1px solid rgba(17, 17, 17, 0.125);
margin-top: 20px;
margin-bottom: 20px;
}
}
</style>