fix(console): horizontal toggle for users, projects, improve UI/UX (#4047)

* fix(console): horizontal toggle for users, projects

* improve input contrast

* toggles, profile UI fix

* lint

* fix safari styles

* fix button placement redirects

* style lint

Co-authored-by: Livio Spring <livio.a@gmail.com>
This commit is contained in:
Max Peintner 2022-07-29 11:14:45 +02:00 committed by GitHub
parent 9ed972f308
commit 5b284f8c9b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 358 additions and 159 deletions

View File

@ -0,0 +1,6 @@
<button class="nav-toggle" [ngClass]="{active}" (click)="clicked.emit()">
<div class="c_label">
<span> {{ label }} </span>
<small *ngIf="count" class="count">({{ count }})</small>
</div>
</button>

View File

@ -0,0 +1,71 @@
@use '@angular/material' as mat;
@mixin nav-toggle-theme($theme) {
$primary: map-get($theme, primary);
$warn: map-get($theme, warn);
$background: map-get($theme, background);
$accent: map-get($theme, accent);
$primary-color: mat.get-color-from-palette($primary, 500);
$warn-color: mat.get-color-from-palette($warn, 500);
$accent-color: mat.get-color-from-palette($accent, 500);
$foreground: map-get($theme, foreground);
$is-dark-theme: map-get($theme, is-dark);
$back: map-get($background, background);
.nav-toggle {
display: flex;
align-items: center;
font-size: 14px;
line-height: 14px;
padding: 0.4rem 12px;
color: mat.get-color-from-palette($foreground, text) !important;
transition: all 0.2s ease;
text-decoration: none;
border-radius: 50vw;
border: none;
font-weight: 400;
margin: 0.25rem 2px;
white-space: nowrap;
position: relative;
background: none;
cursor: pointer;
font-family: 'Lato', -apple-system, BlinkMacSystemFont, sans-serif;
.c_label {
display: flex;
align-items: center;
text-align: center;
.count {
display: none;
margin-left: 6px;
}
}
&.external-link {
padding-right: 2rem;
i {
position: absolute;
right: 8px;
font-size: 1.2rem;
}
}
&:hover {
background: if($is-dark-theme, #ffffff40, #00000010);
}
&.active {
background-color: $primary-color;
color: mat.get-color-from-palette($foreground, toolbar-items) !important;
.c_label {
.count {
display: inline-block;
}
}
}
}
}

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { NavToggleComponent } from './nav-toggle.component';
describe('NavToggleComponent', () => {
let component: NavToggleComponent;
let fixture: ComponentFixture<NavToggleComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ NavToggleComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(NavToggleComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,14 @@
import { Component, EventEmitter, Input, Output } from '@angular/core';
@Component({
selector: 'cnsl-nav-toggle',
templateUrl: './nav-toggle.component.html',
styleUrls: ['./nav-toggle.component.scss'],
})
export class NavToggleComponent {
@Input() public label: string = '';
@Input() public count: number | null = 0;
@Input() public active: boolean = false;
@Output() public clicked: EventEmitter<void> = new EventEmitter<void>();
constructor() {}
}

View File

@ -0,0 +1,12 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { NavToggleComponent } from './nav-toggle.component';
@NgModule({
declarations: [NavToggleComponent],
imports: [CommonModule, RouterModule],
exports: [NavToggleComponent],
})
export class NavToggleModule {}

View File

@ -7,9 +7,10 @@
display: flex; display: flex;
align-items: center; align-items: center;
padding-bottom: 0.5rem; padding-bottom: 0.5rem;
box-sizing: border-box;
&.border-bottom { &.border-bottom {
padding-bottom: 0.5rem; padding-bottom: 1rem;
margin-bottom: 1rem; margin-bottom: 1rem;
border-bottom: 1px solid map-get($foreground, divider); border-bottom: 1px solid map-get($foreground, divider);
} }

View File

@ -80,7 +80,7 @@
min-width: 320px; min-width: 320px;
.formfield { .formfield {
flex: 1; width: 500px;
} }
button { button {

View File

@ -9,22 +9,21 @@
<p class="sub cnsl-secondary-text max-width-description">{{ 'PROJECT.PAGES.LISTDESCRIPTION' | translate }}</p> <p class="sub cnsl-secondary-text max-width-description">{{ 'PROJECT.PAGES.LISTDESCRIPTION' | translate }}</p>
<div class="projects-controls"> <div class="projects-controls">
<div class="project-type-actions"> <div class="project-toggle-group">
<button <cnsl-nav-toggle
class="type-button" label="{{ 'PROJECT.PAGES.TYPE.OWNED' | translate }}"
[ngClass]="{ active: (projectType$ | async) === ProjectType.PROJECTTYPE_OWNED }" [count]="mgmtService.ownedProjectsCount | async"
(click)="setType(ProjectType.PROJECTTYPE_OWNED)" (clicked)="setType(ProjectType.PROJECTTYPE_OWNED)"
> [active]="(projectType$ | async) === ProjectType.PROJECTTYPE_OWNED"
{{ 'PROJECT.PAGES.TYPE.OWNED' | translate }} ({{ (mgmtService?.ownedProjectsCount | async) ?? 0 }}) ></cnsl-nav-toggle>
</button> <cnsl-nav-toggle
<button label="{{ 'PROJECT.PAGES.TYPE.GRANTED' | translate }}"
class="type-button" [count]="mgmtService.grantedProjectsCount | async"
[ngClass]="{ active: (projectType$ | async) === ProjectType.PROJECTTYPE_GRANTED }" (clicked)="setType(ProjectType.PROJECTTYPE_GRANTED)"
(click)="setType(ProjectType.PROJECTTYPE_GRANTED)" [active]="(projectType$ | async) === ProjectType.PROJECTTYPE_GRANTED"
> ></cnsl-nav-toggle>
{{ 'PROJECT.PAGES.TYPE.GRANTED' | translate }} ({{ (mgmtService?.grantedProjectsCount | async) ?? 0 }})
</button>
</div> </div>
<span class="fill-space"></span> <span class="fill-space"></span>
<button class="grid-btn" (click)="grid = !grid" mat-icon-button [attr.data-e2e]="'toggle-grid'"> <button class="grid-btn" (click)="grid = !grid" mat-icon-button [attr.data-e2e]="'toggle-grid'">
<i *ngIf="grid" class="show list view las la-th-list"></i> <i *ngIf="grid" class="show list view las la-th-list"></i>

View File

@ -23,34 +23,33 @@
.projects-controls { .projects-controls {
display: flex; display: flex;
padding-bottom: 0.5rem; align-items: center;
padding-bottom: 1rem;
border-bottom: 1px solid map-get($foreground, dividers); border-bottom: 1px solid map-get($foreground, dividers);
.project-type-actions { .project-toggle-group {
display: flex; display: flex;
align-items: center;
.type-button { .toggle-row {
border: none; display: flex;
background: none; align-items: center;
text-align: left;
padding: 0.75rem 0;
opacity: 0.6;
font-size: 15px;
cursor: pointer;
color: map-get($foreground, text);
&:first-child { i {
margin-right: 1rem; margin-right: 0.5rem;
} }
&:hover { .info-i {
opacity: 1; font-size: 1.2rem;
margin-left: 0.5rem;
margin-right: 0;
} }
&.active { .current-dot {
font-weight: 600; height: 8px;
opacity: 1; width: 8px;
border-radius: 50%;
background-color: rgb(84, 142, 230);
margin-left: 0.5rem;
} }
} }
} }

View File

@ -13,6 +13,7 @@ import { ManagementService } from 'src/app/services/mgmt.service';
export class ProjectsComponent { export class ProjectsComponent {
public zitadelProjectId: string = ''; public zitadelProjectId: string = '';
public projectType$: BehaviorSubject<any> = new BehaviorSubject(ProjectType.PROJECTTYPE_OWNED); public projectType$: BehaviorSubject<any> = new BehaviorSubject(ProjectType.PROJECTTYPE_OWNED);
public projectType: ProjectType = ProjectType.PROJECTTYPE_OWNED;
public ProjectType: any = ProjectType; public ProjectType: any = ProjectType;
public grid: boolean = true; public grid: boolean = true;
constructor( constructor(

View File

@ -15,6 +15,7 @@ import { ActionKeysModule } from 'src/app/modules/action-keys/action-keys.module
import { CardModule } from 'src/app/modules/card/card.module'; import { CardModule } from 'src/app/modules/card/card.module';
import { FilterProjectModule } from 'src/app/modules/filter-project/filter-project.module'; import { FilterProjectModule } from 'src/app/modules/filter-project/filter-project.module';
import { InputModule } from 'src/app/modules/input/input.module'; import { InputModule } from 'src/app/modules/input/input.module';
import { NavToggleModule } from 'src/app/modules/nav-toggle/nav-toggle.module';
import { PaginatorModule } from 'src/app/modules/paginator/paginator.module'; import { PaginatorModule } from 'src/app/modules/paginator/paginator.module';
import { RefreshTableModule } from 'src/app/modules/refresh-table/refresh-table.module'; import { RefreshTableModule } from 'src/app/modules/refresh-table/refresh-table.module';
import { SharedModule } from 'src/app/modules/shared/shared.module'; import { SharedModule } from 'src/app/modules/shared/shared.module';
@ -55,6 +56,7 @@ import { ProjectsComponent } from './projects.component';
LocalizedDatePipeModule, LocalizedDatePipeModule,
RefreshTableModule, RefreshTableModule,
MatRippleModule, MatRippleModule,
NavToggleModule,
], ],
}) })
export class ProjectsModule {} export class ProjectsModule {}

View File

@ -1,62 +1,74 @@
<form [formGroup]="profileForm" *ngIf="profileForm" (ngSubmit)="submitForm()"> <form [formGroup]="profileForm" *ngIf="profileForm" (ngSubmit)="submitForm()">
<div class="user-form-content"> <div class="user-top-content">
<div class="user-form-content inner"> <div class="user-form-content">
<button [disabled]="user && disabled" class="camera-wrapper" type="button" <button
(click)="showEditImage ? openUploadDialog() : null"> [disabled]="user && disabled"
class="camera-wrapper"
type="button"
(click)="showEditImage ? openUploadDialog() : null"
>
<div class="i-wrapper" *ngIf="showEditImage"> <div class="i-wrapper" *ngIf="showEditImage">
<i class="las la-camera"></i> <i class="las la-camera"></i>
</div> </div>
<cnsl-avatar *ngIf="user && user.profile?.displayName && user.profile?.firstName && user.profile?.lastName" <cnsl-avatar
class="avatar" [name]="user.profile?.displayName ?? ''" [avatarUrl]="user?.profile?.avatarUrl || ''" *ngIf="user && user.profile?.displayName && user.profile?.firstName && user.profile?.lastName"
[forColor]="preferredLoginName" [size]="80"> class="avatar"
[name]="user.profile?.displayName ?? ''"
[avatarUrl]="user?.profile?.avatarUrl || ''"
[forColor]="preferredLoginName"
[size]="80"
>
</cnsl-avatar> </cnsl-avatar>
</button> </button>
<div className="usernamediv"> <div class="usernamediv">
<cnsl-form-field class="formfield"> <cnsl-form-field class="formfield">
<cnsl-label>{{ 'USER.PROFILE.USERNAME' | translate }}</cnsl-label> <cnsl-label>{{ 'USER.PROFILE.USERNAME' | translate }}</cnsl-label>
<input cnslInput formControlName="userName" /> <input cnslInput formControlName="userName" />
</cnsl-form-field> </cnsl-form-field>
<button [disabled]="user && disabled" type="button" mat-stroked-button class="edit" <button [disabled]="user && disabled" type="button" mat-stroked-button class="edit" (click)="changeUsername()">
(click)="changeUsername()">{{'USER.PROFILE.CHANGEUSERNAME' | {{ 'USER.PROFILE.CHANGEUSERNAME' | translate }}
translate}}</button> </button>
</div> </div>
</div> </div>
<cnsl-form-field class="formfield"> <div class="user-grid">
<cnsl-label>{{ 'USER.PROFILE.FIRSTNAME' | translate }}</cnsl-label> <cnsl-form-field class="formfield">
<input cnslInput formControlName="firstName" /> <cnsl-label>{{ 'USER.PROFILE.FIRSTNAME' | translate }}</cnsl-label>
</cnsl-form-field> <input cnslInput formControlName="firstName" />
<cnsl-form-field class="formfield"> </cnsl-form-field>
<cnsl-label>{{ 'USER.PROFILE.LASTNAME' | translate }}</cnsl-label> <cnsl-form-field class="formfield">
<input cnslInput formControlName="lastName" /> <cnsl-label>{{ 'USER.PROFILE.LASTNAME' | translate }}</cnsl-label>
</cnsl-form-field> <input cnslInput formControlName="lastName" />
<cnsl-form-field class="formfield"> </cnsl-form-field>
<cnsl-label>{{ 'USER.PROFILE.NICKNAME' | translate }}</cnsl-label> <cnsl-form-field class="formfield">
<input cnslInput formControlName="nickName" /> <cnsl-label>{{ 'USER.PROFILE.NICKNAME' | translate }}</cnsl-label>
</cnsl-form-field> <input cnslInput formControlName="nickName" />
<cnsl-form-field class="formfield"> </cnsl-form-field>
<cnsl-label>{{ 'USER.PROFILE.DISPLAYNAME' | translate }}</cnsl-label> <cnsl-form-field class="formfield">
<input cnslInput formControlName="displayName" /> <cnsl-label>{{ 'USER.PROFILE.DISPLAYNAME' | translate }}</cnsl-label>
</cnsl-form-field> <input cnslInput formControlName="displayName" />
<cnsl-form-field class="formfield"> </cnsl-form-field>
<cnsl-label>{{ 'USER.PROFILE.GENDER' | translate }}</cnsl-label> <cnsl-form-field class="formfield">
<mat-select formControlName="gender"> <cnsl-label>{{ 'USER.PROFILE.GENDER' | translate }}</cnsl-label>
<mat-option *ngFor="let gender of genders" [value]="gender"> <mat-select formControlName="gender">
{{ 'GENDERS.'+gender | translate }} <mat-option *ngFor="let gender of genders" [value]="gender">
</mat-option> {{ 'GENDERS.' + gender | translate }}
</mat-select> </mat-option>
</cnsl-form-field> </mat-select>
<cnsl-form-field class="formfield"> </cnsl-form-field>
<cnsl-label>{{ 'USER.PROFILE.PREFERRED_LANGUAGE' | translate }}</cnsl-label> <cnsl-form-field class="formfield">
<mat-select formControlName="preferredLanguage"> <cnsl-label>{{ 'USER.PROFILE.PREFERRED_LANGUAGE' | translate }}</cnsl-label>
<mat-option *ngFor="let language of languages" [value]="language"> <mat-select formControlName="preferredLanguage">
{{ 'LANGUAGES.'+language | translate }} <mat-option *ngFor="let language of languages" [value]="language">
</mat-option> {{ 'LANGUAGES.' + language | translate }}
</mat-select> </mat-option>
</cnsl-form-field> </mat-select>
</cnsl-form-field>
</div>
</div> </div>
<div class="btn-container"> <div class="btn-container">
<button [disabled]="disabled" class="submit-button" type="submit" color="primary" mat-raised-button>{{ <button [disabled]="disabled" class="submit-button" type="submit" color="primary" mat-raised-button>
'ACTIONS.SAVE' | translate }}</button> {{ 'ACTIONS.SAVE' | translate }}
</button>
</div> </div>
</form> </form>

View File

@ -2,31 +2,28 @@
display: flex; display: flex;
flex-direction: row; flex-direction: row;
flex-wrap: wrap; flex-wrap: wrap;
margin: 0 -.5rem; margin: 0;
width: 100%;
align-items: center;
&.inner { .usernamediv {
margin: 0; margin-left: 0.5rem;
width: 100%; margin-bottom: 0.5rem;
display: flex;
align-items: center;
.usernamediv { .formfield {
margin-left: .5rem; margin: 0;
margin-bottom: .5rem; flex: 1;
}
.formfield { .edit {
margin: 0; display: block;
flex: 1; margin-top: 0.5rem;
} cursor: pointer !important;
.edit {
cursor: pointer !important;
}
} }
} }
.camera-wrapper { .camera-wrapper {
margin: 0 .5rem; margin: 0 0.5rem;
position: relative; position: relative;
border-radius: 50%; border-radius: 50%;
padding: 0; padding: 0;
@ -36,37 +33,49 @@
justify-content: center; justify-content: center;
background: none; background: none;
cursor: pointer; cursor: pointer;
transition: all .3s ease; transition: all 0.3s ease;
overflow: hidden;
.i-wrapper { .i-wrapper {
border-radius: 50%; display: none;
background-color: #00000050; background-color: #00000080;
position: absolute; position: absolute;
top: 0;
z-index: 1; z-index: 1;
left: 0; left: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
transition: all 0.2 ease;
i { i {
font-size: 3rem; font-size: 1.2rem;
margin: 0.25rem;
color: white; color: white;
} }
} }
&:hover { &:hover {
.i-wrapper { .i-wrapper {
background-color: #00000080; display: inline;
} }
} }
} }
.formfield { .formfield {
flex: 1 1 33%; flex: 1 1 33%;
margin: 0 .5rem; margin: 0 0.5rem;
}
}
.user-grid {
display: grid;
grid-template-columns: 1fr;
gap: 1rem;
margin: 0;
@media only screen and (min-width: 700px) {
grid-template-columns: 1fr 1fr;
} }
} }
@ -75,6 +84,6 @@
justify-content: flex-end; justify-content: flex-end;
.submit-button { .submit-button {
border-radius: .5rem; border-radius: 0.5rem;
} }
} }

View File

@ -1,20 +1,26 @@
<span class="title" mat-dialog-title>{{'USER.PROFILE.AVATAR.UPLOADTITLE' | translate}}</span> <span class="title" mat-dialog-title>{{ 'USER.PROFILE.AVATAR.UPLOADTITLE' | translate }}</span>
<div mat-dialog-content> <div mat-dialog-content>
<p class="desc cnsl-secondary-text">{{'USER.PROFILE.AVATAR.CURRENT' | translate}}</p> <p class="desc cnsl-secondary-text">{{ 'USER.PROFILE.AVATAR.CURRENT' | translate }}</p>
<div class="current-pic-wrapper"> <div class="current-pic-wrapper">
<img class="pic" [src]="data.profilePic" *ngIf="data.profilePic" /> <img class="pic" [src]="data.profilePic" *ngIf="data.profilePic" />
<span class="fill-space"></span> <span class="fill-space"></span>
<input #selectedFile style="display: none;" class="file-input" type="file" (change)="onDrop($event)"> <input #selectedFile style="display: none" class="file-input" type="file" (change)="onDrop($event)" />
<button class="btn" mat-raised-button color="primary" type="button" <button class="btn" mat-raised-button color="primary" type="button" (click)="selectedFile.click()">
(click)="selectedFile.click();">{{'USER.PROFILE.AVATAR.UPLOADBTN' | translate}}</button> {{ 'USER.PROFILE.AVATAR.UPLOADBTN' | translate }}
<button *ngIf="data.profilePic" matTooltip="{{'ACTIONS.DELETE' | translate}}" color="warn" (click)="deletePic()" </button>
mat-icon-button> <button
<mat-icon>remove_circle</mat-icon> *ngIf="data.profilePic"
matTooltip="{{ 'ACTIONS.DELETE' | translate }}"
color="warn"
(click)="deletePic()"
mat-icon-button
>
<i class="las la-minus-circle"></i>
</button> </button>
</div> </div>
</div> </div>
<div mat-dialog-actions class="action"> <div mat-dialog-actions class="action">
<button color="primary" mat-stroked-button class="ok-button" (click)="closeDialog()"> <button color="primary" mat-stroked-button class="ok-button" (click)="closeDialog()">
{{'ACTIONS.CLOSE' | translate}} {{ 'ACTIONS.CLOSE' | translate }}
</button> </button>
</div> </div>

View File

@ -49,7 +49,9 @@ export class ProfilePictureComponent {
this.data.profilePic = resp.user?.human?.profile?.avatarUrl ?? ''; this.data.profilePic = resp.user?.human?.profile?.avatarUrl ?? '';
}); });
}) })
.catch((error) => this.toast.showError(error)); .catch((error) => {
this.toast.showError(error.error, false);
});
} }
public closeDialog(): void { public closeDialog(): void {

View File

@ -18,6 +18,7 @@ import { AvatarModule } from 'src/app/modules/avatar/avatar.module';
import { CardModule } from 'src/app/modules/card/card.module'; import { CardModule } from 'src/app/modules/card/card.module';
import { FilterUserModule } from 'src/app/modules/filter-user/filter-user.module'; import { FilterUserModule } from 'src/app/modules/filter-user/filter-user.module';
import { InputModule } from 'src/app/modules/input/input.module'; import { InputModule } from 'src/app/modules/input/input.module';
import { NavToggleModule } from 'src/app/modules/nav-toggle/nav-toggle.module';
import { PaginatorModule } from 'src/app/modules/paginator/paginator.module'; import { PaginatorModule } from 'src/app/modules/paginator/paginator.module';
import { RefreshTableModule } from 'src/app/modules/refresh-table/refresh-table.module'; import { RefreshTableModule } from 'src/app/modules/refresh-table/refresh-table.module';
import { TableActionsModule } from 'src/app/modules/table-actions/table-actions.module'; import { TableActionsModule } from 'src/app/modules/table-actions/table-actions.module';
@ -49,6 +50,7 @@ import { UserTableComponent } from './user-table/user-table.component';
TranslateModule, TranslateModule,
FilterUserModule, FilterUserModule,
RouterModule, RouterModule,
NavToggleModule,
RefreshTableModule, RefreshTableModule,
TableActionsModule, TableActionsModule,
ActionKeysModule, ActionKeysModule,

View File

@ -8,13 +8,17 @@
[emitRefreshOnPreviousRoutes]="refreshOnPreviousRoutes" [emitRefreshOnPreviousRoutes]="refreshOnPreviousRoutes"
[showBorder]="true" [showBorder]="true"
> >
<div leftActions class="user-table-left-actions"> <div leftActions class="user-toggle-group">
<button class="type-button" [ngClass]="{ active: type === Type.TYPE_HUMAN }" (click)="setType(Type.TYPE_HUMAN)"> <cnsl-nav-toggle
{{ 'USER.TABLE.TYPES.HUMAN' | translate }} label="{{ 'USER.TABLE.TYPES.HUMAN' | translate }}"
</button> (clicked)="setType(Type.TYPE_HUMAN)"
<button class="type-button" [ngClass]="{ active: type === Type.TYPE_MACHINE }" (click)="setType(Type.TYPE_MACHINE)"> [active]="type === Type.TYPE_HUMAN"
{{ 'USER.TABLE.TYPES.MACHINE' | translate }} ></cnsl-nav-toggle>
</button> <cnsl-nav-toggle
label="{{ 'USER.TABLE.TYPES.MACHINE' | translate }}"
(clicked)="setType(Type.TYPE_MACHINE)"
[active]="type === Type.TYPE_MACHINE"
></cnsl-nav-toggle>
</div> </div>
<ng-template cnslHasRole [hasRole]="['user.write']" actions> <ng-template cnslHasRole [hasRole]="['user.write']" actions>

View File

@ -4,31 +4,31 @@
$foreground: map-get($theme, foreground); $foreground: map-get($theme, foreground);
$is-dark-theme: map-get($theme, is-dark); $is-dark-theme: map-get($theme, is-dark);
.user-table-left-actions { .user-toggle-group {
display: flex; display: flex;
align-items: center; margin: 0;
height: 100%;
.type-button { .toggle-row {
border: none; display: flex;
background: none; align-items: center;
text-align: left;
padding: 0.75rem 0;
opacity: 0.6;
font-size: 15px;
cursor: pointer;
color: map-get($foreground, text);
&:first-child { i {
margin-right: 1rem; margin-right: 0.5rem;
} }
&:hover { .info-i {
opacity: 1; font-size: 1.2rem;
margin-left: 0.5rem;
margin-right: 0;
} }
&.active { .current-dot {
font-weight: 600; height: 8px;
opacity: 1; width: 8px;
border-radius: 50%;
background-color: rgb(84, 142, 230);
margin-left: 0.5rem;
} }
} }
} }

View File

@ -33,15 +33,19 @@ export class ToastService {
} }
} }
public showError(grpcError: any): void { public showError(error: any | string, isGrpc: boolean = true): void {
const { message, code, metadata } = grpcError; if (isGrpc) {
if (code !== 16) { const { message, code, metadata } = error;
this.translate if (code !== 16) {
.get('ACTIONS.CLOSE') this.translate
.pipe(take(1)) .get('ACTIONS.CLOSE')
.subscribe((value) => { .pipe(take(1))
this.showMessage(decodeURI(message), value, false); .subscribe((value) => {
}); this.showMessage(decodeURI(message), value, false);
});
}
} else {
this.showMessage(error as string, '', false);
} }
} }

