Misc: homepage widget with tickets, and improvements in ticket list

This commit is contained in:
Boris Waaub
2025-09-16 11:16:57 +00:00
committed by Julien Fastré
parent e87429933a
commit 0ba2cbc1e8
33 changed files with 1200 additions and 838 deletions

View File

@@ -1,5 +1,5 @@
<template>
<h2>{{ $t("main_title") }}</h2>
<h2>{{ trans(MAIN_TITLE) }}</h2>
<ul class="nav nav-tabs">
<li class="nav-item">
@@ -11,14 +11,24 @@
<i class="fa fa-dashboard" />
</a>
</li>
<li class="nav-item">
<a
class="nav-link"
:class="{ active: activeTab === 'MyTickets' }"
@click="selectTab('MyTickets')"
>
{{ trans(MY_TICKETS_TAB) }}
<tab-counter :count="ticketListState.value?.count || 0" />
</a>
</li>
<li class="nav-item">
<a
class="nav-link"
:class="{ active: activeTab === 'MyNotifications' }"
@click="selectTab('MyNotifications')"
>
{{ $t("my_notifications.tab") }}
<tab-counter :count="state.notifications.count" />
{{ trans(MY_NOTIFICATIONS_TAB) }}
<tab-counter :count="state.value?.notifications?.count || 0" />
</a>
</li>
<li class="nav-item">
@@ -27,25 +37,17 @@
:class="{ active: activeTab === 'MyAccompanyingCourses' }"
@click="selectTab('MyAccompanyingCourses')"
>
{{ $t("my_accompanying_courses.tab") }}
{{ trans(MY_ACCOMPANYING_COURSES_TAB) }}
</a>
</li>
<!-- <li class="nav-item">
<a class="nav-link"
:class="{'active': activeTab === 'MyWorks'}"
@click="selectTab('MyWorks')">
{{ $t('my_works.tab') }}
<tab-counter :count="state.works.count"></tab-counter>
</a>
</li> -->
<li class="nav-item">
<a
class="nav-link"
:class="{ active: activeTab === 'MyEvaluations' }"
@click="selectTab('MyEvaluations')"
>
{{ $t("my_evaluations.tab") }}
<tab-counter :count="state.evaluations.count" />
{{ trans(MY_EVALUATIONS_TAB) }}
<tab-counter :count="state.value?.evaluations?.count || 0" />
</a>
</li>
<li class="nav-item">
@@ -54,9 +56,12 @@
:class="{ active: activeTab === 'MyTasks' }"
@click="selectTab('MyTasks')"
>
{{ $t("my_tasks.tab") }}
{{ trans(MY_TASKS_TAB) }}
<tab-counter
:count="state.tasks.warning.count + state.tasks.alert.count"
:count="
(state.value?.tasks?.warning?.count || 0) +
(state.value?.tasks?.alert?.count || 0)
"
/>
</a>
</li>
@@ -66,19 +71,25 @@
:class="{ active: activeTab === 'MyWorkflows' }"
@click="selectTab('MyWorkflows')"
>
{{ $t("my_workflows.tab") }}
<tab-counter :count="state.workflows.count + state.workflowsCc.count" />
{{ trans(MY_WORKFLOWS_TAB) }}
<tab-counter
:count="
(state.value?.workflows?.count || 0) +
(state.value?.workflowsCc?.count || 0)
"
/>
</a>
</li>
<li class="nav-item loading ms-auto py-2" v-if="loading">
<i
class="fa fa-circle-o-notch fa-spin fa-lg text-chill-gray"
:title="$t('loading')"
:title="trans(LOADING)"
/>
</li>
</ul>
<div class="my-4">
<my-tickets v-if="activeTab == 'MyTickets'" />
<my-customs v-if="activeTab === 'MyCustoms'" />
<my-works v-else-if="activeTab === 'MyWorks'" />
<my-evaluations v-else-if="activeTab === 'MyEvaluations'" />
@@ -91,63 +102,58 @@
</div>
</template>
<script>
import MyCustoms from "./MyCustoms";
import MyWorks from "./MyWorks";
import MyEvaluations from "./MyEvaluations";
import MyTasks from "./MyTasks";
import MyAccompanyingCourses from "./MyAccompanyingCourses";
import MyNotifications from "./MyNotifications";
<script lang="ts" setup>
import { ref, computed, onMounted } from "vue";
import { useStore } from "vuex";
import MyCustoms from "./MyCustoms.vue";
import MyWorks from "./MyWorks.vue";
import MyEvaluations from "./MyEvaluations.vue";
import MyTasks from "./MyTasks.vue";
import MyAccompanyingCourses from "./MyAccompanyingCourses.vue";
import MyNotifications from "./MyNotifications.vue";
import MyWorkflows from "./MyWorkflows.vue";
import TabCounter from "./TabCounter";
import { mapState } from "vuex";
import MyTickets from "./MyTickets.vue";
import TabCounter from "./TabCounter.vue";
import {
MAIN_TITLE,
MY_TICKETS_TAB,
MY_EVALUATIONS_TAB,
MY_TASKS_TAB,
MY_ACCOMPANYING_COURSES_TAB,
MY_NOTIFICATIONS_TAB,
MY_WORKFLOWS_TAB,
LOADING,
trans,
} from "translator";
const store = useStore();
export default {
name: "App",
components: {
MyCustoms,
MyWorks,
MyEvaluations,
MyTasks,
MyWorkflows,
MyAccompanyingCourses,
MyNotifications,
TabCounter,
},
data() {
return {
activeTab: "MyCustoms",
};
},
computed: {
...mapState(["loading"]),
// just to see all in devtool :
...mapState({
state: (state) => state,
}),
},
methods: {
selectTab(tab) {
if (tab !== "MyCustoms") {
this.$store.dispatch("getByTab", { tab: tab });
}
this.activeTab = tab;
console.log(this.activeTab);
},
},
mounted() {
for (const m of [
"MyNotifications",
"MyAccompanyingCourses",
// 'MyWorks',
"MyEvaluations",
"MyTasks",
"MyWorkflows",
]) {
this.$store.dispatch("getByTab", { tab: m, param: "countOnly=1" });
}
},
};
const activeTab = ref("MyCustoms");
const loading = computed(() => store.state.loading);
const state = computed(() => store.state.homepage);
const ticketListState = computed(() => store.state.ticketList);
function selectTab(tab: string) {
if (tab !== "MyCustoms") {
store.dispatch("getByTab", { tab: tab });
}
activeTab.value = tab;
}
onMounted(() => {
for (const m of [
"MyTickets",
"MyNotifications",
"MyAccompanyingCourses",
// 'MyWorks',
"MyEvaluations",
"MyTasks",
"MyWorkflows",
]) {
store.dispatch("getByTab", { tab: m, param: "countOnly=1" });
}
});
</script>
<style scoped>

