diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 3a66fc54cc2..c5370df3f7c 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -43,7 +43,6 @@ "@gauzy/contracts": "^0.1.0", "@gauzy/desktop-ui-lib": "^0.1.0", "@gauzy/ui-config": "^0.1.0", - "@gauzy/ui-core": "^0.1.0", "@grpc/grpc-js": "^1.6.7", "@nebular/auth": "^12.0.0", "@nebular/bootstrap": "^9.1.0-rc.6", diff --git a/apps/desktop/src/index.ts b/apps/desktop/src/index.ts index 5a4b90ece56..0b765566938 100644 --- a/apps/desktop/src/index.ts +++ b/apps/desktop/src/index.ts @@ -5,10 +5,10 @@ import log from 'electron-log'; console.log = log.log; Object.assign(console, log.functions); -import * as path from 'path'; -import { app, BrowserWindow, ipcMain, Menu, shell, MenuItemConstructorOptions, dialog, nativeTheme } from 'electron'; import * as remoteMain from '@electron/remote/main'; import { setupTitlebar } from 'custom-electron-titlebar/main'; +import { BrowserWindow, Menu, MenuItemConstructorOptions, app, dialog, ipcMain, nativeTheme, shell } from 'electron'; +import * as path from 'path'; import { environment } from './environments/environment'; @@ -29,6 +29,7 @@ import { AppError, AppMenu, DesktopServer, + DesktopThemeListener, DesktopUpdater, DialogErrorHandler, ErrorEventManager, @@ -43,8 +44,7 @@ import { ipcMainHandler, ipcTimer, removeMainListener, - removeTimerListener, - DesktopThemeListener + removeTimerListener } from '@gauzy/desktop-libs'; import { AlwaysOn, @@ -57,10 +57,10 @@ import { createTimeTrackerWindow, createUpdaterWindow } from '@gauzy/desktop-window'; -import { initSentry } from './sentry'; +import * as Sentry from '@sentry/electron'; import { fork } from 'child_process'; import { autoUpdater } from 'electron-updater'; -import * as Sentry from '@sentry/electron'; +import { initSentry } from './sentry'; // the folder where all app data will be stored (e.g. sqlite DB, settings, cache, etc) // C:\Users\USERNAME\AppData\Roaming\gauzy-desktop @@ -535,7 +535,7 @@ app.on('ready', async () => { timeTrackerWindow, settingsWindow, updaterWindow, - imageViewWindow: imageView, + imageViewerWindow: imageView, gauzyWindow, splashScreenWindow: splashScreen.browserWindow, alwaysOnWindow: alwaysOn.browserWindow diff --git a/apps/desktop/src/package.json b/apps/desktop/src/package.json index 525acd156ee..f5452aa0e7f 100644 --- a/apps/desktop/src/package.json +++ b/apps/desktop/src/package.json @@ -29,6 +29,7 @@ "../../../packages/config", "../../../packages/contracts", "../../../packages/ui-config", + "../../../packages/ui-core", "../../../packages/plugin", "../../../packages/plugins/integration-ai", "../../../packages/plugins/integration-hubstaff", diff --git a/apps/gauzy/package.json b/apps/gauzy/package.json index 5d41c85beb5..584846b9852 100644 --- a/apps/gauzy/package.json +++ b/apps/gauzy/package.json @@ -68,7 +68,6 @@ "@gauzy/plugin-onboarding-ui": "^0.1.0", "@gauzy/plugin-public-layout-ui": "^0.1.0", "@gauzy/ui-config": "^0.1.0", - "@gauzy/ui-core": "^0.1.0", "@jitsu/js": "^1.8.2", "@kurkle/color": "^0.2.0", "@nebular/auth": "^12.0.0", diff --git a/apps/gauzy/src/app/pages/dashboard/dashboard.module.ts b/apps/gauzy/src/app/pages/dashboard/dashboard.module.ts index 081f2109e83..4200f5f303b 100644 --- a/apps/gauzy/src/app/pages/dashboard/dashboard.module.ts +++ b/apps/gauzy/src/app/pages/dashboard/dashboard.module.ts @@ -1,31 +1,45 @@ import { NgModule } from '@angular/core'; import { NbAlertModule, + NbBadgeModule, NbButtonModule, NbCardModule, NbDialogModule, NbIconModule, NbInputModule, + NbListModule, + NbPopoverModule, + NbProgressBarModule, NbRouteTabsetModule, NbSelectModule, NbSpinnerModule, + NbToggleModule, NbTooltipModule, NbTreeGridModule } from '@nebular/theme'; import { NgSelectModule } from '@ng-select/ng-select'; +import { TranslateModule } from '@ngx-translate/core'; +import { NgChartsModule } from 'ng2-charts'; import { InfiniteScrollModule } from 'ngx-infinite-scroll'; import { NgxPermissionsModule } from 'ngx-permissions'; -import { TranslateModule } from '@ngx-translate/core'; +import { SwiperModule } from 'swiper/angular'; import { + ActivityItemModule, + CounterPointModule, DynamicTabsModule, + GalleryModule, InfoBlockModule, LineChartModule, NoDataMessageModule, ProfitHistoryModule, RecordsHistoryModule, + ScreenshotsItemModule, SharedModule, SingleStatisticModule, TableComponentsModule, + TimezoneFilterModule, + WidgetLayoutModule, + WindowLayoutModule, WorkInProgressModule } from '@gauzy/ui-core/shared'; import { DashboardRoutingModule } from './dashboard-routing.module'; @@ -34,22 +48,32 @@ import { DataEntryShortcutsComponent } from './data-entry-shortcuts/data-entry-s import { HumanResourcesComponent } from './human-resources/human-resources.component'; import { AccountingComponent } from './accounting/accounting.component'; import { ProjectManagementComponent } from './project-management/project-management.component'; -import { EmployeeChartsModule } from './human-resources/employee-charts/employee-charts.module'; -import { TimeTrackingModule } from './time-tracking/time-tracking.module'; import { ProjectManagementDetailsComponent } from './project-management/project-management-details/project-management-details.component'; -import { TeamModule } from './team/team.module'; +import { TimeTrackingComponent } from './time-tracking/time-tracking.component'; +import { + EmployeeChartsComponent, + EmployeeDoughnutChartComponent, + EmployeeHorizontalBarChartComponent, + EmployeeStackedBarChartComponent +} from './employee-charts'; +import { AllTeamComponent, ChartComponent, TeamCardComponent, TeamComponent, TeamMemberComponent } from './team'; // NB Modules const NB_MODULES = [ NbAlertModule, + NbBadgeModule, NbButtonModule, NbCardModule, NbDialogModule.forChild(), NbIconModule, NbInputModule, + NbListModule, + NbPopoverModule, + NbProgressBarModule, NbRouteTabsetModule, NbSelectModule, NbSpinnerModule, + NbToggleModule, NbTooltipModule, NbTreeGridModule ]; @@ -60,9 +84,31 @@ const THIRD_PARTY_MODULES = [ LineChartModule, NgSelectModule, NgxPermissionsModule.forChild(), + NgChartsModule, + SwiperModule, TranslateModule.forChild() ]; +// Components +const COMPONENTS = [ + DashboardComponent, + DataEntryShortcutsComponent, + AccountingComponent, + HumanResourcesComponent, + ProjectManagementComponent, + ProjectManagementDetailsComponent, + TimeTrackingComponent, + EmployeeChartsComponent, + EmployeeHorizontalBarChartComponent, + EmployeeStackedBarChartComponent, + EmployeeDoughnutChartComponent, + TeamComponent, + TeamCardComponent, + TeamMemberComponent, + ChartComponent, + AllTeamComponent +]; + @NgModule({ imports: [ DashboardRoutingModule, @@ -71,26 +117,21 @@ const THIRD_PARTY_MODULES = [ // Feature Modules RecordsHistoryModule, ProfitHistoryModule, - EmployeeChartsModule, SingleStatisticModule, InfoBlockModule, - TimeTrackingModule, - TeamModule, - // Shared Modules SharedModule, TableComponentsModule, NoDataMessageModule, WorkInProgressModule, - // Custom Modules - DynamicTabsModule + ActivityItemModule, + CounterPointModule, + DynamicTabsModule, + GalleryModule, + ScreenshotsItemModule, + TimezoneFilterModule, + WidgetLayoutModule, + WindowLayoutModule ], - declarations: [ - DashboardComponent, - AccountingComponent, - HumanResourcesComponent, - DataEntryShortcutsComponent, - ProjectManagementComponent, - ProjectManagementDetailsComponent - ] + declarations: [...COMPONENTS] }) export class DashboardModule {} diff --git a/apps/gauzy/src/app/pages/dashboard/human-resources/employee-charts/employee-charts.component.html b/apps/gauzy/src/app/pages/dashboard/employee-charts/employee-charts.component.html similarity index 100% rename from apps/gauzy/src/app/pages/dashboard/human-resources/employee-charts/employee-charts.component.html rename to apps/gauzy/src/app/pages/dashboard/employee-charts/employee-charts.component.html diff --git a/apps/gauzy/src/app/pages/dashboard/human-resources/employee-charts/employee-charts.component.scss b/apps/gauzy/src/app/pages/dashboard/employee-charts/employee-charts.component.scss similarity index 100% rename from apps/gauzy/src/app/pages/dashboard/human-resources/employee-charts/employee-charts.component.scss rename to apps/gauzy/src/app/pages/dashboard/employee-charts/employee-charts.component.scss diff --git a/apps/gauzy/src/app/pages/dashboard/human-resources/employee-charts/employee-charts.component.ts b/apps/gauzy/src/app/pages/dashboard/employee-charts/employee-charts.component.ts similarity index 96% rename from apps/gauzy/src/app/pages/dashboard/human-resources/employee-charts/employee-charts.component.ts rename to apps/gauzy/src/app/pages/dashboard/employee-charts/employee-charts.component.ts index 6a951aab8f3..383922edd8f 100644 --- a/apps/gauzy/src/app/pages/dashboard/human-resources/employee-charts/employee-charts.component.ts +++ b/apps/gauzy/src/app/pages/dashboard/employee-charts/employee-charts.component.ts @@ -16,9 +16,7 @@ export class EmployeeChartsComponent implements OnInit { selectedChart = EmployeeChartEnum.BAR; EmployeeChartEnum: typeof EmployeeChartEnum = EmployeeChartEnum; - @Input() employeeStatistics: IMonthAggregatedEmployeeStatistics[]; - - constructor() {} + @Input() employeeStatistics: IMonthAggregatedEmployeeStatistics[] = []; ngOnInit() {} } diff --git a/apps/gauzy/src/app/pages/dashboard/human-resources/employee-charts/employee-doughnut-chart/employee-doughnut-chart.component.ts b/apps/gauzy/src/app/pages/dashboard/employee-charts/employee-doughnut-chart/employee-doughnut-chart.component.ts similarity index 100% rename from apps/gauzy/src/app/pages/dashboard/human-resources/employee-charts/employee-doughnut-chart/employee-doughnut-chart.component.ts rename to apps/gauzy/src/app/pages/dashboard/employee-charts/employee-doughnut-chart/employee-doughnut-chart.component.ts diff --git a/apps/gauzy/src/app/pages/dashboard/human-resources/employee-charts/employee-horizontal-bar-chart/employee-horizontal-bar-chart.component.ts b/apps/gauzy/src/app/pages/dashboard/employee-charts/employee-horizontal-bar-chart/employee-horizontal-bar-chart.component.ts similarity index 100% rename from apps/gauzy/src/app/pages/dashboard/human-resources/employee-charts/employee-horizontal-bar-chart/employee-horizontal-bar-chart.component.ts rename to apps/gauzy/src/app/pages/dashboard/employee-charts/employee-horizontal-bar-chart/employee-horizontal-bar-chart.component.ts index 4af862bbf2d..42841a07bda 100644 --- a/apps/gauzy/src/app/pages/dashboard/human-resources/employee-charts/employee-horizontal-bar-chart/employee-horizontal-bar-chart.component.ts +++ b/apps/gauzy/src/app/pages/dashboard/employee-charts/employee-horizontal-bar-chart/employee-horizontal-bar-chart.component.ts @@ -7,11 +7,11 @@ import { debounceTime, filter, tap } from 'rxjs/operators'; import { BaseChartDirective } from 'ng2-charts'; import { ChartConfiguration, ChartType } from 'chart.js'; import { environment } from '@gauzy/ui-config'; -import { TranslationBaseComponent } from '@gauzy/ui-core/i18n'; import { CurrencyPosition, IMonthAggregatedEmployeeStatistics, IOrganization } from '@gauzy/contracts'; import { distinctUntilChange } from '@gauzy/ui-core/common'; -import { CurrencyPositionPipe } from '@gauzy/ui-core/shared'; import { Store, months } from '@gauzy/ui-core/core'; +import { TranslationBaseComponent } from '@gauzy/ui-core/i18n'; +import { CurrencyPositionPipe } from '@gauzy/ui-core/shared'; @UntilDestroy({ checkProperties: true }) @Component({ diff --git a/apps/gauzy/src/app/pages/dashboard/human-resources/employee-charts/employee-stacked-bar-chart/employee-stacked-bar-chart.component.ts b/apps/gauzy/src/app/pages/dashboard/employee-charts/employee-stacked-bar-chart/employee-stacked-bar-chart.component.ts similarity index 100% rename from apps/gauzy/src/app/pages/dashboard/human-resources/employee-charts/employee-stacked-bar-chart/employee-stacked-bar-chart.component.ts rename to apps/gauzy/src/app/pages/dashboard/employee-charts/employee-stacked-bar-chart/employee-stacked-bar-chart.component.ts index 487b7c39b88..d4aa1c52bb2 100644 --- a/apps/gauzy/src/app/pages/dashboard/human-resources/employee-charts/employee-stacked-bar-chart/employee-stacked-bar-chart.component.ts +++ b/apps/gauzy/src/app/pages/dashboard/employee-charts/employee-stacked-bar-chart/employee-stacked-bar-chart.component.ts @@ -2,8 +2,8 @@ import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; import { NbThemeService } from '@nebular/theme'; import { TranslateService } from '@ngx-translate/core'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; -import { monthNames } from '@gauzy/ui-core/core'; import { IMonthAggregatedEmployeeStatistics } from '@gauzy/contracts'; +import { monthNames } from '@gauzy/ui-core/core'; import { TranslationBaseComponent } from '@gauzy/ui-core/i18n'; @UntilDestroy() diff --git a/apps/gauzy/src/app/pages/dashboard/employee-charts/index.ts b/apps/gauzy/src/app/pages/dashboard/employee-charts/index.ts new file mode 100644 index 00000000000..2dd3ef3d92d --- /dev/null +++ b/apps/gauzy/src/app/pages/dashboard/employee-charts/index.ts @@ -0,0 +1,4 @@ +export * from './employee-charts.component'; +export * from './employee-stacked-bar-chart/employee-stacked-bar-chart.component'; +export * from './employee-doughnut-chart/employee-doughnut-chart.component'; +export * from './employee-horizontal-bar-chart/employee-horizontal-bar-chart.component'; diff --git a/apps/gauzy/src/app/pages/dashboard/human-resources/employee-charts/employee-charts.module.ts b/apps/gauzy/src/app/pages/dashboard/human-resources/employee-charts/employee-charts.module.ts deleted file mode 100644 index 248f22afd26..00000000000 --- a/apps/gauzy/src/app/pages/dashboard/human-resources/employee-charts/employee-charts.module.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { NbIconModule, NbSelectModule } from '@nebular/theme'; -import { NgChartsModule } from 'ng2-charts'; -import { TranslateModule } from '@ngx-translate/core'; -import { EmployeeChartsComponent } from './employee-charts.component'; -import { EmployeeDoughnutChartComponent } from './employee-doughnut-chart/employee-doughnut-chart.component'; -import { EmployeeHorizontalBarChartComponent } from './employee-horizontal-bar-chart/employee-horizontal-bar-chart.component'; -import { EmployeeStackedBarChartComponent } from './employee-stacked-bar-chart/employee-stacked-bar-chart.component'; - -@NgModule({ - imports: [CommonModule, NgChartsModule, NbIconModule, NbSelectModule, TranslateModule.forChild()], - exports: [EmployeeChartsComponent], - declarations: [ - EmployeeChartsComponent, - EmployeeHorizontalBarChartComponent, - EmployeeStackedBarChartComponent, - EmployeeDoughnutChartComponent - ] -}) -export class EmployeeChartsModule {} diff --git a/apps/gauzy/src/app/pages/dashboard/project-management/project-management-details/project-management-details.component.html b/apps/gauzy/src/app/pages/dashboard/project-management/project-management-details/project-management-details.component.html index 4b4e93b946c..a8ddebd7cb2 100644 --- a/apps/gauzy/src/app/pages/dashboard/project-management/project-management-details/project-management-details.component.html +++ b/apps/gauzy/src/app/pages/dashboard/project-management/project-management-details/project-management-details.component.html @@ -39,9 +39,7 @@ - +
@@ -51,10 +49,7 @@
- + {{ 'DASHBOARD_PAGE.MOST_VIEW_PROJECTS' | translate }} @@ -72,15 +67,11 @@
- +
- +
@@ -101,10 +92,7 @@ > {{ 'BUTTONS.VIEW_ALL' | translate }} -
+
diff --git a/apps/gauzy/src/app/pages/dashboard/project-management/project-management-details/project-management-details.component.ts b/apps/gauzy/src/app/pages/dashboard/project-management/project-management-details/project-management-details.component.ts index 30d1181641a..15a6a928799 100644 --- a/apps/gauzy/src/app/pages/dashboard/project-management/project-management-details/project-management-details.component.ts +++ b/apps/gauzy/src/app/pages/dashboard/project-management/project-management-details/project-management-details.component.ts @@ -1,14 +1,14 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; -import { Router } from '@angular/router'; import { HttpClient } from '@angular/common/http'; +import { Router } from '@angular/router'; import { combineLatest, debounceTime, filter, first, firstValueFrom, Subject } from 'rxjs'; import { tap } from 'rxjs/operators'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { TranslateService } from '@ngx-translate/core'; -import { pluck } from 'underscore'; import { NbDialogService } from '@nebular/theme'; -import { ErrorHandlingService, ServerDataSource, Store, TasksService } from '@gauzy/ui-core/core'; +import { pluck } from 'underscore'; import { + ID, IOrganization, IOrganizationProject, ISelectedEmployee, @@ -16,6 +16,7 @@ import { PermissionsEnum, TaskStatusEnum } from '@gauzy/contracts'; +import { ErrorHandlingService, ServerDataSource, Store, TasksService } from '@gauzy/ui-core/core'; import { API_PREFIX, distinctUntilChange } from '@gauzy/ui-core/common'; import { AddTaskDialogComponent, PaginationFilterBaseComponent } from '@gauzy/ui-core/shared'; import { MyTaskDialogComponent } from '../../../tasks/components/my-task-dialog/my-task-dialog.component'; @@ -30,8 +31,8 @@ export class ProjectManagementDetailsComponent extends PaginationFilterBaseCompo private _smartTableSource: ServerDataSource; private _tasks: ITask[] = []; private _selectedEmployee: ISelectedEmployee; - public selectedEmployeeId: ISelectedEmployee['id']; - private selectedProjectId: IOrganizationProject['id']; + public selectedEmployeeId: ID; + private selectedProjectId: ID; private _organization: IOrganization; private _task$: Subject = this.subject$; private _projects: IOrganizationProject[] = []; @@ -101,26 +102,14 @@ export class ProjectManagementDetailsComponent extends PaginationFilterBaseCompo this._smartTableSource = new ServerDataSource(this._httpClient, { ...(this.selectedEmployeeId - ? { - endPoint: `${API_PREFIX}/tasks/employee` - } - : { - endPoint: `${API_PREFIX}/tasks/pagination` - }), + ? { endPoint: `${API_PREFIX}/tasks/employee` } + : { endPoint: `${API_PREFIX}/tasks/pagination` }), relations: ['project', 'tags'], where: { organizationId, tenantId, - ...(this.selectedEmployeeId - ? { - employeeId: this.selectedEmployeeId - } - : {}), - ...(this.selectedProjectId - ? { - projectId: this.selectedProjectId - } - : {}), + ...(this.selectedEmployeeId ? { employeeId: this.selectedEmployeeId } : {}), + ...(this.selectedProjectId ? { projectId: this.selectedProjectId } : {}), ...(this.filters.where ? this.filters.where : {}) } }); diff --git a/apps/gauzy/src/app/pages/dashboard/project-management/project-management.component.html b/apps/gauzy/src/app/pages/dashboard/project-management/project-management.component.html index 698dfbe4ffd..420e0e27e96 100644 --- a/apps/gauzy/src/app/pages/dashboard/project-management/project-management.component.html +++ b/apps/gauzy/src/app/pages/dashboard/project-management/project-management.component.html @@ -5,9 +5,7 @@

{{ 'DASHBOARD_PAGE.PROJECT_MANAGEMENT' | translate }}

- + -
{{'ORGANIZATIONS_PAGE.TEAMS' | translate}}
+
{{ 'ORGANIZATIONS_PAGE.TEAMS' | translate }}
- {{organization?.statistics?.countWorking}} + {{ organization?.statistics?.countWorking }}
-
/{{organization?.statistics?.countTeams}}
+
/{{ organization?.statistics?.countTeams }}
@@ -27,9 +27,9 @@
{{ 'TIMESHEET.MEMBERS_WORKED' | translate }}
- {{organization?.statistics?.counts?.employeesCount || 0}} + {{ organization?.statistics?.counts?.employeesCount || 0 }}
-
/{{organization?.statistics?.counts?.employeesCountTotal || 0}}
+
/{{ organization?.statistics?.counts?.employeesCountTotal || 0 }}
{{ 'TIMESHEET.PROJECTS_WORKED' | translate }}
- {{organization?.statistics?.counts?.projectsCount || 0}} + {{ organization?.statistics?.counts?.projectsCount || 0 }}
-
/{{projectCount}}
+
/{{ projectCount }}
{{ 'TIMESHEET.PROJECTS_WORKED' | translate }} [value]="organization?.statistics?.counts?.projectsCount" >
-
@@ -66,7 +65,7 @@
{{ 'TIMESHEET.WORKED_FOR_DAY' | translate }}
- {{organization?.statistics?.counts?.weekActivities || 0}} + {{ organization?.statistics?.counts?.weekActivities || 0 }}
%
@@ -74,7 +73,7 @@
{{ 'TIMESHEET.WORKED_FOR_DAY' | translate }}
@@ -85,29 +84,39 @@
{{ 'TIMESHEET.WORKED_FOR_DAY' | translate }}
-
{{team?.statistics?.countWorking}}
-
/{{team?.statistics?.countWorking + team?.statistics.countNotWorking}}
+
{{ team?.statistics?.countWorking }}
+
/{{ team?.statistics?.countWorking + team?.statistics.countNotWorking }}
- {{team?.name}} + {{ team?.name }}
+ status="success" + [text]=" + ('DASHBOARD_PAGE.CHARTS.WORKING_NOW' | translate) + ' ' + team?.statistics?.countOnline + " + >
+ status="warning" + [text]=" + ('DASHBOARD_PAGE.CHARTS.WORKING_TODAY' | translate) + + ' ' + + (team?.statistics?.countWorking - team?.statistics?.countOnline).toString() + " + >
+ status="danger" + [text]=" + ('DASHBOARD_PAGE.CHARTS.NOT_WORKING' | translate) + + ' ' + + team?.statistics?.countNotWorking + " + >
@@ -118,9 +127,8 @@
{{ 'TIMESHEET.WORKED_FOR_DAY' | translate }}
- - + +
diff --git a/apps/gauzy/src/app/pages/dashboard/team/all-team/all-team.component.ts b/apps/gauzy/src/app/pages/dashboard/team/all-team/all-team.component.ts index 2288f17a312..df7b0c1e580 100644 --- a/apps/gauzy/src/app/pages/dashboard/team/all-team/all-team.component.ts +++ b/apps/gauzy/src/app/pages/dashboard/team/all-team/all-team.component.ts @@ -1,7 +1,6 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { UntilDestroy } from '@ngneat/until-destroy'; -import { Store } from '@gauzy/ui-core/core'; -import { OrganizationProjectsService } from '@gauzy/ui-core/core'; +import { OrganizationProjectsService, Store } from '@gauzy/ui-core/core'; @UntilDestroy({ checkProperties: true }) @Component({ diff --git a/apps/gauzy/src/app/pages/dashboard/team/chart/chart.component.ts b/apps/gauzy/src/app/pages/dashboard/team/chart/chart.component.ts index 5ce74085fcc..dc00d560f7f 100644 --- a/apps/gauzy/src/app/pages/dashboard/team/chart/chart.component.ts +++ b/apps/gauzy/src/app/pages/dashboard/team/chart/chart.component.ts @@ -7,7 +7,7 @@ import { TranslationBaseComponent } from '@gauzy/ui-core/i18n'; @UntilDestroy({ checkProperties: true }) @Component({ - selector: 'gauzy-chart', + selector: 'gz-doughnut-chart', templateUrl: './chart.component.html', styleUrls: ['./chart.component.scss'] }) @@ -23,13 +23,14 @@ export class ChartComponent extends TranslationBaseComponent implements OnInit, } private get _labels() { + // Retrieve the statistics + const { countOnline, countWorking, countNotWorking } = this.statistics; + // Build the labels return { labels: [ - `${this.getTranslation('DASHBOARD_PAGE.CHARTS.WORKING_NOW')}: ${this.statistics.countOnline}`, - `${this.getTranslation('DASHBOARD_PAGE.CHARTS.WORKING')}: ${ - this.statistics.countWorking - this.statistics.countOnline - }`, - `${this.getTranslation('DASHBOARD_PAGE.CHARTS.NOT_WORKING')}: ${this.statistics.countNotWorking}` + `${this.getTranslation('DASHBOARD_PAGE.CHARTS.WORKING_NOW')}: ${countOnline}`, + `${this.getTranslation('DASHBOARD_PAGE.CHARTS.WORKING')}: ${countWorking - countOnline}`, + `${this.getTranslation('DASHBOARD_PAGE.CHARTS.NOT_WORKING')}: ${countNotWorking}` ] }; } diff --git a/apps/gauzy/src/app/pages/dashboard/team/index.ts b/apps/gauzy/src/app/pages/dashboard/team/index.ts new file mode 100644 index 00000000000..6fa5cbe50a1 --- /dev/null +++ b/apps/gauzy/src/app/pages/dashboard/team/index.ts @@ -0,0 +1,5 @@ +export * from './all-team/all-team.component'; +export * from './team-card/team-card.component'; +export * from './team-member/team-member.component'; +export * from './team.component'; +export * from './chart/chart.component'; diff --git a/apps/gauzy/src/app/pages/dashboard/team/team-card/team-card.component.ts b/apps/gauzy/src/app/pages/dashboard/team/team-card/team-card.component.ts index 3dcbe4de865..82c7ee03305 100644 --- a/apps/gauzy/src/app/pages/dashboard/team/team-card/team-card.component.ts +++ b/apps/gauzy/src/app/pages/dashboard/team/team-card/team-card.component.ts @@ -1,46 +1,33 @@ import { Component, Input, OnInit } from '@angular/core'; -import { IEmployee, IOrganizationTeam, RolesEnum } from "@gauzy/contracts"; +import { IEmployee, IOrganizationTeam, RolesEnum } from '@gauzy/contracts'; @Component({ - selector: 'gauzy-team-card', - templateUrl: './team-card.component.html', - styleUrls: ['./team-card.component.scss'] + selector: 'gauzy-team-card', + templateUrl: './team-card.component.html', + styleUrls: ['./team-card.component.scss'] }) export class TeamCardComponent implements OnInit { - constructor() { - } - - private _managers: IEmployee[]; - - get managers(): IEmployee[] { - return this._team.members - .filter( - (member) => - member.role && - member.role.name === RolesEnum.MANAGER - ) - .map((item) => item.employee); - } - - private _members: IEmployee[]; - - get members(): IEmployee[] { - return this._team.members - .filter((member) => !member.role) - .map((item) => item.employee); - } - - private _team: IOrganizationTeam | any; - - public get team(): IOrganizationTeam | any { - return this._team; - } - - @Input() - public set team(value: IOrganizationTeam | any) { - this._team = value; - }; - - ngOnInit(): void { - } + constructor() {} + + _managers: IEmployee[]; + get managers(): IEmployee[] { + return this._team.members + .filter((member) => member.role && member.role.name === RolesEnum.MANAGER) + .map((item) => item.employee); + } + + _members: IEmployee[]; + get members(): IEmployee[] { + return this._team.members.filter((member) => !member.role).map((item) => item.employee); + } + + private _team: IOrganizationTeam | any; + public get team(): IOrganizationTeam | any { + return this._team; + } + @Input() public set team(value: IOrganizationTeam | any) { + this._team = value; + } + + ngOnInit(): void {} } diff --git a/apps/gauzy/src/app/pages/dashboard/team/team-member/team-member.component.ts b/apps/gauzy/src/app/pages/dashboard/team/team-member/team-member.component.ts index f92f22c24a8..72038de7285 100644 --- a/apps/gauzy/src/app/pages/dashboard/team/team-member/team-member.component.ts +++ b/apps/gauzy/src/app/pages/dashboard/team/team-member/team-member.component.ts @@ -1,7 +1,7 @@ import { Component, Input, OnInit } from '@angular/core'; +import * as moment from 'moment'; import { IOrganizationTeamEmployee } from '@gauzy/contracts'; import { progressStatus } from '@gauzy/ui-core/common'; -import * as moment from 'moment'; @Component({ selector: 'gauzy-team-member', @@ -9,10 +9,6 @@ import * as moment from 'moment'; styleUrls: ['./team-member.component.scss'] }) export class TeamMemberComponent implements OnInit { - constructor() { - this._isClassic = false; - } - private _member: IOrganizationTeamEmployee | any; public get member(): IOrganizationTeamEmployee | any { @@ -49,5 +45,9 @@ export class TeamMemberComponent implements OnInit { return moment.duration(duration, 'seconds').humanize(); } + constructor() { + this._isClassic = false; + } + ngOnInit(): void {} } diff --git a/apps/gauzy/src/app/pages/dashboard/team/team.component.html b/apps/gauzy/src/app/pages/dashboard/team/team.component.html index 77fb7fd78e9..b5034381cfd 100644 --- a/apps/gauzy/src/app/pages/dashboard/team/team.component.html +++ b/apps/gauzy/src/app/pages/dashboard/team/team.component.html @@ -1,14 +1,20 @@ - -

+ +

-
- {{'ORGANIZATIONS_PAGE.TEAMS' | translate}} + {{ 'ORGANIZATIONS_PAGE.TEAMS' | translate }}

@@ -21,10 +27,10 @@

+ 'team-selected-card': selectedTeam?.isSelected && selectedTeam?.data.id === team?.id + }" + [team]="team" + >

@@ -36,28 +42,28 @@

- {{'TIMESHEET.STATUS'|translate}} + {{ 'TIMESHEET.STATUS' | translate }}
- {{'ORGANIZATIONS_PAGE.NAME'|translate}} + {{ 'ORGANIZATIONS_PAGE.NAME' | translate }}
- {{'MENU.IMPORT_EXPORT.TASK'|translate}} + {{ 'MENU.IMPORT_EXPORT.TASK' | translate }}
- {{'TIMESHEET.DURATION'|translate}} + {{ 'TIMESHEET.DURATION' | translate }}
- {{'MENU.IMPORT_EXPORT.PROGRESS'|translate}} + {{ 'MENU.IMPORT_EXPORT.PROGRESS' | translate }}
- {{'TASKS_PAGE.ESTIMATE'|translate}} + {{ 'TASKS_PAGE.ESTIMATE' | translate }}
@@ -70,27 +76,28 @@

-
- - - -
-
- -

- -
+
+ + + +
+
+ +
+ + + - + + - + diff --git a/apps/gauzy/src/app/pages/dashboard/team/team.component.ts b/apps/gauzy/src/app/pages/dashboard/team/team.component.ts index 6789b255b49..69cd2b2e021 100644 --- a/apps/gauzy/src/app/pages/dashboard/team/team.component.ts +++ b/apps/gauzy/src/app/pages/dashboard/team/team.component.ts @@ -3,12 +3,6 @@ import * as moment from 'moment'; import { combineLatest, debounceTime, tap } from 'rxjs'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { TranslateService } from '@ngx-translate/core'; -import { - DateRangePickerBuilderService, - OrganizationTeamsService, - TimesheetService, - TimesheetStatisticsService -} from '@gauzy/ui-core/core'; import { IGetCountsStatistics, IGetTimeLogReportInput, @@ -18,7 +12,14 @@ import { ITimeLog, ReportGroupFilterEnum } from '@gauzy/contracts'; -import { Store } from '@gauzy/ui-core/core'; +import { + DateRangePickerBuilderService, + ErrorHandlingService, + OrganizationTeamsService, + Store, + TimesheetService, + TimesheetStatisticsService +} from '@gauzy/ui-core/core'; import { BaseSelectorFilterComponent, TimeZoneService } from '@gauzy/ui-core/shared'; @UntilDestroy({ checkProperties: true }) @@ -35,13 +36,14 @@ export class TeamComponent extends BaseSelectorFilterComponent implements OnInit private _selectedOrganizationTeam: IOrganizationTeam; constructor( - private readonly _organizationTeamsService: OrganizationTeamsService, - private readonly _timesheetStatisticsService: TimesheetStatisticsService, - private readonly _timesheetService: TimesheetService, protected readonly translateService: TranslateService, protected readonly dateRangePickerBuilderService: DateRangePickerBuilderService, protected readonly store: Store, - protected readonly timeZoneService: TimeZoneService + protected readonly timeZoneService: TimeZoneService, + private readonly _organizationTeamsService: OrganizationTeamsService, + private readonly _timesheetStatisticsService: TimesheetStatisticsService, + private readonly _timesheetService: TimesheetService, + private readonly _errorHandlingService: ErrorHandlingService ) { super(store, translateService, dateRangePickerBuilderService, timeZoneService); this._selectedTeam = { @@ -160,8 +162,8 @@ export class TeamComponent extends BaseSelectorFilterComponent implements OnInit if (!this.organization) { return; } - const { id: organizationId } = this.organization; - const { tenantId } = this.store.user; + const { id: organizationId, tenantId } = this.organization; + this.isLoading = true; this._organizationTeamsService .getAll(['members', 'members.role', 'members.employee', 'members.employee.user'], { @@ -188,8 +190,8 @@ export class TeamComponent extends BaseSelectorFilterComponent implements OnInit this._dailyLogs = await this._timesheetService.getDailyReport(request); await this.teamMapper(); } catch (error) { - console.log(error); - } finally { + console.log('Error while loading logs', error); + this._errorHandlingService.handleError(error); } } diff --git a/apps/gauzy/src/app/pages/dashboard/team/team.module.ts b/apps/gauzy/src/app/pages/dashboard/team/team.module.ts deleted file mode 100644 index 334a6ecd398..00000000000 --- a/apps/gauzy/src/app/pages/dashboard/team/team.module.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { NgModule } from '@angular/core'; -import { - NbBadgeModule, - NbButtonModule, - NbCardModule, - NbIconModule, - NbProgressBarModule, - NbSpinnerModule -} from '@nebular/theme'; -import { NgChartsModule } from 'ng2-charts'; -import { TranslateModule } from '@ngx-translate/core'; -import { CounterPointModule, NoDataMessageModule, SharedModule } from '@gauzy/ui-core/shared'; -import { TeamComponent } from './team.component'; -import { TeamCardComponent } from './team-card/team-card.component'; -import { TeamMemberComponent } from './team-member/team-member.component'; -import { ChartComponent } from './chart/chart.component'; -import { AllTeamComponent } from './all-team/all-team.component'; - -@NgModule({ - declarations: [TeamComponent, TeamCardComponent, TeamMemberComponent, ChartComponent, AllTeamComponent], - imports: [ - NbBadgeModule, - NbButtonModule, - NbCardModule, - NbIconModule, - NbProgressBarModule, - NbSpinnerModule, - NgChartsModule, - SharedModule, - TranslateModule.forChild(), - CounterPointModule, - NoDataMessageModule - ] -}) -export class TeamModule {} diff --git a/apps/gauzy/src/app/pages/dashboard/time-tracking/time-tracking.module.ts b/apps/gauzy/src/app/pages/dashboard/time-tracking/time-tracking.module.ts deleted file mode 100644 index 23a37a064f4..00000000000 --- a/apps/gauzy/src/app/pages/dashboard/time-tracking/time-tracking.module.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { NgModule } from '@angular/core'; -import { - NbCardModule, - NbSpinnerModule, - NbListModule, - NbProgressBarModule, - NbBadgeModule, - NbToggleModule, - NbIconModule, - NbButtonModule, - NbPopoverModule -} from '@nebular/theme'; -import { NgChartsModule } from 'ng2-charts'; -import { NgxPermissionsModule } from 'ngx-permissions'; -import { TranslateModule } from '@ngx-translate/core'; -import { SwiperModule } from 'swiper/angular'; -import { TimeTrackingComponent } from './time-tracking.component'; -import { - ActivityItemModule, - CounterPointModule, - GalleryModule, - ScreenshotsItemModule, - SharedModule, - TimezoneFilterModule, - WidgetLayoutModule, - WindowLayoutModule -} from '@gauzy/ui-core/shared'; - -@NgModule({ - imports: [ - SharedModule, - NbCardModule, - NbButtonModule, - NbSpinnerModule, - NbListModule, - NbProgressBarModule, - NbToggleModule, - NbIconModule, - NbBadgeModule, - NbPopoverModule, - ScreenshotsItemModule, - ActivityItemModule, - NgChartsModule, - TranslateModule.forChild(), - GalleryModule, - CounterPointModule, - WidgetLayoutModule, - WindowLayoutModule, - SwiperModule, - TimezoneFilterModule, - NgxPermissionsModule.forChild() - ], - declarations: [TimeTrackingComponent], - exports: [TimeTrackingComponent] -}) -export class TimeTrackingModule {} diff --git a/apps/server/package.json b/apps/server/package.json index f7bab33daa1..37503e345b0 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -42,7 +42,6 @@ "@electron/remote": "^2.0.8", "@gauzy/desktop-ui-lib": "^0.1.0", "@gauzy/ui-config": "^0.1.0", - "@gauzy/ui-core": "^0.1.0", "@grpc/grpc-js": "^1.6.7", "@nebular/auth": "^12.0.0", "@nebular/bootstrap": "^9.1.0-rc.6", diff --git a/apps/server/src/package.json b/apps/server/src/package.json index 33b18c7e3ff..8e762b94898 100755 --- a/apps/server/src/package.json +++ b/apps/server/src/package.json @@ -30,6 +30,7 @@ "../../../packages/config", "../../../packages/contracts", "../../../packages/ui-config", + "../../../packages/ui-core", "../../../packages/plugin", "../../../packages/plugins/integration-ai", "../../../packages/plugins/integration-hubstaff", diff --git a/packages/core/src/time-tracking/time-log/commands/handlers/schedule-time-log-entries.handler.ts b/packages/core/src/time-tracking/time-log/commands/handlers/schedule-time-log-entries.handler.ts index cae7152815a..e252dcdfe21 100644 --- a/packages/core/src/time-tracking/time-log/commands/handlers/schedule-time-log-entries.handler.ts +++ b/packages/core/src/time-tracking/time-log/commands/handlers/schedule-time-log-entries.handler.ts @@ -1,11 +1,9 @@ import { ICommandHandler, CommandHandler } from '@nestjs/cqrs'; -import { InjectRepository } from '@nestjs/typeorm'; import { Brackets, SelectQueryBuilder, WhereExpressionBuilder } from 'typeorm'; import * as moment from 'moment'; -import { isEmpty, isNotEmpty } from "@gauzy/common"; -import { DatabaseTypeEnum, getConfig } from '@gauzy/config'; -import { prepareSQLQuery as p } from './../../../../database/database.helper'; +import { isEmpty, isNotEmpty } from '@gauzy/common'; import { ITimeLog } from '@gauzy/contracts'; +import { prepareSQLQuery as p } from './../../../../database/database.helper'; import { TimeLog } from './../../time-log.entity'; import { ScheduleTimeLogEntriesCommand } from '../schedule-time-log-entries.command'; import { RequestContext } from './../../../../core/context'; @@ -13,25 +11,31 @@ import { TypeOrmTimeLogRepository } from '../../repository/type-orm-time-log.rep @CommandHandler(ScheduleTimeLogEntriesCommand) export class ScheduleTimeLogEntriesHandler implements ICommandHandler { + constructor(private readonly typeOrmTimeLogRepository: TypeOrmTimeLogRepository) {} - constructor( - @InjectRepository(TimeLog) - private readonly typeOrmTimeLogRepository: TypeOrmTimeLogRepository, - ) { } - - public async execute(command: ScheduleTimeLogEntriesCommand) { + /** + * Schedule TimeLog Entries + * + * @param command + * @returns + */ + public async execute(command: ScheduleTimeLogEntriesCommand): Promise { const { timeLog } = command; let timeLogs: ITimeLog[] = []; + + // Query the timeLogs + const query = this.typeOrmTimeLogRepository.createQueryBuilder('time_log'); + query.setFindOptions({ + relations: { timeSlots: true } + }); + if (timeLog) { + // Get the tenantId + const tenantId = RequestContext.currentTenantId() || timeLog.tenantId; + + // Get the organizationId const { organizationId, employeeId } = timeLog; - const tenantId = RequestContext.currentTenantId(); - const query = this.typeOrmTimeLogRepository.createQueryBuilder('time_log'); - query.setFindOptions({ - relations: { - timeSlots: true - } - }); query.where((qb: SelectQueryBuilder) => { qb.andWhere( new Brackets((web: WhereExpressionBuilder) => { @@ -57,14 +61,7 @@ export class ScheduleTimeLogEntriesHandler implements ICommandHandler) => { qb.andWhere( new Brackets((web: WhereExpressionBuilder) => { @@ -79,54 +76,56 @@ export class ScheduleTimeLogEntriesHandler implements ICommandHandler 10 - ) { - console.log('Schedule Time Log Entry Updated StoppedAt Using StartedAt', timeLog.startedAt); - await this.typeOrmTimeLogRepository.save({ - id: timeLog.id, - stoppedAt: moment(timeLog.startedAt).add(10, 'seconds').toDate() - }); - } else if (isNotEmpty(timeLog.timeSlots)) { - let stoppedAt: any; - let slotDifference: any; - - const duration = timeLog.timeSlots.reduce( - (sum: number, current: any) => sum + current.duration, 0 - ); - /** - * Adjust stopped date as per database selection - */ - switch (getConfig().dbConnectionOptions.type) { - case DatabaseTypeEnum.sqlite: - case DatabaseTypeEnum.betterSqlite3: - stoppedAt = moment.utc(timeLog.startedAt).add(duration, 'seconds').format('YYYY-MM-DD HH:mm:ss.SSS'); - slotDifference = moment.utc(moment()).diff(stoppedAt, 'minutes'); - break; - case DatabaseTypeEnum.postgres: - case DatabaseTypeEnum.mysql: - stoppedAt = moment(timeLog.startedAt).add(duration, 'seconds').toDate(); - slotDifference = moment().diff(moment.utc(stoppedAt), 'minutes'); - break; - default: - throw Error(`cannot format startedAt, slotDifference due to unsupported database type: ${getConfig().dbConnectionOptions.type}`); + const { timeSlots } = timeLog; + + // Calculate the minutes difference + const minutes = moment().diff(moment.utc(timeLog.startedAt), 'minutes'); + + // Handle case where there are no time slots + if (isEmpty(timeLog.timeSlots)) { + // If the minutes difference is greater than 10, update the stoppedAt date + if (minutes > 10) { + console.log('Schedule Time Log Entry Updated StoppedAt Using StartedAt', timeLog.startedAt); + + // Calculate the stoppedAt date + const stoppedAt = moment.utc(timeLog.startedAt).add(10, 'seconds').toDate(); + + // Calculate the stoppedAt date + await this.typeOrmTimeLogRepository.save({ + id: timeLog.id, + stoppedAt + }); } + } + // Handle case where there are time slots + else if (isNotEmpty(timeLog.timeSlots)) { + // Calculate the duration + const duration = timeSlots.reduce((sum, { duration }) => sum + duration, 0); + + // Calculate the stoppedAt date + const stoppedAt = moment.utc(timeLog.startedAt).add(duration, 'seconds').toDate(); + + // Calculate the minutes difference + const minutes = moment.utc().diff(moment.utc(stoppedAt), 'minutes'); console.log('Schedule Time Log Entry Updated StoppedAt Using StoppedAt', stoppedAt); - if (slotDifference > 10) { + + // If the minutes difference is greater than 10, update the stoppedAt date + if (minutes > 10) { await this.typeOrmTimeLogRepository.save({ id: timeLog.id, - stoppedAt: stoppedAt + stoppedAt }); } } + /** * Stop previous pending timer anyway. * If we have any pending TimeLog entry diff --git a/packages/core/src/time-tracking/time-log/commands/schedule-time-log-entries.command.ts b/packages/core/src/time-tracking/time-log/commands/schedule-time-log-entries.command.ts index d81a17b07cb..650295cd6da 100644 --- a/packages/core/src/time-tracking/time-log/commands/schedule-time-log-entries.command.ts +++ b/packages/core/src/time-tracking/time-log/commands/schedule-time-log-entries.command.ts @@ -4,7 +4,5 @@ import { ITimeLog } from '@gauzy/contracts'; export class ScheduleTimeLogEntriesCommand implements ICommand { static readonly type = 'Adjust [TimeLog] Entries'; - constructor( - public readonly timeLog?: ITimeLog - ) {} -} \ No newline at end of file + constructor(public readonly timeLog?: ITimeLog) {} +} diff --git a/packages/core/src/time-tracking/time-log/commands/time-log-update.command.ts b/packages/core/src/time-tracking/time-log/commands/time-log-update.command.ts index e47579dc5b9..5e2cbf3a86f 100644 --- a/packages/core/src/time-tracking/time-log/commands/time-log-update.command.ts +++ b/packages/core/src/time-tracking/time-log/commands/time-log-update.command.ts @@ -1,4 +1,5 @@ import { ICommand } from '@nestjs/cqrs'; +import { ID } from '@gauzy/contracts'; import { TimeLog } from './../time-log.entity'; export class TimeLogUpdateCommand implements ICommand { @@ -6,7 +7,7 @@ export class TimeLogUpdateCommand implements ICommand { constructor( public readonly input: Partial, - public readonly id: TimeLog['id'] | TimeLog, + public readonly id: ID | TimeLog, public readonly manualTimeSlot?: boolean | null - ) { } + ) {} } diff --git a/packages/core/src/time-tracking/timer/timer.service.ts b/packages/core/src/time-tracking/timer/timer.service.ts index aa93d3c7944..3480f03928f 100644 --- a/packages/core/src/time-tracking/timer/timer.service.ts +++ b/packages/core/src/time-tracking/timer/timer.service.ts @@ -1,9 +1,4 @@ -import { - Injectable, - NotFoundException, - ForbiddenException, - BadRequestException, -} from '@nestjs/common'; +import { Injectable, NotFoundException, ForbiddenException, BadRequestException } from '@nestjs/common'; import { CommandBus } from '@nestjs/cqrs'; import { IsNull, Between, Not, In } from 'typeorm'; import * as moment from 'moment'; @@ -18,7 +13,7 @@ import { PermissionsEnum, ITimeSlot, IEmployee, - IEmployeeFindInput, + IEmployeeFindInput } from '@gauzy/contracts'; import { isNotEmpty } from '@gauzy/common'; import { TimeLog } from '../../core/entities/internal'; @@ -38,7 +33,7 @@ import { IGetConflictTimeLogCommand, ScheduleTimeLogEntriesCommand, TimeLogCreateCommand, - TimeLogUpdateCommand, + TimeLogUpdateCommand } from '../time-log/commands'; import { MikroOrmTimeLogRepository, TypeOrmTimeLogRepository } from '../time-log/repository'; import { TypeOrmEmployeeRepository, MikroOrmEmployeeRepository } from '../../employee/repository'; @@ -49,7 +44,6 @@ const ormType: MultiORM = getORMType(); @Injectable() export class TimerService { - protected ormType: MultiORM = ormType; constructor( @@ -58,7 +52,7 @@ export class TimerService { readonly typeOrmEmployeeRepository: TypeOrmEmployeeRepository, readonly mikroOrmEmployeeRepository: MikroOrmEmployeeRepository, readonly commandBus: CommandBus - ) { } + ) {} /** * Fetches an employee based on the provided query. @@ -66,7 +60,8 @@ export class TimerService { * @param query - The query parameters to find the employee. * @returns A Promise resolving to the employee entity or null. */ - async fetchEmployee(query: IEmployeeFindInput): Promise { // Replace 'Employee' with your actual Employee entity type + async fetchEmployee(query: IEmployeeFindInput): Promise { + // Replace 'Employee' with your actual Employee entity type switch (this.ormType) { case MultiORMEnum.MikroORM: return await this.mikroOrmEmployeeRepository.findOneByOptions(query); @@ -94,7 +89,7 @@ export class TimerService { if (!!permission && isNotEmpty(request.employeeId)) { const { employeeId } = request; - employee = await this.fetchEmployee({ id: employeeId, tenantId, organizationId, }); + employee = await this.fetchEmployee({ id: employeeId, tenantId, organizationId }); } else { const userId = RequestContext.currentUserId(); employee = await this.fetchEmployee({ userId, tenantId, organizationId }); @@ -146,7 +141,10 @@ export class TimerService { const parseMikroOrmOptions = parseTypeORMFindToMikroOrm(lastLogQueryParamsMikroOrm); // Get today's last log (running or completed) - lastLog = await this.mikroOrmTimeLogRepository.findOne(parseMikroOrmOptions.where, parseMikroOrmOptions.mikroOptions) as TimeLog; + lastLog = (await this.mikroOrmTimeLogRepository.findOne( + parseMikroOrmOptions.where, + parseMikroOrmOptions.mikroOptions + )) as TimeLog; break; case MultiORMEnum.TypeORM: @@ -167,7 +165,7 @@ export class TimerService { const status: ITimerStatus = { duration: 0, running: false, - lastLog: null, + lastLog: null }; // Calculate completed timelogs duration @@ -198,11 +196,13 @@ export class TimerService { moment.utc(request.startedAt).toDate() ); const { organizationId, source, logType } = request; + /** - * If source, logType not found in request then reject the request. + * If source or logType is not found in the request, reject the request. */ const c1 = Object.values(TimeLogSourceEnum).includes(source); const c2 = Object.values(TimeLogType).includes(logType); + if (!c1 || !c2) { throw new BadRequestException(); } @@ -212,13 +212,12 @@ export class TimerService { const employee = await this.typeOrmEmployeeRepository.findOneBy({ userId, - tenantId, + tenantId }); if (!employee) { - throw new NotFoundException( - "We couldn't find the employee you were looking for." - ); + throw new NotFoundException("We couldn't find the employee you were looking for."); } + const { id: employeeId } = employee; const lastLog = await this.getLastRunningLog(request); @@ -231,29 +230,26 @@ export class TimerService { * It will manage to create proper entires in database */ console.log('Schedule Time Log Entries Command', lastLog); - await this.commandBus.execute( - new ScheduleTimeLogEntriesCommand(lastLog) - ); + await this.commandBus.execute(new ScheduleTimeLogEntriesCommand(lastLog)); } - await this.typeOrmEmployeeRepository.update({ id: employeeId }, { - isOnline: true, // Employee status (Online/Offline) - isTrackingTime: true, // Employee time tracking status - }); + await this.typeOrmEmployeeRepository.update( + { id: employeeId }, + { + isOnline: true, // Employee status (Online/Offline) + isTrackingTime: true // Employee time tracking status + } + ); - const { - projectId, - taskId, - organizationContactId, - organizationTeamId, - description, - isBillable, - version, - } = request; + // Get the request parameters + const { projectId, taskId, organizationContactId, organizationTeamId } = request; + const { description, isBillable, version } = request; + // Get the current date const now = moment.utc().toDate(); const startedAt = request.startedAt ? moment.utc(request.startedAt).toDate() : now; + // Create the timeLog return await this.commandBus.execute( new TimeLogCreateCommand({ organizationId, @@ -271,7 +267,7 @@ export class TimerService { description: description || null, isBillable: isBillable || false, version: version || null, - isRunning: true, + isRunning: true }) ); } @@ -287,24 +283,32 @@ export class TimerService { '----------------------------------Stopped Timer Date----------------------------------', moment.utc(request.stoppedAt).toDate() ); - const { organizationId } = request; - const tenantId = RequestContext.currentTenantId() || request.tenantId; + // Get the user ID const userId = RequestContext.currentUserId(); + // Get the tenant ID + const tenantId = RequestContext.currentTenantId() || request.tenantId; + // Get the employee const employee = await this.typeOrmEmployeeRepository.findOneBy({ userId, - tenantId, + tenantId }); if (!employee) { throw new NotFoundException("We couldn't find the employee you were looking for."); } + // Get the employee ID const { id: employeeId } = employee; - await this.typeOrmEmployeeRepository.update({ id: employeeId }, { - isOnline: false, // Employee status (Online/Offline) - isTrackingTime: false, // Employee time tracking status - }); + + // Update the employee + await this.typeOrmEmployeeRepository.update( + { id: employeeId }, + { + isOnline: false, // Employee status (Online/Offline) + isTrackingTime: false // Employee time tracking status + } + ); let lastLog = await this.getLastRunningLog(request); if (!lastLog) { @@ -313,12 +317,15 @@ export class TimerService { * So, we have to first create start timer entry in database, then update stop timer entry. * It will manage to create proper entires in database */ - await this.startTimer(request); - lastLog = await this.getLastRunningLog(request); + lastLog = await this.startTimer(request); } + // Get the organization ID + const organizationId = request.organizationTeamId || employee.organizationId; + + // Get the current date and set the initial stoppedAt date const now = moment.utc().toDate(); - const stoppedAt = request.stoppedAt ? moment.utc(request.stoppedAt).toDate() : now; + let stoppedAt = moment.utc(request.stoppedAt ?? now).toDate(); /** Function that performs the date range validation */ try { @@ -327,11 +334,12 @@ export class TimerService { throw new BadRequestException(error); } + // Update the lastLog lastLog = await this.commandBus.execute( new TimeLogUpdateCommand( { stoppedAt, - isRunning: false, + isRunning: false }, lastLog.id, request.manualTimeSlot @@ -340,6 +348,7 @@ export class TimerService { console.log('Stop Timer Time Log', { lastLog }); try { + // Get conflicts time logs const conflicts = await this.commandBus.execute( new IGetConflictTimeLogCommand({ ignoreId: lastLog.id, @@ -347,45 +356,38 @@ export class TimerService { endDate: lastLog.stoppedAt, employeeId: lastLog.employeeId, organizationId: organizationId || lastLog.organizationId, - tenantId, + tenantId }) ); + console.log('Get Conflicts Time Logs', conflicts, { ignoreId: lastLog.id, startDate: lastLog.startedAt, endDate: lastLog.stoppedAt, employeeId: lastLog.employeeId, - organizationId: - request.organizationId || lastLog.organizationId, - tenantId, + organizationId: request.organizationId || lastLog.organizationId, + tenantId }); + + // If there are conflicts, delete them if (isNotEmpty(conflicts)) { const times: IDateRange = { start: new Date(lastLog.startedAt), - end: new Date(lastLog.stoppedAt), + end: new Date(lastLog.stoppedAt) }; - if (isNotEmpty(conflicts)) { - await Promise.all( - await conflicts.map(async (timeLog: ITimeLog) => { - const { timeSlots = [] } = timeLog; - timeSlots.map(async (timeSlot: ITimeSlot) => { - await this.commandBus.execute( - new DeleteTimeSpanCommand( - times, - timeLog, - timeSlot - ) - ); - }); - }) - ); - } + + // Delete conflicts + await Promise.all( + await conflicts.flatMap((timeLog: ITimeLog) => { + const { timeSlots = [] } = timeLog; + timeSlots.map(async (timeSlot: ITimeSlot) => { + await this.commandBus.execute(new DeleteTimeSpanCommand(times, timeLog, timeSlot)); + }); + }) + ); } } catch (error) { - console.error( - 'Error while deleting time span during conflicts timelogs', - error - ); + console.error('Error while deleting time span during conflicts timelogs', error); } return lastLog; @@ -412,38 +414,45 @@ export class TimerService { * @param request * @returns */ - private async getLastRunningLog(request: ITimerToggleInput) { + private async getLastRunningLog(request: ITimerToggleInput): Promise { const userId = RequestContext.currentUserId(); const tenantId = RequestContext.currentTenantId(); + // Replace 'Employee' with your actual Employee entity type const employee = await this.typeOrmEmployeeRepository.findOne({ - where: { - userId, - tenantId, - }, - relations: { - user: true, - }, + where: { userId, tenantId }, + relations: { user: true } }); + + // If employee is not found, throw a NotFoundException if (!employee) { throw new NotFoundException("We couldn't find the employee you were looking for."); } + + // Employee time tracking status if (!employee.isTrackingEnabled) { throw new ForbiddenException(`The time tracking functionality has been disabled for you.`); } + + // Get the employee ID const { id: employeeId } = employee; + + // Get the organization ID + const organizationId = request.organizationTeamId || employee.organizationId; + + // Return the last running log return await this.typeOrmTimeLogRepository.findOne({ where: { stoppedAt: Not(IsNull()), employeeId, tenantId, - organizationId: request.organizationId, - isRunning: true, + organizationId, + isRunning: true }, order: { startedAt: 'DESC', - createdAt: 'DESC', - }, + createdAt: 'DESC' + } }); } @@ -454,7 +463,6 @@ export class TimerService { * @returns The timer status for the employee. */ public async getTimerWorkedStatus(request: ITimerStatusInput): Promise { - const tenantId = RequestContext.currentTenantId() || request.tenantId; const { organizationId, organizationTeamId, source } = request; @@ -466,7 +474,9 @@ export class TimerService { // Check if the current user has any of the specified permissions if (RequestContext.hasAnyPermission(permissions)) { // If yes, set employeeIds based on request.employeeIds or request.employeeId - employeeIds = request.employeeIds ? request.employeeIds.filter(Boolean) : [request.employeeId].filter(Boolean); + employeeIds = request.employeeIds + ? request.employeeIds.filter(Boolean) + : [request.employeeId].filter(Boolean); } else { // EMPLOYEE have the ability to see only their own timer status const employeeId = RequestContext.currentEmployeeId(); @@ -480,9 +490,7 @@ export class TimerService { const knex = this.mikroOrmTimeLogRepository.getKnex(); // Construct your SQL query using knex - let sqlQuery = knex('time_log').select( - knex.raw('DISTINCT ON ("time_log"."employeeId") *') - ); + let sqlQuery = knex('time_log').select(knex.raw('DISTINCT ON ("time_log"."employeeId") *')); // Builds an SQL query with specific where clauses. sqlQuery.whereNotNull('startedAt'); @@ -495,8 +503,12 @@ export class TimerService { isArchived: false }); - if (source) { sqlQuery = sqlQuery.andWhere({ source }); } - if (organizationTeamId) { sqlQuery = sqlQuery.andWhere({ organizationTeamId }); } + if (source) { + sqlQuery = sqlQuery.andWhere({ source }); + } + if (organizationTeamId) { + sqlQuery = sqlQuery.andWhere({ organizationTeamId }); + } // Adds ordering to the SQL query. sqlQuery = sqlQuery.orderBy([ @@ -526,7 +538,7 @@ export class TimerService { const query = this.typeOrmTimeLogRepository.createQueryBuilder('time_log'); // query.innerJoin(`${query.alias}.timeSlots`, 'timeSlots'); query.setFindOptions({ - ...(request['relations'] ? { relations: request['relations'] } : {}), + ...(request['relations'] ? { relations: request['relations'] } : {}) }); query.where({ startedAt: Not(IsNull()), @@ -537,7 +549,7 @@ export class TimerService { isActive: true, isArchived: false, ...(isNotEmpty(source) ? { source } : {}), - ...(isNotEmpty(organizationTeamId) ? { organizationTeamId } : {}), + ...(isNotEmpty(organizationTeamId) ? { organizationTeamId } : {}) }); query.orderBy(p(`"${query.alias}"."employeeId"`), 'ASC'); // Adjust ORDER BY to match the SELECT list query.addOrderBy(p(`"${query.alias}"."startedAt"`), 'DESC'); @@ -551,12 +563,21 @@ export class TimerService { } /** Transform an array of ITimeLog objects into an array of ITimerStatus objects. */ - const statistics: ITimerStatus[] = lastLogs.map((lastLog: ITimeLog) => ({ - duration: lastLog?.duration || 0, - running: lastLog?.isRunning || false, - lastLog: lastLog || null, - timerStatus: lastLog?.isRunning ? 'running' : moment(lastLog?.stoppedAt).diff(new Date(), 'day') > 0 ? 'idle' : 'pause', - })); + const statistics: ITimerStatus[] = lastLogs.map((lastLog: ITimeLog) => { + const duration = lastLog?.duration ?? 0; + const running = lastLog?.isRunning ?? false; + const stoppedAt = lastLog?.stoppedAt ? moment(lastLog.stoppedAt) : moment().subtract(1, 'day'); // Default to 1 day ago if stoppedAt is not present + + // Calculate the timer status + const timerStatus = running ? 'running' : moment().diff(stoppedAt, 'days') > 0 ? 'idle' : 'pause'; + + return { + duration, + running, + lastLog: lastLog ?? null, + timerStatus + }; + }); /** * @returns An array of ITimerStatus objects. diff --git a/packages/plugins/job-employee-ui/package.json b/packages/plugins/job-employee-ui/package.json index a642b237315..7a5cd6c74c2 100644 --- a/packages/plugins/job-employee-ui/package.json +++ b/packages/plugins/job-employee-ui/package.json @@ -40,7 +40,6 @@ "@angular/forms": "^16.2.12", "@angular/router": "^16.2.12", "@gauzy/contracts": "^0.1.0", - "@gauzy/ui-core": "^0.1.0", "@nebular/theme": "^12.0.0", "@ngneat/until-destroy": "^9.2.0", "@ngx-translate/core": "^15.0.0", diff --git a/packages/plugins/job-proposal-ui/package.json b/packages/plugins/job-proposal-ui/package.json index f38e6b4a0dc..4df54349620 100644 --- a/packages/plugins/job-proposal-ui/package.json +++ b/packages/plugins/job-proposal-ui/package.json @@ -41,7 +41,6 @@ "@angular/platform-browser": "^16.2.12", "@angular/router": "^16.2.12", "@gauzy/contracts": "^0.1.0", - "@gauzy/ui-core": "^0.1.0", "@nebular/theme": "^12.0.0", "@ng-select/ng-select": "^11.2.0", "@ngneat/until-destroy": "^9.2.0", diff --git a/packages/plugins/job-search-ui/package.json b/packages/plugins/job-search-ui/package.json index 6cd4434065e..d69f015cf3f 100644 --- a/packages/plugins/job-search-ui/package.json +++ b/packages/plugins/job-search-ui/package.json @@ -41,7 +41,6 @@ "@angular/forms": "^16.2.12", "@angular/router": "^16.2.12", "@gauzy/contracts": "^0.1.0", - "@gauzy/ui-core": "^0.1.0", "@nebular/theme": "^12.0.0", "@ngneat/until-destroy": "^9.2.0", "@ngx-translate/core": "^15.0.0", diff --git a/packages/plugins/legal-ui/package.json b/packages/plugins/legal-ui/package.json index f4ed44a5c3e..608a002610e 100644 --- a/packages/plugins/legal-ui/package.json +++ b/packages/plugins/legal-ui/package.json @@ -36,7 +36,6 @@ }, "dependencies": { "@angular/router": "^16.2.12", - "@gauzy/ui-core": "^0.1.0", "@nebular/auth": "^12.0.0", "@ngx-translate/core": "^15.0.0", "tslib": "^2.6.2" diff --git a/packages/plugins/maintenance-ui/package.json b/packages/plugins/maintenance-ui/package.json index 49928573d18..42778d58a16 100644 --- a/packages/plugins/maintenance-ui/package.json +++ b/packages/plugins/maintenance-ui/package.json @@ -32,7 +32,6 @@ "@angular/router": "^16.2.12", "@gauzy/contracts": "^0.1.0", "@gauzy/ui-config": "^0.1.0", - "@gauzy/ui-core": "^0.1.0", "@nebular/theme": "^12.0.0", "@ngx-translate/core": "^15.0.0", "tslib": "^2.6.2" diff --git a/packages/plugins/onboarding-ui/package.json b/packages/plugins/onboarding-ui/package.json index 9d477606d16..4d2766e3b64 100644 --- a/packages/plugins/onboarding-ui/package.json +++ b/packages/plugins/onboarding-ui/package.json @@ -37,7 +37,6 @@ "dependencies": { "@angular/router": "^16.2.12", "@gauzy/contracts": "^0.1.0", - "@gauzy/ui-core": "^0.1.0", "@nebular/theme": "^12.0.0", "@ngneat/until-destroy": "^9.2.0", "@ngx-translate/core": "^15.0.0", diff --git a/packages/plugins/public-layout-ui/package.json b/packages/plugins/public-layout-ui/package.json index 557dfab7f00..4c3cfb9ea15 100644 --- a/packages/plugins/public-layout-ui/package.json +++ b/packages/plugins/public-layout-ui/package.json @@ -33,7 +33,6 @@ "@angular/router": "^16.2.12", "@fullcalendar/angular": "~6.1.10", "@gauzy/contracts": "^0.1.0", - "@gauzy/ui-core": "^0.1.0", "@nebular/theme": "^12.0.0", "@ng-select/ng-select": "^11.2.0", "@ngneat/until-destroy": "^9.2.0", diff --git a/tsconfig.json b/tsconfig.json index 298183d2ded..e0900849ec6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,8 +11,8 @@ "@gauzy/desktop-window": ["./packages/desktop-window/src/index.ts"], "@gauzy/plugin-job-employee-ui": ["./packages/plugins/job-employee-ui/src/index.ts"], "@gauzy/plugin-job-matching-ui": ["./packages/plugins/job-matching-ui/src/index.ts"], - "@gauzy/plugin-job-search-ui": ["./packages/plugins/job-search-ui/src/index.ts"], "@gauzy/plugin-job-proposal-ui": ["./packages/plugins/job-proposal-ui/src/index.ts"], + "@gauzy/plugin-job-search-ui": ["./packages/plugins/job-search-ui/src/index.ts"], "@gauzy/plugin-legal-ui": ["./packages/plugins/legal-ui/src/index.ts"], "@gauzy/plugin-maintenance-ui": ["./packages/plugins/maintenance-ui/src/index.ts"], "@gauzy/plugin-onboarding-ui": ["./packages/plugins/onboarding-ui/src/index.ts"],