mirror of
https://github.com/andwati/zitadel.git
synced 2026-11-08 15:15:12 +00:00
fix(console): refine onboarding styles and behavior, i18n (#5616)
* fix: console onboarding styles and behavior, i18n * action-row classes * fix: only consider real user events in the onboarding view (#5617) * fix(onboarding): only human events * explicitly exclude system api events --------- Co-authored-by: Max Peintner <[email protected]> --------- Co-authored-by: Elio Bischof <[email protected]> Co-authored-by: Elio Bischof <[email protected]>
This commit is contained in:
co-authored by
Elio Bischof
Elio Bischof
parent
698f46fe6a
commit
2ee7b9c381
@@ -189,6 +189,7 @@
|
||||
border: none;
|
||||
padding: 2px;
|
||||
border-radius: 50vw;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: if($is-dark-theme, #ffffff, $primary-color);
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
margin-right: 1rem;
|
||||
background-color: if($is-dark-theme, map-get($background, state), #e4e7e4);
|
||||
box-shadow: 0 0 3px #0000001a;
|
||||
border: 1px solid rgba(#8795a1, 0.2);
|
||||
|
||||
i {
|
||||
font-size: 1rem;
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
<div class="onboarding-header">
|
||||
<h1 class="title" data-e2e="authenticated-welcome">{{ 'HOME.WELCOME' | translate }}</h1>
|
||||
|
||||
<p class="desc cnsl-secondary-text">{{ 'ONBOARDING.DESCRIPTION' | translate }}</p>
|
||||
<h2 class="desc">{{ 'ONBOARDING.DESCRIPTION' | translate }}</h2>
|
||||
|
||||
<ng-container *ngIf="!adminService.hideOnboarding && (adminService.progressAllDone | async) === false">
|
||||
<div class="onboarding-progress-bar-wrapper">
|
||||
@@ -38,16 +36,34 @@
|
||||
</div>
|
||||
|
||||
<div class="action-content">
|
||||
<div class="text-block">
|
||||
<span class="name">{{ 'ONBOARDING.EVENTS.' + action[0] + '.title' | translate }}</span>
|
||||
<span class="cnsl-secondary-text description">{{
|
||||
'ONBOARDING.EVENTS.' + action[0] + '.description' | translate
|
||||
}}</span>
|
||||
<div class="action-content-row">
|
||||
<div
|
||||
class="icon-wrapper"
|
||||
[ngStyle]="{
|
||||
background: (themeService.isDarkTheme | async) ? action[1].darkcolor + 50 : action[1].lightcolor + 50
|
||||
}"
|
||||
>
|
||||
<div
|
||||
class="inner"
|
||||
[ngStyle]="{
|
||||
background: (themeService.isDarkTheme | async) ? action[1].darkcolor : action[1].lightcolor,
|
||||
color: (themeService.isDarkTheme | async) ? action[1].lightcolor : action[1].darkcolor
|
||||
}"
|
||||
>
|
||||
<i class="{{ action[1].iconClasses }}"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-block">
|
||||
<span class="name">{{ 'ONBOARDING.EVENTS.' + action[0] + '.title' | translate }}</span>
|
||||
<span class="cnsl-secondary-text description">{{
|
||||
'ONBOARDING.EVENTS.' + action[0] + '.description' | translate
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span class="fill-space"></span>
|
||||
<div class="action-row">
|
||||
<span>{{ 'ACTIONS.SETUP' | translate }}</span>
|
||||
<span>{{ 'ONBOARDING.EVENTS.' + action[0] + '.action' | translate }}</span>
|
||||
<mat-icon class="icon">keyboard_arrow_right</mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -23,14 +23,10 @@
|
||||
flex-direction: column;
|
||||
margin-bottom: 2rem;
|
||||
|
||||
.title {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.desc {
|
||||
font-size: 14px;
|
||||
font-size: 1.2rem;
|
||||
margin-top: 0;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.onboarding-progress-bar-wrapper {
|
||||
@@ -71,7 +67,6 @@
|
||||
|
||||
.action-card {
|
||||
position: relative;
|
||||
z-index: 100;
|
||||
margin: 1rem;
|
||||
flex-basis: 270px;
|
||||
text-decoration: none;
|
||||
@@ -91,18 +86,46 @@
|
||||
height: 100%;
|
||||
padding-right: 0.5rem;
|
||||
|
||||
.text-block {
|
||||
.action-content-row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: map-get($foreground, text);
|
||||
padding-top: 1rem;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
|
||||
.name {
|
||||
margin-bottom: 1rem;
|
||||
.icon-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 0.5rem;
|
||||
border-radius: 50vw;
|
||||
flex-shrink: 0;
|
||||
margin-right: 1rem;
|
||||
margin-top: 1rem;
|
||||
|
||||
.inner {
|
||||
border-radius: 50vw;
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 14px;
|
||||
.text-block {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: map-get($foreground, text);
|
||||
padding-top: 1rem;
|
||||
flex: 1;
|
||||
|
||||
.name {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,6 +134,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.done {
|
||||
.action-content {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
.state-circle {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
@@ -126,6 +155,7 @@
|
||||
margin-right: 1rem;
|
||||
background-color: if($is-dark-theme, map-get($background, state), #e4e7e4);
|
||||
box-shadow: 0 0 3px #0000001a;
|
||||
border: 1px solid rgba(#8795a1, 0.2);
|
||||
|
||||
i {
|
||||
font-size: 1rem;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { AdminService } from 'src/app/services/admin.service';
|
||||
import { ThemeService } from 'src/app/services/theme.service';
|
||||
import { ONBOARDING_EVENTS } from 'src/app/utils/onboarding';
|
||||
|
||||
@Component({
|
||||
@@ -10,7 +11,7 @@ import { ONBOARDING_EVENTS } from 'src/app/utils/onboarding';
|
||||
export class OnboardingComponent {
|
||||
public actions = this.adminService.progressEvents;
|
||||
|
||||
constructor(public adminService: AdminService) {
|
||||
constructor(public adminService: AdminService, public themeService: ThemeService) {
|
||||
this.adminService.loadEvents.next(ONBOARDING_EVENTS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,11 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
h2 {
|
||||
text-transform: uppercase;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.shortcut-btn {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<div class="max-width-container">
|
||||
<h1 class="home-title" data-e2e="authenticated-welcome">{{ 'HOME.WELCOME' | translate }}</h1>
|
||||
|
||||
<div class="home-wrapper enlarged-container">
|
||||
<ng-container *ngIf="['iam.read$'] | hasRole | async; else defaultHome">
|
||||
<cnsl-onboarding></cnsl-onboarding>
|
||||
@@ -10,24 +12,66 @@
|
||||
<p class="disclaimer cnsl-secondary-text">{{ 'HOME.DISCLAIMER' | translate }}</p>
|
||||
|
||||
<span class="fill-space"></span>
|
||||
<h2 class="desc">{{ 'ONBOARDING.MOREDESCRIPTION' | translate }}</h2>
|
||||
|
||||
<div class="home-grid-container">
|
||||
<a href="https://zitadel.com/docs" target="_blank" rel="noreferrer" class="grid-item blue">
|
||||
<div class="grid-item-avatar blue">
|
||||
<i class="icon las la-file-alt"></i>
|
||||
<a href="https://zitadel.com/docs" target="_blank" rel="noreferrer" class="grid-item">
|
||||
<div
|
||||
class="icon-wrapper"
|
||||
[ngStyle]="{
|
||||
background: (themeService.isDarkTheme | async) ? bluedark + 50 : bluelight + 50
|
||||
}"
|
||||
>
|
||||
<div
|
||||
class="inner"
|
||||
[ngStyle]="{
|
||||
background: (themeService.isDarkTheme | async) ? bluedark : bluelight,
|
||||
color: (themeService.isDarkTheme | async) ? bluelight : bluedark
|
||||
}"
|
||||
>
|
||||
<i class="las la-file-alt"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span>{{ 'HOME.DOCUMENTATION.TITLE' | translate }}</span>
|
||||
</a>
|
||||
|
||||
<a href="https://zitadel.com/docs/guides/start/quickstart" target="_blank" rel="noreferrer" class="grid-item green">
|
||||
<div class="grid-item-avatar green">
|
||||
<i class="icon las la-play"></i>
|
||||
<a href="https://zitadel.com/docs/guides/start/quickstart" target="_blank" rel="noreferrer" class="grid-item">
|
||||
<div
|
||||
class="icon-wrapper"
|
||||
[ngStyle]="{
|
||||
background: (themeService.isDarkTheme | async) ? greendark + 50 : greenlight + 50
|
||||
}"
|
||||
>
|
||||
<div
|
||||
class="inner"
|
||||
[ngStyle]="{
|
||||
background: (themeService.isDarkTheme | async) ? greendark : greenlight,
|
||||
color: (themeService.isDarkTheme | async) ? greenlight : greendark
|
||||
}"
|
||||
>
|
||||
<i class="las la-play"></i>
|
||||
</div>
|
||||
</div>
|
||||
<span>{{ 'HOME.GETSTARTED.TITLE' | translate }}</span>
|
||||
</a>
|
||||
|
||||
<a href="https://zitadel.com/docs/examples/introduction" target="_blank" rel="noreferrer" class="grid-item green">
|
||||
<div class="grid-item-avatar green">
|
||||
<i class="icon las la-play"></i>
|
||||
<a href="https://zitadel.com/docs/examples/introduction" target="_blank" rel="noreferrer" class="grid-item">
|
||||
<div
|
||||
class="icon-wrapper"
|
||||
[ngStyle]="{
|
||||
background: (themeService.isDarkTheme | async) ? cyandark + 50 : cyanlight + 50
|
||||
}"
|
||||
>
|
||||
<div
|
||||
class="inner"
|
||||
[ngStyle]="{
|
||||
background: (themeService.isDarkTheme | async) ? cyandark : cyanlight,
|
||||
color: (themeService.isDarkTheme | async) ? cyanlight : cyandark
|
||||
}"
|
||||
>
|
||||
<i class="las la-play"></i>
|
||||
</div>
|
||||
</div>
|
||||
<span>{{ 'HOME.QUICKSTARTS.TITLE' | translate }}</span>
|
||||
</a>
|
||||
|
||||
@@ -18,6 +18,11 @@
|
||||
$border-color: if($is-dark-theme, rgba(#8795a1, 0.2), rgba(#8795a1, 0.2));
|
||||
$border-selected-color: if($is-dark-theme, #fff, #000);
|
||||
|
||||
.home-title {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.home-wrapper {
|
||||
position: relative;
|
||||
display: flex;
|
||||
@@ -34,6 +39,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
.desc {
|
||||
font-size: 1.2rem;
|
||||
margin-top: 0;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.home-grid-container {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
@@ -58,24 +69,15 @@
|
||||
font-size: 14px;
|
||||
background-color: $card-background-color;
|
||||
border: 1px solid $border-color;
|
||||
border-radius: 1rem;
|
||||
border-radius: 0.5rem;
|
||||
margin: 0;
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
box-shadow: 0 0 15px #0000001a;
|
||||
color: inherit;
|
||||
|
||||
&.edit-state {
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
&.green {
|
||||
background: linear-gradient(40deg, #059669 30%, #047857);
|
||||
}
|
||||
|
||||
&.blue {
|
||||
background: linear-gradient(40deg, #3b82f6 30%, #4f46e5);
|
||||
}
|
||||
|
||||
.grid-item-avatar {
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
@@ -118,6 +120,26 @@
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 0.5rem;
|
||||
border-radius: 50vw;
|
||||
flex-shrink: 0;
|
||||
margin-right: 1rem;
|
||||
|
||||
.inner {
|
||||
border-radius: 50vw;
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service';
|
||||
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
|
||||
import { ThemeService } from 'src/app/services/theme.service';
|
||||
import { COLORS } from 'src/app/utils/color';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-home',
|
||||
@@ -8,9 +10,18 @@ import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
|
||||
styleUrls: ['./home.component.scss'],
|
||||
})
|
||||
export class HomeComponent {
|
||||
public greendark: string = COLORS[6][700];
|
||||
public greenlight = COLORS[6][200];
|
||||
|
||||
public cyandark: string = COLORS[7][700];
|
||||
public cyanlight = COLORS[7][200];
|
||||
|
||||
public bluedark: string = COLORS[9][700];
|
||||
public bluelight = COLORS[9][200];
|
||||
|
||||
public dark: boolean = true;
|
||||
|
||||
constructor(public authService: GrpcAuthService, breadcrumbService: BreadcrumbService) {
|
||||
constructor(public authService: GrpcAuthService, breadcrumbService: BreadcrumbService, public themeService: ThemeService) {
|
||||
const bread: Breadcrumb = {
|
||||
type: BreadcrumbType.ORG,
|
||||
routerLink: ['/org'],
|
||||
|
||||
@@ -267,9 +267,20 @@ export interface OnboardingActions {
|
||||
oneof: string[];
|
||||
link: string | string[];
|
||||
fragment?: string | undefined;
|
||||
iconClasses?: string;
|
||||
darkcolor: string;
|
||||
lightcolor: string;
|
||||
}
|
||||
|
||||
type OnboardingEvent = { order: number; link: string; fragment: string | undefined; event: Event.AsObject | undefined };
|
||||
type OnboardingEvent = {
|
||||
order: number;
|
||||
link: string;
|
||||
fragment: string | undefined;
|
||||
event: Event.AsObject | undefined;
|
||||
iconClasses?: string;
|
||||
darkcolor: string;
|
||||
lightcolor: string;
|
||||
};
|
||||
type OnboardingEventEntries = Array<[string, OnboardingEvent]> | [];
|
||||
|
||||
@Injectable({
|
||||
@@ -286,14 +297,30 @@ export class AdminService {
|
||||
const eventsReq = new ListEventsRequest().setAsc(true).setEventTypesList(searchForTypes).setAsc(false);
|
||||
return from(this.listEvents(eventsReq)).pipe(
|
||||
map((events) => {
|
||||
const el = events.toObject().eventsList.filter((e) => e.editor?.service !== 'System-API');
|
||||
const el = events.toObject().eventsList.filter((e) => e.editor?.service !== 'System-API' && e.editor?.userId);
|
||||
|
||||
let obj: { [type: string]: OnboardingEvent } = {};
|
||||
actions.map((action) => {
|
||||
const filtered = el.filter((event) => event.type?.type && action.oneof.includes(event.type.type));
|
||||
(obj as any)[action.eventType] = filtered.length
|
||||
? { order: action.order, link: action.link, fragment: action.fragment, event: filtered[0] }
|
||||
: { order: action.order, link: action.link, fragment: action.fragment, event: undefined };
|
||||
? {
|
||||
order: action.order,
|
||||
link: action.link,
|
||||
fragment: action.fragment,
|
||||
event: filtered[0],
|
||||
iconClasses: action.iconClasses,
|
||||
darkcolor: action.darkcolor,
|
||||
lightcolor: action.lightcolor,
|
||||
}
|
||||
: {
|
||||
order: action.order,
|
||||
link: action.link,
|
||||
fragment: action.fragment,
|
||||
event: undefined,
|
||||
iconClasses: action.iconClasses,
|
||||
darkcolor: action.darkcolor,
|
||||
lightcolor: action.lightcolor,
|
||||
};
|
||||
});
|
||||
|
||||
const toArray = Object.entries(obj).sort(([key0, a], [key1, b]) => a.order - b.order);
|
||||
|
||||
@@ -1,16 +1,70 @@
|
||||
import { OnboardingActions } from '../services/admin.service';
|
||||
import { COLORS } from './color';
|
||||
|
||||
const reddark: string = COLORS[0][700];
|
||||
const redlight = COLORS[0][200];
|
||||
|
||||
const yellowdark: string = COLORS[3][700];
|
||||
const yellowlight = COLORS[3][200];
|
||||
|
||||
const greendark: string = COLORS[6][700];
|
||||
const greenlight = COLORS[6][200];
|
||||
|
||||
const bluedark: string = COLORS[9][700];
|
||||
const bluelight = COLORS[9][200];
|
||||
|
||||
const purpledark: string = COLORS[12][700];
|
||||
const purplelight = COLORS[12][200];
|
||||
|
||||
const pinkdark: string = COLORS[15][700];
|
||||
const pinklight = COLORS[15][200];
|
||||
|
||||
export const ONBOARDING_EVENTS: OnboardingActions[] = [
|
||||
{ order: 0, eventType: 'project.added', oneof: ['project.added'], link: ['/projects/create'] },
|
||||
{ order: 1, eventType: 'project.application.added', oneof: ['project.application.added'], link: ['/projects/app-create'] },
|
||||
{ order: 2, eventType: 'user.human.added', oneof: ['user.human.added'], link: ['/users/create'] },
|
||||
{ order: 3, eventType: 'user.grant.added', oneof: ['user.grant.added'], link: ['/grant-create'] },
|
||||
{
|
||||
order: 0,
|
||||
eventType: 'project.added',
|
||||
oneof: ['project.added'],
|
||||
link: ['/projects/create'],
|
||||
iconClasses: 'las la-database',
|
||||
darkcolor: greendark,
|
||||
lightcolor: greenlight,
|
||||
},
|
||||
{
|
||||
order: 1,
|
||||
eventType: 'project.application.added',
|
||||
oneof: ['project.application.added'],
|
||||
link: ['/projects/app-create'],
|
||||
iconClasses: 'lab la-openid',
|
||||
darkcolor: purpledark,
|
||||
lightcolor: purplelight,
|
||||
},
|
||||
{
|
||||
order: 2,
|
||||
eventType: 'user.human.added',
|
||||
oneof: ['user.human.added'],
|
||||
link: ['/users/create'],
|
||||
iconClasses: 'las la-user',
|
||||
darkcolor: bluedark,
|
||||
lightcolor: bluelight,
|
||||
},
|
||||
{
|
||||
order: 3,
|
||||
eventType: 'user.grant.added',
|
||||
oneof: ['user.grant.added'],
|
||||
link: ['/grant-create'],
|
||||
iconClasses: 'las la-shield-alt',
|
||||
darkcolor: reddark,
|
||||
lightcolor: redlight,
|
||||
},
|
||||
{
|
||||
order: 4,
|
||||
eventType: 'instance.policy.label.added',
|
||||
oneof: ['instance.policy.label.added', 'instance.policy.label.changed'],
|
||||
link: ['/settings'],
|
||||
fragment: 'branding',
|
||||
iconClasses: 'las la-swatchbook',
|
||||
darkcolor: pinkdark,
|
||||
lightcolor: pinklight,
|
||||
},
|
||||
{
|
||||
order: 5,
|
||||
@@ -18,5 +72,8 @@ export const ONBOARDING_EVENTS: OnboardingActions[] = [
|
||||
oneof: ['instance.smtp.config.added', 'instance.smtp.config.changed'],
|
||||
link: ['/settings'],
|
||||
fragment: 'notifications',
|
||||
iconClasses: 'las la-envelope',
|
||||
darkcolor: yellowdark,
|
||||
lightcolor: yellowlight,
|
||||
},
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user