mirror of
https://github.com/andwati/zitadel.git
synced 2026-11-06 06:12:16 +00:00
feat(console): integrate app (#7417)
* docs, frameworks view * project select, integrate app page * fix search project autocomplete * framework autocomplete * framwork select component, integrate, mapping to oidc config * param * fix route handler * setname projectid context * app-create page without context * show description of app type, info section * redirects section * updatevalue observable * fix redirect uris section * i18n * setup config * backbutton behavior, cleanup * cleanup * lint * allow other framework jump off * dev mode warning * navigate to project * rm import * i18n, guide link * edit name dialog * show warning for duplicate name
This commit is contained in:
@@ -57,6 +57,13 @@
|
||||
</cnsl-top-view>
|
||||
|
||||
<div class="max-width-container">
|
||||
<cnsl-info-section *ngIf="isNew()" class="problem" [type]="InfoSectionType.INFO">
|
||||
<div class="jumptoproject-row">
|
||||
<span class="jumptoproject">{{ 'APP.PAGES.JUMPTOPROJECT' | translate }}</span>
|
||||
<a [routerLink]="['/projects', projectId]" color="primary" mat-raised-button>{{ 'ACTIONS.CONFIGURE' | translate }}</a>
|
||||
</div>
|
||||
</cnsl-info-section>
|
||||
|
||||
<div
|
||||
class="compliance"
|
||||
*ngIf="app && app.oidcConfig && app.oidcConfig.complianceProblemsList && app.oidcConfig.complianceProblemsList?.length"
|
||||
|
||||
@@ -125,6 +125,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
.jumptoproject-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.jumptoproject {
|
||||
margin-top: 0.25rem;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.compliance {
|
||||
padding-bottom: 1rem;
|
||||
padding-top: 0.5rem;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { COMMA, ENTER, SPACE } from '@angular/cdk/keycodes';
|
||||
import { Location } from '@angular/common';
|
||||
import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
|
||||
import { Component, OnDestroy, OnInit, ViewEncapsulation, signal } from '@angular/core';
|
||||
import { AbstractControl, FormControl, UntypedFormBuilder, UntypedFormControl, UntypedFormGroup } from '@angular/forms';
|
||||
import { MatCheckboxChange } from '@angular/material/checkbox';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
@@ -149,6 +149,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
|
||||
public settingsList: SidenavSetting[] = [{ id: 'configuration', i18nKey: 'APP.CONFIGURATION' }];
|
||||
public currentSetting: string | undefined = this.settingsList[0].id;
|
||||
|
||||
public isNew = signal<boolean>(false);
|
||||
constructor(
|
||||
private envSvc: EnvironmentService,
|
||||
public translate: TranslateService,
|
||||
@@ -245,6 +246,9 @@ export class AppDetailComponent implements OnInit, OnDestroy {
|
||||
public ngOnInit(): void {
|
||||
const projectId = this.route.snapshot.paramMap.get('projectid');
|
||||
const appId = this.route.snapshot.paramMap.get('appid');
|
||||
const isNew = this.route.snapshot.queryParamMap.get('new');
|
||||
|
||||
this.isNew.set(isNew === 'true');
|
||||
|
||||
if (projectId && appId) {
|
||||
this.projectId = projectId;
|
||||
|
||||
@@ -3,6 +3,7 @@ import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
import { AppCreateComponent } from '../apps/app-create/app-create.component';
|
||||
import { AppDetailComponent } from '../apps/app-detail/app-detail.component';
|
||||
import { IntegrateAppComponent } from './integrate/integrate.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
@@ -10,6 +11,11 @@ const routes: Routes = [
|
||||
component: AppCreateComponent,
|
||||
data: { animation: 'AddPage' },
|
||||
},
|
||||
{
|
||||
path: 'integrate',
|
||||
component: IntegrateAppComponent,
|
||||
data: { animation: 'AddPage' },
|
||||
},
|
||||
{
|
||||
path: ':appid',
|
||||
component: AppDetailComponent,
|
||||
|
||||
@@ -43,6 +43,9 @@ import { AuthMethodDialogComponent } from './app-detail/auth-method-dialog/auth-
|
||||
import { AppSecretDialogComponent } from './app-secret-dialog/app-secret-dialog.component';
|
||||
import { AppsRoutingModule } from './apps-routing.module';
|
||||
import { RedirectUrisComponent } from './redirect-uris/redirect-uris.component';
|
||||
import { IntegrateAppComponent } from './integrate/integrate.component';
|
||||
import { OIDCConfigurationComponent } from 'src/app/components/oidc-configuration/oidc-configuration.component';
|
||||
import { FrameworkChangeComponent } from 'src/app/components/framework-change/framework-change.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@@ -50,12 +53,15 @@ import { RedirectUrisComponent } from './redirect-uris/redirect-uris.component';
|
||||
AppDetailComponent,
|
||||
AppSecretDialogComponent,
|
||||
RedirectUrisComponent,
|
||||
IntegrateAppComponent,
|
||||
AdditionalOriginsComponent,
|
||||
AuthMethodDialogComponent,
|
||||
],
|
||||
imports: [
|
||||
FrameworkChangeComponent,
|
||||
CommonModule,
|
||||
A11yModule,
|
||||
OIDCConfigurationComponent,
|
||||
RedirectPipeModule,
|
||||
NameDialogModule,
|
||||
AppRadioModule,
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
<div class="app-integrate-wrapper">
|
||||
<div class="integrate-layout-container">
|
||||
<div class="max-width-container">
|
||||
<div class="top-control">
|
||||
<button (click)="close()" mat-icon-button matTooltip="{{ 'ACTIONS.CLOSE' | translate }}">
|
||||
<mat-icon *ngIf="navigation.isBackPossible">arrow_back</mat-icon>
|
||||
<mat-icon *ngIf="!navigation.isBackPossible">close</mat-icon>
|
||||
</button>
|
||||
<span class="abort">{{ 'APP.PAGES.CREATE' | translate }}</span>
|
||||
<mat-progress-spinner
|
||||
class="progress-spinner"
|
||||
color="primary"
|
||||
*ngIf="loading"
|
||||
diameter="30"
|
||||
mode="indeterminate"
|
||||
></mat-progress-spinner>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="max-width-container">
|
||||
<div class="offset-content">
|
||||
<h1>{{ 'QUICKSTART.ALMOSTDONE' | translate }}</h1>
|
||||
<p>{{ 'QUICKSTART.REVIEWCONFIGURATION_DESCRIPTION' | translate: { value: framework()?.title } }}</p>
|
||||
|
||||
<div class="grid-layout">
|
||||
<div>
|
||||
<cnsl-framework-change
|
||||
*ngIf="framework"
|
||||
class="framework-selector"
|
||||
(frameworkChanged)="setFramework($event)"
|
||||
></cnsl-framework-change>
|
||||
<div class="steps">
|
||||
<div class="step">
|
||||
<span class="step-title">{{ 'PROJECT.PAGES.TITLE' | translate }}</span>
|
||||
<span>{{ projectName$ | async }}</span>
|
||||
</div>
|
||||
|
||||
<div class="step top-border">
|
||||
<a *ngIf="framework()" [href]="'https://zitadel.com' + framework()?.docsLink" target="_blank"
|
||||
>{{ framework()?.title }} {{ 'QUICKSTART.GUIDE' | translate }}</a
|
||||
>
|
||||
<a href="https://zitadel.com/docs/sdk-examples/introduction" target="_blank">{{
|
||||
'QUICKSTART.BROWSEEXAMPLES' | translate
|
||||
}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-wrapper">
|
||||
<cnsl-card class="review-card" title="{{ 'QUICKSTART.REVIEWCONFIGURATION' | translate }}">
|
||||
<cnsl-info-section *ngIf="showRenameWarning | async" [type]="InfoSectionType.WARN"
|
||||
><span class="duplicate-name-warning">{{
|
||||
'QUICKSTART.DUPLICATEAPPRENAME' | translate
|
||||
}}</span></cnsl-info-section
|
||||
>
|
||||
<cnsl-oidc-app-configuration
|
||||
*ngIf="(oidcAppRequest | async)?.toObject() as config"
|
||||
[name]="(oidcAppRequest | async)?.toObject()?.name"
|
||||
(changeName)="editName()"
|
||||
[configuration]="config"
|
||||
></cnsl-oidc-app-configuration>
|
||||
|
||||
<cnsl-info-section *ngIf="framework()?.description as desc" [type]="InfoSectionType.INFO">
|
||||
<p class="review-description">
|
||||
{{ desc }}
|
||||
</p>
|
||||
</cnsl-info-section>
|
||||
</cnsl-card>
|
||||
|
||||
<cnsl-card
|
||||
title="{{ 'QUICKSTART.REDIRECTS' | translate }}"
|
||||
description="{{ 'APP.OIDC.REDIRECTTITLE' | translate }}"
|
||||
>
|
||||
<cnsl-info-section [type]="InfoSectionType.ALERT">
|
||||
<span class="redirect-description">
|
||||
{{ 'QUICKSTART.DEVMODEWARN' | translate }}
|
||||
</span>
|
||||
</cnsl-info-section>
|
||||
<cnsl-info-section *ngIf="(oidcAppRequest | async)?.toObject()?.appType === OIDCAppType.OIDC_APP_TYPE_NATIVE">
|
||||
<span class="redirect-description">
|
||||
{{ 'APP.OIDC.REDIRECTDESCRIPTIONNATIVE' | translate }}
|
||||
</span>
|
||||
</cnsl-info-section>
|
||||
<cnsl-info-section
|
||||
*ngIf="
|
||||
(oidcAppRequest | async)?.toObject()?.appType === OIDCAppType.OIDC_APP_TYPE_WEB ||
|
||||
(oidcAppRequest | async)?.toObject()?.appType === OIDCAppType.OIDC_APP_TYPE_USER_AGENT
|
||||
"
|
||||
>
|
||||
<span class="redirect-description">
|
||||
{{ 'APP.OIDC.REDIRECTDESCRIPTIONWEB' | translate }}
|
||||
</span>
|
||||
</cnsl-info-section>
|
||||
|
||||
<cnsl-redirect-uris
|
||||
*ngIf="requestRedirectValuesSubject$"
|
||||
class="redirect-section"
|
||||
[disabled]="false"
|
||||
[isNative]="(oidcAppRequest | async)?.toObject()?.appType === OIDCAppType.OIDC_APP_TYPE_NATIVE"
|
||||
[(ngModel)]="redirectUris"
|
||||
[getValues]="requestRedirectValuesSubject$"
|
||||
title="{{ 'APP.OIDC.REDIRECT' | translate }}"
|
||||
[devMode]="true"
|
||||
data-e2e="redirect-uris"
|
||||
>
|
||||
</cnsl-redirect-uris>
|
||||
|
||||
<cnsl-redirect-uris
|
||||
*ngIf="requestRedirectValuesSubject$"
|
||||
class="redirect-section"
|
||||
[disabled]="false"
|
||||
[(ngModel)]="postLogoutUrisList"
|
||||
[getValues]="requestRedirectValuesSubject$"
|
||||
title="{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}"
|
||||
[isNative]="(oidcAppRequest | async)?.toObject()?.appType === OIDCAppType.OIDC_APP_TYPE_NATIVE"
|
||||
[devMode]="true"
|
||||
data-e2e="postlogout-uris"
|
||||
>
|
||||
</cnsl-redirect-uris>
|
||||
</cnsl-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="app-integrate-actions">
|
||||
<button
|
||||
mat-raised-button
|
||||
[disabled]="loading"
|
||||
class="create-button"
|
||||
color="primary"
|
||||
(click)="createApp()"
|
||||
data-e2e="create-button"
|
||||
>
|
||||
{{ 'ACTIONS.CREATE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,214 @@
|
||||
h1 {
|
||||
font-weight: 500;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.integrate-layout-container {
|
||||
margin: 0 -2rem 2rem -2rem;
|
||||
padding: 3rem 2rem 14rem 2rem;
|
||||
|
||||
.top-control {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.abort {
|
||||
font-size: 1.2rem;
|
||||
margin-left: 1.5rem;
|
||||
text-transform: uppercase;
|
||||
font-size: 14px;
|
||||
opacity: 0.8;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.progress-spinner {
|
||||
margin-left: 2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.offset-content {
|
||||
margin-top: -14rem;
|
||||
}
|
||||
|
||||
.grid-layout {
|
||||
margin-top: 2rem;
|
||||
display: grid;
|
||||
grid-template-columns: [first] 300px [second] auto;
|
||||
grid-column-gap: 5rem;
|
||||
|
||||
.framework-selector {
|
||||
margin-bottom: 2rem;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.steps {
|
||||
margin-top: 0.5rem;
|
||||
|
||||
.step {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 1rem 0;
|
||||
|
||||
.step-title {
|
||||
font-weight: 700;
|
||||
font-size: 12px;
|
||||
opacity: 0.8;
|
||||
margin-bottom: 0.5rem;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
a {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.redirect-p {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.redirect-description {
|
||||
font-size: 14px;
|
||||
margin: 0.25rem 0 0 0;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 900px) {
|
||||
.integrate-layout-container {
|
||||
margin: 0 -2rem 2rem -2rem;
|
||||
background: transparent;
|
||||
padding: 3rem 2rem 14rem 2rem;
|
||||
}
|
||||
|
||||
.grid-layout {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.steps {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card-wrapper {
|
||||
margin-top: -1rem;
|
||||
|
||||
.duplicate-name-warning {
|
||||
margin-top: 0.25rem;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin app-integrate-theme($theme) {
|
||||
$primary: map-get($theme, primary);
|
||||
$warn: map-get($theme, warn);
|
||||
$background: map-get($theme, background);
|
||||
$accent: map-get($theme, accent);
|
||||
$primary-color: map-get($primary, 500);
|
||||
|
||||
$warn-color: map-get($warn, 500);
|
||||
$accent-color: map-get($accent, 500);
|
||||
$foreground: map-get($theme, foreground);
|
||||
$is-dark-theme: map-get($theme, is-dark);
|
||||
$back: map-get($background, background);
|
||||
|
||||
$list-background-color: map-get($background, 300);
|
||||
$card-background-color: map-get($background, cards);
|
||||
$border-color: if($is-dark-theme, rgba(#8795a1, 0.2), rgba(#8795a1, 0.2));
|
||||
$border-selected-color: if($is-dark-theme, #fff, #000);
|
||||
|
||||
.integrate-layout-container {
|
||||
background: map-get($background, metadata-section);
|
||||
}
|
||||
|
||||
.review-description {
|
||||
font-size: 14px;
|
||||
margin: 0.25rem 0 0 0;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.grid-layout {
|
||||
.step {
|
||||
&.top-border {
|
||||
border-top: 2px solid $border-color;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: $primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
.framework-card-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
|
||||
.framework-card {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
text-decoration: none;
|
||||
border-radius: 0.5rem;
|
||||
box-sizing: border-box;
|
||||
transition: all 0.1s ease-in;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 60px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: fit-content;
|
||||
border: 1px solid rgba(#8795a1, 0.2);
|
||||
padding: 0 0.5rem;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-width: 40px;
|
||||
max-height: 40px;
|
||||
object-fit: contain;
|
||||
object-position: center;
|
||||
}
|
||||
|
||||
.dark-only {
|
||||
display: if($is-dark-theme, block, none);
|
||||
}
|
||||
|
||||
.light-only {
|
||||
display: if($is-dark-theme, none, block);
|
||||
}
|
||||
|
||||
span {
|
||||
margin: 0.5rem;
|
||||
text-align: center;
|
||||
color: map-get($foreground, text);
|
||||
}
|
||||
|
||||
.action-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
font-size: 14px;
|
||||
margin-bottom: 0.5rem;
|
||||
color: map-get($primary, 400);
|
||||
|
||||
.icon {
|
||||
margin-left: 0rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.app-integrate-actions {
|
||||
margin-top: 2rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
|
||||
.create-button {
|
||||
height: 3.5rem;
|
||||
padding: 0 4rem;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
|
||||
import { IntegrateAppComponent } from './integrate.component';
|
||||
|
||||
describe('IntegrateAppComponent', () => {
|
||||
let component: IntegrateAppComponent;
|
||||
let fixture: ComponentFixture<IntegrateAppComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [IntegrateAppComponent],
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(IntegrateAppComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,220 @@
|
||||
import { C, COMMA, ENTER, SPACE } from '@angular/cdk/keycodes';
|
||||
import { StepperSelectionEvent } from '@angular/cdk/stepper';
|
||||
import { Location } from '@angular/common';
|
||||
import { Component, OnDestroy, OnInit, Signal, computed, effect, signal } from '@angular/core';
|
||||
import { AbstractControl, UntypedFormBuilder, UntypedFormControl, UntypedFormGroup } from '@angular/forms';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { ActivatedRoute, Params, Router } from '@angular/router';
|
||||
import { Buffer } from 'buffer';
|
||||
import { BehaviorSubject, Subject, Subscription, combineLatest } from 'rxjs';
|
||||
import { debounceTime, map, takeUntil } from 'rxjs/operators';
|
||||
import { RadioItemAuthType } from 'src/app/modules/app-radio/app-auth-method-radio/app-auth-method-radio.component';
|
||||
import { requiredValidator } from 'src/app/modules/form-field/validators/validators';
|
||||
import {
|
||||
APIAuthMethodType,
|
||||
OIDCAppType,
|
||||
OIDCAuthMethodType,
|
||||
OIDCGrantType,
|
||||
OIDCResponseType,
|
||||
} from 'src/app/proto/generated/zitadel/app_pb';
|
||||
import {
|
||||
AddAPIAppRequest,
|
||||
AddAPIAppResponse,
|
||||
AddOIDCAppRequest,
|
||||
AddOIDCAppResponse,
|
||||
AddSAMLAppRequest,
|
||||
} from 'src/app/proto/generated/zitadel/management_pb';
|
||||
import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
import { AppSecretDialogComponent } from '../app-secret-dialog/app-secret-dialog.component';
|
||||
import { InfoSectionType } from 'src/app/modules/info-section/info-section.component';
|
||||
import { Framework } from 'src/app/components/quickstart/quickstart.component';
|
||||
import { OIDC_CONFIGURATIONS } from 'src/app/utils/framework';
|
||||
import { NavigationService } from 'src/app/services/navigation.service';
|
||||
import { NameDialogComponent } from 'src/app/modules/name-dialog/name-dialog.component';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-integrate',
|
||||
templateUrl: './integrate.component.html',
|
||||
styleUrls: ['./integrate.component.scss'],
|
||||
})
|
||||
export class IntegrateAppComponent implements OnInit, OnDestroy {
|
||||
private destroy$: Subject<void> = new Subject();
|
||||
public projectId: string = '';
|
||||
public loading: boolean = false;
|
||||
public InfoSectionType: any = InfoSectionType;
|
||||
public framework = signal<Framework | undefined>(undefined);
|
||||
public showRenameWarning: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
public oidcAppRequest: BehaviorSubject<AddOIDCAppRequest> = new BehaviorSubject(new AddOIDCAppRequest());
|
||||
|
||||
public OIDCAppType: any = OIDCAppType;
|
||||
public requestRedirectValuesSubject$: Subject<void> = new Subject();
|
||||
|
||||
constructor(
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private router: Router,
|
||||
private toast: ToastService,
|
||||
private dialog: MatDialog,
|
||||
private mgmtService: ManagementService,
|
||||
private _location: Location,
|
||||
private breadcrumbService: BreadcrumbService,
|
||||
public navigation: NavigationService,
|
||||
) {
|
||||
effect(() => {
|
||||
const fwId = this.framework()?.id;
|
||||
const fw = this.framework();
|
||||
if (fw && fwId) {
|
||||
const request = OIDC_CONFIGURATIONS[fwId];
|
||||
request.setProjectId(this.projectId);
|
||||
request.setName(fw.title);
|
||||
request.setDevMode(true);
|
||||
this.requestRedirectValuesSubject$.next();
|
||||
this.showRenameWarning.next(false);
|
||||
|
||||
this.oidcAppRequest.next(request);
|
||||
return request;
|
||||
} else {
|
||||
const request = new AddOIDCAppRequest();
|
||||
this.oidcAppRequest.next(request);
|
||||
return request;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public projectName$ = combineLatest([this.mgmtService.ownedProjects, this.mgmtService.grantedProjects]).pipe(
|
||||
map(([projects, grantedProjects]) => {
|
||||
const project = projects.find((project) => project.id === this.activatedRoute.snapshot.paramMap.get('projectid'));
|
||||
|
||||
const grantedproject = grantedProjects.find(
|
||||
(grantedproject) => grantedproject.projectId === this.activatedRoute.snapshot.paramMap.get('projectid'),
|
||||
);
|
||||
|
||||
return project?.name ?? grantedproject?.projectName ?? '';
|
||||
}),
|
||||
);
|
||||
|
||||
public setFramework(framework: Framework | undefined) {
|
||||
this.framework.set(framework);
|
||||
}
|
||||
|
||||
public ngOnInit(): void {
|
||||
const projectId = this.activatedRoute.snapshot.paramMap.get('projectid');
|
||||
if (projectId) {
|
||||
const breadcrumbs = [
|
||||
new Breadcrumb({
|
||||
type: BreadcrumbType.ORG,
|
||||
routerLink: ['/org'],
|
||||
}),
|
||||
new Breadcrumb({
|
||||
type: BreadcrumbType.PROJECT,
|
||||
name: '',
|
||||
param: { key: 'projectid', value: projectId },
|
||||
routerLink: ['/projects', projectId],
|
||||
isZitadel: false,
|
||||
}),
|
||||
];
|
||||
this.projectId = projectId;
|
||||
this.breadcrumbService.setBreadcrumb(breadcrumbs);
|
||||
}
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
}
|
||||
|
||||
public close(): void {
|
||||
if (this.navigation.isBackPossible) {
|
||||
this._location.back();
|
||||
} else {
|
||||
this.router.navigate(['/projects', this.projectId]);
|
||||
}
|
||||
}
|
||||
|
||||
public createApp(): void {
|
||||
this.loading = true;
|
||||
this.mgmtService
|
||||
.addOIDCApp(this.oidcAppRequest.getValue())
|
||||
.then((resp) => {
|
||||
this.loading = false;
|
||||
this.showRenameWarning.next(false);
|
||||
this.toast.showInfo('APP.TOAST.CREATED', true);
|
||||
if (resp.clientSecret) {
|
||||
this.showSavedDialog(resp);
|
||||
} else {
|
||||
this.router.navigate(['projects', this.projectId, 'apps', resp.appId], { queryParams: { new: true } });
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error.code === 6) {
|
||||
this.showRenameWarning.next(true);
|
||||
}
|
||||
this.loading = false;
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
||||
public editName() {
|
||||
const dialogRef = this.dialog.open(NameDialogComponent, {
|
||||
data: {
|
||||
name: this.oidcAppRequest.getValue()?.getName() ?? '',
|
||||
titleKey: 'APP.NAMEDIALOG.TITLE',
|
||||
descKey: 'APP.NAMEDIALOG.DESCRIPTION',
|
||||
labelKey: 'APP.NAMEDIALOG.NAME',
|
||||
},
|
||||
width: '400px',
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe((name) => {
|
||||
if (name && name !== this.framework()?.title) {
|
||||
const request = this.oidcAppRequest.getValue();
|
||||
request.setName(name);
|
||||
this.showRenameWarning.next(false);
|
||||
this.oidcAppRequest.next(request);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public showSavedDialog(added: AddOIDCAppResponse.AsObject | AddAPIAppResponse.AsObject): void {
|
||||
let clientSecret = '';
|
||||
if (added.clientSecret) {
|
||||
clientSecret = added.clientSecret;
|
||||
}
|
||||
let clientId = '';
|
||||
if (added.clientId) {
|
||||
clientId = added.clientId;
|
||||
}
|
||||
const dialogRef = this.dialog.open(AppSecretDialogComponent, {
|
||||
data: {
|
||||
clientSecret: clientSecret,
|
||||
clientId: clientId,
|
||||
},
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(() => {
|
||||
this.router.navigate(['projects', this.projectId, 'apps', added.appId], { queryParams: { new: true } });
|
||||
});
|
||||
}
|
||||
|
||||
public get redirectUris() {
|
||||
return this.oidcAppRequest.getValue().toObject().redirectUrisList;
|
||||
}
|
||||
|
||||
public set redirectUris(value: string[]) {
|
||||
const request = this.oidcAppRequest.getValue();
|
||||
request.setRedirectUrisList(value);
|
||||
this.oidcAppRequest.next(request);
|
||||
}
|
||||
|
||||
public get postLogoutUrisList() {
|
||||
return this.oidcAppRequest.getValue().toObject().postLogoutRedirectUrisList;
|
||||
}
|
||||
|
||||
public set postLogoutUrisList(value: string[]) {
|
||||
const request = this.oidcAppRequest.getValue();
|
||||
request.setPostLogoutRedirectUrisList(value);
|
||||
this.oidcAppRequest.next(request);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user