task_ui/components/TaskUserReport.vue
2025-04-21 11:49:42 +03:30

913 lines
25 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="container-fluid">
<div class="row">
<div>
<div class="all-selects justify-content-between col-12">
<div class="selects col-md-6 justify-content-start">
<div>
<label class="teams-label"> ماه: </label>
<select
id="month-select"
class="form-control ms-1"
v-model="selectedMonth"
@change="getMainList"
>
<option v-for="month in months" :key="month" :value="month">
{{ getPersianMonthName(month) }}
</option>
</select>
</div>
<div class="me-3 ms-3">
<label class="teams-label"> سال: </label>
<select
id="year-select"
class="form-control"
v-model="selectedYear"
@change="getMainList"
>
<option v-for="year in years" :key="year" :value="year">
{{ year }}
</option>
</select>
</div>
<div>
<label class="teams-label">تیم ها:</label>
<select
v-model="group_id"
@change="getMainList"
class="form-select form-control"
>
<option selected value="">همه</option>
<option v-for="item in groups" :value="item.id">
{{ item.title }}
</option>
</select>
</div>
</div>
<div
class="d-flex align-items-center grouping col-md-6 justify-content-end"
>
<div class="select-teams">
<label class="teams-label-Grouping"> گروه بندی:</label>
<select
v-model="groupType"
@change="getsorting()"
class="form-control"
>
<option selected value="nogroup">بدون گروه بندی</option>
<option value="day">روز</option>
<option value="category">دسته</option>
<option value="title">عنوان</option>
<option value="organ">سازمان</option>
</select>
</div>
<div class="switch-component">
<switch-component
name="switch-view-mode"
@change-mode="showchart($event)"
class="task-admin-switch"
texts1="Normal"
texts2="Chartical"
></switch-component>
</div>
</div>
</div>
<main class="pages-content-container">
<div class="pages-content pt-3">
<div class="" :class="{ 'd-flex': showPanel || showPanelTeams }">
<template v-if="canView">
<my-table
class="p-0 ms-3 col-12 col-md-6"
:tableActions="tableActions"
height="calc(100vh + -14em)"
minHeight="25em"
maxHeight="calc(100dvh + -13em)"
:paginationInfo="pagination"
:sortingInfo="sorting"
:items="mainList"
:fetchingData="fetchingData"
:tableColumns="tableColumns"
:totalPages="pagination.pages"
:showActions="false"
@search="searchUsers"
@reset-form="searchUsers"
@delete-table-item="deleteItem"
@edit-table-item="toggleUsersPanel"
@page-changed="pageChanged"
@page-limit-changed="pageLimitChanged"
@sort-changed="sortChanged"
@on-linked-title-click="onLinkedTitleClick"
>
</my-table>
<div class="col-6 form-sorting_user">
<component
v-if="showcomponent"
:is="changeSorting()"
:listItem="listItem"
:columns="columnGroup"
:workList="workList"
:userId="userId"
></component>
<pie-donut
class="mt-5 me-5 d-flex justify-content-end"
v-if="showChart"
:dataPie="dataPie"
:PieOptions="PieOptions"
width="100%"
height="300px"
></pie-donut>
</div>
</template>
<no-data v-else>
<the-content-loading v-if="fetchingData"></the-content-loading>
<div
v-else
class="d-flex justify-content-center align-items-center"
>
<div
class="alert alert-warning d-flex justify-content-center align-items-center"
>
<span
class="tavasi tavasi-warning-circle color-inherit ms-1 text__32"
></span>
عدم دسترسی
</div>
</div>
</no-data>
<!-- <div class="side-panel" v-if="showPanel">
<div
class="side-panel-header d-flex mb-3 pb-3 border-bottom align-items-center"
>
<h5 class="m-0">جستجو و انتخاب مدیر گروه</h5>
<button
@click.prevent="toggleUsersPanel()"
class="btn me-auto"
type="button"
>
<svg class="icon icon-Component-294--1">
<use xlink:href="#icon-Component-294--1"></use>
</svg>
</button>
</div>
<div class="side-panel-content">
<users-search
v-if="showPanel"
:searchResults="users"
@on-send="searchUsers"
@set-as-admin="setAsAdmin"
></users-search>
</div>
</div> -->
<!-- <div class="side-panel" v-if="showPanelTeams">
<div
class="side-panel-header d-flex mb-3 pb-3 border-bottom align-items-center"
>
<h6 class="m-0">مدیریت کاربران</h6>
<button
@click.prevent="closeShowPanelTeams()"
class="btn btn-outline-primary me-auto"
type="button"
>
x
</button>
</div>
<div class="side-panel-content">
<TeamForm
v-if="showPanelTeams"
@on-pass-by-emit="save"
></TeamForm>
</div>
</div> -->
</div>
</div>
</main>
</div>
</div>
</div>
</template>
<script>
import { mapGetters, mapActions } from "vuex";
import HttpService from "@services/httpService";
import menu from "@task/json/menu.json";
import adminApi from "@apis/adminApi";
import taskApi from "@apis/taskApi";
import { p2e } from "@plugins/persianNumber";
export default {
beforeMount() {
this.httpService = new HttpService(this.taskMicroServiceName);
},
mounted() {
this.checkPermisionBeforGetList();
var d = new Date().toLocaleDateString("fa-IR");
let items = d.split("/");
this.selectedYear = p2e(items[0]);
this.selectedMonth = p2e(items[1]);
this.getWorkingHoursInformation();
this.getTaskGroups();
},
data() {
return {
PieOptions: {
radius: ["40%", "70%"],
selectedMode: "single",
},
showChart: false,
showcomponent: true,
listItem: [],
group_id: "",
groupType: "nogroup",
userId: "",
tasks: [{ category: "", title: "", duration: "" }],
groups: [],
months: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"],
years: ["1400", "1401", "1402", "1403", "1404", "1405"],
selectedYear: "",
selectedMonth: "",
mainList: [],
menu: menu,
firstTimeSearching: false,
httpService: undefined,
tableActions: [
{
showOutside: true,
show: true,
icon: "Component-242--1",
title: "تعیین مدیر",
to: {
name: "undefined",
},
selected: false,
disabled: false,
howToOpen: "",
href: "",
class: "edit-btn",
action: "edit-table-item",
},
{
showOutside: true,
show: true,
icon: "Component-295--1",
title: "حذف",
to: {
name: "undefined",
},
selected: false,
disabled: false,
howToOpen: "",
href: "",
class: "delete-btn",
action: "delete-table-item",
},
],
canView: false,
fetchingData: false,
tableColumns: [
{
isLink: true,
key: "team",
title: "تیم",
width: "2",
},
{
isLink: true,
key: "firstName",
title: "نام",
width: "2",
},
{
isLink: true,
key: "lastname",
title: "نام خانوادگی",
width: "2",
},
{
isLink: true,
key: "useTime",
title: "جمع خالص ماهانه",
width: "2",
},
{
isLink: true,
key: "allTime",
title: "جمع کل ماهانه",
width: "2",
},
{
isLink: true,
key: "noWorkTime",
title: " ساعات غیر کاری",
width: "2",
},
{
isLink: true,
key: "averageDay",
title: "میانگین روز",
width: "2",
},
{
isLink: true,
key: "alldays",
title: "تعداد روز",
width: "2",
},
],
sorting: {
sortby: "",
sortorder: "", // asc | desc | none
},
selectedItem: {},
selectedItemClone: {},
cloneItems: [],
prevSelectedItemIndex: undefined,
users: [],
loading: false,
showPanel: true,
showPanelTeams: false,
workList: [],
rotated: false,
dataPie: [],
};
},
computed: {
...mapGetters(["isSidebarCollapsed"]),
columnGroup() {
if (this.groupType == "category")
return [
{ label: "دسته", key: "category", class: "col-6" },
{ label: "تعداد روز", key: "day_count", class: "col-2" },
{ label: "تعداد فرآیند", key: "task_count", class: "col-2" },
{ label: "مدت", key: "duration", class: "col-2" },
];
else if (this.groupType == "organ")
return [
{ label: "سازمان", key: "organ", class: "col-6" },
{ label: "تعداد روز", key: "day_count", class: "col-2" },
{ label: "تعداد فرآیند", key: "task_count", class: "col-2" },
{ label: "مدت", key: "duration", class: "col-2" },
];
else if (this.groupType == "title")
return [
{ label: "عنوان", key: "title", class: "col-6" },
{ label: "تعداد روز", key: "day_count", class: "col-2" },
{ label: "تعداد فرآیند", key: "category_count", class: "col-2" },
{ label: "مدت", key: "duration", class: "col-2" },
];
else return [
{ label: "تاریخ", key: "normalDate", class: "col-2" },
{ label: "دسته", key: "category", class: "col-3" },
{ label: "عنوان", key: "title", class: "col-6" },
{ label: "مدت", key: "duration", class: "col-1" },
];
},
buildName() {
return process.env.VUE_APP_BUILD_NAME + "-sidebar";
},
},
methods: {
...mapActions(["checkPermissions"]),
checkPermisionBeforGetList() {
if (this.fetchingData) return;
this.fetchingData = true;
this.checkPermissions({ _this: this, permission: "admin_list" })
.then(() => {
this.getMainList()
.then(() => {
this.canView = true;
this.fetchingData = false;
})
.catch(() => {
this.canView = false;
this.fetchingData = false;
});
})
.catch(() => {
this.canView = false;
this.fetchingData = false;
});
},
async getMainList() {
let url = taskApi.taskReport.getList;
let payload = {
group_id: this.group_id,
year: this.selectedYear,
month: this.selectedMonth,
...this.sorting,
...this.pagination,
};
return await this.httpService
.postRequest(url, payload)
.then((res) => {
this.mainList = res.data;
this.pagination = { ...this.pagination, ...res.pagination };
this.onLinkedTitleClick({
rowItem: this.markActive(0),
tableColumn: {},
index: 0,
});
})
.catch((err) => {
console.info(err);
});
},
markActive(index) {
if (this.prevSelectedItemIndex !== undefined) {
this.$set(this.mainList[this.prevSelectedItemIndex], "active", false);
}
this.prevSelectedItemIndex = index;
this.$set(this.mainList[index], "active", true);
this.selectedItemClone = structuredClone(this.mainList[index]);
return this.selectedItemClone;
},
onLinkedTitleClick({ rowItem, tableColumn, index }) {
this.userId = rowItem.user_id;
let payload = {
user_id: this.userId,
year: this.selectedYear,
month: this.selectedMonth,
};
const url = taskApi.workingHours.load;
this.httpService
.postRequest(url, payload)
.then((res) => {
this.workList = res.data;
this.showPanel = true;
this.markActive(index);
})
.finally(() => {
if (this.showChart) {
this.getvalueChartDonut();
}
this.getsorting();
});
},
getWorkingHoursInformation() {
// console.log(this.currentUser);
const payload = {
user_id: this.currentUser.user_id,
// offset: 0,
// limit: 50,
sortby: "taskDate",
sortorder: "desc",
year: this.selectedYear,
month: this.selectedMonth,
};
const url = taskApi.workingHours.load;
this.httpService
.postRequest(url, payload)
.then((res) => {
this.workList = res.data;
// console.log("111 ", this.workList);
})
.finally(() => {
this.getsorting();
if (this.showChart) {
this.getvalueChartDonut();
}
});
},
datefa(item) {
let m = item * 1000;
let d = new Date(m).toLocaleDateString("fa-IR");
return d;
},
selectDate(items) {
if (items.oldDate) {
let item = items.oldDate.split("-");
let date = {};
date.day = item[2];
date.month = item[1];
date.year = item[0];
this.$emit("select-day", date);
}
},
// async getGroupsWithoutAdmin(query = "") {
// let url = `${this.messageMicroServiceName}/${adminApi.groups.groupsWithoutAdmin}`;
// return await this.httpService
// .getRequest(this.updateUrl(url, query))
// .then((res) => {
// this.groups = res.data;
// this.pagination = { ...this.pagination, ...res.pagination };
// });
// },
// getGroupsWithAdmin() {
// if (this.fetchingData) return;
// this.fetchingData = true;
// let url = `${this.messageMicroServiceName}/${adminApi.groups.groupsWithAdmin}`;
// this.httpService
// .getRequest(this.updateUrl(url))
// .then((response) => {
// this.groups = response.data;
// this.pagination = { ...this.pagination, ...response.pagination };
// })
//
// .finally(() => {
// this.fetchingData = false;
// });
// },
updateUrl(url, query = "") {
url = url.replace("@offset", this.pagination.offset);
url = url.replace("@limit", this.pagination.limit);
url = url.replace("@sortby", this.sorting.sortby);
url = url.replace("@sortorder", this.sorting.sortorder);
// for searching groups.
if (query?.length) url = url.replace("@query", query);
else url = url.replace("/@query", query);
return url;
},
searchUsers(query = "") {
this.resetPagination();
this[groupType](query);
// this.getGroupsWithoutAdmin(query);
},
// setSearchType(groupType) {
// this.searchType = groupType;
// this.resetPagination();
// this[groupType]();
// },
setAsAdmin(user) {
if (this.loading) return;
this.loading = true;
const url = this.messageMicroServiceName + "/" + chatApi.groups.setAdmin;
let payload = {
user_id: user.id,
group_id: this.selectedItemClone.id,
};
this.httpService
.postRequest(url, payload)
.then((res) => {
this.getGroupsWithoutAdmin().then(() => {
this.loading = false;
});
this.mySwalToast({
html: res.message,
});
this.showPanel = false;
this.resetForm();
})
.finally(() => {
this.loading = false;
});
},
toggleUsersPanel(index = undefined) {
if (index !== undefined) {
if (this.prevSelectedItemIndex !== undefined)
this.mainList[this.prevSelectedItemIndex].active = false;
this.prevSelectedItemIndex = index;
this.$set(this.mainList[index], "active", true);
this.selectedItemClone = structuredClone(this.mainList[index]);
this.showPanel = true;
this.showPanelTeams = false;
} else {
this.resetForm();
}
},
resetForm() {
this.selectedItemClone = undefined;
this.users = [];
this.showPanel = false;
},
// pagination and sorting
resetPagination() {
this.pagination = {
pages: 0,
total: 0,
page: 1,
offset: 0,
limit: 10,
};
},
pageLimitChanged(paging) {
this.resetPagination();
this.pagination.limit = paging.limit;
this.getMainList();
// this[this.searchType]();
},
pageChanged(paging) {
let page = paging.pageNumber;
page -= 1;
this.pagination.offset = page * paging.limit;
this.pagination.limit = paging.limit;
this.pagination.page = paging.pageNumber;
this.getMainList();
// this[this.searchType]();
},
sortChanged(sorting) {
this.pagination.page = this.pagination.offset = 0;
this.sorting = sorting;
this.getMainList();
// this[this.searchType]();
},
updateList() {
this.resetPagination();
this.getMainList();
},
// not used yet.
deleteItem(index) {
if (this.loading) return;
this.loading = true;
let groupId = this.mainList[index].id;
const payload = {
group_id: groupId,
};
const url = `${this.messageMicroServiceName}/${adminApi.groups.delete}`;
this.mySwalConfirm({
title: "هشدار",
html: "با حذف گروه، تمامی مسائل و پاسخ های آن نیز حذف خواهد شد.",
icon: "warning",
}).then((result) => {
if (result.isConfirmed) {
this.httpService.postRequest(url, payload).then((res) => {
this[this.searchType]().then(() => {
this.loading = false;
// this.showPanel = false;
});
this.mySwalToast({
html: res.message,
});
});
}
});
},
openCreatePanel() {
if (this.showPanelTeams) {
this.showPanelTeams = false;
} else {
this.showPanelTeams = true;
this.showPanel = false;
}
},
closeShowPanelTeams() {
this.showPanelTeams = false;
},
save(formData = undefined) {
if (this.loading) return;
this.loading = true;
const url = formData.id ? apis.users.update : apis.users.create;
formData;
this.httpService
.postRequest(url, formData)
.then((res) => {
this.getUsers().then(() => {
this.loading = false;
});
this.mySwalToast({
html: res.message,
});
this.showPanel = false;
this.resetForm();
})
.finally(() => {
this.loading = false;
});
},
getPersianMonthName(month) {
const monthNames = [
"فروردین",
"اردیبهشت",
"خرداد",
"تیر",
"مرداد",
"شهریور",
"مهر",
"آبان",
"آذر",
"دی",
"بهمن",
"اسفند",
];
return monthNames[parseInt(month) - 1];
},
getTaskGroups() {
const payload = {
organ: "",
sortby: "title",
sortorder: "asc",
};
const url = taskApi.taskReport.groupList;
this.httpService
.postRequest(url, payload)
.then((res) => {
this.groups = res.data;
});
},
changeSorting() {
if (this.groupType == "day") {
return "SortingByDayTasksAdmin";
} else {
return "SortingTasksAdmin";
}
},
getsorting() {
let payload = {
user_id: this.userId,
year: this.selectedYear,
month: this.selectedMonth,
group: this.groupType,
};
const url = taskApi.taskReport.sortgroup;
this.httpService
.postRequest(url, payload)
.then((res) => {
this.listItem = res.data;
})
.finally(() => {
return "SortingTasksAdmin";
});
},
showchart(value) {
if (!value) {
this.showChart = true;
this.showcomponent = false;
this.getvalueChartDonut();
return "PieDonut";
} else {
this.showChart = false;
this.showcomponent = true;
}
},
getvalueChartDonut() {
let payload = {
user_id: this.userId,
year: this.selectedYear,
month: this.selectedMonth,
};
let url = taskApi.taskChart.donut;
var vm = this;
this.httpService.postRequest(url, payload).then((res) => {
vm.dataPie = [];
res.data.forEach((element) => {
vm.dataPie.push({
y: element.Percent,
name: element.category,
// color: colors[5],
drilldown: {
categories: [element.category],
data: [element.Total],
},
});
});
});
},
},
components: {
UsersSearch: () => import("@admin/components/UsersSearch"),
TeamForm: () => import("@task/components/TeamForm"),
SortingTasksAdmin: () => import("@task/components/SortingTasksAdmin"),
SortingByDayTasksAdmin: () =>
import("@task/components/SortingByDayTasksAdmin"),
PieDonut: () => import("@components/charts/PieDonut.vue"),
SwitchComponent: () => import("@components/SwitchComponent.vue"),
},
};
</script>
<style lang="scss">
.task-system {
.table-container {
.table-responsive {
height: calc(100vh + -15em) !important;
.table {
font-size: 1rem;
}
}
}
}
</style>
<style scoped lang="scss">
.form-control {
border-radius: 0.5em;
height: 2.5em;
text-align: end;
}
// .side-panel {
// padding: 1em;
// width: 30em;
// max-width: 100%;
// background-color: #fafafa;
// border-right: 1px solid #eee;
// }
// .pages-content-container {
// margin-right: 6em;
// }
.selects {
display: flex;
// margin-right: 6em;
}
.all-selects {
display: flex;
width: 96%;
}
.select-teams {
display: flex;
align-items: center;
// width: 13em;
margin-top: 1em;
}
.teams-label {
// min-width: 3em;
margin-top: 0.5em;
position: relative;
top: 1.4em;
font-size: 0.8em;
right: 1em;
background-color: white;
z-index: 99;
}
.teams-label-Grouping {
// min-width: 5em;
width: 47%;
margin-top: 0.5em;
position: relative;
bottom: 1.3em;
font-size: 0.8em;
right: 6em;
background-color: white;
z-index: 99;
}
.card-header,
.card {
border-radius: 0.25em !important;
}
.card-body {
border-bottom: 1px rgb(223 223 223) solid;
font-size: 0.9rem;
}
.component {
padding-left: 5em !important;
z-index: 99;
}
.form-control {
width: 6em;
}
</style>