All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 1m4s
- Agregar botón "Crear Webhook de Debug" en WebhookReceiverSection - Detectar si ya existe un webhook apuntando al receptor de debug - Permitir eliminar el webhook de debug - Incluir todos los eventos disponibles al crear el webhook - También incluye mejoras previas de manejo de media y mensajes
214 lines
5.4 KiB
Vue
214 lines
5.4 KiB
Vue
<template>
|
|
<div
|
|
class="flex items-center gap-3 p-3 rounded-lg cursor-pointer transition-colors min-w-[200px] max-w-[280px]"
|
|
:class="fromMe ? 'bg-white/10 hover:bg-white/20' : 'bg-[var(--wa-bg-light)] hover:bg-[var(--wa-border)]'"
|
|
@click="downloadFile"
|
|
>
|
|
<!-- File icon -->
|
|
<div
|
|
class="w-10 h-10 rounded-lg flex items-center justify-center flex-shrink-0"
|
|
:class="iconBgClass"
|
|
>
|
|
<UIcon :name="fileIcon" class="w-5 h-5 text-white" />
|
|
</div>
|
|
|
|
<!-- File info -->
|
|
<div class="flex-1 min-w-0">
|
|
<p
|
|
class="text-sm font-medium truncate"
|
|
:class="fromMe ? 'text-white' : 'text-[var(--wa-text)]'"
|
|
>
|
|
{{ fileName }}
|
|
</p>
|
|
<p
|
|
class="text-xs mt-0.5"
|
|
:class="fromMe ? 'text-white/70' : 'text-[var(--wa-text-muted)]'"
|
|
>
|
|
{{ fileInfo }}
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Download indicator -->
|
|
<div class="flex-shrink-0">
|
|
<UIcon
|
|
v-if="loading"
|
|
name="i-lucide-loader-2"
|
|
class="w-5 h-5 animate-spin"
|
|
:class="fromMe ? 'text-white/70' : 'text-[var(--wa-text-muted)]'"
|
|
/>
|
|
<UIcon
|
|
v-else
|
|
name="i-lucide-download"
|
|
class="w-5 h-5"
|
|
:class="fromMe ? 'text-white/70' : 'text-[var(--wa-text-muted)]'"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import type { MediaInfo } from '~/types/message'
|
|
import { formatFileSize } from '~/types/message'
|
|
|
|
interface Props {
|
|
media: MediaInfo
|
|
instanceId: string
|
|
messageId: string
|
|
fromMe?: boolean
|
|
}
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
fromMe: false
|
|
})
|
|
|
|
const loading = ref(false)
|
|
|
|
const fileName = computed(() => {
|
|
return props.media.filename || 'Documento'
|
|
})
|
|
|
|
const fileInfo = computed(() => {
|
|
const parts: string[] = []
|
|
|
|
// Extensión
|
|
const ext = getFileExtension(props.media.filename || '', props.media.mimetype)
|
|
if (ext) parts.push(ext.toUpperCase())
|
|
|
|
// Tamaño
|
|
if (props.media.filesize) {
|
|
parts.push(formatFileSize(props.media.filesize))
|
|
}
|
|
|
|
return parts.join(' • ') || 'Documento'
|
|
})
|
|
|
|
const fileIcon = computed(() => {
|
|
const mimetype = props.media.mimetype || ''
|
|
const filename = props.media.filename || ''
|
|
|
|
// PDF
|
|
if (mimetype.includes('pdf') || filename.endsWith('.pdf')) {
|
|
return 'i-lucide-file-text'
|
|
}
|
|
|
|
// Hojas de cálculo
|
|
if (mimetype.includes('spreadsheet') || mimetype.includes('excel') ||
|
|
filename.match(/\.(xlsx?|csv|ods)$/i)) {
|
|
return 'i-lucide-file-spreadsheet'
|
|
}
|
|
|
|
// Documentos de texto
|
|
if (mimetype.includes('word') || mimetype.includes('document') ||
|
|
filename.match(/\.(docx?|odt|rtf)$/i)) {
|
|
return 'i-lucide-file-text'
|
|
}
|
|
|
|
// Presentaciones
|
|
if (mimetype.includes('presentation') || mimetype.includes('powerpoint') ||
|
|
filename.match(/\.(pptx?|odp)$/i)) {
|
|
return 'i-lucide-file-presentation'
|
|
}
|
|
|
|
// Código
|
|
if (mimetype.includes('javascript') || mimetype.includes('json') ||
|
|
mimetype.includes('html') || mimetype.includes('css') ||
|
|
filename.match(/\.(js|ts|json|html|css|py|java|c|cpp|go|rs)$/i)) {
|
|
return 'i-lucide-file-code'
|
|
}
|
|
|
|
// Comprimidos
|
|
if (mimetype.includes('zip') || mimetype.includes('rar') ||
|
|
mimetype.includes('tar') || mimetype.includes('gzip') ||
|
|
filename.match(/\.(zip|rar|7z|tar|gz)$/i)) {
|
|
return 'i-lucide-file-archive'
|
|
}
|
|
|
|
// APK
|
|
if (filename.endsWith('.apk')) {
|
|
return 'i-lucide-smartphone'
|
|
}
|
|
|
|
return 'i-lucide-file'
|
|
})
|
|
|
|
const iconBgClass = computed(() => {
|
|
const mimetype = props.media.mimetype || ''
|
|
const filename = props.media.filename || ''
|
|
|
|
// PDF - rojo
|
|
if (mimetype.includes('pdf') || filename.endsWith('.pdf')) {
|
|
return 'bg-red-500'
|
|
}
|
|
|
|
// Excel - verde
|
|
if (mimetype.includes('spreadsheet') || mimetype.includes('excel') ||
|
|
filename.match(/\.(xlsx?|csv)$/i)) {
|
|
return 'bg-green-600'
|
|
}
|
|
|
|
// Word - azul
|
|
if (mimetype.includes('word') || filename.match(/\.(docx?)$/i)) {
|
|
return 'bg-blue-600'
|
|
}
|
|
|
|
// PowerPoint - naranja
|
|
if (mimetype.includes('presentation') || filename.match(/\.(pptx?)$/i)) {
|
|
return 'bg-orange-500'
|
|
}
|
|
|
|
// Comprimidos - amarillo
|
|
if (mimetype.includes('zip') || mimetype.includes('rar') ||
|
|
filename.match(/\.(zip|rar|7z|tar|gz)$/i)) {
|
|
return 'bg-yellow-600'
|
|
}
|
|
|
|
// Código - morado
|
|
if (mimetype.includes('javascript') || mimetype.includes('json') ||
|
|
filename.match(/\.(js|ts|json|py|java)$/i)) {
|
|
return 'bg-purple-600'
|
|
}
|
|
|
|
return 'bg-gray-500'
|
|
})
|
|
|
|
function getFileExtension(filename: string, mimetype?: string): string {
|
|
// Intentar extraer de filename
|
|
const match = filename.match(/\.([a-zA-Z0-9]+)$/)
|
|
if (match) return match[1]
|
|
|
|
// Intentar extraer de mimetype
|
|
if (mimetype) {
|
|
const parts = mimetype.split('/')
|
|
if (parts.length > 1) {
|
|
const subtype = parts[1]
|
|
// Limpiar formatos como "vnd.openxmlformats-officedocument..."
|
|
if (subtype.includes('spreadsheet')) return 'xlsx'
|
|
if (subtype.includes('document')) return 'docx'
|
|
if (subtype.includes('presentation')) return 'pptx'
|
|
return subtype
|
|
}
|
|
}
|
|
|
|
return ''
|
|
}
|
|
|
|
const downloadFile = async () => {
|
|
loading.value = true
|
|
|
|
try {
|
|
const url = props.media.url || `/api/media/${props.instanceId}/${props.messageId}`
|
|
|
|
// Crear link de descarga
|
|
const a = document.createElement('a')
|
|
a.href = url
|
|
a.download = props.media.filename || 'documento'
|
|
a.target = '_blank'
|
|
document.body.appendChild(a)
|
|
a.click()
|
|
document.body.removeChild(a)
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
</script>
|