Feature: Agregar botón para crear webhook de debug automáticamente
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 1m4s
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
This commit is contained in:
172
app/components/messages/MediaPreview.vue
Normal file
172
app/components/messages/MediaPreview.vue
Normal file
@@ -0,0 +1,172 @@
|
||||
<template>
|
||||
<div class="space-y-2">
|
||||
<!-- Files grid -->
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<div
|
||||
v-for="(file, index) in files"
|
||||
:key="index"
|
||||
class="relative group"
|
||||
>
|
||||
<!-- Image preview -->
|
||||
<div
|
||||
v-if="isImage(file)"
|
||||
class="relative"
|
||||
>
|
||||
<img
|
||||
:src="getPreviewUrl(file)"
|
||||
class="h-20 w-20 object-cover rounded-lg"
|
||||
/>
|
||||
<button
|
||||
class="absolute -top-2 -right-2 w-5 h-5 rounded-full bg-red-500 text-white flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity"
|
||||
@click="$emit('remove', index)"
|
||||
>
|
||||
<UIcon name="i-lucide-x" class="w-3 h-3" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Video preview -->
|
||||
<div
|
||||
v-else-if="isVideo(file)"
|
||||
class="relative"
|
||||
>
|
||||
<div class="h-20 w-20 bg-gray-800 rounded-lg flex items-center justify-center">
|
||||
<video
|
||||
:src="getPreviewUrl(file)"
|
||||
class="h-full w-full object-cover rounded-lg"
|
||||
/>
|
||||
<div class="absolute inset-0 flex items-center justify-center">
|
||||
<UIcon name="i-lucide-play-circle" class="w-8 h-8 text-white/80" />
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
class="absolute -top-2 -right-2 w-5 h-5 rounded-full bg-red-500 text-white flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity"
|
||||
@click="$emit('remove', index)"
|
||||
>
|
||||
<UIcon name="i-lucide-x" class="w-3 h-3" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Audio preview -->
|
||||
<div
|
||||
v-else-if="isAudio(file)"
|
||||
class="relative flex items-center gap-2 px-3 py-2 bg-gray-800 rounded-lg"
|
||||
>
|
||||
<UIcon name="i-lucide-music" class="w-5 h-5 text-[var(--wa-green-light)]" />
|
||||
<span class="text-sm text-white truncate max-w-[100px]">{{ file.name }}</span>
|
||||
<button
|
||||
class="w-5 h-5 rounded-full bg-red-500 text-white flex items-center justify-center"
|
||||
@click="$emit('remove', index)"
|
||||
>
|
||||
<UIcon name="i-lucide-x" class="w-3 h-3" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Document preview -->
|
||||
<div
|
||||
v-else
|
||||
class="relative flex items-center gap-2 px-3 py-2 bg-gray-800 rounded-lg"
|
||||
>
|
||||
<UIcon :name="getDocIcon(file)" class="w-5 h-5 text-[var(--wa-blue)]" />
|
||||
<div class="flex flex-col min-w-0">
|
||||
<span class="text-sm text-white truncate max-w-[100px]">{{ file.name }}</span>
|
||||
<span class="text-xs text-gray-400">{{ formatSize(file.size) }}</span>
|
||||
</div>
|
||||
<button
|
||||
class="w-5 h-5 rounded-full bg-red-500 text-white flex items-center justify-center flex-shrink-0"
|
||||
@click="$emit('remove', index)"
|
||||
>
|
||||
<UIcon name="i-lucide-x" class="w-3 h-3" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Caption input -->
|
||||
<div v-if="showCaption && files.length > 0">
|
||||
<UInput
|
||||
:model-value="caption"
|
||||
placeholder="Agregar descripción..."
|
||||
size="sm"
|
||||
class="bg-[var(--wa-bg-light)]"
|
||||
@update:model-value="$emit('update:caption', $event)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
files: File[]
|
||||
caption?: string
|
||||
showCaption?: boolean
|
||||
}
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
caption: '',
|
||||
showCaption: true
|
||||
})
|
||||
|
||||
defineEmits<{
|
||||
remove: [index: number]
|
||||
'update:caption': [value: string]
|
||||
}>()
|
||||
|
||||
// Preview URL cache
|
||||
const previewUrls = new Map<File, string>()
|
||||
|
||||
const getPreviewUrl = (file: File): string => {
|
||||
if (!previewUrls.has(file)) {
|
||||
previewUrls.set(file, URL.createObjectURL(file))
|
||||
}
|
||||
return previewUrls.get(file)!
|
||||
}
|
||||
|
||||
// Cleanup URLs on unmount
|
||||
onUnmounted(() => {
|
||||
previewUrls.forEach(url => URL.revokeObjectURL(url))
|
||||
previewUrls.clear()
|
||||
})
|
||||
|
||||
const isImage = (file: File): boolean => {
|
||||
return file.type.startsWith('image/')
|
||||
}
|
||||
|
||||
const isVideo = (file: File): boolean => {
|
||||
return file.type.startsWith('video/')
|
||||
}
|
||||
|
||||
const isAudio = (file: File): boolean => {
|
||||
return file.type.startsWith('audio/')
|
||||
}
|
||||
|
||||
const getDocIcon = (file: File): string => {
|
||||
const type = file.type
|
||||
const name = file.name.toLowerCase()
|
||||
|
||||
if (type.includes('pdf') || name.endsWith('.pdf')) {
|
||||
return 'i-lucide-file-text'
|
||||
}
|
||||
if (type.includes('spreadsheet') || name.match(/\.(xlsx?|csv)$/)) {
|
||||
return 'i-lucide-file-spreadsheet'
|
||||
}
|
||||
if (type.includes('word') || name.match(/\.(docx?)$/)) {
|
||||
return 'i-lucide-file-text'
|
||||
}
|
||||
if (type.includes('presentation') || name.match(/\.(pptx?)$/)) {
|
||||
return 'i-lucide-file-presentation'
|
||||
}
|
||||
if (type.includes('zip') || name.match(/\.(zip|rar|7z)$/)) {
|
||||
return 'i-lucide-file-archive'
|
||||
}
|
||||
|
||||
return 'i-lucide-file'
|
||||
}
|
||||
|
||||
const formatSize = (bytes: number): string => {
|
||||
if (bytes === 0) return '0 B'
|
||||
const k = 1024
|
||||
const sizes = ['B', 'KB', 'MB', 'GB']
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
||||
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user