View File

@@ -1,35 +1,35 @@
<template>
<div class="alert alert-light">
{{ $t("my_accompanying_courses.description") }}
{{ trans(MY_ACCOMPANYING_COURSES_DESCRIPTION) }}
</div>
<span v-if="noResults" class="chill-no-data-statement">{{
$t("no_data")
}}</span>
<span v-if="noResults" class="chill-no-data-statement">
{{ trans(NO_DATA) }}
</span>
<tab-table v-else>
<template #thead>
<th scope="col">
{{ $t("opening_date") }}
{{ trans(OPENING_DATE) }}
</th>
<th scope="col">
{{ $t("social_issues") }}
{{ trans(SOCIAL_ISSUES) }}
</th>
<th scope="col">
{{ $t("concerned_persons") }}
{{ trans(CONCERNED_PERSONS) }}
</th>
<th scope="col" />
<th scope="col" />
</template>
<template #tbody>
<tr v-for="(c, i) in accompanyingCourses.results" :key="`course-${i}`">
<td>{{ $d(c.openingDate.datetime, "short") }}</td>
<td>{{ $d(new Date(c.openingDate.datetime), "short") }}</td>
<td>
<span
v-for="(i, index) in c.socialIssues"
v-for="(issue, index) in c.socialIssues"
:key="index"
class="chill-entity entity-social-issue"
>
<span class="badge bg-chill-l-gray text-dark">
{{ localizeString(i.title) }}
{{ localizeString(issue.title) }}
</span>
</span>
</td>
@@ -46,15 +46,15 @@
</td>
<td>
<span v-if="c.emergency" class="badge rounded-pill bg-danger me-1">{{
$t("emergency")
trans(EMERGENCY)
}}</span>
<span v-if="c.confidential" class="badge rounded-pill bg-danger">{{
$t("confidential")
trans(CONFIDENTIAL)
}}</span>
</td>
<td>
<a class="btn btn-sm btn-show" :href="getUrl(c)">
{{ $t("show_entity", { entity: $t("the_course") }) }}
{{ trans(SHOW_ENTITY, { entity: trans(THE_COURSE) }) }}
</a>
</td>
</tr>
@@ -62,36 +62,45 @@
</tab-table>
</template>
<script>
import { mapState, mapGetters } from "vuex";
import TabTable from "./TabTable";
import OnTheFly from "ChillMainAssets/vuejs/OnTheFly/components/OnTheFly";
<script lang="ts" setup>
import { computed, ComputedRef } from "vue";
import { useStore } from "vuex";
import TabTable from "./TabTable.vue";
import OnTheFly from "ChillMainAssets/vuejs/OnTheFly/components/OnTheFly.vue";
import { localizeString } from "ChillMainAssets/lib/localizationHelper/localizationHelper";
import { AccompanyingCourse } from "ChillPersonAssets/types";
import { PaginationResponse } from "ChillMainAssets/lib/api/apiMethods";
import {
MY_ACCOMPANYING_COURSES_DESCRIPTION,
OPENING_DATE,
SOCIAL_ISSUES,
CONCERNED_PERSONS,
SHOW_ENTITY,
THE_COURSE,
NO_DATA,
EMERGENCY,
CONFIDENTIAL,
trans,
} from "translator";
const store = useStore();
export default {
name: "MyAccompanyingCourses",
components: {
TabTable,
OnTheFly,
},
computed: {
...mapState(["accompanyingCourses"]),
...mapGetters(["isAccompanyingCoursesLoaded"]),
noResults() {
if (!this.isAccompanyingCoursesLoaded) {
return false;
} else {
return this.accompanyingCourses.count === 0;
}
},
},
methods: {
localizeString,
getUrl(c) {
return `/fr/parcours/${c.id}`;
},
},
};
const accompanyingCourses: ComputedRef<PaginationResponse<AccompanyingCourse>> =
computed(() => store.state.homepage.accompanyingCourses);
const isAccompanyingCoursesLoaded = computed(
() => store.getters.isAccompanyingCoursesLoaded,
);
const noResults = computed(() => {
if (!isAccompanyingCoursesLoaded.value) {
return false;
} else {
return accompanyingCourses.value.count === 0;
}
});
function getUrl(c: { id: number }): string {
return `/fr/parcours/${c.id}`;
}
</script>
<style scoped>

View File

