feat: Update banner system components

This commit is contained in:
2025-04-22 15:36:38 -04:00
parent aa59d293cf
commit 3bb52e7555
3 changed files with 100 additions and 90 deletions

View File

@@ -33,9 +33,9 @@
<Cropper
ref="cropper"
:src="fileUrl"
:aspect-ratio="3"
:aspect-ratio="4"
:stencil-props="{
aspectRatio: 3,
aspectRatio: 4,
class: 'banner-stencil'
}"
/>
@@ -55,13 +55,20 @@
<div class="card-actions">
<button class="secondary"
@click="cancel">
@click="cancel"
:disabled="isUploading">
{{ t('cancel') }}
</button>
<button class="primary"
@click="showCropper ? applyCrop() : publish()"
:disabled="!selectedFile">
{{ showCropper ? t('apply') : t('save') }}
:disabled="!selectedFile || isUploading">
<template v-if="isUploading">
<span class="loading-spinner"></span>
{{ t('uploading') }} ({{ uploadProgress }}%)
</template>
<template v-else>
{{ showCropper ? t('apply') : t('save') }}
</template>
</button>
</div>
</div>
@@ -89,9 +96,8 @@ const fallbackUrl = '/images/hutopymedia/banners/hutopyul.png'
const errorMessage = ref('')
const showCropper = ref(false)
const cropper = ref(null)
const TARGET_WIDTH = 960
const TARGET_HEIGHT = 320
const isUploading = ref(false)
const uploadProgress = ref(0)
// Get translations for this component
const { t } = useI18n()
@@ -151,15 +157,22 @@ const applyCrop = () => {
const client = useClient()
const publish = async () => {
if (!selectedFile.value) return
if (!selectedFile.value || isUploading.value) return
try {
isUploading.value = true
uploadProgress.value = 0
const formData = new FormData()
formData.append('file', selectedFile.value)
const response = await client.post(
`/api/creators/${props.creator.id}/banner`,
formData
formData,
{
onUploadProgress: (progressEvent) => {
uploadProgress.value = Math.round((progressEvent.loaded * 100) / progressEvent.total)
}
}
)
props.creator.images.banner = `${response.data.blobUrl}?t=${Date.now()}`
@@ -167,6 +180,9 @@ const publish = async () => {
} catch (error) {
console.error(error)
errorMessage.value = t('errors.imageUpload')
} finally {
isUploading.value = false
uploadProgress.value = 0
}
}
@@ -202,8 +218,8 @@ const cancel = () => {
}
.preview-image {
@apply w-[960px];
@apply h-[320px];
@apply w-full;
@apply aspect-[4/1];
@apply object-cover;
}
@@ -275,21 +291,51 @@ const cancel = () => {
:deep(.cropper) {
@apply max-h-full;
}
.loading-spinner {
@apply inline-block;
@apply w-4;
@apply h-4;
@apply mr-2;
@apply border-2;
@apply border-white;
@apply border-t-transparent;
@apply rounded-full;
animation: spin 1s linear infinite;
}
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
</style>
<i18n>
{
"en": {
"title": "Banner Editor",
"description": "Upload or edit your profile banner image. The recommended size is 1024x256 pixels (4:1 ratio).",
"chooseImage": "Choose an image",
"clickToEdit": "Click to edit"
"clickToEdit": "Click to edit",
"uploading": "Uploading"
},
"fr": {
"title": "Éditeur de bannière",
"description": "Téléchargez ou modifiez votre image de bannière de profil. La taille recommandée est de 1024x256 pixels (ratio 4:1).",
"chooseImage": "Choisir une image",
"clickToEdit": "Cliquez pour modifier"
"clickToEdit": "Cliquez pour modifier",
"uploading": "Téléchargement"
},
"es": {
"title": "Editor de banner",
"description": "Sube o edita tu imagen de banner de perfil. El tamaño recomendado es de 1024x256 píxeles (ratio 4:1).",
"chooseImage": "Elegir una imagen",
"clickToEdit": "Haga clic para editar"
"clickToEdit": "Haga clic para editar",
"uploading": "Subiendo"
}
}
</i18n>