Skip to content

Commit

Permalink
Feat/weekly recap (#8093)
Browse files Browse the repository at this point in the history
* feat: improve date range picker module

* feat: improve progress status

* feat: add event emitter output event date range picker

* feat: use percent pipe

* feat: create dayjs pipe

* fix: report heigth

* feat: add start current week and end current week

* feat: create weekly features

* feat: add weekly recap to tab

* feat: translated hardcoded strings in weekly-statistic.component.html to use translation keys

* feat: update date range picker, segmented control, weekly calendar, and weekly recap components; refactored organization selector component.
  • Loading branch information
adkif authored Aug 26, 2024
1 parent ab90b51 commit 247f81f
Show file tree
Hide file tree
Showing 36 changed files with 740 additions and 347 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
<nb-icon class="loader" size="medium" icon="loader-outline"></nb-icon>
</ng-container>
<div class="tools">
<ngx-date-range-picker [arrows]="true" [isSingleDatePicker]="true" [isDisableFutureDatePicker]="true"
class="medium"></ngx-date-range-picker>
<ngx-date-range-picker [isLockDatePicker]="true" [arrows]="true" [isSingleDatePicker]="true" [isDisableFutureDatePicker]="true"
class="medium" (rangeChanges)="onRangeChange($event)"></ngx-date-range-picker>
<ngx-filter></ngx-filter>
</div>
</nb-card-header>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { ChangeDetectionStrategy, Component } from '@angular/core';
import { UntilDestroy } from '@ngneat/until-destroy';
import { Observable } from 'rxjs';
import { RecapQuery } from '../../+state/recap.query';
import { RecapStore } from '../../+state/recap.store';
import { IDateRangePicker } from '../../shared/features/date-range-picker/date-picker.interface';

@UntilDestroy({ checkProperties: true })
@Component({
Expand All @@ -13,12 +15,16 @@ import { RecapQuery } from '../../+state/recap.query';
export class RecapComponent {
private readonly basePath = ['/', 'time-tracker', 'daily'];

constructor(private readonly recapQuery: RecapQuery) {}
constructor(private readonly recapQuery: RecapQuery, private readonly recapStore: RecapStore) {}

public get isLoading$(): Observable<boolean> {
return this.recapQuery.isLoading$;
}

public onRangeChange(range: IDateRangePicker) {
this.recapStore.update({ range });
}

public get segments() {
return [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
m[m]':
{trim: 'both'}
}}</span> &#183;
<span class="today">{{dailyActivities$ | async}}%</span></nb-card-header>
<span class="today">{{dailyActivities$ | async | percent:'1.0-2' }}</span></nb-card-header>
<nb-card-body *ngIf="(chartData$ | async).length; else noDataChart">
<ngx-charts-bar-vertical [animations]="animations" [results]="(chartData$ | async)" [gradient]="gradient"
[xAxis]="showXAxis" [yAxis]="showYAxis" [legend]="showLegend" [showXAxisLabel]="showXAxisLabel"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ export class TimeTrackingChartsComponent implements OnInit {
return this.recapQuery.state$.pipe(map((state) => state.count.weekDuration));
}

public get dailyActivities$(): Observable<string> {
return this.recapQuery.state$.pipe(map((state) => (state.count.weekActivities || 0).toFixed(2)));
public get dailyActivities$(): Observable<number> {
return this.recapQuery.state$.pipe(map((state) => state.count.weekActivities / 100));
}

public get chartData$(): Observable<IChartData[]> {
Expand Down
12 changes: 10 additions & 2 deletions packages/desktop-ui-lib/src/lib/recap/recap.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ import { AutoRefreshComponent } from './shared/ui/auto-refresh/auto-refresh.comp
import { ProgressStatusModule } from './shared/ui/progress-status/progress-status.module';
import { ProjectColumnViewModule } from './shared/ui/project-column-view/project-column-view.module';
import { StatisticComponent } from './shared/ui/statistic/statistic.component';
import { WeeklyCalendarComponent } from './weekly/features/weekly-calendar/weekly-calendar.component';
import { WeeklyProgressComponent } from './weekly/features/weekly-progress/weekly-progress.component';
import { WeeklyRecapComponent } from './weekly/features/weekly-recap/weekly-recap.component';
import { WeeklyStatisticComponent } from './weekly/features/weekly-statistic/weekly-statistic.component';

@NgModule({
declarations: [
Expand All @@ -55,7 +59,11 @@ import { StatisticComponent } from './shared/ui/statistic/statistic.component';
StatisticComponent,
AutoRefreshComponent,
ActivityReportComponent,
SegmentedControlComponent
SegmentedControlComponent,
WeeklyRecapComponent,
WeeklyCalendarComponent,
WeeklyProgressComponent,
WeeklyStatisticComponent
],
imports: [
CommonModule,
Expand Down Expand Up @@ -95,6 +103,6 @@ import { StatisticComponent } from './shared/ui/statistic/statistic.component';
RequestQuery,
RequestStore
],
exports: [RecapComponent]
exports: [RecapComponent, WeeklyRecapComponent]
})
export class RecapModule {}
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@
}

.main-report-wrapper {
height: calc(100vh - 8.5rem);
height: calc(100vh - 8rem);
margin-bottom: 0 !important;

.main-report-body {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { IDateRangePicker, ISelectedDateRange, ITimeLogFilters, WeekDaysEnum } from '@gauzy/contracts';
import * as moment from 'moment';
import { TimePeriod } from './date-picker.interface';
import { ISelectedDateRange, ITimeLogFilters, WeekDaysEnum } from '@gauzy/contracts';
import * as momentDefault from 'moment';
import { extendMoment } from 'moment-range';
import { IDateRangePicker, TimePeriod } from './date-picker.interface';

export const moment = extendMoment(momentDefault);
/**
* We are having issue, when organization not allowed future date
* When someone run timer for today, all statistic not displaying correctly
Expand Down Expand Up @@ -66,3 +68,18 @@ export function dayOfWeekAsString(weekDay: WeekDaysEnum): number {
WeekDaysEnum.SATURDAY
].indexOf(weekDay);
}

/**
* Updates the week days based on the specified start and end dates.
* If no dates are provided in the request, it defaults to the current week.
*/
export function updateWeekDays(input: IDateRangePicker) {
const { startDate = moment().startOf('week'), endDate = moment().endOf('week') } = input;

const start = moment(moment(startDate).format('YYYY-MM-DD'));
const end = moment(moment(endDate).format('YYYY-MM-DD'));
const range = Array.from(moment.range(start, end).by('day'));
const weekDays = range.map((date: moment.Moment) => date.format('YYYY-MM-DD'));

return { range, weekDays };
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
</ng-container>
<i class="far fa-calendar" (click)="openDatepicker($event)"></i>
<input nbInput name="date-range" size="small" type="text" ngxDaterangepickerMd
[singleDatePicker]="isSingleDatePicker" unitOfTime="day" [autoApply]="true"
[singleDatePicker]="isSingleDatePicker" [autoApply]="true"
[showCustomRangeLabel]="!isLockDatePicker" [alwaysShowCalendars]="true" [customRangeDirection]="false"
[linkedCalendars]="true" [maxDate]="maxDate" [minDate]="minDate" [ranges]="ranges" [locale]="locale"
[(ngModel)]="selectedDateRange" (datesUpdated)="onDatesUpdated($event)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,3 +225,20 @@ $button-radius: var(--button-rectangle-border-radius);
background-color: unset;
}
}

:host(.right) {
::ng-deep {
&.md-drppicker {
@include nb-ltr(margin-left, -30%);
@include nb-rtl(margin-left, -132%);
}
}
}

:host(.full) {
::ng-deep {
&.md-drppicker {
@include nb-ltr(margin-left, -155%);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import { ChangeDetectionStrategy, Component, inject, Input, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { WeekDaysEnum } from '@gauzy/contracts';
import {
ChangeDetectionStrategy,
Component,
EventEmitter,
inject,
Input,
OnDestroy,
OnInit,
Output,
ViewChild
} from '@angular/core';
import { IOrganization, WeekDaysEnum } from '@gauzy/contracts';
import { distinctUntilChange } from '@gauzy/ui-core/common';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { TranslateService } from '@ngx-translate/core';
Expand All @@ -9,10 +19,10 @@ import {
LocaleConfig,
DaterangepickerComponent as NgxDateRangePickerComponent
} from 'ngx-daterangepicker-material';
import { BehaviorSubject, Subject } from 'rxjs';
import { BehaviorSubject, filter, Subject } from 'rxjs';
import { debounceTime, tap } from 'rxjs/operators';
import { RecapQuery } from '../../../+state/recap.query';
import { RecapStore } from '../../../+state/recap.store';
import { Store } from '../../../../services';
import { Arrow } from './arrow/context/arrow.class';
import { Next, Previous } from './arrow/strategies';
import { DateRangeKeyEnum, DateRanges, IDateRangePicker, TimePeriod } from './date-picker.interface';
Expand Down Expand Up @@ -87,7 +97,7 @@ export class DateRangePickerComponent implements OnInit, OnDestroy {
* Getter & Setter for dynamic unitOfTime
*/
private _unitOfTime: moment.unitOfTime.Base = 'day';
get unitOfTime(): moment.unitOfTime.Base {
public get unitOfTime(): moment.unitOfTime.Base {
return this._unitOfTime;
}
@Input() set unitOfTime(value: moment.unitOfTime.Base) {
Expand Down Expand Up @@ -180,56 +190,82 @@ export class DateRangePickerComponent implements OnInit, OnDestroy {
/** */
@ViewChild(DateRangePickerDirective, { static: true }) public dateRangePickerDirective: DateRangePickerDirective;

constructor(public readonly translateService: TranslateService, readonly recapStore: RecapStore) {}
constructor(public readonly translateService: TranslateService, readonly store: Store) {}

@Output() rangeChanges = new EventEmitter<IDateRangePicker>();

@Input()
public set dates(range: IDateRangePicker) {
this.dates$.next({
...range,
isCustomDate: false
});
}

ngOnInit(): void {
this.range$
.pipe(
distinctUntilChange(),
debounceTime(500),
tap((range: IDateRangePicker) => {
this.recapStore.update({
range: {
startDate: moment(range.startDate).toISOString(),
endDate: moment(range.endDate).toISOString()
}
this.rangeChanges.emit({
startDate: moment(range.startDate).toISOString(),
endDate: moment(range.endDate).toISOString()
});
}),
untilDestroyed(this)
)
.subscribe();
this.selectedDateRange = this.getSelectorDates();
this.createDateRangeMenus();
this.setPastStrategy();
this.setFutureStrategy();
this.store.selectedOrganization$
.pipe(
filter((organization: IOrganization) => !!organization),
tap((organization: IOrganization) => {
this.futureDateAllowed = organization.futureDateAllowed;
this.selectedDateRange = this.getSelectorDates();
this.createDateRangeMenus();
this.setPastStrategy();
this.setFutureStrategy();
}),
untilDestroyed(this)
)
.subscribe();
}

/**
* Create Date Range Translated Menus
*/
createDateRangeMenus() {
this.ranges = {
[DateRangeKeyEnum.TODAY]: [moment(), moment()],
[DateRangeKeyEnum.YESTERDAY]: [moment().subtract(1, 'days'), moment().subtract(1, 'days')]
private createDateRangeMenus() {
const ranges = {
day: {
[DateRangeKeyEnum.TODAY]: [moment().startOf('day'), moment().endOf('day')],
[DateRangeKeyEnum.YESTERDAY]: [
moment().subtract(1, 'days').startOf('day'),
moment().subtract(1, 'days').endOf('day')
]
},
week: {
[DateRangeKeyEnum.CURRENT_WEEK]: [moment().startOf('isoWeek'), moment().endOf('isoWeek')],
[DateRangeKeyEnum.LAST_WEEK]: [
moment().subtract(1, 'week').startOf('isoWeek'),
moment().subtract(1, 'week').endOf('isoWeek')
]
},
month: {
[DateRangeKeyEnum.CURRENT_MONTH]: [moment().startOf('month'), moment().endOf('month')],
[DateRangeKeyEnum.LAST_MONTH]: [
moment().subtract(1, 'month').startOf('month'),
moment().subtract(1, 'month').endOf('month')
]
}
};

// Define the units of time to remove based on conditions
const unitsToRemove = [];

if (this.isLockDatePicker && this.unitOfTime !== 'day') {
unitsToRemove.push(DateRangeKeyEnum.TODAY, DateRangeKeyEnum.YESTERDAY);
}
if (this.isLockDatePicker && this.unitOfTime !== 'week') {
unitsToRemove.push(DateRangeKeyEnum.CURRENT_WEEK, DateRangeKeyEnum.LAST_WEEK);
}
if (this.isLockDatePicker && this.unitOfTime !== 'month') {
unitsToRemove.push(DateRangeKeyEnum.CURRENT_MONTH, DateRangeKeyEnum.LAST_MONTH);
}

// Remove date ranges based on unitsToRemove
unitsToRemove.forEach((unit) => {
delete this.ranges[unit];
});
this.ranges = this.isLockDatePicker
? ranges[this.unitOfTime]
: ({
...ranges.day,
...ranges.week,
...ranges.month
} as any as DateRanges);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { TranslateModule } from '@ngx-translate/core';
import { NgxDaterangepickerMd as NgxDateRangePickerMd } from 'ngx-daterangepicker-material';
import { RecapQuery } from '../../../+state/recap.query';
import { RecapStore } from '../../../+state/recap.store';
import { PipeModule } from '../../../../time-tracker/pipes/pipe.module';
import { dayOfWeekAsString } from './date-picker.utils';
import { DateRangePickerComponent } from './date-range-picker.component';

Expand All @@ -22,6 +23,7 @@ import { DateRangePickerComponent } from './date-range-picker.component';
NbInputModule,
NgSelectModule,
TranslateModule,
PipeModule,
NgxDateRangePickerMd.forRoot({
firstDay: dayOfWeekAsString(WeekDaysEnum.MONDAY)
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ $text-color: var(--gauzy-text-color-2);
// Segment component styles
.segment {
@include flex-center($gap-small);
padding: $padding-small;
padding: 0 $padding-small;

span {
display: none;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<div class="wrapper">
<div class="percentage-col">{{ percentage }}%</div>
<div *ngIf="displayValue.out" class="percentage-col">{{ (percentage / 100) | percent:'1.0-2' }}</div>
<div class="progress-col">
<nb-progress-bar [value]="percentage" [status]="progressStatus(percentage)"
[displayValue]="true" size="tiny">
<nb-progress-bar [value]="percentage" [status]="progressStatus(percentage)" [displayValue]="displayValue.in"
size="tiny">
</nb-progress-bar>
</div>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
.wrapper {
display: flex;
align-items: center;
gap: 0.5rem
}

:host {
Expand All @@ -20,12 +21,6 @@
.progress-container {
height: 5px !important;
}

.progress-value {
span {
display: none;
}
}
}
}
}
Expand All @@ -34,12 +29,11 @@
width: 100%;

.percentage-col {
margin-right: 10px;
width: 60px;
}

.progress-col {
width: 75%;
width: 100%;
display: flex;
align-items: flex-end;
}
Expand Down
Loading

0 comments on commit 247f81f

Please sign in to comment.