Files
Avtex/app/pages/settings.vue
T

209 lines
8.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="max-w-xl mx-auto p-6">
<h1 class="text-2xl font-bold mb-6">Настройки</h1>
<div class="space-y-6">
<!-- Profile Settings -->
<div class="bg-slate-800 border border-slate-700 rounded-xl p-6">
<h2 class="text-lg font-semibold mb-4 flex items-center gap-2">
<span>👤</span> Личный профиль
</h2>
<div class="space-y-4">
<div class="flex items-center gap-4 mb-4">
<img :src="profileForm.avatar" class="w-16 h-16 rounded-full bg-slate-700 object-cover border-2 border-blue-500/30" />
<div class="flex-1">
<label class="text-xs text-slate-500 uppercase font-bold">Ссылка на аватар</label>
<input v-model="profileForm.avatar" class="w-full mt-1 bg-slate-900 border border-slate-700 rounded px-3 py-1.5 text-sm text-slate-200 outline-none focus:border-blue-500" />
</div>
</div>
<div class="grid grid-cols-1 gap-4">
<div>
<label class="text-xs text-slate-500 uppercase font-bold">Имя (Display Name)</label>
<input v-model="profileForm.displayName" class="w-full mt-1 bg-slate-900 border border-slate-700 rounded px-3 py-1.5 text-sm text-slate-200 outline-none focus:border-blue-500" />
</div>
</div>
<div>
<label class="text-xs text-slate-500 uppercase font-bold">О себе (Bio)</label>
<textarea v-model="profileForm.bio" rows="3" class="w-full mt-1 bg-slate-900 border border-slate-700 rounded px-3 py-1.5 text-sm text-slate-200 outline-none focus:border-blue-500 resize-none"></textarea>
</div>
<button
@click="saveProfile"
:disabled="saving"
class="w-full py-2 bg-blue-600 hover:bg-blue-500 text-white rounded-lg font-medium transition disabled:opacity-50"
>
{{ saving ? 'Сохранение...' : 'Сохранить изменения' }}
</button>
<p v-if="saveMessage" :class="saveMessage.type === 'error' ? 'text-red-400' : 'text-emerald-400'" class="text-center text-xs mt-2">
{{ saveMessage.text }}
</p>
</div>
</div>
<!-- My Groups -->
<div class="bg-slate-800 border border-slate-700 rounded-xl p-6">
<h2 class="text-lg font-semibold mb-4 flex items-center gap-2">
<span>👥</span> Мои Группы
</h2>
<div v-if="userGroups.length === 0" class="text-slate-500 text-sm py-4">
Вы не состоите ни в одной группе.
</div>
<div v-else class="space-y-3">
<div v-for="g in userGroups" :key="g.id" class="flex items-center justify-between p-3 bg-slate-900/50 rounded-lg border border-slate-700">
<div>
<div class="font-medium text-slate-200">{{ g.name }}</div>
<div class="text-xs text-slate-500">{{ g.description }}</div>
</div>
<span :class="g.membershipRole === 'admin' ? 'text-purple-400 bg-purple-900/20' : 'text-blue-400 bg-blue-900/20'" class="text-[10px] uppercase font-bold px-2 py-0.5 rounded border border-current/20">
{{ g.membershipRole }}
</span>
</div>
</div>
</div>
<!-- Push Notifications -->
<div class="bg-slate-800 border border-slate-700 rounded-xl p-6">
<h2 class="text-lg font-semibold mb-4 flex items-center gap-2">
<span>🔔</span> Push-уведомления
</h2>
<div class="space-y-3">
<div class="flex items-center justify-between">
<span class="text-slate-300">Статус поддержки</span>
<span :class="isSupported ? 'text-emerald-400' : 'text-red-400'">
{{ isSupported ? 'Доступно' : 'Недоступно' }}
</span>
</div>
<div class="flex items-center justify-between">
<span class="text-slate-300">Разрешение</span>
<span :class="permissionGranted ? 'text-emerald-400' : 'text-yellow-400'">
{{ permissionGranted ? 'Разрешено' : 'Не запрошено' }}
</span>
</div>
<button
v-if="isSupported && !permissionGranted"
@click="enablePush"
class="w-full py-2 bg-blue-600 hover:bg-blue-500 text-white rounded-lg font-medium transition"
>
Включить уведомления
</button>
<button
v-if="isSupported && permissionGranted && !subscription"
@click="subscribe"
class="w-full py-2 bg-emerald-600 hover:bg-emerald-500 text-white rounded-lg font-medium transition"
>
Подписаться на пуши
</button>
<div v-if="subscription" class="text-sm text-emerald-400">
✅ Подписка активна
</div>
</div>
</div>
<!-- Achievements -->
<div class="bg-slate-800 border border-slate-700 rounded-xl p-6">
<h2 class="text-lg font-semibold mb-4 flex items-center gap-2">
<span>🏆</span> Ачивки
</h2>
<div v-if="achievements.length === 0" class="text-slate-400 text-sm">
Пока нет разблокированных ачивок. Завершите задачи в Canvas!
</div>
<div v-else class="space-y-2">
<div
v-for="ach in achievements"
:key="ach.id"
class="flex items-center gap-3 p-3 bg-slate-700 rounded-lg"
>
<span class="text-xl">{{ ach.icon || '🏆' }}</span>
<div>
<div class="font-medium">{{ ach.title }}</div>
<div class="text-xs text-slate-400">{{ ach.description }}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
definePageMeta({
middleware: 'auth'
})
const { user } = useAuth()
const { isSupported, subscription, requestPermission, subscribe: pushSubscribe } = usePush()
const { achievements, loadAchievements } = useAchievements()
const permissionGranted = ref(false)
const saving = ref(false)
const saveMessage = ref(null)
const userGroups = ref([])
const profileForm = reactive({
displayName: '',
username: '',
bio: '',
avatar: ''
})
const loadProfile = async () => {
try {
const data = await $fetch(`/api/users/${user.value.id}/profile`, {
headers: { Authorization: `Bearer ${useAuth().token.value}` }
})
const groupsData = await $fetch('/api/user/groups', {
headers: { Authorization: `Bearer ${useAuth().token.value}` }
})
userGroups.value = groupsData.groups || []
profileForm.displayName = data.profile.displayName || ''
profileForm.username = data.profile.user.username || ''
profileForm.bio = data.profile.bio || ''
profileForm.avatar = data.profile.avatar || ''
} catch (e) {
console.error('Failed to load profile', e)
}
}
const saveProfile = async () => {
saving.value = true
saveMessage.value = null
try {
await $fetch('/api/user/profile', {
method: 'PUT',
body: profileForm,
headers: { Authorization: `Bearer ${useAuth().token.value}` }
})
saveMessage.value = { type: 'success', text: 'Профиль обновлен успешно!' }
// Update local user data if needed
user.value.username = profileForm.username
} catch (e) {
saveMessage.value = { type: 'error', text: e.data?.message || 'Ошибка при сохранении' }
} finally {
saving.value = false
}
}
const enablePush = async () => {
const granted = await requestPermission()
permissionGranted.value = granted
if (granted) {
await pushSubscribe()
}
}
const subscribe = async () => {
await pushSubscribe()
}
onMounted(async () => {
permissionGranted.value = Notification.permission === 'granted'
await Promise.all([
loadAchievements(),
loadProfile()
])
})
</script>