feat(web): improved user onboarding (#18782)
Some checks are pending
CodeQL / Analyze (javascript) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
Docker / pre-job (push) Waiting to run
Docker / Re-Tag ML () (push) Blocked by required conditions
Docker / Re-Tag ML (-armnn) (push) Blocked by required conditions
Docker / Re-Tag ML (-cuda) (push) Blocked by required conditions
Docker / Re-Tag ML (-openvino) (push) Blocked by required conditions
Docker / Re-Tag ML (-rknn) (push) Blocked by required conditions
Docker / Re-Tag ML (-rocm) (push) Blocked by required conditions
Docker / Re-Tag Server () (push) Blocked by required conditions
Docker / Build and Push ML (armnn, linux/arm64, -armnn) (push) Blocked by required conditions
Docker / Build and Push ML (cpu, ) (push) Blocked by required conditions
Docker / Build and Push ML (cuda, linux/amd64, -cuda) (push) Blocked by required conditions
Docker / Build and Push ML (openvino, linux/amd64, -openvino) (push) Blocked by required conditions
Docker / Build and Push ML (rknn, linux/arm64, -rknn) (push) Blocked by required conditions
Docker / Build and Push ML (rocm, linux/amd64, {"linux/amd64": "mich"}, -rocm) (push) Blocked by required conditions
Docker / Build and Push Server (push) Blocked by required conditions
Docker / Docker Build & Push Server Success (push) Blocked by required conditions
Docker / Docker Build & Push ML Success (push) Blocked by required conditions
Docs build / pre-job (push) Waiting to run
Docs build / Docs Build (push) Blocked by required conditions
Static Code Analysis / pre-job (push) Waiting to run
Static Code Analysis / Run Dart Code Analysis (push) Blocked by required conditions
Static Code Analysis / zizmor (push) Waiting to run
Test / pre-job (push) Waiting to run
Test / Test & Lint Server (push) Blocked by required conditions
Test / Unit Test CLI (push) Blocked by required conditions
Test / Unit Test CLI (Windows) (push) Blocked by required conditions
Test / Lint Web (push) Blocked by required conditions
Test / Test Web (push) Blocked by required conditions
Test / Test i18n (push) Blocked by required conditions
Test / End-to-End Lint (push) Blocked by required conditions
Test / Medium Tests (Server) (push) Blocked by required conditions
Test / End-to-End Tests (Server & CLI) (ubuntu-24.04-arm) (push) Blocked by required conditions
Test / End-to-End Tests (Server & CLI) (ubuntu-latest) (push) Blocked by required conditions
Test / End-to-End Tests (Web) (ubuntu-24.04-arm) (push) Blocked by required conditions
Test / End-to-End Tests (Web) (ubuntu-latest) (push) Blocked by required conditions
Test / End-to-End Tests Success (push) Blocked by required conditions
Test / Unit Test Mobile (push) Blocked by required conditions
Test / Unit Test ML (push) Blocked by required conditions
Test / .github Files Formatting (push) Blocked by required conditions
Test / ShellCheck (push) Waiting to run
Test / OpenAPI Clients (push) Waiting to run
Test / SQL Schema Checks (push) Waiting to run

* wip

* added user metadata key

* wip

* restructure onboarding system and add initial locale

* update language card and fix translation updating

* remove prints

* new card formattings

* fix cursed unmount effect

* add OAuth route onboarding

* remove required admin auth for onboarding

* delete the hotwire button

* update open-api files

* delete import

* fix failing oauth onboarding fields

* fix e2e test

* fix web e2e test

* add onboarding to user registration e2e test

* remove todo

this was a holdover during dev and didn't get deleted

* fix server small tests

* use onDestroy to save settings rather than a bind:this

* change to false for isOnboarded

* fix other auth small test

* provide type annotation in user factory metadata field

* remove onboardingCompelted from UserDto

* move translations to onboarding steps array and mark as derived so they update

* break language selector out into its own component as per @danieldietzler suggestion

* remove hello header on card

* fix flixkering on server privacy card

* label/id fixes

* openapi

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
This commit is contained in:
Brandon Wees
2025-06-02 16:09:13 -05:00
committed by GitHub
parent e7d7886f44
commit 74438f5bd8
36 changed files with 961 additions and 235 deletions

View File

@@ -15,6 +15,7 @@ class LoginResponseDto {
LoginResponseDto({
required this.accessToken,
required this.isAdmin,
required this.isOnboarded,
required this.name,
required this.profileImagePath,
required this.shouldChangePassword,
@@ -26,6 +27,8 @@ class LoginResponseDto {
bool isAdmin;
bool isOnboarded;
String name;
String profileImagePath;
@@ -40,6 +43,7 @@ class LoginResponseDto {
bool operator ==(Object other) => identical(this, other) || other is LoginResponseDto &&
other.accessToken == accessToken &&
other.isAdmin == isAdmin &&
other.isOnboarded == isOnboarded &&
other.name == name &&
other.profileImagePath == profileImagePath &&
other.shouldChangePassword == shouldChangePassword &&
@@ -51,6 +55,7 @@ class LoginResponseDto {
// ignore: unnecessary_parenthesis
(accessToken.hashCode) +
(isAdmin.hashCode) +
(isOnboarded.hashCode) +
(name.hashCode) +
(profileImagePath.hashCode) +
(shouldChangePassword.hashCode) +
@@ -58,12 +63,13 @@ class LoginResponseDto {
(userId.hashCode);
@override
String toString() => 'LoginResponseDto[accessToken=$accessToken, isAdmin=$isAdmin, name=$name, profileImagePath=$profileImagePath, shouldChangePassword=$shouldChangePassword, userEmail=$userEmail, userId=$userId]';
String toString() => 'LoginResponseDto[accessToken=$accessToken, isAdmin=$isAdmin, isOnboarded=$isOnboarded, name=$name, profileImagePath=$profileImagePath, shouldChangePassword=$shouldChangePassword, userEmail=$userEmail, userId=$userId]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
json[r'accessToken'] = this.accessToken;
json[r'isAdmin'] = this.isAdmin;
json[r'isOnboarded'] = this.isOnboarded;
json[r'name'] = this.name;
json[r'profileImagePath'] = this.profileImagePath;
json[r'shouldChangePassword'] = this.shouldChangePassword;
@@ -83,6 +89,7 @@ class LoginResponseDto {
return LoginResponseDto(
accessToken: mapValueOfType<String>(json, r'accessToken')!,
isAdmin: mapValueOfType<bool>(json, r'isAdmin')!,
isOnboarded: mapValueOfType<bool>(json, r'isOnboarded')!,
name: mapValueOfType<String>(json, r'name')!,
profileImagePath: mapValueOfType<String>(json, r'profileImagePath')!,
shouldChangePassword: mapValueOfType<bool>(json, r'shouldChangePassword')!,
@@ -137,6 +144,7 @@ class LoginResponseDto {
static const requiredKeys = <String>{
'accessToken',
'isAdmin',
'isOnboarded',
'name',
'profileImagePath',
'shouldChangePassword',

View File

@@ -0,0 +1,99 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// @dart=2.18
// ignore_for_file: unused_element, unused_import
// ignore_for_file: always_put_required_named_parameters_first
// ignore_for_file: constant_identifier_names
// ignore_for_file: lines_longer_than_80_chars
part of openapi.api;
class OnboardingDto {
/// Returns a new [OnboardingDto] instance.
OnboardingDto({
required this.isOnboarded,
});
bool isOnboarded;
@override
bool operator ==(Object other) => identical(this, other) || other is OnboardingDto &&
other.isOnboarded == isOnboarded;
@override
int get hashCode =>
// ignore: unnecessary_parenthesis
(isOnboarded.hashCode);
@override
String toString() => 'OnboardingDto[isOnboarded=$isOnboarded]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
json[r'isOnboarded'] = this.isOnboarded;
return json;
}
/// Returns a new [OnboardingDto] instance and imports its values from
/// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods
static OnboardingDto? fromJson(dynamic value) {
upgradeDto(value, "OnboardingDto");
if (value is Map) {
final json = value.cast<String, dynamic>();
return OnboardingDto(
isOnboarded: mapValueOfType<bool>(json, r'isOnboarded')!,
);
}
return null;
}
static List<OnboardingDto> listFromJson(dynamic json, {bool growable = false,}) {
final result = <OnboardingDto>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = OnboardingDto.fromJson(row);
if (value != null) {
result.add(value);
}
}
}
return result.toList(growable: growable);
}
static Map<String, OnboardingDto> mapFromJson(dynamic json) {
final map = <String, OnboardingDto>{};
if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) {
final value = OnboardingDto.fromJson(entry.value);
if (value != null) {
map[entry.key] = value;
}
}
}
return map;
}
// maps a json object with a list of OnboardingDto-objects as value to a dart map
static Map<String, List<OnboardingDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<OnboardingDto>>{};
if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments
json = json.cast<String, dynamic>();
for (final entry in json.entries) {
map[entry.key] = OnboardingDto.listFromJson(entry.value, growable: growable,);
}
}
return map;
}
/// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{
'isOnboarded',
};
}

View File

@@ -0,0 +1,99 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// @dart=2.18
// ignore_for_file: unused_element, unused_import
// ignore_for_file: always_put_required_named_parameters_first
// ignore_for_file: constant_identifier_names
// ignore_for_file: lines_longer_than_80_chars
part of openapi.api;
class OnboardingResponseDto {
/// Returns a new [OnboardingResponseDto] instance.
OnboardingResponseDto({
required this.isOnboarded,
});
bool isOnboarded;
@override
bool operator ==(Object other) => identical(this, other) || other is OnboardingResponseDto &&
other.isOnboarded == isOnboarded;
@override
int get hashCode =>
// ignore: unnecessary_parenthesis
(isOnboarded.hashCode);
@override
String toString() => 'OnboardingResponseDto[isOnboarded=$isOnboarded]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
json[r'isOnboarded'] = this.isOnboarded;
return json;
}
/// Returns a new [OnboardingResponseDto] instance and imports its values from
/// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods
static OnboardingResponseDto? fromJson(dynamic value) {
upgradeDto(value, "OnboardingResponseDto");
if (value is Map) {
final json = value.cast<String, dynamic>();
return OnboardingResponseDto(
isOnboarded: mapValueOfType<bool>(json, r'isOnboarded')!,
);
}
return null;
}
static List<OnboardingResponseDto> listFromJson(dynamic json, {bool growable = false,}) {
final result = <OnboardingResponseDto>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = OnboardingResponseDto.fromJson(row);
if (value != null) {
result.add(value);
}
}
}
return result.toList(growable: growable);
}
static Map<String, OnboardingResponseDto> mapFromJson(dynamic json) {
final map = <String, OnboardingResponseDto>{};
if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) {
final value = OnboardingResponseDto.fromJson(entry.value);
if (value != null) {
map[entry.key] = value;
}
}
}
return map;
}
// maps a json object with a list of OnboardingResponseDto-objects as value to a dart map
static Map<String, List<OnboardingResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<OnboardingResponseDto>>{};
if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments
json = json.cast<String, dynamic>();
for (final entry in json.entries) {
map[entry.key] = OnboardingResponseDto.listFromJson(entry.value, growable: growable,);
}
}
return map;
}
/// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{
'isOnboarded',
};
}