mirror of
https://github.com/zitadel/zitadel.git
synced 2024-12-04 15:35:10 +00:00
feat(console): integrate app (#7417)
* docs, frameworks view * project select, integrate app page * fix search project autocomplete * framework autocomplete * framwork select component, integrate, mapping to oidc config * param * fix route handler * setname projectid context * app-create page without context * show description of app type, info section * redirects section * updatevalue observable * fix redirect uris section * i18n * setup config * backbutton behavior, cleanup * cleanup * lint * allow other framework jump off * dev mode warning * navigate to project * rm import * i18n, guide link * edit name dialog * show warning for duplicate name
This commit is contained in:
parent
f4c72cbe14
commit
0fcdfe460c
@ -23,7 +23,12 @@
|
||||
"polyfills": ["zone.js"],
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"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"],
|
||||
"scripts": ["./node_modules/tinycolor2/dist/tinycolor-min.js"],
|
||||
"stylePreprocessorOptions": {
|
||||
@ -81,6 +86,7 @@
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "console:build:production"
|
||||
|
@ -29,6 +29,7 @@
|
||||
"@fortawesome/fontawesome-svg-core": "^6.4.2",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.4.2",
|
||||
"@grpc/grpc-js": "^1.9.3",
|
||||
"@netlify/framework-info": "^9.8.10",
|
||||
"@ngx-translate/core": "^15.0.0",
|
||||
"angular-oauth2-oidc": "^15.0.1",
|
||||
"angularx-qrcode": "^16.0.0",
|
||||
|
@ -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>
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
});
|
||||
});
|
@ -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);
|
||||
}
|
||||
}
|
@ -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>
|
@ -0,0 +1,10 @@
|
||||
.framework-change-block {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
@ -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>
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
});
|
||||
});
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -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>
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
});
|
||||
});
|
@ -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();
|
||||
}
|
@ -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>
|
121
console/src/app/components/quickstart/quickstart.component.scss
Normal file
121
console/src/app/components/quickstart/quickstart.component.scss
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
});
|
||||
});
|
@ -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}`,
|
||||
};
|
||||
});
|
||||
}
|
@ -1,10 +1,17 @@
|
||||
<div
|
||||
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 === 'WARN'" 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">
|
||||
<ng-content></ng-content>
|
||||
|
@ -37,13 +37,19 @@ export class SearchProjectAutocompleteComponent implements OnInit, OnDestroy {
|
||||
@Output() public selectionChanged: EventEmitter<{
|
||||
project: Project.AsObject | GrantedProject.AsObject;
|
||||
type: ProjectType;
|
||||
name: string;
|
||||
}> = new EventEmitter();
|
||||
@Output() public valueChanged: EventEmitter<string> = new EventEmitter();
|
||||
|
||||
private unsubscribed$: Subject<void> = new Subject();
|
||||
constructor(private mgmtService: ManagementService) {
|
||||
this.myControl.valueChanges
|
||||
.pipe(
|
||||
takeUntil(this.unsubscribed$),
|
||||
tap((value) => {
|
||||
const name = typeof value === 'string' ? value : value.name ? value.name : '';
|
||||
this.valueChanged.emit(name);
|
||||
}),
|
||||
debounceTime(200),
|
||||
tap(() => (this.isLoading = true)),
|
||||
switchMap((value) => {
|
||||
@ -124,44 +130,6 @@ export class SearchProjectAutocompleteComponent implements OnInit, OnDestroy {
|
||||
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 {
|
||||
const p: Project.AsObject | GrantedProject.AsObject = event.option.value;
|
||||
const type = (p as Project.AsObject).id
|
||||
@ -170,8 +138,17 @@ export class SearchProjectAutocompleteComponent implements OnInit, OnDestroy {
|
||||
? ProjectType.PROJECTTYPE_GRANTED
|
||||
: 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({
|
||||
project: p,
|
||||
name,
|
||||
type: type,
|
||||
});
|
||||
}
|
||||
|
@ -1,25 +1,53 @@
|
||||
<cnsl-create-layout title="{{ 'APP.PAGES.CREATE' | translate }}" (closed)="close()">
|
||||
<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
|
||||
class="block"
|
||||
[autocompleteType]="ProjectAutocompleteType.PROJECT_OWNED"
|
||||
(selectionChanged)="selectProject($any($event.project))"
|
||||
<cnsl-search-project-autocomplete
|
||||
class="block"
|
||||
[autocompleteType]="ProjectAutocompleteType.PROJECT_OWNED"
|
||||
(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">
|
||||
<button
|
||||
color="primary"
|
||||
mat-raised-button
|
||||
class="continue-button"
|
||||
[disabled]="!projectId"
|
||||
(click)="goToAppCreatePage()"
|
||||
[disabled]="(!project && !projectName) || !(framework() || customFramework())"
|
||||
(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>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -5,6 +5,20 @@ h1 {
|
||||
.app-create-main-content {
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -17,9 +31,4 @@ h1 {
|
||||
padding: 0 4rem;
|
||||
}
|
||||
}
|
||||
|
||||
.complexity-view {
|
||||
width: 100%;
|
||||
margin: 0 0.5rem;
|
||||
}
|
||||
}
|
||||
|
@ -1,19 +1,19 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
|
||||
import { OrgCreateComponent } from './org-create.component';
|
||||
import { AppCreateComponent } from './app-create.component';
|
||||
|
||||
describe('OrgCreateComponent', () => {
|
||||
let component: OrgCreateComponent;
|
||||
let fixture: ComponentFixture<OrgCreateComponent>;
|
||||
describe('AppCreateComponent', () => {
|
||||
let component: AppCreateComponent;
|
||||
let fixture: ComponentFixture<AppCreateComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [OrgCreateComponent],
|
||||
declarations: [AppCreateComponent],
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(OrgCreateComponent);
|
||||
fixture = TestBed.createComponent(AppCreateComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
@ -1,40 +1,144 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { Component, OnDestroy, signal } from '@angular/core';
|
||||
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 { 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 { 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({
|
||||
selector: 'cnsl-app-create',
|
||||
templateUrl: './app-create.component.html',
|
||||
styleUrls: ['./app-create.component.scss'],
|
||||
})
|
||||
export class AppCreateComponent {
|
||||
public projectId: string = '';
|
||||
export class AppCreateComponent implements OnDestroy {
|
||||
public InfoSectionType: any = InfoSectionType;
|
||||
public project?: {
|
||||
project: Project.AsObject | GrantedProject.AsObject;
|
||||
type: ProjectType;
|
||||
name: string;
|
||||
} = undefined;
|
||||
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(
|
||||
private router: Router,
|
||||
private mgmtService: ManagementService,
|
||||
breadcrumbService: BreadcrumbService,
|
||||
activatedRoute: ActivatedRoute,
|
||||
private _location: Location,
|
||||
private navigation: NavigationService,
|
||||
) {
|
||||
const bread: Breadcrumb = {
|
||||
type: BreadcrumbType.ORG,
|
||||
routerLink: ['/org'],
|
||||
};
|
||||
breadcrumbService.setBreadcrumb([bread]);
|
||||
|
||||
activatedRoute.queryParams.pipe(takeUntil(this.destroy$)).subscribe((params: Params) => {
|
||||
const { framework } = params;
|
||||
if (framework) {
|
||||
this.initialParam.set(framework);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public goToAppCreatePage(): void {
|
||||
this.router.navigate(['/projects', this.projectId, 'apps', 'create']);
|
||||
public findFramework(id: string) {
|
||||
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 {
|
||||
window.history.back();
|
||||
}
|
||||
|
||||
public selectProject(project: Project.AsObject): void {
|
||||
if (project.id) {
|
||||
this.projectId = project.id;
|
||||
if (this.navigation.isBackPossible) {
|
||||
this._location.back();
|
||||
} 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]);
|
||||
} 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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 { AppCreateRoutingModule } from './app-create-routing.module';
|
||||
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({
|
||||
declarations: [AppCreateComponent],
|
||||
imports: [
|
||||
FrameworkChangeComponent,
|
||||
AppCreateRoutingModule,
|
||||
FrameworkAutocompleteComponent,
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
@ -35,9 +39,6 @@ import { AppCreateComponent } from './app-create.component';
|
||||
HasRolePipeModule,
|
||||
TranslateModule,
|
||||
HasRoleModule,
|
||||
MatCheckboxModule,
|
||||
PasswordComplexityViewModule,
|
||||
MatSlideToggleModule,
|
||||
],
|
||||
})
|
||||
export default class AppCreateModule {}
|
||||
|
@ -2,6 +2,8 @@
|
||||
<h1 class="home-title" data-e2e="authenticated-welcome">{{ 'HOME.WELCOME' | translate }}</h1>
|
||||
|
||||
<div class="home-wrapper enlarged-container">
|
||||
<cnsl-quickstart></cnsl-quickstart>
|
||||
|
||||
<ng-container *ngIf="['iam.read$'] | hasRole | async; else defaultHome">
|
||||
<cnsl-onboarding></cnsl-onboarding>
|
||||
</ng-container>
|
||||
|
@ -19,6 +19,7 @@
|
||||
.home-title {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
.home-wrapper {
|
||||
|
@ -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 { HomeRoutingModule } from './home-routing.module';
|
||||
import { HomeComponent } from './home.component';
|
||||
import { QuickstartComponent } from 'src/app/components/quickstart/quickstart.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [HomeComponent],
|
||||
imports: [
|
||||
QuickstartComponent,
|
||||
CommonModule,
|
||||
MatIconModule,
|
||||
HasRoleModule,
|
||||
|
@ -57,6 +57,13 @@
|
||||
</cnsl-top-view>
|
||||
|
||||
<div class="max-width-container">
|
||||
<cnsl-info-section *ngIf="isNew()" class="problem" [type]="InfoSectionType.INFO">
|
||||
<div class="jumptoproject-row">
|
||||
<span class="jumptoproject">{{ 'APP.PAGES.JUMPTOPROJECT' | translate }}</span>
|
||||
<a [routerLink]="['/projects', projectId]" color="primary" mat-raised-button>{{ 'ACTIONS.CONFIGURE' | translate }}</a>
|
||||
</div>
|
||||
</cnsl-info-section>
|
||||
|
||||
<div
|
||||
class="compliance"
|
||||
*ngIf="app && app.oidcConfig && app.oidcConfig.complianceProblemsList && app.oidcConfig.complianceProblemsList?.length"
|
||||
|
@ -125,6 +125,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
.jumptoproject-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.jumptoproject {
|
||||
margin-top: 0.25rem;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.compliance {
|
||||
padding-bottom: 1rem;
|
||||
padding-top: 0.5rem;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { COMMA, ENTER, SPACE } from '@angular/cdk/keycodes';
|
||||
import { Location } from '@angular/common';
|
||||
import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
|
||||
import { Component, OnDestroy, OnInit, ViewEncapsulation, signal } from '@angular/core';
|
||||
import { AbstractControl, FormControl, UntypedFormBuilder, UntypedFormControl, UntypedFormGroup } from '@angular/forms';
|
||||
import { MatCheckboxChange } from '@angular/material/checkbox';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
@ -149,6 +149,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
|
||||
public settingsList: SidenavSetting[] = [{ id: 'configuration', i18nKey: 'APP.CONFIGURATION' }];
|
||||
public currentSetting: string | undefined = this.settingsList[0].id;
|
||||
|
||||
public isNew = signal<boolean>(false);
|
||||
constructor(
|
||||
private envSvc: EnvironmentService,
|
||||
public translate: TranslateService,
|
||||
@ -245,6 +246,9 @@ export class AppDetailComponent implements OnInit, OnDestroy {
|
||||
public ngOnInit(): void {
|
||||
const projectId = this.route.snapshot.paramMap.get('projectid');
|
||||
const appId = this.route.snapshot.paramMap.get('appid');
|
||||
const isNew = this.route.snapshot.queryParamMap.get('new');
|
||||
|
||||
this.isNew.set(isNew === 'true');
|
||||
|
||||
if (projectId && appId) {
|
||||
this.projectId = projectId;
|
||||
|
@ -3,6 +3,7 @@ import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
import { AppCreateComponent } from '../apps/app-create/app-create.component';
|
||||
import { AppDetailComponent } from '../apps/app-detail/app-detail.component';
|
||||
import { IntegrateAppComponent } from './integrate/integrate.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
@ -10,6 +11,11 @@ const routes: Routes = [
|
||||
component: AppCreateComponent,
|
||||
data: { animation: 'AddPage' },
|
||||
},
|
||||
{
|
||||
path: 'integrate',
|
||||
component: IntegrateAppComponent,
|
||||
data: { animation: 'AddPage' },
|
||||
},
|
||||
{
|
||||
path: ':appid',
|
||||
component: AppDetailComponent,
|
||||
|
@ -43,6 +43,9 @@ import { AuthMethodDialogComponent } from './app-detail/auth-method-dialog/auth-
|
||||
import { AppSecretDialogComponent } from './app-secret-dialog/app-secret-dialog.component';
|
||||
import { AppsRoutingModule } from './apps-routing.module';
|
||||
import { RedirectUrisComponent } from './redirect-uris/redirect-uris.component';
|
||||
import { IntegrateAppComponent } from './integrate/integrate.component';
|
||||
import { OIDCConfigurationComponent } from 'src/app/components/oidc-configuration/oidc-configuration.component';
|
||||
import { FrameworkChangeComponent } from 'src/app/components/framework-change/framework-change.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@ -50,12 +53,15 @@ import { RedirectUrisComponent } from './redirect-uris/redirect-uris.component';
|
||||
AppDetailComponent,
|
||||
AppSecretDialogComponent,
|
||||
RedirectUrisComponent,
|
||||
IntegrateAppComponent,
|
||||
AdditionalOriginsComponent,
|
||||
AuthMethodDialogComponent,
|
||||
],
|
||||
imports: [
|
||||
FrameworkChangeComponent,
|
||||
CommonModule,
|
||||
A11yModule,
|
||||
OIDCConfigurationComponent,
|
||||
RedirectPipeModule,
|
||||
NameDialogModule,
|
||||
AppRadioModule,
|
||||
|
@ -0,0 +1,138 @@
|
||||
<div class="app-integrate-wrapper">
|
||||
<div class="integrate-layout-container">
|
||||
<div class="max-width-container">
|
||||
<div class="top-control">
|
||||
<button (click)="close()" mat-icon-button matTooltip="{{ 'ACTIONS.CLOSE' | translate }}">
|
||||
<mat-icon *ngIf="navigation.isBackPossible">arrow_back</mat-icon>
|
||||
<mat-icon *ngIf="!navigation.isBackPossible">close</mat-icon>
|
||||
</button>
|
||||
<span class="abort">{{ 'APP.PAGES.CREATE' | translate }}</span>
|
||||
<mat-progress-spinner
|
||||
class="progress-spinner"
|
||||
color="primary"
|
||||
*ngIf="loading"
|
||||
diameter="30"
|
||||
mode="indeterminate"
|
||||
></mat-progress-spinner>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="max-width-container">
|
||||
<div class="offset-content">
|
||||
<h1>{{ 'QUICKSTART.ALMOSTDONE' | translate }}</h1>
|
||||
<p>{{ 'QUICKSTART.REVIEWCONFIGURATION_DESCRIPTION' | translate: { value: framework()?.title } }}</p>
|
||||
|
||||
<div class="grid-layout">
|
||||
<div>
|
||||
<cnsl-framework-change
|
||||
*ngIf="framework"
|
||||
class="framework-selector"
|
||||
(frameworkChanged)="setFramework($event)"
|
||||
></cnsl-framework-change>
|
||||
<div class="steps">
|
||||
<div class="step">
|
||||
<span class="step-title">{{ 'PROJECT.PAGES.TITLE' | translate }}</span>
|
||||
<span>{{ projectName$ | async }}</span>
|
||||
</div>
|
||||
|
||||
<div class="step top-border">
|
||||
<a *ngIf="framework()" [href]="'https://zitadel.com' + framework()?.docsLink" target="_blank"
|
||||
>{{ framework()?.title }} {{ 'QUICKSTART.GUIDE' | translate }}</a
|
||||
>
|
||||
<a href="https://zitadel.com/docs/sdk-examples/introduction" target="_blank">{{
|
||||
'QUICKSTART.BROWSEEXAMPLES' | translate
|
||||
}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-wrapper">
|
||||
<cnsl-card class="review-card" title="{{ 'QUICKSTART.REVIEWCONFIGURATION' | translate }}">
|
||||
<cnsl-info-section *ngIf="showRenameWarning | async" [type]="InfoSectionType.WARN"
|
||||
><span class="duplicate-name-warning">{{
|
||||
'QUICKSTART.DUPLICATEAPPRENAME' | translate
|
||||
}}</span></cnsl-info-section
|
||||
>
|
||||
<cnsl-oidc-app-configuration
|
||||
*ngIf="(oidcAppRequest | async)?.toObject() as config"
|
||||
[name]="(oidcAppRequest | async)?.toObject()?.name"
|
||||
(changeName)="editName()"
|
||||
[configuration]="config"
|
||||
></cnsl-oidc-app-configuration>
|
||||
|
||||
<cnsl-info-section *ngIf="framework()?.description as desc" [type]="InfoSectionType.INFO">
|
||||
<p class="review-description">
|
||||
{{ desc }}
|
||||
</p>
|
||||
</cnsl-info-section>
|
||||
</cnsl-card>
|
||||
|
||||
<cnsl-card
|
||||
title="{{ 'QUICKSTART.REDIRECTS' | translate }}"
|
||||
description="{{ 'APP.OIDC.REDIRECTTITLE' | translate }}"
|
||||
>
|
||||
<cnsl-info-section [type]="InfoSectionType.ALERT">
|
||||
<span class="redirect-description">
|
||||
{{ 'QUICKSTART.DEVMODEWARN' | translate }}
|
||||
</span>
|
||||
</cnsl-info-section>
|
||||
<cnsl-info-section *ngIf="(oidcAppRequest | async)?.toObject()?.appType === OIDCAppType.OIDC_APP_TYPE_NATIVE">
|
||||
<span class="redirect-description">
|
||||
{{ 'APP.OIDC.REDIRECTDESCRIPTIONNATIVE' | translate }}
|
||||
</span>
|
||||
</cnsl-info-section>
|
||||
<cnsl-info-section
|
||||
*ngIf="
|
||||
(oidcAppRequest | async)?.toObject()?.appType === OIDCAppType.OIDC_APP_TYPE_WEB ||
|
||||
(oidcAppRequest | async)?.toObject()?.appType === OIDCAppType.OIDC_APP_TYPE_USER_AGENT
|
||||
"
|
||||
>
|
||||
<span class="redirect-description">
|
||||
{{ 'APP.OIDC.REDIRECTDESCRIPTIONWEB' | translate }}
|
||||
</span>
|
||||
</cnsl-info-section>
|
||||
|
||||
<cnsl-redirect-uris
|
||||
*ngIf="requestRedirectValuesSubject$"
|
||||
class="redirect-section"
|
||||
[disabled]="false"
|
||||
[isNative]="(oidcAppRequest | async)?.toObject()?.appType === OIDCAppType.OIDC_APP_TYPE_NATIVE"
|
||||
[(ngModel)]="redirectUris"
|
||||
[getValues]="requestRedirectValuesSubject$"
|
||||
title="{{ 'APP.OIDC.REDIRECT' | translate }}"
|
||||
[devMode]="true"
|
||||
data-e2e="redirect-uris"
|
||||
>
|
||||
</cnsl-redirect-uris>
|
||||
|
||||
<cnsl-redirect-uris
|
||||
*ngIf="requestRedirectValuesSubject$"
|
||||
class="redirect-section"
|
||||
[disabled]="false"
|
||||
[(ngModel)]="postLogoutUrisList"
|
||||
[getValues]="requestRedirectValuesSubject$"
|
||||
title="{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}"
|
||||
[isNative]="(oidcAppRequest | async)?.toObject()?.appType === OIDCAppType.OIDC_APP_TYPE_NATIVE"
|
||||
[devMode]="true"
|
||||
data-e2e="postlogout-uris"
|
||||
>
|
||||
</cnsl-redirect-uris>
|
||||
</cnsl-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="app-integrate-actions">
|
||||
<button
|
||||
mat-raised-button
|
||||
[disabled]="loading"
|
||||
class="create-button"
|
||||
color="primary"
|
||||
(click)="createApp()"
|
||||
data-e2e="create-button"
|
||||
>
|
||||
{{ 'ACTIONS.CREATE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,214 @@
|
||||
h1 {
|
||||
font-weight: 500;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.integrate-layout-container {
|
||||
margin: 0 -2rem 2rem -2rem;
|
||||
padding: 3rem 2rem 14rem 2rem;
|
||||
|
||||
.top-control {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.abort {
|
||||
font-size: 1.2rem;
|
||||
margin-left: 1.5rem;
|
||||
text-transform: uppercase;
|
||||
font-size: 14px;
|
||||
opacity: 0.8;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.progress-spinner {
|
||||
margin-left: 2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.offset-content {
|
||||
margin-top: -14rem;
|
||||
}
|
||||
|
||||
.grid-layout {
|
||||
margin-top: 2rem;
|
||||
display: grid;
|
||||
grid-template-columns: [first] 300px [second] auto;
|
||||
grid-column-gap: 5rem;
|
||||
|
||||
.framework-selector {
|
||||
margin-bottom: 2rem;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.steps {
|
||||
margin-top: 0.5rem;
|
||||
|
||||
.step {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 1rem 0;
|
||||
|
||||
.step-title {
|
||||
font-weight: 700;
|
||||
font-size: 12px;
|
||||
opacity: 0.8;
|
||||
margin-bottom: 0.5rem;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
a {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.redirect-p {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.redirect-description {
|
||||
font-size: 14px;
|
||||
margin: 0.25rem 0 0 0;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 900px) {
|
||||
.integrate-layout-container {
|
||||
margin: 0 -2rem 2rem -2rem;
|
||||
background: transparent;
|
||||
padding: 3rem 2rem 14rem 2rem;
|
||||
}
|
||||
|
||||
.grid-layout {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.steps {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card-wrapper {
|
||||
margin-top: -1rem;
|
||||
|
||||
.duplicate-name-warning {
|
||||
margin-top: 0.25rem;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin app-integrate-theme($theme) {
|
||||
$primary: map-get($theme, primary);
|
||||
$warn: map-get($theme, warn);
|
||||
$background: map-get($theme, background);
|
||||
$accent: map-get($theme, accent);
|
||||
$primary-color: map-get($primary, 500);
|
||||
|
||||
$warn-color: map-get($warn, 500);
|
||||
$accent-color: map-get($accent, 500);
|
||||
$foreground: map-get($theme, foreground);
|
||||
$is-dark-theme: map-get($theme, is-dark);
|
||||
$back: map-get($background, background);
|
||||
|
||||
$list-background-color: map-get($background, 300);
|
||||
$card-background-color: map-get($background, cards);
|
||||
$border-color: if($is-dark-theme, rgba(#8795a1, 0.2), rgba(#8795a1, 0.2));
|
||||
$border-selected-color: if($is-dark-theme, #fff, #000);
|
||||
|
||||
.integrate-layout-container {
|
||||
background: map-get($background, metadata-section);
|
||||
}
|
||||
|
||||
.review-description {
|
||||
font-size: 14px;
|
||||
margin: 0.25rem 0 0 0;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.grid-layout {
|
||||
.step {
|
||||
&.top-border {
|
||||
border-top: 2px solid $border-color;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: $primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
.framework-card-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
|
||||
.framework-card {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
text-decoration: none;
|
||||
border-radius: 0.5rem;
|
||||
box-sizing: border-box;
|
||||
transition: all 0.1s ease-in;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 60px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: fit-content;
|
||||
border: 1px solid rgba(#8795a1, 0.2);
|
||||
padding: 0 0.5rem;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-width: 40px;
|
||||
max-height: 40px;
|
||||
object-fit: contain;
|
||||
object-position: center;
|
||||
}
|
||||
|
||||
.dark-only {
|
||||
display: if($is-dark-theme, block, none);
|
||||
}
|
||||
|
||||
.light-only {
|
||||
display: if($is-dark-theme, none, block);
|
||||
}
|
||||
|
||||
span {
|
||||
margin: 0.5rem;
|
||||
text-align: center;
|
||||
color: map-get($foreground, text);
|
||||
}
|
||||
|
||||
.action-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
font-size: 14px;
|
||||
margin-bottom: 0.5rem;
|
||||
color: map-get($primary, 400);
|
||||
|
||||
.icon {
|
||||
margin-left: 0rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.app-integrate-actions {
|
||||
margin-top: 2rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
|
||||
.create-button {
|
||||
height: 3.5rem;
|
||||
padding: 0 4rem;
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
|
||||
import { IntegrateAppComponent } from './integrate.component';
|
||||
|
||||
describe('IntegrateAppComponent', () => {
|
||||
let component: IntegrateAppComponent;
|
||||
let fixture: ComponentFixture<IntegrateAppComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [IntegrateAppComponent],
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(IntegrateAppComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,220 @@
|
||||
import { C, COMMA, ENTER, SPACE } from '@angular/cdk/keycodes';
|
||||
import { StepperSelectionEvent } from '@angular/cdk/stepper';
|
||||
import { Location } from '@angular/common';
|
||||
import { Component, OnDestroy, OnInit, Signal, computed, effect, signal } from '@angular/core';
|
||||
import { AbstractControl, UntypedFormBuilder, UntypedFormControl, UntypedFormGroup } from '@angular/forms';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { ActivatedRoute, Params, Router } from '@angular/router';
|
||||
import { Buffer } from 'buffer';
|
||||
import { BehaviorSubject, Subject, Subscription, combineLatest } from 'rxjs';
|
||||
import { debounceTime, map, takeUntil } from 'rxjs/operators';
|
||||
import { RadioItemAuthType } from 'src/app/modules/app-radio/app-auth-method-radio/app-auth-method-radio.component';
|
||||
import { requiredValidator } from 'src/app/modules/form-field/validators/validators';
|
||||
import {
|
||||
APIAuthMethodType,
|
||||
OIDCAppType,
|
||||
OIDCAuthMethodType,
|
||||
OIDCGrantType,
|
||||
OIDCResponseType,
|
||||
} from 'src/app/proto/generated/zitadel/app_pb';
|
||||
import {
|
||||
AddAPIAppRequest,
|
||||
AddAPIAppResponse,
|
||||
AddOIDCAppRequest,
|
||||
AddOIDCAppResponse,
|
||||
AddSAMLAppRequest,
|
||||
} from 'src/app/proto/generated/zitadel/management_pb';
|
||||
import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
import { AppSecretDialogComponent } from '../app-secret-dialog/app-secret-dialog.component';
|
||||
import { InfoSectionType } from 'src/app/modules/info-section/info-section.component';
|
||||
import { Framework } from 'src/app/components/quickstart/quickstart.component';
|
||||
import { OIDC_CONFIGURATIONS } from 'src/app/utils/framework';
|
||||
import { NavigationService } from 'src/app/services/navigation.service';
|
||||
import { NameDialogComponent } from 'src/app/modules/name-dialog/name-dialog.component';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-integrate',
|
||||
templateUrl: './integrate.component.html',
|
||||
styleUrls: ['./integrate.component.scss'],
|
||||
})
|
||||
export class IntegrateAppComponent implements OnInit, OnDestroy {
|
||||
private destroy$: Subject<void> = new Subject();
|
||||
public projectId: string = '';
|
||||
public loading: boolean = false;
|
||||
public InfoSectionType: any = InfoSectionType;
|
||||
public framework = signal<Framework | undefined>(undefined);
|
||||
public showRenameWarning: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
public oidcAppRequest: BehaviorSubject<AddOIDCAppRequest> = new BehaviorSubject(new AddOIDCAppRequest());
|
||||
|
||||
public OIDCAppType: any = OIDCAppType;
|
||||
public requestRedirectValuesSubject$: Subject<void> = new Subject();
|
||||
|
||||
constructor(
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private router: Router,
|
||||
private toast: ToastService,
|
||||
private dialog: MatDialog,
|
||||
private mgmtService: ManagementService,
|
||||
private _location: Location,
|
||||
private breadcrumbService: BreadcrumbService,
|
||||
public navigation: NavigationService,
|
||||
) {
|
||||
effect(() => {
|
||||
const fwId = this.framework()?.id;
|
||||
const fw = this.framework();
|
||||
if (fw && fwId) {
|
||||
const request = OIDC_CONFIGURATIONS[fwId];
|
||||
request.setProjectId(this.projectId);
|
||||
request.setName(fw.title);
|
||||
request.setDevMode(true);
|
||||
this.requestRedirectValuesSubject$.next();
|
||||
this.showRenameWarning.next(false);
|
||||
|
||||
this.oidcAppRequest.next(request);
|
||||
return request;
|
||||
} else {
|
||||
const request = new AddOIDCAppRequest();
|
||||
this.oidcAppRequest.next(request);
|
||||
return request;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public projectName$ = combineLatest([this.mgmtService.ownedProjects, this.mgmtService.grantedProjects]).pipe(
|
||||
map(([projects, grantedProjects]) => {
|
||||
const project = projects.find((project) => project.id === this.activatedRoute.snapshot.paramMap.get('projectid'));
|
||||
|
||||
const grantedproject = grantedProjects.find(
|
||||
(grantedproject) => grantedproject.projectId === this.activatedRoute.snapshot.paramMap.get('projectid'),
|
||||
);
|
||||
|
||||
return project?.name ?? grantedproject?.projectName ?? '';
|
||||
}),
|
||||
);
|
||||
|
||||
public setFramework(framework: Framework | undefined) {
|
||||
this.framework.set(framework);
|
||||
}
|
||||
|
||||
public ngOnInit(): void {
|
||||
const projectId = this.activatedRoute.snapshot.paramMap.get('projectid');
|
||||
if (projectId) {
|
||||
const breadcrumbs = [
|
||||
new Breadcrumb({
|
||||
type: BreadcrumbType.ORG,
|
||||
routerLink: ['/org'],
|
||||
}),
|
||||
new Breadcrumb({
|
||||
type: BreadcrumbType.PROJECT,
|
||||
name: '',
|
||||
param: { key: 'projectid', value: projectId },
|
||||
routerLink: ['/projects', projectId],
|
||||
isZitadel: false,
|
||||
}),
|
||||
];
|
||||
this.projectId = projectId;
|
||||
this.breadcrumbService.setBreadcrumb(breadcrumbs);
|
||||
}
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
}
|
||||
|
||||
public close(): void {
|
||||
if (this.navigation.isBackPossible) {
|
||||
this._location.back();
|
||||
} else {
|
||||
this.router.navigate(['/projects', this.projectId]);
|
||||
}
|
||||
}
|
||||
|
||||
public createApp(): void {
|
||||
this.loading = true;
|
||||
this.mgmtService
|
||||
.addOIDCApp(this.oidcAppRequest.getValue())
|
||||
.then((resp) => {
|
||||
this.loading = false;
|
||||
this.showRenameWarning.next(false);
|
||||
this.toast.showInfo('APP.TOAST.CREATED', true);
|
||||
if (resp.clientSecret) {
|
||||
this.showSavedDialog(resp);
|
||||
} else {
|
||||
this.router.navigate(['projects', this.projectId, 'apps', resp.appId], { queryParams: { new: true } });
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error.code === 6) {
|
||||
this.showRenameWarning.next(true);
|
||||
}
|
||||
this.loading = false;
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
||||
public editName() {
|
||||
const dialogRef = this.dialog.open(NameDialogComponent, {
|
||||
data: {
|
||||
name: this.oidcAppRequest.getValue()?.getName() ?? '',
|
||||
titleKey: 'APP.NAMEDIALOG.TITLE',
|
||||
descKey: 'APP.NAMEDIALOG.DESCRIPTION',
|
||||
labelKey: 'APP.NAMEDIALOG.NAME',
|
||||
},
|
||||
width: '400px',
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe((name) => {
|
||||
if (name && name !== this.framework()?.title) {
|
||||
const request = this.oidcAppRequest.getValue();
|
||||
request.setName(name);
|
||||
this.showRenameWarning.next(false);
|
||||
this.oidcAppRequest.next(request);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public showSavedDialog(added: AddOIDCAppResponse.AsObject | AddAPIAppResponse.AsObject): void {
|
||||
let clientSecret = '';
|
||||
if (added.clientSecret) {
|
||||
clientSecret = added.clientSecret;
|
||||
}
|
||||
let clientId = '';
|
||||
if (added.clientId) {
|
||||
clientId = added.clientId;
|
||||
}
|
||||
const dialogRef = this.dialog.open(AppSecretDialogComponent, {
|
||||
data: {
|
||||
clientSecret: clientSecret,
|
||||
clientId: clientId,
|
||||
},
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(() => {
|
||||
this.router.navigate(['projects', this.projectId, 'apps', added.appId], { queryParams: { new: true } });
|
||||
});
|
||||
}
|
||||
|
||||
public get redirectUris() {
|
||||
return this.oidcAppRequest.getValue().toObject().redirectUrisList;
|
||||
}
|
||||
|
||||
public set redirectUris(value: string[]) {
|
||||
const request = this.oidcAppRequest.getValue();
|
||||
request.setRedirectUrisList(value);
|
||||
this.oidcAppRequest.next(request);
|
||||
}
|
||||
|
||||
public get postLogoutUrisList() {
|
||||
return this.oidcAppRequest.getValue().toObject().postLogoutRedirectUrisList;
|
||||
}
|
||||
|
||||
public set postLogoutUrisList(value: string[]) {
|
||||
const request = this.oidcAppRequest.getValue();
|
||||
request.setPostLogoutRedirectUrisList(value);
|
||||
this.oidcAppRequest.next(request);
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import { DatePipe } from '@angular/common';
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import * as moment from 'moment';
|
||||
import moment from 'moment';
|
||||
import { supportedLanguages } from 'src/app/utils/language';
|
||||
|
||||
@Pipe({
|
||||
|
70
console/src/app/utils/framework.ts
Normal file
70
console/src/app/utils/framework.ts
Normal 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']),
|
||||
};
|
@ -119,6 +119,30 @@
|
||||
"SETTINGS": "Настройки",
|
||||
"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": "Действия",
|
||||
"FILTER": "Филтър",
|
||||
@ -138,6 +162,7 @@
|
||||
"ADD": "Добавете",
|
||||
"CREATE": "Създавайте",
|
||||
"CONTINUE": "продължи",
|
||||
"CONTINUEWITH": "Продължете с {{value}}",
|
||||
"BACK": "обратно",
|
||||
"CLOSE": "Близо",
|
||||
"CLEAR": "ясно",
|
||||
@ -1952,6 +1977,7 @@
|
||||
"DATECHANGED": "Променен",
|
||||
"URLS": "URL адреси",
|
||||
"DELETE": "Изтриване на приложение",
|
||||
"JUMPTOPROJECT": "За да конфигурирате роли, оторизации и други, отидете до проекта.",
|
||||
"DETAIL": {
|
||||
"TITLE": "детайл",
|
||||
"STATE": {
|
||||
|
@ -119,6 +119,30 @@
|
||||
"SETTINGS": "Nastavení",
|
||||
"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": "Akce",
|
||||
"FILTER": "Filtr",
|
||||
@ -138,6 +162,7 @@
|
||||
"ADD": "Přidat",
|
||||
"CREATE": "Vytvořit",
|
||||
"CONTINUE": "Pokračovat",
|
||||
"CONTINUEWITH": "Pokračovat s {{value}}",
|
||||
"BACK": "Zpět",
|
||||
"CLOSE": "Zavřít",
|
||||
"CLEAR": "Vyčistit",
|
||||
@ -1971,6 +1996,7 @@
|
||||
"DATECHANGED": "Změněno",
|
||||
"URLS": "URL adresy",
|
||||
"DELETE": "Smazat aplikaci",
|
||||
"JUMPTOPROJECT": "Chcete-li nakonfigurovat role, oprávnění a další, přejděte do projektu.",
|
||||
"DETAIL": {
|
||||
"TITLE": "Detail",
|
||||
"STATE": {
|
||||
|
@ -119,6 +119,30 @@
|
||||
"SETTINGS": "Einstellungen",
|
||||
"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": "Aktionen",
|
||||
"FILTER": "Filter",
|
||||
@ -138,6 +162,7 @@
|
||||
"ADD": "Hinzufügen",
|
||||
"CREATE": "Erstellen",
|
||||
"CONTINUE": "Weiter",
|
||||
"CONTINUEWITH": "Mit {{value}} fortfahren",
|
||||
"BACK": "Zurück",
|
||||
"CLOSE": "Schliessen",
|
||||
"CLEAR": "Zurücksetzen",
|
||||
@ -1961,6 +1986,7 @@
|
||||
"DATECHANGED": "Geändert",
|
||||
"URLS": "URLs",
|
||||
"DELETE": "App löschen",
|
||||
"JUMPTOPROJECT": "Um Rollen, Berechtigungen und mehr zu konfigurieren, navigieren Sie zum Projekt.",
|
||||
"DETAIL": {
|
||||
"TITLE": "Detail",
|
||||
"STATE": {
|
||||
|
@ -43,7 +43,7 @@
|
||||
}
|
||||
},
|
||||
"ONBOARDING": {
|
||||
"DESCRIPTION": "Your onboarding process",
|
||||
"DESCRIPTION": "Your next steps",
|
||||
"MOREDESCRIPTION": "more shortcuts",
|
||||
"COMPLETED": "completed",
|
||||
"DISMISS": "No thanks, I'm a pro.",
|
||||
@ -119,6 +119,30 @@
|
||||
"SETTINGS": "Settings",
|
||||
"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",
|
||||
"FILTER": "Filter",
|
||||
@ -138,6 +162,7 @@
|
||||
"ADD": "Add",
|
||||
"CREATE": "Create",
|
||||
"CONTINUE": "Continue",
|
||||
"CONTINUEWITH": "Continue with {{value}}",
|
||||
"BACK": "Back",
|
||||
"CLOSE": "Close",
|
||||
"CLEAR": "Clear",
|
||||
@ -1972,7 +1997,7 @@
|
||||
"DESCRIPTION": "Here you can edit your application data and it's configuration.",
|
||||
"CREATE": "Create application",
|
||||
"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_SUB": "A recommended configuration will be automatically generated.",
|
||||
"STATE": "Status",
|
||||
@ -1980,6 +2005,7 @@
|
||||
"DATECHANGED": "Changed",
|
||||
"URLS": "URLs",
|
||||
"DELETE": "Delete App",
|
||||
"JUMPTOPROJECT": "To configure roles, authorizations and more, navigate to the project.",
|
||||
"DETAIL": {
|
||||
"TITLE": "Detail",
|
||||
"STATE": {
|
||||
|
@ -119,6 +119,30 @@
|
||||
"SETTINGS": "Ajustes",
|
||||
"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": "Acciones",
|
||||
"FILTER": "Filtrar",
|
||||
@ -138,6 +162,7 @@
|
||||
"ADD": "Añadir",
|
||||
"CREATE": "Crear",
|
||||
"CONTINUE": "Continuar",
|
||||
"CONTINUEWITH": "Continuar con {{value}}",
|
||||
"BACK": "Atrás",
|
||||
"CLOSE": "Cerrar",
|
||||
"CLEAR": "Limpiar",
|
||||
@ -1959,6 +1984,7 @@
|
||||
"DATECHANGED": "Cambiada",
|
||||
"URLS": "URLs",
|
||||
"DELETE": "Borrar App",
|
||||
"JUMPTOPROJECT": "Para configurar roles, autorizaciones y más, navegue hasta el proyecto.",
|
||||
"DETAIL": {
|
||||
"TITLE": "Detalle",
|
||||
"STATE": {
|
||||
|
@ -119,6 +119,30 @@
|
||||
"SETTINGS": "Paramètres",
|
||||
"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",
|
||||
"FILTER": "Filtrer",
|
||||
@ -138,6 +162,7 @@
|
||||
"ADD": "Ajouter",
|
||||
"CREATE": "Créer",
|
||||
"CONTINUE": "Continuer",
|
||||
"CONTINUEWITH": "Continuez avec {{value}}",
|
||||
"BACK": "Retour",
|
||||
"CLOSE": "Fermer",
|
||||
"CLEAR": "Effacer",
|
||||
@ -1962,6 +1987,7 @@
|
||||
"DATECHANGED": "Modifié",
|
||||
"URLS": "URLs",
|
||||
"DELETE": "Supprimer l'application",
|
||||
"JUMPTOPROJECT": "Pour configurer des rôles, des autorisations et bien plus encore, accédez au projet.",
|
||||
"DETAIL": {
|
||||
"TITLE": "Détail",
|
||||
"STATE": {
|
||||
|
@ -119,6 +119,30 @@
|
||||
"SETTINGS": "Impostazioni",
|
||||
"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": "Azioni",
|
||||
"FILTER": "Filtra",
|
||||
@ -138,6 +162,7 @@
|
||||
"ADD": "Aggiungi",
|
||||
"CREATE": "Crea",
|
||||
"CONTINUE": "Continua",
|
||||
"CONTINUEWITH": "Continue con {{value}}",
|
||||
"BACK": "Indietro",
|
||||
"CLOSE": "chiudi",
|
||||
"CLEAR": "Resetta",
|
||||
@ -1962,6 +1987,7 @@
|
||||
"DATECHANGED": "Cambiato",
|
||||
"URLS": "URLs",
|
||||
"DELETE": "Rimuovi App",
|
||||
"JUMPTOPROJECT": "Per configurare ruoli, autorizzazioni e altro, vai al progetto.",
|
||||
"DETAIL": {
|
||||
"TITLE": "Dettagli",
|
||||
"STATE": {
|
||||
|
@ -119,6 +119,30 @@
|
||||
"SETTINGS": "設定",
|
||||
"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": "アクション",
|
||||
"FILTER": "絞り込み",
|
||||
@ -138,6 +162,7 @@
|
||||
"ADD": "追加",
|
||||
"CREATE": "作成",
|
||||
"CONTINUE": "次へ",
|
||||
"CONTINUEWITH": "{{value}} に進みます",
|
||||
"BACK": "戻る",
|
||||
"CLOSE": "閉じる",
|
||||
"CLEAR": "消去する",
|
||||
@ -1953,6 +1978,7 @@
|
||||
"DATECHANGED": "更新日",
|
||||
"URLS": "URL",
|
||||
"DELETE": "アプリを削除する",
|
||||
"JUMPTOPROJECT": "ロール、権限などを構成するには、プロジェクトに移動します。",
|
||||
"DETAIL": {
|
||||
"TITLE": "詳細",
|
||||
"STATE": {
|
||||
|
@ -119,6 +119,30 @@
|
||||
"SETTINGS": "Подесувања",
|
||||
"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": "Акции",
|
||||
"FILTER": "Филтер",
|
||||
@ -138,6 +162,7 @@
|
||||
"ADD": "Додади",
|
||||
"CREATE": "Креирај",
|
||||
"CONTINUE": "Продолжи",
|
||||
"CONTINUEWITH": "Продолжи со {{value}}",
|
||||
"BACK": "Назад",
|
||||
"CLOSE": "Затвори",
|
||||
"CLEAR": "Исчисти",
|
||||
@ -1959,6 +1984,7 @@
|
||||
"DATECHANGED": "Изменето",
|
||||
"URLS": "URLs",
|
||||
"DELETE": "Избриши апликација",
|
||||
"JUMPTOPROJECT": "За да ги конфигурирате улогите, овластувањата и друго, одете до проектот.",
|
||||
"DETAIL": {
|
||||
"TITLE": "Детали",
|
||||
"STATE": {
|
||||
|
@ -119,6 +119,30 @@
|
||||
"SETTINGS": "Instellingen",
|
||||
"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": "Acties",
|
||||
"FILTER": "Filter",
|
||||
@ -138,6 +162,7 @@
|
||||
"ADD": "Toevoegen",
|
||||
"CREATE": "Aanmaken",
|
||||
"CONTINUE": "Doorgaan",
|
||||
"CONTINUEWITH": "Ga verder met {{value}}",
|
||||
"BACK": "Terug",
|
||||
"CLOSE": "Sluiten",
|
||||
"CLEAR": "Leegmaken",
|
||||
@ -1980,6 +2005,7 @@
|
||||
"DATECHANGED": "Gewijzigd",
|
||||
"URLS": "URL's",
|
||||
"DELETE": "Verwijder App",
|
||||
"JUMPTOPROJECT": "Om rollen, autorisaties en meer te configureren, navigeert u naar het project.",
|
||||
"DETAIL": {
|
||||
"TITLE": "Detail",
|
||||
"STATE": {
|
||||
|
@ -119,6 +119,30 @@
|
||||
"SETTINGS": "Ustawienia",
|
||||
"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": "Akcje",
|
||||
"FILTER": "Filtruj",
|
||||
@ -138,6 +162,7 @@
|
||||
"ADD": "Dodaj",
|
||||
"CREATE": "Utwórz",
|
||||
"CONTINUE": "Kontynuuj",
|
||||
"CONTINUEWITH": "Kontynuuj z {{value}}",
|
||||
"BACK": "Wstecz",
|
||||
"CLOSE": "Zamknij",
|
||||
"CLEAR": "Wyczyść",
|
||||
@ -1962,6 +1987,7 @@
|
||||
"DATECHANGED": "Zmienione",
|
||||
"URLS": "Adresy URL",
|
||||
"DELETE": "Usuń aplikację",
|
||||
"JUMPTOPROJECT": "Aby skonfigurować role, uprawnienia i nie tylko, przejdź do projektu.",
|
||||
"DETAIL": {
|
||||
"TITLE": "Szczegóły",
|
||||
"STATE": {
|
||||
|
@ -119,6 +119,30 @@
|
||||
"SETTINGS": "Configurações",
|
||||
"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": "Ações",
|
||||
"FILTER": "Filtrar",
|
||||
@ -138,6 +162,7 @@
|
||||
"ADD": "Adicionar",
|
||||
"CREATE": "Criar",
|
||||
"CONTINUE": "Continuar",
|
||||
"CONTINUEWITH": "Continue com {{value}}",
|
||||
"BACK": "Voltar",
|
||||
"CLOSE": "Fechar",
|
||||
"CLEAR": "Limpar",
|
||||
@ -1957,6 +1982,7 @@
|
||||
"DATECHANGED": "Alterado",
|
||||
"URLS": "URLs",
|
||||
"DELETE": "Excluir App",
|
||||
"JUMPTOPROJECT": "Para configurar funções, autorizações e muito mais, navegue até o projeto.",
|
||||
"DETAIL": {
|
||||
"TITLE": "Detalhe",
|
||||
"STATE": {
|
||||
|
@ -115,6 +115,30 @@
|
||||
"SETTINGS": "Настройки",
|
||||
"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": "Действия",
|
||||
"FILTER": "Фильтр",
|
||||
@ -134,6 +158,7 @@
|
||||
"ADD": "Добавить",
|
||||
"CREATE": "Создать",
|
||||
"CONTINUE": "Продолжить",
|
||||
"CONTINUEWITH": "Продолжить с {{value}}",
|
||||
"BACK": "Назад",
|
||||
"CLOSE": "Закрыть",
|
||||
"CLEAR": "Очистить",
|
||||
@ -1956,6 +1981,7 @@
|
||||
"DATECHANGED": "Дата изменения",
|
||||
"URLS": "URL-адреса",
|
||||
"DELETE": "Удалить приложение",
|
||||
"JUMPTOPROJECT": "Чтобы настроить роли, полномочия и многое другое, перейдите к проекту.",
|
||||
"DETAIL": {
|
||||
"TITLE": "Деталь",
|
||||
"STATE": {
|
||||
|
@ -119,6 +119,30 @@
|
||||
"SETTINGS": "设置",
|
||||
"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": "操作",
|
||||
"FILTER": "过滤",
|
||||
@ -138,6 +162,7 @@
|
||||
"ADD": "添加",
|
||||
"CREATE": "创建",
|
||||
"CONTINUE": "继续",
|
||||
"CONTINUEWITH": "继续 {{value}}",
|
||||
"BACK": "返回",
|
||||
"CLOSE": "关闭",
|
||||
"CLEAR": "清除",
|
||||
@ -1961,6 +1986,7 @@
|
||||
"DATECHANGED": "修改于",
|
||||
"URLS": "URLs",
|
||||
"DELETE": "删除应用",
|
||||
"JUMPTOPROJECT": "要配置角色、授权等,请导航到项目。",
|
||||
"DETAIL": {
|
||||
"TITLE": "详情",
|
||||
"STATE": {
|
||||
|
@ -29,6 +29,7 @@
|
||||
@import 'src/app/modules/top-view/top-view.component';
|
||||
@import 'src/app/pages/projects/projects.component';
|
||||
@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/pages/users/user-detail/auth-user-detail/auth-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/label/label.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/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/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/filter/filter.component.scss';
|
||||
@import 'src/app/modules/policies/message-texts/message-texts.component.scss';
|
||||
@ -83,7 +88,9 @@
|
||||
@include info-overlay-theme($theme);
|
||||
@include app-auth-method-radio-theme($theme);
|
||||
@include security-policy-theme($theme);
|
||||
@include framework-change-theme($theme);
|
||||
@include search-user-autocomplete-theme($theme);
|
||||
@include quickstart-theme($theme);
|
||||
@include project-role-chips-theme($theme);
|
||||
@include card-theme($theme);
|
||||
@include idp-settings-theme($theme);
|
||||
@ -124,6 +131,7 @@
|
||||
@include refresh-table-theme($theme);
|
||||
@include accounts-card-theme($theme);
|
||||
@include sidenav-theme($theme);
|
||||
@include framework-autocomplete-theme($theme);
|
||||
@include info-section-theme($theme);
|
||||
@include actions-theme($theme);
|
||||
@include filter-theme($theme);
|
||||
@ -131,11 +139,12 @@
|
||||
@include project-grid-theme($theme);
|
||||
@include granted-project-detail-theme($theme);
|
||||
@include user-grants-theme($theme);
|
||||
@include app-create-theme($theme);
|
||||
@include info-row-theme($theme);
|
||||
@include redirect-uris-theme($theme);
|
||||
@include action-keys-theme($theme);
|
||||
@include codemirror-theme($theme);
|
||||
@include contact-theme($theme);
|
||||
@include app-create-theme($theme);
|
||||
@include app-integrate-theme($theme);
|
||||
@include domain-verification-theme($theme);
|
||||
}
|
||||
|
@ -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);
|
||||
$foreground: map-get($caos-light-app-theme, foreground);
|
||||
$primary: map-get($caos-light-app-theme, primary);
|
||||
|
||||
--warn: #cd3d56;
|
||||
--success: #10b981;
|
||||
@ -477,6 +478,7 @@ $caos-dark-app-theme: modify-palette($caos-dark-app-theme, foreground, $cnsl-dar
|
||||
.dark-theme {
|
||||
$background: map-get($caos-dark-app-theme, background);
|
||||
$foreground: map-get($caos-dark-app-theme, foreground);
|
||||
$primary: map-get($caos-dark-app-theme, primary);
|
||||
|
||||
--warn: #ff3b5b;
|
||||
--success: #10b981;
|
||||
|
@ -4,11 +4,6 @@
|
||||
$background: map-get($theme, background);
|
||||
$foreground: map-get($theme, foreground);
|
||||
|
||||
// .data-e2e-success {
|
||||
// background-color: map-get($background, cards) !important;
|
||||
// color: var(--success) !important;
|
||||
// }
|
||||
|
||||
.data-e2e-failure {
|
||||
.mdc-snackbar__surface {
|
||||
background-color: map-get($background, toast) !important;
|
||||
|
@ -16,6 +16,8 @@
|
||||
"experimentalDecorators": true,
|
||||
"moduleResolution": "node",
|
||||
"importHelpers": true,
|
||||
"resolveJsonModule": true,
|
||||
"esModuleInterop": true,
|
||||
"target": "ES2022",
|
||||
"module": "ES2022",
|
||||
"useDefineForClassFields": false,
|
||||
|
@ -2910,6 +2910,22 @@
|
||||
"@material/theme" "15.0.0-canary.bc9ae6c9c.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":
|
||||
version "16.2.2"
|
||||
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"
|
||||
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":
|
||||
version "1.3.8"
|
||||
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"
|
||||
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:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz"
|
||||
@ -3864,7 +3893,7 @@ ajv-keywords@^5.1.0:
|
||||
dependencies:
|
||||
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"
|
||||
resolved "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz"
|
||||
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"
|
||||
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:
|
||||
version "3.1.0"
|
||||
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"
|
||||
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:
|
||||
version "1.0.5"
|
||||
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:
|
||||
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:
|
||||
version "1.1.2"
|
||||
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"
|
||||
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:
|
||||
version "4.0.4"
|
||||
resolved "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz"
|
||||
@ -6100,6 +6151,13 @@ has@^1.0.3:
|
||||
dependencies:
|
||||
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:
|
||||
version "2.0.3"
|
||||
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"
|
||||
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:
|
||||
version "6.1.1"
|
||||
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"
|
||||
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:
|
||||
version "1.0.4"
|
||||
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:
|
||||
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:
|
||||
version "2.2.1"
|
||||
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"
|
||||
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:
|
||||
version "2.0.4"
|
||||
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:
|
||||
p-locate "^5.0.0"
|
||||
|
||||
locate-path@^7.1.0:
|
||||
locate-path@^7.0.0, locate-path@^7.1.0:
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-7.2.0.tgz#69cb1779bd90b35ab1e771e1f2f89a202c2a8a8a"
|
||||
integrity sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==
|
||||
@ -7503,6 +7585,16 @@ nopt@^6.0.0:
|
||||
dependencies:
|
||||
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:
|
||||
version "5.0.0"
|
||||
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"
|
||||
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:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz"
|
||||
@ -7818,6 +7917,13 @@ p-map@^4.0.0:
|
||||
dependencies:
|
||||
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:
|
||||
version "4.6.2"
|
||||
resolved "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz"
|
||||
@ -7867,7 +7973,7 @@ parent-module@^1.0.0:
|
||||
dependencies:
|
||||
callsites "^3.0.0"
|
||||
|
||||
parse-json@^5.0.0:
|
||||
parse-json@^5.0.0, parse-json@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz"
|
||||
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"
|
||||
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:
|
||||
version "1.0.1"
|
||||
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"
|
||||
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:
|
||||
version "2.3.8"
|
||||
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"
|
||||
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:
|
||||
version "0.18.0"
|
||||
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"
|
||||
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:
|
||||
version "1.6.18"
|
||||
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"
|
||||
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"
|
||||
resolved "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz"
|
||||
integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==
|
||||
|
@ -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.
|
||||
|
||||
import { Tile } from "../../src/components/tile";
|
||||
import { Frameworks } from "../../src/components/frameworks";
|
||||
|
||||
### Resources
|
||||
|
||||
<div className="tile-wrapper">
|
||||
<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>
|
||||
<Frameworks />
|
||||
|
||||
### OIDC Libraries
|
||||
|
||||
|
115
docs/frameworks.json
Normal file
115
docs/frameworks.json
Normal 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
|
||||
}
|
||||
]
|
21
docs/src/components/frameworks.jsx
Normal file
21
docs/src/components/frameworks.jsx
Normal 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>
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue
Block a user