Clone
LogoUploader.vue
<template>
  <div class="space-y-4">
    <div v-if="entry.logo_url" class="space-y-3">
      <div class="flex items-center space-x-4">
        <img 
          :src="getLogoUrl(entry.logo_url)" 
          alt="Logo" 
          class="w-20 h-20 object-contain border rounded-lg p-2"
        />
        <div class="flex-1">
          <p class="text-sm text-gray-600 mb-2">Current logo</p>
          <button
            @click="deleteLogo"
            :disabled="deleting"
            class="text-red-600 hover:text-red-700 text-sm font-medium"
          >
            {{ deleting ? 'Deleting...' : 'Remove Logo' }}
          </button>
        </div>
      </div>
    </div>
    
    <div>
      <label class="block text-sm font-medium text-gray-700 mb-1">
        {{ entry.logo_url ? 'Replace Logo' : 'Add Logo' }}
      </label>
      <input
        type="file"
        ref="fileInput"
        accept="image/png,image/jpeg,image/svg+xml,image/webp"
        @change="handleFileSelect"
        class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 file:mr-4 file:py-2 file:px-4 file:rounded-lg file:border-0 file:text-sm file:font-semibold file:bg-indigo-50 file:text-indigo-700 hover:file:bg-indigo-100"
      />
      <p class="text-xs text-gray-500 mt-1">
        PNG, JPG, SVG, or WebP. Max 5MB.
      </p>
    </div>
    
    <div v-if="selectedFile" class="flex justify-end space-x-3">
      <button
        @click="cancelUpload"
        class="px-4 py-2 text-gray-700 bg-gray-100 rounded-lg hover:bg-gray-200 transition"
      >
        Cancel
      </button>
      <button
        @click="uploadLogo"
        :disabled="uploading"
        class="px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition disabled:opacity-50"
      >
        {{ uploading ? 'Uploading...' : 'Upload Logo' }}
      </button>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { useAuthStore } from '../stores/auth'

const props = defineProps({
  entry: {
    type: Object,
    required: true
  }
})

const emit = defineEmits(['update'])

const authStore = useAuthStore()
const fileInput = ref(null)
const selectedFile = ref(null)
const uploading = ref(false)
const deleting = ref(false)

function getLogoUrl(logoUrl) {
  // If it's a relative URL, prepend the backend URL
  if (logoUrl?.startsWith('/')) {
    return `${import.meta.env.VITE_API_URL?.replace('/api', '') || 'http://localhost:8787'}${logoUrl}`
  }
  return logoUrl
}

function handleFileSelect(event) {
  const file = event.target.files[0]
  if (file) {
    // Validate file
    const maxSize = 5 * 1024 * 1024 // 5MB
    if (file.size > maxSize) {
      alert('File too large. Maximum size: 5MB')
      event.target.value = ''
      return
    }
    
    const allowedTypes = ['image/png', 'image/jpeg', 'image/svg+xml', 'image/webp']
    if (!allowedTypes.includes(file.type)) {
      alert('Invalid file type. Allowed: PNG, JPG, SVG, WebP')
      event.target.value = ''
      return
    }
    
    selectedFile.value = file
  }
}

function cancelUpload() {
  selectedFile.value = null
  if (fileInput.value) {
    fileInput.value.value = ''
  }
}

async function uploadLogo() {
  if (!selectedFile.value) return
  
  uploading.value = true
  const formData = new FormData()
  formData.append('logo', selectedFile.value)
  formData.append('entryId', props.entry.id)
  
  try {
    const response = await fetch('/api/logo/upload', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${authStore.token}`
      },
      body: formData
    })
    
    if (!response.ok) {
      const error = await response.json()
      throw new Error(error.error || 'Failed to upload logo')
    }
    
    const data = await response.json()
    
    // Emit update event with new logo URL
    emit('update', { ...props.entry, logo_url: data.logoUrl })
    
    // Reset
    selectedFile.value = null
    if (fileInput.value) {
      fileInput.value.value = ''
    }
    
    alert('Logo uploaded successfully!')
  } catch (error) {
    alert(error.message || 'Failed to upload logo')
  } finally {
    uploading.value = false
  }
}

async function deleteLogo() {
  if (!confirm('Are you sure you want to remove the logo?')) return
  
  deleting.value = true
  
  try {
    const response = await fetch(`/api/logo/delete/${props.entry.id}`, {
      method: 'DELETE',
      headers: {
        'Authorization': `Bearer ${authStore.token}`
      }
    })
    
    if (!response.ok) {
      const error = await response.json()
      throw new Error(error.error || 'Failed to delete logo')
    }
    
    // Emit update event with no logo
    emit('update', { ...props.entry, logo_url: null })
    
    alert('Logo removed successfully!')
  } catch (error) {
    alert(error.message || 'Failed to delete logo')
  } finally {
    deleting.value = false
  }
}
</script>