add files

This commit is contained in:
Mehdi104797 2025-04-21 11:49:42 +03:30
commit 0e61ff5f8d
26 changed files with 7630 additions and 0 deletions

7
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,7 @@
{
"workbench.colorCustomizations": {
"activityBar.background": "#452236",
"titleBar.activeBackground": "#61304C",
"titleBar.activeForeground": "#FCF9FB"
}
}

34
apis/taskApi.js Normal file
View File

@ -0,0 +1,34 @@
export default {
workingHours: {
list: "general/get",
load: "general/load",
add: "general/add",
taskAdd: "details/add",
taskEdit: "details/edit",
day: "details/page/day",
delete: "details/delete",
deleteHours: "general/delete/time",
complitionAll: "details/complition/title",
complition: "details/complition/title/{{query}}",
copy: "general/copy",
move: "general/move",
},
taskChart: {
donut: "chart/getCategory",
},
taskTeams: {
getList: "group/list",
delete: "group/delete ",
edit: "group/edit ",
add:"group/add",
members:"perm/list",
addMembers:"perm/add",
deleteMembers:"perm/delete",
editMembers:"perm/edit",
},
taskReport: {
getList: "general/getList",
groupList:"group/list",
sortgroup:"general/load/group",
},
};

View File

@ -0,0 +1,568 @@
.main-page__content {
// height: calc(100dvh - 68px);
height: calc(100% - 70px);
min-height: calc(100% - 70px);
margin-right: var(--sidebar-collapsed-width);
display: flex;
.menu-bar__content {
position: static !important;
flex: 1 1 100%;
max-width: 30em;
// max-width: 20em;
width: auto;
// min-width: 40em;
&.show-list-panel {
right: 0 !important;
}
.date-picker {
.vpd-input-group {
.vpd-icon-btn {
height: 1.8em;
}
// .form-control {
// width: 18em;
// }
}
}
.alert {
display: flex;
border-radius: 0.25em !important;
margin-bottom: 0.5em !important;
&-secondary {
margin-top: 1em;
}
&-light {
}
}
.card {
border-radius: 0.25em !important;
display: flex;
&-header {
display: flex;
}
&-body {
display: flex;
justify-content: space-between;
}
}
.btn:hover {
text-decoration: none;
}
.year-dropdown {
margin-right: 1em;
}
.mounth-dropdown {
margin-right: 1em;
}
.group-item {
.group-row {
.group-picture-container {
.group-picture.mini-mode {
display: none;
}
.group-picture {
display: inline;
}
}
}
}
.groups-header {
.left-icons {
color: #bdc6d0;
}
}
}
#accordionExample {
// max-height: 51em !important;
max-height: calc(100dvh - 13em)!important;
overflow: auto !important;
}
.rotate-icon {
transform-origin: center center;
transition: transform 0.3s ease; /* افکت دوران با سرعت 0.3 ثانیه */
}
.rotate-icon.rotated {
transform: rotate(-90deg);
}
.main-page {
&_header {
display: flex;
// margin-top: 1em;
}
&_total {
border: 1px rgb(223 223 223) solid;
border-radius: 0.25em !important;
font-size: 1.2rem;
padding: 1em;
&-1 {
display: flex;
justify-content: space-between;
&_a {
color: var(--color-items-4);
}
&_b {
color: var(--color-items-5);
}
}
&-2 {
display: flex;
justify-content: space-between;
&_a {
color: var(--color-items-4);
}
&_b {
color: var(--color-items-3);
}
}
}
&_date {
border-radius: 0.25em !important;
// padding: 1em;
&-daysweek {
font-size: 1.3rem;
color: var(--color-items-5);
margin-bottom: 2em;
}
&-icon-left {
display: flex;
align-items: flex-end;
justify-content: center;
}
&-icon-right {
display: flex;
align-items: flex-end;
justify-content: center;
}
#typeNumber {
font-size: 1rem;
color: var(--color-items-5);
}
.form-control {
border-radius: 0.5em !important;
height: calc(2em + 0.35rem + 2px) !important;
padding: 0.5em;
width: 6em;
&.multiselect {
border: 1px solid var(--primary-color);
width: 14em;
}
}
}
&_body {
// border: 1px rgb(223 223 223) solid;
height: 30em;
margin-top: 1em;
border-radius: 0.25em;
// &-form-outline{
// max-height: 4em !important;
// overflow: auto !important;
// }
// .tasks_form {
// min-height: 18em;
// height: auto;
// overflow-y: auto;
// max-height: calc(100vh - 30em);
// }
&-items {
height: 3.5em;
&-move-copy {
display: flex;
flex-direction: row;
justify-content: center;
.date-picker {
input {
display: none !important;
}
svg {
display: none !important;
}
.vpd-icon-btn {
padding: unset !important ;
background-color: unset !important;
margin-bottom: unset !important;
}
// :hover {
// color: #6acfef;
// transition-duration: 0.3s;
// }
}
}
.task-form-control {
border: unset;
border-radius: unset !important;
&.Description {
border-left: unset !important;
text-align: center;
direction: rtl;
}
&.task-title {
text-align: center;
direction: rtl;
}
}
// &.record{
// cursor: pointer;
// }
&-icon {
// font-size:2rem;
color: var(--color-items-3);
display: flex;
justify-content: flex-end;
&-record {
color: var(--color-items-6);
justify-content: center;
display: flex;
font-size: 1.5rem;
height: 2.5em;
}
&-close {
color: unset !important;
}
&-add {
color: var(--color-items-2);
}
}
&-text-record {
color: var(--color-items-6);
}
&-first {
background-color: #f7fafe;
border: 1px rgb(223 223 223) solid;
}
.label-0 {
display: flex;
border-left: 1px var(--color-items-7) solid;
align-items: center;
height: 2em;
justify-content: center;
}
.label-1 {
display: flex;
align-items: center;
height: 2em;
justify-content: center;
}
.label-2 {
display: flex;
height: 2em;
border-left: 1px var(--color-items-7) solid;
border-right: 1px var(--color-items-7) solid;
align-items: center;
justify-content: center;
z-index: 9;
}
.label-3 {
display: flex;
height: 2em;
justify-content: center;
border-left: 1px var(--color-items-7) solid !important;
border-right: 1px var(--color-items-7) solid;
align-items: center;
}
.label-Description {
display: flex;
height: 2em;
justify-content: center;
align-items: center;
}
.label-4 {
display: flex;
height: 2em;
justify-content: space-between;
align-items: center;
padding-left: 0 !important;
}
&.labels {
background-color: #f1f1f1;
font-size: 1.3rem;
margin-left: 0 !important;
}
&.items {
color: var(--color-items-4);
&-bottom {
border-top: unset;
}
&-top {
border-bottom: unset;
}
}
&.total {
background-color: #f1f1f1;
font-size: 1.5rem;
}
.total-all-text {
color: var(--color-items-4);
}
.total-all-number {
color: black;
font-weight: bold;
font-size: 2rem;
}
.indecisive {
color: var(--color-items-4);
&-number {
color: var(--color-items-3);
font-weight: bold;
font-size: 2rem;
}
}
}
&-icon {
font-size: 2rem;
color: var(--color-items-2);
display: flex;
justify-content: flex-end;
&-close {
font-size: 2rem;
color: var(--color-items-3);
display: flex;
justify-content: flex-end;
}
&-add {
font-size: 1.5rem;
}
}
.items-form-control {
width: 6em;
margin-right: 1em;
border-radius: 0.5em !important;
height: calc(2.2em + 0.35rem + 2px) !important;
margin-right: 0.2em;
}
.form-label {
margin-top: 0.5em;
color: var(--color-items-4);
margin-left: 8em;
}
}
.alert {
display: flex;
border: 1px rgb(223 223 223) solid;
border-radius: 0.25em !important;
margin-bottom: 0.5em !important;
&-secondary {
}
&-light {
}
}
}
.coolinput {
display: flex;
flex-direction: column;
width: fit-content;
position: relative;
max-width: 240px;
}
.coolinput label.text {
font-size: 0.75rem;
color: var(--color-items-4);
font-weight: 700;
position: absolute;
top: 0.5rem;
margin: 0 0 0 3em;
padding: 0 3px;
background: #fff;
width: auto !important;
top: -0.8em;
right: 2em;
white-space: nowrap;
padding: 0 0.5em;
}
.coolinput input[type="text"].input {
padding: 11px 10px;
font-size: 1rem;
border: 1px #eee solid;
border-radius: 5px;
background: #fff;
}
.coolinput input[type="text"].input:focus {
outline: none;
}
input {
text-align: center;
direction: ltr;
}
.multiselect {
input {
text-align: right !important ;
direction: ltr;
}
&-lable {
width: 5em !important;
color: var(--color-items-5);
background-color: #fff;
z-index: 99;
margin: 0 0 0 10em !important;
font-size: 0.58rem;
}
}
.task-Form-Organ {
border-left: 1px var(--color-items-7) solid !important;
}
// .tasks_form{
// max-height: 20em;
// overflow: auto;
// }
// .right-section-mobile{
// // position: fixed;
// // z-index: 99;
// // top: 0;
// // right: -20em;
// // height:80% ;
// // -webkit-box-shadow: -5px 0 6px 0 #666;
// // box-shadow: -5px 0 6px 0 #666;
// // width: 30em;
// // background-color: #fff;
// }
.btn-copy {
font-size: 1rem;
width: 14em;
height: 35px;
display: flex;
align-items: center;
justify-content: flex-start;
border: none;
border-radius: 4px;
overflow: hidden;
box-shadow: 2px 2px 10px rgba(0, 0, 0, 0.164);
cursor: pointer;
}
.btn-move {
font-size: 1rem;
width: 14em;
height: 35px;
display: flex;
align-items: center;
justify-content: flex-start;
border: none;
border-radius: 4px;
overflow: hidden;
box-shadow: 2px 2px 10px rgba(0, 0, 0, 0.164);
cursor: pointer;
}
.text-move {
width: 90%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
// color: white;
background-color: rgb(2, 153, 153);
}
.text-copy {
width: 90%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
// color: white;
background-color: #196f93;
}
.svgIcon-move {
width: 20%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background-color: teal;
color: white;
}
.svgIcon-copy {
width: 20%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background-color: #004761;
color: white;
}
.btn-move:hover .text-move {
background-color: rgb(0, 133, 133);
transition-duration: 0.3s;
}
.btn-copy:hover .text-copy {
background-color: #03688d;
transition-duration: 0.3s;
}
.btn-move:hover .svgIcon-move {
background-color: rgb(0, 105, 105);
transition-duration: 0.3s;
}
.btn-copy:hover .svgIcon-copy {
background-color: #015370;
transition-duration: 0.3s;
}
}
.card-header {
padding: 0.65rem 0.35rem !important;
&_titles {
&-icon {
font-size: 0.6rem;
}
&.text-1 {
color: var(--color-items-1);
padding-right: unset !important;
padding-left: unset !important;
}
&.text-2 {
color: var(--color-items-1);
// margin-right: 4em;
// margin-left: 4em;
padding-right: unset !important;
padding-left: unset !important;
}
&.text-3 {
color: var(--color-items-2);
padding-right: unset !important;
padding-left: unset !important;
}
&.text-4 {
color: var(--color-items-3);
// margin-right: 4em;
padding-right: unset !important;
padding-left: unset !important;
// margin-left: 4em;
}
&.text-5 {
color: var(--color-items-3);
// margin-left: 4em;
}
&.tavasi {
font-size: 1.5rem;
// margin-right: auto;
}
}
&_items {
display: flex;
justify-content: space-between;
&.item-1 {
}
}
}

View File

@ -0,0 +1,267 @@
@media (max-width: 575.98px) {
.form-outline2 {
padding-right: unset !important;
padding-left: unset !important;
max-width: 4em !important;
}
.main-page_header {
margin-top: 0 !important;
flex-direction: column-reverse !important;
}
.main-page_body-items-move-copy {
display: flex;
flex-direction: column;
align-items: center;
}
.main-page_date .form-control {
border-radius: 0.5em !important;
height: calc(2em + 0.35rem + 2px) !important;
padding: 0.5em;
width: 4em;
}
.breadcrumbs{
display: none;
}
}
// @media only screen and (min-width: 576px) and (max-width: 767.98px) {
// .main-page_header {
// max-height: 10em;
// }
// }
@media only screen and (min-width: 768px) and (max-width: 991.98px) {
}
@media only screen and (min-width: 992px) and (max-width: 1049.98px) {
}
@media only screen and (min-width: 1050px) and (max-width: 1399.99px) {
}
@media (min-width: 1400px) {
}
@media (max-width: 1400px) {
.menu-bar__content {
display: none;
}
// .multiselect-lable{
// display: none;
// }
}
@media (min-width: 992px) {
}
@media (max-width: 992px) {
.menu-bar__content {
display: none;
}
}
@media (max-width: 768px) {
.hiddens {
display: none !important;
}
.main-page_total {
font-size: 1rem !important;
height: 6em;
}
.icon {
width: 1em !important;
}
.main-page_body .form-label {
margin-right: 0.5em !important;
}
.main-page_header {
flex-direction: column-reverse !important;
}
.items-form-control {
width: 5em !important;
}
.total-all-number,
.indecisive-number {
font-size: 1rem !important;
font-weight: bold;
}
.hours-form {
height: 5em !important;
border-radius: 0.5em;
}
.main-page_body-items.labels {
display: none !important;
}
.main-page_body-items .label-1 {
border-right: 1px var(--color-items-7) solid !important;
}
.main-page_body-items .label-3 {
}
.task-form-control {
border-left: unset !important;
}
// .coolinput input[type="text"].input {
// font-size: 0.75rem !important;
// }
.coolinput {
padding-right: unset !important;
padding-left: unset !important;
}
.row {
&-basic {
display: none !important;
}
}
.form-label {
margin-top: 0.5em;
color: var(--color-items-4);
margin-left: 2em !important;
}
.main-page_date-daysweek {
font-size: 1.5rem !important;
}
.icon-Component-358--1 {
width: 1.5em !important;
}
.icon-Component-359--1 {
width: 1.5em !important;
}
.coolinput label.text {
margin: 0 0 0 2em;
}
.tribute-container.mini {
width: 18em;
}
.multiselect__single {
font-size: 1rem !important;
font-weight: bold !important;
}
.form-outline {
&.day {
margin-left: 0.5em;
}
&.year {
margin-right: 2.5em;
}
}
.multiselect {
&.template {
border: 3px solid rgb(127, 170, 170);
padding: 0.7em !important;
width: 15em;
}
&-lable {
margin: 0 0 0 10em !important;
}
}
.multiselect__content-wrapper {
z-index: 99;
left: 0;
}
.main-page_body-items.total {
font-size: 1rem !important;
font-weight: bold;
}
.main-page_body-items-move-copy.in-desktop {
display: none !important;
}
.btn-copy {
width: 4em !important;
}
.btn-move {
width: 4em !important;
}
.svgIcon-move {
width: 0% !important;
}
.svgIcon-copy {
width: 0% !important;
}
.text-move {
width: 100% !important;
}
.text-copy {
width: 100% !important;
}
.prevDay,
.nextDay {
display: none !important;
}
.main-page_date {
display: flex !important;
flex-direction: column !important;
}
.form-control.multiselect {
width: 26em !important;
}
.task-dashboard {
.my-card {
&.Gantt {
margin-top: 1.5em !important;
}
}
}
.pages-content-container {
margin-right: 0 !important;
padding-right: 0 !important;
padding-left: 0 !important;
}
.pages-content {
margin-top: 0 !important;
margin-left: 0 !important;
margin-right: 0.8em !important;
}
.selects {
margin-right: 0 !important;
}
.jahat-pagination {
display: flex;
flex-direction: column !important;
}
.table-responsive {
max-height: calc(100dvh + -18.5em) !important ;
}
.page-border-top {
justify-content: flex-start !important;
}
.all-selects {
flex-direction: column !important;
}
.grouping,
.form-sorting_user {
display: none !important;
}
// .tasks_form{
// max-height: 20em;
// overflow: auto;
// }
// .main-page_date{
// padding-right: unset !important;
// padding-left: unset !important;
// }
// .main-page_header{
// padding-right: unset !important;
// padding-left: unset !important;
// }
}
@media (min-width: 768px) {
.hiddens {
display: inline-block !important;
}
.row {
&-mobile {
display: none !important;
}
}
.task-navbar {
display: none;
}
.main-page_body-items-move-copy.in-mobile {
display: none !important;
}
}
// .tasks_form{
// max-height: 28em;
// overflow: auto;
// }
// .avatar{
// display: none !important
// }

View File