View File

@ -12,6 +12,7 @@
@import 'src/app/modules/app-card/app-card.component'; @import 'src/app/modules/app-card/app-card.component';
@import 'src/app/modules/contributors/contributors.component'; @import 'src/app/modules/contributors/contributors.component';
@import 'src/app/modules/nav/nav.component'; @import 'src/app/modules/nav/nav.component';
@import 'src/app/modules/nav-toggle/nav-toggle.component';
@import './styles/toast.scss'; @import './styles/toast.scss';
@import 'src/app/modules/table-actions/table-actions.component'; @import 'src/app/modules/table-actions/table-actions.component';
@import 'src/app/modules/org-context/org-context.component.scss'; @import 'src/app/modules/org-context/org-context.component.scss';
@ -62,6 +63,7 @@
@include main-theme($theme); @include main-theme($theme);
@include avatar-theme($theme); @include avatar-theme($theme);
@include nav-theme($theme); @include nav-theme($theme);
@include nav-toggle-theme($theme);
@include header-theme($theme); @include header-theme($theme);
@include app-type-radio-theme($theme); @include app-type-radio-theme($theme);
@include projects-theme($theme); @include projects-theme($theme);

View File

@ -446,9 +446,18 @@ $custom-typography: mat.define-typography-config(
} }
} }
.mat-button-toggle-button { .mat-button-toggle-group-appearance-standard {
border-color: map-get($foreground, divider);
}
.mat-button-toggle {
background-color: mat.get-color-from-palette($background, cards); background-color: mat.get-color-from-palette($background, cards);
transition: background-color 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); transition: background-color 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
transition: border-color 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
&.mat-button-toggle-checked {
background-color: #00000010;
}
} }
.main-container, .main-container,
@ -509,9 +518,18 @@ $custom-typography: mat.define-typography-config(
} }
} }
.mat-button-toggle-button { .mat-button-toggle-group-appearance-standard {
border-color: map-get($foreground, divider);
}
.mat-button-toggle {
background-color: mat.get-color-from-palette($background, cards); background-color: mat.get-color-from-palette($background, cards);
transition: background-color 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); transition: background-color 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
transition: border-color 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
&.mat-button-toggle-checked {
background-color: mat.get-color-from-palette($background, background);
}
} }
.main-container, .main-container,
@ -601,3 +619,15 @@ i {
.mat-checkbox-inner-container.mat-checkbox-inner-container-no-side-margin { .mat-checkbox-inner-container.mat-checkbox-inner-container-no-side-margin {
margin-right: 0.5rem !important; margin-right: 0.5rem !important;
} }
.mat-button-toggle-button {
display: flex;
height: 36px;
line-height: 36px !important;
align-items: center;
font-size: 14px !important;
}
.mat-button-toggle-label-content {
line-height: 36px;
}

View File

@ -20,8 +20,8 @@
transform: all 0.2 linear; transform: all 0.2 linear;
font-size: 1rem; font-size: 1rem;
border: none; border: none;
border: 1px solid if($is-dark-theme, #f9f7f725, #1a191938); border: 1px solid if($is-dark-theme, #f9f7f775, #1a191954);
background-color: if($is-dark-theme, #00000020, #00000004); background-color: if($is-dark-theme, #00000040, #00000004);
border-radius: 4px; border-radius: 4px;
height: 40px; height: 40px;
padding: 10px; padding: 10px;
@ -34,7 +34,7 @@
margin-bottom: 2px; margin-bottom: 2px;
&:hover { &:hover {
border-color: if($is-dark-theme, #aeafb1, #1a1b1b); border-color: if($is-dark-theme, #e0e0e0, #1a1b1b);
} }
&:active, &:active,