Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stage #8147

Merged
merged 6 commits into from
Sep 3, 2024
Merged

Stage #8147

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions .github/workflows/desktop-timer-app-stage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,22 @@
- name: Print environment variable names
run: |
echo "Environment Variable Names:"
printenv | cut -d= -f1

Check warning on line 191 in .github/workflows/desktop-timer-app-stage.yml

View workflow job for this annotation

GitHub Actions / Cspell

Unknown word (printenv)

- name: Print environment variables that are large
shell: powershell
run: |
# List all environment variables
foreach ($envVar in [System.Environment]::GetEnvironmentVariables().Keys) {
# Get the value of the environment variable
$value = [System.Environment]::GetEnvironmentVariable($envVar)

# Check if the value length is greater than 100 bytes
if ([Text.Encoding]::UTF8.GetByteCount($value) -gt 100) {
Write-Output $envVar
}
}

- name: Build Desktop Timer App
run: 'yarn build:desktop-timer:windows:release:gh'
env:
Expand All @@ -216,10 +230,10 @@
ChocolateyInstall: ''
ChromeWebDriver: ''
COBERTURA_HOME: ''
# COMPUTERNAME: ''

Check warning on line 233 in .github/workflows/desktop-timer-app-stage.yml

View workflow job for this annotation

GitHub Actions / Cspell

Unknown word (COMPUTERNAME)
# COMSPEC: ''

Check warning on line 234 in .github/workflows/desktop-timer-app-stage.yml

View workflow job for this annotation

GitHub Actions / Cspell

Unknown word (COMSPEC)
# CONDA: ''
DEPLOYMENT_BASEPATH: ''

Check warning on line 236 in .github/workflows/desktop-timer-app-stage.yml

View workflow job for this annotation

GitHub Actions / Cspell

Unknown word (BASEPATH)
SBT_HOME: ''
SELENIUM_JAR_PATH: ''
STATS_BLT: ''
Expand Down Expand Up @@ -281,8 +295,8 @@
GOROOT_1_21_X64: ''
GOROOT_1_22_X64: ''
GRADLE_HOME: ''
# HOMEDRIVE: ''

Check warning on line 298 in .github/workflows/desktop-timer-app-stage.yml

View workflow job for this annotation

GitHub Actions / Cspell

Unknown word (HOMEDRIVE)
# HOMEPATH: ''
HOMEPATH: ''

Check warning on line 299 in .github/workflows/desktop-timer-app-stage.yml

View workflow job for this annotation

GitHub Actions / Cspell

Unknown word (HOMEPATH)
IEWebDriver: ''
ImageOS: ''
ImageVersion: ''
Expand All @@ -291,8 +305,8 @@
JAVA_HOME_17_X64: ''
JAVA_HOME_21_X64: ''
JAVA_HOME_8_X64: ''
# LOCALAPPDATA: ''

Check warning on line 308 in .github/workflows/desktop-timer-app-stage.yml

View workflow job for this annotation

GitHub Actions / Cspell

Unknown word (LOCALAPPDATA)
# LOGONSERVER: ''

Check warning on line 309 in .github/workflows/desktop-timer-app-stage.yml

View workflow job for this annotation

GitHub Actions / Cspell

Unknown word (LOGONSERVER)
M2: ''
M2_REPO: ''
MAVEN_OPTS: ''
Expand All @@ -300,7 +314,7 @@
# npm_config_prefix: ''
NUMBER_OF_PROCESSORS: ''
OS: ''
# PATHEXT: ''
PATHEXT: ''

Check warning on line 317 in .github/workflows/desktop-timer-app-stage.yml

View workflow job for this annotation

GitHub Actions / Cspell

Unknown word (PATHEXT)
PERFLOG_LOCATION_SETTING: ''
PGBIN: ''
PGDATA: ''
Expand All @@ -327,7 +341,7 @@
GeckoWebDriver: ''
GHCUP_INSTALL_BASE_PREFIX: ''
GHCUP_MSYS2: ''
RTOOLS44_HOME: ''

Check warning on line 344 in .github/workflows/desktop-timer-app-stage.yml

View workflow job for this annotation

GitHub Actions / Cspell

Unknown word (RTOOLS)
RUNNER_ARCH: ''
RUNNER_ENVIRONMENT: ''
RUNNER_NAME: ''
Expand Down
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);
}
}
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);
}
}
}
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());
}
}
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>
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 });
}
}
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>
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%;
}
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);
}
}
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>
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%;
}
Loading
Loading