97 lines
2.4 KiB
Vue
97 lines
2.4 KiB
Vue
<template>
|
|
<main class="flex items-center justify-center page-container">
|
|
<div class="card">
|
|
<div class="card-body text-center">
|
|
<div v-if="loading" class="spinner-dots" role="status">
|
|
<span /><span /><span />
|
|
</div>
|
|
<p v-else-if="error" class="text-error">{{ error }}</p>
|
|
<p v-else>Redirecting...</p>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, onMounted } from 'vue';
|
|
import { useRoute, useRouter } from 'vue-router';
|
|
import { useAuthStore } from '@/stores/auth';
|
|
import { useNotificationStore } from '@/stores/notifications';
|
|
|
|
const route = useRoute();
|
|
const router = useRouter();
|
|
const authStore = useAuthStore();
|
|
const notificationStore = useNotificationStore();
|
|
|
|
const loading = ref(true);
|
|
const error = ref<string | null>(null);
|
|
|
|
onMounted(async () => {
|
|
try {
|
|
const accessToken = route.query.access_token as string | undefined;
|
|
const refreshToken = route.query.refresh_token as string | undefined;
|
|
const legacyToken = route.query.token as string | undefined;
|
|
|
|
const tokenToUse = accessToken || legacyToken;
|
|
|
|
if (!tokenToUse) {
|
|
throw new Error('No token provided');
|
|
}
|
|
|
|
await authStore.setTokens({ access_token: tokenToUse, refresh_token: refreshToken });
|
|
notificationStore.addNotification({ message: 'Login successful', type: 'success' });
|
|
router.push('/');
|
|
} catch (err) {
|
|
error.value = err instanceof Error ? err.message : 'Authentication failed';
|
|
notificationStore.addNotification({ message: error.value, type: 'error' });
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<style scoped>
|
|
.page-container {
|
|
min-height: 100vh;
|
|
min-height: 100dvh;
|
|
padding: 1rem;
|
|
}
|
|
|
|
.card {
|
|
width: 100%;
|
|
max-width: 400px;
|
|
text-align: center;
|
|
}
|
|
|
|
.spinner-dots {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.spinner-dots span {
|
|
width: 0.75rem;
|
|
height: 0.75rem;
|
|
background-color: var(--primary);
|
|
border-radius: 50%;
|
|
animation: bounce 0.5s infinite alternate;
|
|
}
|
|
|
|
.spinner-dots span:nth-child(2) {
|
|
animation-delay: 0.2s;
|
|
}
|
|
|
|
.spinner-dots span:nth-child(3) {
|
|
animation-delay: 0.4s;
|
|
}
|
|
|
|
@keyframes bounce {
|
|
from {
|
|
transform: translateY(0);
|
|
}
|
|
|
|
to {
|
|
transform: translateY(-0.5rem);
|
|
}
|
|
}
|
|
</style> |