From 0fcdfe460c71f353b7a8b79c70f6402fc480c885 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Wed, 28 Feb 2024 17:52:21 +0100 Subject: [PATCH] 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 --- console/angular.json | 8 +- console/package.json | 1 + .../framework-autocomplete.component.html | 35 +++ .../framework-autocomplete.component.scss | 69 ++++++ .../framework-autocomplete.component.spec.ts | 21 ++ .../framework-autocomplete.component.ts | 63 +++++ .../framework-change-dialog.component.html | 17 ++ .../framework-change-dialog.component.scss | 10 + .../framework-change-dialog.component.ts | 41 ++++ .../framework-change.component.html | 14 ++ .../framework-change.component.scss | 82 +++++++ .../framework-change.component.spec.ts | 21 ++ .../framework-change.component.ts | 88 +++++++ .../oidc-configuration.component.html | 53 +++++ .../oidc-configuration.component.scss | 23 ++ .../oidc-configuration.component.spec.ts | 21 ++ .../oidc-configuration.component.ts | 35 +++ .../quickstart/quickstart.component.html | 23 ++ .../quickstart/quickstart.component.scss | 121 ++++++++++ .../quickstart/quickstart.component.spec.ts | 21 ++ .../quickstart/quickstart.component.ts | 42 ++++ .../info-section/info-section.component.html | 9 +- .../search-project-autocomplete.component.ts | 53 ++--- .../app-create/app-create.component.html | 50 +++- .../app-create/app-create.component.scss | 19 +- .../app-create/app-create.component.spec.ts | 12 +- .../pages/app-create/app-create.component.ts | 130 +++++++++-- .../app/pages/app-create/app-create.module.ts | 7 +- .../src/app/pages/home/home.component.html | 2 + .../src/app/pages/home/home.component.scss | 1 + console/src/app/pages/home/home.module.ts | 2 + .../apps/app-detail/app-detail.component.html | 7 + .../apps/app-detail/app-detail.component.scss | 10 + .../apps/app-detail/app-detail.component.ts | 6 +- .../projects/apps/apps-routing.module.ts | 6 + .../app/pages/projects/apps/apps.module.ts | 6 + .../apps/integrate/integrate.component.html | 138 +++++++++++ .../apps/integrate/integrate.component.scss | 214 +++++++++++++++++ .../integrate/integrate.component.spec.ts | 24 ++ .../apps/integrate/integrate.component.ts | 220 ++++++++++++++++++ .../localized-date.pipe.ts | 2 +- console/src/app/utils/framework.ts | 70 ++++++ console/src/assets/i18n/bg.json | 26 +++ console/src/assets/i18n/cs.json | 26 +++ console/src/assets/i18n/de.json | 26 +++ console/src/assets/i18n/en.json | 30 ++- console/src/assets/i18n/es.json | 26 +++ console/src/assets/i18n/fr.json | 26 +++ console/src/assets/i18n/it.json | 26 +++ console/src/assets/i18n/ja.json | 26 +++ console/src/assets/i18n/mk.json | 26 +++ console/src/assets/i18n/nl.json | 26 +++ console/src/assets/i18n/pl.json | 26 +++ console/src/assets/i18n/pt.json | 26 +++ console/src/assets/i18n/ru.json | 26 +++ console/src/assets/i18n/zh.json | 26 +++ console/src/component-themes.scss | 11 +- console/src/styles.scss | 2 + console/src/styles/toast.scss | 5 - console/tsconfig.json | 2 + console/yarn.lock | 150 +++++++++++- docs/docs/sdk-examples/introduction.mdx | 86 +------ docs/frameworks.json | 115 +++++++++ docs/src/components/frameworks.jsx | 21 ++ 64 files changed, 2381 insertions(+), 176 deletions(-) create mode 100644 console/src/app/components/framework-autocomplete/framework-autocomplete.component.html create mode 100644 console/src/app/components/framework-autocomplete/framework-autocomplete.component.scss create mode 100644 console/src/app/components/framework-autocomplete/framework-autocomplete.component.spec.ts create mode 100644 console/src/app/components/framework-autocomplete/framework-autocomplete.component.ts create mode 100644 console/src/app/components/framework-change/framework-change-dialog.component.html create mode 100644 console/src/app/components/framework-change/framework-change-dialog.component.scss create mode 100644 console/src/app/components/framework-change/framework-change-dialog.component.ts create mode 100644 console/src/app/components/framework-change/framework-change.component.html create mode 100644 console/src/app/components/framework-change/framework-change.component.scss create mode 100644 console/src/app/components/framework-change/framework-change.component.spec.ts create mode 100644 console/src/app/components/framework-change/framework-change.component.ts create mode 100644 console/src/app/components/oidc-configuration/oidc-configuration.component.html create mode 100644 console/src/app/components/oidc-configuration/oidc-configuration.component.scss create mode 100644 console/src/app/components/oidc-configuration/oidc-configuration.component.spec.ts create mode 100644 console/src/app/components/oidc-configuration/oidc-configuration.component.ts create mode 100644 console/src/app/components/quickstart/quickstart.component.html create mode 100644 console/src/app/components/quickstart/quickstart.component.scss create mode 100644 console/src/app/components/quickstart/quickstart.component.spec.ts create mode 100644 console/src/app/components/quickstart/quickstart.component.ts create mode 100644 console/src/app/pages/projects/apps/integrate/integrate.component.html create mode 100644 console/src/app/pages/projects/apps/integrate/integrate.component.scss create mode 100644 console/src/app/pages/projects/apps/integrate/integrate.component.spec.ts create mode 100644 console/src/app/pages/projects/apps/integrate/integrate.component.ts create mode 100644 console/src/app/utils/framework.ts create mode 100644 docs/frameworks.json create mode 100644 docs/src/components/frameworks.jsx diff --git a/console/angular.json b/console/angular.json index ad3d13efd2..f110d0b32b 100644 --- a/console/angular.json +++ b/console/angular.json @@ -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" diff --git a/console/package.json b/console/package.json index 9bed6d4452..70390d87e1 100644 --- a/console/package.json +++ b/console/package.json @@ -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", diff --git a/console/src/app/components/framework-autocomplete/framework-autocomplete.component.html b/console/src/app/components/framework-autocomplete/framework-autocomplete.component.html new file mode 100644 index 0000000000..c66b61b727 --- /dev/null +++ b/console/src/app/components/framework-autocomplete/framework-autocomplete.component.html @@ -0,0 +1,35 @@ +
+ + {{ 'QUICKSTART.FRAMEWORK' | translate }} + + + + + + + +
+
+
+ + +
+ + + {{ framework.title }} +
+
+
+ +
+
+
+ + + {{ 'QUICKSTART.FRAMEWORK_OTHER' | translate }} +
+
+
+
+
+
diff --git a/console/src/app/components/framework-autocomplete/framework-autocomplete.component.scss b/console/src/app/components/framework-autocomplete/framework-autocomplete.component.scss new file mode 100644 index 0000000000..ed0eaf2c2d --- /dev/null +++ b/console/src/app/components/framework-autocomplete/framework-autocomplete.component.scss @@ -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); + } + } + } +} diff --git a/console/src/app/components/framework-autocomplete/framework-autocomplete.component.spec.ts b/console/src/app/components/framework-autocomplete/framework-autocomplete.component.spec.ts new file mode 100644 index 0000000000..074d5148cf --- /dev/null +++ b/console/src/app/components/framework-autocomplete/framework-autocomplete.component.spec.ts @@ -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; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [FrameworkAutocompleteComponent], + }); + fixture = TestBed.createComponent(FrameworkAutocompleteComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/console/src/app/components/framework-autocomplete/framework-autocomplete.component.ts b/console/src/app/components/framework-autocomplete/framework-autocomplete.component.ts new file mode 100644 index 0000000000..ec62d4335d --- /dev/null +++ b/console/src/app/components/framework-autocomplete/framework-autocomplete.component.ts @@ -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 = new EventEmitter(); + public filteredOptions: Observable = 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); + } +} diff --git a/console/src/app/components/framework-change/framework-change-dialog.component.html b/console/src/app/components/framework-change/framework-change-dialog.component.html new file mode 100644 index 0000000000..654d55fb52 --- /dev/null +++ b/console/src/app/components/framework-change/framework-change-dialog.component.html @@ -0,0 +1,17 @@ +