@ -0,0 +1,60 @@
.multiselect__tags {
border: unset !important;
}
.multiselect__content-wrapper {
z-index: 99;
// right: -0.6em;
// width: 24em;
// max-height: 30em !important;
}
// .multiselect__select{
// left: 1.5em !important;
// }
// <!-- #region multiselect styles -->
// <style lang="scss">
.multiselect__option--selected.multiselect__option--highlight:after {
content: "\e940";
font-family: "tavasi" !important;
speak: never;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
color: #fff !important;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-size: 1.2rem;
}
.multiselect__option--highlight:after {
content: "\e994";
font-family: "tavasi" !important;
speak: never;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
color: #fff !important;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.multiselect__option--selected:after {
content: "\e994";
font-family: "tavasi" !important;
speak: never;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
color: green !important;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #aaa;
background: inherit;
}
// </style>
// <!-- #endregion -->

View File

@ -0,0 +1,7 @@
@import "./components/_variabels";
.task-system {
@import "./components/mian";
@import "./components/responsive";
@import "./components/multiselect";
}

View File

@ -0,0 +1,668 @@
<template>
<div
class="modal fade share-modal__modal"
id="share-modal"
tabindex="-1"
aria-labelledby="exampleModalLabel"
aria-hidden="true"
>
<div class="modal-dialog">
<div class="modal-content popUp-tab share-modal">
<div class="popUp-tab__content tab-content share-modal__content">
<div class="share-modal__search-content">
<div class="share-modal__content">
<div class="share-modal__search-top p-3">
<!-- <i class="tavasi tavasi-Component-360--58"
><span class="path1"></span><span class="path2"></span
></i> -->
<span class="text__gray me-1"> انتخاب عضو </span>
</div>
<div class="share-modal__search">
<input
type="text"
placeholder="جستجوی عضو جدید..."
id="searchForName"
v-model="query"
@keyup="showSearchResult"
/>
</div>
<div
id="user-search-recommend"
class="share-modal__search-recommend"
:class="{ show: suggestionMode }"
>
<div class="search-recommend">
<div
class="search-users__item"
v-for="(user, i) in userSuggestions"
:key="'h' + i"
>
<div class="search-users__pic">
<img :src="userAvatar(user)" alt="" />
</div>
<div class="search-users__content">
<div class="search-users__name">
{{ user.full_name }}
</div>
<div class="search-users__id">@{{ user.username }}</div>
</div>
<div class="search-users__type">
<div class="order-select main-page__date-select">
<select
@change="addUser($event, user)"
class="form-control d-block"
>
<option selected disabled :value="undefined">
دسترسی
</option>
<option
v-for="role in roles"
:key="role.id"
:value="role.id"
>
{{ role.title }}
</option>
</select>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import HttpService from "@services/httpService";
export default {
name: "sharemodal",
props: ["item", "itemType"],
mounted() {
this.httpService = new HttpService(this.loginMicroServiceName);
},
data() {
return {
httpService: undefined,
newItemType: 0,
suggestionMode: false,
query: "",
userSuggestions: [],
userToAdd: [],
roles: [
{ title: "عادی",id:1},
{ title: "مشاهده",id:10},
{ title: "سرگروه",id:500},
{ title: "مالک",id:1000 },
],
};
},
methods: {
// formData(url, data = []) {
// const formData = new FormData();
// for (const [key, value] of Object.entries(data)) {
// if (value !== undefined && value !== null) formData.append(key, value);
// }
// return axios.post(url, formData);
// },
showSearchResult() {
var vm = this;
if (this.query.length > 2) {
this.httpService
.postRequest("user/suggestion", { query: this.query })
.then((response) => {
var items = response.data;
vm.userSuggestions = items;
vm.suggestionMode = true;
});
} else {
vm.userSuggestions = [];
vm.suggestionMode = false;
}
},
addUser(ev, item) {
this.userToAdd.push(item);
this.suggestionMode = false;
this.$emit("update-member", {
user_id: item.user_id,
perm_type: +ev.target.value,
});
// this.addPerm(ev, item);
},
addPerm(ev, item) {
// var guid = this.item.guid;
// this.userToAdd.forEach(function (item) {
// ApiService.post(this, "perm/set", {
// pid: guid,
// act: "add",
// read: 1,
// write: 0,
// username: item.username,
// });
// });
},
},
};
</script>
<style lang="scss">
@import "../../../assets/permit/scss/permit.scss";
</style>
<style scoped lang="scss">
.share-modal {
height: 20em;
background-color: #fff;
top: 7em;
left: 2em;
}
.share-modal__search-top {
background-color: #eee;
padding-bottom: 1em;
font-size: 15px;
font-weight: 500;
}
.share-modal__search-recommend {
opacity: 1;
display: block;
position: static;
visibility: visible;
height: calc(100% - 12.5em);
overflow: auto;
display: none;
opacity: unset;
visibility: hidden;
top: 117px;
right: 0px;
width: 100%;
background: white;
-webkit-transition: all 0.3s ease-in-out;
transition: all 0.3s ease-in-out;
opacity: 0;
visibility: hidden;
display: none;
z-index: 10;
&.show {
position: static !important;
opacity: 1 !important;
display: block !important;
visibility: visible !important;
}
}
.share-modal {
width: 30em !important;
&__content {
width: 30em !important;
}
}
@media screen and (max-width: 35.938em) {
.share-modal {
width: 23em !important;
&__content {
width: 23em !important;
}
}
}
// &__modal {
// .modal-dialog {
// transform: unset !important;
// transition: unset !important;
// }
// }
// &__content {
// position: relative;
// width: 337px;
// height: 385px;
// display: flex;
// flex-direction: column;
// }
// &__search-content {
// position: absolute;
// top: 0;
// left: 0;
// width: 100%;
// height: 100%;
// transition: all 0.3s ease-in-out;
// }
// &__search {
// border-bottom: 1px solid #f1f1f1;
// display: flex;
// align-items: center;
// padding-top: 15px;
// padding-bottom: 15px;
// span {
// font-size: 16px;
// // color: var(--color-2);
// width: 50px;
// display: flex;
// justify-content: center;
// i {
// cursor: pointer;
// color: #92a2b2;
// font-size: 24px;
// width: 70px;
// display: flex;
// justify-content: center;
// align-items: center;
// }
// }
// #get-back {
// width: 70px;
// }
// input {
// flex-grow: 2;
// border: unset;
// outline: unset;
// // color: var(--color-2);
// font-size: 14px;
// &::placeholder {
// color: #92a2b2;
// font-size: 14px;
// }
// }
// }
// &__add-person {
// width: 50px;
// display: flex;
// justify-content: center;
// i {
// font-size: 24px;
// -webkit-transition: all 0.5s ease-in-out;
// transition: all 0.5s ease-in-out;
// font-size: 23px;
// background: -webkit-linear-gradient(#a9bdc4 0%, #c6cfda 100%);
// background-clip: border-box;
// -webkit-background-clip: text;
// -webkit-text-fill-color: transparent;
// }
// &:hover {
// i {
// background: -webkit-linear-gradient(#00b6e3 0%, #81e6ff 100%);
// background-clip: border-box;
// -webkit-background-clip: text;
// -webkit-text-fill-color: transparent;
// }
// }
// }
// &__search-recommend {
// display: none;
// opacity: unset;
// visibility: hidden;
// position: absolute;
// top: 57px;
// right: 0px;
// width: 100%;
// height: calc(100% - 57px);
// background: white;
// transition: all 0.3s ease-in-out;
// opacity: 0;
// visibility: hidden;
// display: none;
// z-index: 10;
// .os-host {
// height: 100%;
// }
// .os-host-resize-disabled.os-host-scrollbar-horizontal-hidden > .os-scrollbar-vertical {
// bottom: 0;
// right: 7px;
// top: 17px;
// width: 0px;
// }
// .os-theme-dark > .os-scrollbar > .os-scrollbar-track > .os-scrollbar-handle {
// background: rgba(0, 0, 0, 0.16);
// }
// }
// &__users {
// flex-grow: 1;
// max-height: 259px;
// .os-scrollbar-vertical {
// width: 0;
// }
// }
// &__icon {
// display: flex;
// justify-content: center;
// align-items: center;
// margin-bottom: 18px;
// img {
// width: 142px;
// padding-top: 42px;
// }
// }
// &__text {
// color: #92a2b2;
// font-size: 14px;
// padding-bottom: 37px;
// width: 246px;
// text-align: center;
// margin-right: auto;
// margin-left: auto;
// }
// &__search-content-footer {
// display: flex;
// align-items: flex-end;
// justify-content: space-between;
// padding-top: 15px;
// padding-bottom: 15px;
// border-top: 1px solid #bac4ce;
// padding-right: 22px;
// padding-left: 22px;
// }
// &__sign {
// display: flex;
// flex-direction: column;
// span {
// font-size: 12px;
// color: #92a2b2;
// display: block;
// margin-bottom: 2px;
// }
// a {
// // color: var(--color-1);
// font-size: 12px;
// }
// }
// &__settings {
// a {
// }
// }
// &__settings-btn {
// font-size: 14px;
// // color: var(--color-1);
// }
// &__settings-content {
// position: absolute;
// top: 0;
// left: 0;
// width: 100%;
// height: 100%;
// transition: all 0.3s ease-in-out;
// opacity: 0;
// visibility: hidden;
// display: none;
// }
// &__title {
// padding-top: 15px;
// padding-bottom: 15px;
// border-bottom: 1px solid #f1f1f1;
// padding-right: 22px;
// padding-left: 22px;
// h4 {
// font-size: 16px;
// }
// }
// &__form {
// padding-right: 22px;
// padding-left: 22px;
// &-question {
// font-size: 14px;
// font-weight: 500;
// text-shadow: 0 -0.75px #6b6b6b;
// margin-bottom: 17px;
// color: #444444;
// }
// &-item {
// padding-top: 17px;
// &:last-child {
// margin-bottom: 17px;
// }
// }
// &-radio-item {
// display: flex;
// align-items: center;
// &:not(:last-child) {
// margin-bottom: 20px;
// }
// input {
// }
// label {
// color: #444444;
// font-size: 14px;
// margin-right: 16px;
// margin-bottom: 0;
// }
// }
// &-btn {
// min-width: 57px;
// height: 32px;
// border-radius: 20px;
// display: flex;
// justify-content: center;
// align-items: center;
// margin-right: 10px;
// }
// &-radio {
// position: relative;
// input {
// position: absolute;
// opacity: 0;
// cursor: pointer;
// height: 20px;
// width: 20px;
// z-index: 5;
// }
// span {
// display: flex;
// height: 20px;
// width: 20px;
// border-radius: 50%;
// border: 2px solid #bac4ce;
// transition: all 0.3s ease-in-out;
// display: flex;
// justify-content: center;
// align-items: center;
// &::before {
// content: " ";
// width: 10px;
// height: 10px;
// border-radius: 50%;
// display: flex;
// background: linear-gradient(#3def90 0%, #1ed975 100%);
// opacity: 0;
// transition: all 0.3s ease-in-out;
// }
// }
// input:checked ~ span::before {
// opacity: 1;
// }
// }
// }
// &__form-btn--deactive {
// font-size: 14px;
// // color: var(--color-2);
// &:hover {
// opacity: 0.8;
// }
// }
// &__from-btn--active {
// // border: 2px solid var(--color-1);
// // color: var(--color-1);
// font-size: 14px;
// &:hover {
// color: white;
// // background: var(--color-1);
// }
// }
// &__setting-footer {
// margin-top: auto;
// border-top: 1px solid #f1f1f1;
// display: flex;
// align-items: center;
// justify-content: flex-end;
// padding: 16px;
// }
// &__people-search {
// background: #fff;
// position: absolute;
// top: 0;
// display: none;
// left: 0;
// width: 100%;
// height: 100%;
// transition: all 0.3s ease-in-out;
// opacity: 0;
// visibility: hidden;
// .os-host {
// height: 100%;
// }
// .os-host-resize-disabled.os-host-scrollbar-horizontal-hidden > .os-scrollbar-vertical {
// bottom: 0;
// right: 7px;
// top: 17px;
// width: 0px;
// }
// .os-theme-dark > .os-scrollbar > .os-scrollbar-track > .os-scrollbar-handle {
// background: rgba(0, 0, 0, 0.16);
// }
// }
// &__user-search-content {
// opacity: unset;
// width: 100%;
// height: calc(100% - 57px);
// background: white;
// transition: all 0.3s ease-in-out;
// top: 57px;
// display: none;
// .os-host {
// height: 100%;
// }
// .os-host-resize-disabled.os-host-scrollbar-horizontal-hidden > .os-scrollbar-vertical {
// bottom: 0;
// right: 7px;
// top: 17px;
// width: 0px;
// }
// .os-theme-dark > .os-scrollbar > .os-scrollbar-track > .os-scrollbar-handle {
// background: rgba(0, 0, 0, 0.16);
// }
// }
// &__users-footer {
// display: flex;
// flex-direction: column;
// }
// &__textarea {
// border-top: 1px solid #f1f1f1;
// padding-top: 12px;
// padding-bottom: 12px;
// padding-right: 22px;
// padding-left: 22px;
// }
// &__invite-row {
// border-top: 1px solid #f1f1f1;
// display: flex;
// align-items: center;
// padding-top: 17.5px;
// padding-bottom: 26px;
// justify-content: space-between;
// padding-right: 22px;
// padding-left: 22px;
// }
// &__select {
// font-size: 14px;
// .main-page__date-select .select-selected {
// border: unset;
// padding: 0;
// }
// .main-page__date-select .select-selected {
// height: 32px;
// }
// .select-items {
// width: 160px;
// }
// .main-page__date-select .select-items {
// top: unset;
// bottom: calc(5px + 100%);
// }
// }
// &__send-invite {
// a {
// display: flex;
// justify-content: center;
// align-items: center;
// min-width: 120px;
// border-radius: 20px;
// // border: 2px solid var(--color-1);
// height: 32px;
// font-size: 14px;
// &:hover {
// color: white;
// // background: var(--color-1);
// }
// }
// }
// }
</style>

270
components/HoursForm.vue Normal file
View File

