first commit
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"navigation": {
|
||||
"screenshots": "Screenshots"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"navigation": {
|
||||
"screenshots": "Скриншоты"
|
||||
}
|
||||
}
|
||||
31
resources/frontend/core/modules/Screenshots/module.init.js
Normal file
31
resources/frontend/core/modules/Screenshots/module.init.js
Normal file
@@ -0,0 +1,31 @@
|
||||
export const ModuleConfig = {
|
||||
routerPrefix: 'screenshots',
|
||||
loadOrder: 20,
|
||||
moduleName: 'Screenshots',
|
||||
};
|
||||
|
||||
export function init(context) {
|
||||
context.addRoute({
|
||||
path: '/screenshots',
|
||||
name: 'screenshots',
|
||||
component: () => import(/* webpackChunkName: "screenshots" */ './views/Screenshots.vue'),
|
||||
meta: {
|
||||
auth: true,
|
||||
},
|
||||
});
|
||||
|
||||
context.addNavbarEntry({
|
||||
label: 'navigation.screenshots',
|
||||
to: {
|
||||
name: 'screenshots',
|
||||
},
|
||||
displayCondition: store => store.getters['screenshots/enabled'],
|
||||
});
|
||||
|
||||
context.addLocalizationData({
|
||||
en: require('./locales/en'),
|
||||
ru: require('./locales/ru'),
|
||||
});
|
||||
|
||||
return context;
|
||||
}
|
||||
@@ -0,0 +1,308 @@
|
||||
<template>
|
||||
<div class="screenshots">
|
||||
<h1 class="page-title">{{ $t('navigation.screenshots') }}</h1>
|
||||
<div class="controls-row">
|
||||
<div class="controls-row__item">
|
||||
<Calendar :sessionStorageKey="sessionStorageKey" @change="onCalendarChange" />
|
||||
</div>
|
||||
<div class="controls-row__item">
|
||||
<UserSelect @change="onUsersChange" />
|
||||
</div>
|
||||
<div class="controls-row__item">
|
||||
<ProjectSelect @change="onProjectsChange" />
|
||||
</div>
|
||||
|
||||
<div class="controls-row__item">
|
||||
<TimezonePicker :value="timezone" @onTimezoneChange="onTimezoneChange" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="at-container">
|
||||
<div class="at-container__inner">
|
||||
<template v-if="this.intervals.length > 0">
|
||||
<div class="row">
|
||||
<div v-for="interval in this.intervals" :key="interval.id" class="col-4 screenshots__card">
|
||||
<Screenshot
|
||||
class="screenshot"
|
||||
:disableModal="true"
|
||||
:interval="interval"
|
||||
:task="interval.task"
|
||||
:user="modal.user"
|
||||
:timezone="timezone"
|
||||
@click="showImage(interval)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ScreenshotModal
|
||||
:project="modal.project"
|
||||
:interval="modal.interval"
|
||||
:show="modal.show"
|
||||
:showNavigation="true"
|
||||
:task="modal.task"
|
||||
:user="modal.user"
|
||||
@close="onHide"
|
||||
@remove="removeScreenshot"
|
||||
@showNext="showNext"
|
||||
@showPrevious="showPrevious"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<div v-else class="no-data">
|
||||
<span>{{ $t('message.no_data') }}</span>
|
||||
</div>
|
||||
<preloader v-if="isDataLoading"></preloader>
|
||||
</div>
|
||||
</div>
|
||||
<div class="screenshots__pagination">
|
||||
<at-pagination
|
||||
:total="intervalsTotal"
|
||||
:current="page"
|
||||
:page-size="limit"
|
||||
@page-change="loadPage"
|
||||
></at-pagination>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapMutations } from 'vuex';
|
||||
import Calendar from '@/components/Calendar';
|
||||
import Screenshot from '@/components/Screenshot';
|
||||
import ScreenshotModal from '@/components/ScreenshotModal';
|
||||
import UserSelect from '@/components/UserSelect';
|
||||
import ProjectService from '@/services/resource/project.service';
|
||||
import TimeIntervalService from '@/services/resource/time-interval.service';
|
||||
import { getStartOfDayInTimezone, getEndOfDayInTimezone, getDateWithTimezoneDifference } from '@/utils/time';
|
||||
import Preloader from '@/components/Preloader';
|
||||
import ProjectSelect from '@/components/ProjectSelect';
|
||||
import TimezonePicker from '@/components/TimezonePicker';
|
||||
|
||||
export default {
|
||||
name: 'Screenshots',
|
||||
components: {
|
||||
Calendar,
|
||||
Screenshot,
|
||||
ScreenshotModal,
|
||||
UserSelect,
|
||||
Preloader,
|
||||
ProjectSelect,
|
||||
TimezonePicker,
|
||||
},
|
||||
data() {
|
||||
const limit = 15;
|
||||
const localStorageKey = 'user-select.users';
|
||||
const sessionStorageKey = 'amazingcat.session.storage.screenshots';
|
||||
|
||||
return {
|
||||
intervals: [],
|
||||
userIDs: null,
|
||||
projectsList: [],
|
||||
datepickerDateStart: '',
|
||||
datepickerDateEnd: '',
|
||||
projectService: new ProjectService(),
|
||||
intervalService: new TimeIntervalService(),
|
||||
modal: {
|
||||
show: false,
|
||||
},
|
||||
limit: limit,
|
||||
page: 1,
|
||||
intervalsTotal: 0,
|
||||
localStorageKey: localStorageKey,
|
||||
sessionStorageKey: sessionStorageKey,
|
||||
isDataLoading: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters('dashboard', ['timezone']),
|
||||
...mapGetters('timeline', ['service', 'users']),
|
||||
...mapGetters('user', ['user', 'companyData']),
|
||||
},
|
||||
watch: {
|
||||
companyData() {
|
||||
this.getScreenshots();
|
||||
},
|
||||
timezone() {
|
||||
this.getScreenshots();
|
||||
},
|
||||
},
|
||||
async created() {
|
||||
window.addEventListener('keydown', this.onKeyDown);
|
||||
try {
|
||||
await this.getScreenshots();
|
||||
} catch ({ response }) {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
console.log(response ? response : 'request to screenshots is canceled');
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('keydown', this.onKeyDown);
|
||||
},
|
||||
methods: {
|
||||
getStartOfDayInTimezone,
|
||||
getEndOfDayInTimezone,
|
||||
...mapMutations({
|
||||
setTimezone: 'dashboard/setTimezone',
|
||||
}),
|
||||
onTimezoneChange(timezone) {
|
||||
this.setTimezone(timezone);
|
||||
},
|
||||
onHide() {
|
||||
this.modal.show = false;
|
||||
},
|
||||
onKeyDown(e) {
|
||||
if (e.key === 'ArrowLeft') {
|
||||
e.preventDefault();
|
||||
this.showPrevious();
|
||||
} else if (e.key === 'ArrowRight') {
|
||||
e.preventDefault();
|
||||
this.showNext();
|
||||
}
|
||||
},
|
||||
showPrevious() {
|
||||
const currentIndex = this.intervals.findIndex(x => x.id === this.modal.interval.id);
|
||||
|
||||
if (currentIndex !== 0) {
|
||||
this.modal.interval = this.intervals[currentIndex - 1];
|
||||
}
|
||||
},
|
||||
showNext() {
|
||||
const currentIndex = this.intervals.findIndex(x => x.id === this.modal.interval.id);
|
||||
|
||||
if (currentIndex + 1 !== this.intervals.length) {
|
||||
this.modal.interval = this.intervals[currentIndex + 1];
|
||||
}
|
||||
},
|
||||
showImage(interval) {
|
||||
this.modal = {
|
||||
interval,
|
||||
user: interval.user,
|
||||
task: interval.task,
|
||||
project: interval.task?.project,
|
||||
show: true,
|
||||
};
|
||||
},
|
||||
onUsersChange(userIDs) {
|
||||
this.userIDs = userIDs;
|
||||
if (this._isMounted) {
|
||||
this.getScreenshots();
|
||||
}
|
||||
},
|
||||
onProjectsChange(projectIDs) {
|
||||
this.projectsList = projectIDs;
|
||||
if (this._isMounted) {
|
||||
this.getScreenshots();
|
||||
}
|
||||
},
|
||||
onCalendarChange({ start, end }) {
|
||||
this.datepickerDateStart = start;
|
||||
this.datepickerDateEnd = end;
|
||||
this.getScreenshots();
|
||||
},
|
||||
async getScreenshots() {
|
||||
if (
|
||||
this.userIDs === 'undefined' ||
|
||||
!this.datepickerDateStart ||
|
||||
!this.datepickerDateEnd ||
|
||||
!this.timezone ||
|
||||
!this.companyData.timezone
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isDataLoading = true;
|
||||
|
||||
const startAt = getDateWithTimezoneDifference(
|
||||
this.datepickerDateStart,
|
||||
this.companyData.timezone,
|
||||
this.timezone,
|
||||
);
|
||||
const endAt = getDateWithTimezoneDifference(
|
||||
this.datepickerDateEnd,
|
||||
this.companyData.timezone,
|
||||
this.timezone,
|
||||
false,
|
||||
);
|
||||
|
||||
try {
|
||||
const { data } = await this.intervalService.getAll({
|
||||
where: {
|
||||
user_id: ['in', this.userIDs],
|
||||
'task.project_id': ['in', this.projectsList],
|
||||
start_at: ['between', [startAt, endAt]],
|
||||
},
|
||||
page: this.page,
|
||||
with: ['task', 'task.project', 'user'],
|
||||
});
|
||||
this.intervalsTotal = data.pagination.total;
|
||||
this.intervals = data.data;
|
||||
} catch ({ response }) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isDataLoading = false;
|
||||
},
|
||||
async removeScreenshot(id) {
|
||||
try {
|
||||
await this.intervalService.deleteItem(id);
|
||||
this.$Notify({
|
||||
type: 'success',
|
||||
title: this.$t('notification.screenshot.delete.success.title'),
|
||||
message: this.$t('notification.screenshot.delete.success.message'),
|
||||
});
|
||||
|
||||
this.intervals = this.intervals.filter(interval => interval.id !== id);
|
||||
this.onHide();
|
||||
} catch (e) {
|
||||
this.$Notify({
|
||||
type: 'error',
|
||||
title: this.$t('notification.screenshot.delete.error.title'),
|
||||
message: this.$t('notification.screenshot.delete.error.message'),
|
||||
});
|
||||
}
|
||||
},
|
||||
async loadPage(page) {
|
||||
this.page = page;
|
||||
await this.getScreenshots();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.at-container {
|
||||
overflow: hidden;
|
||||
margin-bottom: $layout-01;
|
||||
|
||||
&__inner {
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
|
||||
.screenshots {
|
||||
&__card {
|
||||
margin-bottom: $layout-02;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&__pagination {
|
||||
flex-direction: row-reverse;
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
.screenshot::v-deep {
|
||||
.screenshot-image {
|
||||
img {
|
||||
height: 150px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.no-data {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user