{{ 'QUICKSTART.DIALOG.CHANGE.TITLE' | translate }}

+ + {{ 'QUICKSTART.DIALOG.CHANGE.DESCRIPTION' | translate }} +
+ +
+
+
+ + + + +
diff --git a/console/src/app/components/framework-change/framework-change-dialog.component.scss b/console/src/app/components/framework-change/framework-change-dialog.component.scss new file mode 100644 index 0000000000..c01c948c74 --- /dev/null +++ b/console/src/app/components/framework-change/framework-change-dialog.component.scss @@ -0,0 +1,10 @@ +.framework-change-block { + display: flex; + flex-direction: column; + align-items: stretch; +} + +.actions { + display: flex; + justify-content: space-between; +} diff --git a/console/src/app/components/framework-change/framework-change-dialog.component.ts b/console/src/app/components/framework-change/framework-change-dialog.component.ts new file mode 100644 index 0000000000..228d593cf9 --- /dev/null +++ b/console/src/app/components/framework-change/framework-change-dialog.component.ts @@ -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(undefined); + + constructor( + public dialogRef: MatDialogRef, + @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()); + } +} diff --git a/console/src/app/components/framework-change/framework-change.component.html b/console/src/app/components/framework-change/framework-change.component.html new file mode 100644 index 0000000000..6c65249d2d --- /dev/null +++ b/console/src/app/components/framework-change/framework-change.component.html @@ -0,0 +1,14 @@ +
+
+
+
+ + +
+ {{ framework.title }} +
+ +
+
diff --git a/console/src/app/components/framework-change/framework-change.component.scss b/console/src/app/components/framework-change/framework-change.component.scss new file mode 100644 index 0000000000..e89ba17ac8 --- /dev/null +++ b/console/src/app/components/framework-change/framework-change.component.scss @@ -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; + } + } + } + } + } +} diff --git a/console/src/app/components/framework-change/framework-change.component.spec.ts b/console/src/app/components/framework-change/framework-change.component.spec.ts new file mode 100644 index 0000000000..a12074e874 --- /dev/null +++ b/console/src/app/components/framework-change/framework-change.component.spec.ts @@ -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; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [FrameworkChangeComponent], + }); + fixture = TestBed.createComponent(FrameworkChangeComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/console/src/app/components/framework-change/framework-change.component.ts b/console/src/app/components/framework-change/framework-change.component.ts new file mode 100644 index 0000000000..4efaf42761 --- /dev/null +++ b/console/src/app/components/framework-change/framework-change.component.ts @@ -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 = new Subject(); + public framework: BehaviorSubject = new BehaviorSubject(undefined); + public showFrameworkAutocomplete = signal(false); + @Output() public frameworkChanged: EventEmitter = 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); + } + }); + } +} diff --git a/console/src/app/components/oidc-configuration/oidc-configuration.component.html b/console/src/app/components/oidc-configuration/oidc-configuration.component.html new file mode 100644 index 0000000000..ecc3929a82 --- /dev/null +++ b/console/src/app/components/oidc-configuration/oidc-configuration.component.html @@ -0,0 +1,53 @@ +
+
+ + {{ 'APP.NAME' | translate }} + + + {{ name }} + + +
+ +
+ + {{ 'APP.TYPE' | translate }} + + + {{ 'APP.OIDC.APPTYPE.' + configuration.appType | translate }} + +
+
+ + {{ 'APP.GRANT' | translate }} + + + [ + {{ 'APP.OIDC.GRANT.' + element | translate }} + {{ i < configuration.grantTypesList.length - 1 ? ', ' : '' }} ] + +
+
+ + {{ 'APP.OIDC.RESPONSETYPE' | translate }} + + + [ + {{ 'APP.OIDC.RESPONSE.' + element | translate }} + {{ i < configuration.responseTypesList.length - 1 ? ', ' : '' }} ] + +
+ +
+ + {{ 'APP.AUTHMETHOD' | translate }} + + + + {{ 'APP.OIDC.AUTHMETHOD.' + configuration.authMethodType | translate }} + + +
+
diff --git a/console/src/app/components/oidc-configuration/oidc-configuration.component.scss b/console/src/app/components/oidc-configuration/oidc-configuration.component.scss new file mode 100644 index 0000000000..4bc7646b81 --- /dev/null +++ b/console/src/app/components/oidc-configuration/oidc-configuration.component.scss @@ -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; + } + } + } +} diff --git a/console/src/app/components/oidc-configuration/oidc-configuration.component.spec.ts b/console/src/app/components/oidc-configuration/oidc-configuration.component.spec.ts new file mode 100644 index 0000000000..6155ba5693 --- /dev/null +++ b/console/src/app/components/oidc-configuration/oidc-configuration.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { QuickstartComponent } from './quickstart.component'; + +describe('QuickstartComponent', () => { + let component: QuickstartComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [QuickstartComponent], + }); + fixture = TestBed.createComponent(QuickstartComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/console/src/app/components/oidc-configuration/oidc-configuration.component.ts b/console/src/app/components/oidc-configuration/oidc-configuration.component.ts new file mode 100644 index 0000000000..c020bd1caa --- /dev/null +++ b/console/src/app/components/oidc-configuration/oidc-configuration.component.ts @@ -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 = new EventEmitter(); +} diff --git a/console/src/app/components/quickstart/quickstart.component.html b/console/src/app/components/quickstart/quickstart.component.html new file mode 100644 index 0000000000..18e0172692 --- /dev/null +++ b/console/src/app/components/quickstart/quickstart.component.html @@ -0,0 +1,23 @@ +
+
+