@ -0,0 +1,270 @@
<template>
<form class="hours-form mb-1">
<div class="row-hours">
<div class="form-group col coolinput">
<label for="input" class="form-label text"> ورود :</label>
<input
name="input"
v-model="hourswork.inTime"
v-on:blur="inTimeCorrect(hourswork.inTime, $event)"
v-on:focus="timeFocus('intime', hourswork.inTime)"
placeholder="00:00"
ref="intime"
type="text"
maxlength="5"
class="form-control items-form-control input"
pattern="/^\d{1,2}:\d{1,2}$/gm"
required="required"
:class="{ invalid: invalidInTime }"
/>
</div>
<div class="form-group col coolinput">
<label for="input" class="form-label text">خروج :</label>
<input
name="input"
v-model="hourswork.outTime"
v-on:blur="outTimeCorrect(hourswork.outTime, $event)"
v-on:focus="timeFocus('outtime', hourswork.outTime)"
placeholder="00:00"
ref="outtime"
type="text"
maxlength="5"
required="required"
pattern="/^\d{1,2}:\d{1,2}$/gm"
class="form-control items-form-control input"
:class="{ invalid: invalidOutTime }"
/>
</div>
<div class="form-group col coolinput">
<label for="input" class="form-label text">غیرمفید :</label>
<input
name="input"
v-model="hourswork.noWorkTime"
v-on:blur="noWorkTimeCorrect(hourswork.noWorkTime)"
v-on:focus="timeFocus('noworktime', hourswork.noWorkTime)"
placeholder="00:00"
ref="noworktime"
type="text"
class="form-control items-form-control input"
@keydown.tab="saveHoursWork()"
/>
</div>
<div class="form-group col-auto d-flex me-auto align-items-center">
<button-component
v-if="hoursFormsLenght <= 1"
@click="deleteMainTime()"
buttonText=""
v-tooltip="'حذف زمان کاری'"
>
<span class="text-danger">
<svg class="icon icon-Component-295--1">
<use xlink:href="#icon-Component-295--1"></use>
</svg>
</span>
</button-component>
<span
@click="saveHoursWork()"
title=" ثبت"
class="btn main-page_body-icon"
>
<svg class="icon icon-Component-233--1 main-page_body-icon-add">
<use xlink:href="#icon-Component-233--1"></use>
</svg>
</span>
<span
v-if="!itemIndex && hoursFormsLenght < 3"
title=" اضافه کردن"
class="btn main-page_body-icon pe-0 ps-0"
>
<svg
title="اضافه کردن"
@click="openFormHoursInformation()"
class="icon icon-Component-133--1"
>
<use xlink:href="#icon-Component-133--1"></use>
</svg>
</span>
<span
v-else
title="حذف"
@click="deleteFormHoursInformation(hourswork)"
class="btn main-page_body-icon-close pe-0 ps-0"
>
<svg class="icon icon-Component-21--1">
<use xlink:href="#icon-Component-21--1"></use>
</svg>
</span>
</div>
</div>
</form>
</template>
<script>
export default {
props: {
itemIndex: {
default: 0,
},
hoursFormsLenght: {
default: 0,
},
hourswork: {
default() {
return {
inTime: "",
outTime: "",
noWorkTime: "",
};
},
},
},
emits: ["delete-form-hours", "save-form-hours", "add-new-record"],
data() {
return {
invalidInTime: false,
invalidOutTime: false,
};
},
computed: {
// ...mapGetters(["currentUser"]),
// ...mapGetters("list", ["listGetter"]),
},
methods: {
timeFocus(ref, value) {
if (!value) value = "00:00";
this.$refs[ref].select();
},
inTimeCorrect(value) {
if (!value) value = "00:00";
let res = this.timeCorrect(value);
if (res) this.hourswork.inTime = res;
},
outTimeCorrect(value) {
if (!value) value = "00:00";
let res = this.timeCorrect(value);
if (res) this.hourswork.outTime = res;
},
noWorkTimeCorrect(value) {
if (!value) value = "00:00";
let res = this.timeCorrect(value);
if (res) this.hourswork.noWorkTime = res;
},
timeCorrect(value) {
// if (value.charCodeAt()) {
// }
if (value == null) return value;
value = value.trim();
value = value.replace(".", ":");
value = value.replace(" ", ":");
if (value.indexOf(":") == -1) {
if (value.length == 0) value = "00:00";
else if (value.length == 1) value = "0" + value + ":00";
else if (value.length == 2) value = value + ":00";
else if (value.length == 3)
value = "0" + value.substring(0, 1) + ":" + value.substring(1);
else if (value.length == 4)
value =
value.substring(0, 2) + ":" + value.substring(2).padStart(2, "0");
else if (value.length > 4)
value = value.substring(0, 2) + ":" + value.substring(3, 5);
}
return value;
},
deleteMainTime() {
this.mySwalConfirm({
title: "هشدار!!!",
html: "تمامی کارهای امروز پاک خواهد. آیا مطمئن هستید؟ ",
}).then((result) => {
if (result.isConfirmed) {
this.$emit("delete-form-hours");
}
});
},
deleteFormHoursInformation() {
this.$emit("delete-form-hours");
},
saveHoursWork() {
const inTimeIsInvalid1 = this.hourswork.inTime == null;
const inTimeIsInvalid2 = this.hourswork.inTime?.indexOf("00:00") > -1;
// const inTimeIsInvalid3 = this.hourswork.inTime?.indexOf("0:00") > -1;
const outTimeIsInvalid1 = this.hourswork.outTime == null;
const outTimeIsInvalid2 = this.hourswork.outTime?.indexOf("00:00") > -1;
// const outTimeIsInvalid3 = this.hourswork.outTime?.indexOf("0:00") > -1;
if (
inTimeIsInvalid1 ||
inTimeIsInvalid2 ||
// inTimeIsInvalid3 ||
outTimeIsInvalid1 ||
outTimeIsInvalid2
// || outTimeIsInvalid3
) {
this.invalidInTime = inTimeIsInvalid1 || inTimeIsInvalid2;
this.invalidOutTime = outTimeIsInvalid1 || outTimeIsInvalid2;
this.mySwalToast({
icon: "error",
title: "ورودی نامعتبر",
});
} else {
this.invalidInTime = false;
this.invalidOutTime = false;
this.noWorkTimeCorrect(this.hourswork.noWorkTime);
this.$emit("save-form-hours");
}
},
openFormHoursInformation() {
const inTimeIsInvalid =
this.hourswork.inTime == null || this.hourswork.inTime == "00:00";
const outTimeIsInvalid =
this.hourswork.outTime == null || this.hourswork.outTime == "00:00";
if (inTimeIsInvalid || outTimeIsInvalid) {
this.invalidInTime = inTimeIsInvalid;
this.invalidOutTime = outTimeIsInvalid;
this.mySwalToast({
icon: "error",
title: "ورودی نامعتبر",
});
} else {
this.invalidInTime = false;
this.invalidOutTime = false;
this.$emit("add-new-record");
}
},
},
};
</script>
<style lang="scss" scoped>
.hours-form {
border: 1px rgb(223, 223, 223) solid;
height: 5em;
border-radius: 0.5em;
}
.form-group {
display: flex;
align-items: center;
}
.row-hours {
display: flex;
align-items: center;
justify-content: flex-start;
}
.form-control {
&.invalid {
border: 1px solid red !important;
}
}
</style>

863
components/MainSection.vue Normal file
View File

@ -0,0 +1,863 @@
<template>
<div class="main-page container-fluid">
<div class="row d-flex justify-content-center">
<div class="col-12 main-page_header d-flex">
<!-- <SelectTemplate class="col-sm-12 col-md-2 task-Form-category">
</SelectTemplate> -->
<div class="col-12 main-page_date mt-3 d-flex align-items-center">
<div class="flex-grow-1">
<h4 class="main-page_date-daysweek m-0">{{ weekDay }}</h4>
<div class="d-flex justify-content-center">
<div class="row d-flex justify-content-center">
<div class="main-page_date-icon-right col-auto px-1">
<span class="d-flex btn" title="روز بعد" @click="nextDay()">
<svg class="icon icon-Component-358--1">
<use xlink:href="#icon-Component-358--1"></use>
</svg>
<span class="nextDay">بعدی</span>
</span>
</div>
<div class="form-outline day col-auto px-1 coolinput">
<label class="form-label text" for="typeNumber">روز:</label>
<input
name="input"
v-model.number="workDay"
min="0"
max="32"
placeholder="--"
type="number"
id="typeNumber"
class="main-page_date form-control input"
/>
</div>
<div class="form-outline form-outline2 col-auto px-1 coolinput">
<label class="form-label text" for="typeNumber">ماه:</label>
<input
v-model.number="workMonth"
min="01"
max="12"
placeholder="--"
type="number"
id="typeNumber"
class="main-page_date form-control input"
/>
</div>
<div class="form-outline year col-auto px-1 coolinput">
<label class="form-label text" for="typeNumber">سال:</label>
<input
min="1400"
v-model.number="workYear"
placeholder="14--"
type="number"
id="typeNumber"
class="main-page_date form-control input"
/>
</div>
<div class="main-page_date-icon-left col-auto px-1">
<span class="d-flex btn" title="روز قبل" @click="prevDay()">
<span class="prevDay">قبلی</span>
<svg class="icon icon-Component3601">
<use xlink:href="#icon-Component3601"></use>
</svg>
</span>
</div>
</div>
</div>
</div>
<div
v-can="'times_user_search'"
class="coolinput select-template d-flex align-items-center mb-2 mb-md-0 ms-lg-2"
>
<label class="multiselect-lable text no-wrap width-auto" for="users"
>جستجوی کاربر:</label
>
<multiselect
class="multiselect template form-control input w-auto"
id="users"
track-by="user_id"
placeholder="جستجوی..."
v-model="selectedUser"
:show-labels="false"
:options="foundUsers"
:searchable="true"
:preserve-search="true"
:internal-search="false"
:clearOnSelect="false"
:clear-on-select="false"
:close-on-select="true"
:options-limit="300"
:limit="3"
:limit-text="limitText"
:max-height="350"
:customLabel="
(item) => {
return `${item.first_name} ${item.last_name}`;
}
"
@search-change="asyncFind"
@select="addUserToGroup"
@close="resetFoundUsers"
>
</multiselect>
</div>
<!-- <div class="coolinput select-template d-flex align-items-center">
<label class="multiselect-lable text" for="input">نوع قالب</label>
<multiselect
class="multiselect template form-control input w-auto"
v-model="templateValue"
:options="multiSelectOptions"
:max-height="250"
@select="setTagTemplate"
label="name"
track-by="title"
></multiselect>
</div> -->
</div>
</div>
</div>
<div class="main-page_body">
<div class="main-page_body-form-outline">
<hours-form
v-for="(hourswork, index) in hoursForms"
:key="index"
:itemIndex="index"
:hourswork="hourswork"
:hoursFormsLenght="hoursForms.length"
@add-new-record="openFormHoursInformation(index)"
@delete-form-hours="deleteFormHoursInformation(index)"
@save-form-hours="saveHoursWork(hourswork, index)"
></hours-form>
</div>
<div class="overlay3 overlay3-entrance1 3-entrance2 3-entrance3">
<div class="main-page_body-items labels d-flex align-items-center">
<label
v-if="templateValue.title == 'multi-org_workbase'"
class="col-1 label-0"
style="background-color: #97f295 !important"
>سازمان</label
>
<label class="col-2 label-1">*دسته</label>
<label class="col-3 label-2">*عنوان</label>
<label class="col-1 label-3">*مدت</label>
<label class="col-4 label-Description">چالش،پیشنهاد،توضیح</label>
</div>
</div>
<div class="tasks_form">
<task-form
v-for="(taskForm, index) in taskForms"
:key="index"
:hoursFormsLenght="index"
:taskForm="taskForm"
:itemIndex="index"
:templateValue="templateValue"
@add-new-record="openFormTaskInformation()"
@close-form-tasks="closeFormTaskInformation(taskForm.id, index)"
@save-form-tasks="postInTaskWork"
></task-form>
</div>
<div
class="main-page_body-items record d-flex justify-content-center align-items-center col-12"
>
<span
title="رکورد جدید"
@click="openFormTaskInformation()"
class="btn main-page_body-items-icon-record align-items-center"
>
<svg class="icon icon-Component-133--1">
<use xlink:href="#icon-Component-133--1"></use>
</svg>
<span class="main-page_body-items-text-record">رکورد جدید</span>
</span>
</div>
<div class="main-page_body-items total d-flex align-items-center col-12">
<label class="col total-all-text"
>جمع کل : <span class="total-all-number ms-3">{{ sum }}</span></label
>
<label class="col indecisive"
>بلاتکلیف : <span class="indecisive-number">{{ noWork }}</span></label
>
<div class="main-page_body-items-move-copy in-desktop mt-2">
<div class="btn-move move ms-3">
<date-picker
title="انتقال"
label="انتقال محتوا به تاریخ دیگر"
class="date-picker text-move"
v-model="dateValueMove"
format="YYYY-MM-DD"
display-format="jYYYY-jMM-jDD"
@input="PostDateToMoveInformation()"
/>
<span class="svgIcon-move" title="انتقال">
<svg class="icon icon-forward">
<use xlink:href="#icon-forward"></use>
</svg>
</span>
</div>
<div class="copy btn-copy">
<date-picker
title="کپی"
label="کپی محتوا به تاریخ دیگر"
class="date-picker text-copy"
v-model="dateValueCopy"
format="YYYY-MM-DD"
display-format="jYYYY-jMM-jDD"
@input="PostDateToCopyInformation()"
/>
<span class="svgIcon-copy" title="کپی">
<svg class="icon icon-copy2">
<use xlink:href="#icon-copy2"></use>
</svg>
</span>
</div>
</div>
<div class="main-page_body-items-move-copy in-mobile mt-2">
<div class="btn-move move ms-3">
<date-picker
title="انتقال"
label="انتقال"
class="date-picker text-move"
v-model="dateValueMove"
format="YYYY-MM-DD"
display-format="jYYYY-jMM-jDD"
@input="PostDateToMoveInformation()"
/>
<span class="svgIcon-move" title="انتقال">
<svg class="icon icon-forward">
<use xlink:href="#icon-forward"></use>
</svg>
</span>
</div>
<div class="copy btn-copy">
<date-picker
title="کپی"
label="کپی"
class="date-picker text-copy"
v-model="dateValueCopy"
format="YYYY-MM-DD"
display-format="jYYYY-jMM-jDD"
@input="PostDateToCopyInformation()"
/>
<span class="svgIcon-copy" title="کپی">
<svg class="icon icon-copy2">
<use xlink:href="#icon-copy2"></use>
</svg>
</span>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
// import ImageUploader from "vue-image-upload-resize";
// import keyValueApi from "@apis/keyValueApi";
// import { mapActions, mapGetters, mapMutations } from "vuex";
import taskApi from "@apis/taskApi";
import HttpService from "@services/httpService";
import VuePersianDatetimePicker from "vue-persian-datetime-picker";
import { p2e } from "@plugins/persianNumber";
import { mapGetters, mapMutations } from "vuex";
export default {
beforeMount() {
this.httpService = new HttpService();
},
mounted() {
this.setCurrentUserData();
var d = new Date().toLocaleDateString("fa-IR");
let items = d.split("/");
let year = p2e(items[0]);
let month = p2e(items[1]);
let day = p2e(items[2]);
let weekDay = new Date().toLocaleDateString("fa-IR", { weekday: "short" });
this.setDate(day, month, year, weekDay);
this.getDetailsDay();
if(this.taskSchemaGetter?.organTypeOptions){
this.multiSelectOptions = this.taskSchemaGetter?.organTypeOptions;
this.templateValue = this.taskSchemaGetter?.organTypeOptions[0];
}
},
data() {
return {
foundUsers: [],
selectedUser: {
id: undefined,
user_id: undefined,
first_name: undefined,
last_name: undefined,
username: undefined,
full_name: undefined,
avatar: undefined,
color: undefined,
},
multiSelectOptions: [],
templateValue: {
name: "تک سازمانی - کارمحور",
title: "single-org_workbase",
},
hoursForms: [
{
inTime: "",
outTime: "",
noWorkTime: "",
},
],
taskForms: [
{
organ: "",
category: "",
title: "",
duration: "",
description: "",
},
],
inTime: "",
outTime: "",
noWorkTime: "",
workDay: "",
workMonth: "",
workYear: "",
weekDay: "",
// useFullTime: "",
// allTime: "",
taskId: "",
noWork: "",
sum: "",
itemsId: "",
loading: false,
hoursformCount: 1,
isMultiOrgan: false,
dateValueCopy: "",
dateValueMove: "",
};
},
computed: {
...mapGetters(["currentUser", "taskSchemaGetter"]),
},
methods: {
setCurrentUserData() {
this.selectedUser = {
id: this.currentUser.user_id,
user_id: this.currentUser.user_id,
first_name: this.currentUser.user_data.first_name,
last_name: this.currentUser.user_data.last_name,
username: this.currentUser.user_data.username,
full_name: null,
avatar: this.currentUser.user_data.username,
color: null,
};
},
limitText(count) {
return `و ${count} کاربر دیگر`;
},
asyncFind(query) {
const url = this.loginMicroServiceName + "/user/suggestion";
if (query.trim().length) {
this.httpService.postRequest(url, { query }).then((response) => {
this.foundUsers = response.data ?? [];
});
}
},
addUserToGroup(selectedUser) {
this.$root.$emit("request-based-on-new-user-data", selectedUser.id);
},
resetFoundUsers() {
// this.foundUsers = [];
// this.fetchingData = false;
},
getDetailsDay() {
this.hoursForms = [];
let payload = {
user_id: this.selectedUser.user_id,
};
const url = this.taskMicroServiceName + taskApi.workingHours.day;
if (this.workDay) {
payload.taskDate =
this.workYear + "-" + this.workMonth + "-" + this.workDay;
}
this.httpService.postRequest(url, payload).then((res) => {
if (res.hourswork?.length) {
this.hoursForms = res.hourswork;
this.itemsId = res.task_id;
} else
this.hoursForms.push({
inTime: null,
outTime: null,
noWorkTime: null,
});
this.taskForms = res.data;
this.taskForms.push({
organ: "",
category: "",
title: "",
duration: "",
description: "",
});
this.noWork = res.day_sum.noWork;
this.sum = res.day_sum.sum;
let { day, month, year, weekDay } = res;
this.setDate(day, month, year, weekDay);
});
},
setDate(day, month, year, weekDay) {
this.weekDay = weekDay;
this.workDay = day;
this.workMonth = month;
this.workYear = year;
},
// openFormHoursInformation(index) {
// if (
// this.hoursForms[index].inTime !== null &&
// this.hoursForms[index].outTime !== null &&
// this.hoursForms[index].noWorkTime !== null
// ) {
// this.hoursForms.push({
// inTime: "",
// outTime: "",
// noWorkTime: "",
// });
// this.saveHoursWork();
// }
// },
openFormHoursInformation(index) {
this.hoursformCount++;
this.hoursForms.push({
inTime: "",
outTime: "",
noWorkTime: "",
});
},
deleteFormHoursInformation(index) {
this.hoursformCount--;
const url = this.taskMicroServiceName + taskApi.workingHours.deleteHours;
let payload = { id: this.itemsId, index: this.hoursForms[index].index ?? (index + 1) };
if (
this.hoursForms[index].inTime != "" ||
this.hoursForms[index].outTime != ""
) {
this.mySwalConfirm({
title: "هشدار!!!",
html: "از حذف این مورد مطمئن هستید؟",
}).then((result) => {
if (result.isConfirmed) {
this.httpService.postRequest(url, payload).then((response) => {
this.getDetailsDay();
this.mySwalToast({
title: "تبریک",
html: "مورد با موفقیت حذف شد",
icon: "success",
});
});
}
});
} else {
this.hoursForms.splice(index, 1);
}
},
openFormTaskInformation() {
// if (this.itemsId == "") {
// this.saveHoursWork(this.hoursForms[0], 0, false);
// }
this.taskForms.push({
organ: "",
category: "",
title: "",
duration: "",
description: "",
});
},
closeFormTaskInformation(taskFormsId, index) {
const url = this.taskMicroServiceName + taskApi.workingHours.delete;
const payload = { id: taskFormsId };
if (taskFormsId) {
this.mySwalConfirm({
title: "هشدار!!!",
html: "از حذف این مورد مطمئن هستید؟",
}).then((result) => {
if (result.isConfirmed) {
this.httpService.postRequest(url, payload).then((response) => {
this.getDetailsDay();
this.mySwalToast({
title: "تبریک",
html: "مورد با موفقیت حذف شد",
icon: "success",
});
});
}
});
} else if (
this.taskForms[index].organ != "" ||
this.taskForms[index].category != "" ||
this.taskForms[index].duration != "" ||
this.taskForms[index].title != ""
) {
this.mySwalConfirm({
title: "هشدار!!!",
html: "از حذف این مورد مطمئن هستید؟",
}).then((result) => {
if (result.isConfirmed) {
this.taskForms.splice(index, 1);
this.mySwalToast({
title: "تبریک",
html: "مورد با موفقیت حذف شد",
icon: "success",
});
}
});
} else {
this.taskForms.splice(index, 1);
}
},
validTime(value) {
const regex = /^\d{1,2}:\d{1,2}$/gm;
let isValid = value.match(regex);
return isValid;
},
timeCorrect(value) {
if (value == null) return value;
value = value.trim();
value = value.replace(".", ":");
value = value.replace(" ", ":");
if (value.indexOf(":") == -1) {
if (value.length == 0) value = "00:00";
else if (value.length == 1) value = "0" + value + ":00";
else if (value.length == 2) value = value + ":00";
else if (value.length == 3)
value = "0" + value.substring(0, 1) + ":" + value.substring(1);
else if (value.length == 4)
value =
value.substring(0, 2) + ":" + value.substring(2).padStart(2, "0");
else if (value.length > 4)
value = value.substring(0, 2) + ":" + value.substring(2, 2);
}
return value;
},
saveHoursWork(hourswork, index, refreshPage = true) {
if (!this.validTime(hourswork.inTime)) {
if (!hourswork.inTime) hourswork.inTime = "00:00";
hourswork.inTime = timeCorrect(hourswork.inTime);
}
if (!this.validTime(hourswork.outTime)) {
if (!hourswork.outTime) hourswork.outTime = "00:00";
hourswork.outTime = timeCorrect(hourswork.outTime);
}
if (!this.validTime(hourswork.noWorkTime)) {
if (!hourswork.noWorkTime) hourswork.noWorkTime = "00:00";
hourswork.noWorkTime = timeCorrect(hourswork.noWorkTime);
}
const payload = {
taskDate: this.workYear + "-" + this.workMonth + "-" + this.workDay,
inTime: hourswork.inTime ?? "00:00",
outTime: hourswork.outTime ?? "00:00",
noWorkTime: hourswork.noWorkTime ?? "00:00",
index: index + 1,
};
const url = this.taskMicroServiceName + taskApi.workingHours.add;
this.httpService
.postRequest(url, payload)
.then((res) => {
this.itemsId = res.id;
// let { day, month, year, weekDay } = res;
// this.setDate(day, month, year, weekDay);
this.mySwalToast({
title: res.message,
});
})
.finally(() => {
// if (refreshPage) this.getWorkingHours(false);
this.getDetailsDay();
});
},
postInTaskWork(taskForm) {
if (this.loading) return;
this.loading = true;
let payload = {
id: taskForm.id ?? undefined,
task_id: this.itemsId,
category: taskForm.category,
organ: taskForm.organ,
title: taskForm.title,
duration: taskForm.duration,
description: taskForm.description,
};
let url = taskForm.id
? taskApi.workingHours.taskEdit
: taskApi.workingHours.taskAdd;
url = this.taskMicroServiceName + url;
this.httpService
.postRequest(url, payload)
.then((res) => {
this.mySwalToast({
title: res.message,
});
this.getDetailsDay();
})
.finally(() => {
this.loading = false;
});
},
setWorkDay(value) {
this.workDay = 26;
},
nextDay(nextDay) {
this.workDay += 1;
this.getDetailsDay();
},
prevDay(prevDay) {
if (this.workDay > 1) this.workDay -= 1;
else {
if (this.workMonth == 1) {
this.workMonth = 12;
this.workYear -= 1;
} else {
this.workMonth -= 1;
}
if (this.workMonth == 12) this.workDay = 29;
else if (this.workMonth > 6) this.workDay = 30;
//if(this.workMonth < 6)
else this.workDay = 31;
}
this.getDetailsDay();
},
setCurrentDate(date) {
this.workDay = date.day;
this.workMonth = date.month;
this.workYear = date.year;
this.getDetailsDay();
},
setTagTemplate(newTag) {
this.templateValue = newTag;
},
datefa(item) {
var m = item;
var d = new Date(m).toLocaleDateString("fa-IR");
return d;
},
PostDateToMoveInformation() {
let oldDateMove =
this.workYear + "/" + this.workMonth + "/" + this.workDay;
let newDateMove = this.datefa(this.dateValueMove);
let payload = {
user_id: this.$store.getters.currentUser.user_id,
oldDate: oldDateMove,
newDate: newDateMove,
id: this.itemsId,
};
let payloadReplace = {
user_id: this.$store.getters.currentUser.user_id,
oldDate: oldDateMove,
newDate: newDateMove,
id: this.itemsId,
replace: 1,
append: 0,
};
let payloadAppend = {
user_id: this.$store.getters.currentUser.user_id,
oldDate: oldDateMove,
newDate: newDateMove,
id: this.itemsId,
replace: 0,
append: 1,
};
const url = this.taskMicroServiceName + taskApi.workingHours.move;
this.mySwalConfirm({
title: "هشدار!!!",
html:
"آیا از انتقال موارد از تاریخ:" +
" " +
"«" +
oldDateMove +
"»" +
" " +
"به تاریخ:" +
" " +
"«" +
newDateMove +
"»" +
" " +
"مطمئن هستید؟",
}).then((result) => {
if (result.isConfirmed) {
this.httpService.postRequest(url, payload).then((response) => {
// console.log(response);
if (response?.check_replace == 1) {
this.mySwalConfirm({
title: "هشدار!!!",
html: "برای این تاریخ موارد مشابه وجود دارد آیا مایل به اضافه یا جایگزینی هستید؟",
showDenyButton: true,
showCancelButton: true,
confirmButtonText: "جایگزین",
denyButtonText: `اضافه`,
}).then((result) => {
if (result.isConfirmed) {
this.httpService
.postRequest(url, payloadReplace)
.then((response) => {});
this.mySwalToast({
title: "تبریک",
html: "محتوا با موفقیت جایگزین شد",
icon: "success",
});
this.getDetailsDay();
} else if (result.isDenied) {
this.httpService
.postRequest(url, payloadAppend)
.then((response) => {});
this.mySwalToast({
title: "تبریک",
html: "محتوا با موفقیت اضافه شد",
icon: "success",
});
this.getDetailsDay();
}
});
} else {
this.mySwalToast({
title: "تبریک",
html: "محتوا با موفقیت منتقل شد",
icon: "success",
});
this.getDetailsDay();
}
});
}
});
},
PostDateToCopyInformation() {
let oldDateCopy =
this.workYear + "/" + this.workMonth + "/" + this.workDay;
let newDateCopy = this.datefa(this.dateValueCopy);
let payload = {
user_id: this.$store.getters.currentUser.user_id,
oldDate: oldDateCopy,
newDate: newDateCopy,
id: this.itemsId,
};
let payloadReplace = {
user_id: this.$store.getters.currentUser.user_id,
oldDate: oldDateCopy,
newDate: newDateCopy,
id: this.itemsId,
replace: 1,
append: 0,
};
let payloadAppend = {
user_id: this.$store.getters.currentUser.user_id,
oldDate: oldDateCopy,
newDate: newDateCopy,
id: this.itemsId,
replace: 0,
append: 1,
};
const url = this.taskMicroServiceName + taskApi.workingHours.copy;
this.mySwalConfirm({
title: "هشدار!!!",
html:
"آیا از کپی موارد از تاریخ:" +
" " +
"«" +
oldDateCopy +
"»" +
" " +
"به تاریخ:" +
" " +
"«" +
newDateCopy +
"»" +
" " +
"مطمئن هستید؟",
}).then((result) => {
if (result.isConfirmed) {
this.httpService.postRequest(url, payload).then((response) => {
if (response?.check_replace == 1) {
this.mySwalConfirm({
title: "هشدار!!!",
html: "برای این تاریخ موارد مشابه وجود دارد آیا مایل به اضافه یا جایگزینی هستید؟",
confirmButtonText: "جایگزین",
denyButtonText: `اضافه`,
}).then((result) => {
if (result.isConfirmed) {
this.httpService
.postRequest(url, payloadReplace)
.then((response) => {});
this.mySwalToast({
title: "تبریک",
html: "محتوا با موفقیت جایگزین شد",
icon: "success",
});
this.getDetailsDay();
} else if (result.isDenied) {
this.httpService
.postRequest(url, payloadAppend)
.then((response) => {});
this.mySwalToast({
title: "تبریک",
html: "محتوا با موفقیت اضافه شد",
icon: "success",
});
this.getDetailsDay();
}
});
} else {
this.mySwalToast({
title: "تبریک",
html: "موارد با موفقیت کپی شد",
icon: "success",
});
this.getDetailsDay();
}
});
}
});
},
},
components: {
TaskForm: () => import("@task/components/TaskForm"),
HoursForm: () => import("@task/components/HoursForm"),
Multiselect: () => import("vue-multiselect"),
datePicker: VuePersianDatetimePicker,
},
};
</script>
<style lang="scss"></style>

