-
Notifications
You must be signed in to change notification settings - Fork 565
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #8147 from ever-co/develop
Stage
- Loading branch information
Showing
41 changed files
with
670 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
16 changes: 16 additions & 0 deletions
16
packages/desktop-ui-lib/src/lib/recap/monthly/+state/monthly.query.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { Injectable } from '@angular/core'; | ||
import { Query } from '@datorama/akita'; | ||
import { Observable } from 'rxjs'; | ||
import { IDateRangePicker } from '../../shared/features/date-range-picker/date-picker.interface'; | ||
import { IMonthlyRecapState, MonthlyRecapStore } from './monthly.store'; | ||
|
||
@Injectable({ providedIn: 'root' }) | ||
export class MonthlyRecapQuery extends Query<IMonthlyRecapState> { | ||
public readonly range$: Observable<IDateRangePicker> = this.select((state) => state.range); | ||
public readonly state$: Observable<IMonthlyRecapState> = this.select(); | ||
public readonly isLoading$: Observable<boolean> = this.selectLoading(); | ||
|
||
constructor(protected store: MonthlyRecapStore) { | ||
super(store); | ||
} | ||
} |
100 changes: 100 additions & 0 deletions
100
packages/desktop-ui-lib/src/lib/recap/monthly/+state/monthly.service.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import { Injectable } from '@angular/core'; | ||
import { ICountsStatistics, IGetCountsStatistics, IGetTimeLogInput, ReportDayData } from '@gauzy/contracts'; | ||
import { Observable } from 'rxjs'; | ||
import { RequestQuery } from '../../+state/request/request.query'; | ||
import { Store, TimeTrackerDateManager, ToastrNotificationService } from '../../../services'; | ||
import { TimesheetService, TimesheetStatisticsService } from '../../services/timesheet'; | ||
import { IDateRangePicker } from '../../shared/features/date-range-picker/date-picker.interface'; | ||
import { MonthlyRecapQuery } from './monthly.query'; | ||
import { IMonthlyRecapState, MonthlyRecapStore } from './monthly.store'; | ||
|
||
@Injectable({ | ||
providedIn: 'root' | ||
}) | ||
export class MonthlyRecapService { | ||
constructor( | ||
private readonly timesheetStatisticsService: TimesheetStatisticsService, | ||
private readonly timesheetService: TimesheetService, | ||
private readonly notificationService: ToastrNotificationService, | ||
private readonly monthlyQuery: MonthlyRecapQuery, | ||
private readonly monthlyStore: MonthlyRecapStore, | ||
private readonly requestQuery: RequestQuery, | ||
private readonly store: Store | ||
) {} | ||
|
||
public update(state: Partial<IMonthlyRecapState>) { | ||
this.monthlyStore.update(state); | ||
} | ||
|
||
public get state$(): Observable<IMonthlyRecapState> { | ||
return this.monthlyQuery.state$; | ||
} | ||
|
||
public get range$(): Observable<IDateRangePicker> { | ||
return this.monthlyQuery.range$; | ||
} | ||
|
||
public get range(): IDateRangePicker { | ||
return this.monthlyQuery.getValue().range; | ||
} | ||
|
||
public get count(): ICountsStatistics { | ||
return this.monthlyQuery.getValue().count; | ||
} | ||
|
||
public get monthlyActivities(): ReportDayData[] { | ||
return this.monthlyQuery.getValue().monthlyActivities; | ||
} | ||
|
||
public async getMonthActivities(): Promise<void> { | ||
try { | ||
this.monthlyStore.setLoading(true); | ||
const { organizationId, tenantId, user } = this.store; | ||
const employeeIds = [user.employee.id]; | ||
const timeZone = user.timeZone; | ||
const timeFormat = user.timeFormat; | ||
const request: IGetTimeLogInput = { | ||
...this.requestQuery.request, | ||
...this.range, | ||
organizationId, | ||
employeeIds, | ||
tenantId, | ||
timeFormat, | ||
timeZone, | ||
unitOfTime: 'month' | ||
}; | ||
const monthlyActivities = await this.timesheetService.getWeeklyReportChart(request); | ||
this.monthlyStore.update({ monthlyActivities }); | ||
} catch (error) { | ||
this.notificationService.error(error.message || 'An error occurred while fetching tasks.'); | ||
this.monthlyStore.setError(error); | ||
} finally { | ||
this.monthlyStore.setLoading(false); | ||
} | ||
} | ||
|
||
public async getCounts(): Promise<void> { | ||
try { | ||
this.monthlyStore.setLoading(true); | ||
const { organizationId, tenantId, user } = this.store; | ||
const employeeIds = [user.employee.id]; | ||
const request: IGetCountsStatistics = { | ||
...this.requestQuery.request, | ||
...this.range, | ||
organizationId, | ||
employeeIds, | ||
onlyMe: true, | ||
tenantId, | ||
todayStart: TimeTrackerDateManager.startToday, | ||
todayEnd: TimeTrackerDateManager.endToday | ||
}; | ||
const count = await this.timesheetStatisticsService.getCounts(request); | ||
this.monthlyStore.update({ count }); | ||
} catch (error) { | ||
this.notificationService.error(error.message || 'An error occurred while fetching tasks.'); | ||
this.monthlyStore.setError(error); | ||
} finally { | ||
this.monthlyStore.setLoading(false); | ||
} | ||
} | ||
} |
37 changes: 37 additions & 0 deletions
37
packages/desktop-ui-lib/src/lib/recap/monthly/+state/monthly.store.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { Injectable } from '@angular/core'; | ||
import { Store, StoreConfig } from '@datorama/akita'; | ||
import { ICountsStatistics, ReportDayData } from '@gauzy/contracts'; | ||
import { TimeTrackerDateManager } from '../../../services'; | ||
import { IDateRangePicker } from '../../shared/features/date-range-picker/date-picker.interface'; | ||
|
||
export interface IMonthlyRecapState { | ||
count: ICountsStatistics; | ||
range: IDateRangePicker; | ||
monthlyActivities: ReportDayData[]; | ||
} | ||
|
||
export function createInitialState(): IMonthlyRecapState { | ||
return { | ||
monthlyActivities: [], | ||
range: { | ||
startDate: TimeTrackerDateManager.startCurrentMonth, | ||
endDate: TimeTrackerDateManager.endCurrentMonth | ||
}, | ||
count: { | ||
projectsCount: 0, | ||
employeesCount: 0, | ||
weekActivities: 0, | ||
weekDuration: 0, | ||
todayActivities: 0, | ||
todayDuration: 0 | ||
} | ||
}; | ||
} | ||
|
||
@StoreConfig({ name: '_monthlyRecap' }) | ||
@Injectable({ providedIn: 'root' }) | ||
export class MonthlyRecapStore extends Store<IMonthlyRecapState> { | ||
constructor() { | ||
super(createInitialState()); | ||
} | ||
} |
2 changes: 2 additions & 0 deletions
2
...op-ui-lib/src/lib/recap/monthly/features/monthly-calendar/monthly-calendar.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
<ngx-date-range-picker unitOfTime="month" [dates]="selectedDateRange$ | async" [isLockDatePicker]="true" [arrows]="true" | ||
[isSingleDatePicker]="false" (rangeChanges)="onRangeChange($event)" class="medium full"></ngx-date-range-picker> |
Empty file.
22 changes: 22 additions & 0 deletions
22
...ktop-ui-lib/src/lib/recap/monthly/features/monthly-calendar/monthly-calendar.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { ChangeDetectionStrategy, Component } from '@angular/core'; | ||
import { map } from 'rxjs'; | ||
import { MonthlyRecapService } from '../../+state/monthly.service'; | ||
import { IDateRangePicker } from '../../../shared/features/date-range-picker/date-picker.interface'; | ||
|
||
@Component({ | ||
selector: 'ngx-monthly-calendar', | ||
templateUrl: './monthly-calendar.component.html', | ||
styleUrls: ['./monthly-calendar.component.scss'], | ||
changeDetection: ChangeDetectionStrategy.OnPush | ||
}) | ||
export class MonthlyCalendarComponent { | ||
constructor(private readonly monthlyRecapService: MonthlyRecapService) {} | ||
|
||
public get selectedDateRange$() { | ||
return this.monthlyRecapService.state$.pipe(map((state) => state.range)); | ||
} | ||
|
||
public onRangeChange(range: IDateRangePicker) { | ||
this.monthlyRecapService.update({ range }); | ||
} | ||
} |
22 changes: 22 additions & 0 deletions
22
...op-ui-lib/src/lib/recap/monthly/features/monthly-progress/monthly-progress.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<div class="week-wrapper"> | ||
<div> | ||
<div class="week-range"> | ||
{{(range$ | async).startDate | dateTime : 'MMMM'}} | ||
</div> | ||
</div> | ||
<ng-container *ngIf="(monthlyActivities$ | async)?.sum; else noMonthlyData"> | ||
<div class="weeks row" *ngFor="let month of monthWeekdays"> | ||
<div class="week col">{{ getWeekRange(month.week) }}</div> | ||
<ngx-progress-status class="report-progress col-6" | ||
[percentage]="((sumPerWeek(month, monthlyActivities$ | async) * 100)) / ((monthlyActivities$ | async)?.sum ?? 1)"></ngx-progress-status> | ||
<div class="col hours text-end">{{ sumPerWeek(month, monthlyActivities$ | async) | durationFormat : 'h[h] | ||
m[m] | ||
s[s]':{trim:'both'} }} | ||
</div> | ||
</div> | ||
</ng-container> | ||
</div> | ||
|
||
<ng-template #noMonthlyData> | ||
<ngx-no-data-message [message]="'TIMER_TRACKER.RECAP.NO_MONTHLY_ACTIVITY' | translate"></ngx-no-data-message> | ||
</ng-template> |
41 changes: 41 additions & 0 deletions
41
...op-ui-lib/src/lib/recap/monthly/features/monthly-progress/monthly-progress.component.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
@import 'report'; | ||
|
||
.activity { | ||
display: flex; | ||
align-items: center; | ||
justify-content: space-between; | ||
gap: 1rem; | ||
} | ||
|
||
.weeks { | ||
display: flex; | ||
align-items: center; | ||
justify-content: space-between; | ||
border-top: 0.5px solid var(--select-filled-control-disabled-text-color); | ||
padding-top: 1rem; | ||
color: var(--gauzy-text-color-1); | ||
font-weight: 500; | ||
|
||
.week, | ||
.hours { | ||
text-wrap: nowrap; | ||
} | ||
} | ||
|
||
.week-range { | ||
font-size: 16px; | ||
font-weight: 400; | ||
line-height: 16px; | ||
letter-spacing: -0.009em; | ||
color: var(--gauzy-text-color-2); | ||
} | ||
|
||
.week-wrapper { | ||
display: flex; | ||
flex-direction: column; | ||
gap: 1rem; | ||
padding: 1rem; | ||
border-radius: var(--border-radius); | ||
background-color: var(--gauzy-card-3); | ||
height: 100%; | ||
} |
47 changes: 47 additions & 0 deletions
47
...ktop-ui-lib/src/lib/recap/monthly/features/monthly-progress/monthly-progress.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import { ChangeDetectionStrategy, Component } from '@angular/core'; | ||
import { ReportDayData } from '@gauzy/contracts'; | ||
import { map, tap } from 'rxjs'; | ||
import { MonthlyRecapService } from '../../+state/monthly.service'; | ||
import { updateMonthWeeks, weekDateRange } from '../../../shared/features/date-range-picker'; | ||
|
||
export interface IMonthWeekdays { | ||
week: string; | ||
days: string[]; | ||
} | ||
|
||
@Component({ | ||
selector: 'ngx-monthly-progress', | ||
templateUrl: './monthly-progress.component.html', | ||
styleUrls: ['./monthly-progress.component.scss'], | ||
changeDetection: ChangeDetectionStrategy.OnPush | ||
}) | ||
export class MonthlyProgressComponent { | ||
public monthWeekdays: IMonthWeekdays[] = []; | ||
|
||
constructor(private readonly monthlyRecapService: MonthlyRecapService) {} | ||
|
||
public get monthlyActivities$() { | ||
return this.monthlyRecapService.state$.pipe(map((state) => state.monthlyActivities[0])); | ||
} | ||
|
||
public get range$() { | ||
return this.monthlyRecapService.state$.pipe( | ||
tap((state) => { | ||
const { monthWeekdays } = updateMonthWeeks(state.range); | ||
this.monthWeekdays = monthWeekdays; | ||
}), | ||
map((state) => state.range) | ||
); | ||
} | ||
|
||
public sumPerWeek(month: IMonthWeekdays, data: ReportDayData): void { | ||
return month.days.reduce((acc, curr) => { | ||
const sum = data?.dates?.[curr]?.sum || 0; | ||
return acc + sum; | ||
}, 0); | ||
} | ||
|
||
public getWeekRange(number: number): string { | ||
return weekDateRange(number); | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
.../desktop-ui-lib/src/lib/recap/monthly/features/monthly-recap/monthly-recap.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
<nb-card class="main-report-wrapper"> | ||
<nb-card-header class="p-3 main-report-header"> | ||
<ng-container *ngIf="isLoading$ | async"> | ||
<nb-icon class="loader" size="medium" icon="loader-outline"></nb-icon> | ||
</ng-container> | ||
<div class="tools ml-auto"> | ||
<ngx-monthly-calendar></ngx-monthly-calendar> | ||
<ngx-filter></ngx-filter> | ||
</div> | ||
</nb-card-header> | ||
<nb-card-body class="main-report-body recap-monthly"> | ||
<ngx-monthly-statistic></ngx-monthly-statistic> | ||
<ngx-monthly-progress class="h-100"></ngx-monthly-progress> | ||
</nb-card-body> | ||
</nb-card> |
11 changes: 11 additions & 0 deletions
11
.../desktop-ui-lib/src/lib/recap/monthly/features/monthly-recap/monthly-recap.component.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
@import '../../../features/recap/recap.component.scss'; | ||
|
||
.recap-monthly { | ||
display: flex; | ||
flex-direction: column; | ||
gap: 1rem; | ||
background-color: var(--gauzy-card-2); | ||
padding: 1rem; | ||
border-radius: var(--border-radius); | ||
height: 100%; | ||
} |
Oops, something went wrong.