Initial commit from monorepo
This commit is contained in:
123
app/components/UserAvatar.vue
Normal file
123
app/components/UserAvatar.vue
Normal file
@@ -0,0 +1,123 @@
|
||||
<template>
|
||||
<div class="flex flex-col items-center space-y-4">
|
||||
<!-- Avatar -->
|
||||
<div class="relative">
|
||||
<div
|
||||
class="w-24 h-24 rounded-full overflow-hidden border-4 border-base-300 shadow-lg"
|
||||
:class="{ 'animate-pulse bg-base-200': loading }"
|
||||
>
|
||||
<div
|
||||
v-if="!loading && avatarSvg"
|
||||
v-html="avatarSvg"
|
||||
class="w-full h-full"
|
||||
/>
|
||||
<div
|
||||
v-else-if="!loading"
|
||||
class="w-full h-full bg-gradient-to-br from-primary to-primary/70 flex items-center justify-center text-primary-content text-2xl font-bold"
|
||||
>
|
||||
{{ initials }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Change avatar button -->
|
||||
<button
|
||||
@click="regenerateAvatar"
|
||||
:disabled="loading"
|
||||
class="absolute -bottom-1 -right-1 w-8 h-8 bg-primary hover:bg-primary/80 disabled:bg-base-300 text-primary-content rounded-full flex items-center justify-center shadow-lg transition-colors"
|
||||
:title="$t('profile.regenerate_avatar')"
|
||||
>
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- User name -->
|
||||
<div class="text-center">
|
||||
<p class="font-semibold text-base-content">{{ displayName }}</p>
|
||||
<p class="text-sm text-base-content/60" v-if="userId">ID: {{ userId }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
userId: String,
|
||||
firstName: String,
|
||||
lastName: String,
|
||||
avatarId: String,
|
||||
editable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['avatar-changed'])
|
||||
|
||||
const loading = ref(false)
|
||||
const avatarSvg = ref('')
|
||||
|
||||
// Computed properties
|
||||
const displayName = computed(() => {
|
||||
const first = props.firstName || ''
|
||||
const last = props.lastName || ''
|
||||
return `${first} ${last}`.trim() || 'User'
|
||||
})
|
||||
|
||||
const initials = computed(() => {
|
||||
const first = props.firstName?.charAt(0) || ''
|
||||
const last = props.lastName?.charAt(0) || ''
|
||||
return (first + last).toUpperCase() || '?'
|
||||
})
|
||||
|
||||
// Generate avatar via DiceBear API
|
||||
const generateAvatar = async (seed) => {
|
||||
if (!seed) return
|
||||
|
||||
try {
|
||||
loading.value = true
|
||||
|
||||
// Use DiceBear API to generate SVG avatar
|
||||
const response = await fetch(`https://api.dicebear.com/7.x/avataaars/svg?seed=${encodeURIComponent(seed)}&backgroundColor=b6e3f4,c0aede,d1d4f9`)
|
||||
|
||||
if (response.ok) {
|
||||
avatarSvg.value = await response.text()
|
||||
} else {
|
||||
console.error('Failed to generate avatar:', response.status)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error generating avatar:', error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Generate new random avatar ID
|
||||
const regenerateAvatar = async () => {
|
||||
if (!props.editable || loading.value) return
|
||||
|
||||
const newAvatarId = Math.random().toString(36).substring(2, 15)
|
||||
|
||||
// Update avatar locally first
|
||||
await generateAvatar(newAvatarId)
|
||||
|
||||
// Notify parent about avatar change
|
||||
emit('avatar-changed', newAvatarId)
|
||||
}
|
||||
|
||||
// Watch avatarId changes
|
||||
watch(() => props.avatarId, (newAvatarId) => {
|
||||
if (newAvatarId) {
|
||||
generateAvatar(newAvatarId)
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
// If no avatarId, generate deterministic one based on userId
|
||||
onMounted(async () => {
|
||||
if (!props.avatarId && props.userId) {
|
||||
// Build deterministic ID from userId
|
||||
const fallbackSeed = props.userId
|
||||
await generateAvatar(fallbackSeed)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
Reference in New Issue
Block a user