View File

@ -0,0 +1,450 @@
<template>
<div class="user-search-list">
<form class="search-filter" role="search" @submit.prevent="sendQuery">
<div class="mb-3">
<div class="input-group">
<div class="input-group-append">
<span class="tavasi tavasi-Component-198--1"></span>
</div>
<input
ref="search-input"
dir="rtl"
v-model.trim="searchText"
type="search"
required
class="form-control"
id="search-query"
placeholder="جستجو..."
name="search-query"
aria-label="جستجو در اسناد، عناوین و واژگان"
aria-describedby="basic-addon1"
size="50"
@keyup="sendQuery()"
@keydown="onKeyDown()"
/>
<button
v-if="searchText.length"
@click="resetFormAndList"
type="button"
class="btn clear-search p-0"
>
<svg class="icon icon-Component-294--1">
<use xlink:href="#icon-Component-294--1"></use>
</svg>
</button>
</div>
</div>
</form>
<div class="d-flex justify-content-between alert alert-secondary">
<div class="col-6">نام و نام خانوادگی</div>
<div class="col-4 d-flex justify-content-center">دسترسی</div>
<div class="col-2 me-3">حذف</div>
</div>
<div class="user-list">
<div
v-for="list in listMember"
class="d-flex justify-content-between alert alert-light col-12"
>
<div class="col-6">
{{ list.description }}
</div>
<div class="order-select main-page__date-select col-4">
<select @change="addUser( $event,list)" class="form-control d-block">
<option selected disabled :value="undefined">
{{ list.perm }}
</option>
<option v-for="role in roles" :key="role.id" :value="role.id">
{{ role.title }}
</option>
</select>
</div>
<div class="col-2 d-flex">
<!-- <span class="btn" title="ویرایش" @click="editMember(list)">
<svg class="icon edit-member icon-Component-242--1">
<use xlink:href="#icon-Component-242--1"></use>
</svg>
</span> -->
<span class="btn me-3" title="حذف" @click="deleteMember(list)">
<svg class="icon delete-member icon-Component-295--1">
<use xlink:href="#icon-Component-295--1"></use>
</svg>
</span>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
item: "",
parentLoading: false,
searchResults: {
default() {
return [];
},
},
listMember: {
default: null,
},
},
watch: {
searchResults(newValu) {
this.users = newValu;
},
},
mounted() {
this.users = this.searchResults;
},
data() {
return {
roles: [
{ title: "عادی", id: 1 },
{ title: "مشاهده", id: 10 },
{ title: "سرگروه", id: 500 },
{ title: "مالک", id: 1000 },
],
users: [],
typingTimer: 0,
doneTypingInterval: 1000,
searchText: "",
};
},
methods: {
selectUserAsAdmin(user) {
this.$emit("set-as-admin", user);
},
getGroupAvatar(user = undefined) {
try {
if (user) {
if (isValidHttpUrl(user.avatar)) return user.avatar;
else {
if ("name" in user) {
const nameArray = user.name?.split(" ");
if (nameArray?.length > 1) {
const initials =
nameArray[0].charAt(0) + nameArray[1].charAt(0);
return this.generateAvatarFromChars(initials);
} else {
const initials = nameArray[0].charAt(0);
return this.generateAvatarFromChars(initials);
}
} else if ("lastname" in user) {
const nameArray = user.lastname?.split(" ");
if (nameArray?.length > 1) {
const initials =
nameArray[0].charAt(0) + nameArray[1].charAt(0);
return this.generateAvatarFromChars(initials);
} else {
const initials = nameArray[0].charAt(0);
return this.generateAvatarFromChars(initials);
}
}
if ("username" in user) {
const nameArray = user.username?.split(" ");
if (nameArray?.length > 1) {
const initials =
nameArray[0].charAt(0) + nameArray[1].charAt(0);
return this.generateAvatarFromChars(initials);
} else {
const initials = nameArray[0].charAt(0);
return this.generateAvatarFromChars(initials);
}
}
}
}
} catch (err) {
return require("@assets/common/img/default.svg");
}
},
sendQuery() {
clearTimeout(this.typingTimer);
this.typingTimer = setTimeout(() => {
this.search();
}, this.doneTypingInterval);
},
onKeyDown() {
clearTimeout(this.typingTimer);
},
search() {
this.$emit("on-send", this.searchText);
},
resetFormAndList() {
this.searchText = "";
this.users = [];
},
deleteMember(list) {
this.$emit("delete-member", list);
},
addUser(event, item) {
this.$emit("edit-member", {
user_id: item.user_id,
id: item.id,
perm_type: +event.target.value,
});
// this.addPerm(ev, item);
},
},
};
</script>
<style scoped lang="scss">
.delete-member {
font-size: 0.8rem;
color: rgb(189, 61, 87);
}
.alert {
margin-bottom: 0.5rem;
}
%unread-box {
display: flex;
align-items: center;
.btn {
padding: 0 0.2em;
margin: 0;
color: #888;
color: #888;
&:hover {
background-color: #ddd;
}
&:not(:last-child) {
margin-left: 0.2em;
}
svg {
font-size: 0.7rem;
}
}
}
.user-search-list {
// max-width: 25em;
.search-filter {
display: flex;
flex-direction: row-reverse;
// position: absolute;
z-index: 999;
// top: 24px;
// background-color: #fff;
width: 100%;
justify-content: center;
// background-color: red;
.input-group {
border-radius: 1.6em;
border: 1px solid #eee;
}
.input-group-text {
// display: flex;
// align-items: center;
// justify-content: center;
font-size: 0.8rem;
// border: 1px solid #ddd;
}
.input-group-append {
.tavasi {
display: flex;
align-items: center;
padding: 0.4em;
}
.input-group-text {
// border-top-right-radius: 0.5em;
// border-bottom-right-radius: 0.5em;
// background-color: transparent;
}
}
.input-group-prepend {
position: relative;
.clear-search {
position: absolute;
right: -3em;
top: 0;
bottom: 0;
margin: auto;
display: flex;
align-items: center;
}
.input-group-text {
// background-color: transparent;
// border-top-left-radius: 0.5em;
// border-bottom-left-radius: 0.5em;
}
}
.form-control {
height: 2.5em;
border-color: transparent;
border-radius: 2.5em;
&::-webkit-search-decoration,
&::-webkit-search-cancel-button,
&::-webkit-search-results-button,
&::-webkit-search-results-decoration {
-webkit-appearance: none;
}
}
.close-search {
color: #7f8891;
}
}
.user-list {
height: calc(100dvh - 18em);
overflow-y: auto;
.group-item {
.group-row {
display: flex;
align-items: center;
width: 100%;
max-width: 100%;
margin-bottom: 0.3em;
flex: 1;
&.enable-hover:hover {
.group-picture-container {
// .context-menu-dropdown {
// display: flex;
// }
// .group-picture {
// display: none;
// }
}
.group-content {
background-color: #f0f0f0;
}
}
.group-picture-container {
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
// margin-left: 0.5em;
border: 1px solid #ccc;
width: 2.3em;
height: 2.3em;
position: relative;
background-color: #fff;
.context-menu-dropdown {
display: none;
}
.group-picture {
object-fit: cover;
object-position: center;
overflow: hidden;
border-radius: 50%;
width: 100%;
height: 100%;
}
}
.group-content {
flex: 1;
border-radius: 0;
position: relative;
padding: 0.3em;
&:active,
&.active {
background-color: #d8f8fd;
}
.group-title-container {
display: flex;
align-items: flex-start;
flex-direction: column;
label {
color: #6f6f6f;
}
}
.group-description-container {
display: flex;
align-items: center;
justify-content: space-between;
.group-unread-indicator {
font-size: 0.7rem;
background-color: #00b6e3;
font-family: "sahel-light";
display: flex;
justify-content: center;
align-items: center;
padding-top: 0.4em;
}
.group-description {
color: #6f6f6f;
font-size: 0.8em;
margin: 0;
flex: 1;
text-align: right;
// @include textOverflow(15em);
}
}
.unread-button {
.unread-message-label {
font-size: 0.7rem;
color: #6f6f6f;
margin-left: 0.3em;
line-height: 1;
}
display: flex;
align-items: center;
justify-content: flex-start;
margin-bottom: 0.2em;
font-size: 0.9rem;
line-height: 1.2;
}
}
&.lobbies {
padding-right: 1.5em;
&.has-indicator::after {
left: auto;
right: 0em;
}
}
}
.card-header {
display: flex;
align-items: center;
.group-description-container .group-description {
max-width: 11em !important;
}
}
.unreads-box {
@extend %unread-box;
}
&.unReads:hover {
cursor: pointer;
}
&:hover {
.unreads-box {
box-shadow: 0px 0px 5px 2px #ebebeb;
border-radius: 1em;
}
}
}
}
}
</style>

