![]() Server : Apache System : Linux server2.corals.io 4.18.0-348.2.1.el8_5.x86_64 #1 SMP Mon Nov 15 09:17:08 EST 2021 x86_64 User : corals ( 1002) PHP Version : 7.4.33 Disable Function : exec,passthru,shell_exec,system Directory : /home/corals/ts.corals.io/frontend/pages/ |
<template> <div> <div class="row d-none d-md-flex"> <div class="col-md-6 offset-md-1"> <div class="ml-1 pb-2 d-flex align-items-center"> <b-button-group> <b-button :disabled="!days.length" @click="goPrev" variant="outline-secondary" v-can="`view entry`"> <fa icon="chevron-left"/> </b-button> <b-button :disabled="!days.length" @click="goNext" variant="outline-secondary" v-can="`view entry`"> <fa icon="chevron-right"/> </b-button> </b-button-group> <h2 class="d-inline-block mb-0 mx-3" v-if="selectedDate && dayViewEnabled">{{ selectedDate.title }}</h2> <h2 class="d-inline-block mb-0 mx-2" v-else-if="!dayViewEnabled"> {{ this.start_week + ' - ' + this.end_week }}</h2> <b-skeleton animation="fade" class="d-inline-block mb-0 mx-3 w-25 sk-h2" v-else></b-skeleton> <a href="#" @click.prevent="returnToToday" v-if="!isTodayOrWeekSelected" v-can="`view entry`"> <small> <span v-if="dayViewEnabled">Return to today</span> <span v-else>Return to current week</span> </small> </a> </div> </div> <div class="col-md-5 d-flex align-items-center justify-content-end"> <b-button-group class="mr-2 rounded "> <b-button class="btn-light border-secondary" :class="[dayViewEnabled ? 'active' : '']" v-b-tooltip.hover title="Day view" @click.prevent="hideWeekView"> <fa icon="calendar-week" class="text-dark"/> Day </b-button> <b-button class="btn-light border-secondary" :class="[!dayViewEnabled ? 'active' : '']" v-b-tooltip.hover title="Week view" @click.prevent="showWeekView"> <fa icon="calendar-week" class="text-dark"/> Week </b-button> </b-button-group> <b-button @click.prevent="openFilterModal" class="mr-1" variant="warning" :disabled="!days.length" v-if="$isAdmin()"> <fa icon="filter"/> </b-button> <b-form-datepicker v-model="specificDate" button-variant="info" class="mr-1" dropleft button-only :disabled="!days.length"/> </div> </div> <div class="row d-md-none no-gutters"> <div class="col-10"> <div class="d-flex align-items-center"> <b-button-group class="d-inline-block"> <b-button :disabled="!days.length" @click="goPrev" variant="outline-secondary" class="px-2"> <fa icon="chevron-left"/> </b-button> <b-button :disabled="!days.length" @click="goNext" variant="outline-secondary" class="px-2"> <fa icon="chevron-right"/> </b-button> </b-button-group> <h4 class="d-inline-block mb-0 mx-2 mx-md-3" v-if="selectedDate && dayViewEnabled"> {{ selectedDate.title }}</h4> <h5 class="d-inline-block mb-0 mx-md-3" v-else-if="!dayViewEnabled"> {{ this.start_week + '-' + this.end_week }}</h5> <b-skeleton animation="fade" class="d-inline-block mb-0 mx-2 mx-md-3 w-50 sk-h4" v-else></b-skeleton> </div> <a href="#" class="d-block text-right mb-1" @click.prevent="returnToToday" v-if="!isTodayOrWeekSelected" v-can="`view entry`"> <small> <span v-if="dayViewEnabled">Return to today</span> <span v-else>Return to current week</span> </small> </a> <div v-else class="mb-1"> <small> </small> </div> </div> <div class="col-2 text-right"> <b-form-datepicker v-model="specificDate" dropleft button-variant="info" button-only :disabled="!days.length"/> </div> </div> <div class="row no-gutters"> <div class="col-md-1 text-md-center d-none d-md-block pt-2"> <button class="btn btn-success py-1 px-2" v-can="`create entry`" v-b-tooltip.hover title="New entry" :disabled="!days.length" @click.prevent="openModal"> <fa icon="plus" size="3x"/> </button> <small class="d-block"><b v-if="dayViewEnabled">New entry</b> <b v-else>New Row</b></small> </div> <div class="col-12 d-block d-md-none justify-content-between d-flex" :class="$isAdmin()?'col-10':'col-12'"> <b-button class="btn btn-success mb-2" :disabled="!days.length" @click.prevent="openModal"> <fa icon="plus"/> <span v-if="dayViewEnabled">New entry</span> <span v-else>New Row</span> </b-button> <b-button-group class="mb-2 rounded"> <b-button class="btn-light border-secondary" :class="[dayViewEnabled ? 'active' : '']" v-b-tooltip.hover title="Day view" @click.prevent="hideWeekView"> <fa icon="calendar-week" class="text-dark"/> Day </b-button> <b-button class="btn-light border-secondary" :class="[!dayViewEnabled ? 'active' : '']" v-b-tooltip.hover title="Week view" @click.prevent="showWeekView"> <fa icon="calendar-week" class="text-dark"/> Week </b-button> </b-button-group> <b-button @click.prevent="openFilterModal" class="mb-2" v-isAdmin :disabled="!days.length" variant="warning"> <fa icon="filter"/> </b-button> </div> <div class="col-md-11"> <div class="row d-flex" v-isAdmin v-if="filterForm.initialized"> <div class="col-md-12"> <ul class="list-inline m-0"> <li class="list-inline-item" v-if="filterForm.user_id"> <small><b>User:</b> {{ selectedUserFilter.label }}</small> <sup class="text-danger cursor-pointer"> <fa icon="times" @click.prevent="clearFilterItem('user_id')"/> </sup> </li> <li class="list-inline-item" v-if=" filterForm.client_id"> <small><b>Client:</b> {{ selectedClientFilter }}</small> <sup class="text-danger cursor-pointer"> <fa icon="times" @click.prevent="clearFilterItem('client_id')"/> </sup> </li> <li class="list-inline-item" v-if="filterForm.project_id"> <small><b>Project:</b> {{ selectedProjectFilter }}</small> <sup class="text-danger cursor-pointer"> <fa icon="times" @click.prevent="clearFilterItem('project_id')"/> </sup> </li> </ul> </div> </div> <div v-if="dayViewEnabled"> <Day :showLoader="showLoader" :days="days" :selectedDayIndex="selectedDayIndex" :evaluationEnabled="evaluationEnabled" :showBillable="showBillable" :weekTotalBillableTime="weekTotalBillableTime" :weekTotalTime="weekTotalTime" :form="form" :showAddEntryModal="showAddEntryModal" :entryModalTitle="entryModalTitle" /> </div> <div v-else> <Week :showLoader="showLoader" :days="days" :weekDays="getWeekDays" :showCreateRowModal="showCreateRowModal"/> </div> </div> </div> <div class="d-flex justify-content-center"> <div class="text-sm col-md-8"> <b-alert variant="warning" show fade v-if="membersWithoutEntries.length && $isAdmin()"> <b> Users without entries for {{ selectedDate.title }}: </b> <br/> <div v-for="member in membersWithoutEntries">- {{ member }}</div> </b-alert> </div> </div> <template v-if="$isAdmin()"> <b-modal content-class="shadow" title="Filters" no-close-on-backdrop :id="filtersModalId"> <c-overlay :show="!filterForm.initialized"> <div> <corals-select :form="tempFilterForm" label="User" field="user_id" :options="filterForm.initialized?filterForm.getFormData('users'):[]"/> </div> <div> <corals-select :form="tempFilterForm" label="Project" field="project_id" :options="filterForm.initialized?filterForm.getFormData('projects'):[]"/> </div> <div> <corals-select :form="tempFilterForm" label="Client" field="client_id" :options="filterForm.initialized?filterForm.getFormData('clients'):[]"/> </div> </c-overlay> <template #modal-footer> <button class="btn btn-primary" @click.prevent="filter"> <fa icon="search"></fa> </button> <button class="btn btn-secondary" @click.prevent="resetFilters"> <fa icon="eraser"></fa> </button> </template> </b-modal> </template> </div> </template> <script> import COverlay from "@/components/layout/COverlay"; import commonMixin from "@/mixins/commonMixin"; import Week from "@/components/TimesheetCalender/Week"; import Day from "@/components/TimesheetCalender/Day"; export default { name: "timesheet-calender", components: {COverlay, Week, Day}, middleware: 'Authorization', mixins: [commonMixin], provide() { return { fetchNewWeek: this.fetchNewWeek, removeRowWeek: this.removeRowWeek, showEditEntryModal: this.showEditEntryModal, onModalHidden: this.onModalHidden, updateCreateEntry: this.updateCreateEntry, updateSelectedDayIndex:this.updateSelectedDayIndex, afterFormSubmitted: this.afterFormSubmitted, } }, async fetch() { if (this.hasFiltersMethod()) { await this.initFilterForm() } let date = this.todayDate; if (this.$route.query.date && this.$route.query.date.match(/^\d{4}-\d{2}-\d{2}$/) !== null) { date = this.$route.query.date; } await this.fetchWeekAndSelectDate(date); }, data() { return { prev_week: null, next_week: null, start_week: null, end_week: null, dayViewEnabled: true, specificDate: this.selectedDate, days: [], weekDays: [], weekTotalTime: '00:00', weekTotalBillableTime: '00:00', filtersModalId: 'filter-modal', filterForm: {}, tempFilterForm: this.$form({ user_id: this.$route.query.user_id || '', project_id: this.$route.query.project_id || '', client_id: this.$route.query.client_id || '' }, {fetchFormDataURL: 'timesheet/timesheet-calender/get-filters-form-data'}), showBillable: false, showAddEntryModal: false, showCreateRowModal: false, selectedDayIndex: null, showLoader: false, selectedEntry: null, form: this.$form({ activity_id: '', project_id: '', user_id: '', spent_at: new Date(), spent_from: new Date(), spent_to: new Date(), hours: '', minutes: '', tags: [], evaluation_hours: '', evaluation_minutes: '', description: '', has_reviewed: 0, multiple_entries: 0, all_member_users: 0, }, {fetchFormDataURL: 'timesheet/entries/get-form-data', model: 'entry'}) } }, methods: { showWeekView() { this.dayViewEnabled = false; }, hideWeekView() { this.dayViewEnabled = true; }, removeRowWeek(result) { this.weekDays = result; }, openFilterModal() { if (!this.filterForm.initialized) { this.initFilterForm(); } this.$bvModal.show(this.filtersModalId); }, async initFilterForm() { this.filterForm = await this.$form({ user_id: this.$route.query.user_id || '', project_id: this.$route.query.project_id || '', client_id: this.$route.query.client_id || '' }, {fetchFormDataURL: 'timesheet/timesheet-calender/get-filters-form-data'}); }, resetFilters() { this.filterForm.reset(); this.tempFilterForm.reset(); this.filter(); }, filter() { this.filterForm.replace(this.tempFilterForm); this.fetchWeekAndSelectDate(this.selectedDate.date) this.$bvModal.hide(this.filtersModalId); }, appendAllFiltersToURLParameters() { this.$router.push({ query: this.getParametersList() }); }, getParametersList() { let selectedDate = this.selectedDate; let filters = {}, date = null; if (this.filterForm) { filters = { user_id: this.filterForm.user_id || '', client_id: this.filterForm.client_id || '', project_id: this.filterForm.project_id || '' }; } if (selectedDate && selectedDate.date) { date = selectedDate.date; } else if (this.$route.query.date && this.$route.query.date.match(/^\d{4}-\d{2}-\d{2}$/) !== null) { date = this.$route.query.date; } Object.keys(filters).forEach((k) => (!filters[k]) && delete filters[k]); return Object.assign(filters, { date: date }); }, clearFilterItem(key) { this.tempFilterForm[key] = ''; this.filter(); }, showEditEntryModal(entry) { this.selectedEntry = entry; this.showAddEntryModal = true; }, onModalHidden() { if (this.dayViewEnabled) { this.form.replace(this.form.originalData); this.form.errors.purge(); this.selectedEntry = null; this.showAddEntryModal = false; } else { this.showCreateRowModal = false; } }, openModal() { if (this.$cant('create', 'entry')) { return; } if (this.dayViewEnabled) { // this.form.user_id = this.$auth.user.id; this.form.id = ''; this.form.spent_at = this.selectedDate.date; this.showAddEntryModal = true; } else { this.showCreateRowModal = true; } }, async updateCreateEntry() { if (this.selectedEntry) { this.updateSelectedEntry() } else { this.createNewEntry() } }, updateSelectedEntry() { if (this.$cant('update', 'entry')) { return; } this.form.put(`timesheet/projects/${this.selectedEntry.project_id}/entries/${this.selectedEntry.id}`) .then(this.afterFormSubmitted); }, createNewEntry() { if (this.$cant('create', 'entry')) { return; } if (!this.form.project_id) { return; } this.form.post(`timesheet/projects/${this.form.project_id}/entries`) .then(async response => { await this.afterFormSubmitted(); }) }, async afterFormSubmitted() { this.selectedEntry = null; // this.form.user_id = this.$auth.user.id; this.form.id = ''; this.form.spent_at = this.selectedDate.date; this.form.setDefaults(); this.showAddEntryModal = false; await this.fetchWeekAndSelectDate(this.selectedDate.date); }, returnToToday() { if (!this.selectDay(this.todayDate)) { this.fetchNewWeek(this.todayDate).then(() => { this.selectDay(this.todayDate) }); } }, selectDay(dayDate) { let todaySelected = false; for (const dayIndex in this.days) { if (this.days[dayIndex].date === dayDate) { this.selectedDayIndex = Number(dayIndex); todaySelected = true; break; } } this.appendAllFiltersToURLParameters(dayDate); return todaySelected; }, async goNext() { if (!this.dayViewEnabled) { await this.fetchNewWeek(this.next_week); this.selectedDayIndex = 0; } else { if (!this.days[this.selectedDayIndex + 1]) { await this.fetchNewWeek(this.selectedDate.next_day); this.selectedDayIndex = 0; } else { this.selectedDayIndex++; } } }, async goPrev() { if (!this.dayViewEnabled) { await this.fetchNewWeek(this.prev_week); this.selectedDayIndex = this.days.length - 1; } else { if (!this.days[this.selectedDayIndex - 1]) { await this.fetchNewWeek(this.selectedDate.prev_day); this.selectedDayIndex = this.days.length - 1; } else { this.selectedDayIndex--; } } }, async fetchNewWeek(startDate) { if (this.$cant('view', 'entry')) { return; } this.showLoader = true; let url = `timesheet/timesheet-calender/get-week?start_date=${startDate}`, getFilterDataAsParameters = this.objectAsQueryString(this.getParametersList()); if (getFilterDataAsParameters) { url += `&${getFilterDataAsParameters}`; } let response = await this.$axios .get(url) .then(({data}) => data.data) .finally(() => this.showLoader = false) this.days = response.days; this.weekDays = response.weekDays.week; this.prev_week = response.weekDays.prev_week; this.next_week = response.weekDays.next_week; this.start_week = response.weekDays.start_week; this.end_week = response.weekDays.end_week; this.weekTotalTime = response.week_total_time; this.weekTotalBillableTime = response.week_total_evaluation_time; }, getFilterDataAsParameters() { if (!this.filterForm.initialized) { return } this.appendAllFiltersToURLParameters(); }, async fetchWeekAndSelectDate(date) { if (this.$cant('view', 'entry')) { return; } await this.fetchNewWeek(date) this.selectDay(date); }, hasFiltersMethod() { return this.hasFilters; }, updateSelectedDayIndex(index){ this.selectedDayIndex = index; } }, computed: { evaluationEnabled() { return this.$store.getters.settings('evaluation_enabled'); }, membersWithoutEntries() { if (!this.days.length) { return []; } if (this.selectedDayIndex == null) { return []; } return this.days[this.selectedDayIndex].members_without_entries || []; }, getWeekDays() { return this.weekDays; }, hasFilters() { for (let f of ['user_id', 'project_id', 'client_id']) { if (this.filterForm[f] || this.$route.query[f]) { return true; } } return false; }, isTodayOrWeekSelected() { if (!this.days.length) { return true; } if (!this.dayViewEnabled) { return this.todayDate > this.prev_week && this.todayDate < this.next_week; } for (let dayIndex in this.days) { if (this.days[dayIndex].date === this.todayDate) { return this.selectedDayIndex === Number(dayIndex); } } return false; }, selectedDate() { let sDate = this.days[this.selectedDayIndex]; if (sDate) { this.specificDate = sDate.date; } return sDate; }, todayDate() { let today = new Date(), dd = String(today.getDate()).padStart(2, '0'), mm = String(today.getMonth() + 1).padStart(2, '0'), yyyy = today.getFullYear(); return `${yyyy}-${mm}-${dd}`; }, entryModalTitle() { return (this.selectedEntry ? `Update` : `Create`) + ' Entry'; }, selectedUserFilter() { if (this.filterForm.initialized && this.filterForm.user_id) { return this.filterForm.getFormData('users').filter((user) => { return user.value == this.filterForm.user_id; }).shift(); } return ''; }, selectedProjectFilter() { if (!this.filterForm.initialized || !this.filterForm.project_id) { return; } let projects = this.filterForm.getFormData('projects'); let client = ''; for (let i in projects) { if (projects[i].group === true) { client = projects[i].label continue; } if (projects[i].value == this.filterForm.project_id) { return projects[i].label + (client ? `[${client}]` : ''); } } return ''; }, selectedClientFilter() { if (this.filterForm.initialized && this.filterForm.client_id) { return this.filterForm.getFormData('clients')[this.filterForm.client_id]; } return ''; } }, watch: { specificDate(targetDate) { if (!this.selectDay(targetDate)) { this.fetchWeekAndSelectDate(targetDate); } } }, beforeMount() { this.$eventBus.$on('fetch-week-and-select-date', this.fetchWeekAndSelectDate) }, beforeDestroy() { this.$eventBus.$off('fetch-week-and-select-date'); } } </script> <style scoped> .calendar-week { /*background: transparent;*/ } .calendar-week >>> .card-header { padding: .5rem 1rem !important; } .calendar-week >>> .card-body { padding: 0 1rem !important; } .b-skeleton-text { height: 20px; } .sk-h2 { height: 39px; } .sk-h4 { height: 29px; } .b-form-datepicker >>> .btn { padding: 6px 10px !important; } </style>