import { AfterViewInit , ChangeDetectorRef , Component , OnDestroy , OnInit } from '@angular/core' ; import { UntilDestroy , untilDestroyed } from '@ngneat/until-destroy' ; import { IOrganization , IGetTimeSlotStatistics , IGetActivitiesStatistics , IGetProjectsStatistics , IGetTasksStatistics , IGetMembersStatistics , PermissionsEnum , IGetCountsStatistics , ICountsStatistics , IMembersStatistics , IActivitiesStatistics , ITimeSlotStatistics , IProjectsStatistics , ITasksStatistics , IGetManualTimesStatistics , IManualTimesStatistics , IUser , ISelectedDateRange } from '@gauzy/contracts' ; import { combineLatest , Subject , Subscription , timer } from 'rxjs' ; import { debounceTime , filter , tap } from 'rxjs/operators' ; import { indexBy , range , reduce } from 'underscore' ; import { distinctUntilChange , progressStatus , toUTC } from '@gauzy/common-angular' ; import * as moment from 'moment' ; import { NgxPermissionsService } from 'ngx-permissions' ; import { TranslateService } from '@ngx-translate/core' ; import { TimesheetStatisticsService } from '../../../@shared/timesheet/timesheet-statistics.service' ; import { Store } from '../../../@core/services' ; import { GalleryService } from '../../../@shared/gallery' ; import { TranslationBaseComponent } from '../../../@shared/language-base' ; @ UntilDestroy ( { checkProperties : true } ) @ Component ( { selector : 'ga-time-tracking' , templateUrl : './time-tracking.component.html' , styleUrls : [ './time-tracking.component.scss' ] } ) export class TimeTrackingComponent extends TranslationBaseComponent implements AfterViewInit , OnInit , OnDestroy { timeSlotEmployees : ITimeSlotStatistics [ ] = [ ] ; activities : IActivitiesStatistics [ ] = [ ] ; projects : IProjectsStatistics [ ] = [ ] ; tasks : ITasksStatistics [ ] = [ ] ; members : IMembersStatistics [ ] = [ ] ; manualTimes : IManualTimesStatistics [ ] = [ ] ; counts : ICountsStatistics ; public organization : IOrganization ; logs$ : Subject < any > = new Subject ( ) ; timeSlotLoading = true ; activitiesLoading = true ; projectsLoading = true ; tasksLoading = true ; memberLoading = true ; countsLoading = true ; manualTimeLoading = true ; PermissionsEnum = PermissionsEnum ; progressStatus = progressStatus ; employeeId : string = null ; projectId : string = null ; tenantId : string = null ; organizationId : string = null ; private autoRefresh$ : Subscription ; autoRefresh : boolean = true ; today : Date = new Date ( moment ( ) . format ( 'YYYY-MM-DD HH:mm:ss' ) ) ; private _selectedDateRange : ISelectedDateRange = { start : moment ( ) . startOf ( 'week' ) . toDate ( ) , end : moment ( ) . endOf ( 'week' ) . toDate ( ) , isCustomDate : false } ; get selectedDateRange ( ) : ISelectedDateRange { return this . _selectedDateRange ; } set selectedDateRange ( range ) { if ( range ) { if ( ! range . hasOwnProperty ( 'isCustomDate' ) ) { range . isCustomDate = true ; } /**
             * Check, if start date is Greater Than end date
             */ if ( moment ( range . start ) . isAfter ( range . end ) ) { this . _selectedDateRange = { start : range . end , end : range . start , isCustomDate : range . isCustomDate } ; } else { this . _selectedDateRange = range ; } if ( range . start && range . end ) { this . logs$ . next ( true ) ; } } } constructor ( private readonly timesheetStatisticsService , private readonly store , private readonly galleryService , private readonly ngxPermissionsService , public readonly translateService , private readonly changeRef ) { super ( translateService ) ; } async ngOnInit ( ) { this . store . user$ . pipe ( filter ( ( user ) => ! ! user ) , distinctUntilChange ( ) , tap ( ( user ) => ( this . tenantId = user . tenantId ) ) , untilDestroyed ( this ) ) . subscribe ( ) ; this . logs$ . pipe ( debounceTime ( 500 ) , tap ( ( ) => this . getStatistics ( ) ) , untilDestroyed ( this ) ) . subscribe ( ) ; } ngAfterViewInit ( ) { const storeOrganization$ = this . store . selectedOrganization$ ; const storeEmployee$ = this . store . selectedEmployee$ ; const storeProject$ = this . store . selectedProject$ ; combineLatest ( [ storeOrganization$ , storeEmployee$ , storeProject$ ] ) . pipe ( filter ( ( [ organization ] ) => ! ! organization ) , distinctUntilChange ( ) , tap ( ( [ organization , employee , project ] ) => { this . organization = organization ; this . organizationId = organization . id ; this . employeeId = employee ? employee . id : null ; this . projectId = project ? project . id : null ; this . logs$ . next ( true ) ; } ) , tap ( ( ) => this . setAutoRefresh ( true ) ) , untilDestroyed ( this ) ) . subscribe ( ) ; } ngAfterViewChecked ( ) { this . changeRef . detectChanges ( ) ; } getStatistics ( ) { if ( ! this . organization ) { return ; } this . getCounts ( ) ; this . getTimeSlots ( ) ; this . getActivities ( ) ; this . getProjects ( ) ; this . getTasks ( ) ; this . getManualTimes ( ) ; this . getMembers ( ) ; } setAutoRefresh ( value ) { if ( this . autoRefresh$ ) { this . autoRefresh$ . unsubscribe ( ) ; } if ( value ) { this . autoRefresh$ = timer ( 0 , 60000 * 2 ) . pipe ( filter ( ( timer ) => ! ! timer ) , tap ( ( ) => this . logs$ . next ( true ) ) , untilDestroyed ( this ) ) . subscribe ( ) ; } } getTimeSlots ( ) { const { tenantId , organizationId , employeeId , projectId } = this ; const timeSlotRequest = { tenantId , organizationId , employeeId , projectId , startDate : toUTC ( this . selectedDateRange . start ) . format ( 'YYYY-MM-DD HH:mm' ) , endDate : toUTC ( this . selectedDateRange . end ) . format ( 'YYYY-MM-DD HH:mm' ) } ; this . timeSlotLoading = true ; this . timesheetStatisticsService . getTimeSlots ( timeSlotRequest ) . then ( ( resp ) => { this . timeSlotEmployees = resp ; } ) . finally ( ( ) => { this . timeSlotLoading = false ; } ) ; } onDelete ( ) { this . logs$ . next ( true ) ; } getCounts ( ) { const { tenantId , organizationId , employeeId , projectId , selectedDateRange } = this ; const request = { tenantId , organizationId , employeeId , projectId , startDate : toUTC ( selectedDateRange . start ) . format ( 'YYYY-MM-DD HH:mm' ) , endDate : toUTC ( selectedDateRange . end ) . format ( 'YYYY-MM-DD HH:mm' ) } ; this . countsLoading = true ; this . timesheetStatisticsService . getCounts ( request ) . then ( ( resp ) => { this . counts = resp ; } ) . finally ( ( ) => { this . countsLoading = false ; } ) ; } getActivities ( ) { const { tenantId , organizationId , employeeId , projectId , selectedDateRange } = this ; const activityRequest = { tenantId , organizationId , employeeId , projectId , startDate : toUTC ( selectedDateRange . start ) . format ( 'YYYY-MM-DD HH:mm' ) , endDate : toUTC ( selectedDateRange . end ) . format ( 'YYYY-MM-DD HH:mm' ) } ; this . activitiesLoading = true ; this . timesheetStatisticsService . getActivities ( activityRequest ) . then ( ( resp ) => { const sum = reduce ( resp , ( memo , activity ) => memo + parseInt ( activity . duration + '' , 10 ) , 0 ) ; this . activities = resp . map ( ( activity ) => { activity . durationPercentage = ( activity . duration * 100 ) / sum ; return activity ; } ) ; } ) . finally ( ( ) => { this . activitiesLoading = false ; } ) ; } getProjects ( ) { const { tenantId , organizationId , employeeId , projectId , selectedDateRange } = this ; const projectRequest = { tenantId , organizationId , employeeId , projectId , startDate : toUTC ( selectedDateRange . start ) . format ( 'YYYY-MM-DD HH:mm' ) , endDate : toUTC ( selectedDateRange . end ) . format ( 'YYYY-MM-DD HH:mm' ) } ; this . projectsLoading = true ; this . timesheetStatisticsService . getProjects ( projectRequest ) . then ( ( resp ) => { this . projects = resp ; } ) . finally ( ( ) => { this . projectsLoading = false ; } ) ; } getTasks ( ) { const { tenantId , organizationId , employeeId , projectId , selectedDateRange } = this ; const taskRequest = { tenantId , organizationId , employeeId , projectId , startDate : toUTC ( selectedDateRange . start ) . format ( 'YYYY-MM-DD HH:mm' ) , endDate : toUTC ( selectedDateRange . end ) . format ( 'YYYY-MM-DD HH:mm' ) } ; this . tasksLoading = true ; this . timesheetStatisticsService . getTasks ( taskRequest ) . then ( ( resp ) => { this . tasks = resp ; } ) . finally ( ( ) => { this . tasksLoading = false ; } ) ; } getManualTimes ( ) { const { tenantId , organizationId , employeeId , projectId , selectedDateRange } = this ; const request = { tenantId , organizationId , employeeId , projectId , startDate : toUTC ( selectedDateRange . start ) . format ( 'YYYY-MM-DD HH:mm' ) , endDate : toUTC ( selectedDateRange . end ) . format ( 'YYYY-MM-DD HH:mm' ) } ; this . manualTimeLoading = true ; this . timesheetStatisticsService . getManualTimes ( request ) . then ( ( resp ) => { this . manualTimes = resp ; } ) . finally ( ( ) => { this . manualTimeLoading = false ; } ) ; } async getMembers ( ) { if ( ! await this . ngxPermissionsService . hasPermission ( PermissionsEnum . CHANGE_SELECTED_EMPLOYEE ) ) { return ; } const { tenantId , organizationId , employeeId , projectId , selectedDateRange } = this ; const memberRequest = { tenantId , organizationId , employeeId , projectId , startDate : toUTC ( selectedDateRange . start ) . format ( 'YYYY-MM-DD HH:mm' ) , endDate : toUTC ( selectedDateRange . end ) . format ( 'YYYY-MM-DD HH:mm' ) } ; this . memberLoading = true ; this . timesheetStatisticsService . getMembers ( memberRequest ) . then ( ( resp ) => { this . members = resp . map ( ( member ) => { const week = indexBy ( member . weekHours , 'day' ) ; const sum = reduce ( member . weekHours , ( memo , day ) => memo + parseInt ( day . duration + '' , 10 ) , 0 ) ; member . weekHours = range ( 0 , 7 ) . map ( ( day ) => { if ( week [ day ] ) { week [ day ] . duration = ( week [ day ] . duration * 100 ) / sum ; return week [ day ] ; } else { return { day , duration : 0 } ; } } ) ; return member ; } ) ; } ) . finally ( ( ) => { this . memberLoading = false ; } ) ; } ngOnDestroy ( ) { this . galleryService . clearGallery ( ) ; } nextDay ( ) { const startDate = moment ( this . selectedDateRange . end ) . add ( 1 , "week" ) . startOf ( "week" ) . toDate ( ) ; const date = moment ( startDate ) ; if ( date . isAfter ( this . today ) ) { return ; } this . selectedDateRange = { start : startDate , end : moment ( startDate ) . endOf ( "week" ) . toDate ( ) , isCustomDate : false } ; } previousDay ( ) { const startDate = moment ( this . selectedDateRange . end ) . subtract ( 1 , "week" ) . startOf ( "week" ) . toDate ( ) ; this . selectedDateRange = { start : startDate , end : moment ( startDate ) . endOf ( "week" ) . toDate ( ) , isCustomDate : false } ; } todayDate ( ) { this . selectedDateRange = { start : this . today , end : this . today , } ; } }