146
components/Navbar.vue Normal file
View File

@ -0,0 +1,146 @@
<template>
<nav
class="navbar navbar-expand-md fixed-top"
:class="{ expanded: !isSidebarCollapsed }"
>
<div class="d-flex align-items-center">
<!-- <div class="d-md-none dropdown-hamburger">
<router-link
:to="{ name: 'defaultRoute' }"
title="سامانه ها"
class="btn ps-0"
>
<svg class="icon icon-Home-21 main-page_body-items-icon-close">
<use xlink:href="#icon-Home-21"></use>
</svg>
</router-link>
</div> -->
<div class="d-md-none">
<button
name="button"
type="button"
class="toggle-mobile-nav dropdown-hamburger"
@click.prevent="toggleSidebarMenu()"
>
<span class="sr-only">باز کردن منوی کنار</span>
<svg class="s18" data-testid="sidebar-icon">
<use href="@assets/common/img/icons.svg#sidebar"></use>
</svg>
</button>
</div>
<!-- <div v-if="$route.name === 'taskTimes'" class="d-md-none dropdown-hamburger">
<router-link :to="{ name: 'taskDashboard' }" title="داشبورد" class="btn ps-0">
<svg class="icon icon-projects main-page_body-items-icon-close">
<use xlink:href="#icon-projects"></use>
</svg>
</router-link>
</div> -->
<!-- <div v-if="$route.name === 'taskDashboard'" class="d-md-none dropdown-hamburger pt-2"> -->
<!-- <router-link :to="{ name: 'taskTimes' }" title="وظایف" class="btn ps-0">
<svg class="icon icon-task-list main-page_body-items-icon-close">
<use xlink:href="#icon-task-list"></use>
</svg>
</router-link> -->
<!-- </div> -->
</div>
<ul class="navbar-nav ms-md-3 me-auto">
<!-- <li class="nav-item">
<notification></notification>
</li> -->
<!-- <li v-if="!isMajlesBuild" class="nav-item">
<select-language-dropdown
:class="buildName"
toggleClass="dropdown-toggle"
></select-language-dropdown>
</li> -->
<li class="nav-item" v-if="!isMajlesBuild">
<user-avatar-dropdown
:class="buildName + '-navbar'"
class="position-static task-avatar"
></user-avatar-dropdown>
</li>
</ul>
</nav>
</template>
<script>
import { mapGetters, mapMutations, mapActions } from "vuex";
export default {
beforeMount() {
// clearBodyClass();
},
mounted() {
// this.setBodyClass("list-system");
},
destroyed() {
// clearBodyClass();
},
data() {
return {
// #region mehdi
statusPag: 0,
nomber: 0,
statusPagHedear: 1,
// #endregion
};
},
computed: {
...mapGetters(["getPanelStatus", "getRefreshForm", "isSidebarCollapsed"]),
navTitle() {
return process.env.VUE_APP_TITLE;
},
},
methods: {
searchStart(e) {
let text = e.target.elements.searchinput.value;
if (text == "") {
} else {
this.$router.push({
name: "searchResult",
query: { q: text },
});
}
},
toggleSidebarMenu() {
this.$store.commit("TOGGLE_SIDEBAR_MENU");
},
// ...mapActions(["setBodyClass"]),
},
components: {
Navbar: () =>
import(
"@dashboard/majles/components/Navbar"
),
// MySystem: () =>
// import(
// "@dashboard/default/pages/MySystem"
// ),
// Notification: () =>
// import( "@notifications/components/Notification.vue"),
},
};
</script>
<style scoped lang="scss">
.navbar {
// position:fixed!important;
// margin-right: var(--sidebar-collapsed-width);
border-bottom: none;
background-color: #fff;
}
.dropdown-toggle {
color: #fff;
margin-right: 1.5em;
}
.dropdown-toggle:hover {
color: #00b6e3 !important;
}
.nav-logo {
height: 50px;
}
.dropdown-menu {
left: auto;
}
</style>

400
components/RightSection.vue Normal file
View File

@ -0,0 +1,400 @@
<template>
<div class="menu-bar__content home-list p-3">
<div class="home-list-header">
<div class="d-flex justify-content-between">
<div class="home-list-header form-control w-30">
<span class="main-page_total-1_a"> خالص ماهانه :</span
><span class="main-page_total-1_b me-1">{{ useFullTime }}</span>
</div>
<div class="home-list-header form-control me-1 ms-1 w-30">
<span class="main-page_total-2_a"> کل ماهانه :</span
><span class="main-page_total-2_b me-1">{{ allTime }}</span>
</div>
<div class="home-list-header form-control w-30">
<span class="main-page_total-1_a"> میانگین روزانه:</span
><span class="main-page_total-1_b me-1">{{ dailyAve }}</span>
</div>
</div>
<div class="alert alert-secondary" role="alert">
<!-- <select
id="month-select"
class="form-select ms-1"
v-model="selectedMonth"
@change="getWorkingHoursInformation()"
>
<option v-for="month in months" :key="month" :value="month">
{{ getPersianMonthName(month) }}
</option>
</select> -->
<Multiselect
v-model="selectedMonth"
id="month-select"
:options="months"
:custom-label="getPersianMonthName"
placeholder="ماه را انتخاب کنید"
@input="getWorkingHoursInformation"
:searchable="false"
:close-on-select="true"
:show-labels="false"
class="ms-1"
/>
<div class="col-1 main-page_date-icon-left">
<span class="btn" title="ماه قبل">
<svg class="icon icon-Component3601">
<use xlink:href="#icon-Component3601"></use>
</svg>
</span>
</div>
<!-- <select
id="year-select"
class="form-select"
v-model="selectedYear"
@change="getWorkingHoursInformation()"
>
<option v-for="year in years" :key="year" :value="year">
{{ year }}
</option>
</select> -->
<Multiselect
v-model="selectedYear"
:options="years"
placeholder="سال را انتخاب کنید"
@input="getWorkingHoursInformation"
:searchable="false"
:close-on-select="true"
:show-labels="false"
class=""
/>
</div>
</div>
<div class="accordion" id="accordionExample">
<div class="card">
<div class="card-header col-12">
<!-- <h2 class="mb-0"> -->
<button
class="btn d-flex justify-content-between collapsed w-100 pe-0"
type="button"
data-bs-toggle="collapse"
>
<span class="card-header_titles text-1 col-3">روز </span>
<span class="card-header_titles text-2 col-3"> تاریخ </span>
<span class="card-header_titles text-3 col-3">
<!-- <span class="card-header_titles-icon">
<svg class="icon icon-arrow-down">
<use xlink:href="#icon-arrow-down"></use>
</svg>
</span> -->
جمع کل</span
>
<span class="card-header_titles text-4 col-3">
<!-- <span class="card-header_titles-icon">
<svg class="icon icon-arrow-up-1">
<use xlink:href="#icon-arrow-up-1"></use>
</svg>
</span> -->
بلاتکلیف</span
>
<span class="card-header_titles text-5"></span>
<i
class="card-header_titles rotate-icon tavasi tavasi-Component-22--1"
></i>
<!-- </div> -->
</button>
<!-- </h2> -->
</div>
</div>
</div>
<div class="accordion" id="accordionExample">
<div class="card" v-for="items in list" :key="items.id">
<div class="card-header col-12" :id="'heading' + items.id">
<!-- <h2 class="mb-0"> -->
<button
class="btn d-flex justify-content-between collapsed w-100 pe-0"
type="button"
data-bs-toggle="collapse"
:data-bs-target="'#collapse' + items.id"
:aria-controls="'collapse' + items.id"
@click.stop="selectDate(items)"
:aria-expanded="isOpenAccordion(items.id)"
>
<!-- <div class="card-header_titles"> -->
<span class="card-header_titles text-1 col-3"
>{{ getDayOfWeekFromTimestamp(items.taskDate) }}
</span>
<span class="card-header_titles text-2 col-3">{{
items.oldDate
}}</span>
<span class="card-header_titles text-3 col-3">
<!-- <span class="card-header_titles-icon">
<svg class="icon icon-arrow-down">
<use xlink:href="#icon-arrow-down"></use>
</svg>
</span> -->
{{ items.minuteDay }}</span
>
<span class="card-header_titles text-4 col-3">
<!-- <span class="card-header_titles-icon">
<svg class="icon icon-arrow-up-1">
<use xlink:href="#icon-arrow-up-1"></use>
</svg>
</span> -->
{{ items.noWork }}</span
>
<span class="card-header_titles text-5">{{
items.noWorkTime
}}</span>
<i
:class="{ rotated: rotated }"
class="card-header_titles rotate-icon tavasi tavasi-Component-22--1"
></i>
<!-- </div> -->
</button>
<!-- </h2> -->
</div>
<div
v-if="isOpenAccordion(items.id)"
:id="'collapse' + items.id"
class="collapse show"
:aria-labelledby="'heading' + items.id"
data-parent="#accordionExample"
>
<div class="card-body">
<span class="card-body_items item-1">مدل سازی</span>
<span class="card-body_items item-2">اجرائیات</span>
<span class="card-body_items item-3">120</span>
<span class="card-body_items item-4"></span>
<span class="card-body_items item-5">{{ items.description }} </span>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import taskApi from "@apis/taskApi";
import { mapGetters } from "vuex";
import HttpService from "@services/httpService";
import VuePersianDatetimePicker from "vue-persian-datetime-picker";
import { p2e } from "@plugins/persianNumber";
import thiqatRoutes from "../../../routes/thiqatRoutes";
export default {
// mixins: [dragDropMoveMixin],
beforeMount() {
this.httpService = new HttpService(this.taskMicroServiceName);
this.activeUserId = this.currentUser.user_id;
// event fired from MainSection.vue.
this.$root.$on("request-based-on-new-user-data", (userId) => {
this.activeUserId = userId;
this.getWorkingHoursInformation();
});
},
mounted() {
var d = new Date().toLocaleDateString("fa-IR");
let items = d.split("/");
this.selectedYear = p2e(items[0]);
this.selectedMonth = p2e(items[1]);
this.getWorkingHoursInformation();
},
data() {
return {
months: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"],
years: ["1400", "1401", "1402", "1403", "1404", "1405"],
selectedMonth: "0",
selectedYear: "1402",
useFullTime: "",
allTime: "",
dailyAve: "",
entranceTime: [],
departureTime: [],
uselessTime: [],
list: [],
selectedAccordion: null,
rotated: false,
activeUserId: undefined,
};
},
computed: {
// ...mapGetters([
// "isSidebarCollapsed",
// "getForwardItem",
// "userPermisionGetter",
// "getPanelStatus",
// "sidebarListStatusGetter",
// ]),
...mapGetters("list", ["listGetter"]),
...mapGetters(["currentUser"]),
taskMicroServiceName() {
return process.env.VUE_APP_TASK;
},
},
methods: {
// ...mapMutations([
// "TOGGLE_PANEL",
// "checkPermissions",
// "SET_SIDEBAR_LIST_STATUS",
// ]),
// ...mapMutations("list", ["SET_LIST"]),
getWorkingHoursInformation() {
const payload = {
user_id: this.activeUserId,
offset: 0,
limit: 50,
sortby: "taskDate",
sortorder: "desc",
year: this.selectedYear,
month: this.selectedMonth,
};
// console.log(this.activeUserId)
// console.log(payload)
const url = taskApi.workingHours.load;
this.httpService
.postRequest(url, payload)
.then((res) => {
this.list = res.data;
this.useFullTime = res.useFullTime;
this.allTime = res.allTime;
this.dailyAve = res.dailyAve;
});
},
getDetailsMonth() {
this.hoursForms = [];
let payload = {};
const url = taskApi.workingHours.day;
if (this.workMonth) {
payload.taskDate =
this.workYear + "-" + this.workMonth + "-" + this.workDay;
}
this.httpService.postRequest(url, payload).then((res) => {
if (res.hourswork?.length) {
this.hoursForms = res.hourswork;
this.itemsId = res.task_id;
} else
this.hoursForms.push({
inTime: null,
outTime: null,
noWorkTime: null,
});
this.taskForms = res.data;
this.taskForms.push({
organ: "",
category: "",
title: "",
duration: "",
description: "",
});
this.noWork = res.day_sum.noWork;
this.sum = res.day_sum.sum;
let { day, month, year, weekDay } = res;
this.setDate(day, month, year, weekDay);
});
},
setDate(day, month, year, weekDay) {
this.weekDay = weekDay;
this.workDay = day;
this.workMonth = month;
this.workYear = year;
},
datefa(item) {
let m = item * 1000;
let d = new Date(m).toLocaleDateString("fa-IR");
return d;
},
getDayOfWeekFromTimestamp(timestamp) {
let m = timestamp * 1000;
let d = new Date(m);
let options = { weekday: "long" };
let dayOfWeek = new Intl.DateTimeFormat("fa-IR", options).format(d);
return dayOfWeek;
},
toggleAccordion(accordionId) {
if (this.selectedAccordion === accordionId) {
this.selectedAccordion = null;
} else {
this.selectedAccordion = accordionId;
}
this.rotateIcon();
},
isOpenAccordion(accordionId) {
return this.selectedAccordion === accordionId;
},
rotateIcon() {
this.rotated = !this.rotated;
},
nextMonth(nextMonth) {
this.workMonth += 1;
this.getDetailsDay();
},
prevMonth(prevMonth) {
this.workMonth -= 1;
this.getDetailsDay();
},
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);
}
},
getPersianMonthName(month) {
const monthNames = [
"فروردین",
"اردیبهشت",
"خرداد",
"تیر",
"مرداد",
"شهریور",
"مهر",
"آبان",
"آذر",
"دی",
"بهمن",
"اسفند",
];
return monthNames[month - 1];
},
},
components: {
datePicker: VuePersianDatetimePicker,
Multiselect: () => import("vue-multiselect"),
},
};
</script>
<style scoped lang="scss">
.home-list-header {
&.form-control {
padding: 0.175rem 0.35rem !important;
box-shadow: 0 0.4688rem 2.1875rem rgba(4, 9, 20, 0.03),
0 0.9375rem 1.4063rem rgba(4, 9, 20, 0.03),
0 0.25rem 0.5313rem rgba(4, 9, 20, 0.03),
0 0.125rem 0.1875rem rgba(4, 9, 20, 0.03);
border: unset !important;
border-radius: 0.25em;
font-size: 0.9rem;
}
}
</style>

