diff --git a/next-ui/src/App.vue b/next-ui/src/App.vue
index f803f41a4..d47aabfaf 100644
--- a/next-ui/src/App.vue
+++ b/next-ui/src/App.vue
@@ -1,6 +1,8 @@
+
+
diff --git a/next-ui/src/api/komga-client.ts b/next-ui/src/api/komga-client.ts
index 4de9d9c86..4e7f24bca 100644
--- a/next-ui/src/api/komga-client.ts
+++ b/next-ui/src/api/komga-client.ts
@@ -1,6 +1,7 @@
import type { Middleware } from 'openapi-fetch'
import createClient from 'openapi-fetch'
import type { paths } from '@/generated/openapi/komga'
+import { globalProperties } from '@/main'
// Middleware that throws on error, so it works with Pinia Colada
const coladaMiddleware: Middleware = {
@@ -23,7 +24,11 @@ const coladaMiddleware: Middleware = {
onError() {
throw new Error('error', {
cause: {
- message: 'Server is unreachable',
+ message: globalProperties.$intl.formatMessage({
+ description: 'Network error: error message when the server cannot be reached',
+ defaultMessage: 'Server is unreachable',
+ id: '/6WuF/',
+ }),
},
})
},
diff --git a/next-ui/src/components.d.ts b/next-ui/src/components.d.ts
index ea8a0936e..7da3029ff 100644
--- a/next-ui/src/components.d.ts
+++ b/next-ui/src/components.d.ts
@@ -29,6 +29,7 @@ declare module 'vue' {
LocaleSelector: typeof import('./components/LocaleSelector.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
+ SnackQueue: typeof import('./components/SnackQueue.vue')['default']
ThemeSelector: typeof import('./components/ThemeSelector.vue')['default']
}
}
diff --git a/next-ui/src/components/SnackQueue.vue b/next-ui/src/components/SnackQueue.vue
new file mode 100644
index 000000000..f5b3dc1b0
--- /dev/null
+++ b/next-ui/src/components/SnackQueue.vue
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
diff --git a/next-ui/src/main.ts b/next-ui/src/main.ts
index c8fc17224..ffffa61e5 100644
--- a/next-ui/src/main.ts
+++ b/next-ui/src/main.ts
@@ -18,3 +18,5 @@ const app = createApp(App)
registerPlugins(app)
app.mount('#app')
+
+export const globalProperties = app.config.globalProperties
diff --git a/next-ui/src/pages/login.vue b/next-ui/src/pages/login.vue
index 07caf3dbc..4605ca721 100644
--- a/next-ui/src/pages/login.vue
+++ b/next-ui/src/pages/login.vue
@@ -4,9 +4,9 @@
:disabled="isLoading"
@submit.prevent="submitForm()"
>
-
+
-
+
@@ -41,12 +41,17 @@
"
type="password"
:rules="[rules.required()]"
+ :error-messages="loginError"
+ @update:modelValue="loginError = ''"
/>
-
-
+
+
+
+ {{
+ $formatMessage({
+ description: 'Login screen: Forgot your password link',
+ defaultMessage: 'Forgot your password?',
+ id: 'r6JNfI',
+ })
+ }}
+
+
@@ -73,28 +94,19 @@
"
:loading="isLoading"
type="submit"
+ block
/>
-
-
-
-
-
- Dismiss
-
-
+
+
+
+
+
+
+
+
@@ -105,15 +117,19 @@
import { type ErrorCause, komgaClient } from '@/api/komga-client'
import { useMutation, useQueryCache } from '@pinia/colada'
import { useRules } from 'vuetify/labs/rules'
+import { useMessagesStore } from '@/stores/messages'
+import { useIntl } from 'vue-intl'
+import { commonMessages } from '@/utils/i18n/common-messages'
const rules = useRules()
+const messagesStore = useMessagesStore()
+const intl = useIntl()
const formValid = ref(false)
const username = ref('')
const password = ref('')
const rememberMe = ref(false)
-const showError = ref(false)
-const showErrorText = ref('')
+const loginError = ref('')
const router = useRouter()
const route = useRoute()
@@ -139,8 +155,17 @@ const { mutate: performLogin, isLoading } = useMutation({
else void router.push('/')
},
onError: (error) => {
- showErrorText.value = (error.cause as ErrorCause).message || 'Invalid authentication'
- showError.value = true
+ if ((error.cause as ErrorCause).status === 401)
+ loginError.value = intl.formatMessage({
+ description: 'Login screen: error message displayed when login failed',
+ defaultMessage: 'Invalid login or password',
+ id: 'AjWlka',
+ })
+ else
+ messagesStore.messages.push({
+ text:
+ (error.cause as ErrorCause).message || intl.formatMessage(commonMessages.networkError),
+ })
},
})
diff --git a/next-ui/src/stores/messages.ts b/next-ui/src/stores/messages.ts
new file mode 100644
index 000000000..69ca869f5
--- /dev/null
+++ b/next-ui/src/stores/messages.ts
@@ -0,0 +1,8 @@
+// Utilities
+import { defineStore } from 'pinia'
+
+export const useMessagesStore = defineStore('messages', {
+ state: () => ({
+ messages: [] as object[],
+ }),
+})
diff --git a/next-ui/src/utils/i18n/common-messages.ts b/next-ui/src/utils/i18n/common-messages.ts
index 29eb789db..2698daf6a 100644
--- a/next-ui/src/utils/i18n/common-messages.ts
+++ b/next-ui/src/utils/i18n/common-messages.ts
@@ -11,4 +11,9 @@ export const commonMessages = {
defaultMessage: 'There might be a problem with your connection or your server.',
id: 'hYO2n6',
}),
+ networkError: defineMessage({
+ description: 'Common message: a network error happened when communicating with the server',
+ defaultMessage: 'Network error',
+ id: 'Z/EY89',
+ }),
}