Skip to content

Commit

Permalink
Merge pull request #7779 from ever-co/fix/time-tracker-organization-s…
Browse files Browse the repository at this point in the history
…elector

[Fix] Can't Choose Organization on Desktop Timer
  • Loading branch information
rahul-rocket authored Apr 29, 2024
2 parents a0bf9d2 + 1576cbb commit 7540c2b
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 116 deletions.
17 changes: 14 additions & 3 deletions apps/desktop-timer/src/app/app.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,30 @@ import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { firstValueFrom } from 'rxjs';
import { UserOrganizationService } from '@gauzy/desktop-ui-lib';
import { IUserOrganization } from '@gauzy/contracts';
import { IUser } from '@gauzy/contracts';

@Injectable({
providedIn: 'root'
})
export class AppService {
constructor(private http: HttpClient, private readonly _userOrganizationService: UserOrganizationService) {}
constructor(
private readonly http: HttpClient,
private readonly _userOrganizationService: UserOrganizationService
) { }

public pingServer({ host }) {
return firstValueFrom(this.http.get(host + '/api'));
}

public async getUserDetail(): Promise<IUserOrganization> {
/**
* Retrieves detailed information about the current user.
*
* This function fetches user details from the `_userOrganizationService`.
* It returns a promise that resolves to an `IUser` object containing user details.
*
* @returns A promise resolving to the user details (`IUser`).
*/
public async getUserDetail(): Promise<IUser> {
return await this._userOrganizationService.detail();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export class UserOrganizationService extends TenantAwareCrudService<UserOrganiza
if (includeEmployee) {
try {
// Extract user IDs from the items array
const userIds = items.map(organization => organization.user.id);
const userIds = items.map((organization: IUserOrganization) => organization.user.id);

// Fetch all employee details in bulk for the extracted user IDs
const employees = await this.employeeService.findEmployeesByUserIds(userIds);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
IUserUpdateInput,
LanguagesEnum,
} from '@gauzy/contracts';
import { distinctUntilChange } from '@gauzy/common-angular';
import { UserOrganizationService } from '../time-tracker/organization-selector/user-organization.service';
import { LanguageService } from './language.service';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
Expand All @@ -21,7 +22,6 @@ import {
import { Store } from '../services';
import { LanguageSelectorService } from './language-selector.service';
import { ElectronService } from '../electron/services';
import { distinctUntilChange } from '@gauzy/common-angular';

@UntilDestroy({ checkProperties: true })
@Component({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,42 @@ import {
NgZone,
Input,
} from '@angular/core';
import { IOrganization, IUserOrganization } from '@gauzy/contracts';
import { IOrganization, IUser } from '@gauzy/contracts';
import { uniq } from 'underscore';
import { UserOrganizationService } from './user-organization.service';
import { ElectronService } from '../../electron/services';

@Component({
selector: 'ga-organization-selector',
selector: 'ngx-desktop-timer-organization-selector',
templateUrl: './organization-selector.component.html',
styleUrls: ['./organization-selector.component.scss'],
})
export class OrganizationSelectorComponent implements OnInit, AfterViewInit {
private _user: IUserOrganization;
private _auth: any;

public organizations: IOrganization[] = [];
public selectedOrganization: IOrganization;

/* A getter and setter. */
private _isDisabled: boolean;
organizations: IOrganization[] = [];
selectedOrganization: IOrganization;
@Output()
organizationChange: EventEmitter<IOrganization>;
public get disabled(): boolean {
return this._isDisabled;
}
@Input() public set disabled(value: boolean) {
this._isDisabled = value;
}

/* A getter and setter. */
private _user: IUser;
public get user(): IUser {
return this._user;
}
public set user(value: IUser) {
if (value) {
this._user = value;
}
}

@Output() organizationChange: EventEmitter<IOrganization>;

constructor(
private readonly _userOrganizationService: UserOrganizationService,
Expand All @@ -34,93 +52,114 @@ export class OrganizationSelectorComponent implements OnInit, AfterViewInit {
this.organizationChange = new EventEmitter();
this._isDisabled = false;
}

ngOnInit() { }

/**
* Component lifecycle hook for operations after the view initializes.
*/
ngAfterViewInit(): void {
this._electronService.ipcRenderer.on(
'timer_tracker_show',
(event, arg) => {
this._ngZone.run(async () => {
try {
this.user = await this._userOrganizationService.detail();
await this.loadOrganizations();
} catch (error) {
console.log('[error]', error)
}
});
}
);
// Subscribe to Electron IPC events and handle in Angular's zone
this._electronService.ipcRenderer.on('timer_tracker_show', (event, arg) => {
this._ngZone.run(async () => {
try {
// Fetch user details
this.user = await this._userOrganizationService.detail();
// Load organizations for the user
await this.loadOrganizations();
} catch (error) {
console.error('Error handling IPC event:', error); // Log the error with context
}
});
});
}

ngOnInit() {}
/**
* Sets the selected organization and emits an event indicating that the organization has changed.
*
* @param organization The organization to select.
*/
public selectOrganization(organization: IOrganization): void {
// Ensure that a valid organization is provided
if (!organization) {
console.warn('No organization provided to select.');
return; // Exit early if organization is null or undefined
}

// Set the selected organization
this.selectedOrganization = organization;

public selectOrganization(organization: IOrganization) {
if (organization) {
this.selectedOrganization = organization;
// Emit an event indicating the organization has changed
if (this.organizationChange) {
this.organizationChange.emit(organization);
}
}

/**
* Load organizations for the current user and set the selected organization based on predefined conditions.
*
* This function fetches organizations from the `_userOrganizationService`, filters them based on the current user,
* and sets the `selectedOrganization` property accordingly.
*
* @returns A promise that resolves when organizations are loaded.
*/
private async loadOrganizations(): Promise<void> {
// Return early if the user is not defined
if (!this.user) {
return;
}
const { tenantId, userId } = this.user;

const { items = [] } = await this._userOrganizationService.all(
['organization'], { userId, tenantId }
);

const organizations: IOrganization[] = items
.map(({ organization, userId }) => {
return userId === this.user.id ? organization : null;
})
.filter((organization) => !!organization);
this.organizations = uniq(organizations, (item) => item.id);

if (this.organizations.length > 0) {
const defaultOrganization = this.organizations.find(
(organization: IOrganization) => organization.isDefault
);
const [firstOrganization] = this.organizations;

if (this.organizationId) {
const organization = this.organizations.find(
(organization: IOrganization) =>
organization.id === this.organizationId

try {
const { id: userId, tenantId } = this.user;

// Fetch all user organizations, including related 'organization' data
const { items = [] } = await this._userOrganizationService.getAll(['organization'], { userId, tenantId });

// Extract organizations where the user ID matches
const organizations: IOrganization[] = items
.map(({ organization, userId }) => (userId === this.user.id ? organization : null))
.filter(Boolean); // Remove null values

// Remove duplicate organizations by unique ID
this.organizations = uniq(organizations, (item: IOrganization) => item.id);

if (this.organizations.length > 0) {
// Find the default organization
const defaultOrganization = this.organizations.find(
(organization) => organization.isDefault
);
this.selectedOrganization =
organization || defaultOrganization || firstOrganization;
} else {
// set default organization as selected
this.selectedOrganization =
defaultOrganization || firstOrganization;
}
}
}
public get organizationId(): string {
if (this.selectedOrganization) {
return this.selectedOrganization.id;
} else {
return null;
}
}

/* A getter and setter. */
public set user(value: IUserOrganization) {
if (value) {
this._user = value;
}
}
// Select the first organization from the list
const [firstOrganization] = this.organizations;

public get user(): IUserOrganization {
return this._user;
}
if (this.organizationId) {
// Find the organization that matches the provided ID
const matchingOrganization = this.organizations.find(
(organization) => organization.id === this.organizationId
);

@Input()
public set disabled(value: boolean) {
this._isDisabled = value;
// Set the selected organization, fallback to default or first
this.selectedOrganization = matchingOrganization || defaultOrganization || firstOrganization;
} else {
// If no specific ID, use default or first organization
this.selectedOrganization = defaultOrganization || firstOrganization;
}
}
} catch (error) {
console.error('Error loading organizations:', error); // Error handling
}
}

public get disabled(): boolean {
return this._isDisabled;
/**
* Getter for the organization ID from the selected organization.
*
* This getter retrieves the 'id' of the selected organization if available.
* If 'selectedOrganization' is not set, it returns `null`.
*
* @returns The organization ID or `null` if not available.
*/
public get organizationId(): string | null {
// Return the ID if 'selectedOrganization' is defined, otherwise return null
return this.selectedOrganization?.id ?? null;
}
}
Loading

0 comments on commit 7540c2b

Please sign in to comment.