View File

@ -0,0 +1,141 @@
<template>
<div class="form-sort">
<div class="container-fluid">
<div class="row">
<div class="" id="accordion">
<div class="card" v-for="(listItem, j) in workList" :key="j">
<div class="card-header" :id="'heading' + j">
<button
@click="getDetailsDay(listItem, j)"
class="btn d-flex justify-content-between collapsed w-100 pe-0"
type="button"
data-bs-toggle="collapse"
:data-bs-target="'#collapse' + j"
:aria-controls="'collapse' + j"
>
<span class="card-header_titles text-1 col-3"
>{{ getDayOfWeekFromTimestamp(listItem.taskDate) }}
</span>
<span class="card-header_titles text-2 col-3">{{
listItem.oldDate
}}</span>
<span class="card-header_titles text-3 col-3">
{{ listItem.minuteDay }}</span
>
<span class="card-header_titles text-4 col-2">
{{ listItem.noWork }}</span
>
<span class="card-header_titles col-1 text-5 rotate-icon">
<i
class="card-header_titles rotate-icon tavasi tavasi-Component-25--1"
></i
></span>
</button>
</div>
<div
:id="'collapse' + j"
class="collapse"
:class="{ show: 'collapse' + j == 'collapse0' }"
:aria-labelledby="'heading' + j"
data-parent="#accordion"
>
<div
v-for="(taskItem, k) in listItem.tasks"
:key="k"
class="card-body d-flex justify-content-between align-items-center"
>
<span class="card-body_items item-1"
>{{ taskItem.category }}
</span>
<span class="card-body_items item-2">{{ taskItem.title }}</span>
<span class="card-body_items item-3">{{
taskItem.duration
}}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import HttpService from "@services/httpService";
import taskApi from "@apis/taskApi";
export default {
props: {
workList: [],
userId: "",
},
beforeMount() {
this.httpService = new HttpService( this.taskMicroServiceName
);
},
computed: {
taskMicroServiceName() {
return process.env.VUE_APP_TASK;
},
},
methods: {
getDayOfWeekFromTimestamp(timestamp) {
let m = timestamp * 1000;
let d = new Date(m);
let options = { weekday: "long" };
let dayOfWeek = new Intl.DateTimeFormat("fa-IR", options).format(d);
return dayOfWeek;
},
getDetailsDay(hourItem) {
this.hoursForms = [];
let payload = {
user_id: this.userId,
taskDate: hourItem.oldDate,
};
const url = taskApi.workingHours.day;
this.httpService
.postRequest(url, payload)
.then((res) => {
this.$set(hourItem, "tasks", res.data);
// this.sortForms = res.data;
})
},
},
};
</script>
<style scoped lang="scss">
// .form-group {
// max-width: 44em !important;
// height: 4em;
// overflow: auto;
// border: 1px rgb(223 223 223) solid;
// border-radius: 0.25em;
// margin-top: 0;
// margin-bottom: 0;
// font-size: 0.8rem;
// }
#accordion {
width: 100% !important;
// min-width: 36em !important;
height: calc(100vh - 11em);
overflow: auto;
}
.card-header,
.card {
border-radius: 0.25em !important;
// min-width: 35em !important;
}
.table-responsive {
height: calc(100vh + -13em) !important;
}
.card-body {
border-bottom: 1px rgb(223 223 223) solid;
font-size: 0.9rem;
}
</style>

View File

@ -0,0 +1,104 @@
<template>
<div class="form-sort" :style="{ width: $attrs.width,}">
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div
class="row align-items-center justify-content-center style-label p-3"
>
<div v-for="item in columns" :class="item.class">
{{ item.label }}
</div>
</div>
</div>
</div>
</div>
<div class="container-fluid set-scroll firefox-scrollbar">
<div class="row">
<div v-for="item in listItem" class="col-12">
<div
class="row align-items-center justify-content-center my-border-bottom"
>
<div class="col-style" v-for="col in columns" :class="col.class">
{{ getValue(item, col.key) }}
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
listItem: [],
columns: {
default() {
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" },
];
},
},
},
methods : {
getValue(item, key){
if(item[key] && item[key] != "")
return item[key]
else
return "نامشخص"
}
},
};
</script>
<style scoped lang="scss">
.form-sort {
padding: 1em;
// width: 40em;
// border-radius: 0.25em;
// border: 1px rgb(223 223 223) solid;
box-shadow: -1px 2px 11px 3px #eee;
.set-scroll {
height: calc(100vh - 18em);
overflow-y: auto;
overflow-x: hidden;
.my-border-bottom {
position: relative;
&::before {
content:'';
display: block;
position: absolute;
bottom: 0;
left: 0;
right: 0;
width: 75%;
margin: auto;
border-bottom: 1px solid #eee;
}
.col-style {
display: flex;
align-items: center;
line-height: 3;
font-size: 0.9rem;
// height: 4em;
// border-bottom: 1px rgb(223 223 223) solid;
}
}
}
.style-label {
background-color: #eee;
}
}
</style>

493
components/TaskForm.vue Normal file
View File

@ -0,0 +1,493 @@
<template>
<form class="task-form">
<div class="row row-basic mx-0 justify-content-end">
<div
class="main-page_body-items items items-top d-flex align-items-center col-12"
>
<div
v-if="templateValue.title == 'multi-org_workbase'"
class="col-1 ps-0 pe-0 task-Form-Organ"
style="color: #97f295 !important"
>
<multiselect
v-model.number="taskForm.organ"
class="multiselect Organ"
tag-placeholder="Add this as new tag"
placeholder="سازمان"
:options="organOptions"
:taggable="true"
:close-on-select="true"
:clear-on-select="false"
:searchable="true"
:max-height="250"
:preserve-search="true"
@tag="addTagOrgan"
@select="organSelect"
></multiselect>
</div>
<div class="col-2 ps-0 pe-0 task-Form-category">
<multiselect
class="multiselect category"
id="task-Form-category"
v-model.number="taskForm.category"
tag-placeholder="Add this as new tag"
placeholder="دسته"
:options="categoryOptions"
:taggable="true"
:close-on-select="true"
:clear-on-select="false"
:searchable="true"
:max-height="250"
:preserve-search="true"
@tag="addTagCategury"
></multiselect>
<!-- <pre
class="language-json"
><code>{{ taskForm.category }}</code></pre> -->
</div>
<vue-tribute
class="col-3 ps-0 pe-0 tribute-container"
:options="tributeOptions"
>
<input
class="label-1 form-control task-form-control task-title"
placeholder="عنوان"
v-model="taskForm.title"
cols="20"
rows="3"
@tribute-no-match="noMatchFound"
@tribute-replaced="textReplaced"
@keydown.tab="foucsNext('duration')"
/>
</vue-tribute>
<div class="col-1">
<input
v-model="taskForm.duration"
ref="duration"
type="text"
placeholder="مدت "
@blur="timeCorrect(taskForm.duration)"
v-on:focus="timeFocus('duration', taskForm.duration)"
class="label-3 form-control task-form-control"
/>
</div>
<div class="col-4 label-4">
<input
v-model="taskForm.description"
type="text"
placeholder="گویا و مختصر بنویسید"
class="label-4 form-control task-form-control Description"
@keydown.tab="postInTaskWork(taskForm)"
/>
<div class="d-flex align-items-center">
<span
@click="postInTaskWork(taskForm)"
title="ثبت"
class="btn main-page_body-items-icon ps-0"
>
<svg
class="icon icon-Component-233--1 main-page_body-items-icon-add"
>
<use xlink:href="#icon-Component-233--1"></use>
</svg>
</span>
<button-component
@click="closeFormTaskInformation(taskForm)"
buttonText=""
v-tooltip="'حذف فرایند'"
>
<span class="text-danger">
<svg class="icon icon-Component-295--1">
<use xlink:href="#icon-Component-295--1"></use>
</svg>
</span>
</button-component>
</div>
</div>
</div>
</div>
<div class="row row-mobile mx-0 justify-content-end">
<div
class="main-page_body-items items items-top d-flex align-items-center col-12"
>
<!-- <vue-tribute class="col-6 ps-0 pe-0" :options="tributeOptions"> -->
<div
v-if="templateValue.title == 'multi-org_workbase'"
class="col-6"
style="color: #97f295 !important"
>
<multiselect
v-model.number="taskForm.organ"
class="multiselect Organ Organ-mobile"
tag-placeholder="Add this as new tag"
placeholder="سازمان"
:options="organOptions"
:taggable="true"
:close-on-select="true"
:clear-on-select="false"
:searchable="true"
:max-height="250"
:preserve-search="true"
@tag="addTagOrgan"
@select="organSelect"
></multiselect>
<!-- <pre
class="language-json"
><code>{{ taskForm.category }}</code></pre> -->
</div>
<div class="col-6">
<multiselect
id="task-Form-category"
v-model.number="taskForm.category"
tag-placeholder="Add this as new tag"
placeholder="دسته"
:options="categoryOptions"
:taggable="true"
:close-on-select="true"
:clear-on-select="false"
:searchable="true"
:max-height="250"
:preserve-search="true"
@tag="addTagCategury"
></multiselect>
</div>
<!-- </vue-tribute> -->
</div>
<div
class="col-12 label-4 main-page_body-items items items-bottom d-flex align-items-center mb-1"
>
<vue-tribute class="col-5 ps-0 pe-0" :options="tributeOptions">
<input
class="label-1 form-control task-form-control task-title"
placeholder="عنوان"
v-model="taskForm.title"
cols="20"
rows="3"
@tribute-no-match="noMatchFound"
@tribute-replaced="textReplaced"
/>
</vue-tribute>
<input
v-model="taskForm.duration"
ref="duration2"
type="text"
placeholder="مدت "
@blur="timeCorrect(taskForm.duration)"
v-on:focus="timeFocus('duration2', taskForm.duration)"
class="col-2 label-3 form-control task-form-control"
/>
<input
v-model="taskForm.description"
type="text"
placeholder="توضیحات(گویا،مختصر)"
class="label-5 form-control task-form-control Description"
/>
</div>
<div class="d-flex align-items-center ms-2">
<span
@click="postInTaskWork(taskForm)"
title="ثبت"
class="btn main-page_body-items-icon ps-0"
>
<svg class="icon icon-Component-233--1 main-page_body-items-icon-add">
<use xlink:href="#icon-Component-233--1"></use>
</svg>
</span>
<span title="حذف" class="btn ps-0">
<svg
@click="closeFormTaskInformation(taskForm)"
class="icon icon-Component-295--1 main-page_body-items-icon-close"
>
<use xlink:href="#icon-Component-295--1"></use>
</svg>
</span>
</div>
</div>
</form>
</template>
<script>
import taskApi from "@apis/taskApi";
import HttpService from "@services/httpService";
import { mapGetters } from "vuex";
import VueTribute from "vue-tribute";
export default {
props: {
itemIndex: {
default: 0,
},
isMultiOrgan: {
default: false,
},
taskForm: {
default() {
return {
organ: "",
category: "",
title: "",
duration: "",
description: "",
};
},
},
templateValue: {
default() {
return {
name: "تک سازمانی - کارمحور",
title: "single-org_workbase",
};
},
},
},
emits: ["close-form-tasks", "save-form-tasks", "add-new-record"],
beforeMount() {
this.httpService = new HttpService(this.taskMicroServiceName);
},
mounted() {
if (this.taskSchemaGetter?.categoryOptions)
this.categoryOptions = this.taskSchemaGetter?.categoryOptions;
if (this.taskSchemaGetter?.organOptions) {
this.organOptions = this.taskSchemaGetter?.organOptions;
}
if( this.taskForm.organ == "" && this.organOptions.length ){
this.taskForm.organ = this.organOptions[0];
}
},
data() {
return {
categoryOptions: [],
organOptions: [],
showOrgan: this.isMultiOrgan,
userLang: "fa-IR",
tributeOptions: {
// symbol or string that starts the lookup
trigger: "",
// element to target for @mentions
iframe: null,
// class added in the flyout menu for active item
selectClass: "highlight",
// class added to the menu container
containerClass: "tribute-container mini",
// class added to each list item
itemClass: "my-tribute-class",
// function called on select that returns the content to insert
selectTemplate: function (item) {
return item.original.title;
// return "@" + item.original.value;
},
// template for displaying item in menu
menuItemTemplate: function (item) {
return item.original.title;
},
// template for when no match is found (optional),
// If no template is provided, menu is hidden.
noMatchTemplate:
'<span class="no-result">مورد مشابهی ثبت نشده است.</span>',
// specify an alternative parent container for the menu
// container must be a positioned element for the menu to appear correctly ie. `position: relative;`
// default container is the body
menuContainer: document.body,
// column to search against in the object (accepts function or string)
lookup: "title",
// column that contains the content to insert by default
fillAttr: "title",
// REQUIRED: array of objects to match or a function that returns data (see 'Loading remote data' for an example)
// values: [
// {
// key: null,
// value: "sghl",
// },
// ],
// values: ["سلام", "باسلام", "علیک سلام", "بی سلام", "صدتا سلام"],
values: (text, cb) => {
this.remoteSearch(text, (items) => cb(items));
},
// When your values function is async, an optional loading template to show
loadingItemTemplate: null,
// specify whether a space is required before the trigger string
requireLeadingSpace: true,
// specify whether a space is allowed in the middle of mentions
allowSpaces: true,
// optionally specify a custom suffix for the replace text
// (defaults to empty space if undefined)
// replaceTextSuffix: "",
// specify whether the menu should be positioned. Set to false and use in conjuction with menuContainer to create an inline menu
// (defaults to true)
positionMenu: true,
// when the spacebar is hit, select the current match
spaceSelectsMatch: false,
// turn tribute into an autocomplete
autocompleteMode: true,
// Customize the elements used to wrap matched strings within the results list
// defaults to <span></span> if undefined
searchOpts: {
pre: '<span style="color:red">',
post: "</span>",
skip: false, // true will skip local search, useful if doing server-side search
},
// Limits the number of items in the menu
menuItemLimit: 25,
// specify the minimum number of characters that must be typed before menu appears
menuShowMinLength: 0,
},
};
},
computed: {
...mapGetters(["taskSchemaGetter"]),
taskMicroServiceName() {
return process.env.VUE_APP_TASK;
},
},
methods: {
organSelect($event) {
// console.log($event);
// console.log(this.taskForm?.organ);
// console.log(this.taskSchemaGetter?.organCategories);
if (
this.taskForm?.organ &&
this.taskSchemaGetter?.organCategories &&
this.taskSchemaGetter?.organCategories[this.taskForm?.organ]
) {
// console.log("this.taskForm.organ");
this.categoryOptions =
this.taskSchemaGetter?.organCategories[this.taskForm?.organ];
} else {
this.categoryOptions = this.taskSchemaGetter?.categoryOptions;
}
},
foucsNext(ref) {
this.$refs[ref].focus();
},
// setMultiOrgan(value){
// this.isMultiOrgan = value;
// },
timeFocus(ref, value) {
//if (value == "00:00" || value == "" || value == "مدت")
this.$refs[ref].select();
},
timeCorrect(duration) {
duration = duration.trim();
duration = duration.replace(".", ":");
duration = duration.replace(" ", ":");
if (duration.indexOf(":") == -1) {
if (duration.length == 0) duration = "00:00";
else if (duration.length == 1) duration = "0" + duration + ":00";
else if (duration.length == 2) duration = duration + ":00";
else if (duration.length == 3)
duration =
"0" + duration.substring(0, 1) + ":" + duration.substring(1);
else if (duration.length == 4)
duration =
duration.substring(0, 2) +
":" +
duration.substring(2).padStart(2, "0");
else if (duration.length > 4)
duration = duration.substring(0, 2) + ":" + duration.substring(3, 5);
}
this.taskForm.duration = duration;
},
noMatchFound(args) {
// console.info(args);
},
textReplaced(args) {
// console.info(args);
},
closeFormTaskInformation() {
this.$emit("close-form-tasks");
},
postInTaskWork(taskForm) {
this.$emit("save-form-tasks", taskForm);
},
openFormTaskInformation() {
this.$emit("add-new-record");
},
addTagCategury(newTag) {
// const tag = {
// name: newTag,
// };
this.taskForm.category = newTag;
},
addTagOrgan(newTag) {
// const tag = {
// name: newTag,
// };
this.taskForm.organ = newTag;
},
addTagTitle(newTag) {
// const tag = {
// name: newTag,
// };
this.taskForm.title = newTag;
},
remoteSearch(text, cb) {
let url = taskApi.workingHours.complition.replace("{{query}}", text);
if (text == "") url = taskApi.workingHours.complitionAll;
this.httpService.getRequest(url).then((response) => {
cb(response.data);
});
},
isNumberKey(evt) {
var charCode = evt.which ? evt.which : evt.keyCode;
if (charCode > 31 && (charCode < 48 || charCode > 57)) return false;
return true;
},
},
components: {
VueTribute: () => import("vue-tribute"),
Multiselect: () => import("vue-multiselect"),
},
};
</script>
<style lang="scss" scoped>
// input::placeholder {
// color: #d4dce3;
// }
// textarea::placeholder {
// color: #d4dce3;
// }
.row {
border: 1px rgb(223 223 223) solid;
border-radius: 0.5em;
margin-bottom: 0.5em;
}
.task-Form-title {
border-right: 1px var(--color-items-7) solid;
}
// .my-tribute-class {
// background: #d4dce3;
// }
</style>

