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:
Max Peintner 2024-02-28 17:52:21 +01:00 committed by GitHub
parent f4c72cbe14
commit 0fcdfe460c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
64 changed files with 2381 additions and 176 deletions

View File

@ -23,7 +23,12 @@
"polyfills": ["zone.js"], "polyfills": ["zone.js"],
"tsConfig": "tsconfig.app.json", "tsConfig": "tsconfig.app.json",
"inlineStyleLanguage": "scss", "inlineStyleLanguage": "scss",
"assets": ["src/favicon.ico", "src/assets", "src/manifest.webmanifest"], "assets": [
"src/favicon.ico",
"src/assets",
"src/manifest.webmanifest",
{ "glob": "**/*", "input": "../docs/static/img", "output": "assets/docs/img" }
],
"styles": ["src/styles.scss"], "styles": ["src/styles.scss"],
"scripts": ["./node_modules/tinycolor2/dist/tinycolor-min.js"], "scripts": ["./node_modules/tinycolor2/dist/tinycolor-min.js"],
"stylePreprocessorOptions": { "stylePreprocessorOptions": {
@ -81,6 +86,7 @@
}, },
"serve": { "serve": {
"builder": "@angular-devkit/build-angular:dev-server", "builder": "@angular-devkit/build-angular:dev-server",
"configurations": { "configurations": {
"production": { "production": {
"browserTarget": "console:build:production" "browserTarget": "console:build:production"

View File

@ -29,6 +29,7 @@
"@fortawesome/fontawesome-svg-core": "^6.4.2", "@fortawesome/fontawesome-svg-core": "^6.4.2",
"@fortawesome/free-brands-svg-icons": "^6.4.2", "@fortawesome/free-brands-svg-icons": "^6.4.2",
"@grpc/grpc-js": "^1.9.3", "@grpc/grpc-js": "^1.9.3",
"@netlify/framework-info": "^9.8.10",
"@ngx-translate/core": "^15.0.0", "@ngx-translate/core": "^15.0.0",
"angular-oauth2-oidc": "^15.0.1", "angular-oauth2-oidc": "^15.0.1",
"angularx-qrcode": "^16.0.0", "angularx-qrcode": "^16.0.0",

View File

@ -0,0 +1,35 @@
<form>
<cnsl-form-field class="full-width">
<cnsl-label>{{ 'QUICKSTART.FRAMEWORK' | translate }}</cnsl-label>
<input cnslInput type="text" placeholder="" #nameInput [formControl]="myControl" [matAutocomplete]="auto" />
<mat-autocomplete #auto="matAutocomplete" (optionSelected)="selected($event)">
<mat-option *ngIf="isLoading()" class="is-loading">
<mat-spinner diameter="30"></mat-spinner>
</mat-option>
<mat-option *ngFor="let framework of filteredOptions | async" [value]="framework.id">
<div class="framework-option">
<div class="framework-option-column">
<div class="img-wrapper">
<img class="dark-only" *ngIf="framework.imgSrcDark" [src]="framework.imgSrcDark" />
<img class="light-only" *ngIf="framework.imgSrcLight" [src]="framework.imgSrcLight" />
</div>
<span class="fill-space"></span>
<span>{{ framework.title }}</span>
</div>
</div>
</mat-option>
<mat-option *ngIf="withCustom" [value]="'other'">
<div class="framework-option">
<div class="framework-option-column">
<div class="img-wrapper"></div>
<span class="fill-space"></span>
<span>{{ 'QUICKSTART.FRAMEWORK_OTHER' | translate }}</span>
</div>
</div>
</mat-option>
</mat-autocomplete>
</cnsl-form-field>
</form>

View File

@ -0,0 +1,69 @@
@mixin framework-autocomplete-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);
.full-width {
width: 100%;
}
input {
max-width: 500px;
}
.framework-option {
display: flex;
align-items: center;
.framework-option-column {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
width: 100%;
span {
line-height: normal;
}
.fill-space {
flex: 1;
}
.img-wrapper {
width: 50px;
margin-right: 1rem;
img {
width: 100%;
height: 100%;
max-width: 30px;
max-height: 30px;
object-fit: contain;
object-position: center;
}
}
.dark-only {
display: if($is-dark-theme, block, none);
}
.light-only {
display: if($is-dark-theme, none, block);
}
}
}
}

View File

@ -0,0 +1,21 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FrameworkAutocompleteComponent } from './framework-autocomplete.component';
describe('FrameworkAutocompleteComponent', () => {
let component: FrameworkAutocompleteComponent;
let fixture: ComponentFixture<FrameworkAutocompleteComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [FrameworkAutocompleteComponent],
});
fixture = TestBed.createComponent(FrameworkAutocompleteComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,63 @@
import { CommonModule } from '@angular/common';
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnInit, Output, signal } from '@angular/core';
import { RouterModule } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core';
import { MatButtonModule } from '@angular/material/button';
import { MatSelectModule } from '@angular/material/select';
import { MatAutocompleteModule, MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { InputModule } from 'src/app/modules/input/input.module';
import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { Observable, map, of, startWith, switchMap, tap } from 'rxjs';
import { Framework } from '../quickstart/quickstart.component';
@Component({
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
selector: 'cnsl-framework-autocomplete',
templateUrl: './framework-autocomplete.component.html',
styleUrls: ['./framework-autocomplete.component.scss'],
imports: [
TranslateModule,
RouterModule,
MatSelectModule,
MatAutocompleteModule,
ReactiveFormsModule,
MatProgressSpinnerModule,
FormsModule,
CommonModule,
MatButtonModule,
InputModule,
],
})
export class FrameworkAutocompleteComponent implements OnInit {
public isLoading = signal(false);
@Input() public frameworkId?: string;
@Input() public frameworks: Framework[] = [];
@Input() public withCustom: boolean = false;
public myControl: FormControl = new FormControl();
@Output() public selectionChanged: EventEmitter<string> = new EventEmitter();
public filteredOptions: Observable<Framework[]> = of([]);
constructor() {}
public ngOnInit() {
this.filteredOptions = this.myControl.valueChanges.pipe(
startWith(''),
map((value) => {
return this._filter(value || '');
}),
);
}
private _filter(value: string): Framework[] {
const filterValue = value.toLowerCase();
return this.frameworks
.filter((option) => option.id)
.filter((option) => option.title.toLowerCase().includes(filterValue));
}
public selected(event: MatAutocompleteSelectedEvent): void {
this.selectionChanged.emit(event.option.value);
}
}

View File

@ -0,0 +1,17 @@
<h2 mat-dialog-title>{{ 'QUICKSTART.DIALOG.CHANGE.TITLE' | translate }}</h2>
<mat-dialog-content>
{{ 'QUICKSTART.DIALOG.CHANGE.DESCRIPTION' | translate }}
<div class="framework-change-block">
<cnsl-framework-autocomplete
[frameworkId]="data.framework.id"
[frameworks]="data.frameworks"
(selectionChanged)="findFramework($event)"
></cnsl-framework-autocomplete>
</div>
</mat-dialog-content>
<div>
<mat-dialog-actions class="actions">
<button mat-stroked-button mat-dialog-close>{{ 'ACTIONS.CANCEL' | translate }}</button>
<button color="primary" mat-raised-button (click)="close()" cdkFocusInitial>{{ 'ACTIONS.CHANGE' | translate }}</button>
</mat-dialog-actions>
</div>

View File

@ -0,0 +1,10 @@
.framework-change-block {
display: flex;
flex-direction: column;
align-items: stretch;
}
.actions {
display: flex;
justify-content: space-between;
}

View File

@ -0,0 +1,41 @@
import { Component, Inject, signal } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import {
MAT_DIALOG_DATA,
MatDialogActions,
MatDialogClose,
MatDialogContent,
MatDialogModule,
MatDialogRef,
MatDialogTitle,
} from '@angular/material/dialog';
import { FrameworkAutocompleteComponent } from '../framework-autocomplete/framework-autocomplete.component';
import { Framework } from '../quickstart/quickstart.component';
import { TranslateModule } from '@ngx-translate/core';
@Component({
selector: 'cnsl-framework-change-dialog',
templateUrl: './framework-change-dialog.component.html',
styleUrls: ['./framework-change-dialog.component.scss'],
standalone: true,
imports: [MatButtonModule, MatDialogModule, TranslateModule, FrameworkAutocompleteComponent],
})
export class FrameworkChangeDialogComponent {
public framework = signal<Framework | undefined>(undefined);
constructor(
public dialogRef: MatDialogRef<FrameworkChangeDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
) {
this.framework.set(data.framework);
}
public findFramework(id: string) {
const temp = this.data.frameworks.find((f: Framework) => f.id === id);
this.framework.set(temp);
}
public close() {
this.dialogRef.close(this.framework());
}
}

View File

@ -0,0 +1,14 @@
<div class="framework-change-wrapper">
<div class="framework-card-wrapper">
<div class="framework-card card" *ngIf="framework | async as framework">
<div [routerLink]="['/app-create']" [queryParams]="{ id: framework.docsLink }" class="">
<img class="dark-only" *ngIf="framework.imgSrcDark" [src]="framework.imgSrcDark" />
<img class="light-only" *ngIf="framework.imgSrcLight" [src]="framework.imgSrcLight" />
</div>
<span>{{ framework.title }}</span>
</div>
<button (click)="openDialog()" mat-stroked-button>
{{ 'ACTIONS.CHANGE' | translate }}
</button>
</div>
</div>

View File

@ -0,0 +1,82 @@
@mixin framework-change-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);
.framework-change-wrapper {
.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;
// background-color: if($is-dark-theme, map-get($background, state), #e4e7e4);
// box-shadow: 0 0 3px #0000001a;
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;
}
}
}
}
}
}

View File

@ -0,0 +1,21 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FrameworkChangeComponent } from './framework-change.component';
describe('FrameworkChangeComponent', () => {
let component: FrameworkChangeComponent;
let fixture: ComponentFixture<FrameworkChangeComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [FrameworkChangeComponent],
});
fixture = TestBed.createComponent(FrameworkChangeComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,88 @@
import { CommonModule } from '@angular/common';
import { Component, EventEmitter, OnDestroy, OnInit, Output, effect, signal } from '@angular/core';
import { ActivatedRoute, Params, RouterModule } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core';
import frameworkDefinition from '../../../../../docs/frameworks.json';
import { MatButtonModule } from '@angular/material/button';
import { listFrameworks, hasFramework, getFramework } from '@netlify/framework-info';
import { FrameworkName } from '@netlify/framework-info/lib/generated/frameworkNames';
import { FrameworkAutocompleteComponent } from '../framework-autocomplete/framework-autocomplete.component';
import { Framework } from '../quickstart/quickstart.component';
import { BehaviorSubject, Subject, takeUntil } from 'rxjs';
import {
MatDialog,
MatDialogActions,
MatDialogClose,
MatDialogContent,
MatDialogModule,
MatDialogRef,
MatDialogTitle,
} from '@angular/material/dialog';
import { FrameworkChangeDialogComponent } from './framework-change-dialog.component';
@Component({
standalone: true,
selector: 'cnsl-framework-change',
templateUrl: './framework-change.component.html',
styleUrls: ['./framework-change.component.scss'],
imports: [TranslateModule, RouterModule, CommonModule, MatButtonModule, FrameworkAutocompleteComponent],
})
export class FrameworkChangeComponent implements OnInit, OnDestroy {
private destroy$: Subject<void> = new Subject();
public framework: BehaviorSubject<Framework | undefined> = new BehaviorSubject<Framework | undefined>(undefined);
public showFrameworkAutocomplete = signal<boolean>(false);
@Output() public frameworkChanged: EventEmitter<Framework> = new EventEmitter();
public frameworks: Framework[] = frameworkDefinition.map((f) => {
return {
...f,
fragment: '',
imgSrcDark: `assets${f.imgSrcDark}`,
imgSrcLight: `assets${f.imgSrcLight ? f.imgSrcLight : f.imgSrcDark}`,
};
});
constructor(
private activatedRoute: ActivatedRoute,
private dialog: MatDialog,
) {
this.framework.pipe(takeUntil(this.destroy$)).subscribe((value) => {
this.frameworkChanged.emit(value);
});
}
public ngOnInit() {
this.activatedRoute.queryParams.pipe(takeUntil(this.destroy$)).subscribe((params: Params) => {
const { framework } = params;
if (framework) {
this.findFramework(framework);
}
});
}
public ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
public findFramework(id: string) {
const temp = this.frameworks.find((f) => f.id === id);
this.framework.next(temp);
this.frameworkChanged.emit(temp);
}
public openDialog(): void {
const ref = this.dialog.open(FrameworkChangeDialogComponent, {
width: '400px',
data: {
framework: this.framework.value,
frameworks: this.frameworks,
},
});
ref.afterClosed().subscribe((resp) => {
if (resp) {
this.framework.next(resp);
}
});
}
}

View File

@ -0,0 +1,53 @@
<div class="configuration-wrapper">
<div class="row">
<span class="left cnsl-secondary-text">
{{ 'APP.NAME' | translate }}
</span>
<span class="right name">
<span>{{ name }}</span>
<button (click)="changeName.emit()" mat-icon-button><i class="las la-pen"></i></button>
</span>
</div>
<div class="row">
<span class="left cnsl-secondary-text">
{{ 'APP.TYPE' | translate }}
</span>
<span class="right">
{{ 'APP.OIDC.APPTYPE.' + configuration.appType | translate }}
</span>
</div>
<div class="row">
<span class="left cnsl-secondary-text">
{{ 'APP.GRANT' | translate }}
</span>
<span class="right" *ngIf="configuration.grantTypesList && configuration.grantTypesList.length > 0">
[<span *ngFor="let element of configuration.grantTypesList ?? []; index as i">
{{ 'APP.OIDC.GRANT.' + element | translate }}
{{ i < configuration.grantTypesList.length - 1 ? ', ' : '' }} </span
>]
</span>
</div>
<div class="row">
<span class="left cnsl-secondary-text">
{{ 'APP.OIDC.RESPONSETYPE' | translate }}
</span>
<span class="right" *ngIf="configuration.responseTypesList && configuration.responseTypesList.length > 0">
[<span *ngFor="let element of configuration.responseTypesList ?? []; index as i">
{{ 'APP.OIDC.RESPONSE.' + element | translate }}
{{ i < configuration.responseTypesList.length - 1 ? ', ' : '' }} </span
>]
</span>
</div>
<div class="row">
<span class="left cnsl-secondary-text">
{{ 'APP.AUTHMETHOD' | translate }}
</span>
<span class="right">
<span>
{{ 'APP.OIDC.AUTHMETHOD.' + configuration.authMethodType | translate }}
</span>
</span>
</div>
</div>

View File

@ -0,0 +1,23 @@
.configuration-wrapper {
.row {
display: flex;
justify-content: space-between;
align-items: center;
.left,
.right {
margin-bottom: 0.5rem;
font-size: 14px;
}
.name {
display: flex;
align-items: center;
button {
margin-right: -0.5rem;
margin-left: 0.25rem;
}
}
}
}

View File

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

View File

@ -0,0 +1,35 @@
import { CommonModule } from '@angular/common';
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { RouterModule } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core';
import frameworkDefinition from '../../../../../docs/frameworks.json';
import { MatButtonModule } from '@angular/material/button';
import { listFrameworks, hasFramework, getFramework } from '@netlify/framework-info';
import { FrameworkName } from '@netlify/framework-info/lib/generated/frameworkNames';
import { AddOIDCAppRequest } from 'src/app/proto/generated/zitadel/management_pb';
export type FrameworkDefinition = {
id?: FrameworkName | string;
title: string;
imgSrcDark: string;
imgSrcLight?: string;
docsLink: string;
external?: boolean;
};
export type Framework = FrameworkDefinition & {
fragment: string;
};
@Component({
standalone: true,
selector: 'cnsl-oidc-app-configuration',
templateUrl: './oidc-configuration.component.html',
styleUrls: ['./oidc-configuration.component.scss'],
imports: [TranslateModule, RouterModule, CommonModule, MatButtonModule],
})
export class OIDCConfigurationComponent {
@Input() public name?: string;
@Input() public configuration: AddOIDCAppRequest.AsObject = new AddOIDCAppRequest().toObject();
@Output() public changeName: EventEmitter<string> = new EventEmitter();
}

View File

@ -0,0 +1,23 @@
<div class="quickstart-header">
<div class="quickstart-left">
<h2>{{ 'QUICKSTART.TITLE' | translate }}</h2>
<p class="description">{{ 'QUICKSTART.DESCRIPTION' | translate }}</p>
<div class="btn-wrapper">
<a mat-raised-button color="primary" [routerLink]="['/projects', 'app-create']">{{
'QUICKSTART.BTN_START' | translate
}}</a>
<a mat-stroked-button color="primary" href="https://zitadel.com/docs/sdk-examples/introduction" target="_blank">{{
'QUICKSTART.BTN_LEARNMORE' | translate
}}</a>
</div>
</div>
<div class="quickstart-card-wrapper">
<ng-container *ngFor="let framework of frameworks.slice(0, 18)">
<a [routerLink]="['/projects', 'app-create']" [queryParams]="{ framework: framework.id }" class="quickstart-card card">
<img class="dark-only" *ngIf="framework.imgSrcDark" [src]="framework.imgSrcDark" alt="{{ framework.title }}" />
<img class="light-only" *ngIf="framework.imgSrcLight" [src]="framework.imgSrcLight" alt="{{ framework.title }}" />
</a>
</ng-container>
</div>
</div>

View File

@ -0,0 +1,121 @@
@mixin quickstart-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);
.quickstart-header {
display: flex;
flex-direction: row;
margin: 0 -2rem;
padding: 2rem;
margin-bottom: 2rem;
gap: 5rem;
justify-content: space-between;
background-color: map-get($background, metadata-section);
.quickstart-left {
display: flex;
flex-direction: column;
max-width: 400px;
.btn-wrapper {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 1rem;
margin: 1rem 0;
}
}
.quickstart-card-wrapper {
display: grid;
grid-template-columns: repeat(6, 1fr);
grid-column-gap: 1rem;
grid-row-gap: 1rem;
grid-auto-columns: 0;
overflow-x: hidden;
box-sizing: border-box;
max-width: 600px;
margin-left: auto;
.quickstart-card {
position: relative;
flex-shrink: 0;
text-decoration: none;
cursor: pointer;
border-radius: 0.5rem;
box-sizing: border-box;
transition: all 0.1s ease-in;
display: flex;
flex-direction: column;
height: 60px;
width: 60px;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 0 3px #0000001a;
border: 1px solid rgba(#8795a1, 0.2);
color: var(--success);
opacity: 0.8;
&:hover {
border: 2px solid var(--success);
opacity: 1;
}
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;
}
}
&:hover {
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.12);
}
}
}
}
}

