- Replace identity management with a new authentication system across the application. - Update App.vue to integrate LoginDialog and remove PasswordDialog. - Modify Navbar.vue to handle user authentication state and logout functionality. - Enhance Home.vue to display user information upon login. - Implement routing changes in index.ts to enforce authentication requirements for protected routes.
237 lines
No EOL
6.9 KiB
Vue
237 lines
No EOL
6.9 KiB
Vue
<script setup lang="ts">
|
|
import { ref, computed } from 'vue'
|
|
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog'
|
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
|
import { Button } from '@/components/ui/button'
|
|
import { Input } from '@/components/ui/input'
|
|
import { Label } from '@/components/ui/label'
|
|
import { User } from 'lucide-vue-next'
|
|
import { auth } from '@/composables/useAuth'
|
|
import { toast } from 'vue-sonner'
|
|
|
|
interface Props {
|
|
isOpen: boolean
|
|
}
|
|
|
|
interface Emits {
|
|
(e: 'update:isOpen', value: boolean): void
|
|
(e: 'success'): void
|
|
}
|
|
|
|
defineProps<Props>()
|
|
const emit = defineEmits<Emits>()
|
|
|
|
// Form states
|
|
const activeTab = ref('login')
|
|
const isLoading = ref(false)
|
|
const error = ref('')
|
|
|
|
// Login form
|
|
const loginForm = ref({
|
|
username: '',
|
|
password: ''
|
|
})
|
|
|
|
// Register form
|
|
const registerForm = ref({
|
|
username: '',
|
|
email: '',
|
|
password: '',
|
|
password_repeat: ''
|
|
})
|
|
|
|
const canLogin = computed(() => {
|
|
return loginForm.value.username.trim() && loginForm.value.password.trim()
|
|
})
|
|
|
|
const canRegister = computed(() => {
|
|
const { username, password, password_repeat } = registerForm.value
|
|
return username.trim() && password.trim() && password === password_repeat && password.length >= 6
|
|
})
|
|
|
|
async function handleLogin() {
|
|
if (!canLogin.value) return
|
|
|
|
try {
|
|
isLoading.value = true
|
|
error.value = ''
|
|
|
|
await auth.login({
|
|
username: loginForm.value.username,
|
|
password: loginForm.value.password
|
|
})
|
|
|
|
toast.success('Login successful!')
|
|
emit('success')
|
|
handleClose()
|
|
} catch (err) {
|
|
error.value = err instanceof Error ? err.message : 'Login failed'
|
|
toast.error('Login failed. Please check your credentials.')
|
|
} finally {
|
|
isLoading.value = false
|
|
}
|
|
}
|
|
|
|
async function handleRegister() {
|
|
if (!canRegister.value) return
|
|
|
|
try {
|
|
isLoading.value = true
|
|
error.value = ''
|
|
|
|
await auth.register({
|
|
username: registerForm.value.username,
|
|
email: registerForm.value.email || undefined,
|
|
password: registerForm.value.password,
|
|
password_repeat: registerForm.value.password_repeat
|
|
})
|
|
|
|
toast.success('Registration successful!')
|
|
emit('success')
|
|
handleClose()
|
|
} catch (err) {
|
|
error.value = err instanceof Error ? err.message : 'Registration failed'
|
|
toast.error('Registration failed. Please try again.')
|
|
} finally {
|
|
isLoading.value = false
|
|
}
|
|
}
|
|
|
|
function handleClose() {
|
|
emit('update:isOpen', false)
|
|
// Reset forms
|
|
loginForm.value = { username: '', password: '' }
|
|
registerForm.value = { username: '', email: '', password: '', password_repeat: '' }
|
|
error.value = ''
|
|
isLoading.value = false
|
|
}
|
|
|
|
function handleKeydown(event: KeyboardEvent) {
|
|
if (event.key === 'Enter') {
|
|
if (activeTab.value === 'login') {
|
|
handleLogin()
|
|
} else {
|
|
handleRegister()
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<Dialog :open="isOpen" @update:open="handleClose">
|
|
<DialogContent class="sm:max-w-[425px]">
|
|
<DialogHeader>
|
|
<DialogTitle class="flex items-center gap-2">
|
|
<User class="w-5 h-5" />
|
|
Authentication
|
|
</DialogTitle>
|
|
<DialogDescription>
|
|
Login to your account or create a new one
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
|
|
<Tabs v-model="activeTab" class="w-full">
|
|
<TabsList class="grid w-full grid-cols-2">
|
|
<TabsTrigger value="login">Login</TabsTrigger>
|
|
<TabsTrigger value="register">Register</TabsTrigger>
|
|
</TabsList>
|
|
|
|
<TabsContent value="login" class="space-y-4">
|
|
<div class="space-y-4">
|
|
<div class="space-y-2">
|
|
<Label for="login-username">Username or Email</Label>
|
|
<Input
|
|
id="login-username"
|
|
v-model="loginForm.username"
|
|
placeholder="Enter your username or email"
|
|
:disabled="isLoading"
|
|
@keydown="handleKeydown"
|
|
/>
|
|
</div>
|
|
<div class="space-y-2">
|
|
<Label for="login-password">Password</Label>
|
|
<Input
|
|
id="login-password"
|
|
type="password"
|
|
v-model="loginForm.password"
|
|
placeholder="Enter your password"
|
|
:disabled="isLoading"
|
|
@keydown="handleKeydown"
|
|
/>
|
|
</div>
|
|
<p v-if="error && activeTab === 'login'" class="text-sm text-destructive">
|
|
{{ error }}
|
|
</p>
|
|
<Button
|
|
@click="handleLogin"
|
|
:disabled="isLoading || !canLogin"
|
|
class="w-full"
|
|
>
|
|
<span v-if="isLoading" class="animate-spin mr-2">⚡</span>
|
|
Login
|
|
</Button>
|
|
</div>
|
|
</TabsContent>
|
|
|
|
<TabsContent value="register" class="space-y-4">
|
|
<div class="space-y-4">
|
|
<div class="space-y-2">
|
|
<Label for="register-username">Username</Label>
|
|
<Input
|
|
id="register-username"
|
|
v-model="registerForm.username"
|
|
placeholder="Choose a username"
|
|
:disabled="isLoading"
|
|
@keydown="handleKeydown"
|
|
/>
|
|
</div>
|
|
<div class="space-y-2">
|
|
<Label for="register-email">Email (optional)</Label>
|
|
<Input
|
|
id="register-email"
|
|
type="email"
|
|
v-model="registerForm.email"
|
|
placeholder="Enter your email"
|
|
:disabled="isLoading"
|
|
@keydown="handleKeydown"
|
|
/>
|
|
</div>
|
|
<div class="space-y-2">
|
|
<Label for="register-password">Password</Label>
|
|
<Input
|
|
id="register-password"
|
|
type="password"
|
|
v-model="registerForm.password"
|
|
placeholder="Choose a password"
|
|
:disabled="isLoading"
|
|
@keydown="handleKeydown"
|
|
/>
|
|
</div>
|
|
<div class="space-y-2">
|
|
<Label for="register-password-repeat">Confirm Password</Label>
|
|
<Input
|
|
id="register-password-repeat"
|
|
type="password"
|
|
v-model="registerForm.password_repeat"
|
|
placeholder="Confirm your password"
|
|
:disabled="isLoading"
|
|
@keydown="handleKeydown"
|
|
/>
|
|
</div>
|
|
<p v-if="error && activeTab === 'register'" class="text-sm text-destructive">
|
|
{{ error }}
|
|
</p>
|
|
<Button
|
|
@click="handleRegister"
|
|
:disabled="isLoading || !canRegister"
|
|
class="w-full"
|
|
>
|
|
<span v-if="isLoading" class="animate-spin mr-2">⚡</span>
|
|
Register
|
|
</Button>
|
|
</div>
|
|
</TabsContent>
|
|
</Tabs>
|
|
</DialogContent>
|
|
</Dialog>
|
|
</template> |