@@ -1,93 +1,72 @@
<template>
<span v-if="noResults" class="chill-no-data-statement">{{
$t("no_dashboard")
}}</span>
<span v-if="noResults" class="chill-no-data-statement">
{{ trans(NO_DASHBOARD) }}
</span>
<div v-else id="dashboards" class="container g-3">
<div class="row">
<div class="mbloc col-xs-12 col-sm-4">
<div class="custom1">
<ul class="list-unstyled">
<li v-if="counter.notifications > 0">
<i18n-t
keypath="counter.unread_notifications"
tag="span"
:class="counterClass"
:plural="counter.notifications"
>
<template #n>
<span>{{ counter.notifications }}</span>
</template>
</i18n-t>
<li v-if="(counter.value?.notifications || 0) > 0">
<span :class="counterClass">
{{
trans(COUNTER_UNREAD_NOTIFICATIONS, {
n: counter.value?.notifications || 0,
})
}}
</span>
</li>
<li v-if="counter.accompanyingCourses > 0">
<i18n-t
keypath="counter.assignated_courses"
tag="span"
:class="counterClass"
:plural="counter.accompanyingCourses"
>
<template #n>
<span>{{ counter.accompanyingCourses }}</span>
</template>
</i18n-t>
<li v-if="(counter.value?.accompanyingCourses || 0) > 0">
<span :class="counterClass">
{{
trans(COUNTER_ASSIGNATED_COURSES, {
n: counter.value?.accompanyingCourses || 0,
})
}}
</span>
</li>
<li v-if="counter.works > 0">
<i18n-t
keypath="counter.assignated_actions"
tag="span"
:class="counterClass"
:plural="counter.works"
>
<template #n>
<span>{{ counter.works }}</span>
</template>
</i18n-t>
<li v-if="(counter.value?.works || 0) > 0">
<span :class="counterClass">
{{
trans(COUNTER_ASSIGNATED_ACTIONS, {
n: counter.value?.works || 0,
})
}}
</span>
</li>
<li v-if="counter.evaluations > 0">
<i18n-t
keypath="counter.assignated_evaluations"
tag="span"
:class="counterClass"
:plural="counter.evaluations"
>
<template #n>
<span>{{ counter.evaluations }}</span>
</template>
</i18n-t>
<li v-if="(counter.value?.evaluations || 0) > 0">
<span :class="counterClass">
{{
trans(COUNTER_ASSIGNATED_EVALUATIONS, {
n: counter.value?.evaluations || 0,
})
}}
</span>
</li>
<li v-if="counter.tasksAlert > 0">
<i18n-t
keypath="counter.alert_tasks"
tag="span"
:class="counterClass"
:plural="counter.tasksAlert"
>
<template #n>
<span>{{ counter.tasksAlert }}</span>
</template>
</i18n-t>
<li v-if="(counter.value?.tasksAlert || 0) > 0">
<span :class="counterClass">
{{
trans(COUNTER_ALERT_TASKS, {
n: counter.value?.tasksAlert || 0,
})
}}
</span>
</li>
<li v-if="counter.tasksWarning > 0">
<i18n-t
keypath="counter.warning_tasks"
tag="span"
:class="counterClass"
:plural="counter.tasksWarning"
>
<template #n>
<span>{{ counter.tasksWarning }}</span>
</template>
</i18n-t>
<li v-if="(counter.value?.tasksWarning || 0) > 0">
<span :class="counterClass">
{{
trans(COUNTER_WARNING_TASKS, {
n: counter.value?.tasksWarning || 0,
})
}}
</span>
</li>
</ul>
</div>
</div>
<template v-if="this.hasDashboardItems">
<template
v-for="(dashboardItem, index) in this.dashboardItems"
:key="index"
>
<template v-if="hasDashboardItems">
<template v-for="(dashboardItem, index) in dashboardItems" :key="index">
<div
class="mbloc col-xs-12 col-sm-8 news"
v-if="dashboardItem.type === 'news'"
@@ -100,44 +79,53 @@
</div>
</template>
<script>
import { mapGetters } from "vuex";
<script lang="ts" setup>
import { ref, computed, onMounted } from "vue";
import { useStore } from "vuex";
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
import News from "./DashboardWidgets/News.vue";
import {
NO_DASHBOARD,
COUNTER_UNREAD_NOTIFICATIONS,
COUNTER_ASSIGNATED_COURSES,
COUNTER_ASSIGNATED_ACTIONS,
COUNTER_ASSIGNATED_EVALUATIONS,
COUNTER_ALERT_TASKS,
COUNTER_WARNING_TASKS,
trans,
} from "translator";
export default {
name: "MyCustoms",
components: {
News,
},
data() {
return {
counterClass: {
counter: true, //hack to pass class 'counter' in i18n-t
},
dashboardItems: [],
masonry: null,
};
},
computed: {
...mapGetters(["counter"]),
noResults() {
return false;
},
hasDashboardItems() {
return this.dashboardItems.length > 0;
},
},
mounted() {
makeFetch("GET", "/api/1.0/main/dashboard-config-item.json")
.then((response) => {
this.dashboardItems = response;
})
.catch((error) => {
throw error;
});
},
};
interface MyCustom {
id: number;
type: string;
metadata: Record<string, unknown>;
userId: number;
position: string;
}
const store = useStore();
const counter = computed(() => store.getters.counter);
const counterClass = { counter: true };
const dashboardItems = ref<MyCustom[]>([]);
const noResults = computed(() => false);
const hasDashboardItems = computed(() => dashboardItems.value.length > 0);
onMounted(async () => {
try {
const response: MyCustom[] = await makeFetch(
"GET",
"/api/1.0/main/dashboard-config-item.json",
);
dashboardItems.value = response;
} catch (error) {
throw error;
}
});
</script>
<style lang="scss" scoped>

View File