View File

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

View File

@ -0,0 +1,42 @@
import { CommonModule } from '@angular/common';
import { Component } from '@angular/core';
import { RouterModule } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core';
import frameworkDefinition from '../../../../../docs/frameworks.json';
import { MatButtonModule } from '@angular/material/button';
import { listFrameworks, hasFramework, getFramework } from '@netlify/framework-info';
import { FrameworkName } from '@netlify/framework-info/lib/generated/frameworkNames';
import { OIDC_CONFIGURATIONS } from 'src/app/utils/framework';
export type FrameworkDefinition = {
id?: FrameworkName | string;
title: string;
description?: string;
imgSrcDark: string;
imgSrcLight?: string;
docsLink: string;
external?: boolean;
};
export type Framework = FrameworkDefinition & {
fragment: string;
};
@Component({
standalone: true,
selector: 'cnsl-quickstart',
templateUrl: './quickstart.component.html',
styleUrls: ['./quickstart.component.scss'],
imports: [TranslateModule, RouterModule, CommonModule, MatButtonModule],
})
export class QuickstartComponent {
public frameworks: FrameworkDefinition[] = frameworkDefinition
.filter((f) => f.id && OIDC_CONFIGURATIONS[f.id])
.map((f) => {
return {
...f,
imgSrcDark: `assets${f.imgSrcDark}`,
imgSrcLight: `assets${f.imgSrcLight ? f.imgSrcLight : f.imgSrcDark}`,
};
});
}

View File

@ -1,10 +1,17 @@
<div <div
class="info-section-row" class="info-section-row"
[ngClass]="{ info: type === 'INFO', warn: type === 'WARN', alert: type === 'ALERT', fit: fitWidth }" [ngClass]="{
info: type === 'INFO',
warn: type === 'WARN',
alert: type === 'ALERT',
success: type === 'SUCCESS',
fit: fitWidth
}"
> >
<i *ngIf="type === 'INFO'" class="icon las la-info"></i> <i *ngIf="type === 'INFO'" class="icon las la-info"></i>
<i *ngIf="type === 'WARN'" class="icon las la-exclamation"></i> <i *ngIf="type === 'WARN'" class="icon las la-exclamation"></i>
<i *ngIf="type === 'ALERT'" class="icon las la-exclamation"></i> <i *ngIf="type === 'ALERT'" class="icon las la-exclamation"></i>
<i *ngIf="type === 'SUCCESS'" class="icon las la-check-circle"></i>
<div class="info-section-content"> <div class="info-section-content">
<ng-content></ng-content> <ng-content></ng-content>

View File

