Skip to content
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,13 @@ export class AppComponent extends BaseComponent implements OnInit {
this.loggedAccount = user ? _.cloneDeep(user) : AppComponent.ANONYMOUS_ACCOUNT_INFO;
this.cdr.detectChanges();
});

this.initAdminSlideToggle();
}

private initAdminSlideToggle(): void {
const flag = this.localStorageService.adminPriveleges;
this.loggedUserService.emitAdminPrivelegesChanges(Boolean(flag));
}

private setMomentOptions(): void {
Expand Down
11 changes: 10 additions & 1 deletion src/app/auth/logged-user.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Injectable } from "@angular/core";
import { catchError, first } from "rxjs/operators";
import { Observable, ReplaySubject, Subject, of } from "rxjs";
import { BehaviorSubject, Observable, ReplaySubject, Subject, of } from "rxjs";
import { NavigationService } from "../services/navigation.service";
import { MaybeNull } from "../common/app.types";
import { isNull } from "lodash";
Expand All @@ -20,6 +20,7 @@ import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
export class LoggedUserService extends UnsubscribeDestroyRefAdapter {
private loggedInUser: MaybeNull<AccountFragment> = null;
private loggedInUser$: Subject<MaybeNull<AccountFragment>> = new ReplaySubject<MaybeNull<AccountFragment>>(1);
private adminPriveleges$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);