View File

@ -0,0 +1,772 @@
<template>
<div class=" container-fluid">
<div class="all-selects justify-content-between col-12">
<div class="selects col-md-4 justify-content-start align-items-center">
<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-select 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>
<button
title="شروع"
type="button"
class="btn btn-primary btn-start"
@click.prevent="setActiveTab(navItem)"
>
شروع
</button>
</div> -->
</div>
<div
class="d-flex align-items-center grouping col-md-8 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"
:class="{ expanded: !isSidebarCollapsed }"
>
<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-4"
: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"
@edit-table-item="toggleUsersPanel"
@page-changed="pageChanged"
@page-limit-changed="pageLimitChanged"
@sort-changed="sortChanged"
>
</my-table>
</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="col-md-8 form-sorting_group">
<component
v-if="showcomponent"
:is="changeSorting()"
:listItem="listItem"
:columns="columnGroup"
:workList="workList"
:userId="userId"
></component>
<pie-donut
class="mt-5"
v-if="showChart"
:dataPie="dataPie"
:PieOptions="PieOptions"
width="90%"
height="300px"
></pie-donut>
</div>
</div>
</div>
</main>
</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: "firstName",
title: "نام",
width: "2",
},
{
isLink: true,
key: "lastname",
title: "نام خانوادگی",
width: "2",
},
{
isLink: true,
key: "team",
title: "تیم",
width: "2",
},
{
isLink: true,
key: "useTime",
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: [],
allUserId: "",
};
},
computed: {
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: {
...mapGetters(["isSidebarCollapsed"]),
...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) => {
// console.log("mainList", res.data);
this.mainList = res.data;
this.allUserId = this.mainList.map((item) => item.user_id).join(",");
this.pagination = { ...this.pagination, ...res.pagination };
// this.onLinkedTitleClick({
// rowItem: this.markActive(0),
// tableColumn: {},
// index: 0,
// });
})
.catch((err) => {
console.info(err);
})
.finally(() => {
this.getsorting();
if (this.showChart) {
this.getvalueChartDonut();
}
});
},
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 }) {
// // console.log(this.group_id)
// 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;
})
.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);
}
},
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);
},
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();
},
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 = {
users_id: this.allUserId,
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;
// console.log("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 = {
users_id: this.allUserId,
year: this.selectedYear,
month: this.selectedMonth,
};
let url = taskApi.taskChart.donut;
var vm = this;
this.httpService.postRequest(url, payload).then((res) => {
// console.log("chart", res.data);
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: 4em;
}
.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;
}
.btn-start {
margin-top: 2em;
margin-right: 1em;
}
.form-control{
width: 6em;
}
</style>

40
components/TaskLayout.vue Normal file
View File

@ -0,0 +1,40 @@
<template>
<router-view :key="$route.fullPath"></router-view>
</template>
<script>
import { mapActions } from "vuex";
import { clearBodyClass } from "@utilities/utilities";
export default {
data() {
return {
userLastStateIsLoaded: true,
modalComponentName: "",
actionMode: 1,
};
},
methods: {
...mapActions(["setBodyClass", "getState"]),
},
beforeCreate() {
ApiService.init(process.env.VUE_APP_LIST_BASE_URL);
},
beforeMount() {
clearBodyClass();
},
mounted() {
this.setBodyClass(process.env.VUE_APP_TASK_SYSTEM);
document.title = process.env.VUE_APP_CHAT_PAGE_TITLE;
},
destroyed() {
clearBodyClass();
},
};
</script>
<style lang="scss">
@import "../../assets/task/scss/task";
</style>

View File

@ -0,0 +1,912 @@
<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>

135
components/TeamForm.vue Normal file
View File

@ -0,0 +1,135 @@
<template>
<div class="sign-up__form">
<div class="sign-up__form-inputs">
<div class="sign-up__row sign-up__simple-input">
<div class="form-group position-relative">
<label for="">عنوان:</label>
<input
class="form-control elem__placeholder-gray"
v-model.trim="value.title"
type="text"
placeholder="عنوان"
autocomplete="on"
/>
</div>
</div>
<div class="sign-up__row sign-up__simple-input">
<div class="form-group position-relative">
<label for="">سازمان:</label>
<input
class="form-control elem__placeholder-gray"
v-model.trim="value.organ"
type="text"
placeholder="سازمان"
autocomplete="on"
/>
</div>
<div class="d-flex justify-content-end">
<div class="sign-up__btn-container ms-3">
<a @click="closeForm()" class="btn">انصراف</a>
</div>
<button class="btn btn-primary" @click="submit()">
<the-button-loading v-if="loading"></the-button-loading>
<template v-if="value.id">
{{ $t("Update") }}
</template>
<template v-else>
{{ $t("Save") }}
</template>
</button>
</div>
</div>
</div>
</div>
</template>
<script>
// import taskApi from "@apis/taskApi";
import HttpService from "@services/httpService";
export default {
beforeMount() {
this.httpService = new HttpService( this.taskMicroServiceName
);
},
props: {
parentLoading: false,
teamData: {
default() {
return {
id: undefined,
organ: null,
title: null,
};
},
},
},
watch: {
teamData(newValu) {
this.value = newValu;
},
parentLoading(newVal) {
this.loading = newVal;
},
},
mounted() {
this.value = this.teamData;
},
data() {
return {
// organ: "",
// title: "",
loading: this.parentLoading,
value: {
id: undefined,
organ: null,
title: null,
},
};
},
computed: {
taskMicroServiceName() {
return process.env.VUE_APP_TASK;
},
},
methods: {
submit() {
this.loading = true;
this.$emit("on-pass-by-emit", this.value);
},
// addTeams() {
// this.$emit("add-teams");
// },
closeForm() {
this.$emit("close-form");
},
// getGroups() {
// this.$emit("get-groups");
// },
// addTeams() {
// let url = taskApi.taskTeams.add;
// let payload = {
// title: this.value.title,
// organ: this.value.organ,
// };
// this.httpService
// .postRequest(url, payload)
// .then((res) => {
// this.mySwalToast({
// html: res.message,
// });
// })
// .finally(() => {
// this.getGroups();
// });
// },
},
};
</script>
<style scoped lang="scss">
// input::placeholder {
// color: #d7d7d7;
// }
</style>

46
json/menu.json Normal file
View File

@ -0,0 +1,46 @@
{
"tasks": [
{
"color": 5,
"icon": "Home-21",
"link": "defaultRoute",
"title": "پیشخوان",
"translateKey": "Dashboard"
},
{
"color": 5,
"icon": "projects",
"link": "taskDashboard",
"title": "پیشخوان مدیریت وظایف",
"translateKey": "TaskDashboard"
},
{
"color": 5,
"icon": "task-list",
"link": "taskTimes",
"title": "ساعات",
"translateKey": "TaskTimes"
},
{
"color": 5,
"icon": "task-time",
"link": "taskList",
"title": "وظایف",
"translateKey": "TaskList"
},
{
"color": 5,
"icon": "groups",
"link": "teams",
"title": "تیم ها",
"translateKey": "Teams"
},
{
"color": 5,
"icon": "portal-roles",
"link": "taskReport",
"title": "گزارش وظایف",
"translateKey": "taskReport"
}
]
}

View File

@ -0,0 +1,56 @@
[
{
"showOutside": false,
"show": true,
"icon": "Component-242--1",
"title": "ویرایش",
"to": {
"name": "undefined"
},
"selected": false,
"disabled": false,
"howToOpen": "",
"href": "",
"class": "edit-btn",
"action": "edit-table-item",
"can": "forms_edit"
},
{
"showOutside": false,
"show": true,
"icon": "Component-295--1",
"title": "حذف",
"to": {
"name": "undefined"
},
"selected": false,
"disabled": false,
"howToOpen": "",
"href": "",
"class": "delete-btn",
"action": "delete-table-item",
"can": "forms_delete"
},
{
"showOutside": false,
"show": true,
"icon": "groups",
"title": "اعضا",
"to": {
"name": "undefined"
},
"selected": false,
"disabled": false,
"howToOpen": "",
"href": "",
"class": "",
"action": "select-list-columns",
"can": "form_select-list-columns"
}
]

162
pages/Task.vue Normal file
View File

@ -0,0 +1,162 @@
<template>
<section>
<Navbar class="task-navbar"> </Navbar>
<the-sidebar2
:showUserAvatar="true"
:menu="menu"
@statusPage="statusPage"
></the-sidebar2>
<main class="main-page__content" :class="{ expanded: !isSidebarCollapsed }">
<right-section
ref="chat-list-panel"
@select-day="selectDay"
:show-header="statusPagHedear"
:key="render"
id="group"
></right-section>
<div class="flex-grow-1">
<div class="position-relative d-flex justify-content-center"></div>
<template>
<MainSection
ref="mainsection"
:key="render"
></MainSection>
</template>
</div>
<div class="right-section-mobile"></div>
<!-- <mobile-footer></mobile-footer> -->
</main>
</section>
</template>
<script>
import { mapGetters, mapMutations, mapActions } from "vuex";
import HttpService from "@services/httpService";
import menu from "@task/json/menu.json";
export default {
beforeMount() {
this.httpService = new HttpService();
this.getSchemas();
},
// mixins: [commentMixin],
mounted() {
this.TOGGLE_PANEL(false);
},
watch: {
getPanelStatus(status) {
if (status) this.openForm();
else this.closeModal();
},
$route: {
handler: function () {
this.sidebarCollapsedSetter(true);
},
deep: true,
immediate: true,
},
},
data() {
return {
nomber: 0,
statusPagHedear: 1,
render: 1,
showListPanel: false,
menu: menu,
};
},
computed: {
...mapGetters([
"getPanelStatus",
"isSidebarCollapsed",
"organNameGetter",
]),
},
methods: {
...mapMutations([
"TOGGLE_PANEL",
"sidebarCollapsedSetter",
"taskSchemaSetter",
]),
...mapActions(["checkPermissions"]),
getSchemas() {
let url = this.repoMicroServiceName + "schema";
this.httpService
.postRequest(url, {
organ: this.organNameGetter,
system: "task",
build_state: this.buildState,
})
.then((response) => {
this.taskSchemaSetter(response.data.task[0]);
this.render++;
})
.finally(() => {
this.loading = false;
});
},
selectedListBackTitle(name) {
if (name == "groups") return "دانشتو با بقیه به اشتراک بزار.";
if (name == "privates") return "به هر کی دوست داری، پیام بده.";
if (name == "lobbies") return "به تالار گفتگو خوش اومدی";
},
// showSearch() {
// search in current group, not all groups.
// this.$refs["chat-list-panel"].showSearchs(true);
// },
// #region mehdi
statusPage($event) {
const number = $event;
if (number == 1) {
if (this.$refs["chat-list-panel"].showMainpag == false) {
this.$refs["chat-list-panel"].showMainpag = true;
}
} else {
if (this.$refs["chat-list-panel"].showMainpag == false) {
this.$refs["chat-list-panel"].showMainpag = true;
}
}
},
// #endregion
toggleSidebarMenu() {
this.TOGGLE_SIDEBAR_MENU();
},
showToggleListPanel() {
this.showListPanel = !this.showListPanel;
this.$refs["chat-list-panel"].showMainpag = true;
},
// #region mehdi
openPagGrup(data) {
this.enablePanelToggling = data;
if (this.enablePanelToggling == 1) {
this.showToggleListPanel();
}
},
selectDay(date) {
this.$refs.mainsection.setCurrentDate(date);
},
// #endregion
},
components: {
MainSection: () => import("@task/components/MainSection"),
RightSection: () => import("@task/components/RightSection"),
Navbar: () => import("@task/components/Navbar"),
},
};
</script>
<style lang="scss" scoped></style>

289
pages/TaskDashboard.vue Normal file
View File