@ -37,13 +37,19 @@ export class SearchProjectAutocompleteComponent implements OnInit, OnDestroy {
@Output() public selectionChanged: EventEmitter<{ @Output() public selectionChanged: EventEmitter<{
project: Project.AsObject | GrantedProject.AsObject; project: Project.AsObject | GrantedProject.AsObject;
type: ProjectType; type: ProjectType;
name: string;
}> = new EventEmitter(); }> = new EventEmitter();
@Output() public valueChanged: EventEmitter<string> = new EventEmitter();
private unsubscribed$: Subject<void> = new Subject(); private unsubscribed$: Subject<void> = new Subject();
constructor(private mgmtService: ManagementService) { constructor(private mgmtService: ManagementService) {
this.myControl.valueChanges this.myControl.valueChanges
.pipe( .pipe(
takeUntil(this.unsubscribed$), takeUntil(this.unsubscribed$),
tap((value) => {
const name = typeof value === 'string' ? value : value.name ? value.name : '';
this.valueChanged.emit(name);
}),
debounceTime(200), debounceTime(200),
tap(() => (this.isLoading = true)), tap(() => (this.isLoading = true)),
switchMap((value) => { switchMap((value) => {
@ -124,44 +130,6 @@ export class SearchProjectAutocompleteComponent implements OnInit, OnDestroy {
return project && project.projectName ? `${project.projectName}` : project && project.name ? `${project.name}` : ''; return project && project.projectName ? `${project.projectName}` : project && project.name ? `${project.name}` : '';
} }
public add(event: MatChipInputEvent): void {
if (!this.matAutocomplete.isOpen) {
const input = event.chipInput?.inputElement;
const value = event.value;
if ((value || '').trim()) {
const index = this.filteredProjects.findIndex((project) => {
if (project?.projectName) {
return project.projectName === value;
} else if (project?.name) {
return project.name === value;
} else {
return false;
}
});
if (index > -1) {
if (this.projects && this.projects.length > 0) {
this.projects.push(this.filteredProjects[index]);
} else {
this.projects = [this.filteredProjects[index]];
}
}
}
if (input) {
input.value = '';
}
}
}
public remove(project: GrantedProject.AsObject): void {
const index = this.projects.indexOf(project);
if (index >= 0) {
this.projects.splice(index, 1);
}
}
public selected(event: MatAutocompleteSelectedEvent): void { public selected(event: MatAutocompleteSelectedEvent): void {
const p: Project.AsObject | GrantedProject.AsObject = event.option.value; const p: Project.AsObject | GrantedProject.AsObject = event.option.value;
const type = (p as Project.AsObject).id const type = (p as Project.AsObject).id
@ -170,8 +138,17 @@ export class SearchProjectAutocompleteComponent implements OnInit, OnDestroy {
? ProjectType.PROJECTTYPE_GRANTED ? ProjectType.PROJECTTYPE_GRANTED
: ProjectType.PROJECTTYPE_OWNED; : ProjectType.PROJECTTYPE_OWNED;
const name = (p as Project.AsObject).name
? (p as Project.AsObject).name
: (p as GrantedProject.AsObject).projectName
? (p as GrantedProject.AsObject).projectName
: '';
console.log(name);
this.selectionChanged.emit({ this.selectionChanged.emit({
project: p, project: p,
name,
type: type, type: type,
}); });
} }

View File

@ -1,25 +1,53 @@
<cnsl-create-layout title="{{ 'APP.PAGES.CREATE' | translate }}" (closed)="close()"> <cnsl-create-layout title="{{ 'APP.PAGES.CREATE' | translate }}" (closed)="close()">
<div class="app-create-main-content"> <div class="app-create-main-content">
<h1>{{ 'APP.PAGES.CREATE_SELECT_PROJECT' | translate }}</h1> <cnsl-framework-change *ngIf="initialParam()" (frameworkChanged)="framework.set($event)"></cnsl-framework-change>
<div class="content-wrapper" [ngClass]="{ reverse: initialParam() }">
<div>
<h1>{{ 'APP.PAGES.CREATE_SELECT_PROJECT' | translate }}</h1>
<cnsl-search-project-autocomplete <cnsl-search-project-autocomplete
class="block" class="block"
[autocompleteType]="ProjectAutocompleteType.PROJECT_OWNED" [autocompleteType]="ProjectAutocompleteType.PROJECT_OWNED"
(selectionChanged)="selectProject($any($event.project))" (selectionChanged)="selectProject($any($event))"
(valueChanged)="projectName = $any($event)"
>
</cnsl-search-project-autocomplete>
<p>{{ 'APP.PAGES.CREATE_NEW_PROJECT' | translate }}</p>
</div>
<div *ngIf="!initialParam()">
<h1>{{ 'QUICKSTART.SELECT_FRAMEWORK' | translate }}</h1>
<cnsl-framework-autocomplete
*ngIf="frameworks"
[frameworkId]="framework()?.id"
[frameworks]="frameworks"
[withCustom]="true"
(selectionChanged)="findFramework($event)"
></cnsl-framework-autocomplete>
</div>
</div>
<cnsl-info-section *ngIf="error()" [type]="InfoSectionType.WARN"
><span class="error-msg">{{ error() }}</span></cnsl-info-section
> >
</cnsl-search-project-autocomplete>
<div [innerHtml]="'APP.PAGES.CREATE_NEW_PROJECT' | translate: { url: '/projects/create' }"></div>
<div class="app-create-btn-container"> <div class="app-create-btn-container">
<button <button
color="primary" color="primary"
mat-raised-button mat-raised-button
class="continue-button" class="continue-button"
[disabled]="!projectId" [disabled]="(!project && !projectName) || !(framework() || customFramework())"
(click)="goToAppCreatePage()" (click)="project && project.name === projectName ? goToAppIntegratePage() : createProjectAndContinue()"
> >
{{ 'ACTIONS.CONTINUE' | translate }} {{
!project && !projectName
? ('ACTIONS.CONTINUE' | translate)
: project && project.name === projectName
? ('ACTIONS.CONTINUEWITH' | translate: { value: project.name })
: ('QUICKSTART.CREATEPROJECTFORAPP' | translate: { value: projectName })
}}
</button> </button>
</div> </div>
</div> </div>

View File

@ -5,6 +5,20 @@ h1 {
.app-create-main-content { .app-create-main-content {
max-width: 35rem; max-width: 35rem;
.content-wrapper {
display: flex;
flex-direction: column;
&.reverse {
flex-direction: column-reverse;
}
}
.error-msg {
margin-top: 0.25rem;
display: block;
}
.app-create-btn-container { .app-create-btn-container {
display: flex; display: flex;
align-items: center; align-items: center;
@ -17,9 +31,4 @@ h1 {
padding: 0 4rem; padding: 0 4rem;
} }
} }
.complexity-view {
width: 100%;
margin: 0 0.5rem;
}
} }

View File

@ -1,19 +1,19 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { OrgCreateComponent } from './org-create.component'; import { AppCreateComponent } from './app-create.component';
describe('OrgCreateComponent', () => { describe('AppCreateComponent', () => {
let component: OrgCreateComponent; let component: AppCreateComponent;
let fixture: ComponentFixture<OrgCreateComponent>; let fixture: ComponentFixture<AppCreateComponent>;
beforeEach(waitForAsync(() => { beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [OrgCreateComponent], declarations: [AppCreateComponent],
}).compileComponents(); }).compileComponents();
})); }));
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(OrgCreateComponent); fixture = TestBed.createComponent(AppCreateComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });

View File

@ -1,40 +1,144 @@
import { Component } from '@angular/core'; import { Component, OnDestroy, signal } from '@angular/core';
import { Router } from '@angular/router'; import { ActivatedRoute, Navigation, Params, Router } from '@angular/router';
import { Subject, takeUntil } from 'rxjs';
import { InfoSectionType } from 'src/app/modules/info-section/info-section.component';
import { ProjectType } from 'src/app/modules/project-members/project-members-datasource';
import { ProjectAutocompleteType } from 'src/app/modules/search-project-autocomplete/search-project-autocomplete.component'; import { ProjectAutocompleteType } from 'src/app/modules/search-project-autocomplete/search-project-autocomplete.component';
import { Project } from 'src/app/proto/generated/zitadel/project_pb'; import { AddProjectRequest, AddProjectResponse } from 'src/app/proto/generated/zitadel/management_pb';
import { GrantedProject, Project } from 'src/app/proto/generated/zitadel/project_pb';
import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service'; import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service';
import { ManagementService } from 'src/app/services/mgmt.service';
import { Framework } from 'src/app/components/quickstart/quickstart.component';
import frameworkDefinition from '../../../../../docs/frameworks.json';
import { NavigationService } from 'src/app/services/navigation.service';
import { Location } from '@angular/common';
@Component({ @Component({
selector: 'cnsl-app-create', selector: 'cnsl-app-create',
templateUrl: './app-create.component.html', templateUrl: './app-create.component.html',
styleUrls: ['./app-create.component.scss'], styleUrls: ['./app-create.component.scss'],
}) })
export class AppCreateComponent { export class AppCreateComponent implements OnDestroy {
public projectId: string = ''; public InfoSectionType: any = InfoSectionType;
public project?: {
project: Project.AsObject | GrantedProject.AsObject;
type: ProjectType;
name: string;
} = undefined;
public ProjectAutocompleteType: any = ProjectAutocompleteType; public ProjectAutocompleteType: any = ProjectAutocompleteType;
public projectName: string = '';
public error = signal('');
public framework = signal<Framework | undefined>(undefined);
public customFramework = signal<boolean>(false);
public initialParam = signal<string>('');
public destroy$: Subject<void> = new Subject();
public frameworks: Framework[] = frameworkDefinition.map((f) => {
return {
...f,
fragment: '',
imgSrcDark: `assets${f.imgSrcDark}`,
imgSrcLight: `assets${f.imgSrcLight ? f.imgSrcLight : f.imgSrcDark}`,
};
});
constructor( constructor(
private router: Router, private router: Router,
private mgmtService: ManagementService,
breadcrumbService: BreadcrumbService, breadcrumbService: BreadcrumbService,
activatedRoute: ActivatedRoute,
private _location: Location,
private navigation: NavigationService,
) { ) {
const bread: Breadcrumb = { const bread: Breadcrumb = {
type: BreadcrumbType.ORG, type: BreadcrumbType.ORG,
routerLink: ['/org'], routerLink: ['/org'],
}; };
breadcrumbService.setBreadcrumb([bread]); breadcrumbService.setBreadcrumb([bread]);
activatedRoute.queryParams.pipe(takeUntil(this.destroy$)).subscribe((params: Params) => {
const { framework } = params;
if (framework) {
this.initialParam.set(framework);
}
});
} }
public goToAppCreatePage(): void { public findFramework(id: string) {
this.router.navigate(['/projects', this.projectId, 'apps', 'create']); if (id !== 'other') {
this.customFramework.set(false);
const temp = this.frameworks.find((f) => f.id === id);
this.framework.set(temp);
} else {
this.customFramework.set(true);
}
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
public goToAppIntegratePage(): void {
if (this.project && this.customFramework()) {
const id = (this.project.project as Project.AsObject).id
? (this.project.project as Project.AsObject).id
: (this.project.project as GrantedProject.AsObject).projectId
? (this.project.project as GrantedProject.AsObject).projectId
: '';
this.router.navigate(['/projects', id, 'apps', 'create']);
} else if (this.project && this.framework()) {
const id = (this.project.project as Project.AsObject).id
? (this.project.project as Project.AsObject).id
: (this.project.project as GrantedProject.AsObject).projectId
? (this.project.project as GrantedProject.AsObject).projectId
: '';
this.router.navigate(['/projects', id, 'apps', 'integrate'], { queryParams: { framework: this.framework()?.id } });
}
} }
public close(): void { public close(): void {
window.history.back(); if (this.navigation.isBackPossible) {
} this._location.back();
} else {
public selectProject(project: Project.AsObject): void { if (this.project && this.framework()) {
if (project.id) { const id = (this.project.project as Project.AsObject).id
this.projectId = project.id; ? (this.project.project as Project.AsObject).id
: (this.project.project as GrantedProject.AsObject).projectId
? (this.project.project as GrantedProject.AsObject).projectId
: '';
this.router.navigate(['/projects', id]);
} else {
this.router.navigate(['/projects']);
}
} }
} }
public selectProject(project: {
project: Project.AsObject | GrantedProject.AsObject;
type: ProjectType;
name: string;
}): void {
if (project) {
this.project = project;
}
}
public createProjectAndContinue() {
const project = new AddProjectRequest();
project.setName(this.projectName);
return this.mgmtService
.addProject(project.toObject())
.then((resp: AddProjectResponse.AsObject) => {
this.error.set('');
this.router.navigate(['/projects', resp.id, 'apps', 'integrate'], {
queryParams: { framework: this.framework()?.id },
});
})
.catch((error) => {
const { message } = error;
this.error.set(message);
});
}
} }

View File

@ -17,11 +17,15 @@ import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.mod
import { SearchProjectAutocompleteModule } from 'src/app/modules/search-project-autocomplete/search-project-autocomplete.module'; import { SearchProjectAutocompleteModule } from 'src/app/modules/search-project-autocomplete/search-project-autocomplete.module';
import { AppCreateRoutingModule } from './app-create-routing.module'; import { AppCreateRoutingModule } from './app-create-routing.module';
import { AppCreateComponent } from './app-create.component'; import { AppCreateComponent } from './app-create.component';
import { FrameworkAutocompleteComponent } from 'src/app/components/framework-autocomplete/framework-autocomplete.component';
import { FrameworkChangeComponent } from 'src/app/components/framework-change/framework-change.component';
@NgModule({ @NgModule({
declarations: [AppCreateComponent], declarations: [AppCreateComponent],
imports: [ imports: [
FrameworkChangeComponent,
AppCreateRoutingModule, AppCreateRoutingModule,
FrameworkAutocompleteComponent,
CommonModule, CommonModule,
FormsModule, FormsModule,
ReactiveFormsModule, ReactiveFormsModule,
@ -35,9 +39,6 @@ import { AppCreateComponent } from './app-create.component';
HasRolePipeModule, HasRolePipeModule,
TranslateModule, TranslateModule,
HasRoleModule, HasRoleModule,
MatCheckboxModule,
PasswordComplexityViewModule,
MatSlideToggleModule,
], ],
}) })
export default class AppCreateModule {} export default class AppCreateModule {}

View File

@ -2,6 +2,8 @@
<h1 class="home-title" data-e2e="authenticated-welcome">{{ 'HOME.WELCOME' | translate }}</h1> <h1 class="home-title" data-e2e="authenticated-welcome">{{ 'HOME.WELCOME' | translate }}</h1>
<div class="home-wrapper enlarged-container"> <div class="home-wrapper enlarged-container">
<cnsl-quickstart></cnsl-quickstart>
<ng-container *ngIf="['iam.read$'] | hasRole | async; else defaultHome"> <ng-container *ngIf="['iam.read$'] | hasRole | async; else defaultHome">
<cnsl-onboarding></cnsl-onboarding> <cnsl-onboarding></cnsl-onboarding>
</ng-container> </ng-container>

View File

@ -19,6 +19,7 @@
.home-title { .home-title {
font-size: 2rem; font-size: 2rem;
margin-bottom: 1rem; margin-bottom: 1rem;
margin-top: 3rem;
} }
.home-wrapper { .home-wrapper {

View File

@ -13,10 +13,12 @@ import OnboardingModule from 'src/app/modules/onboarding/onboarding.module';
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module'; import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
import { HomeRoutingModule } from './home-routing.module'; import { HomeRoutingModule } from './home-routing.module';
import { HomeComponent } from './home.component'; import { HomeComponent } from './home.component';
import { QuickstartComponent } from 'src/app/components/quickstart/quickstart.component';
@NgModule({ @NgModule({
declarations: [HomeComponent], declarations: [HomeComponent],
imports: [ imports: [
QuickstartComponent,
CommonModule, CommonModule,
MatIconModule, MatIconModule,
HasRoleModule, HasRoleModule,

View File

@ -57,6 +57,13 @@
</cnsl-top-view> </cnsl-top-view>
<div class="max-width-container"> <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 <div
class="compliance" class="compliance"
*ngIf="app && app.oidcConfig && app.oidcConfig.complianceProblemsList && app.oidcConfig.complianceProblemsList?.length" *ngIf="app && app.oidcConfig && app.oidcConfig.complianceProblemsList && app.oidcConfig.complianceProblemsList?.length"

View File

@ -125,6 +125,16 @@
} }
} }
.jumptoproject-row {
display: flex;
justify-content: space-between;
.jumptoproject {
margin-top: 0.25rem;
display: block;
}
}
.compliance { .compliance {
padding-bottom: 1rem; padding-bottom: 1rem;
padding-top: 0.5rem; padding-top: 0.5rem;

View File

@ -1,6 +1,6 @@
import { COMMA, ENTER, SPACE } from '@angular/cdk/keycodes'; import { COMMA, ENTER, SPACE } from '@angular/cdk/keycodes';
import { Location } from '@angular/common'; 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 { AbstractControl, FormControl, UntypedFormBuilder, UntypedFormControl, UntypedFormGroup } from '@angular/forms';
import { MatCheckboxChange } from '@angular/material/checkbox'; import { MatCheckboxChange } from '@angular/material/checkbox';
import { MatDialog } from '@angular/material/dialog'; 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 settingsList: SidenavSetting[] = [{ id: 'configuration', i18nKey: 'APP.CONFIGURATION' }];
public currentSetting: string | undefined = this.settingsList[0].id; public currentSetting: string | undefined = this.settingsList[0].id;
public isNew = signal<boolean>(false);
constructor( constructor(
private envSvc: EnvironmentService, private envSvc: EnvironmentService,
public translate: TranslateService, public translate: TranslateService,
@ -245,6 +246,9 @@ export class AppDetailComponent implements OnInit, OnDestroy {
public ngOnInit(): void { public ngOnInit(): void {
const projectId = this.route.snapshot.paramMap.get('projectid'); const projectId = this.route.snapshot.paramMap.get('projectid');
const appId = this.route.snapshot.paramMap.get('appid'); const appId = this.route.snapshot.paramMap.get('appid');
const isNew = this.route.snapshot.queryParamMap.get('new');
this.isNew.set(isNew === 'true');
if (projectId && appId) { if (projectId && appId) {
this.projectId = projectId; this.projectId = projectId;

View File

@ -3,6 +3,7 @@ import { RouterModule, Routes } from '@angular/router';
import { AppCreateComponent } from '../apps/app-create/app-create.component'; import { AppCreateComponent } from '../apps/app-create/app-create.component';
import { AppDetailComponent } from '../apps/app-detail/app-detail.component'; import { AppDetailComponent } from '../apps/app-detail/app-detail.component';
import { IntegrateAppComponent } from './integrate/integrate.component';
const routes: Routes = [ const routes: Routes = [
{ {
@ -10,6 +11,11 @@ const routes: Routes = [
component: AppCreateComponent, component: AppCreateComponent,
data: { animation: 'AddPage' }, data: { animation: 'AddPage' },
}, },
{
path: 'integrate',
component: IntegrateAppComponent,
data: { animation: 'AddPage' },
},
{ {
path: ':appid', path: ':appid',
component: AppDetailComponent, component: AppDetailComponent,

View File

@ -43,6 +43,9 @@ import { AuthMethodDialogComponent } from './app-detail/auth-method-dialog/auth-
import { AppSecretDialogComponent } from './app-secret-dialog/app-secret-dialog.component'; import { AppSecretDialogComponent } from './app-secret-dialog/app-secret-dialog.component';
import { AppsRoutingModule } from './apps-routing.module'; import { AppsRoutingModule } from './apps-routing.module';
import { RedirectUrisComponent } from './redirect-uris/redirect-uris.component'; 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({ @NgModule({
declarations: [ declarations: [
@ -50,12 +53,15 @@ import { RedirectUrisComponent } from './redirect-uris/redirect-uris.component';
AppDetailComponent, AppDetailComponent,
AppSecretDialogComponent, AppSecretDialogComponent,
RedirectUrisComponent, RedirectUrisComponent,
IntegrateAppComponent,
AdditionalOriginsComponent, AdditionalOriginsComponent,
AuthMethodDialogComponent, AuthMethodDialogComponent,
], ],
imports: [ imports: [
FrameworkChangeComponent,
CommonModule, CommonModule,
A11yModule, A11yModule,
OIDCConfigurationComponent,
RedirectPipeModule, RedirectPipeModule,
NameDialogModule, NameDialogModule,
AppRadioModule, AppRadioModule,

View File

@ -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>

View File

@ -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;
}
}

View File

@ -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();
});
});

View File

@ -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);
}
}

View File

@ -1,7 +1,7 @@
import { DatePipe } from '@angular/common'; import { DatePipe } from '@angular/common';
import { Pipe, PipeTransform } from '@angular/core'; import { Pipe, PipeTransform } from '@angular/core';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import * as moment from 'moment'; import moment from 'moment';
import { supportedLanguages } from 'src/app/utils/language'; import { supportedLanguages } from 'src/app/utils/language';
@Pipe({ @Pipe({

View File

@ -0,0 +1,70 @@
import { Framework } from '@netlify/framework-info/lib/types';
import { AddOIDCAppRequest } from '../proto/generated/zitadel/management_pb';
import { FrameworkName } from '@netlify/framework-info/lib/generated/frameworkNames';
import { OIDCAppType, OIDCAuthMethodType, OIDCGrantType, OIDCResponseType } from '../proto/generated/zitadel/app_pb';
type OidcAppConfigurations = {
[framework: string]: AddOIDCAppRequest;
};
export const OIDC_CONFIGURATIONS: OidcAppConfigurations = {
// user agent applications
['angular']: new AddOIDCAppRequest()
.setAppType(OIDCAppType.OIDC_APP_TYPE_USER_AGENT)
.setAuthMethodType(OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE)
.setResponseTypesList([OIDCResponseType.OIDC_RESPONSE_TYPE_CODE])
.setGrantTypesList([OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE])
.setRedirectUrisList(['http://localhost:4200/callback'])
.setPostLogoutRedirectUrisList(['http://localhost:4200']),
['react']: new AddOIDCAppRequest()
.setAppType(OIDCAppType.OIDC_APP_TYPE_USER_AGENT)
.setAuthMethodType(OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE)
.setResponseTypesList([OIDCResponseType.OIDC_RESPONSE_TYPE_CODE])
.setGrantTypesList([OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE])
.setRedirectUrisList(['http://localhost:3000/callback'])
.setPostLogoutRedirectUrisList(['http://localhost:3000']),
['vue']: new AddOIDCAppRequest()
.setAppType(OIDCAppType.OIDC_APP_TYPE_USER_AGENT)
.setAuthMethodType(OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE)
.setResponseTypesList([OIDCResponseType.OIDC_RESPONSE_TYPE_CODE])
.setGrantTypesList([OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE])
.setRedirectUrisList(['http://localhost:5173/auth/signinwin/zitadel'])
.setPostLogoutRedirectUrisList(['http://localhost:5173']),
// web applications
['next']: new AddOIDCAppRequest()
.setAppType(OIDCAppType.OIDC_APP_TYPE_WEB)
.setAuthMethodType(OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_BASIC)
.setResponseTypesList([OIDCResponseType.OIDC_RESPONSE_TYPE_CODE])
.setGrantTypesList([OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE])
.setRedirectUrisList(['http://localhost:3000/callback'])
.setPostLogoutRedirectUrisList(['http://localhost:3000']),
['java']: new AddOIDCAppRequest()
.setAppType(OIDCAppType.OIDC_APP_TYPE_WEB)
.setAuthMethodType(OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE)
.setResponseTypesList([OIDCResponseType.OIDC_RESPONSE_TYPE_CODE])
.setGrantTypesList([OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE])
.setRedirectUrisList(['http://localhost:18080/webapp/login/oauth2/code/zitadel'])
.setPostLogoutRedirectUrisList(['http://localhost:18080/webapp']),
['symfony']: new AddOIDCAppRequest()
.setAppType(OIDCAppType.OIDC_APP_TYPE_WEB)
.setAuthMethodType(OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_BASIC)
.setResponseTypesList([OIDCResponseType.OIDC_RESPONSE_TYPE_CODE])
.setGrantTypesList([OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE])
.setRedirectUrisList(['http://localhost:8000/login_check'])
.setPostLogoutRedirectUrisList(['http://localhost:8000/logout']),
['django']: new AddOIDCAppRequest()
.setAppType(OIDCAppType.OIDC_APP_TYPE_WEB)
.setAuthMethodType(OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE)
.setResponseTypesList([OIDCResponseType.OIDC_RESPONSE_TYPE_CODE])
.setGrantTypesList([OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE])
.setRedirectUrisList(['http://localhost:8000/oidc/callback/'])
.setPostLogoutRedirectUrisList(['http://localhost:8000/oidc/logout/ ']),
// native
['flutter']: new AddOIDCAppRequest()
.setAppType(OIDCAppType.OIDC_APP_TYPE_NATIVE)
.setAuthMethodType(OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE)
.setResponseTypesList([OIDCResponseType.OIDC_RESPONSE_TYPE_CODE])
.setGrantTypesList([OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE])
.setRedirectUrisList(['http://localhost:4444/auth.html', 'com.example.zitadelflutter'])
.setPostLogoutRedirectUrisList(['http://localhost:4444', 'com.example.zitadelflutter']),
};

View File

@ -119,6 +119,30 @@
"SETTINGS": "Настройки", "SETTINGS": "Настройки",
"CUSTOMERPORTAL": "Портал за клиенти" "CUSTOMERPORTAL": "Портал за клиенти"
}, },
"QUICKSTART": {
"TITLE": "Интегрирайте ZITADEL във вашето приложение",
"DESCRIPTION": "Интегрирайте ZITADEL във вашето приложение или използвайте някой от нашите образци, за да започнете за минути.",
"BTN_START": "Създаване на приложение",
"BTN_LEARNMORE": "Научете повече",
"CREATEPROJECTFORAPP": "Създаване на проект {{value}}",
"SELECT_FRAMEWORK": "Изберете рамка",
"FRAMEWORK": "Рамка",
"FRAMEWORK_OTHER": "други (OIDC, SAML, API)",
"ALMOSTDONE": "Почти сте готови.",
"REVIEWCONFIGURATION": "Конфигурация на прегледа",
"REVIEWCONFIGURATION_DESCRIPTION": "Създадохме основна конфигурация за {{value}} приложения. След създаването можете да адаптирате тази конфигурация към вашите нужди.",
"REDIRECTS": "Конфигуриране на пренасочвания",
"DEVMODEWARN": "Режимът Dev е активиран по подразбиране. Можете да актуализирате стойностите за производство по-късно.",
"GUIDE": "Guide",
"BROWSEEXAMPLES": "Преглед на примери",
"DUPLICATEAPPRENAME": "Вече съществува приложение със същото име. Моля, изберете друго име.",
"DIALOG": {
"CHANGE": {
"TITLE": "Рамка за промяна",
"DESCRIPTION": "Изберете една от наличните рамки за бърза настройка на вашето приложение."
}
}
},
"ACTIONS": { "ACTIONS": {
"ACTIONS": "Действия", "ACTIONS": "Действия",
"FILTER": "Филтър", "FILTER": "Филтър",
@ -138,6 +162,7 @@
"ADD": "Добавете", "ADD": "Добавете",
"CREATE": "Създавайте", "CREATE": "Създавайте",
"CONTINUE": "продължи", "CONTINUE": "продължи",
"CONTINUEWITH": "Продължете с {{value}}",
"BACK": "обратно", "BACK": "обратно",
"CLOSE": "Близо", "CLOSE": "Близо",
"CLEAR": "ясно", "CLEAR": "ясно",
@ -1952,6 +1977,7 @@
"DATECHANGED": "Променен", "DATECHANGED": "Променен",
"URLS": "URL адреси", "URLS": "URL адреси",
"DELETE": "Изтриване на приложение", "DELETE": "Изтриване на приложение",
"JUMPTOPROJECT": "За да конфигурирате роли, оторизации и други, отидете до проекта.",
"DETAIL": { "DETAIL": {
"TITLE": "детайл", "TITLE": "детайл",
"STATE": { "STATE": {

View File

@ -119,6 +119,30 @@
"SETTINGS": "Nastavení", "SETTINGS": "Nastavení",
"CUSTOMERPORTAL": "Zákaznický portál" "CUSTOMERPORTAL": "Zákaznický portál"
}, },
"QUICKSTART": {
"TITLE": "Integrovat ZITADEL do vaší aplikace",
"DESCRIPTION": "Integrujte ZITADEL do své aplikace nebo použijte některou z našich ukázek a začněte během několika minut.",
"BTN_START": "Vytvořit aplikaci",
"BTN_LEARNMORE": "Zjistěte více",
"CREATEPROJECTFORAPP": "Vytvořit projekt {{value}}",
"SELECT_FRAMEWORK": "Vybrat framework",
"FRAMEWORK": "Framework",
"FRAMEWORK_OTHER": "jiný (OIDC, SAML, API)",
"ALMOSTDONE": "Jste téměř hotovi.",
"REVIEWCONFIGURATION": "Přezkoumání konfigurace",
"REVIEWCONFIGURATION_DESCRIPTION": "Vytvořili jsme základní konfiguraci pro {{hodnota}} aplikace. Tuto konfiguraci můžete po vytvoření přizpůsobit svým potřebám.",
"REDIRECTS": "Konfigurace přesměrování",
"DEVMODEWARN": "Dev Mode je ve výchozím nastavení povolen. Hodnoty pro produkci můžete aktualizovat později.",
"GUIDE": "Guide",
"BROWSEEXAMPLES": "Procházet ukázky",
"DUPLICATEAPPRENAME": "Aplikace se stejným neme již existuje. Vyberte prosím jiný název.",
"DIALOG": {
"CHANGE": {
"TITLE": "Rámec změn",
"DESCRIPTION": "Vyberte si jeden z dostupných frameworků pro rychlé nastavení vaší aplikace."
}
}
},
"ACTIONS": { "ACTIONS": {
"ACTIONS": "Akce", "ACTIONS": "Akce",
"FILTER": "Filtr", "FILTER": "Filtr",
@ -138,6 +162,7 @@
"ADD": "Přidat", "ADD": "Přidat",
"CREATE": "Vytvořit", "CREATE": "Vytvořit",
"CONTINUE": "Pokračovat", "CONTINUE": "Pokračovat",
"CONTINUEWITH": "Pokračovat s {{value}}",
"BACK": "Zpět", "BACK": "Zpět",
"CLOSE": "Zavřít", "CLOSE": "Zavřít",
"CLEAR": "Vyčistit", "CLEAR": "Vyčistit",
@ -1971,6 +1996,7 @@
"DATECHANGED": "Změněno", "DATECHANGED": "Změněno",
"URLS": "URL adresy", "URLS": "URL adresy",
"DELETE": "Smazat aplikaci", "DELETE": "Smazat aplikaci",
"JUMPTOPROJECT": "Chcete-li nakonfigurovat role, oprávnění a další, přejděte do projektu.",
"DETAIL": { "DETAIL": {
"TITLE": "Detail", "TITLE": "Detail",
"STATE": { "STATE": {

View File

@ -119,6 +119,30 @@
"SETTINGS": "Einstellungen", "SETTINGS": "Einstellungen",
"CUSTOMERPORTAL": "Kundenportal" "CUSTOMERPORTAL": "Kundenportal"
}, },
"QUICKSTART": {
"TITEL": "Integriere ZITADEL in deine Anwendung",
"DESCRIPTION": "Integriere ZITADEL in deine Anwendung oder verwende eines unserer Beispiele, um in wenigen Minuten loszulegen.",
"BTN_START": "Anwendung erstellen",
"BTN_LEARNMORE": "Mehr erfahren",
"CREATEPROJECTFORAPP": "Projekt erstellen {{value}}",
"SELECT_FRAMEWORK": "Framework auswählen",
"FRAMEWORK": "Framework",
"FRAMEWORK_OTHER": "Andere (OIDC, SAML, API)",
"ALMOSTDONE": "Wir sind fast fertig.",
"REVIEWCONFIGURATION": "Konfiguration überprüfen",
"REVIEWCONFIGURATION_DESCRIPTION": "Wir haben eine Grundkonfiguration für {{wert}}-Anwendungen erstellt. Du kannst diese Konfiguration nach der Erstellung an deine Bedürfnisse anpassen.",
"REDIRECTS": "Redirects konfigurieren",
"DEVMODEWARN": "Der Dev-Modus ist standardmäßig aktiviert. Sie können Werte für die Produktion später aktualisieren.",
"GUIDE": "Guide",
"BROWSEEXAMPLES": "Beispiele durchsuchen",
"DUPLICATEAPPRENAME": "Es gibt bereits eine App mit demselben Neme. Bitte wählen Sie einen anderen Namen.",
"DIALOG": {
"CHANGE": {
"TITLE": "Framework ändern",
"DESCRIPTION": "Wähle eines der verfügbaren Frameworks für die schnelle Einrichtung deiner Anwendung."
}
}
},
"ACTIONS": { "ACTIONS": {
"ACTIONS": "Aktionen", "ACTIONS": "Aktionen",
"FILTER": "Filter", "FILTER": "Filter",
@ -138,6 +162,7 @@
"ADD": "Hinzufügen", "ADD": "Hinzufügen",
"CREATE": "Erstellen", "CREATE": "Erstellen",
"CONTINUE": "Weiter", "CONTINUE": "Weiter",
"CONTINUEWITH": "Mit {{value}} fortfahren",
"BACK": "Zurück", "BACK": "Zurück",
"CLOSE": "Schliessen", "CLOSE": "Schliessen",
"CLEAR": "Zurücksetzen", "CLEAR": "Zurücksetzen",
@ -1961,6 +1986,7 @@
"DATECHANGED": "Geändert", "DATECHANGED": "Geändert",
"URLS": "URLs", "URLS": "URLs",
"DELETE": "App löschen", "DELETE": "App löschen",
"JUMPTOPROJECT": "Um Rollen, Berechtigungen und mehr zu konfigurieren, navigieren Sie zum Projekt.",
"DETAIL": { "DETAIL": {
"TITLE": "Detail", "TITLE": "Detail",
"STATE": { "STATE": {

View File

@ -43,7 +43,7 @@
} }
}, },
"ONBOARDING": { "ONBOARDING": {
"DESCRIPTION": "Your onboarding process", "DESCRIPTION": "Your next steps",
"MOREDESCRIPTION": "more shortcuts", "MOREDESCRIPTION": "more shortcuts",
"COMPLETED": "completed", "COMPLETED": "completed",
"DISMISS": "No thanks, I'm a pro.", "DISMISS": "No thanks, I'm a pro.",
@ -119,6 +119,30 @@
"SETTINGS": "Settings", "SETTINGS": "Settings",
"CUSTOMERPORTAL": "Customer Portal" "CUSTOMERPORTAL": "Customer Portal"
}, },
"QUICKSTART": {
"TITLE": "Integrate ZITADEL into your application",
"DESCRIPTION": "Integrate ZITADEL into your application or use one of our samples to get started in minutes.",
"BTN_START": "Create Application",
"BTN_LEARNMORE": "Learn More",
"CREATEPROJECTFORAPP": "Create Project {{value}}",
"SELECT_FRAMEWORK": "Select Framework",
"FRAMEWORK": "Framework",
"FRAMEWORK_OTHER": "Other (OIDC, SAML, API)",
"ALMOSTDONE": "You're almost done.",
"REVIEWCONFIGURATION": "Review Configuration",
"REVIEWCONFIGURATION_DESCRIPTION": "We've created a basic configuration for {{value}} applications. You can adapt this configuration to your needs after creation.",
"REDIRECTS": "Configure redirects",
"DEVMODEWARN": "Dev Mode is enabled by default. You can update values for production later.",
"GUIDE": "Guide",
"BROWSEEXAMPLES": "Browse Examples and SDKs",
"DUPLICATEAPPRENAME": "An app with the same name exists already. Please choose a different name.",
"DIALOG": {
"CHANGE": {
"TITLE": "Change Framework",
"DESCRIPTION": "Choose one of the available frameworks for quick setup of your application."
}
}
},
"ACTIONS": { "ACTIONS": {
"ACTIONS": "Actions", "ACTIONS": "Actions",
"FILTER": "Filter", "FILTER": "Filter",
@ -138,6 +162,7 @@
"ADD": "Add", "ADD": "Add",
"CREATE": "Create", "CREATE": "Create",
"CONTINUE": "Continue", "CONTINUE": "Continue",
"CONTINUEWITH": "Continue with {{value}}",
"BACK": "Back", "BACK": "Back",
"CLOSE": "Close", "CLOSE": "Close",
"CLEAR": "Clear", "CLEAR": "Clear",
@ -1972,7 +1997,7 @@
"DESCRIPTION": "Here you can edit your application data and it's configuration.", "DESCRIPTION": "Here you can edit your application data and it's configuration.",
"CREATE": "Create application", "CREATE": "Create application",
"CREATE_SELECT_PROJECT": "Select your project first", "CREATE_SELECT_PROJECT": "Select your project first",
"CREATE_NEW_PROJECT": "or create a new one <a href='{{url}}' title='Create project'>here</a>.", "CREATE_NEW_PROJECT": "or enter the name for your new project",
"CREATE_DESC_TITLE": "Enter Your Application Details Step by Step", "CREATE_DESC_TITLE": "Enter Your Application Details Step by Step",
"CREATE_DESC_SUB": "A recommended configuration will be automatically generated.", "CREATE_DESC_SUB": "A recommended configuration will be automatically generated.",
"STATE": "Status", "STATE": "Status",
@ -1980,6 +2005,7 @@
"DATECHANGED": "Changed", "DATECHANGED": "Changed",
"URLS": "URLs", "URLS": "URLs",
"DELETE": "Delete App", "DELETE": "Delete App",
"JUMPTOPROJECT": "To configure roles, authorizations and more, navigate to the project.",
"DETAIL": { "DETAIL": {
"TITLE": "Detail", "TITLE": "Detail",
"STATE": { "STATE": {

View File

@ -119,6 +119,30 @@
"SETTINGS": "Ajustes", "SETTINGS": "Ajustes",
"CUSTOMERPORTAL": "Portal del cliente" "CUSTOMERPORTAL": "Portal del cliente"
}, },
"QUICKSTART": {
"TITLE": "Integra ZITADEL en tu aplicación",
"DESCRIPTION": "Integra ZITADEL en tu aplicación o utiliza uno de nuestros ejemplos para empezar en cuestión de minutos",
"BTN_START": "Crear aplicación",
"BTN_LEARNMORE": "Aprende más",
"CREATEPROJECTFORAPP": "Crear proyecto {{value}}",
"SELECT_FRAMEWORK": "Seleccionar marco",
"FRAMEWORK": "Framework",
"FRAMEWORK_OTHER": "Otro (OIDC, SAML, API)",
"ALMOSTDONE": "Ya casi has terminado",
"REVIEWCONFIGURATION": "Configuración de revisión",
"REVIEWCONFIGURATION_DESCRIPTION": "Hemos creado una configuración básica para aplicaciones {{value}}. Puedes adaptar esta configuración a tus necesidades después de crearla.",
"REDIRECTS": "Configurar redirecciones",
"DEVMODEWARN": "El modo de desarrollo está habilitado de forma predeterminada. Puede actualizar los valores para la producción más adelante.",
"GUIDE": "Guía",
"BROWSEEXAMPLES": "Explora ejemplos",
"DUPLICATEAPPRENAME": "Ya existe una aplicación con el mismo nombre. Por favor, elige un nombre diferente.",
"DIALOG": {
"CAMBIAR": {
"TITLE": "Cambio framework",
"DESCRIPTION": "Elige uno de los frameworks disponibles para configurar rápidamente tu aplicación."
}
}
},
"ACTIONS": { "ACTIONS": {
"ACTIONS": "Acciones", "ACTIONS": "Acciones",
"FILTER": "Filtrar", "FILTER": "Filtrar",
@ -138,6 +162,7 @@
"ADD": "Añadir", "ADD": "Añadir",
"CREATE": "Crear", "CREATE": "Crear",
"CONTINUE": "Continuar", "CONTINUE": "Continuar",
"CONTINUEWITH": "Continuar con {{value}}",
"BACK": "Atrás", "BACK": "Atrás",
"CLOSE": "Cerrar", "CLOSE": "Cerrar",
"CLEAR": "Limpiar", "CLEAR": "Limpiar",
@ -1959,6 +1984,7 @@
"DATECHANGED": "Cambiada", "DATECHANGED": "Cambiada",
"URLS": "URLs", "URLS": "URLs",
"DELETE": "Borrar App", "DELETE": "Borrar App",
"JUMPTOPROJECT": "Para configurar roles, autorizaciones y más, navegue hasta el proyecto.",
"DETAIL": { "DETAIL": {
"TITLE": "Detalle", "TITLE": "Detalle",
"STATE": { "STATE": {

View File

@ -119,6 +119,30 @@
"SETTINGS": "Paramètres", "SETTINGS": "Paramètres",
"CUSTOMERPORTAL": "Customer Portal" "CUSTOMERPORTAL": "Customer Portal"
}, },
"QUICKSTART": {
"TITLE": "Intégrer ZITADEL dans votre application",
"DESCRIPTION": "Intégrez ZITADEL dans votre application ou utilisez l'un de nos échantillons pour démarrer en quelques minutes",
"BTN_START": "Créer une application",
"BTN_LEARNMORE": "En savoir plus",
"CREATEPROJECTFORAPP": "Créer un projet {{value}}",
"SELECT_FRAMEWORK": "Select Framework",
"FRAMEWORK": "Framework",
"FRAMEWORK_OTHER": "Autre (OIDC, SAML, API)",
"ALMOSTDONE": "Vous avez presque terminé",
"REVIEWCONFIGURATION": "Review Configuration",
"REVIEWCONFIGURATION_DESCRIPTION": "Nous avons créé une configuration de base pour les applications {{value}}. Vous pouvez adapter cette configuration à vos besoins après sa création.",
"REDIRECTS": "Configurer les redirections",
"DEVMODEWARN": "Le mode développement est activé par défaut. Vous pouvez mettre à jour les valeurs pour la production ultérieurement.",
"GUIDE": "Guide",
"BROWSEEXAMPLES": "Parcourez les exemples",
"DUPLICATEAPPRENAME": "Une application avec ce nom existe déjà. Veuillez choisir un autre nom.",
"DIALOG": {
"CHANGE": {
"TITLE": "Cadre de changement",
"DESCRIPTION": "Choisissez l'un des frameworks disponibles pour une configuration rapide de votre application."
}
}
},
"ACTIONS": { "ACTIONS": {
"ACTIONS": "Actions", "ACTIONS": "Actions",
"FILTER": "Filtrer", "FILTER": "Filtrer",
@ -138,6 +162,7 @@
"ADD": "Ajouter", "ADD": "Ajouter",
"CREATE": "Créer", "CREATE": "Créer",
"CONTINUE": "Continuer", "CONTINUE": "Continuer",
"CONTINUEWITH": "Continuez avec {{value}}",
"BACK": "Retour", "BACK": "Retour",
"CLOSE": "Fermer", "CLOSE": "Fermer",
"CLEAR": "Effacer", "CLEAR": "Effacer",
@ -1962,6 +1987,7 @@
"DATECHANGED": "Modifié", "DATECHANGED": "Modifié",
"URLS": "URLs", "URLS": "URLs",
"DELETE": "Supprimer l'application", "DELETE": "Supprimer l'application",
"JUMPTOPROJECT": "Pour configurer des rôles, des autorisations et bien plus encore, accédez au projet.",
"DETAIL": { "DETAIL": {
"TITLE": "Détail", "TITLE": "Détail",
"STATE": { "STATE": {

View File

@ -119,6 +119,30 @@
"SETTINGS": "Impostazioni", "SETTINGS": "Impostazioni",
"CUSTOMERPORTAL": "Customer Portal" "CUSTOMERPORTAL": "Customer Portal"
}, },
"QUICKSTART": {
"TITLE": "Integra ZITADEL nella tua applicazione",
"DESCRIPTION": "Integra ZITADEL nella tua applicazione o utilizza uno dei nostri esempi per iniziare in pochi minuti",
"BTN_START": "Crea applicazione",
"BTN_LEARNMORE": "Mostra di più",
"CREATEPROJECTFORAPP": "Crea progetto {{value}}",
"SELECT_FRAMEWORK": "Seleziona Framework",
"FRAMEWORK": "Framework",
"FRAMEWORK_OTHER": "Altro (OIDC, SAML, API)",
"ALMOSTDONE": "Hai quasi finito",
"REVIEWCONFIGURATION": "Controlla la configurazione",
"REVIEWCONFIGURATION_DESCRIPTION": "Abbiamo creato una configurazione base per le applicazioni {{value}}. Può adattare la configurazione alle proprie esigenze dopo la creazione.",
"REDIRECTS": "Configura i reindirizzamenti",
"DEVMODEWARN": "La modalità sviluppatore è abilitata per impostazione predefinita. È possibile aggiornare i valori per la produzione in un secondo momento.",
"GUIDE": "Guida",
"BROWSEEXAMPLES": "Esplora esempi e SDK",
"DUPLICATEAPPRENAME": "Un'app con lo stesso nome esiste già. Scegli un nome diverso.",
"DIALOG": {
"CHANGE": {
"TITLE": "Modifica framework",
"DESCRIPTION": "Scegliere uno dei framework disponibili per configurare rapidamente la propria applicazione"
}
}
},
"ACTIONS": { "ACTIONS": {
"ACTIONS": "Azioni", "ACTIONS": "Azioni",
"FILTER": "Filtra", "FILTER": "Filtra",
@ -138,6 +162,7 @@
"ADD": "Aggiungi", "ADD": "Aggiungi",
"CREATE": "Crea", "CREATE": "Crea",
"CONTINUE": "Continua", "CONTINUE": "Continua",
"CONTINUEWITH": "Continue con {{value}}",
"BACK": "Indietro", "BACK": "Indietro",
"CLOSE": "chiudi", "CLOSE": "chiudi",
"CLEAR": "Resetta", "CLEAR": "Resetta",
@ -1962,6 +1987,7 @@
"DATECHANGED": "Cambiato", "DATECHANGED": "Cambiato",
"URLS": "URLs", "URLS": "URLs",
"DELETE": "Rimuovi App", "DELETE": "Rimuovi App",
"JUMPTOPROJECT": "Per configurare ruoli, autorizzazioni e altro, vai al progetto.",
"DETAIL": { "DETAIL": {
"TITLE": "Dettagli", "TITLE": "Dettagli",
"STATE": { "STATE": {

View File

@ -119,6 +119,30 @@
"SETTINGS": "設定", "SETTINGS": "設定",
"CUSTOMERPORTAL": "カスタマーポータル" "CUSTOMERPORTAL": "カスタマーポータル"
}, },
"QUICKSTART": {
"TITLE": "ZITADELをアプリケーションに統合する",
"DESCRIPTION": "あなたのアプリケーションにZITADELを組み込むか、当社のサンプルを使って数分で始められます。",
"BTN_START": "アプリケーションの作成",
"BTN_LEARNMORE": "さらに詳しく",
"CREATEPROJECTFORAPP": "プロジェクトの作成 {{value}}",
"SELECT_FRAMEWORK": "フレームワークを選択",
"FRAMEWORK": "フレームワーク",
"FRAMEWORK_OTHER": "他の (OIDC, SAML, API)",
"ALMOSTDONE": "あと少しだ。",
"REVIEWCONFIGURATION": "レビュー構成",
"REVIEWCONFIGURATION_DESCRIPTION": "ここでは{{value}}アプリケーションの基本設定を作成しました。この構成は作成後にあなたのニーズに合わせることができます。",
"REDIRECTS": "リダイレクトの設定",
"DEVMODEWARN": "開発モードはデフォルトで有効になっています。実稼働用の値は後で更新できます。",
"GUIDE": "ガイド",
"BROWSEEXAMPLES": "サンプルを見る",
"DUPLICATEAPPRENAME": "同じ名前のアプリがすでに存在します。別の名前を選択してください。",
"DIALOG": {
"CHANGE": {
"TITLE": "変更の枠組み",
"DESCRIPTION": "利用可能なフレームワークのいずれかを選択して、アプリケーションをすばやくセットアップできます。"
}
}
},
"ACTIONS": { "ACTIONS": {
"ACTIONS": "アクション", "ACTIONS": "アクション",
"FILTER": "絞り込み", "FILTER": "絞り込み",
@ -138,6 +162,7 @@
"ADD": "追加", "ADD": "追加",
"CREATE": "作成", "CREATE": "作成",
"CONTINUE": "次へ", "CONTINUE": "次へ",
"CONTINUEWITH": "{{value}} に進みます",
"BACK": "戻る", "BACK": "戻る",
"CLOSE": "閉じる", "CLOSE": "閉じる",
"CLEAR": "消去する", "CLEAR": "消去する",
@ -1953,6 +1978,7 @@
"DATECHANGED": "更新日", "DATECHANGED": "更新日",
"URLS": "URL", "URLS": "URL",
"DELETE": "アプリを削除する", "DELETE": "アプリを削除する",
"JUMPTOPROJECT": "ロール、権限などを構成するには、プロジェクトに移動します。",
"DETAIL": { "DETAIL": {
"TITLE": "詳細", "TITLE": "詳細",
"STATE": { "STATE": {

View File

@ -119,6 +119,30 @@
"SETTINGS": "Подесувања", "SETTINGS": "Подесувања",
"CUSTOMERPORTAL": "Портал за клиенти" "CUSTOMERPORTAL": "Портал за клиенти"
}, },
"QUICKSTART": {
"TITLE": "Интегрирајте го ZITADEL во вашата апликација",
"DESCRIPTION": "Интегрирајте го ZITADEL во вашата апликација или користете еден од нашите примероци за да започнете за неколку минути.",
"BTN_START": "Креирај апликација",
"BTN_LEARNMORE": "Научи повеќе",
"CREATEPROJECTFORAPP": "Креирај проект {{value}}",
"SELECT_FRAMEWORK": "Изберете Рамка",
"FRAMEWORK": "Рамка",
"FRAMEWORK_OTHER": "Друго (OIDC, SAML, API)",
"ALMOSTDONE": "Речиси сте готови.",
"REVIEWCONFIGURATION": "Прегледајте ја конфигурацијата",
"REVIEWCONFIGURATION_DESCRIPTION": "Создадовме основна конфигурација за {{value}} апликации. Можете да ја прилагодите оваа конфигурација на вашите потреби по креирањето.",
"REDIRECTS": "Конфигурирајте пренасочувања",
"DEVMODEWARN": "Режимот на развој е стандардно овозможен. Може да ги ажурирате вредностите за производство подоцна.",
"GUIDE": "Водич",
"BROWSEEXAMPLES": "Прегледај примероци",
"DUPLICATEAPPRENAME": "Веќе постои апликација со истиот непријател. Ве молиме изберете друго име.",
"DIALOG": {
"CHANGE": {
"TITLE": "Променете ја рамката",
"DESCRIPTION": "Изберете една од достапните рамки за брзо поставување на вашата апликација."
}
}
},
"ACTIONS": { "ACTIONS": {
"ACTIONS": "Акции", "ACTIONS": "Акции",
"FILTER": "Филтер", "FILTER": "Филтер",
@ -138,6 +162,7 @@
"ADD": "Додади", "ADD": "Додади",
"CREATE": "Креирај", "CREATE": "Креирај",
"CONTINUE": "Продолжи", "CONTINUE": "Продолжи",
"CONTINUEWITH": "Продолжи со {{value}}",
"BACK": "Назад", "BACK": "Назад",
"CLOSE": "Затвори", "CLOSE": "Затвори",
"CLEAR": "Исчисти", "CLEAR": "Исчисти",
@ -1959,6 +1984,7 @@
"DATECHANGED": "Изменето", "DATECHANGED": "Изменето",
"URLS": "URLs", "URLS": "URLs",
"DELETE": "Избриши апликација", "DELETE": "Избриши апликација",
"JUMPTOPROJECT": "За да ги конфигурирате улогите, овластувањата и друго, одете до проектот.",
"DETAIL": { "DETAIL": {
"TITLE": "Детали", "TITLE": "Детали",
"STATE": { "STATE": {

View File

@ -119,6 +119,30 @@
"SETTINGS": "Instellingen", "SETTINGS": "Instellingen",
"CUSTOMERPORTAL": "Klantenportaal" "CUSTOMERPORTAL": "Klantenportaal"
}, },
"QUICKSTART": {
"TITLE": "Integreer ZITADEL in uw toepassing",
"DESCRIPTION": "Integreer ZITADEL in uw toepassing of gebruik een van onze voorbeelden om binnen enkele minuten aan de slag te gaan.",
"BTN_START": "Applicatie maken",
"BTN_LEARNMORE": "Meer informatie",
"CREATEPROJECTFORAPP": "Creëer Project {{value}}",
"SELECT_FRAMEWORK": "Kies Framework",
"FRAMEWORK": "Framework",
"FRAMEWORK_OTHER": "Ander (OIDC, SAML, API)",
"ALMOSTDONE": "Je bent bijna klaar",
"REVIEWCONFIGURATION": "Reviewconfiguratie",
"REVIEWCONFIGURATION_DESCRIPTION": "We hebben een basisconfiguratie gemaakt voor {{value}} toepassingen. Je kunt deze configuratie na het aanmaken aanpassen aan je behoeften.",
"REDIRECTS": "Configureer omleidingen",
"DEVMODEWARN": "Dev-modus is standaard ingeschakeld. U kunt waarden voor productie later bijwerken.",
"GUIDE": "Guide",
"BROWSEEXAMPLES": "Bekijk voorbeelden",
"DUPLICATEAPPRENAME": "Er bestaat al een app met dezelfde aartsvijand. Kies een andere naam.",
"DIALOG": {
"CHANGE": {
"TITLE": "Verander Framework",
"DESCRIPTION": "Kies een van de beschikbare frameworks voor het snel instellen van je applicatie."
}
}
},
"ACTIONS": { "ACTIONS": {
"ACTIONS": "Acties", "ACTIONS": "Acties",
"FILTER": "Filter", "FILTER": "Filter",
@ -138,6 +162,7 @@
"ADD": "Toevoegen", "ADD": "Toevoegen",
"CREATE": "Aanmaken", "CREATE": "Aanmaken",
"CONTINUE": "Doorgaan", "CONTINUE": "Doorgaan",
"CONTINUEWITH": "Ga verder met {{value}}",
"BACK": "Terug", "BACK": "Terug",
"CLOSE": "Sluiten", "CLOSE": "Sluiten",
"CLEAR": "Leegmaken", "CLEAR": "Leegmaken",
@ -1980,6 +2005,7 @@
"DATECHANGED": "Gewijzigd", "DATECHANGED": "Gewijzigd",
"URLS": "URL's", "URLS": "URL's",
"DELETE": "Verwijder App", "DELETE": "Verwijder App",
"JUMPTOPROJECT": "Om rollen, autorisaties en meer te configureren, navigeert u naar het project.",
"DETAIL": { "DETAIL": {
"TITLE": "Detail", "TITLE": "Detail",
"STATE": { "STATE": {

View File

@ -119,6 +119,30 @@
"SETTINGS": "Ustawienia", "SETTINGS": "Ustawienia",
"CUSTOMERPORTAL": "Portal klienta" "CUSTOMERPORTAL": "Portal klienta"
}, },
"QUICKSTART": {
"TITLE": "Zintegruj ZITADEL ze swoją aplikacją",
"DESCRIPTION": "Zintegruj ZITADEL ze swoją aplikacją lub skorzystaj z jednego z naszych przykładów, aby rozpocząć pracę w ciągu kilku minut",
"BTN_START": "Utwórz aplikację",
"BTN_LEARNMORE": "Dowiedz się więcej",
"CREATEPROJECTFORAPP": "Utwórz projekt {{value}}",
"SELECT_FRAMEWORK": "Wybierz Framework",
"FRAMEWORK": "Ramy",
"FRAMEWORK_OTHER": "Inny (OIDC, SAML, API)",
"ALMOSTDONE": "Już prawie skończyłeś",
"REVIEWCONFIGURATION": "Konfiguracja recenzji",
"REVIEWCONFIGURATION_DESCRIPTION": "Stworzyliśmy podstawową konfigurację dla aplikacji {{value}}. Możesz dostosować tę konfigurację do swoich potrzeb po jej utworzeniu.",
"REDIRECTS": "Skonfiguruj przekierowania",
"DEVMODEWARN": "Tryb deweloperski jest domyślnie włączony. Możesz później zaktualizować wartości dla produkcji.",
"GUIDE": "Przewodnik",
"BROWSEEXAMPLES": "Przeglądaj przykłady",
"DUPLICATEAPPRENAME": "Aplikacja o tej samej nazwie już istnieje. Proszę wybrać inną nazwę.",
"DIALOG": {
"CHANGE": {
"TITLE": "Ramy zmian",
"DESCRIPTION": "Wybierz jeden z dostępnych frameworków, aby szybko skonfigurować swoją aplikację"
}
}
},
"ACTIONS": { "ACTIONS": {
"ACTIONS": "Akcje", "ACTIONS": "Akcje",
"FILTER": "Filtruj", "FILTER": "Filtruj",
@ -138,6 +162,7 @@
"ADD": "Dodaj", "ADD": "Dodaj",
"CREATE": "Utwórz", "CREATE": "Utwórz",
"CONTINUE": "Kontynuuj", "CONTINUE": "Kontynuuj",
"CONTINUEWITH": "Kontynuuj z {{value}}",
"BACK": "Wstecz", "BACK": "Wstecz",
"CLOSE": "Zamknij", "CLOSE": "Zamknij",
"CLEAR": "Wyczyść", "CLEAR": "Wyczyść",
@ -1962,6 +1987,7 @@
"DATECHANGED": "Zmienione", "DATECHANGED": "Zmienione",
"URLS": "Adresy URL", "URLS": "Adresy URL",
"DELETE": "Usuń aplikację", "DELETE": "Usuń aplikację",
"JUMPTOPROJECT": "Aby skonfigurować role, uprawnienia i nie tylko, przejdź do projektu.",
"DETAIL": { "DETAIL": {
"TITLE": "Szczegóły", "TITLE": "Szczegóły",
"STATE": { "STATE": {

View File

@ -119,6 +119,30 @@
"SETTINGS": "Configurações", "SETTINGS": "Configurações",
"CUSTOMERPORTAL": "Portal do Cliente" "CUSTOMERPORTAL": "Portal do Cliente"
}, },
"QUICKSTART": {
"TITLE": "Integrar a ZITADEL na sua aplicação",
"DESCRIÇÃO": "Integre a ZITADEL na sua aplicação ou utilize uma das nossas amostras para começar em minutos.",
"BTN_START": "Criar aplicação",
"BTN_LEARNMORE": "Saiba mais",
"CREATEPROJECTFORAPP": "Criar projeto {{value}}",
"SELECT_FRAMEWORK": "Selecionar estrutura",
"FRAMEWORK": "Estrutura",
"FRAMEWORK_OTHER": "Outro (OIDC, SAML, API)",
"ALMOSTDONE": "Está quase a terminar",
"REVIEWCONFIGURATION": "Configuração de revisão",
"REVIEWCONFIGURATION_DESCRIPTION": "Criámos uma configuração básica para aplicações {{value}}. Pode adaptar esta configuração às suas necessidades após a criação.",
"REDIRECTS": "Configurar redireccionamentos",
"DEVMODEWARN": "O modo Dev está habilitado por padrão. Você pode atualizar os valores para produção posteriormente.",
"GUIDE": "Guia",
"BROWSEEXAMPLES": "Navegar por exemplos",
"DUPLICATEAPPRENAME": "Já existe um aplicativo com o mesmo neme. Escolha um nome diferente.",
"DIALOG": {
"CHANGE": {
"TITLE": "Alterar Quadro",
"DESCRIÇÃO": "Escolha uma das estruturas disponíveis para uma configuração rápida da sua aplicação."
}
}
},
"ACTIONS": { "ACTIONS": {
"ACTIONS": "Ações", "ACTIONS": "Ações",
"FILTER": "Filtrar", "FILTER": "Filtrar",
@ -138,6 +162,7 @@
"ADD": "Adicionar", "ADD": "Adicionar",
"CREATE": "Criar", "CREATE": "Criar",
"CONTINUE": "Continuar", "CONTINUE": "Continuar",
"CONTINUEWITH": "Continue com {{value}}",
"BACK": "Voltar", "BACK": "Voltar",
"CLOSE": "Fechar", "CLOSE": "Fechar",
"CLEAR": "Limpar", "CLEAR": "Limpar",
@ -1957,6 +1982,7 @@
"DATECHANGED": "Alterado", "DATECHANGED": "Alterado",
"URLS": "URLs", "URLS": "URLs",
"DELETE": "Excluir App", "DELETE": "Excluir App",
"JUMPTOPROJECT": "Para configurar funções, autorizações e muito mais, navegue até o projeto.",
"DETAIL": { "DETAIL": {
"TITLE": "Detalhe", "TITLE": "Detalhe",
"STATE": { "STATE": {

View File

@ -115,6 +115,30 @@
"SETTINGS": "Настройки", "SETTINGS": "Настройки",
"CUSTOMERPORTAL": "Клиентский портал" "CUSTOMERPORTAL": "Клиентский портал"
}, },
"QUICKSTART": {
"TITLE": "Интегрируйте ZITADEL в свое приложение",
"ОПИСАНИЕ": "Интегрируйте ZITADEL в свое приложение или воспользуйтесь одним из наших образцов, чтобы начать работу за считанные минуты",
"BTN_START": "Создать приложение",
"BTN_LEARNMORE": "Узнать больше",
"CREATEPROJECTFORAPP": "Создать проект {{value}}",
"SELECT_FRAMEWORK": "Выбрать фреймворк",
"FRAMEWORK": "Рамка",
"FRAMEWORK_OTHER": "Другое (OIDC, SAML, API)",
"ALMOSTDONE": "Вы почти закончили",
"REVIEWCONFIGURATION": "Конфигурация обзора",
"REVIEWCONFIGURATION_DESCRIPTION": "Мы создали базовую конфигурацию для {{value}} приложений. После создания вы можете адаптировать эту конфигурацию под свои нужды.",
"REDIRECTS": "Настроить редиректы",
"DEVMODEWARN": "Режим разработки включен по умолчанию. Значения для производства можно обновить позже.",
"GUIDE": "Руководство",
"BROWSEEXAMPLES": "Просмотреть примеры",
"DUPLICATEAPPRENAME": "Приложение с таким названием уже существует. Пожалуйста, выберите другое имя.",
"DIALOG": {
"CHANGE": {
"TITLE": "Рамки изменений",
"ОПИСАНИЕ": "Выберите один из доступных фреймворков для быстрой настройки вашего приложения"
}
}
},
"ACTIONS": { "ACTIONS": {
"ACTIONS": "Действия", "ACTIONS": "Действия",
"FILTER": "Фильтр", "FILTER": "Фильтр",
@ -134,6 +158,7 @@
"ADD": "Добавить", "ADD": "Добавить",
"CREATE": "Создать", "CREATE": "Создать",
"CONTINUE": "Продолжить", "CONTINUE": "Продолжить",
"CONTINUEWITH": "Продолжить с {{value}}",
"BACK": "Назад", "BACK": "Назад",
"CLOSE": "Закрыть", "CLOSE": "Закрыть",
"CLEAR": "Очистить", "CLEAR": "Очистить",
@ -1956,6 +1981,7 @@
"DATECHANGED": "Дата изменения", "DATECHANGED": "Дата изменения",
"URLS": "URL-адреса", "URLS": "URL-адреса",
"DELETE": "Удалить приложение", "DELETE": "Удалить приложение",
"JUMPTOPROJECT": "Чтобы настроить роли, полномочия и многое другое, перейдите к проекту.",
"DETAIL": { "DETAIL": {
"TITLE": "Деталь", "TITLE": "Деталь",
"STATE": { "STATE": {

View File

@ -119,6 +119,30 @@
"SETTINGS": "设置", "SETTINGS": "设置",
"CUSTOMERPORTAL": "客户门户网站" "CUSTOMERPORTAL": "客户门户网站"
}, },
"QUICKSTART": {
"TITLE": "将 ZITADEL 集成到您的应用程序中",
"DESCRIPTION": "将 ZITADEL 集成到您的应用程序中,或使用我们的示例,几分钟内即可开始使用。",
"BTN_START": "创建应用程序",
"BTN_LEARNMORE": "了解更多",
"CREATEPROJECTFORAPP": "创建项目 {{value}}",
"SELECT_FRAMEWORK": "选择框架",
"FRAMEWORK": "框架",
"FRAMEWORK_OTHER": "其他 (OIDC, SAML, API)",
"ALMOSTDONE": "就快好了",
"REVIEWCONFIGURATION": "审查配置",
"REVIEWCONFIGURATION_DESCRIPTION": "我们为 {{value}} 应用程序创建了一个基本配置。创建后,您可以根据自己的需要调整该配置。",
"REDIRECTS": "配置重定向",
"DEVMODEWARN": "默认情况下启用开发模式。您可以稍后更新生产值。",
"GUIDE": "指南",
"BROWSEEXAMPLES": "浏览示例",
"DUPLICATEAPPRENAME": "具有相同 neme 的应用程序已经存在。请选择不同的名称。",
"DIALOG": {
"CHANGE": {
"TITLE": "变革框架",
"DESCRIPTION": "从可用的框架中选择一个,快速设置您的应用程序。"
}
}
},
"ACTIONS": { "ACTIONS": {
"ACTIONS": "操作", "ACTIONS": "操作",
"FILTER": "过滤", "FILTER": "过滤",
@ -138,6 +162,7 @@
"ADD": "添加", "ADD": "添加",
"CREATE": "创建", "CREATE": "创建",
"CONTINUE": "继续", "CONTINUE": "继续",
"CONTINUEWITH": "继续 {{value}}",
"BACK": "返回", "BACK": "返回",
"CLOSE": "关闭", "CLOSE": "关闭",
"CLEAR": "清除", "CLEAR": "清除",
@ -1961,6 +1986,7 @@
"DATECHANGED": "修改于", "DATECHANGED": "修改于",
"URLS": "URLs", "URLS": "URLs",
"DELETE": "删除应用", "DELETE": "删除应用",
"JUMPTOPROJECT": "要配置角色、授权等,请导航到项目。",
"DETAIL": { "DETAIL": {
"TITLE": "详情", "TITLE": "详情",
"STATE": { "STATE": {

View File

@ -29,6 +29,7 @@
@import 'src/app/modules/top-view/top-view.component'; @import 'src/app/modules/top-view/top-view.component';
@import 'src/app/pages/projects/projects.component'; @import 'src/app/pages/projects/projects.component';
@import 'src/app/modules/edit-text/edit-text.component.scss'; @import 'src/app/modules/edit-text/edit-text.component.scss';
@import 'src/app/pages/projects/apps/integrate/integrate.component.scss';
@import 'src/app/modules/providers/providers.scss'; @import 'src/app/modules/providers/providers.scss';
@import 'src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component'; @import 'src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component';
@import 'src/app/pages/users/user-detail/user-detail/user-detail.component'; @import 'src/app/pages/users/user-detail/user-detail/user-detail.component';
@ -43,10 +44,14 @@
@import 'src/app/modules/form-field/field/form-field.component.scss'; @import 'src/app/modules/form-field/field/form-field.component.scss';
@import 'src/app/modules/label/label.component.scss'; @import 'src/app/modules/label/label.component.scss';
@import 'src/app/modules/string-list/string-list.component.scss'; @import 'src/app/modules/string-list/string-list.component.scss';
@import 'src/app/components/quickstart/quickstart.component.scss';
@import 'src/app/components/framework-autocomplete/framework-autocomplete.component.scss';
@import 'src/app/components/framework-change/framework-change.component.scss';
@import 'src/app/modules/meta-layout/meta.scss'; @import 'src/app/modules/meta-layout/meta.scss';
@import 'src/app/pages/projects/owned-projects/project-grant-detail/project-grant-illustration/project-grant-illustration.component'; @import 'src/app/pages/projects/owned-projects/project-grant-detail/project-grant-illustration/project-grant-illustration.component';
@import 'src/app/modules/accounts-card/accounts-card.component.scss'; @import 'src/app/modules/accounts-card/accounts-card.component.scss';
@import 'src/app/modules/onboarding-card/onboarding-card.component.scss'; @import 'src/app/modules/onboarding-card/onboarding-card.component.scss';
@import 'src/app/pages/app-create/app-create.component.scss';
@import 'src/app/modules/onboarding/onboarding.component.scss'; @import 'src/app/modules/onboarding/onboarding.component.scss';
@import 'src/app/modules/filter/filter.component.scss'; @import 'src/app/modules/filter/filter.component.scss';
@import 'src/app/modules/policies/message-texts/message-texts.component.scss'; @import 'src/app/modules/policies/message-texts/message-texts.component.scss';
@ -83,7 +88,9 @@
@include info-overlay-theme($theme); @include info-overlay-theme($theme);
@include app-auth-method-radio-theme($theme); @include app-auth-method-radio-theme($theme);
@include security-policy-theme($theme); @include security-policy-theme($theme);
@include framework-change-theme($theme);
@include search-user-autocomplete-theme($theme); @include search-user-autocomplete-theme($theme);
@include quickstart-theme($theme);
@include project-role-chips-theme($theme); @include project-role-chips-theme($theme);
@include card-theme($theme); @include card-theme($theme);
@include idp-settings-theme($theme); @include idp-settings-theme($theme);
@ -124,6 +131,7 @@
@include refresh-table-theme($theme); @include refresh-table-theme($theme);
@include accounts-card-theme($theme); @include accounts-card-theme($theme);
@include sidenav-theme($theme); @include sidenav-theme($theme);
@include framework-autocomplete-theme($theme);
@include info-section-theme($theme); @include info-section-theme($theme);
@include actions-theme($theme); @include actions-theme($theme);
@include filter-theme($theme); @include filter-theme($theme);
@ -131,11 +139,12 @@
@include project-grid-theme($theme); @include project-grid-theme($theme);
@include granted-project-detail-theme($theme); @include granted-project-detail-theme($theme);
@include user-grants-theme($theme); @include user-grants-theme($theme);
@include app-create-theme($theme);
@include info-row-theme($theme); @include info-row-theme($theme);
@include redirect-uris-theme($theme); @include redirect-uris-theme($theme);
@include action-keys-theme($theme); @include action-keys-theme($theme);
@include codemirror-theme($theme); @include codemirror-theme($theme);
@include contact-theme($theme); @include contact-theme($theme);
@include app-create-theme($theme); @include app-integrate-theme($theme);
@include domain-verification-theme($theme); @include domain-verification-theme($theme);
} }

View File

@ -400,6 +400,7 @@ $caos-dark-app-theme: modify-palette($caos-dark-app-theme, foreground, $cnsl-dar
$background: map-get($caos-light-app-theme, background); $background: map-get($caos-light-app-theme, background);
$foreground: map-get($caos-light-app-theme, foreground); $foreground: map-get($caos-light-app-theme, foreground);
$primary: map-get($caos-light-app-theme, primary);
--warn: #cd3d56; --warn: #cd3d56;
--success: #10b981; --success: #10b981;
@ -477,6 +478,7 @@ $caos-dark-app-theme: modify-palette($caos-dark-app-theme, foreground, $cnsl-dar
.dark-theme { .dark-theme {
$background: map-get($caos-dark-app-theme, background); $background: map-get($caos-dark-app-theme, background);
$foreground: map-get($caos-dark-app-theme, foreground); $foreground: map-get($caos-dark-app-theme, foreground);
$primary: map-get($caos-dark-app-theme, primary);
--warn: #ff3b5b; --warn: #ff3b5b;
--success: #10b981; --success: #10b981;

View File

@ -4,11 +4,6 @@
$background: map-get($theme, background); $background: map-get($theme, background);
$foreground: map-get($theme, foreground); $foreground: map-get($theme, foreground);
// .data-e2e-success {
// background-color: map-get($background, cards) !important;
// color: var(--success) !important;
// }
.data-e2e-failure { .data-e2e-failure {
.mdc-snackbar__surface { .mdc-snackbar__surface {
background-color: map-get($background, toast) !important; background-color: map-get($background, toast) !important;

View File

@ -16,6 +16,8 @@
"experimentalDecorators": true, "experimentalDecorators": true,
"moduleResolution": "node", "moduleResolution": "node",
"importHelpers": true, "importHelpers": true,
"resolveJsonModule": true,
"esModuleInterop": true,
"target": "ES2022", "target": "ES2022",
"module": "ES2022", "module": "ES2022",
"useDefineForClassFields": false, "useDefineForClassFields": false,

View File

@ -2910,6 +2910,22 @@
"@material/theme" "15.0.0-canary.bc9ae6c9c.0" "@material/theme" "15.0.0-canary.bc9ae6c9c.0"
tslib "^2.1.0" tslib "^2.1.0"
"@netlify/framework-info@^9.8.10":
version "9.8.10"
resolved "https://registry.yarnpkg.com/@netlify/framework-info/-/framework-info-9.8.10.tgz#a18589f132dafb5cb7f86c05a9895b9118633fe1"
integrity sha512-VT8ejAaB/XU2xRpdpQinHUO1YL3+BMx6LJ49wJk2u9Yq/VI1/gYCi5VqbqTHBQXJUlOi84YuiRlrDBsLpPr8eg==
dependencies:
ajv "^8.12.0"
filter-obj "^5.0.0"
find-up "^6.3.0"
is-plain-obj "^4.0.0"
locate-path "^7.0.0"
p-filter "^3.0.0"
p-locate "^6.0.0"
process "^0.11.10"
read-pkg-up "^9.0.0"
semver "^7.3.8"
"@ngtools/webpack@16.2.2": "@ngtools/webpack@16.2.2":
version "16.2.2" version "16.2.2"
resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-16.2.2.tgz#55fac744d1aca4542fb9a4ff16a48d2b384ffd37" resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-16.2.2.tgz#55fac744d1aca4542fb9a4ff16a48d2b384ffd37"
@ -3344,6 +3360,11 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.7.0.tgz#c03de4572f114a940bc2ca909a33ddb2b925e470" resolved "https://registry.yarnpkg.com/@types/node/-/node-20.7.0.tgz#c03de4572f114a940bc2ca909a33ddb2b925e470"
integrity sha512-zI22/pJW2wUZOVyguFaUL1HABdmSVxpXrzIqkjsHmyUjNhPoWM1CKfvVuXfetHhIok4RY573cqS0mZ1SJEnoTg== integrity sha512-zI22/pJW2wUZOVyguFaUL1HABdmSVxpXrzIqkjsHmyUjNhPoWM1CKfvVuXfetHhIok4RY573cqS0mZ1SJEnoTg==
"@types/normalize-package-data@^2.4.1":
version "2.4.4"
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz#56e2cc26c397c038fab0e3a917a12d5c5909e901"
integrity sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==
"@types/opentype.js@^1.3.8": "@types/opentype.js@^1.3.8":
version "1.3.8" version "1.3.8"
resolved "https://registry.yarnpkg.com/@types/opentype.js/-/opentype.js-1.3.8.tgz#741be92429d1c2d64b5fa79cf692f74b49d6007f" resolved "https://registry.yarnpkg.com/@types/opentype.js/-/opentype.js-1.3.8.tgz#741be92429d1c2d64b5fa79cf692f74b49d6007f"
@ -3845,6 +3866,14 @@ aggregate-error@^3.0.0:
clean-stack "^2.0.0" clean-stack "^2.0.0"
indent-string "^4.0.0" indent-string "^4.0.0"
aggregate-error@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-4.0.1.tgz#25091fe1573b9e0be892aeda15c7c66a545f758e"
integrity sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w==
dependencies:
clean-stack "^4.0.0"
indent-string "^5.0.0"
ajv-formats@2.1.1, ajv-formats@^2.1.1: ajv-formats@2.1.1, ajv-formats@^2.1.1:
version "2.1.1" version "2.1.1"
resolved "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz" resolved "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz"
@ -3864,7 +3893,7 @@ ajv-keywords@^5.1.0:
dependencies: dependencies:
fast-deep-equal "^3.1.3" fast-deep-equal "^3.1.3"
ajv@8.12.0, ajv@^8.0.0, ajv@^8.9.0: ajv@8.12.0, ajv@^8.0.0, ajv@^8.12.0, ajv@^8.9.0:
version "8.12.0" version "8.12.0"
resolved "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz" resolved "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz"
integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==
@ -4490,6 +4519,13 @@ clean-stack@^2.0.0:
resolved "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz" resolved "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz"
integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==
clean-stack@^4.0.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-4.2.0.tgz#c464e4cde4ac789f4e0735c5d75beb49d7b30b31"
integrity sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg==
dependencies:
escape-string-regexp "5.0.0"
cli-cursor@3.1.0, cli-cursor@^3.1.0: cli-cursor@3.1.0, cli-cursor@^3.1.0:
version "3.1.0" version "3.1.0"
resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz" resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz"
@ -5318,6 +5354,11 @@ escape-html@~1.0.3:
resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz" resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz"
integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
escape-string-regexp@5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8"
integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==
escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
version "1.0.5" version "1.0.5"
resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz"
@ -5647,6 +5688,11 @@ fill-range@^7.0.1:
dependencies: dependencies:
to-regex-range "^5.0.1" to-regex-range "^5.0.1"
filter-obj@^5.0.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-5.1.0.tgz#5bd89676000a713d7db2e197f660274428e524ed"
integrity sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng==
finalhandler@1.1.2: finalhandler@1.1.2:
version "1.1.2" version "1.1.2"
resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz" resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz"
@ -5845,6 +5891,11 @@ function-bind@^1.1.1:
resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz"
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
function-bind@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
gauge@^4.0.3: gauge@^4.0.3:
version "4.0.4" version "4.0.4"
resolved "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz" resolved "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz"
@ -6100,6 +6151,13 @@ has@^1.0.3:
dependencies: dependencies:
function-bind "^1.1.1" function-bind "^1.1.1"
hasown@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.1.tgz#26f48f039de2c0f8d3356c223fb8d50253519faa"
integrity sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==
dependencies:
function-bind "^1.1.2"
hdr-histogram-js@^2.0.1: hdr-histogram-js@^2.0.1:
version "2.0.3" version "2.0.3"
resolved "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-2.0.3.tgz" resolved "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-2.0.3.tgz"
@ -6114,6 +6172,13 @@ hdr-histogram-percentiles-obj@^3.0.0:
resolved "https://registry.npmjs.org/hdr-histogram-percentiles-obj/-/hdr-histogram-percentiles-obj-3.0.0.tgz" resolved "https://registry.npmjs.org/hdr-histogram-percentiles-obj/-/hdr-histogram-percentiles-obj-3.0.0.tgz"
integrity sha512-7kIufnBqdsBGcSZLPJwqHT3yhk1QTsSlFsVD3kx5ixH/AlgBs9yM1q6DPhXZ8f8gtdqgh7N7/5btRLpQsS2gHw== integrity sha512-7kIufnBqdsBGcSZLPJwqHT3yhk1QTsSlFsVD3kx5ixH/AlgBs9yM1q6DPhXZ8f8gtdqgh7N7/5btRLpQsS2gHw==
hosted-git-info@^4.0.1:
version "4.1.0"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.1.0.tgz#827b82867e9ff1c8d0c4d9d53880397d2c86d224"
integrity sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==
dependencies:
lru-cache "^6.0.0"
hosted-git-info@^6.0.0: hosted-git-info@^6.0.0:
version "6.1.1" version "6.1.1"
resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.1.tgz" resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.1.tgz"
@ -6345,6 +6410,11 @@ indent-string@^4.0.0:
resolved "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz" resolved "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz"
integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==
indent-string@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-5.0.0.tgz#4fd2980fccaf8622d14c64d694f4cf33c81951a5"
integrity sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==
infer-owner@^1.0.4: infer-owner@^1.0.4:
version "1.0.4" version "1.0.4"
resolved "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz" resolved "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz"
@ -6433,6 +6503,13 @@ is-core-module@^2.11.0, is-core-module@^2.8.1:
dependencies: dependencies:
has "^1.0.3" has "^1.0.3"
is-core-module@^2.5.0:
version "2.13.1"
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384"
integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==
dependencies:
hasown "^2.0.0"
is-docker@^2.0.0, is-docker@^2.1.1: is-docker@^2.0.0, is-docker@^2.1.1:
version "2.2.1" version "2.2.1"
resolved "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz" resolved "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz"
@ -6499,6 +6576,11 @@ is-plain-obj@^3.0.0:
resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz" resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz"
integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==
is-plain-obj@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0"
integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==
is-plain-object@^2.0.4: is-plain-object@^2.0.4:
version "2.0.4" version "2.0.4"
resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz" resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz"
@ -7009,7 +7091,7 @@ locate-path@^6.0.0:
dependencies: dependencies:
p-locate "^5.0.0" p-locate "^5.0.0"
locate-path@^7.1.0: locate-path@^7.0.0, locate-path@^7.1.0:
version "7.2.0" version "7.2.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-7.2.0.tgz#69cb1779bd90b35ab1e771e1f2f89a202c2a8a8a" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-7.2.0.tgz#69cb1779bd90b35ab1e771e1f2f89a202c2a8a8a"
integrity sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA== integrity sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==
@ -7503,6 +7585,16 @@ nopt@^6.0.0:
dependencies: dependencies:
abbrev "^1.0.0" abbrev "^1.0.0"
normalize-package-data@^3.0.2:
version "3.0.3"
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-3.0.3.tgz#dbcc3e2da59509a0983422884cd172eefdfa525e"
integrity sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==
dependencies:
hosted-git-info "^4.0.1"
is-core-module "^2.5.0"
semver "^7.3.4"
validate-npm-package-license "^3.0.1"
normalize-package-data@^5.0.0: normalize-package-data@^5.0.0:
version "5.0.0" version "5.0.0"
resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-5.0.0.tgz" resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-5.0.0.tgz"
@ -7769,6 +7861,13 @@ os-tmpdir@~1.0.1, os-tmpdir@~1.0.2:
resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz"
integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==
p-filter@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/p-filter/-/p-filter-3.0.0.tgz#ce50e03b24b23930e11679ab8694bd09a2d7ed35"
integrity sha512-QtoWLjXAW++uTX67HZQz1dbTpqBfiidsB6VtQUC9iR85S120+s0T5sO6s+B5MLzFcZkrEd/DGMmCjR+f2Qpxwg==
dependencies:
p-map "^5.1.0"
p-limit@^2.2.0: p-limit@^2.2.0:
version "2.3.0" version "2.3.0"
resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz"
@ -7818,6 +7917,13 @@ p-map@^4.0.0:
dependencies: dependencies:
aggregate-error "^3.0.0" aggregate-error "^3.0.0"
p-map@^5.1.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/p-map/-/p-map-5.5.0.tgz#054ca8ca778dfa4cf3f8db6638ccb5b937266715"
integrity sha512-VFqfGDHlx87K66yZrNdI4YGtD70IRyd+zSvgks6mzHPRNkoKy+9EKP4SFC77/vTTQYmRmti7dvqC+m5jBrBAcg==
dependencies:
aggregate-error "^4.0.0"
p-retry@^4.5.0: p-retry@^4.5.0:
version "4.6.2" version "4.6.2"
resolved "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz" resolved "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz"
@ -7867,7 +7973,7 @@ parent-module@^1.0.0:
dependencies: dependencies:
callsites "^3.0.0" callsites "^3.0.0"
parse-json@^5.0.0: parse-json@^5.0.0, parse-json@^5.2.0:
version "5.2.0" version "5.2.0"
resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz" resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz"
integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==
@ -8130,6 +8236,11 @@ process-nextick-args@~2.0.0:
resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz" resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz"
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
process@^0.11.10:
version "0.11.10"
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==
promise-inflight@^1.0.1: promise-inflight@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz" resolved "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz"
@ -8307,6 +8418,25 @@ read-package-json@^6.0.0:
normalize-package-data "^5.0.0" normalize-package-data "^5.0.0"
npm-normalize-package-bin "^3.0.0" npm-normalize-package-bin "^3.0.0"
read-pkg-up@^9.0.0:
version "9.1.0"
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-9.1.0.tgz#38ca48e0bc6c6b260464b14aad9bcd4e5b1fbdc3"
integrity sha512-vaMRR1AC1nrd5CQM0PhlRsO5oc2AAigqr7cCrZ/MW/Rsaflz4RlgzkpL4qoU/z1F6wrbd85iFv1OQj/y5RdGvg==
dependencies:
find-up "^6.3.0"
read-pkg "^7.1.0"
type-fest "^2.5.0"
read-pkg@^7.1.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-7.1.0.tgz#438b4caed1ad656ba359b3e00fd094f3c427a43e"
integrity sha512-5iOehe+WF75IccPc30bWTbpdDQLOCc3Uu8bi3Dte3Eueij81yx1Mrufk8qBx/YAbR4uL1FdUr+7BKXDwEtisXg==
dependencies:
"@types/normalize-package-data" "^2.4.1"
normalize-package-data "^3.0.2"
parse-json "^5.2.0"
type-fest "^2.0.0"
readable-stream@^2.0.1, readable-stream@~2.3.6: readable-stream@^2.0.1, readable-stream@~2.3.6:
version "2.3.8" version "2.3.8"
resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz" resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz"
@ -8676,6 +8806,13 @@ semver@^6.0.0, semver@^6.3.0, semver@^6.3.1:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
semver@^7.3.4:
version "7.6.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d"
integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==
dependencies:
lru-cache "^6.0.0"
send@0.18.0: send@0.18.0:
version "0.18.0" version "0.18.0"
resolved "https://registry.npmjs.org/send/-/send-0.18.0.tgz" resolved "https://registry.npmjs.org/send/-/send-0.18.0.tgz"
@ -9369,6 +9506,11 @@ type-fest@^0.21.3:
resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz" resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz"
integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==
type-fest@^2.0.0, type-fest@^2.5.0:
version "2.19.0"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b"
integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==
type-is@~1.6.18: type-is@~1.6.18:
version "1.6.18" version "1.6.18"
resolved "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz" resolved "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz"
@ -9516,7 +9658,7 @@ v8-compile-cache@2.3.0:
resolved "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz" resolved "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz"
integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==
validate-npm-package-license@^3.0.4: validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.4:
version "3.0.4" version "3.0.4"
resolved "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz" resolved "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz"
integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==

View File

@ -8,93 +8,11 @@ To achieve your goals as fast as possible, we provide you with SDKs, Example Rep
The SDKs and Integration depend on the framework and language you are using. The SDKs and Integration depend on the framework and language you are using.
import { Tile } from "../../src/components/tile"; import { Frameworks } from "../../src/components/frameworks";
### Resources ### Resources
<div className="tile-wrapper"> <Frameworks />
<Tile title="Angular"
imageSource="/docs/img/tech/angular.svg"
link="/docs/sdk-examples/angular"></Tile>
<Tile title="Flutter"
imageSource="/docs/img/tech/flutter.svg"
link="/docs/sdk-examples/flutter"></Tile>
<Tile title="Go"
imageSource="/docs/img/tech/golang.svg"
link="/docs/sdk-examples/go"></Tile>
<Tile
title="Java"
imageSource="/docs/img/tech/java.svg"
link="/docs/sdk-examples/java"></Tile>
<Tile
title="NestJS"
imageSource="/docs/img/tech/nestjs.svg"
link="/docs/sdk-examples/nestjs"></Tile>
<Tile
title="Next.js"
imageSource="/docs/img/tech/nextjs.svg"
imageSourceLight="/docs/img/tech/nextjslight.svg"
link="/docs/sdk-examples/nextjs"></Tile>
<Tile
title="Python Django"
imageSource="/docs/img/tech/django.png"
link="/docs/sdk-examples/python-django"></Tile>
<Tile
title="Python Flask"
imageSource="/docs/img/tech/flask.svg"
imageSourceLight="/docs/img/tech/flasklight.svg"
link="/docs/sdk-examples/python-flask"></Tile>
<Tile
title="React"
imageSource="/docs/img/tech/react.png"
link="/docs/sdk-examples/react"></Tile>
<Tile
title="Symfony"
imageSource="/docs/img/tech/php.svg"
link="/docs/sdk-examples/symfony"></Tile>
<Tile
title="Vue.js"
imageSource="/docs/img/tech/vue.svg"
link="/docs/sdk-examples/vue"></Tile>
<Tile
title="Dart"
imageSource="/docs/img/tech/dart.svg"
link="https://github.com/smartive/zitadel-dart"
external="true"></Tile>
<Tile
title="Elixir"
imageSource="/docs/img/tech/elixir.svg"
link="https://github.com/maennchen/zitadel_api"
external="true"></Tile>
<Tile
title="NextAuth"
imageSource="/docs/img/tech/nextjs.svg"
imageSourceLight="/docs/img/tech/nextjslight.svg"
link="https://next-auth.js.org/providers/zitadel"
external="true"></Tile>
<Tile
title="Node.js"
imageSource="/docs/img/tech/nodejs.svg"
link="https://www.npmjs.com/package/@zitadel/node"
external="true"></Tile>
<Tile
title=".Net"
imageSource="/docs/img/tech/dotnet.svg"
link="https://github.com/smartive/zitadel-net"
external="true"></Tile>
<Tile
title="Passport.js"
imageSource="/docs/img/tech/passportjs.svg"
link="https://github.com/buehler/node-passport-zitadel"
external="true"></Tile>
<Tile
title="Rust"
imageSourceLight="/docs/img/tech/rust.svg"
imageSource="/docs/img/tech/rustlight.svg"
link="https://github.com/smartive/zitadel-rust"
external="true"></Tile>
</div>
### OIDC Libraries ### OIDC Libraries

115
docs/frameworks.json Normal file
View File

@ -0,0 +1,115 @@
[
{
"id": "angular",
"title": "Angular",
"description": "This preset sets up an OIDC configuration with Authentication Code Flow, secured by PKCE",
"imgSrcLight": "/docs/img/tech/angular.svg",
"imgSrcDark": "/docs/img/tech/angular.svg",
"docsLink": "/docs/sdk-examples/angular"
},
{
"id": "flutter",
"title": "Flutter",
"imgSrcDark": "/docs/img/tech/flutter.svg",
"docsLink": "/docs/sdk-examples/flutter"
},
{
"title": "Go",
"imgSrcDark": "/docs/img/tech/golang.svg",
"docsLink": "/docs/sdk-examples/go"
},
{
"id": "java",
"title": "Java",
"imgSrcDark": "/docs/img/tech/java.svg",
"docsLink": "/docs/sdk-examples/java"
},
{
"title": "NestJS",
"imgSrcDark": "/docs/img/tech/nestjs.svg",
"docsLink": "/docs/sdk-examples/nestjs"
},
{
"id": "next",
"title": "Next.js",
"imgSrcDark": "/docs/img/tech/nextjs.svg",
"imgSrcLight": "/docs/img/tech/nextjslight.svg",
"docsLink": "/docs/sdk-examples/nextjs"
},
{
"id": "django",
"title": "Python Django",
"imgSrcDark": "/docs/img/tech/django.png",
"docsLink": "/docs/sdk-examples/python-django"
},
{
"title": "Python Flask",
"imgSrcDark": "/docs/img/tech/flask.svg",
"imgSrcLight": "/docs/img/tech/flasklight.svg",
"docsLink": "/docs/sdk-examples/python-flask"
},
{
"id": "react",
"title": "React",
"description": "This preset sets up an OIDC configuration with Authentication Code Flow, secured by PKCE",
"imgSrcDark": "/docs/img/tech/react.png",
"docsLink": "/docs/sdk-examples/react"
},
{
"id": "symfony",
"title": "Symfony",
"imgSrcDark": "/docs/img/tech/php.svg",
"docsLink": "/docs/sdk-examples/symfony"
},
{
"id": "vue",
"title": "Vue.js",
"description": "This preset sets up an OIDC configuration with Authentication Code Flow, secured by PKCE",
"imgSrcDark": "/docs/img/tech/vue.svg",
"docsLink": "/docs/sdk-examples/vue"
},
{
"title": "Dart",
"imgSrcDark": "/docs/img/tech/dart.svg",
"docsLink": "https://github.com/smartive/zitadel-dart",
"external": true
},
{
"title": "Elixir",
"imgSrcDark": "/docs/img/tech/elixir.svg",
"docsLink": "https://github.com/maennchen/zitadel_api",
"external": true
},
{
"title": "NextAuth",
"imgSrcDark": "/docs/img/tech/nextjs.svg",
"imgSrcLight": "/docs/img/tech/nextjslight.svg",
"docsLink": "https://next-auth.js.org/providers/zitadel",
"external": true
},
{
"title": "Node.js",
"imgSrcDark": "/docs/img/tech/nodejs.svg",
"docsLink": "https://www.npmjs.com/package/@zitadel/node",
"external": true
},
{
"title": ".Net",
"imgSrcDark": "/docs/img/tech/dotnet.svg",
"docsLink": "https://github.com/smartive/zitadel-net",
"external": true
},
{
"title": "Passport.js",
"imgSrcDark": "/docs/img/tech/passportjs.svg",
"docsLink": "https://github.com/buehler/node-passport-zitadel",
"external": true
},
{
"title": "Rust",
"imgSrcLight": "/docs/img/tech/rust.svg",
"imgSrcDark": "/docs/img/tech/rustlight.svg",
"docsLink": "https://github.com/smartive/zitadel-rust",
"external": true
}
]

View File

@ -0,0 +1,21 @@
import React from "react";
import { Tile } from "./tile";
import frameworks from "../../frameworks.json";
export function Frameworks({}) {
return (
<div className="tile-wrapper">
{frameworks.map((framework) => {
return (
<Tile
title={framework.title}
imageSource={framework.imgSrcDark}
imageSourceLight={framework.imgSrcLight}
link={framework.docsLink}
external={framework.external}
></Tile>
);
})}
</div>
);
}