constructor(
private loginService: LoginService,
Expand All @@ -38,6 +39,14 @@ export class LoggedUserService extends UnsubscribeDestroyRefAdapter {
.subscribe((user: AccountFragment) => this.changeUser(user));
}

public get adminPrivelegesChanges(): Observable<boolean> {
return this.adminPriveleges$.asObservable();
}

public emitAdminPrivelegesChanges(value: boolean): void {
return this.adminPriveleges$.next(value);
}

public initializeCompletes(): Observable<void> {
const loginInstructions: AppConfigLoginInstructions | null = this.appConfigService.loginInstructions;
if (loginInstructions) {
Expand Down
22 changes: 16 additions & 6 deletions src/app/auth/settings/account-settings.component.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<div class="pt-4 p-responsive clearfix" *ngIf="user$ | async as user">
<div class="pt-4 p-responsive clearfix" *ngIf="componentData$ | async as data">
<div class="d-md-flex align-items-center justify-content-between mt-1 mb-4">
<div class="d-flex align-items-center mb-2 mb-md-0">
<img
[src]="user.avatarUrl ?? DEFAULT_AVATAR_URL"
[alt]="'@' + user.accountName"
[src]="data.user?.avatarUrl ?? DEFAULT_AVATAR_URL"
[alt]="'@' + data.user?.accountName"
size="48"
height="48"
width="48"
Expand All @@ -12,17 +12,27 @@
/>
<div class="flex-auto mx-4 mt-3">
<a
[routerLink]="['/', user.accountName]"
[routerLink]="['/', data.user?.accountName]"
class="color-fg-default fw-bold-custom fs-5"
data-test-id="user-name-link"
>
{{ user.displayName }} <span class="text-muted">({{ user.accountName }})</span>
{{ data.user?.displayName }} <span class="text-muted">({{ data.user?.accountName }})</span>
</a>
<div class="d-flex align-items-center flex-wrap">
<p class="color-fg-muted">Your personal account</p>
</div>
</div>
</div>
<div class="box p-4" *ngIf="isAdmin">
<mat-slide-toggle
[(ngModel)]="data.adminPriveleges"
(change)="adminSlideToggleChange($event)"
color="primary"
class="mat-elevation-z0"
>
<span class="ps-2"> Admin priveleges</span>
</mat-slide-toggle>
</div>
</div>
<div
class="d-flex layout layout--flowRow-until-md layout--sidebar-position-start layout--sidebarPosition-flowRow-start"
Expand Down Expand Up @@ -284,7 +294,7 @@
</div>
<div class="content">
<ng-container *ngIf="activeTab === AccountSettingsTabs.ACCESS_TOKENS">
<app-access-tokens-tab [account]="user" />
<app-access-tokens-tab [account]="data.user!" />
</ng-container>
</div>
</div>
Expand Down
16 changes: 16 additions & 0 deletions src/app/auth/settings/account-settings.component.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
@import "var";

:host {
::ng-deep {
.mat-slide-toggle {
.mat-slide-toggle-ripple {
position: relative;
}
}

.mdc-switch {
.mdc-switch__ripple {
display: none;
}
}
}
}

.p-responsive {
padding: 0 60px;

Expand Down
35 changes: 32 additions & 3 deletions src/app/auth/settings/account-settings.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import { AccountFragment } from "src/app/api/kamu.graphql.interface";
import { AccountSettingsTabs } from "./account-settings.constants";
import { ChangeDetectionStrategy, Component, inject, OnInit } from "@angular/core";
import { ActivatedRoute, NavigationEnd, Router } from "@angular/router";
import { filter } from "rxjs/operators";
import { filter, map } from "rxjs/operators";
import { BaseComponent } from "src/app/common/base.component";
import AppValues from "src/app/common/app.values";
import { MaybeNull, MaybeUndefined } from "src/app/common/app.types";
import { Observable } from "rxjs";
import { combineLatest, Observable } from "rxjs";
import { LoggedUserService } from "../logged-user.service";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { MatSlideToggleChange } from "@angular/material/slide-toggle";
import { LocalStorageService } from "src/app/services/local-storage.service";

@Component({
selector: "app-settings",
Expand All @@ -23,10 +25,17 @@ export class AccountSettingsComponent extends BaseComponent implements OnInit {

public activeTab: AccountSettingsTabs = AccountSettingsTabs.PROFILE;
public user$: Observable<MaybeNull<AccountFragment>>;
public adminPriveleges$: Observable<boolean>;

private router = inject(Router);
private route = inject(ActivatedRoute);
private loggedUserService = inject(LoggedUserService);
private localStorageService = inject(LocalStorageService);

public componentData$: Observable<{
user: MaybeNull<AccountFragment>;
adminPriveleges: boolean;
}>;

public ngOnInit(): void {
this.router.events
Expand All @@ -39,13 +48,33 @@ export class AccountSettingsComponent extends BaseComponent implements OnInit {
});

this.extractActiveTabFromRoute();
this.user$ = this.loggedUserService.loggedInUserChanges;

this.componentData$ = combineLatest([
this.loggedUserService.loggedInUserChanges,
this.loggedUserService.adminPrivelegesChanges,
]).pipe(
map(([user, adminPriveleges]) => {
return {
user,
adminPriveleges,
};
}),
);
}

public getRouteLink(tab: AccountSettingsTabs): string {
return `/${ProjectLinks.URL_SETTINGS}/${tab}`;
}

public get isAdmin(): boolean {
return this.loggedUserService.isAdmin;
}

public adminSlideToggleChange(event: MatSlideToggleChange): void {
this.loggedUserService.emitAdminPrivelegesChanges(event.checked);
this.localStorageService.setAdminPriveleges(event.checked);
}

private extractActiveTabFromRoute(): void {
const categoryParam: MaybeUndefined<string> = this.route.snapshot.params[
ProjectLinks.URL_PARAM_CATEGORY
Expand Down
2 changes: 2 additions & 0 deletions src/app/common/app.values.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export default class AppValues {
public static readonly LOCAL_STORAGE_LOGIN_CALLBACK_URL = "login_callback_url";
public static readonly LOCAL_STORAGE_LOGIN_REDIRECT_URL = "login_redirect_url";
public static readonly LOCAL_STORAGE_ACCOUNT_ID = "account_id";
public static readonly LOCAL_STORAGE_ADMIN_PRIVELEGES = "admin_priveleges";

public static readonly SESSION_STORAGE_SIDE_PANEL_VISIBLE = "side_panel_visible";
public static readonly DEFAULT_USER_DISPLAY_NAME = "anonymous";
public static readonly DEFAULT_AVATAR_URL = "https://avatars.githubusercontent.com/u/11951648?v=4";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,32 +68,48 @@
<span class="d-block">Discussions</span>
</div>
</mat-button-toggle>
<mat-button-toggle
data-test-id="navigateToFlows"
value="flows"
[class.active-link]="isDatasetViewTypeFlows"
*ngIf="shouldAllowSettingsTab"
>
<a [routerLink]="[datasetLink]" [queryParams]="{ tab: DatasetViewTypeEnum.Flows }" class="menu-link">
<div class="d-flex align-items-center">
<mat-icon class="fs-4 d-block me-1">add_task</mat-icon>
<span class="d-block">Flows</span>
</div>
</a>
</mat-button-toggle>
<mat-button-toggle
data-test-id="navigateToSettings"
value="settings"
[class.active-link]="isDatasetViewTypeSettings"
*ngIf="shouldAllowSettingsTab"
>
<a [routerLink]="[datasetLink]" [queryParams]="{ tab: DatasetViewTypeEnum.Settings }" class="menu-link">
<div class="d-flex align-items-center">
<svg-icon name="account" class="me-1" />
<span class="d-block">Settings</span>
</div>
</a>
</mat-button-toggle>
<ng-container *ngIf="viewModeElement$ | async as viewMode">
<mat-button-toggle
data-test-id="navigateToFlows"
value="flows"
[class.active-link]="isDatasetViewTypeFlows"
*ngIf="viewMode === ViewModeElement.PERMISSIONS_MODE || viewMode === ViewModeElement.ADMIN_MODE"
>
<a
[routerLink]="[datasetLink]"
[queryParams]="{ tab: DatasetViewTypeEnum.Flows }"
class="menu-link"
>
<div class="d-flex align-items-center">
<mat-icon class="fs-4 d-block me-1">add_task</mat-icon>
<span class="d-block">Flows</span>
<mat-icon *ngIf="viewMode === ViewModeElement.ADMIN_MODE" class="admin-icon d-block me-1">
admin_panel_settings</mat-icon
>
</div>
</a>
</mat-button-toggle>
<mat-button-toggle
data-test-id="navigateToSettings"
value="settings"
[class.active-link]="isDatasetViewTypeSettings"
*ngIf="viewMode === ViewModeElement.PERMISSIONS_MODE || viewMode === ViewModeElement.ADMIN_MODE"
>
<a
[routerLink]="[datasetLink]"
[queryParams]="{ tab: DatasetViewTypeEnum.Settings }"
class="menu-link"
>
<div class="d-flex align-items-center">
<svg-icon name="account" class="me-1" />
<span class="d-block">Settings</span>
<mat-icon *ngIf="viewMode === ViewModeElement.ADMIN_MODE" class="admin-icon d-block me-1">
admin_panel_settings</mat-icon
>
</div>
</a>
</mat-button-toggle>
</ng-container>
</mat-button-toggle-group>
</div>
<app-data-access-panel [datasetBasics]="datasetBasics" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,10 @@
height: inherit;
border-radius: inherit;
}

.admin-icon {
position: relative;
font-size: 16px;
bottom: 5px;
color: #378bb0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import { DatasetViewTypeEnum } from "../dataset-view.interface";
import { SideNavHelper } from "../../common/sidenav.helper";
import { isMobileView, promiseWithCatch } from "src/app/common/app.helpers";
import { DatasetBasicsFragment, DatasetPermissionsFragment } from "src/app/api/kamu.graphql.interface";
import { DatasetPermissionsService } from "../dataset.permissions.service";
import { ElementsViewService, ViewModeElement } from "src/app/services/elements-view.service";
import { Observable } from "rxjs";

@Component({
selector: "app-dataset-view-menu",
Expand All @@ -36,8 +37,10 @@ export class DatasetViewMenuComponent implements OnInit, AfterViewInit {

private sideNavHelper: SideNavHelper;

private datasetPermissionsServices = inject(DatasetPermissionsService);
private elementsViewService = inject(ElementsViewService);
private widgetHeightService = inject(WidgetHeightService);
public viewModeElement$: Observable<ViewModeElement>;
public readonly ViewModeElement: typeof ViewModeElement = ViewModeElement;

public ngAfterViewInit(): void {
this.widgetHeightService.setWidgetOffsetTop(
Expand All @@ -50,6 +53,7 @@ export class DatasetViewMenuComponent implements OnInit, AfterViewInit {
if (this.sidenav) {
this.sideNavHelper = new SideNavHelper(this.sidenav);
}
this.viewModeElement$ = this.elementsViewService.viewModeElement();
}

public get isDatasetViewTypeOverview(): boolean {
Expand Down Expand Up @@ -84,10 +88,6 @@ export class DatasetViewMenuComponent implements OnInit, AfterViewInit {
return this.datasetViewType === DatasetViewTypeEnum.Settings;
}

public get shouldAllowSettingsTab(): boolean {
return this.datasetPermissionsServices.shouldAllowSettingsTab(this.datasetPermissions);
}

public get datasetLink(): string {
return `/${this.datasetBasics.owner.accountName}/${this.datasetBasics.name}/`;
}
Expand Down
4 changes: 3 additions & 1 deletion src/app/dataset-view/dataset.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ import { DatasetInfo } from "../interface/navigation.interface";
import { promiseWithCatch } from "../common/app.helpers";
import { DatasetRequestBySql } from "../interface/dataset.interface";
import { MaybeNull, MaybeUndefined } from "../common/app.types";
import { DatasetPermissionsService } from "./dataset.permissions.service";
import { ReplaySubject, Subject, of } from "rxjs";
import { LineageGraphNodeData, LineageGraphNodeKind } from "./additional-components/lineage-component/lineage-model";
import _ from "lodash";
import { BaseDatasetDataComponent } from "../common/base-dataset-data.component";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { ElementsViewService } from "../services/elements-view.service";
import { DatasetPermissionsService } from "./dataset.permissions.service";

@Component({
selector: "app-dataset",
Expand All @@ -33,6 +34,7 @@ export class DatasetComponent extends BaseDatasetDataComponent implements OnInit
private datasetPermissionsServices = inject(DatasetPermissionsService);
private router = inject(Router);
private cdr = inject(ChangeDetectorRef);
private elementsViewService = inject(ElementsViewService);

public ngOnInit(): void {
const urlDatasetInfo = this.getDatasetInfoFromUrl();
Expand Down
16 changes: 16 additions & 0 deletions src/app/services/elements-view.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';

import { ElementsViewService } from './elements-view.service';

describe('ElementsViewService', () => {
let service: ElementsViewService;

beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(ElementsViewService);
});

it('should be created', () => {
expect(service).toBeTruthy();
});
});
Loading