feat(scripts): agregar capacidad de eliminar guiones desde el panel
Añade endpoints DELETE para guiones y guiones_generados (Vercel + Express), métodos en api.js y botón de papelera con confirmación en ScriptsView. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@ -1,16 +1,28 @@
|
||||
import { supabase } from '../../backend/lib/supabase.js'
|
||||
|
||||
export default async function handler(req, res) {
|
||||
if (req.method !== 'GET') return res.status(405).json({ error: 'Método no permitido' })
|
||||
|
||||
const { id } = req.query
|
||||
|
||||
const { data, error } = await supabase
|
||||
.from('guiones_generados')
|
||||
.select('*')
|
||||
.eq('id', id)
|
||||
.single()
|
||||
if (req.method === 'GET') {
|
||||
const { data, error } = await supabase
|
||||
.from('guiones_generados')
|
||||
.select('*')
|
||||
.eq('id', id)
|
||||
.single()
|
||||
|
||||
if (error) return res.status(404).json({ error: 'Guion no encontrado' })
|
||||
res.json({ generado: data })
|
||||
if (error) return res.status(404).json({ error: 'Guion no encontrado' })
|
||||
return res.json({ generado: data })
|
||||
}
|
||||
|
||||
if (req.method === 'DELETE') {
|
||||
const { error } = await supabase
|
||||
.from('guiones_generados')
|
||||
.delete()
|
||||
.eq('id', id)
|
||||
|
||||
if (error) return res.status(500).json({ error: error.message })
|
||||
return res.json({ ok: true })
|
||||
}
|
||||
|
||||
res.status(405).json({ error: 'Método no permitido' })
|
||||
}
|
||||
|
||||
@ -1,16 +1,28 @@
|
||||
import { supabase } from '../../backend/lib/supabase.js'
|
||||
|
||||
export default async function handler(req, res) {
|
||||
if (req.method !== 'GET') return res.status(405).json({ error: 'Método no permitido' })
|
||||
|
||||
const { id } = req.query
|
||||
|
||||
const { data, error } = await supabase
|
||||
.from('guiones')
|
||||
.select('*')
|
||||
.eq('id', id)
|
||||
.single()
|
||||
if (req.method === 'GET') {
|
||||
const { data, error } = await supabase
|
||||
.from('guiones')
|
||||
.select('*')
|
||||
.eq('id', id)
|
||||
.single()
|
||||
|
||||
if (error) return res.status(404).json({ error: 'Guion no encontrado' })
|
||||
res.json(data)
|
||||
if (error) return res.status(404).json({ error: 'Guion no encontrado' })
|
||||
return res.json(data)
|
||||
}
|
||||
|
||||
if (req.method === 'DELETE') {
|
||||
const { error } = await supabase
|
||||
.from('guiones')
|
||||
.delete()
|
||||
.eq('id', id)
|
||||
|
||||
if (error) return res.status(500).json({ error: error.message })
|
||||
return res.json({ ok: true })
|
||||
}
|
||||
|
||||
res.status(405).json({ error: 'Método no permitido' })
|
||||
}
|
||||
|
||||
@ -65,6 +65,17 @@ app.get('/api/guiones/:id', async (req, res) => {
|
||||
res.json(data)
|
||||
})
|
||||
|
||||
// ── DELETE /api/guiones/:id ─────────────────────────────────
|
||||
app.delete('/api/guiones/:id', async (req, res) => {
|
||||
const { error } = await supabase
|
||||
.from('guiones')
|
||||
.delete()
|
||||
.eq('id', req.params.id)
|
||||
|
||||
if (error) return res.status(500).json({ error: error.message })
|
||||
res.json({ ok: true })
|
||||
})
|
||||
|
||||
// ── GET /api/nichos ─────────────────────────────────────────
|
||||
// Lista de nichos distintos para el selector del formulario
|
||||
app.get('/api/nichos', async (req, res) => {
|
||||
@ -348,6 +359,17 @@ app.get('/api/generados/:id', async (req, res) => {
|
||||
res.json(data)
|
||||
})
|
||||
|
||||
// ── DELETE /api/generados/:id ───────────────────────────────
|
||||
app.delete('/api/generados/:id', async (req, res) => {
|
||||
const { error } = await supabase
|
||||
.from('guiones_generados')
|
||||
.delete()
|
||||
.eq('id', req.params.id)
|
||||
|
||||
if (error) return res.status(500).json({ error: error.message })
|
||||
res.json({ ok: true })
|
||||
})
|
||||
|
||||
app.listen(PORT, () => console.log(`Backend local corriendo en http://localhost:${PORT}`))
|
||||
|
||||
// ── Middleware global de manejo de errores ───────────────────
|
||||
|
||||
@ -28,10 +28,12 @@ export const api = {
|
||||
listar: (params = {}) => request('/guiones?' + new URLSearchParams(params)),
|
||||
listarTodos: (params = {}) => request('/guiones?' + new URLSearchParams({ ...params, todos: '1' })),
|
||||
obtener: (id) => request(`/guiones/${id}`),
|
||||
eliminar: (id) => request(`/guiones/${id}`, { method: 'DELETE' }),
|
||||
},
|
||||
generados: {
|
||||
listar: (params = {}) => request('/generados?' + new URLSearchParams(params)),
|
||||
obtener: (id) => request(`/generados/${id}`),
|
||||
listar: (params = {}) => request('/generados?' + new URLSearchParams(params)),
|
||||
obtener: (id) => request(`/generados/${id}`),
|
||||
eliminar: (id) => request(`/generados/${id}`, { method: 'DELETE' }),
|
||||
},
|
||||
analizar: (body) => request('/analizar', { method: 'POST', body: JSON.stringify(body) }),
|
||||
generar: (body) => request('/generar', { method: 'POST', body: JSON.stringify(body) }),
|
||||
|
||||
@ -178,9 +178,19 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<button class="p-1.5 rounded-lg text-ink-3 hover:text-accent hover:bg-accent-subtle transition-all opacity-0 group-hover:opacity-100">
|
||||
<span class="material-symbols-outlined text-[16px]">open_in_new</span>
|
||||
</button>
|
||||
<div class="flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-all">
|
||||
<button class="p-1.5 rounded-lg text-ink-3 hover:text-accent hover:bg-accent-subtle transition-all">
|
||||
<span class="material-symbols-outlined text-[16px]">open_in_new</span>
|
||||
</button>
|
||||
<button
|
||||
@click.stop="confirmarEliminar(g)"
|
||||
:disabled="eliminandoId === g.id"
|
||||
class="p-1.5 rounded-lg text-ink-3 hover:text-red-400 hover:bg-red-950/40 transition-all disabled:opacity-50"
|
||||
>
|
||||
<span v-if="eliminandoId === g.id" class="material-symbols-outlined text-[16px] animate-spin">progress_activity</span>
|
||||
<span v-else class="material-symbols-outlined text-[16px]">delete</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -301,6 +311,7 @@ const totalGuiones = ref(0)
|
||||
const modalGenerado = ref(null)
|
||||
const cargandoModal = ref(false)
|
||||
const copiadoModal = ref(false)
|
||||
const eliminandoId = ref(null)
|
||||
|
||||
const filtros = ref({
|
||||
tipo: 'analizados',
|
||||
@ -410,6 +421,30 @@ async function copiarGuionModal() {
|
||||
setTimeout(() => { copiadoModal.value = false }, 2000)
|
||||
}
|
||||
|
||||
async function confirmarEliminar(g) {
|
||||
const titulo = filtros.value.tipo === 'analizados'
|
||||
? (g.tema_principal || 'este guion')
|
||||
: (g.titulo_sugerido || 'este guion')
|
||||
|
||||
if (!confirm(`¿Eliminar "${titulo}"? Esta acción no se puede deshacer.`)) return
|
||||
|
||||
eliminandoId.value = g.id
|
||||
try {
|
||||
if (filtros.value.tipo === 'analizados') {
|
||||
await api.guiones.eliminar(g.id)
|
||||
} else {
|
||||
await api.generados.eliminar(g.id)
|
||||
}
|
||||
guiones.value = guiones.value.filter(x => x.id !== g.id)
|
||||
totalGuiones.value = Math.max(0, totalGuiones.value - 1)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
alert('No se pudo eliminar: ' + e.message)
|
||||
} finally {
|
||||
eliminandoId.value = null
|
||||
}
|
||||
}
|
||||
|
||||
function plataformaBadge(p) {
|
||||
const map = {
|
||||
tiktok: 'bg-red-950 text-red-400 border border-red-800',
|
||||
|
||||
Reference in New Issue
Block a user