Skip to content

Commit

Permalink
add user preferences component
Browse files Browse the repository at this point in the history
* This Commit adds Preferences in navbar
* Updates the navbar

Signed-off-by: bupd <[email protected]>
  • Loading branch information
bupd committed Nov 23, 2024
1 parent 0752337 commit 41a40bc
Show file tree
Hide file tree
Showing 20 changed files with 344 additions and 3 deletions.
2 changes: 2 additions & 0 deletions src/portal/src/app/base/base.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { MemberGuard } from '../shared/router-guard/member-guard-activate.servic
import { ProjectRoutingResolver } from '../services/routing-resolvers/project-routing-resolver.service';
import { PasswordSettingComponent } from './password-setting/password-setting.component';
import { AccountSettingsModalComponent } from './account-settings/account-settings-modal.component';
import { PreferenceSettingsComponent } from './preference-settings/preference-settings.component';
import { ForgotPasswordComponent } from './password-setting/forgot-password/forgot-password.component';
import { GlobalConfirmationDialogComponent } from './global-confirmation-dialog/global-confirmation-dialog.component';

Expand Down Expand Up @@ -169,6 +170,7 @@ const routes: Routes = [
declarations: [
HarborShellComponent,
PasswordSettingComponent,
PreferenceSettingsComponent,
AccountSettingsModalComponent,
ForgotPasswordComponent,
GlobalConfirmationDialogComponent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@
</div>
</clr-main-container>
<account-settings-modal></account-settings-modal>
<preference-settings></preference-settings>
<password-setting></password-setting>
<global-confirmation-dialog></global-confirmation-dialog>
<about-dialog></about-dialog>
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { SkinableConfig } from '../../services/skinable-config.service';
import { AppConfigService } from '../../services/app-config.service';
import { ErrorHandler } from '../../shared/units/error-handler';
import { AccountSettingsModalComponent } from '../account-settings/account-settings-modal.component';
import { PreferenceSettingsComponent } from '../preference-settings/preference-settings.component';
import { InlineAlertComponent } from '../../shared/components/inline-alert/inline-alert.component';
import { ScannerService } from '../../../../ng-swagger-gen/services/scanner.service';
import { UserService } from '../../../../ng-swagger-gen/services/user.service';
Expand Down Expand Up @@ -128,6 +129,9 @@ describe('HarborShellComponent', () => {
).componentInstance;
component.accountSettingsModal.inlineAlert =
TestBed.createComponent(InlineAlertComponent).componentInstance;
component.prefSetting = TestBed.createComponent(
PreferenceSettingsComponent
).componentInstance;
component.pwdSetting = TestBed.createComponent(
PasswordSettingComponent
).componentInstance;
Expand All @@ -149,6 +153,16 @@ describe('HarborShellComponent', () => {
fixture.nativeElement.querySelector('#account_settings_username');
expect(accountSettingsUsernameInput).toBeTruthy();
});
it('should open users preferenses', async () => {
component.openModal({
modalName: modalEvents.PREFERENCES,
modalFlag: false,
});
await fixture.whenStable();
const dropdowns =
fixture.nativeElement.querySelector('.dropdowns');
expect(dropdowns).toBeTruthy();
});
it('should open users changPwd', async () => {
component.openModal({
modalName: modalEvents.CHANGE_PWD,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { THEME_ARRAY, ThemeInterface } from '../../services/theme';
import { clone } from '../../shared/units/utils';
import { ThemeService } from '../../services/theme.service';
import { AccountSettingsModalComponent } from '../account-settings/account-settings-modal.component';
import { PreferenceSettingsComponent } from '../preference-settings/preference-settings.component';
import {
EventService,
HarborEvent,
Expand All @@ -53,6 +54,9 @@ export class HarborShellComponent implements OnInit, OnDestroy {
@ViewChild(AccountSettingsModalComponent)
accountSettingsModal: AccountSettingsModalComponent;

@ViewChild(PreferenceSettingsComponent)
prefSetting: PreferenceSettingsComponent;

@ViewChild(PasswordSettingComponent)
pwdSetting: PasswordSettingComponent;

Expand Down Expand Up @@ -176,6 +180,9 @@ export class HarborShellComponent implements OnInit, OnDestroy {
case modalEvents.USER_PROFILE:
this.accountSettingsModal.open();
break;
case modalEvents.PREFERENCES:
this.prefSetting.open();
break;
case modalEvents.CHANGE_PWD:
this.pwdSetting.open();
break;
Expand Down
1 change: 1 addition & 0 deletions src/portal/src/app/base/modal-events.const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// limitations under the License.
export const modalEvents = {
USER_PROFILE: 'USER_PROFILE',
PREFERENCES: 'PREFERENCES',
CHANGE_PWD: 'CHANGE_PWD',
ABOUT: 'ABOUT',
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<clr-modal
[(clrModalOpen)]="opened"
[clrModalClosable]="false"
[clrModalStaticBackdrop]="false">
<h3 class="modal-title">{{ 'CHANGE_PWD.TITLE' | translate }}</h3>
<div class="modal-body dialog-body">
<div class="dropdowns">
<clr-dropdown class="dropdown-lang dropdown bottom-left">
<button class="nav-icon nav-icon-width" clrDropdownToggle>
<clr-icon shape="world" class="icon-left"></clr-icon>
<span class="currentLocale">{{ currentLang }}</span>
<clr-icon size="10" shape="caret down"></clr-icon>
</button>
<clr-dropdown-menu *clrIfOpen>
<a
*ngFor="let lang of guiLanguages"
href="javascript:void(0)"
clrDropdownItem
(click)="switchLanguage(lang[0])"
[class.lang-selected]="matchLang(lang[0])"
>{{ lang[1][0] }}</a
>
</clr-dropdown-menu>
</clr-dropdown>
<div class="nav-divider"></div>
<clr-dropdown class="dropdown-locale dropdown bottom-left">
<button class="nav-icon nav-icon-width" clrDropdownToggle>
<clr-icon shape="date" class="icon-left"></clr-icon>
<span class="currentLocale">{{
currentDatetimeRendering | translate
}}</span>
<clr-icon size="10" shape="caret down"></clr-icon>
</button>
<clr-dropdown-menu *clrIfOpen>
<a
*ngFor="let rendering of guiDatetimeRenderings"
href="javascript:void(0)"
clrDropdownItem
(click)="switchDatetimeRendering(rendering[0])"
[class.locale-selected]="
matchDatetimeRendering(rendering[0])
"
>{{ rendering[1] | translate }}</a
>
</clr-dropdown-menu>
</clr-dropdown>
</div>
</div>
<div class="modal-footer margin-left-override">
<button type="button" class="btn btn-primary" (click)="close()">
{{ 'BUTTON.CLOSE' | translate }}
</button>
</div>
</clr-modal>
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
.sign-in-override {
padding-left: 0 !important;
padding-right: 5px !important;
}

.sign-up-override {
padding-left: 5px !important;
}

.custom-divider {
display: inline-block;
border-right: 2px inset snow;
padding: 2px 0;
vertical-align: middle;
height: 24px;
}

.locale-selected {
font-weight: bold;
}

.nav-divider {
display: inline-block;
width: 1px;
height: 20px;
background-color: #fafafa;
position: relative;
opacity: 0.15;
content: '';
}

.nav-about-link {
padding-left: 1.6rem !important;
padding-right: 1rem !important;
min-width: 125px;
text-align: left !important;
}

.harbor-logo {
width: 40px;
margin-right: 15px;
}

.nav-icon-width {
width: auto !important;
padding-left: 18px !important;

.icon-left {
left: -8px;
}
/* stylelint-disable */
.currentLocale {
padding-right: 40px;
padding-left: 10px;
}
}

.dropdowns {
display: flex;
flex-direction: row;
align-content: center;
justify-content: flex-start;
align-items: center;
gap: 20px;
}

.user-icon {
left: -2px;
}

.headerLogo {
width: 36px;
height: 36px;
object-fit: fill;
}

.dropdown-item {
outline: none;
}

.dropdown-locale {
padding-right: 0.5rem;
}

.user-down {
right: 1rem !important;
}
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// Copyright Project Harbor Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { Component, OnInit } from '@angular/core';
import { AppConfigService } from '../../services/app-config.service';
import { SkinableConfig } from '../../services/skinable-config.service';
import { TranslateService } from '@ngx-translate/core';
import { getDatetimeRendering } from 'src/app/shared/units/shared.utils';
import { registerLocaleData } from '@angular/common';
import { forkJoin, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { ClrCommonStrings } from '@clr/angular/utils/i18n/common-strings.interface';
import { ClrCommonStringsService } from '@clr/angular';
import {
DATETIME_RENDERINGS,
DatetimeRendering,
DEFAULT_DATETIME_RENDERING_LOCALSTORAGE_KEY,
DEFAULT_LANG_LOCALSTORAGE_KEY,
DefaultDatetimeRendering,
DeFaultLang,
LANGUAGES,
stringsForClarity,
SupportedLanguage,
} from '../../shared/entities/shared.const';

@Component({
selector: 'preference-settings',
templateUrl: 'preference-settings.component.html',
styleUrls: ['preference-settings.component.scss'],
})
export class PreferenceSettingsComponent implements OnInit {
readonly guiLanguages = Object.entries(LANGUAGES);
readonly guiDatetimeRenderings = Object.entries(DATETIME_RENDERINGS);
selectedLang: SupportedLanguage = DeFaultLang;
selectedDatetimeRendering: DatetimeRendering = DefaultDatetimeRendering;
opened: boolean = false;
build: string = '4276418';
customIntroduction: string;
customName: string;
customLogo: string;

constructor(
private appConfigService: AppConfigService,
private skinableConfig: SkinableConfig,
private translate: TranslateService,
private commonStrings: ClrCommonStringsService
) {}

ngOnInit(): void {
// custom skin
let customSkinObj = this.skinableConfig.getSkinConfig();
if (customSkinObj) {
if (customSkinObj.product) {
this.customLogo = customSkinObj.product.logo;
this.customName = customSkinObj.product.name;
this.customIntroduction = customSkinObj.product.introduction;
}
}
this.selectedLang = this.translate.currentLang as SupportedLanguage;
if (this.selectedLang) {
registerLocaleData(
LANGUAGES[this.selectedLang][1],
this.selectedLang
);
this.translateClarityComponents();
}
this.selectedDatetimeRendering = getDatetimeRendering();
}

//Internationalization for Clarity components, refer to https://clarity.design/documentation/internationalization
translateClarityComponents() {
const translatedObservables: Observable<string | any>[] = [];
const translatedStringsForClarity: Partial<ClrCommonStrings> = {};
for (let key in stringsForClarity) {
translatedObservables.push(
this.translate.get(stringsForClarity[key]).pipe(
map(res => {
return [key, res];
})
)
);
}
forkJoin(translatedObservables).subscribe(res => {
if (res?.length) {
res.forEach(item => {
translatedStringsForClarity[item[0]] = item[1];
});
this.commonStrings.localize(translatedStringsForClarity);
}
});
}

public get version(): string {
let appConfig = this.appConfigService.getConfig();
return appConfig.harbor_version;
}

public get currentLang(): string {
if (this.selectedLang) {
return LANGUAGES[this.selectedLang][0] as string;
}
return null;
}

public get currentDatetimeRendering(): string {
return DATETIME_RENDERINGS[this.selectedDatetimeRendering];
}

matchLang(lang: SupportedLanguage): boolean {
return lang === this.selectedLang;
}

matchDatetimeRendering(datetime: DatetimeRendering): boolean {
return datetime === this.selectedDatetimeRendering;
}

// Switch languages
switchLanguage(lang: SupportedLanguage): void {
this.selectedLang = lang;
localStorage.setItem(DEFAULT_LANG_LOCALSTORAGE_KEY, lang);
// due to the bug(https://github.com/ngx-translate/core/issues/1258) of translate module
// have to reload
this.translate.use(lang).subscribe(() => window.location.reload());
}

switchDatetimeRendering(datetime: DatetimeRendering): void {
this.selectedDatetimeRendering = datetime;
localStorage.setItem(
DEFAULT_DATETIME_RENDERING_LOCALSTORAGE_KEY,
datetime
);
// have to reload,as HarborDatetimePipe is pure pipe
window.location.reload();
}

public open(): void {
this.opened = true;
}

public close(): void {
this.opened = false;
}
}
Loading

0 comments on commit 41a40bc

Please sign in to comment.