{{ 'QUICKSTART.TITLE' | translate }}

+

{{ 'QUICKSTART.DESCRIPTION' | translate }}

+ + +
+
+ + + {{ framework.title }} + {{ framework.title }} + + +
+
diff --git a/console/src/app/components/quickstart/quickstart.component.scss b/console/src/app/components/quickstart/quickstart.component.scss new file mode 100644 index 0000000000..5b1ecb5717 --- /dev/null +++ b/console/src/app/components/quickstart/quickstart.component.scss @@ -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); + } + } + } + } +} diff --git a/console/src/app/components/quickstart/quickstart.component.spec.ts b/console/src/app/components/quickstart/quickstart.component.spec.ts new file mode 100644 index 0000000000..6155ba5693 --- /dev/null +++ b/console/src/app/components/quickstart/quickstart.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { QuickstartComponent } from './quickstart.component'; + +describe('QuickstartComponent', () => { + let component: QuickstartComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [QuickstartComponent], + }); + fixture = TestBed.createComponent(QuickstartComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/console/src/app/components/quickstart/quickstart.component.ts b/console/src/app/components/quickstart/quickstart.component.ts new file mode 100644 index 0000000000..fcac992562 --- /dev/null +++ b/console/src/app/components/quickstart/quickstart.component.ts @@ -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}`, + }; + }); +} diff --git a/console/src/app/modules/info-section/info-section.component.html b/console/src/app/modules/info-section/info-section.component.html index 7792bcb12a..277944ba51 100644 --- a/console/src/app/modules/info-section/info-section.component.html +++ b/console/src/app/modules/info-section/info-section.component.html @@ -1,10 +1,17 @@
+
diff --git a/console/src/app/modules/search-project-autocomplete/search-project-autocomplete.component.ts b/console/src/app/modules/search-project-autocomplete/search-project-autocomplete.component.ts index 4dbece43e2..a3886b2ea1 100644 --- a/console/src/app/modules/search-project-autocomplete/search-project-autocomplete.component.ts +++ b/console/src/app/modules/search-project-autocomplete/search-project-autocomplete.component.ts @@ -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 = new EventEmitter(); private unsubscribed$: Subject = 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, }); } diff --git a/console/src/app/pages/app-create/app-create.component.html b/console/src/app/pages/app-create/app-create.component.html index 3ea6614d82..4cc21eae1c 100644 --- a/console/src/app/pages/app-create/app-create.component.html +++ b/console/src/app/pages/app-create/app-create.component.html @@ -1,25 +1,53 @@
-

{{ 'APP.PAGES.CREATE_SELECT_PROJECT' | translate }}

+ +
+
+

{{ 'APP.PAGES.CREATE_SELECT_PROJECT' | translate }}

- + + +

{{ 'APP.PAGES.CREATE_NEW_PROJECT' | translate }}

+
+ +
+

{{ 'QUICKSTART.SELECT_FRAMEWORK' | translate }}

+ + +
+
+ + {{ error() }} - - -
diff --git a/console/src/app/pages/app-create/app-create.component.scss b/console/src/app/pages/app-create/app-create.component.scss index d8d3127bd8..dab7d177ba 100644 --- a/console/src/app/pages/app-create/app-create.component.scss +++ b/console/src/app/pages/app-create/app-create.component.scss @@ -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; - } } diff --git a/console/src/app/pages/app-create/app-create.component.spec.ts b/console/src/app/pages/app-create/app-create.component.spec.ts index fc3c776c1d..265eaafc70 100644 --- a/console/src/app/pages/app-create/app-create.component.spec.ts +++ b/console/src/app/pages/app-create/app-create.component.spec.ts @@ -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; +describe('AppCreateComponent', () => { + let component: AppCreateComponent; + let fixture: ComponentFixture; beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - declarations: [OrgCreateComponent], + declarations: [AppCreateComponent], }).compileComponents(); })); beforeEach(() => { - fixture = TestBed.createComponent(OrgCreateComponent); + fixture = TestBed.createComponent(AppCreateComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/console/src/app/pages/app-create/app-create.component.ts b/console/src/app/pages/app-create/app-create.component.ts index b4d8045b77..8dcfeb7a35 100644 --- a/console/src/app/pages/app-create/app-create.component.ts +++ b/console/src/app/pages/app-create/app-create.component.ts @@ -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(undefined); + public customFramework = signal(false); + public initialParam = signal(''); + public destroy$: Subject = 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); + }); + } } diff --git a/console/src/app/pages/app-create/app-create.module.ts b/console/src/app/pages/app-create/app-create.module.ts index fa7a0aa4de..8e9ea1e7ff 100644 --- a/console/src/app/pages/app-create/app-create.module.ts +++ b/console/src/app/pages/app-create/app-create.module.ts @@ -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 {} diff --git a/console/src/app/pages/home/home.component.html b/console/src/app/pages/home/home.component.html index 58fdbbd65b..2770252bda 100644 --- a/console/src/app/pages/home/home.component.html +++ b/console/src/app/pages/home/home.component.html @@ -2,6 +2,8 @@

{{ 'HOME.WELCOME' | translate }}

+ + diff --git a/console/src/app/pages/home/home.component.scss b/console/src/app/pages/home/home.component.scss index 8dac8e43c5..9c410ed3e4 100644 --- a/console/src/app/pages/home/home.component.scss +++ b/console/src/app/pages/home/home.component.scss @@ -19,6 +19,7 @@ .home-title { font-size: 2rem; margin-bottom: 1rem; + margin-top: 3rem; } .home-wrapper { diff --git a/console/src/app/pages/home/home.module.ts b/console/src/app/pages/home/home.module.ts index 5184b40112..2b750dd820 100644 --- a/console/src/app/pages/home/home.module.ts +++ b/console/src/app/pages/home/home.module.ts @@ -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, diff --git a/console/src/app/pages/projects/apps/app-detail/app-detail.component.html b/console/src/app/pages/projects/apps/app-detail/app-detail.component.html index 25c657a120..e98dc18453 100644 --- a/console/src/app/pages/projects/apps/app-detail/app-detail.component.html +++ b/console/src/app/pages/projects/apps/app-detail/app-detail.component.html @@ -57,6 +57,13 @@
+ +
+ {{ 'APP.PAGES.JUMPTOPROJECT' | translate }} + {{ 'ACTIONS.CONFIGURE' | translate }} +
+
+
(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; diff --git a/console/src/app/pages/projects/apps/apps-routing.module.ts b/console/src/app/pages/projects/apps/apps-routing.module.ts index 1ab252dcf0..ddd3171458 100644 --- a/console/src/app/pages/projects/apps/apps-routing.module.ts +++ b/console/src/app/pages/projects/apps/apps-routing.module.ts @@ -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, diff --git a/console/src/app/pages/projects/apps/apps.module.ts b/console/src/app/pages/projects/apps/apps.module.ts index 5b3bf7fe07..6f0471b488 100644 --- a/console/src/app/pages/projects/apps/apps.module.ts +++ b/console/src/app/pages/projects/apps/apps.module.ts @@ -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, diff --git a/console/src/app/pages/projects/apps/integrate/integrate.component.html b/console/src/app/pages/projects/apps/integrate/integrate.component.html new file mode 100644 index 0000000000..4b2b567eb3 --- /dev/null +++ b/console/src/app/pages/projects/apps/integrate/integrate.component.html @@ -0,0 +1,138 @@ +
+
+
+
+ + {{ 'APP.PAGES.CREATE' | translate }} + +
+
+
+ +
+
+

{{ 'QUICKSTART.ALMOSTDONE' | translate }}

+

{{ 'QUICKSTART.REVIEWCONFIGURATION_DESCRIPTION' | translate: { value: framework()?.title } }}

+ +
+
+ +
+
+ {{ 'PROJECT.PAGES.TITLE' | translate }} + {{ projectName$ | async }} +
+ + +
+
+
+ + {{ + 'QUICKSTART.DUPLICATEAPPRENAME' | translate + }} + + + +

+ {{ desc }} +

+
+
+ + + + + {{ 'QUICKSTART.DEVMODEWARN' | translate }} + + + + + {{ 'APP.OIDC.REDIRECTDESCRIPTIONNATIVE' | translate }} + + + + + {{ 'APP.OIDC.REDIRECTDESCRIPTIONWEB' | translate }} + + + + + + + + + +
+
+ +
+ +
+
+
+
diff --git a/console/src/app/pages/projects/apps/integrate/integrate.component.scss b/console/src/app/pages/projects/apps/integrate/integrate.component.scss new file mode 100644 index 0000000000..7258f4e335 --- /dev/null +++ b/console/src/app/pages/projects/apps/integrate/integrate.component.scss @@ -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; + } +} diff --git a/console/src/app/pages/projects/apps/integrate/integrate.component.spec.ts b/console/src/app/pages/projects/apps/integrate/integrate.component.spec.ts new file mode 100644 index 0000000000..88c43d7bc0 --- /dev/null +++ b/console/src/app/pages/projects/apps/integrate/integrate.component.spec.ts @@ -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; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [IntegrateAppComponent], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(IntegrateAppComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/console/src/app/pages/projects/apps/integrate/integrate.component.ts b/console/src/app/pages/projects/apps/integrate/integrate.component.ts new file mode 100644 index 0000000000..da249d6304 --- /dev/null +++ b/console/src/app/pages/projects/apps/integrate/integrate.component.ts @@ -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 = new Subject(); + public projectId: string = ''; + public loading: boolean = false; + public InfoSectionType: any = InfoSectionType; + public framework = signal(undefined); + public showRenameWarning: BehaviorSubject = new BehaviorSubject(false); + public oidcAppRequest: BehaviorSubject = new BehaviorSubject(new AddOIDCAppRequest()); + + public OIDCAppType: any = OIDCAppType; + public requestRedirectValuesSubject$: Subject = 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); + } +} diff --git a/console/src/app/pipes/localized-date-pipe/localized-date.pipe.ts b/console/src/app/pipes/localized-date-pipe/localized-date.pipe.ts index 6030dc463c..5094b67917 100644 --- a/console/src/app/pipes/localized-date-pipe/localized-date.pipe.ts +++ b/console/src/app/pipes/localized-date-pipe/localized-date.pipe.ts @@ -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({ diff --git a/console/src/app/utils/framework.ts b/console/src/app/utils/framework.ts new file mode 100644 index 0000000000..2869a81a5e --- /dev/null +++ b/console/src/app/utils/framework.ts @@ -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']), +}; diff --git a/console/src/assets/i18n/bg.json b/console/src/assets/i18n/bg.json index c1f99d1ed2..acd03a7ddd 100644 --- a/console/src/assets/i18n/bg.json +++ b/console/src/assets/i18n/bg.json @@ -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": { diff --git a/console/src/assets/i18n/cs.json b/console/src/assets/i18n/cs.json index 54be4f2f99..21e5bd163f 100644 --- a/console/src/assets/i18n/cs.json +++ b/console/src/assets/i18n/cs.json @@ -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": { diff --git a/console/src/assets/i18n/de.json b/console/src/assets/i18n/de.json index 56ecafe8ba..688abad7a3 100644 --- a/console/src/assets/i18n/de.json +++ b/console/src/assets/i18n/de.json @@ -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": { diff --git a/console/src/assets/i18n/en.json b/console/src/assets/i18n/en.json index 0e611c23de..fbee0e8755 100644 --- a/console/src/assets/i18n/en.json +++ b/console/src/assets/i18n/en.json @@ -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 here.", + "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": { diff --git a/console/src/assets/i18n/es.json b/console/src/assets/i18n/es.json index 2fe15802c7..2e629e4857 100644 --- a/console/src/assets/i18n/es.json +++ b/console/src/assets/i18n/es.json @@ -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": { diff --git a/console/src/assets/i18n/fr.json b/console/src/assets/i18n/fr.json index 14e09e8784..91827506a4 100644 --- a/console/src/assets/i18n/fr.json +++ b/console/src/assets/i18n/fr.json @@ -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": { diff --git a/console/src/assets/i18n/it.json b/console/src/assets/i18n/it.json index 97ee129f72..6cdd88f4a9 100644 --- a/console/src/assets/i18n/it.json +++ b/console/src/assets/i18n/it.json @@ -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": { diff --git a/console/src/assets/i18n/ja.json b/console/src/assets/i18n/ja.json index 838ed74857..8dc013b258 100644 --- a/console/src/assets/i18n/ja.json +++ b/console/src/assets/i18n/ja.json @@ -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": { diff --git a/console/src/assets/i18n/mk.json b/console/src/assets/i18n/mk.json index fd8ace082c..c9aa1065fe 100644 --- a/console/src/assets/i18n/mk.json +++ b/console/src/assets/i18n/mk.json @@ -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": { diff --git a/console/src/assets/i18n/nl.json b/console/src/assets/i18n/nl.json index 615e88d6f6..f99aaf0bff 100644 --- a/console/src/assets/i18n/nl.json +++ b/console/src/assets/i18n/nl.json @@ -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": { diff --git a/console/src/assets/i18n/pl.json b/console/src/assets/i18n/pl.json index fd20e13efc..e9b7962a86 100644 --- a/console/src/assets/i18n/pl.json +++ b/console/src/assets/i18n/pl.json @@ -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": { diff --git a/console/src/assets/i18n/pt.json b/console/src/assets/i18n/pt.json index 52764d4b95..a1f71c7573 100644 --- a/console/src/assets/i18n/pt.json +++ b/console/src/assets/i18n/pt.json @@ -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": { diff --git a/console/src/assets/i18n/ru.json b/console/src/assets/i18n/ru.json index c3f1d76f64..663efe19b9 100644 --- a/console/src/assets/i18n/ru.json +++ b/console/src/assets/i18n/ru.json @@ -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": { diff --git a/console/src/assets/i18n/zh.json b/console/src/assets/i18n/zh.json index f7742d9bfe..681aa22b70 100644 --- a/console/src/assets/i18n/zh.json +++ b/console/src/assets/i18n/zh.json @@ -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": { diff --git a/console/src/component-themes.scss b/console/src/component-themes.scss index aecbde2870..afa97269fb 100644 --- a/console/src/component-themes.scss +++ b/console/src/component-themes.scss @@ -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); } diff --git a/console/src/styles.scss b/console/src/styles.scss index f2819b7c0c..4752d7fc59 100644 --- a/console/src/styles.scss +++ b/console/src/styles.scss @@ -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; diff --git a/console/src/styles/toast.scss b/console/src/styles/toast.scss index a3af4b4ab1..79a566a323 100644 --- a/console/src/styles/toast.scss +++ b/console/src/styles/toast.scss @@ -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; diff --git a/console/tsconfig.json b/console/tsconfig.json index 1301bf238f..6a02495fee 100644 --- a/console/tsconfig.json +++ b/console/tsconfig.json @@ -16,6 +16,8 @@ "experimentalDecorators": true, "moduleResolution": "node", "importHelpers": true, + "resolveJsonModule": true, + "esModuleInterop": true, "target": "ES2022", "module": "ES2022", "useDefineForClassFields": false, diff --git a/console/yarn.lock b/console/yarn.lock index e8fdeb5102..de6a28e268 100644 --- a/console/yarn.lock +++ b/console/yarn.lock @@ -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== diff --git a/docs/docs/sdk-examples/introduction.mdx b/docs/docs/sdk-examples/introduction.mdx index 5ee006c6c5..d3f279bbba 100644 --- a/docs/docs/sdk-examples/introduction.mdx +++ b/docs/docs/sdk-examples/introduction.mdx @@ -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 -
- - - - - - - - - - - - - - - - - - - -
+ ### OIDC Libraries diff --git a/docs/frameworks.json b/docs/frameworks.json new file mode 100644 index 0000000000..0bf3e9d012 --- /dev/null +++ b/docs/frameworks.json @@ -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 + } +] diff --git a/docs/src/components/frameworks.jsx b/docs/src/components/frameworks.jsx new file mode 100644 index 0000000000..ca402ec7ff --- /dev/null +++ b/docs/src/components/frameworks.jsx @@ -0,0 +1,21 @@ +import React from "react"; +import { Tile } from "./tile"; +import frameworks from "../../frameworks.json"; + +export function Frameworks({}) { + return ( +
+ {frameworks.map((framework) => { + return ( + + ); + })} +
+ ); +}