Error handling on signup, login and home view

This commit is contained in:
2026-03-10 20:35:52 +01:00
parent f4b986c79e
commit 6a3fd249a4
6 changed files with 54 additions and 18 deletions

View File

@@ -7,7 +7,7 @@ public class MaxLengthRule : AbstractRule
{ {
private readonly string _stringValue; private readonly string _stringValue;
private readonly int _maximumLentgh; private readonly int _maximumLentgh;
protected override string ErrorCode => "minimum_length"; protected override string ErrorCode => "maximum_length";
protected override string ErrorMessage protected override string ErrorMessage
=> $"Length of '{ValueObjectName}' must be at most {_maximumLentgh} characters"; => $"Length of '{ValueObjectName}' must be at most {_maximumLentgh} characters";

View File

@@ -10,12 +10,10 @@ namespace AipsCore.Infrastructure.Authentication.AuthService;
public class EfAuthService : IAuthService public class EfAuthService : IAuthService
{ {
private readonly AipsDbContext _dbContext;
private readonly UserManager<Persistence.User.User> _userManager; private readonly UserManager<Persistence.User.User> _userManager;
public EfAuthService(AipsDbContext dbContext, UserManager<Persistence.User.User> userManager) public EfAuthService(UserManager<Persistence.User.User> userManager)
{ {
_dbContext = dbContext;
_userManager = userManager; _userManager = userManager;
} }
@@ -27,8 +25,8 @@ public class EfAuthService : IAuthService
if (!result.Succeeded) if (!result.Succeeded)
{ {
var errors = string.Join(", ", result.Errors.Select(e => e.Description)); var validationErrors = result.Errors.Select(e => new ValidationError(e.Code, e.Description)).ToList();
throw new Exception($"User registration failed: {errors}"); throw new ValidationException(validationErrors);
} }
await _userManager.AddToRoleAsync(entity, UserRole.User.Name); await _userManager.AddToRoleAsync(entity, UserRole.User.Name);

View File

@@ -72,7 +72,14 @@ async function request<T = any>(method: string, url: string, body?: any, attempt
if (!res.ok) { if (!res.ok) {
const errorBody = await parseJsonOrThrow(res) const errorBody = await parseJsonOrThrow(res)
throw Object.assign(new Error(res.statusText || 'Request failed'), { status: res.status, body: errorBody })
if (errorBody?.errors) {
const messages = errorBody.errors.map((e: any) => e.message ?? e)
const err = new Error(messages[0]) as any
err.messages = messages
throw err
}
} }
return await parseJsonOrThrow(res) return await parseJsonOrThrow(res)

View File

@@ -18,7 +18,12 @@ const selectedJoinPolicy = ref(WhiteboardJoinPolicy.FreeToJoin)
const maxParticipants = ref(10) const maxParticipants = ref(10)
const showCreateModal = ref(false) const showCreateModal = ref(false)
const newWhiteboardError = ref<string[]>([])
const joinWithCodeError = ref<string[]>([])
async function handleCreateNewWhiteboard() { async function handleCreateNewWhiteboard() {
newWhiteboardError.value = []
if (!whiteboardTitle.value.trim()) { if (!whiteboardTitle.value.trim()) {
alert('Please enter a title for the whiteboard.') alert('Please enter a title for the whiteboard.')
return return
@@ -37,12 +42,16 @@ async function handleCreateNewWhiteboard() {
maxParticipants.value = 10 maxParticipants.value = 10
await router.push({ name: 'whiteboard', params: { id: newWhiteboardId } }) await router.push({ name: 'whiteboard', params: { id: newWhiteboardId } })
} catch (e) { } catch (e: any) {
console.error('Failed to create new whiteboard', e) newWhiteboardError.value = e.messages || ['Failed to create whiteboard']
} finally {
whiteboards.isLoading = false
} }
} }
async function joinWithCode() { async function joinWithCode() {
joinWithCodeError.value = []
if (joinCode.value.length !== 8) { if (joinCode.value.length !== 8) {
alert('Please enter a valid 8-digit code.') alert('Please enter a valid 8-digit code.')
return return
@@ -59,7 +68,7 @@ async function joinWithCode() {
await router.push({ name: 'whiteboard', params: { id: joinResult.whiteboardId } }) await router.push({ name: 'whiteboard', params: { id: joinResult.whiteboardId } })
} catch (err: any) { } catch (err: any) {
console.error(err) joinWithCodeError.value = err.messages || ['Failed to join whiteboard with code']
} }
} }
@@ -99,6 +108,13 @@ async function joinWithCode() {
style="min-height: calc(100vh - 56px);" style="min-height: calc(100vh - 56px);"
> >
<div style="width: 320px;"> <div style="width: 320px;">
<div v-if="joinWithCodeError.length" class="alert alert-danger">
<ul>
<li v-for="errorMessage in joinWithCodeError" :key="errorMessage">{{ errorMessage }}</li>
</ul>
</div>
<input <input
v-model="joinCode" v-model="joinCode"
type="text" type="text"
@@ -126,6 +142,13 @@ async function joinWithCode() {
<button type="button" class="btn-close btn-close-white" @click="showCreateModal = false"></button> <button type="button" class="btn-close btn-close-white" @click="showCreateModal = false"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div v-if="newWhiteboardError.length" class="alert alert-danger">
<ul>
<li v-for="errorMessage in newWhiteboardError" :key="errorMessage">{{ errorMessage }}</li>
</ul>
</div>
<input <input
v-model="whiteboardTitle" v-model="whiteboardTitle"
type="text" type="text"

View File

@@ -8,17 +8,17 @@ const auth = useAuthStore()
const email = ref('') const email = ref('')
const password = ref('') const password = ref('')
const error = ref('') const error = ref<string[]>([])
const loading = ref(false) const loading = ref(false)
async function onSubmit() { async function onSubmit() {
error.value = '' error.value = []
loading.value = true loading.value = true
try { try {
await auth.login({ email: email.value, password: password.value }) await auth.login({ email: email.value, password: password.value })
router.push('/') router.push('/')
} catch (e: any) { } catch (e: any) {
error.value = e.message || 'Login failed' error.value = e.messages || ['Login failed']
} finally { } finally {
loading.value = false loading.value = false
} }
@@ -30,7 +30,11 @@ async function onSubmit() {
<div class="card-body p-4"> <div class="card-body p-4">
<h2 class="card-title text-center mb-4">Log In</h2> <h2 class="card-title text-center mb-4">Log In</h2>
<div v-if="error" class="alert alert-danger">{{ error }}</div> <div v-if="error.length" class="alert alert-danger">
<ul>
<li v-for="m in error" :key="m">{{ m }}</li>
</ul>
</div>
<form @submit.prevent="onSubmit"> <form @submit.prevent="onSubmit">
<div class="mb-3"> <div class="mb-3">

View File

@@ -9,17 +9,17 @@ const auth = useAuthStore()
const username = ref('') const username = ref('')
const email = ref('') const email = ref('')
const password = ref('') const password = ref('')
const error = ref('') const error = ref<string[]>([])
const loading = ref(false) const loading = ref(false)
async function onSubmit() { async function onSubmit() {
error.value = '' error.value = []
loading.value = true loading.value = true
try { try {
await auth.signup({ username: username.value, email: email.value, password: password.value }) await auth.signup({ username: username.value, email: email.value, password: password.value })
router.push('/') router.push('/')
} catch (e: any) { } catch (e: any) {
error.value = e.message || 'Signup failed' error.value = e.messages || ['Signup failed']
} finally { } finally {
loading.value = false loading.value = false
} }
@@ -31,7 +31,11 @@ async function onSubmit() {
<div class="card-body p-4"> <div class="card-body p-4">
<h2 class="card-title text-center mb-4">Sign Up</h2> <h2 class="card-title text-center mb-4">Sign Up</h2>
<div v-if="error" class="alert alert-danger">{{ error }}</div> <div v-if="error.length" class="alert alert-danger">
<ul>
<li v-for="m in error" :key="m">{{ m }}</li>
</ul>
</div>
<form @submit.prevent="onSubmit"> <form @submit.prevent="onSubmit">
<div class="mb-3"> <div class="mb-3">