@@ -1,44 +1,50 @@
<template>
<div class="accompanying-course-work">
<div class="alert alert-light">
{{ $t("my_evaluations.description") }}
{{ trans(MY_EVALUATIONS_DESCRIPTION) }}
</div>
<span v-if="noResults" class="chill-no-data-statement">{{
$t("no_data")
}}</span>
<span v-if="noResults" class="chill-no-data-statement">
{{ trans(NO_DATA) }}
</span>
<tab-table v-else>
<template #thead>
<th scope="col">
{{ $t("max_date") }}
{{ trans(MAX_DATE) }}
</th>
<th scope="col">
{{ $t("evaluation") }}
{{ trans(EVALUATION) }}
</th>
<th scope="col">
{{ $t("SocialAction") }}
{{ trans(SOCIAL_ACTION) }}
</th>
<th scope="col" />
</template>
<template #tbody>
<tr v-for="(e, i) in evaluations.results" :key="`evaluation-${i}`">
<td>{{ $d(e.maxDate.datetime, "short") }}</td>
<td>
{{ localizeString(e.evaluation.title) }}
{{
e.maxDate?.datetime
? $d(new Date(e.maxDate.datetime), "short")
: ""
}}
</td>
<td>
{{ localizeString(e.evaluation?.title ?? null) }}
</td>
<td>
<span class="chill-entity entity-social-issue">
<span class="badge bg-chill-l-gray text-dark">
{{ e.accompanyingPeriodWork.socialAction.issue.text }}
{{ e.accompanyingPeriodWork?.socialAction?.issue?.text ?? "" }}
</span>
</span>
<h4 class="badge-title">
<span class="title_label" />
<span class="title_action">
{{ e.accompanyingPeriodWork.socialAction.text }}
{{ e.accompanyingPeriodWork?.socialAction?.text ?? "" }}
</span>
</h4>
<span
v-for="person in e.accompanyingPeriodWork.persons"
v-for="person in e.accompanyingPeriodWork?.persons ?? []"
class="me-1"
:key="person.id"
>
@@ -55,18 +61,22 @@
<div class="btn-group-vertical" role="group" aria-label="Actions">
<a class="btn btn-sm btn-show" :href="getUrl(e)">
{{
$t("show_entity", {
entity: $t("the_evaluation"),
trans(SHOW_ENTITY, {
entity: trans(THE_EVALUATION),
})
}}
</a>
<a
class="btn btn-sm btn-show"
:href="getUrl(e.accompanyingPeriodWork.accompanyingPeriod)"
:href="getUrl(e.accompanyingPeriodWork.accompanyingPeriod!)"
v-if="
e.accompanyingPeriodWork &&
e.accompanyingPeriodWork.accompanyingPeriod
"
>
{{
$t("show_entity", {
entity: $t("the_course"),
trans(SHOW_ENTITY, {
entity: trans(THE_COURSE),
})
}}
</a>
@@ -78,46 +88,70 @@
</div>
</template>
<script>
import { mapState, mapGetters } from "vuex";
import TabTable from "./TabTable";
import OnTheFly from "ChillMainAssets/vuejs/OnTheFly/components/OnTheFly";
<script lang="ts" setup>
import { computed } from "vue";
import { useStore } from "vuex";
import TabTable from "./TabTable.vue";
import OnTheFly from "ChillMainAssets/vuejs/OnTheFly/components/OnTheFly.vue";
import { localizeString } from "../../lib/localizationHelper/localizationHelper";
export default {
name: "MyEvaluations",
components: {
TabTable,
OnTheFly,
},
computed: {
...mapState(["evaluations"]),
...mapGetters(["isEvaluationsLoaded"]),
noResults() {
if (!this.isEvaluationsLoaded) {
return false;
} else {
return this.evaluations.count === 0;
}
},
},
methods: {
localizeString,
getUrl(e) {
switch (e.type) {
case "accompanying_period_work_evaluation":
let anchor = "#evaluations";
return `/fr/person/accompanying-period/work/${e.accompanyingPeriodWork.id}/edit${anchor}`;
case "accompanying_period_work":
return `/fr/person/accompanying-period/work/${e.id}/edit`;
case "accompanying_period":
return `/fr/parcours/${e.id}`;
default:
throw "entity type unknown";
}
},
},
};
const store = useStore();
import type { ComputedRef } from "vue";
import {
AccompanyingPeriod,
AccompanyingPeriodWorkEvaluation,
AccompanyingPeriodWork,
} from "ChillPersonAssets/types";
import { PaginationResponse } from "ChillMainAssets/lib/api/apiMethods";
import {
MY_EVALUATIONS_DESCRIPTION,
MAX_DATE,
EVALUATION,
SHOW_ENTITY,
THE_COURSE,
THE_EVALUATION,
SOCIAL_ACTION,
NO_DATA,
trans,
} from "translator";
const evaluations: ComputedRef<
PaginationResponse<AccompanyingPeriodWorkEvaluation>
> = computed(() => store.state.homepage.evaluations);
const isEvaluationsLoaded = computed(() => store.getters.isEvaluationsLoaded);
const noResults = computed(() => {
if (!isEvaluationsLoaded.value) {
return false;
} else {
return evaluations.value.count === 0;
}
});
function getUrl(
e:
| AccompanyingPeriodWorkEvaluation
| AccompanyingPeriod
| AccompanyingPeriodWork,
): string {
if (!e) {
throw "entity is undefined";
}
if ("type" in e && typeof e.type === "string") {
switch (e.type) {
case "accompanying_period_work_evaluation":
return `/fr/person/accompanying-period/work/${e.accompanyingPeriodWork?.id}/edit#evaluations`;
case "accompanying_period_work":
return `/fr/person/accompanying-period/work/${e.id}/edit`;
case "accompanying_period":
return `/fr/parcours/${e.id}`;
default:
throw "entity type unknown";
}
}
return "";
}
</script>
<style scoped></style>

View File

@@ -1,26 +1,26 @@
<template>
<div class="alert alert-light">
{{ $t("my_notifications.description") }}
{{ trans(MY_NOTIFICATIONS_DESCRIPTION) }}
</div>
<span v-if="noResults" class="chill-no-data-statement">{{
$t("no_data")
}}</span>
<span v-if="noResults" class="chill-no-data-statement">
{{ trans(NO_DATA) }}
</span>
<tab-table v-else>
<template #thead>
<th scope="col">
{{ $t("Date") }}
{{ trans(DATE) }}
</th>
<th scope="col">
{{ $t("Subject") }}
{{ trans(SUBJECT) }}
</th>
<th scope="col">
{{ $t("From") }}
{{ trans(FROM) }}
</th>
<th scope="col" />
</template>
<template #tbody>
<tr v-for="(n, i) in notifications.results" :key="`notify-${i}`">
<td>{{ $d(n.date.datetime, "long") }}</td>
<td>{{ $d(new Date(n.date.datetime), "long") }}</td>
<td>
<span class="unread">
<i class="fa fa-envelope-o" />
@@ -31,11 +31,11 @@
{{ n.sender.text }}
</td>
<td v-else>
{{ $t("automatic_notification") }}
{{ trans(AUTOMATIC_NOTIFICATION) }}
</td>
<td>
<a class="btn btn-sm btn-show" :href="getEntityUrl(n)">
{{ $t("show_entity", { entity: getEntityName(n) }) }}
{{ trans(SHOW_ENTITY, { entity: getEntityName(n) }) }}
</a>
</td>
</tr>
@@ -43,65 +43,82 @@
</tab-table>
</template>
<script>
import { mapState, mapGetters } from "vuex";
import TabTable from "./TabTable";
import { appMessages } from "ChillMainAssets/vuejs/HomepageWidget/js/i18n";
<script lang="ts" setup>
import { computed, ComputedRef } from "vue";
import { useStore } from "vuex";
import TabTable from "./TabTable.vue";
import { Notification } from "ChillPersonAssets/types";
export default {
name: "MyNotifications",
components: {
TabTable,
},
computed: {
...mapState(["notifications"]),
...mapGetters(["isNotificationsLoaded"]),
noResults() {
if (!this.isNotificationsLoaded) {
return false;
} else {
return this.notifications.count === 0;
}
},
},
methods: {
getNotificationUrl(n) {
return `/fr/notification/${n.id}/show`;
},
getEntityName(n) {
switch (n.relatedEntityClass) {
case "Chill\\ActivityBundle\\Entity\\Activity":
return appMessages.fr.the_activity;
case "Chill\\PersonBundle\\Entity\\AccompanyingPeriod":
return appMessages.fr.the_course;
case "Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWork":
return appMessages.fr.the_action;
case "Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWorkEvaluationDocument":
return appMessages.fr.the_evaluation_document;
case "Chill\\MainBundle\\Entity\\Workflow\\EntityWorkflow":
return appMessages.fr.the_workflow;
default:
throw "notification type unknown";
}
},
getEntityUrl(n) {
switch (n.relatedEntityClass) {
case "Chill\\ActivityBundle\\Entity\\Activity":
return `/fr/activity/${n.relatedEntityId}/show`;
case "Chill\\PersonBundle\\Entity\\AccompanyingPeriod":
return `/fr/parcours/${n.relatedEntityId}`;
case "Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWork":
return `/fr/person/accompanying-period/work/${n.relatedEntityId}/show`;
case "Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWorkEvaluationDocument":
return `/fr/person/accompanying-period/work/evaluation/document/${n.relatedEntityId}/show`;
case "Chill\\MainBundle\\Entity\\Workflow\\EntityWorkflow":
return `/fr/main/workflow/${n.relatedEntityId}/show`;
default:
throw "notification type unknown";
}
},
},
};
import {
MY_NOTIFICATIONS_DESCRIPTION,
DATE,
FROM,
SUBJECT,
SHOW_ENTITY,
THE_ACTIVITY,
THE_COURSE,
THE_ACTION,
THE_EVALUATION_DOCUMENT,
THE_WORKFLOW,
NO_DATA,
AUTOMATIC_NOTIFICATION,
trans,
} from "translator";
import { PaginationResponse } from "ChillMainAssets/lib/api/apiMethods";
const store = useStore();
const notifications: ComputedRef<PaginationResponse<Notification>> = computed(
() => store.state.homepage.notifications,
);
const isNotificationsLoaded = computed(
() => store.getters.isNotificationsLoaded,
);
const noResults = computed(() => {
if (!isNotificationsLoaded.value) {
return false;
} else {
return notifications.value.count === 0;
}
});
function getNotificationUrl(n: Notification): string {
return `/fr/notification/${n.id}/show`;
}
function getEntityName(n: Notification): string {
switch (n.relatedEntityClass) {
case "Chill\\ActivityBundle\\Entity\\Activity":
return trans(THE_ACTIVITY);
case "Chill\\PersonBundle\\Entity\\AccompanyingPeriod":
return trans(THE_COURSE);
case "Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWork":
return trans(THE_ACTION);
case "Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWorkEvaluationDocument":
return trans(THE_EVALUATION_DOCUMENT);
case "Chill\\MainBundle\\Entity\\Workflow\\EntityWorkflow":
return trans(THE_WORKFLOW);
default:
throw "notification type unknown";
}
}
function getEntityUrl(n: Notification): string {
switch (n.relatedEntityClass) {
case "Chill\\ActivityBundle\\Entity\\Activity":
return `/fr/activity/${n.relatedEntityId}/show`;
case "Chill\\PersonBundle\\Entity\\AccompanyingPeriod":
return `/fr/parcours/${n.relatedEntityId}`;
case "Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWork":
return `/fr/person/accompanying-period/work/${n.relatedEntityId}/show`;
case "Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWorkEvaluationDocument":
return `/fr/person/accompanying-period/work/evaluation/document/${n.relatedEntityId}/show`;
case "Chill\\MainBundle\\Entity\\Workflow\\EntityWorkflow":
return `/fr/main/workflow/${n.relatedEntityId}/show`;
default:
throw "notification type unknown";
}
}
</script>
<style lang="scss" scoped>

View File

@@ -1,36 +1,38 @@
<template>
<div class="alert alert-light">
{{ $t("my_tasks.description_warning") }}
{{ trans(MY_TASKS_DESCRIPTION_WARNING) }}
</div>
<span v-if="noResultsAlert" class="chill-no-data-statement">{{
$t("no_data")
}}</span>
<span v-if="noResultsAlert" class="chill-no-data-statement">
{{ trans(NO_DATA) }}
</span>
<tab-table v-else>
<template #thead>
<th scope="col">
{{ $t("warning_date") }}
{{ trans(WARNING_DATE) }}
</th>
<th scope="col">
{{ $t("max_date") }}
{{ trans(MAX_DATE) }}
</th>
<th scope="col">
{{ $t("task") }}
{{ trans(TASK) }}
</th>
<th scope="col" />
</template>
<template #tbody>
<tr v-for="(t, i) in tasks.alert.results" :key="`task-alert-${i}`">
<td v-if="null !== t.warningDate">
{{ $d(t.warningDate.datetime, "short") }}
<td v-if="t.warningDate !== null">
{{ $d(new Date(t.warningDate.datetime), "short") }}
</td>
<td v-else />
<td>
<span class="outdated">{{ $d(t.endDate.datetime, "short") }}</span>
<span class="outdated">{{
$d(new Date(t.endDate.datetime), "short")
}}</span>
</td>
<td>{{ t.title }}</td>
<td>
<a class="btn btn-sm btn-show" :href="getUrl(t)">
{{ $t("show_entity", { entity: $t("the_task") }) }}
{{ trans(SHOW_ENTITY, { entity: trans(THE_TASK) }) }}
</a>
</td>
</tr>
@@ -38,21 +40,21 @@
</tab-table>
<div class="alert alert-light">
{{ $t("my_tasks.description_alert") }}
{{ trans(MY_TASKS_DESCRIPTION_ALERT) }}
</div>
<span v-if="noResultsWarning" class="chill-no-data-statement">{{
$t("no_data")
}}</span>
<span v-if="noResultsWarning" class="chill-no-data-statement">
{{ trans(NO_DATA) }}
</span>
<tab-table v-else>
<template #thead>
<th scope="col">
{{ $t("warning_date") }}
{{ trans(WARNING_DATE) }}
</th>
<th scope="col">
{{ $t("max_date") }}
{{ trans(MAX_DATE) }}
</th>
<th scope="col">
{{ $t("task") }}
{{ trans(TASK) }}
</th>
<th scope="col" />
</template>
@@ -60,14 +62,14 @@
<tr v-for="(t, i) in tasks.warning.results" :key="`task-warning-${i}`">
<td>
<span class="outdated">{{
$d(t.warningDate.datetime, "short")
$d(new Date(t.warningDate.datetime), "short")
}}</span>
</td>
<td>{{ $d(t.endDate.datetime, "short") }}</td>
<td>{{ $d(new Date(t.endDate.datetime), "short") }}</td>
<td>{{ t.title }}</td>
<td>
<a class="btn btn-sm btn-show" :href="getUrl(t)">
{{ $t("show_entity", { entity: $t("the_task") }) }}
{{ trans(SHOW_ENTITY, { entity: trans(THE_TASK) }) }}
</a>
</td>
</tr>
@@ -75,39 +77,51 @@
</tab-table>
</template>
<script>
import { mapState, mapGetters } from "vuex";
import TabTable from "./TabTable";
<script lang="ts" setup>
import { computed, ComputedRef } from "vue";
import { useStore } from "vuex";
import TabTable from "./TabTable.vue";
import {
MY_TASKS_DESCRIPTION_ALERT,
MY_TASKS_DESCRIPTION_WARNING,
MAX_DATE,
WARNING_DATE,
TASK,
SHOW_ENTITY,
THE_TASK,
NO_DATA,
trans,
} from "translator";
import { TasksState } from "./store/modules/homepage";
import { Alert, Warning } from "ChillPersonAssets/types";
export default {
name: "MyTasks",
components: {
TabTable,
},
computed: {
...mapState(["tasks"]),
...mapGetters(["isTasksWarningLoaded", "isTasksAlertLoaded"]),
noResultsAlert() {
if (!this.isTasksAlertLoaded) {
return false;
} else {
return this.tasks.alert.count === 0;
}
},
noResultsWarning() {
if (!this.isTasksWarningLoaded) {
return false;
} else {
return this.tasks.warning.count === 0;
}
},
},
methods: {
getUrl(t) {
return `/fr/task/single-task/${t.id}/show`;
},
},
};
const store = useStore();
const tasks: ComputedRef<TasksState> = computed(
() => store.state.homepage.tasks,
);
const isTasksWarningLoaded = computed(() => store.getters.isTasksWarningLoaded);
const isTasksAlertLoaded = computed(() => store.getters.isTasksAlertLoaded);
const noResultsAlert = computed(() => {
if (!isTasksAlertLoaded.value) {
return false;
} else {
return tasks.value.alert.count === 0;
}
});
const noResultsWarning = computed(() => {
if (!isTasksWarningLoaded.value) {
return false;
} else {
return tasks.value.warning.count === 0;
}
});
function getUrl(t: Warning | Alert): string {
return `/fr/task/single-task/${t.id}/show`;
}
</script>
<style scoped>

View File

@@ -0,0 +1,58 @@
<template>
<div class="col-12">
<!-- Loading state -->
<div
v-if="isTicketLoading"
class="d-flex justify-content-center align-items-center"
style="height: 200px"
>
<div class="text-center">
<div class="spinner-border mb-3" role="status">
<span class="visually-hidden">{{
trans(CHILL_TICKET_LIST_LOADING_TICKET)
}}</span>
</div>
<div class="text-muted">
{{ trans(CHILL_TICKET_LIST_LOADING_TICKET) }}
</div>
</div>
</div>
<!-- Ticket list -->
<ticket-list-component
v-else
:tickets="ticketList"
title=""
:hasMoreTickets="pagination.next !== null"
@fetchNextPage="fetchNextPage"
/>
</div>
</template>
<script lang="ts" setup>
import { computed } from "vue";
import { useStore } from "vuex";
// Components
import TicketListComponent from "../../../../../ChillTicketBundle/src/Resources/public/vuejs/TicketList/components/TicketListComponent.vue";
// Types
import { Pagination } from "ChillMainAssets/lib/api/apiMethods";
import { TicketSimple } from "src/Bundle/ChillTicketBundle/src/Resources/public/types";
// Translation
import { CHILL_TICKET_LIST_LOADING_TICKET, trans } from "translator";
const store = useStore();
const pagination = computed(() => store.getters.getPagination as Pagination);
const ticketList = computed(
() => store.getters.getTicketList as TicketSimple[],
);
const isTicketLoading = computed(() => store.getters.isTicketLoading);
const fetchNextPage = async () => {
if (pagination.value.next) {
await store.dispatch("fetchTicketListByUrl", pagination.value.next);
}
};
</script>

View File

@@ -1,26 +1,27 @@
<template>
<div class="alert alert-light">
{{ $t("my_workflows.description") }}
{{ trans(MY_WORKFLOWS_DESCRIPTION) }}
</div>
<my-workflows-table :workflows="workflows" />
<div class="alert alert-light">
{{ $t("my_workflows.description_cc") }}
{{ trans(MY_WORKFLOWS_DESCRIPTION_CC) }}
</div>
<my-workflows-table :workflows="workflowsCc" />
</template>
<script>
import { mapState } from "vuex";
<script lang="ts" setup>
import { computed } from "vue";
import { useStore } from "vuex";
import MyWorkflowsTable from "./MyWorkflowsTable.vue";
import {
MY_WORKFLOWS_DESCRIPTION,
MY_WORKFLOWS_DESCRIPTION_CC,
trans,
} from "translator";
export default {
name: "MyWorkflows",
components: {
MyWorkflowsTable,
},
computed: {
...mapState(["workflows", "workflowsCc"]),
},
};
const store = useStore();
const workflows = computed(() => store.state.homepage.workflows);
const workflowsCc = computed(() => store.state.homepage.workflowsCc);
</script>

View File

@@ -1,17 +1,17 @@
<template>
<span v-if="hasNoResults(workflows)" class="chill-no-data-statement">{{
$t("no_data")
}}</span>
<span v-if="hasNoResults" class="chill-no-data-statement">
{{ trans(NO_DATA) }}
</span>
<tab-table v-else>
<template #thead>
<th scope="col">
{{ $t("Object_workflow") }}
{{ trans(OBJECT_WORKFLOW) }}
</th>
<th scope="col">
{{ $t("Step") }}
{{ trans(STEP) }}
</th>
<th scope="col">
{{ $t("concerned_users") }}
{{ trans(CONCERNED_USERS) }}
</th>
<th scope="col" />
</template>
@@ -29,7 +29,7 @@
<span
v-if="w.isOnHoldAtCurrentStep"
class="badge bg-success rounded-pill"
>{{ $t("on_hold") }}</span
>{{ trans(ON_HOLD) }}</span
>
</div>
</td>
@@ -46,7 +46,7 @@
</td>
<td>
<a class="btn btn-sm btn-show" :href="getUrl(w)">
{{ $t("show_entity", { entity: $t("the_workflow") }) }}
{{ trans(SHOW_ENTITY, { entity: trans(THE_WORKFLOW) }) }}
</a>
</td>
</tr>
@@ -54,38 +54,48 @@
</tab-table>
</template>
<script>
import { mapGetters } from "vuex";
import TabTable from "./TabTable";
import OnTheFly from "ChillMainAssets/vuejs/OnTheFly/components/OnTheFly";
<script lang="ts" setup>
import { computed } from "vue";
import { useStore } from "vuex";
import TabTable from "./TabTable.vue";
import OnTheFly from "ChillMainAssets/vuejs/OnTheFly/components/OnTheFly.vue";
import { Workflow } from "ChillPersonAssets/types";
import {
CONCERNED_USERS,
OBJECT_WORKFLOW,
ON_HOLD,
SHOW_ENTITY,
THE_WORKFLOW,
NO_DATA,
STEP,
trans,
} from "translator";
import { PaginationResponse } from "ChillMainAssets/lib/api/apiMethods";
export default {
name: "MyWorkflows",
components: {
TabTable,
OnTheFly,
},
props: ["workflows"],
computed: {
...mapGetters(["isWorkflowsLoaded"]),
},
methods: {
hasNoResults(workflows) {
if (!this.isWorkflowsLoaded) {
return false;
} else {
return workflows.count === 0;
}
},
getUrl(w) {
return `/fr/main/workflow/${w.id}/show`;
},
getStep(w) {
const lastStep = w.steps.length - 1;
return w.steps[lastStep].currentStep.text;
},
},
};
const props = defineProps<{
workflows: PaginationResponse<Workflow>;
}>();
const store = useStore();
const isWorkflowsLoaded = computed(() => store.getters.isWorkflowsLoaded);
const hasNoResults = computed(() => {
if (!isWorkflowsLoaded.value) {
return false;
} else {
return props.workflows.count === 0;
}
});
function getUrl(w: Workflow): string {
return `/fr/main/workflow/${w.id}/show`;
}
function getStep(w: Workflow): string {
const lastStep = w.steps.length - 1;
return w.steps[lastStep].currentStep.text;
}
</script>
<style scoped>

View File

@@ -1,27 +1,26 @@
// CURRENTLY NOT IN USE
<template>
<div class="accompanying-course-work">
<div class="alert alert-light">
{{ $t("my_works.description") }}
{{ trans(MY_WORKS_DESCRIPTION) }}
</div>
<span v-if="noResults" class="chill-no-data-statement">{{
$t("no_data")
}}</span>
<span v-if="noResults" class="chill-no-data-statement">
{{ trans(NO_DATA) }}
</span>
<tab-table v-else>
<template #thead>
<th scope="col">
{{ $t("StartDate") }}
{{ trans(START_DATE) }}
</th>
<th scope="col">
{{ $t("SocialAction") }}
{{ trans(SOCIAL_ACTION) }}
</th>
<th scope="col">
{{ $t("concerned_persons") }}
{{ trans(CONCERNED_PERSONS) }}
</th>
<th scope="col" />
</template>
<template #tbody>
<tr v-for="(w, i) in works.results" :key="`works-${i}`">
<tr v-for="(w, i) in works.value.results" :key="`works-${i}`">
<td>{{ $d(w.startDate.datetime, "short") }}</td>
<td>
<span class="chill-entity entity-social-issue">
@@ -51,8 +50,8 @@
<div class="btn-group-vertical" role="group" aria-label="Actions">
<a class="btn btn-sm btn-update" :href="getUrl(w)">
{{
$t("show_entity", {
entity: $t("the_action"),
trans(SHOW_ENTITY, {
entity: trans(THE_ACTION),
})
}}
</a>
@@ -61,8 +60,8 @@
:href="getUrl(w.accompanyingPeriod)"
>
{{
$t("show_entity", {
entity: $t("the_course"),
trans(SHOW_ENTITY, {
entity: trans(THE_COURSE),
})
}}
</a>
@@ -74,41 +73,47 @@
</div>
</template>
<script>
import { mapState, mapGetters } from "vuex";
import TabTable from "./TabTable";
import OnTheFly from "ChillMainAssets/vuejs/OnTheFly/components/OnTheFly";
<script lang="ts" setup>
import { computed } from "vue";
import { useStore } from "vuex";
import TabTable from "./TabTable.vue";
import OnTheFly from "ChillMainAssets/vuejs/OnTheFly/components/OnTheFly.vue";
import {
MY_WORKS_DESCRIPTION,
CONCERNED_PERSONS,
SHOW_ENTITY,
THE_COURSE,
THE_ACTION,
SOCIAL_ACTION,
START_DATE,
NO_DATA,
trans,
} from "translator";
import { Workflow } from "ChillPersonAssets/types";
export default {
name: "MyWorks",
components: {
TabTable,
OnTheFly,
},
computed: {
...mapState(["works"]),
...mapGetters(["isWorksLoaded"]),
noResults() {
if (!this.isWorksLoaded) {
return false;
} else {
return this.works.count === 0;
}
},
},
methods: {
getUrl(e) {
switch (e.type) {
case "accompanying_period_work":
return `/fr/person/accompanying-period/work/${e.id}/edit`;
case "accompanying_period":
return `/fr/parcours/${e.id}`;
default:
throw "entity type unknown";
}
},
},
};
const store = useStore();
const works = computed(() => store.state.homepage.works);
const isWorksLoaded = computed(() => store.getters.isWorksLoaded);
const noResults = computed(() => {
if (!isWorksLoaded.value) {
return false;
} else {
return works.value.count === 0;
}
});
function getUrl(e: Workflow): string {
switch (e.type) {
case "accompanying_period_work":
return `/fr/person/accompanying-period/work/${e.id}/edit`;
case "accompanying_period":
return `/fr/parcours/${e.id}`;
default:
throw "entity type unknown";
}
}
</script>
<style scoped></style>

View File

@@ -4,14 +4,14 @@
</span>
</template>
<script>
export default {
name: "TabCounter",
props: ["count"],
computed: {
isCounterAvailable() {
return typeof this.count !== "undefined" && this.count > 0;
},
},
};
<script lang="ts" setup>
import { computed } from "vue";
const props = defineProps<{
count: number;
}>();
const isCounterAvailable = computed(
() => typeof props.count !== "undefined" && props.count > 0,
);
</script>

View File

@@ -11,11 +11,8 @@
</table>
</template>
<script>
export default {
name: "TabTable",
props: [],
};
<script lang="ts" setup>
// Pas de props à définir, composant slot simple
</script>
<style scoped></style>

View File

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

View File

@@ -0,0 +1,55 @@
import { createStore } from "vuex";
import { State as HomepageStates, moduleHomepage } from "./modules/homepage";
import {
State as TicketListStates,
moduleTicketList,
} from "../../../../../../ChillTicketBundle/src/Resources/public/vuejs/TicketApp/store/modules/ticket_list";
import {
State as TicketStates,
moduleTicket,
} from "../../../../../../ChillTicketBundle/src/Resources/public/vuejs/TicketApp/store/modules/ticket";
import {
State as CommentStates,
moduleComment,
} from "../../../../../../ChillTicketBundle/src/Resources/public/vuejs/TicketApp/store/modules/comment";
import {
moduleMotive,
State as MotiveStates,
} from "../../../../../../ChillTicketBundle/src/Resources/public/vuejs/TicketApp/store/modules/motive";
import {
State as AddresseeStates,
moduleAddressee,
} from "../../../../../../ChillTicketBundle/src/Resources/public/vuejs/TicketApp/store/modules/addressee";
import {
modulePersons,
State as PersonsState,
} from "../../../../../../ChillTicketBundle/src/Resources/public/vuejs/TicketApp/store/modules/persons";
import {
moduleUser,
State as UserState,
} from "../../../../../../ChillTicketBundle/src/Resources/public/vuejs/TicketApp/store/modules/user";
export interface RootState {
homepage: HomepageStates;
motive: MotiveStates;
ticket: TicketStates;
comment: CommentStates;
addressee: AddresseeStates;
persons: PersonsState;
ticketList: TicketListStates;
user: UserState;
}
export const store = createStore<RootState>({
modules: {
homepage: moduleHomepage,
motive: moduleMotive,
ticket: moduleTicket,
comment: moduleComment,
addressee: moduleAddressee,
person: modulePersons,
ticketList: moduleTicketList,
user: moduleUser,
},
});

View File

@@ -1,90 +1,113 @@
import "es6-promise/auto";
import { createStore } from "vuex";
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
import { Module } from "vuex";
import {
makeFetch,
PaginationResponse,
} from "ChillMainAssets/lib/api/apiMethods";
import {
AccompanyingCourse,
Alert,
Evaluation,
Warning,
Workflow,
WorflowCc,
} from "ChillPersonAssets/types";
import { RootState } from "..";
const debug = process.env.NODE_ENV !== "production";
export interface TasksState {
warning: PaginationResponse<Warning>;
alert: PaginationResponse<Alert>;
}
const isEmpty = (obj) => {
return (
obj &&
Object.keys(obj).length <= 1 &&
Object.getPrototypeOf(obj) === Object.prototype
);
};
export interface State {
evaluations: PaginationResponse<Evaluation>;
tasks: TasksState;
accompanyingCourses: PaginationResponse<AccompanyingCourse>;
notifications: PaginationResponse<Notification>;
workflows: PaginationResponse<Workflow>;
workflowsCc: PaginationResponse<WorflowCc>;
errorMsg: unknown[];
loading: boolean;
ticketsLoading: boolean;
}
const store = createStore({
strict: debug,
export const moduleHomepage: Module<State, RootState> = {
state: {
// works: {},
evaluations: {},
evaluations: {
count: 0,
} as PaginationResponse<Evaluation>,
tasks: {
warning: {},
alert: {},
warning: {
count: 0,
} as PaginationResponse<Warning>,
alert: {
count: 0,
} as PaginationResponse<Alert>,
},
accompanyingCourses: {},
notifications: {},
workflows: {},
workflowsCc: {},
accompanyingCourses: {
count: 0,
} as PaginationResponse<AccompanyingCourse>,
notifications: {
count: 0,
} as PaginationResponse<Notification>,
workflows: {
count: 0,
} as PaginationResponse<Workflow>,
workflowsCc: {
count: 0,
} as PaginationResponse<WorflowCc>,
ticketsLoading: false,
errorMsg: [],
loading: false,
},
getters: {
// isWorksLoaded(state) {
// return !isEmpty(state.works);
// },
isTicketLoading: (state) => {
return state.ticketsLoading;
},
isEvaluationsLoaded(state) {
return !isEmpty(state.evaluations);
return Array.isArray(state.evaluations.results);
},
isTasksWarningLoaded(state) {
return !isEmpty(state.tasks.warning);
return Array.isArray(state.tasks.warning.results);
},
isTasksAlertLoaded(state) {
return !isEmpty(state.tasks.alert);
return Array.isArray(state.tasks.alert.results);
},
isAccompanyingCoursesLoaded(state) {
return !isEmpty(state.accompanyingCourses);
return Array.isArray(state.accompanyingCourses.results);
},
isNotificationsLoaded(state) {
return !isEmpty(state.notifications);
return Array.isArray(state.notifications.results);
},
isWorkflowsLoaded(state) {
return !isEmpty(state.workflows);
return Array.isArray(state.workflows.results);
},
counter(state) {
counter(state, getters, rootState, rootGetters) {
return {
// works: state.works.count,
evaluations: state.evaluations.count,
tasksWarning: state.tasks.warning.count,
tasksAlert: state.tasks.alert.count,
accompanyingCourses: state.accompanyingCourses.count,
notifications: state.notifications.count,
workflows: state.workflows.count,
tickets: rootGetters["ticketList/getCount"] || 0,
evaluations: state.evaluations.count || 0,
tasksWarning: state.tasks.warning.count || 0,
tasksAlert: state.tasks.alert.count || 0,
accompanyingCourses: state.accompanyingCourses.count || 0,
notifications: state.notifications.count || 0,
workflows: state.workflows.count || 0,
};
},
},
mutations: {
// addWorks(state, works) {
// //console.log('addWorks', works);
// state.works = works;
// },
addEvaluations(state, evaluations) {
//console.log('addEvaluations', evaluations);
state.evaluations = evaluations;
},
addTasksWarning(state, tasks) {
//console.log('addTasksWarning', tasks);
state.tasks.warning = tasks;
},
addTasksAlert(state, tasks) {
//console.log('addTasksAlert', tasks);
state.tasks.alert = tasks;
},
addCourses(state, courses) {
//console.log('addCourses', courses);
state.accompanyingCourses = courses;
},
addNotifications(state, notifications) {
//console.log('addNotifications', notifications);
state.notifications = notifications;
},
addWorkflows(state, workflows) {
@@ -96,29 +119,29 @@ const store = createStore({
setLoading(state, bool) {
state.loading = bool;
},
setTicketsLoading(state, bool) {
state.ticketsLoading = bool;
},
catchError(state, error) {
state.errorMsg.push(error);
},
},
actions: {
getByTab({ commit, getters }, { tab, param }) {
async getByTab({ commit, getters, dispatch }, { tab, param }) {
switch (tab) {
// case 'MyWorks':
// if (!getters.isWorksLoaded) {
// commit('setLoading', true);
// const url = `/api/1.0/person/accompanying-period/work/my-near-end${'?'+ param}`;
// makeFetch('GET', url)
// .then((response) => {
// commit('addWorks', response);
// commit('setLoading', false);
// })
// .catch((error) => {
// commit('catchError', error);
// throw error;
// })
// ;
// }
// break;
case "MyTickets":
if (!getters.isTicketsLoaded) {
commit("setTicketsLoading", true);
// Utilise l'action du module ticket_list
await dispatch(
"fetchTicketList",
{ byAddresseeToMe: true },
{ root: true },
);
commit("setTicketsLoading", false);
}
break;
case "MyEvaluations":
if (!getters.isEvaluationsLoaded) {
commit("setLoading", true);
@@ -180,7 +203,6 @@ const store = createStore({
const url = `/api/1.0/main/notification/my/unread${"?" + param}`;
makeFetch("GET", url)
.then((response) => {
console.log("notifications", response);
commit("addNotifications", response);
commit("setLoading", false);
})
@@ -217,6 +239,4 @@ const store = createStore({
}
},
},
});
export { store };
};

View File

@@ -46,10 +46,7 @@
</li>
</ul>
<ul
class="badge-suggest add-items inline"
style="justify-content: flex-end; display: flex"
>
<ul class="badge-suggest add-items inline text-end">
<li
v-for="s in suggested"
:key="s.type + s.id"