diff --git a/apps/gauzy/src/app/@shared/pipes/time-format.pipe.ts b/apps/gauzy/src/app/@shared/pipes/time-format.pipe.ts index 979eae53b86..8cc77113eb6 100644 --- a/apps/gauzy/src/app/@shared/pipes/time-format.pipe.ts +++ b/apps/gauzy/src/app/@shared/pipes/time-format.pipe.ts @@ -1,9 +1,9 @@ import { Pipe, PipeTransform, OnDestroy } from '@angular/core'; -import { Store } from '../../@core/services/store.service'; -import { IOrganization } from '@gauzy/contracts'; +import { filter, tap } from 'rxjs/operators'; import * as moment from 'moment'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; -import { filter } from 'rxjs/operators'; +import { IOrganization, TimeFormatEnum } from '@gauzy/contracts'; +import { Store } from '../../@core/services/store.service'; @UntilDestroy({ checkProperties: true }) @Pipe({ @@ -11,24 +11,34 @@ import { filter } from 'rxjs/operators'; pure: false }) export class TimeFormatPipe implements PipeTransform, OnDestroy { - private format: 12 | 24; + private format: TimeFormatEnum; - constructor(private store: Store) { + constructor(private readonly store: Store) { this.store.selectedOrganization$ .pipe( filter((organization: IOrganization) => !!organization), + tap((organization: IOrganization) => { + this.format = organization?.timeFormat ?? TimeFormatEnum.FORMAT_12_HOURS; + }), untilDestroyed(this) ) - .subscribe((org: IOrganization) => { - this.format = org ? org.timeFormat : 12; - }); + .subscribe(); } - transform(value: any, seconds: boolean = false): any { + /** + * Transforms a given value into a formatted time string. + * @param value The value to transform into a time string. This can be a string, number, Date object, or any value parsable by moment.js. + * @param timeFormat The time format to use. If not provided, it defaults to `this.format`. + * @param seconds Optional. If true, include seconds in the formatted time string. Defaults to false. + * @returns A formatted time string based on the input value and format options. + */ + transform(value: any, timeFormat: number = this.format, seconds: boolean = false): any { let format = 'HH:mm' + (seconds ? ':ss' : ''); - if (this.format === 12) { + + if (timeFormat === TimeFormatEnum.FORMAT_12_HOURS) { format = 'hh:mm' + (seconds ? ':ss' : '') + ' A'; } + let date = moment(value); if (!date.isValid()) { date = moment.utc(value, 'HH:mm'); diff --git a/apps/gauzy/src/app/@shared/timesheet/gauzy-filters/filters.component.scss b/apps/gauzy/src/app/@shared/timesheet/gauzy-filters/filters.component.scss index 785b4878525..c1a3d586647 100644 --- a/apps/gauzy/src/app/@shared/timesheet/gauzy-filters/filters.component.scss +++ b/apps/gauzy/src/app/@shared/timesheet/gauzy-filters/filters.component.scss @@ -29,7 +29,7 @@ $status: 'basic'; } .filter-item-list { .filter-item { - max-width: 263px; + min-width: 220px; } .select-box, nb-select { @@ -51,23 +51,17 @@ $status: 'basic'; } &:focus { - background-color: nb-theme( - select-outline-#{$status}-focus-background-color - ); + background-color: nb-theme(select-outline-#{$status}-focus-background-color); border-color: nb-theme(select-outline-#{$status}-focus-border-color); } &:hover { - background-color: nb-theme( - select-outline-#{$status}-hover-background-color - ); + background-color: nb-theme(select-outline-#{$status}-hover-background-color); border-color: nb-theme(select-outline-#{$status}-hover-border-color); } &[disabled] { color: nb-theme(select-outline-#{$status}-disabled-text-color); - background-color: nb-theme( - select-outline-#{$status}-disabled-background-color - ); + background-color: nb-theme(select-outline-#{$status}-disabled-background-color); border-color: nb-theme(select-outline-#{$status}-disabled-border-color); nb-icon { @@ -81,14 +75,10 @@ $status: 'basic'; } &.top { - border-top-color: nb-theme( - select-outline-#{$status}-adjacent-border-color - ); + border-top-color: nb-theme(select-outline-#{$status}-adjacent-border-color); } &.bottom { - border-bottom-color: nb-theme( - select-outline-#{$status}-adjacent-border-color - ); + border-bottom-color: nb-theme(select-outline-#{$status}-adjacent-border-color); } } } diff --git a/apps/gauzy/src/app/@shared/timesheet/gauzy-filters/gauzy-filters.component.html b/apps/gauzy/src/app/@shared/timesheet/gauzy-filters/gauzy-filters.component.html index b005187cd0b..8024c77b210 100644 --- a/apps/gauzy/src/app/@shared/timesheet/gauzy-filters/gauzy-filters.component.html +++ b/apps/gauzy/src/app/@shared/timesheet/gauzy-filters/gauzy-filters.component.html @@ -2,8 +2,12 @@
-
- +
+
@@ -27,29 +31,22 @@ nbButton status="basic" outline - [nbPopover]="templateRef" nbPopoverPlacement="bottom" + [nbPopover]="activityLevelSliderTemplate" + nbPopoverTrigger="click" > - + {{ 'TIMESHEET.ACTIVITY_LEVEL' | translate }} : {{ activityLevel?.start }}% - {{ activityLevel?.end }}% - - {{ 'TIMESHEET.SELECT_ACTIVITY_LEVEL' | translate }} - - -
- -
-
+ + {{ 'TIMESHEET.SELECT_ACTIVITY_LEVEL' | translate }} +
@@ -75,3 +72,14 @@
+ + +
+ +
+
diff --git a/apps/gauzy/src/app/@shared/timesheet/gauzy-filters/gauzy-filters.component.ts b/apps/gauzy/src/app/@shared/timesheet/gauzy-filters/gauzy-filters.component.ts index 88997b9de7b..d89dae548ec 100644 --- a/apps/gauzy/src/app/@shared/timesheet/gauzy-filters/gauzy-filters.component.ts +++ b/apps/gauzy/src/app/@shared/timesheet/gauzy-filters/gauzy-filters.component.ts @@ -8,15 +8,15 @@ import { OnInit, Output } from '@angular/core'; -import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; -import { Options, ChangeContext } from '@angular-slider/ngx-slider'; -import { ITimeLogFilters, PermissionsEnum, TimeLogSourceEnum, TimeLogType } from '@gauzy/contracts'; import { Subject } from 'rxjs'; import { debounceTime, take, tap } from 'rxjs/operators'; +import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { TranslateService } from '@ngx-translate/core'; import { pick } from 'underscore'; -import { ActivityLevel, TimesheetFilterService } from '../timesheet-filter.service'; +import { Options, ChangeContext } from '@angular-slider/ngx-slider'; +import { ITimeLogFilters, PermissionsEnum, TimeFormatEnum, TimeLogSourceEnum, TimeLogType } from '@gauzy/contracts'; import { TranslationBaseComponent } from '@gauzy/ui-sdk/shared'; +import { ActivityLevel, TimesheetFilterService } from '../timesheet-filter.service'; @UntilDestroy({ checkProperties: true }) @Component({ @@ -50,6 +50,7 @@ export class GauzyFiltersComponent extends TranslationBaseComponent implements A */ private filters$: Subject = new Subject(); private _filters: ITimeLogFilters = { + timeFormat: TimeFormatEnum.FORMAT_12_HOURS, source: [], logType: [], activityLevel: ActivityLevel @@ -67,6 +68,7 @@ export class GauzyFiltersComponent extends TranslationBaseComponent implements A } this.cd.detectChanges(); } + @Input() isTimeformat: boolean = false; @Output() filtersChange: EventEmitter = new EventEmitter(); @@ -108,24 +110,38 @@ export class GauzyFiltersComponent extends TranslationBaseComponent implements A this.cd.detectChanges(); } - setActivityLevel($event: ChangeContext): void { + /** + * + * @param activity + */ + setActivityLevel(activity: ChangeContext): void { this.filters.activityLevel = { - start: $event.value, - end: $event.highValue + start: activity.value, + end: activity.highValue }; this.activityLevel = this.filters.activityLevel; this.triggerFilterChange(); } + /** + * + */ triggerFilterChange(): void { this.filters$.next(true); } + /** + * + */ clearFilters(): void { this.filters = this.timesheetFilterService.clear(); this.triggerFilterChange(); } + /** + * + * @returns + */ hasFilter(): boolean { return ( (this._filters.source && this._filters.source.length >= 1) || @@ -135,11 +151,35 @@ export class GauzyFiltersComponent extends TranslationBaseComponent implements A ); } + /** + * + * @returns + */ arrangedFilters(): ITimeLogFilters { Object.keys(this.filters).forEach((key) => (this.filters[key] === undefined ? delete this.filters[key] : {})); return this.filters; } + /** + * Handles the event when the time format is changed. + * + * @param timeformat The new time format. + */ + timeFormatChanged(timeFormat: TimeFormatEnum): void { + this.filters.timeFormat = timeFormat; + this.triggerFilterChange(); + } + + /** + * Handles the event when the time zone is changed. + * + * @param timezone The new time zone. + */ + timeZoneChanged(timeZone: string): void { + this.filters.timeZone = timeZone; + this.triggerFilterChange(); + } + /** * Generate Dynamic Timelog Source Selector */ diff --git a/apps/gauzy/src/app/@shared/timesheet/gauzy-filters/gauzy-filters.module.ts b/apps/gauzy/src/app/@shared/timesheet/gauzy-filters/gauzy-filters.module.ts index 06fbfd428dc..09d9682e6c6 100644 --- a/apps/gauzy/src/app/@shared/timesheet/gauzy-filters/gauzy-filters.module.ts +++ b/apps/gauzy/src/app/@shared/timesheet/gauzy-filters/gauzy-filters.module.ts @@ -1,7 +1,7 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; -import { NbButtonModule, NbIconModule, NbSelectModule } from '@nebular/theme'; +import { NbButtonModule, NbIconModule, NbPopoverModule, NbSelectModule } from '@nebular/theme'; import { NgxSliderModule } from '@angular-slider/ngx-slider'; import { TranslateModule } from '@gauzy/ui-sdk/i18n'; import { PipesModule } from '../../pipes/pipes.module'; @@ -16,6 +16,7 @@ import { TimezoneFilterModule } from './timezone-filter/timezone-filter.module'; FormsModule, NbButtonModule, NbIconModule, + NbPopoverModule, NbSelectModule, NgxSliderModule, PipesModule, diff --git a/apps/gauzy/src/app/@shared/timesheet/gauzy-filters/timezone-filter/timezone-filter.component.html b/apps/gauzy/src/app/@shared/timesheet/gauzy-filters/timezone-filter/timezone-filter.component.html index 24e0bbf2bb1..4d95a187225 100644 --- a/apps/gauzy/src/app/@shared/timesheet/gauzy-filters/timezone-filter/timezone-filter.component.html +++ b/apps/gauzy/src/app/@shared/timesheet/gauzy-filters/timezone-filter/timezone-filter.component.html @@ -1,19 +1,21 @@ - - -
+ +
Time Zone
@@ -33,7 +35,7 @@
-
Time Format
+
Time Format
(); @Output() timeFormatChange = new EventEmitter(); constructor( private readonly _route: ActivatedRoute, - private readonly _router: Router, - private readonly _store: Store + private readonly _store: Store, + private readonly _navigationService: NavigationService ) {} ngOnInit(): void { @@ -87,8 +58,8 @@ export class TimezoneFilterComponent implements AfterViewInit, OnInit, OnDestroy combineLatest([queryParams$, storeOrganization$]) .pipe( tap(([queryParams, organization]) => { - this.applyTimeFormat(queryParams, organization.timeFormat); - this.applyTimeZone(queryParams, TimeZoneEnum.ORG_TIMEZONE); + if (this.isTimeformat) this.applyTimeFormat(queryParams, organization.timeFormat); + if (this.isTimezone) this.applyTimeZone(queryParams, TimeZoneEnum.ORG_TIMEZONE); }), // Handle component lifecycle to avoid memory leaks untilDestroyed(this) @@ -110,8 +81,8 @@ export class TimezoneFilterComponent implements AfterViewInit, OnInit, OnDestroy .pipe( distinctUntilChange(), tap(([queryParams, user]) => { - this.applyTimeFormat(queryParams, user.timeFormat); - this.applyTimeZone(queryParams, TimeZoneEnum.MINE_TIMEZONE); + if (this.isTimeformat) this.applyTimeFormat(queryParams, user.timeFormat); + if (this.isTimezone) this.applyTimeZone(queryParams, TimeZoneEnum.MINE_TIMEZONE); }), // Handle component lifecycle to avoid memory leaks untilDestroyed(this) @@ -121,18 +92,18 @@ export class TimezoneFilterComponent implements AfterViewInit, OnInit, OnDestroy /** * Applies the appropriate time format based on query parameters, organization settings, and employee settings. + * * @param queryParams The query parameters from the route. * @param organization The organization details. - * @param employee The selected employee details. */ private applyTimeFormat(queryParams: Params, timeFormat: number): void { const { time_format } = queryParams; // Apply query parameters first if (time_format) { - this.selectTimeFormat(time_format); + this.updateSelectedTimeFormat(parseInt(time_format, 10)); } else { - this.selectTimeFormat(timeFormat); + this.updateSelectedTimeFormat(timeFormat); } } @@ -146,9 +117,9 @@ export class TimezoneFilterComponent implements AfterViewInit, OnInit, OnDestroy // Apply query parameters first if (time_zone) { - this.selectTimeZone(time_zone); + this.updateSelectedTimeZone(time_zone); } else { - this.selectTimeZone(timeZone); + this.updateSelectedTimeZone(timeZone); } } @@ -190,14 +161,10 @@ export class TimezoneFilterComponent implements AfterViewInit, OnInit, OnDestroy this.timeFormatChange.emit(timeFormat); - if (this.navigate) { - // Update query parameter 'time_format' - await this._router.navigate([], { - relativeTo: this._route, - queryParams: { time_format: timeFormat.toString() }, - queryParamsHandling: 'merge' - }); - } + // Updates the query parameters of the current route without navigating away. + await this._navigationService.updateQueryParams({ + time_format: timeFormat.toString() + }); } /** @@ -211,14 +178,10 @@ export class TimezoneFilterComponent implements AfterViewInit, OnInit, OnDestroy this.timeZoneChange.emit(this.getTimeZone(this.selectedTimeZone)); - if (this.navigate) { - // Update query parameter 'time_zone' - await this._router.navigate([], { - relativeTo: this._route, - queryParams: { time_zone: timeZone.toString() }, - queryParamsHandling: 'merge' - }); - } + // Updates the query parameters of the current route without navigating away. + await this._navigationService.updateQueryParams({ + time_zone: timeZone.toString() + }); } /** diff --git a/apps/gauzy/src/app/@shared/timesheet/screenshots/screenshots-item/screenshots-item.component.html b/apps/gauzy/src/app/@shared/timesheet/screenshots/screenshots-item/screenshots-item.component.html index c75193353d4..5cbcc035d81 100644 --- a/apps/gauzy/src/app/@shared/timesheet/screenshots/screenshots-item/screenshots-item.component.html +++ b/apps/gauzy/src/app/@shared/timesheet/screenshots/screenshots-item/screenshots-item.component.html @@ -1,18 +1,12 @@ -
+
- +
- + {{ employees.length }} @@ -46,9 +40,7 @@ [message]="'ACTIVITY.DELETE_CONFIRM' | translate" (confirm)="deleteSlot(timeSlot)" > - + @@ -20,6 +12,7 @@
} + */ + getEvents(arg: any, callback: Function): Promise { if (!this.organization || isEmpty(this.request)) { return; } const startDate = moment(arg.start).startOf('day').format('YYYY-MM-DD HH:mm:ss'); const endDate = moment(arg.end).subtract(1, 'days').endOf('day').format('YYYY-MM-DD HH:mm:ss'); - const appliedFilter = pick(this.filters, 'source', 'activityLevel', 'logType'); - const request: IGetTimeLogInput = { ...appliedFilter, ...this.getFilterRequest({ diff --git a/apps/gauzy/src/app/pages/reports/expenses-report/expenses-report/expenses-report.component.html b/apps/gauzy/src/app/pages/reports/expenses-report/expenses-report/expenses-report.component.html index 27c4ec3eb8d..adad39f1b0d 100644 --- a/apps/gauzy/src/app/pages/reports/expenses-report/expenses-report/expenses-report.component.html +++ b/apps/gauzy/src/app/pages/reports/expenses-report/expenses-report/expenses-report.component.html @@ -1,18 +1,11 @@ - +

{{ 'REPORT_PAGE.EXPENSES_REPORT' | translate }}

- + [saveFilters]="(datePickerConfig$ | async).isSaveDatePicker" (filtersChange)="filtersChange($event)" >
- +
+ +
@@ -41,15 +36,11 @@

- +
- +

diff --git a/apps/gauzy/src/app/pages/reports/expenses-report/expenses-report/expenses-report.component.scss b/apps/gauzy/src/app/pages/reports/expenses-report/expenses-report/expenses-report.component.scss index 9a2c779e47d..097ba829b51 100644 --- a/apps/gauzy/src/app/pages/reports/expenses-report/expenses-report/expenses-report.component.scss +++ b/apps/gauzy/src/app/pages/reports/expenses-report/expenses-report/expenses-report.component.scss @@ -2,33 +2,28 @@ @import 'gauzy/_gauzy-overrides'; :host { - ::ng-deep { - .action-select button { - background-color: nb-theme(gauzy-card-1) !important; - border-radius: nb-theme(select-rectangle-border-radius); - } + ::ng-deep { + .action-select button { + background-color: nb-theme(gauzy-card-1) !important; + border-radius: nb-theme(select-rectangle-border-radius); } + } } :host { .filters { display: flex; justify-content: flex-end; - gap: 5px; } ::ng-deep { ng-select { min-width: 10rem; } - @include ng-select-overrides( - 2rem, - $default-button-radius, - $default-box-shadow - ); + @include ng-select-overrides(2rem, $default-button-radius, $default-box-shadow); } } .report-body { - height: calc(100% - 8rem) !important; + height: calc(100% - 8rem) !important; } diff --git a/apps/gauzy/src/app/pages/reports/manual-time/manual-time/manual-time.component.html b/apps/gauzy/src/app/pages/reports/manual-time/manual-time/manual-time.component.html index 65ecc475548..d9b783c1175 100644 --- a/apps/gauzy/src/app/pages/reports/manual-time/manual-time/manual-time.component.html +++ b/apps/gauzy/src/app/pages/reports/manual-time/manual-time/manual-time.component.html @@ -1,13 +1,11 @@ - -

- - {{ 'REPORT_PAGE.MANUAL_TIME_EDIT_REPORT' | translate }} - + +

+ + {{ 'REPORT_PAGE.MANUAL_TIME_EDIT_REPORT' | translate }} +

- + [hasActivityLevelFilter]="false" (filtersChange)="filtersChange($event)" > - +
+ +

@@ -49,75 +49,91 @@

-
+
-
{{ 'REPORT_PAGE.EMPLOYEE' | translate }}
+
+ {{ 'REPORT_PAGE.EMPLOYEE' | translate }} +
+ employeeEl; + context: { $implicit: timeLogRow?.employee } + " + >
-
{{ 'REPORT_PAGE.PROJECT' | translate }}
+
+ {{ 'REPORT_PAGE.PROJECT' | translate }} +
+ projectEl; + context: { $implicit: timeLogRow?.project } + " + >
-
{{ 'REPORT_PAGE.TO_DO' | translate }}
+
+ {{ 'REPORT_PAGE.TO_DO' | translate }} +
+ taskEl; + context: { $implicit: timeLogRow?.task } + " + >
-
{{ 'REPORT_PAGE.REASON' | translate }}
+
+ {{ 'REPORT_PAGE.REASON' | translate }} +
{{ timeLogRow.reason || '—' }}
-
{{ 'REPORT_PAGE.FROM' | translate }}
+
+ {{ 'REPORT_PAGE.FROM' | translate }} +
{{ timeLogRow.createdAt | timeFormat }}
-
{{ 'REPORT_PAGE.TIME_SPAN' | translate }}
+
+ {{ 'REPORT_PAGE.TIME_SPAN' | translate }} +
{{ timeLogRow.duration | durationFormat }}
-
{{ 'REPORT_PAGE.CHANGED_AT' | translate }}
+
+ {{ 'REPORT_PAGE.CHANGED_AT' | translate }} +
{{ timeLogRow.editedAt | dateTimeFormat }}
+
+ {{ 'REPORT_PAGE.ACTION' | translate }} +
{{ 'REPORT_PAGE.ACTION' | translate }}
-
+ class="responsive-table-content day-col" + [ngSwitch]="timeLogRow.isEdited" + >
- - - - {{ 'REPORT_PAGE.NO_PROJECT' | translate }} - + + + {{ 'REPORT_PAGE.NO_PROJECT' | translate }} + - {{ task?.title }} - - {{ 'REPORT_PAGE.NO_TASK' | translate }} - + {{ task?.title }} + + {{ 'REPORT_PAGE.NO_TASK' | translate }} + -
- -
- - {{ 'REPORT_PAGE.NO_EMPLOYEE' | translate }} - +
+ +
+ + {{ 'REPORT_PAGE.NO_EMPLOYEE' | translate }} +
-
-
-
- {{ 'REPORT_PAGE.EMPLOYEE' | translate }} -
-
- {{ 'REPORT_PAGE.PROJECT' | translate }} -
-
- {{ 'REPORT_PAGE.TO_DO' | translate }} -
-
- {{ 'REPORT_PAGE.REASON' | translate }} -
-
- {{ 'REPORT_PAGE.FROM' | translate }} -
-
- {{ 'REPORT_PAGE.TIME_SPAN' | translate }} -
-
- {{ 'REPORT_PAGE.CHANGED_AT' | translate }} -
-
- {{ 'REPORT_PAGE.ACTION' | translate }} -
-
-
+
+
+
+ {{ 'REPORT_PAGE.EMPLOYEE' | translate }} +
+
+ {{ 'REPORT_PAGE.PROJECT' | translate }} +
+
+ {{ 'REPORT_PAGE.TO_DO' | translate }} +
+
+ {{ 'REPORT_PAGE.REASON' | translate }} +
+
+ {{ 'REPORT_PAGE.FROM' | translate }} +
+
+ {{ 'REPORT_PAGE.TIME_SPAN' | translate }} +
+
+ {{ 'REPORT_PAGE.CHANGED_AT' | translate }} +
+
+ {{ 'REPORT_PAGE.ACTION' | translate }} +
+
+
diff --git a/packages/common-angular/src/utils/shared-utils.ts b/packages/common-angular/src/utils/shared-utils.ts index ef82e28376f..1d20e02fc37 100644 --- a/packages/common-angular/src/utils/shared-utils.ts +++ b/packages/common-angular/src/utils/shared-utils.ts @@ -24,6 +24,15 @@ export function isNotNullOrUndefined(value: T | undefined | null): value is T return value !== undefined && value !== null; } +/** + * Check if a value is null, undefined, or an empty string. + * @param value The value to check. + * @returns true if the value is null, undefined, or an empty string, false otherwise. + */ +export function isNotNullOrUndefinedOrEmpty(value: T | undefined | null): boolean { + return isNotNullOrUndefined(value) && value !== ''; +} + // It will use for pass nested object or array in query params in get method. export function toParams(query) { let params: HttpParams = new HttpParams(); diff --git a/packages/contracts/src/timesheet.model.ts b/packages/contracts/src/timesheet.model.ts index 11d06f24db2..d0599f6ca8b 100644 --- a/packages/contracts/src/timesheet.model.ts +++ b/packages/contracts/src/timesheet.model.ts @@ -1,21 +1,11 @@ -import { - IBasePerTenantAndOrganizationEntityModel, - IBaseRelationsEntityModel, -} from './base-entity.model'; -import { - IOrganizationContact, - OrganizationContactBudgetTypeEnum, -} from './organization-contact.model'; +import { IBasePerTenantAndOrganizationEntityModel, IBaseRelationsEntityModel } from './base-entity.model'; +import { IOrganizationContact, OrganizationContactBudgetTypeEnum } from './organization-contact.model'; import { IOrganizationProject, IRelationalOrganizationProject, - OrganizationProjectBudgetTypeEnum, + OrganizationProjectBudgetTypeEnum } from './organization-projects.model'; -import { - IEmployee, - IEmployeeFindInput, - IRelationalEmployee, -} from './employee.model'; +import { IEmployee, IEmployeeFindInput, IRelationalEmployee } from './employee.model'; import { ITask } from './task.model'; import { ITag } from './tag.model'; import { IPaginationInput } from './core.model'; @@ -23,6 +13,7 @@ import { ReportGroupByFilter } from './report.model'; import { IUser } from './user.model'; import { IRelationalOrganizationTeam } from './organization-team.model'; import { IScreenshot } from './screenshot.model'; +import { TimeFormatEnum } from './organization.model'; export interface ITimesheet extends IBasePerTenantAndOrganizationEntityModel { employee: IEmployee; @@ -78,17 +69,15 @@ export enum TimesheetStatus { PENDING = 'PENDING', IN_REVIEW = 'IN REVIEW', DENIED = 'DENIED', - APPROVED = 'APPROVED', + APPROVED = 'APPROVED' } -export interface IUpdateTimesheetStatusInput - extends IBasePerTenantAndOrganizationEntityModel { +export interface IUpdateTimesheetStatusInput extends IBasePerTenantAndOrganizationEntityModel { ids: string | string[]; status?: TimesheetStatus; } -export interface ISubmitTimesheetInput - extends IBasePerTenantAndOrganizationEntityModel { +export interface ISubmitTimesheetInput extends IBasePerTenantAndOrganizationEntityModel { ids: string | string[]; status: 'submit' | 'unsubmit'; } @@ -106,7 +95,10 @@ export interface IDateRange { start: Date; end: Date; } -export interface ITimeLog extends IBasePerTenantAndOrganizationEntityModel, IRelationalOrganizationProject, IRelationalOrganizationTeam { +export interface ITimeLog + extends IBasePerTenantAndOrganizationEntityModel, + IRelationalOrganizationProject, + IRelationalOrganizationTeam { employee: IEmployee; employeeId: IEmployee['id']; timesheet?: ITimesheet; @@ -133,8 +125,7 @@ export interface ITimeLog extends IBasePerTenantAndOrganizationEntityModel, IRel isEdited?: boolean; } -export interface ITimeLogCreateInput - extends IBasePerTenantAndOrganizationEntityModel { +export interface ITimeLogCreateInput extends IBasePerTenantAndOrganizationEntityModel { employeeId: string; timesheetId?: string; taskId?: string; @@ -148,8 +139,7 @@ export interface ITimeLogCreateInput isBilled?: boolean; } -export interface ITimeSlotCreateInput - extends IBasePerTenantAndOrganizationEntityModel { +export interface ITimeSlotCreateInput extends IBasePerTenantAndOrganizationEntityModel { employeeId: string; duration: number; keyboard: number; @@ -163,12 +153,12 @@ export enum TimeLogType { TRACKED = 'TRACKED', MANUAL = 'MANUAL', IDLE = 'IDLE', - RESUMED = 'RESUMED', + RESUMED = 'RESUMED' } export enum ManualTimeLogAction { ADDED = 'ADDED', - EDITED = 'EDITED', + EDITED = 'EDITED' } export enum TimeLogSourceEnum { @@ -178,11 +168,10 @@ export enum TimeLogSourceEnum { BROWSER_EXTENSION = 'BROWSER_EXTENSION', HUBSTAFF = 'HUBSTAFF', UPWORK = 'UPWORK', - TEAMS = 'TEAMS', + TEAMS = 'TEAMS' } -export interface ITimeLogFilters - extends IBasePerTenantAndOrganizationEntityModel { +export interface ITimeLogFilters extends IBasePerTenantAndOrganizationEntityModel { date?: Date | string; startDate?: Date | string; endDate?: Date | string; @@ -199,11 +188,12 @@ export interface ITimeLogFilters taskIds?: string[]; defaultRange?: boolean; unitOfTime?: any; - categoryId?: string + categoryId?: string; + timeZone?: string; + timeFormat?: TimeFormatEnum; } -export interface ITimeLogTodayFilters - extends IBasePerTenantAndOrganizationEntityModel { +export interface ITimeLogTodayFilters extends IBasePerTenantAndOrganizationEntityModel { todayStart?: Date | string; todayEnd?: Date | string; } @@ -231,16 +221,14 @@ export interface ITimeSlot extends IBasePerTenantAndOrganizationEntityModel { isAllowDelete?: boolean; } -export interface ITimeSlotTimeLogs - extends IBasePerTenantAndOrganizationEntityModel { +export interface ITimeSlotTimeLogs extends IBasePerTenantAndOrganizationEntityModel { timeLogs: ITimeLog[]; timeSlots: ITimeSlot[]; timeLogId: string; timeSlotId: string; } -export interface ITimeSlotMinute - extends IBasePerTenantAndOrganizationEntityModel { +export interface ITimeSlotMinute extends IBasePerTenantAndOrganizationEntityModel { timeSlot?: ITimeSlot; timeSlotId?: string; keyboard?: number; @@ -282,8 +270,7 @@ export interface IDailyActivity { childItems?: IDailyActivity[]; } -export interface ICreateActivityInput - extends IBasePerTenantAndOrganizationEntityModel { +export interface ICreateActivityInput extends IBasePerTenantAndOrganizationEntityModel { employeeId?: string; projectId?: string; duration?: number; @@ -300,7 +287,7 @@ export interface ICreateActivityInput export enum ActivityType { URL = 'URL', - APP = 'APP', + APP = 'APP' } export interface IURLMetaData { @@ -310,9 +297,11 @@ export interface IURLMetaData { [x: string]: any; } - - -export interface ITimerStatusInput extends ITimeLogTodayFilters, IBaseRelationsEntityModel, IRelationalEmployee, IRelationalOrganizationTeam { +export interface ITimerStatusInput + extends ITimeLogTodayFilters, + IBaseRelationsEntityModel, + IRelationalEmployee, + IRelationalOrganizationTeam { source?: TimeLogSourceEnum; employeeIds?: string[]; } @@ -340,7 +329,7 @@ export interface ITimerPosition { export interface ITimerToggleInput extends IBasePerTenantAndOrganizationEntityModel, - Pick { + Pick { projectId?: string; taskId?: string; organizationContactId?: string; @@ -382,32 +371,24 @@ export interface IGetTimeLogReportInput extends IGetTimeLogInput { isEdited?: boolean; } -export interface IGetTimeLogConflictInput - extends IBasePerTenantAndOrganizationEntityModel, - IBaseRelationsEntityModel { +export interface IGetTimeLogConflictInput extends IBasePerTenantAndOrganizationEntityModel, IBaseRelationsEntityModel { ignoreId?: string | string[]; startDate: string | Date; endDate: string | Date; employeeId: string; } -export interface IGetTimeSlotInput - extends ITimeLogFilters, - IBaseRelationsEntityModel { +export interface IGetTimeSlotInput extends ITimeLogFilters, IBaseRelationsEntityModel { [x: string]: any; } -export interface IGetActivitiesInput - extends ITimeLogFilters, - IPaginationInput, - IBaseRelationsEntityModel { +export interface IGetActivitiesInput extends ITimeLogFilters, IPaginationInput, IBaseRelationsEntityModel { types?: string[]; titles?: string[]; groupBy?: string; } -export interface IBulkActivitiesInput - extends IBasePerTenantAndOrganizationEntityModel { +export interface IBulkActivitiesInput extends IBasePerTenantAndOrganizationEntityModel { employeeId: string; projectId?: string; activities: IActivity[]; @@ -528,13 +509,11 @@ export interface IClientBudgetLimitReport { remainingBudget?: number; } -export interface IDeleteTimeSlot - extends IBasePerTenantAndOrganizationEntityModel { +export interface IDeleteTimeSlot extends IBasePerTenantAndOrganizationEntityModel { ids: string[]; } -export interface IDeleteTimeLog - extends IBasePerTenantAndOrganizationEntityModel { +export interface IDeleteTimeLog extends IBasePerTenantAndOrganizationEntityModel { logIds: string[]; forceDelete: boolean; } diff --git a/packages/ui-sdk/src/lib/common/src/utils/shared-utils.ts b/packages/ui-sdk/src/lib/common/src/utils/shared-utils.ts index 465b63161df..52ba986b1e9 100644 --- a/packages/ui-sdk/src/lib/common/src/utils/shared-utils.ts +++ b/packages/ui-sdk/src/lib/common/src/utils/shared-utils.ts @@ -25,6 +25,15 @@ export function isNotNullOrUndefined(value: T | undefined | null): value is T return value !== undefined && value !== null; } +/** + * Check if a value is null, undefined, or an empty string. + * @param value The value to check. + * @returns true if the value is null, undefined, or an empty string, false otherwise. + */ +export function isNotNullOrUndefinedOrEmpty(value: T | undefined | null): boolean { + return isNotNullOrUndefined(value) && value !== ''; +} + // It will use for pass nested object or array in query params in get method. export function toParams(query: any) { let params: HttpParams = new HttpParams(); diff --git a/packages/ui-sdk/src/lib/core/src/services/navigation/navigation.service.ts b/packages/ui-sdk/src/lib/core/src/services/navigation/navigation.service.ts index e8197d75cf1..7300454a434 100644 --- a/packages/ui-sdk/src/lib/core/src/services/navigation/navigation.service.ts +++ b/packages/ui-sdk/src/lib/core/src/services/navigation/navigation.service.ts @@ -1,6 +1,7 @@ import { Injectable } from '@angular/core'; import { ActivatedRoute, QueryParamsHandling, Router } from '@angular/router'; import { Location } from '@angular/common'; +import { isNotNullOrUndefinedOrEmpty } from '@gauzy/ui-sdk/common'; @Injectable({ providedIn: 'root' @@ -70,7 +71,7 @@ export class NavigationService { const uniqueQueryParams: { [key: string]: string | string[] | boolean } = {}; for (const key in finalQueryParams) { const value = finalQueryParams[key]; - if (typeof value != 'undefined') { + if (isNotNullOrUndefinedOrEmpty(value)) { if (Array.isArray(value)) { uniqueQueryParams[key] = Array.from(new Set(value)); } else {