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'
|
import { supabase } from '../../backend/lib/supabase.js'
|
||||||
|
|
||||||
export default async function handler(req, res) {
|
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 { id } = req.query
|
||||||
|
|
||||||
const { data, error } = await supabase
|
if (req.method === 'GET') {
|
||||||
.from('guiones_generados')
|
const { data, error } = await supabase
|
||||||
.select('*')
|
.from('guiones_generados')
|
||||||
.eq('id', id)
|
.select('*')
|
||||||
.single()
|
.eq('id', id)
|
||||||
|
.single()
|
||||||
|
|
||||||
if (error) return res.status(404).json({ error: 'Guion no encontrado' })
|
if (error) return res.status(404).json({ error: 'Guion no encontrado' })
|
||||||
res.json({ generado: data })
|
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'
|
import { supabase } from '../../backend/lib/supabase.js'
|
||||||
|
|
||||||
export default async function handler(req, res) {
|
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 { id } = req.query
|
||||||
|
|
||||||
const { data, error } = await supabase
|
if (req.method === 'GET') {
|
||||||
.from('guiones')
|
const { data, error } = await supabase
|
||||||
.select('*')
|
.from('guiones')
|
||||||
.eq('id', id)
|
.select('*')
|
||||||
.single()
|
.eq('id', id)
|
||||||
|
.single()
|
||||||
|
|
||||||
if (error) return res.status(404).json({ error: 'Guion no encontrado' })
|
if (error) return res.status(404).json({ error: 'Guion no encontrado' })
|
||||||
res.json(data)
|
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)
|
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 ─────────────────────────────────────────
|
// ── GET /api/nichos ─────────────────────────────────────────
|
||||||
// Lista de nichos distintos para el selector del formulario
|
// Lista de nichos distintos para el selector del formulario
|
||||||
app.get('/api/nichos', async (req, res) => {
|
app.get('/api/nichos', async (req, res) => {
|
||||||
@ -348,6 +359,17 @@ app.get('/api/generados/:id', async (req, res) => {
|
|||||||
res.json(data)
|
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}`))
|
app.listen(PORT, () => console.log(`Backend local corriendo en http://localhost:${PORT}`))
|
||||||
|
|
||||||
// ── Middleware global de manejo de errores ───────────────────
|
// ── Middleware global de manejo de errores ───────────────────
|
||||||
|
|||||||
@ -28,10 +28,12 @@ export const api = {
|
|||||||
listar: (params = {}) => request('/guiones?' + new URLSearchParams(params)),
|
listar: (params = {}) => request('/guiones?' + new URLSearchParams(params)),
|
||||||
listarTodos: (params = {}) => request('/guiones?' + new URLSearchParams({ ...params, todos: '1' })),
|
listarTodos: (params = {}) => request('/guiones?' + new URLSearchParams({ ...params, todos: '1' })),
|
||||||
obtener: (id) => request(`/guiones/${id}`),
|
obtener: (id) => request(`/guiones/${id}`),
|
||||||
|
eliminar: (id) => request(`/guiones/${id}`, { method: 'DELETE' }),
|
||||||
},
|
},
|
||||||
generados: {
|
generados: {
|
||||||
listar: (params = {}) => request('/generados?' + new URLSearchParams(params)),
|
listar: (params = {}) => request('/generados?' + new URLSearchParams(params)),
|
||||||
obtener: (id) => request(`/generados/${id}`),
|
obtener: (id) => request(`/generados/${id}`),
|
||||||
|
eliminar: (id) => request(`/generados/${id}`, { method: 'DELETE' }),
|
||||||
},
|
},
|
||||||
analizar: (body) => request('/analizar', { method: 'POST', body: JSON.stringify(body) }),
|
analizar: (body) => request('/analizar', { method: 'POST', body: JSON.stringify(body) }),
|
||||||
generar: (body) => request('/generar', { method: 'POST', body: JSON.stringify(body) }),
|
generar: (body) => request('/generar', { method: 'POST', body: JSON.stringify(body) }),
|
||||||
|
|||||||
@ -178,9 +178,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</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">
|
<div class="flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-all">
|
||||||
<span class="material-symbols-outlined text-[16px]">open_in_new</span>
|
<button class="p-1.5 rounded-lg text-ink-3 hover:text-accent hover:bg-accent-subtle transition-all">
|
||||||
</button>
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -301,6 +311,7 @@ const totalGuiones = ref(0)
|
|||||||
const modalGenerado = ref(null)
|
const modalGenerado = ref(null)
|
||||||
const cargandoModal = ref(false)
|
const cargandoModal = ref(false)
|
||||||
const copiadoModal = ref(false)
|
const copiadoModal = ref(false)
|
||||||
|
const eliminandoId = ref(null)
|
||||||
|
|
||||||
const filtros = ref({
|
const filtros = ref({
|
||||||
tipo: 'analizados',
|
tipo: 'analizados',
|
||||||
@ -410,6 +421,30 @@ async function copiarGuionModal() {
|
|||||||
setTimeout(() => { copiadoModal.value = false }, 2000)
|
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) {
|
function plataformaBadge(p) {
|
||||||
const map = {
|
const map = {
|
||||||
tiktok: 'bg-red-950 text-red-400 border border-red-800',
|
tiktok: 'bg-red-950 text-red-400 border border-red-800',
|
||||||
|
|||||||
Reference in New Issue
Block a user