Fix org creation from initial state and wrong password error handling

- Organization creation now works even before orgs are loaded (fixes state guard)
- Org creation UI now preserves state and shows inline error messages
- Wrong password login no longer triggers global logout; shows inline error instead
- ApiClient now excludes /auth/ endpoints from global 401 session expiry
This commit is contained in:
Leon Bösche
2026-01-11 00:28:02 +01:00
parent 6186c4c779
commit 7cf55325d4
2 changed files with 55 additions and 36 deletions

View File

@@ -97,59 +97,73 @@ class OrganizationBloc extends Bloc<OrganizationEvent, OrganizationState> {
CreateOrganization event,
Emitter<OrganizationState> emit,
) async {
final currentState = state;
if (currentState is OrganizationLoaded) {
final name = event.name.trim();
if (name.isEmpty) {
final name = event.name.trim();
if (name.isEmpty) {
// Try to preserve current state if possible
if (state is OrganizationLoaded) {
emit(
OrganizationLoaded(
organizations: currentState.organizations,
selectedOrg: currentState.selectedOrg,
organizations: (state as OrganizationLoaded).organizations,
selectedOrg: (state as OrganizationLoaded).selectedOrg,
isLoading: false,
error: 'Organization name cannot be empty',
),
);
return;
}
if (currentState.organizations.any((org) => org.name == name)) {
return;
}
// Get existing organizations list
List<Organization> existingOrgs = [];
Organization? selectedOrg;
if (state is OrganizationLoaded) {
existingOrgs = (state as OrganizationLoaded).organizations;
selectedOrg = (state as OrganizationLoaded).selectedOrg;
// Check for duplicate name (client-side validation)
if (existingOrgs.any((org) => org.name == name)) {
emit(
OrganizationLoaded(
organizations: currentState.organizations,
selectedOrg: currentState.selectedOrg,
organizations: existingOrgs,
selectedOrg: selectedOrg,
isLoading: false,
error: 'Organization with this name already exists',
),
);
return;
}
}
// Set loading state
emit(
OrganizationLoaded(
organizations: existingOrgs,
selectedOrg: selectedOrg,
isLoading: true,
),
);
try {
final newOrg = await orgApi.createOrganization(name);
final updatedOrgs = [...existingOrgs, newOrg];
emit(
OrganizationLoaded(organizations: updatedOrgs, selectedOrg: newOrg),
);
// Reset blocs and load permissions for new org
permissionBloc.add(PermissionsReset());
fileBrowserBloc.add(ResetFileBrowser());
uploadBloc.add(ResetUploads());
permissionBloc.add(LoadPermissions(newOrg.id));
} catch (e) {
emit(
OrganizationLoaded(
organizations: currentState.organizations,
selectedOrg: currentState.selectedOrg,
isLoading: true,
organizations: existingOrgs,
selectedOrg: selectedOrg,
isLoading: false,
error: _getErrorMessage(e),
),
);
try {
final newOrg = await orgApi.createOrganization(name);
final updatedOrgs = [...currentState.organizations, newOrg];
emit(
OrganizationLoaded(organizations: updatedOrgs, selectedOrg: newOrg),
);
// Reset blocs and load permissions for new org
permissionBloc.add(PermissionsReset());
fileBrowserBloc.add(ResetFileBrowser());
uploadBloc.add(ResetUploads());
permissionBloc.add(LoadPermissions(newOrg.id));
} catch (e) {
emit(
OrganizationLoaded(
organizations: currentState.organizations,
selectedOrg: currentState.selectedOrg,
isLoading: false,
error: _getErrorMessage(e),
),
);
}
}
}
}

View File

@@ -29,8 +29,13 @@ class ApiClient {
},
onError: (error, handler) async {
if (error.response?.statusCode == 401) {
// Session expired, trigger logout
_sessionBloc.add(SessionExpired());
final path = error.requestOptions.path;
// Do not expire session for auth endpoints; show inline error instead
final isAuthEndpoint = path.startsWith('/auth/');
if (!isAuthEndpoint) {
// Session expired, trigger logout
_sessionBloc.add(SessionExpired());
}
}
return handler.next(error);
},