@ -0,0 +1,289 @@
<template>
<section>
<Navbar class="task-navbar"> </Navbar>
<the-sidebar2
:showUserAvatar="true"
:menu="menu"
@statusPage="statusPage"
></the-sidebar2>
<main
class="task-dashboard main-page__content"
:class="{ expanded: !isSidebarCollapsed }"
>
<div class="flex-grow-1">
<div class="position-relative d-flex justify-content-center"></div>
<section class="container-fluid">
<div class="row">
<div class="col-sm-12 col-md-6 mt-3">
<div class="my-card DonutChart">
<!-- <h2 class="title">Tasks by status</h2> -->
<pie-donut
:dataPie="dataPie"
:PieOptions="PieOptions"
width="90%"
height="300px"
ref="donutchart"
></pie-donut>
<div class="content"></div>
</div>
</div>
<div class="col-sm-12 col-md-3 mt-3">
<div class="my-card">
<h2 class="title">Completed tasks</h2>
<div class="content">
<span class="value">28</span
><span class="subtitle">tasks completed</span>
</div>
</div>
</div>
<div class="col-sm-12 col-md-3 mt-3">
<div class="my-card">
<h2 class="title">Incomplete tasks</h2>
<div class="content">
<span class="value">14</span
><span class="subtitle">to be done</span>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12 col-md-4">
<div class="my-card total-tasks">
<h2 class="title">Total tasks by assignee</h2>
<div class="content"></div>
</div>
</div>
<div class="col-sm-12 col-md-8">
<div class="my-card Gantt">
<h2 class="title">Timeline</h2>
<div class="content">
<Gantt class="Gantt"></Gantt>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="my-card">
<h2 class="title">Cumulative flow</h2>
<div class="content"></div>
</div>
</div>
</div>
</section>
</div>
</main>
</section>
</template>
<script>
// import commentMixin from "@mixins/commentMixin";
// import apis from "@apis/listApi";
import { mapGetters, mapMutations, mapActions } from "vuex";
// import { handleErrors } from "@utilities/utilities";
// import HttpService from "@services/httpService";
import menu from "@task/json/menu.json";
import taskApi from "@apis/taskApi";
import HttpService from "@services/httpService";
export default {
beforeMount() {
// const headers = {
// "app-id": process.env.VUE_APP_APP_ID,
// // "lang": process.env.VUE_APP_LANG,
// // "app-version-code": process.env.VUE_APP_APP_VERSION,
// };
// this.httpService = new HttpService(this.repoMicroServiceName);
},
// mixins: [commentMixin],
beforeMount() {
this.httpService = new HttpService( this.taskMicroServiceName
);
},
mounted() {
this.TOGGLE_PANEL(false);
this.getvalueChartDonut();
},
watch: {
getPanelStatus(status) {
if (status) this.openForm();
else this.closeModal();
},
$route: {
handler: function () {
this.sidebarCollapsedSetter(true);
},
deep: true,
immediate: true,
},
// listIdGetter(newId) {
// this.filterBy(this.selectedFilter);
// },
},
data() {
return {
PieOptions: {
radius: ["40%", "70%"],
selectedMode: "single",
},
// #region mehdi
nomber: 0,
statusPagHedear: 1,
dataPie: [],
// #endregion
rerenderChatList: 1,
showListPanel: false,
menu: menu,
};
},
computed: {
...mapGetters([
"getPanelStatus",
"isSidebarCollapsed",
"sidebarListStatusGetter",
]),
...mapGetters("list", [
"listComponentNameGetter",
"selectedProjectGetter",
"listIdGetter",
"selectedItemGetter",
"listGetter",
]),
taskMicroServiceName() {
return process.env.VUE_APP_TASK;
},
},
methods: {
...mapMutations([
"TOGGLE_PANEL",
"sidebarCollapsedSetter",
"SET_SIDEBAR_LIST_STATUS",
]),
...mapMutations("list", [
"SET_LIST_COMPONENT_NAME",
"SET_SELECTED_ITEM",
"SET_SELECTED_PROJECT",
"SET_LIST_ID",
"SET_LIST",
"SET_LIST_ID",
]),
...mapActions(["checkPermissions", "storeState", "getState"]),
selectedListBackTitle(name) {
if (name == "groups") return "دانشتو با بقیه به اشتراک بزار.";
if (name == "privates") return "به هر کی دوست داری، پیام بده.";
if (name == "lobbies") return "به تالار گفتگو خوش اومدی";
},
// showSearch() {
// search in current group, not all groups.
// this.$refs["chat-list-panel"].showSearchs(true);
// },
// #region mehdi
statusPage($event) {
const number = $event;
if (number == 1) {
if (this.$refs["chat-list-panel"].showMainpag == false) {
this.$refs["chat-list-panel"].showMainpag = true;
}
} else {
if (this.$refs["chat-list-panel"].showMainpag == false) {
this.$refs["chat-list-panel"].showMainpag = true;
}
}
},
// #endregion
toggleSidebarMenu() {
this.TOGGLE_SIDEBAR_MENU();
},
showToggleListPanel() {
this.showListPanel = !this.showListPanel;
this.$refs["chat-list-panel"].showMainpag = true;
},
// #region mehdi
openPagGrup(data) {
this.enablePanelToggling = data;
if (this.enablePanelToggling == 1) {
this.showToggleListPanel();
}
},
// #endregion
getvalueChartDonut() {
let payload = {
user_id: this.$store.getters.currentUser.user_id,
};
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: {
MainSection: () => import("@task/components/MainSection"),
RightSection: () => import("@task/components/RightSection"),
Navbar: () => import("@task/components/Navbar"),
Gantt: () => import("@components/charts/Gantt.vue"),
PieDonut: () => import("@components/charts/PieDonut.vue"),
},
};
</script>
<style scoped lang="scss">
.task-dashboard {
.row {
margin-top: 1.5em;
margin-bottom: 1.5em;
}
.my-card {
// &.DonutChart{
// width: 54em;
// }
// &.Gantt{
// width: 72.5em;
// }
// &.total-tasks{
// width: unset;
// }
// &.ComparetasksChart{
// width: 100%;
// }
// width: 26em;
height: 18em;
display: flex;
flex-direction: column;
background-color: #fff;
border-radius: 1em;
overflow: hidden;
box-shadow: 0 0.4688rem 2.1875rem rgba(4, 9, 20, 0.03),
0 0.9375rem 1.4063rem rgba(4, 9, 20, 0.03),
0 0.25rem 0.5313rem rgba(4, 9, 20, 0.03),
0 0.125rem 0.1875rem rgba(4, 9, 20, 0.03);
.title {
font-size: 1.2em;
text-align: left;
margin: 1em;
}
.content {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
}
}
}
</style>

142
pages/TaskReport.vue Normal file
View File

@ -0,0 +1,142 @@
<template>
<div>
<the-sidebar2
:class="buildName"
:menu="menu"
:showUserAvatar="true"
></the-sidebar2>
<div class="container-fluid">
<div>
<div>
<div class="col">
<div
class="main-page__content navbar row align-items-center p-0"
:class="{ expanded: !isSidebarCollapsed }"
>
<div class="col col-lg-auto order-1 breadcrumbs">
<Breadcrumbs />
</div>
<div class="col-12 col-lg order-3 order-lg-2">
<div
class="nav-tabs-container nav-tabs d-flex justify-content-center"
>
<ul class="nav">
<li
class="nav-item desktop"
v-for="(navItem, index) in navList"
:key="index"
>
<button
:title="navItem.title"
type="button"
class="btn nav-link"
@click.prevent="setActiveTab(navItem)"
:class="{
active: activeTabGetter?.key == navItem.key,
}"
>
{{ navItem.title }}
</button>
</li>
</ul>
</div>
</div>
<div class="col-auto col-lg-auto order-2 order-lg-3 d-flex">
<user-avatar-dropdown
style="position: static"
class="user-avatar-component"
></user-avatar-dropdown>
<select-language-dropdown></select-language-dropdown>
</div>
</div>
</div>
</div>
<div
class="main-page__content"
:class="{ expanded: !isSidebarCollapsed }"
>
<component
v-if="mainComponentName"
:is="mainComponentName"
></component>
</div>
</div>
</div>
</div>
</template>
<script>
import HttpService from "@services/httpService";
import menu from "@task/json/menu.json";
import { mapGetters, mapMutations } from "vuex";
export default {
beforeMount() {
this.httpService = new HttpService(this.taskMicroServiceName);
},
mounted() {
this.setActiveTab(this.navList[0]);
},
data() {
return {
httpService: undefined,
menu: menu,
mainComponentName: "TaskUserReport",
navList: [
{
title: "کاربران",
key: "users",
},
{ title: "گروه ها", key: "group" },
],
};
},
computed: {
...mapGetters("entity", ["activeTabGetter"]),
...mapGetters(["isSidebarCollapsed"]),
},
methods: {
...mapMutations("entity", ["activeTabSetter"]),
setActiveTab(tab) {
this.activeTabSetter(tab);
if (tab.key == "group") {
this.mainComponentName = "TaskGroupReport";
} else {
this.mainComponentName = "TaskUserReport";
}
},
},
components: {
TaskUserReport: () => import("@task/components/TaskUserReport.vue"),
TaskGroupReport: () => import("@task/components/TaskGroupReport.vue"),
},
};
</script>
<style lang="scss">
// .breadcrumbs {
// margin-right: 7.5em !important;
// }
// .expanded {
// margin-right: 3em;
// }
.nav-item {
.nav-tabs .nav-link:hover,
.nav-tabs .nav-link:focus {
color: var(--primary-color);
border-color: transparent;
transition: background-color 0.3s ease, color 0.3s ease;
}
.nav-link {
padding: 0.5rem 2.5rem !important;
color: var(--text-primary);
}
}
.navbar {
background-color: #eee;
}
.nav-tabs {
border-bottom: unset;
}
</style>

598
pages/TaskTeams.vue Normal file
View File

@ -0,0 +1,598 @@
<template>
<div>
<div class=" container-fluid">
<header>
<the-navbar></the-navbar>
</header>
<the-sidebar2 :menu="menu" :showUserAvatar="true"></the-sidebar2>
<main
class="pages-content-container main-page__content"
:class="{ expanded: !isSidebarCollapsed }"
>
<div class="pages-content mt-4 mt-lg-0 col-12">
<div class="" :class="{ 'd-flex': showPanel || showPanelTeams }">
<template v-if="canView">
<my-table
:tableActions="tableActions"
height="auto"
:hasSearch="true"
minHeight="25em"
maxHeight="calc(100dvh - 5em)"
:paginationInfo="pagination"
:sortingInfo="sorting"
:items="groupLists"
:fetchingData="fetchingData"
:tableColumns="tableColumns"
:totalPages="pagination.pages"
@search="searchGroups"
@reset-form="searchGroups"
@delete-table-item="deleteItem"
@edit-table-item="toggleUsersPanel"
@page-changed="pageChanged"
@page-limit-changed="pageLimitChanged"
@sort-changed="sortChanged"
@on-linked-title-click="onLinkedTitleClick"
>
<template v-slot:tableHeaderActions>
<div class="d-flex">
<!-- <div class="dropdown">
<button
class="btn dropdown-toggle"
type="button"
data-bs-toggle="dropdown"
aria-expanded="false"
>
{{ groupsTranslation[searchType] }}
</button>
<div class="dropdown-menu text-end">
<button
class="dropdown-item"
type="button"
@click.prevent="setSearchType('getGroupsWithoutAdmin')"
:class="{
active: searchType == 'getGroupsWithoutAdmin',
}"
value="getGroupsWithoutAdmin"
>
{{ groupsTranslation["getGroupsWithoutAdmin"] }}
</button>
<button
class="dropdown-item"
type="button"
@click.prevent="setSearchType('getGroupsWithAdmin')"
:class="{ active: searchType == 'getGroupsWithAdmin' }"
value="getGroupsWithAdmin"
>
{{ groupsTranslation["getGroupsWithAdmin"] }}
</button>
</div>
</div> -->
<button
v-if="createPanel"
@click.prevent="openCreatePanel()"
class="btn btn-primary"
type="button"
>
افزودن تیم
</button>
</div>
</template>
</my-table>
</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="addMember()"
class="btn btn-primary"
type="button"
>
افزودن عضو جدید
</button>
<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">
<MembersSearch
v-if="showPanel"
:listMember="listMember"
:searchResults="users"
@on-send="searchUsers"
@delete-member="deleteMembers"
@edit-member="editMembers"
></MembersSearch>
</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"
@close-form="closeShowPanelTeams()"
:teamData="selectedItemClone"
></TeamForm>
</div>
</div>
</div>
</div>
<AddMemberToTeamModal
@update-member="updateMembers"
:item="[]"
v-if="showAddMember"
class="add-member"
></AddMemberToTeamModal>
</main>
</div>
</div>
</template>
<script>
import { mapGetters, mapActions } from "vuex";
import HttpService from "@services/httpService";
import menu from "@task/json/menu.json";
import permitApis from "@apis/permitApi";
import taskApi from "@apis/taskApi";
export default {
beforeMount() {
this.httpService = new HttpService(this.taskMicroServiceName);
},
mounted() {
this.checkPermisionBeforGetList();
},
data() {
return {
groupsId: "",
showAddMember: false,
listMember: [],
users: [],
createPanel: true,
searchType: "getGroupsWithoutAdmin",
groupLists: [],
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: "title",
title: "عنوان",
width: "4",
},
{
isLink: true,
key: "organ",
title: "سازمان",
width: "4",
},
],
pagination: {
page: 1,
pages: 0,
total: 0,
offset: 0,
limit: 10,
},
sorting: {
sortby: "title",
sortorder: "asc", // asc | desc | none
},
// users: [],
loading: false,
showPanel: false,
showPanelTeams: false,
selectedItemClone: undefined,
prevSelectedItemIndex: undefined,
};
},
computed: {
...mapGetters(["isSidebarCollapsed"]),
...mapGetters("permit", ["projectGetter"]),
},
methods: {
...mapActions(["checkPermissions"]),
async getGroups() {
let url = taskApi.taskTeams.getList;
let payload = {
organ: "",
...this.sorting,
...this.pagination,
};
return await this.httpService.postRequest(url, payload).then((res) => {
this.groupLists = res.data;
// this.pagination.pages = res.pagination.pages;
this.pagination = { ...this.pagination, ...res.pagination };
});
},
checkPermisionBeforGetList() {
if (this.fetchingData) return;
this.fetchingData = true;
this.checkPermissions({ _this: this, permission: "teams_list" })
.then(() => {
this.getGroups()
.then(() => {
this.canView = true;
this.fetchingData = false;
})
.catch(() => {
this.canView = false;
this.fetchingData = false;
});
})
.catch(() => {
this.canView = false;
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;
},
save(formData = undefined) {
if (this.loading) return;
this.loading = true;
const url = formData.id ? taskApi.taskTeams.edit : taskApi.taskTeams.add;
this.httpService
.postRequest(url, formData)
.then((res) => {
this.getGroups().then(() => {
this.loading = false;
});
this.mySwalToast({
html: res.message,
});
this.showPanel = false;
this.showPanelTeams = false;
this.createPanel = true;
this.resetForm();
})
.finally(() => {
this.loading = false;
});
},
deleteItem(index) {
// if (this.loading) return;
this.loading = true;
let groupId = this.groupLists[index].id;
const payload = {
id: groupId,
};
let url = taskApi.taskTeams.delete;
this.mySwalConfirm({
title: "هشدار",
html: "از حذف این مورد مطمئن هستید؟",
icon: "warning",
}).then((result) => {
if (result.isConfirmed) {
this.httpService.postRequest(url, payload).then((res) => {
this.getGroups().then(() => {
this.loading = false;
// this.showPanel = false;
});
this.mySwalToast({
html: res.message,
});
});
}
});
},
openCreatePanel() {
this.selectedItemClone = undefined;
if (this.showPanelTeams) {
this.showPanelTeams = false;
} else {
this.showPanelTeams = true;
this.showPanel = false;
this.createPanel = false;
}
},
toggleUsersPanel(index = undefined) {
if (index !== undefined) {
if (this.prevSelectedItemIndex !== undefined)
this.groupLists[this.prevSelectedItemIndex].active = false;
this.prevSelectedItemIndex = index;
this.$set(this.groupLists[index], "active", true);
this.selectedItemClone = structuredClone(this.groupLists[index]);
this.showPanel = false;
this.showPanelTeams = true;
this.createPanel = false;
} else {
this.resetForm();
}
},
closeShowPanelTeams() {
this.showPanelTeams = false;
this.createPanel = true;
},
resetForm() {
this.selectedItemClone = undefined;
this.showPanel = false;
this.showPanelTeams = 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.getGroups();
},
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.getGroups();
},
sortChanged(sorting) {
this.pagination.page = this.pagination.offset = 0;
this.sorting = sorting;
this.getGroups();
},
searchGroups(query) {
this.resetPagination();
this[this.searchType](query);
},
async searchUsers(query = "") {
// this.resetPagination();
let url = this.loginMicroServiceName + permitApis.users.search;
url = url.replace("{{offset}}", 0);
url = url.replace("{{limit}}", 50);
url = url.replace("{{sortby}}", "username");
url = url.replace("{{sortorder}}", "desc");
// removing '/' before query if query is empty.
if (query) url = url.replace("{{query}}", query);
else url = url.replace("/{{query}}", query);
return await this.httpService.getRequest(url).then((res) => {
this.users = res.data;
// this.pagination = { ...this.pagination, ...res.pagination };
});
},
onLinkedTitleClick(index) {
this.showPanelTeams = false;
this.showPanel = true;
this.groupsId = index.index;
this.getMembers();
// if (this.showPanel) {
// this.showPanel = false;
// } else {
// this.showPanelTeams = false;
// this.showPanel = true;
// }
},
getMembers() {
let payload = {
group_id: this.groupsId + 1,
offset: 0,
limit: 50,
sortby: "id",
sortorder: "asc",
};
const url = taskApi.taskTeams.members;
this.httpService.postRequest(url, payload).then((res) => {
this.listMember = res.data;
});
},
addMember() {
this.showAddMember = true;
setTimeout(() => {
$("#share-modal").modal("show");
}, 500);
},
updateMembers(payload) {
const formData = {
...payload,
...{ group_id: this.groupsId + 1 },
};
const url = taskApi.taskTeams.addMembers;
this.httpService
.postRequest(url, formData)
.then((response) => {
this.getMembers();
this.mySwalToast({
title: "تبریک",
html: response.message,
icon: "success",
});
})
.catch((err) => {
this.mySwalToast({
title: "خطا",
html: err.response.message ?? err.message,
icon: "error",
});
})
.finally(() => {
$("#share-modal").modal("hide");
setTimeout(() => {
this.showModal = true;
}, 500);
this.loading = false;
});
},
deleteMembers(list) {
let payload = {
id: list.id,
};
const url = taskApi.taskTeams.deleteMembers;
this.httpService.postRequest(url, payload);
this.mySwalConfirm({
title: "هشدار!!!",
html: "از حذف این مورد مطمئن هستید؟",
}).then((result) => {
if (result.isConfirmed) {
this.getMembers();
this.mySwalToast({
title: "تبریک",
html: "مورد با موفقیت حذف شد",
icon: "success",
});
}
});
},
editMembers(payload) {
// console.log(this.listMember.id)
const formData = {
...payload,
...{ project_id: this.projectId },
};
const url = taskApi.taskTeams.editMembers;
this.httpService
.postRequest(url, formData)
.then((response) => {
this.getMembers();
this.mySwalToast({
title: "تبریک",
html: response.message,
icon: "success",
});
})
.catch((err) => {
this.mySwalToast({
title: "خطا",
html: err.response.message ?? err.message,
icon: "error",
});
})
.finally(() => {
$("#share-modal").modal("hide");
setTimeout(() => {
this.showModal = true;
}, 500);
this.loading = false;
});
},
},
components: {
MembersSearch: () => import("@task/components/MembersSearch"),
TeamForm: () => import("@task/components/TeamForm"),
AddMemberToTeamModal: () => import("@task/components/AddMemberToTeamModal"),
},
};
</script>
<style scoped lang="scss">
.side-panel {
padding: 1em;
width: 30em;
max-width: 100%;
background-color: #fafafa;
border-right: 1px solid #eee;
}
// .pages-content-container {
// margin-right: 8em;
// margin-left: 2em;
// }
</style>