4 Commits

Author SHA1 Message Date
6e2b3d72f1 Se envían correctamente los ids de los archivos de referencia para su procesamiento en el backend 2025-10-27 17:14:50 -06:00
0c5c3f935b comm 2025-10-27 15:45:16 -06:00
8da08b6bf1 La pantalla se volvía negra al abrir el dialogo de eliminar carrera
La razón es que se rendereaba un dialogo de borrado por carrera, pero al abrir uno se abrian los demás también
2025-10-24 13:01:33 -06:00
1fe8f2b6a8 Se corrigen bugs sobre crear carreras, filtrado y que aparezcan las materias cuando se crean 2025-10-24 12:36:39 -06:00
5 changed files with 56 additions and 23 deletions

View File

@@ -30,7 +30,7 @@ export function useDeleteCarreraDialog(carreraId: string, onDeleted?: () => void
const dialog = ( const dialog = (
<Dialog open={open} onOpenChange={setOpen}> <Dialog open={open} onOpenChange={setOpen}>
<DialogContent> <DialogContent className="bg-white">
<DialogHeader> <DialogHeader>
<DialogTitle className="font-mono" >¿Eliminar carrera?</DialogTitle> <DialogTitle className="font-mono" >¿Eliminar carrera?</DialogTitle>
<DialogDescription> <DialogDescription>

View File

@@ -11,9 +11,11 @@ import { supabase, useSupabaseAuth } from "@/auth/supabase"
import { Field } from "./Field" import { Field } from "./Field"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs" import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs"
import { asignaturaKeys } from "./planQueries" import { asignaturaKeys } from "./planQueries"
import { useRouter } from "@tanstack/react-router"
export function AddAsignaturaButton({ planId, onAdded }: { planId: string; onAdded?: () => void }) { export function AddAsignaturaButton({ planId, onAdded }: { planId: string; onAdded?: () => void }) {
const qc = useQueryClient() const qc = useQueryClient()
const router = useRouter()
const supabaseAuth = useSupabaseAuth() const supabaseAuth = useSupabaseAuth()
const [open, setOpen] = useState(false) const [open, setOpen] = useState(false)
const [saving, setSaving] = useState(false) const [saving, setSaving] = useState(false)
@@ -45,7 +47,13 @@ export function AddAsignaturaButton({ planId, onAdded }: { planId: string; onAdd
objetivos: toNull(f.objetivos), objetivos: toNull(f.objetivos),
contenidos: [], bibliografia: [], criterios_evaluacion: null, contenidos: [], bibliografia: [], criterios_evaluacion: null,
} }
const { error } = await supabase.from("asignaturas").insert([payload]) const { error,data } = await supabase.from("asignaturas").insert([payload]).select().single()
console.log(data);
router.invalidate()
router.navigate({
to: "/asignatura/$asignaturaId",
params: { asignaturaId: data.id },
})
setSaving(false) setSaving(false)
if (error) { alert(error.message); return } if (error) { alert(error.message); return }
setOpen(false) setOpen(false)
@@ -64,8 +72,17 @@ export function AddAsignaturaButton({ planId, onAdded }: { planId: string; onAdd
body: JSON.stringify({ planEstudiosId: planId, prompt: iaPrompt, semestre: iaSemestre.trim() ? Number(iaSemestre) : undefined, insert: true, uuid: supabaseAuth.user?.id }), body: JSON.stringify({ planEstudiosId: planId, prompt: iaPrompt, semestre: iaSemestre.trim() ? Number(iaSemestre) : undefined, insert: true, uuid: supabaseAuth.user?.id }),
}) })
if (!res.ok) throw new Error(await res.text()) if (!res.ok) throw new Error(await res.text())
const data = await res.json()
console.log("Asignatura generada:", data)
const asignaturaId = data.asignaturaId || data.insertResult?.id
if (!asignaturaId) throw new Error("No se recibió el ID de la asignatura generada")
confetti({ particleCount: 120, spread: 80, origin: { y: 0.6 } }) confetti({ particleCount: 120, spread: 80, origin: { y: 0.6 } })
setOpen(false) setOpen(false)
router.invalidate()
router.navigate({
to: "/asignatura/$asignaturaId",
params: { asignaturaId },
})
onAdded?.() onAdded?.()
// qc.invalidateQueries({ queryKey: asignaturaKeys.preview(planId) }) // qc.invalidateQueries({ queryKey: asignaturaKeys.preview(planId) })
// qc.invalidateQueries({ queryKey: asignaturaKeys.count(planId) }) // qc.invalidateQueries({ queryKey: asignaturaKeys.count(planId) })

View File

@@ -104,7 +104,7 @@ export function CreatePlanDialog({ open, onOpenChange }: { open: boolean; onOpen
setDbFiles((data || []).map((file: any) => ({ setDbFiles((data || []).map((file: any) => ({
id: file.documentos_id, id: file.documentos_id,
titulo: file.titulo_archivo, titulo: file.titulo_archivo,
s3_file_path: file.titulo_archivo, s3_file_path: `prueba-referencias/documento_${file.documentos_id}.pdf`,
fecha_subida: file.fecha_subida, fecha_subida: file.fecha_subida,
tags: file.tags || [], tags: file.tags || [],
}))); })));
@@ -118,35 +118,35 @@ export function CreatePlanDialog({ open, onOpenChange }: { open: boolean; onOpen
const isSelected = useCallback((path: string) => selectedFiles.includes(path), [selectedFiles]); const isSelected = useCallback((path: string) => selectedFiles.includes(path), [selectedFiles]);
const toggleSelected = useCallback((path: string) => { const toggleSelected = useCallback((id: string) => {
setSelectedFiles(prev => prev.includes(path) ? prev.filter(p => p !== path) : [...prev, path]); setSelectedFiles(prev => prev.includes(id) ? prev.filter(p => p !== id) : [...prev, id]);
}, []); }, []);
const replaceSelection = useCallback((path: string) => { const replaceSelection = useCallback((id: string) => {
setSelectedFiles([path]); setSelectedFiles([id]);
}, []); }, []);
const rangeSelect = useCallback((start: number, end: number) => { const rangeSelect = useCallback((start: number, end: number) => {
const [s, e] = start < end ? [start, end] : [end, start]; const [s, e] = start < end ? [start, end] : [end, start];
const paths = dbFiles.slice(s, e + 1).map(f => f.s3_file_path); const ids = dbFiles.slice(s, e + 1).map(f => f.id);
setSelectedFiles(prev => Array.from(new Set([...prev, ...paths]))); setSelectedFiles(prev => Array.from(new Set([...prev, ...ids])));
}, [dbFiles]); }, [dbFiles]);
const handleCardClick = useCallback((e: React.MouseEvent, index: number, file: { s3_file_path: string }) => { const handleCardClick = useCallback((e: React.MouseEvent, index: number, file: { id: string }) => {
const path = file.s3_file_path; const id = file.id;
if (e.shiftKey && lastSelectedIndex !== null) { if (e.shiftKey && lastSelectedIndex !== null) {
rangeSelect(lastSelectedIndex, index); rangeSelect(lastSelectedIndex, index);
} else if (e.metaKey || e.ctrlKey) { } else if (e.metaKey || e.ctrlKey) {
toggleSelected(path); toggleSelected(id);
setLastSelectedIndex(index); setLastSelectedIndex(index);
} else { } else {
if (isSelected(path) && selectedFiles.length === 1) { if (isSelected(id) && selectedFiles.length === 1) {
// si ya es el único seleccionado, des-selecciona // si ya es el único seleccionado, des-selecciona
setSelectedFiles([]); setSelectedFiles([]);
setLastSelectedIndex(null); setLastSelectedIndex(null);
} else { } else {
replaceSelection(path); replaceSelection(id);
setLastSelectedIndex(index); setLastSelectedIndex(index);
} }
} }
@@ -167,7 +167,7 @@ export function CreatePlanDialog({ open, onOpenChange }: { open: boolean; onOpen
prompt: prompt, prompt: prompt,
insert: true, insert: true,
files: selectedFiles, files: selectedFiles,
uuid: auth.user?.id, created_by: auth.user?.id,
}) })
const newId = (res as any)?.id || (res as any)?.plan?.id || (res as any)?.data?.id const newId = (res as any)?.id || (res as any)?.plan?.id || (res as any)?.data?.id
if (newId) { if (newId) {
@@ -261,7 +261,7 @@ export function CreatePlanDialog({ open, onOpenChange }: { open: boolean; onOpen
<div role="grid" className="grid gap-4 xs:grid-cols-2 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5"> <div role="grid" className="grid gap-4 xs:grid-cols-2 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5">
{dbFiles.map((file, index) => { {dbFiles.map((file, index) => {
const ext = fileExt(file.titulo); const ext = fileExt(file.titulo);
const selected = isSelected(file.s3_file_path); const selected = isSelected(file.id);
console.log(file); console.log(file);
return ( return (

View File

@@ -126,6 +126,17 @@ function RouteComponent() {
const [detail, setDetail] = useState<CarreraRow | null>(null) const [detail, setDetail] = useState<CarreraRow | null>(null)
const [editCarrera, setEditCarrera] = useState<CarreraRow | null>(null) const [editCarrera, setEditCarrera] = useState<CarreraRow | null>(null)
const [createOpen, setCreateOpen] = useState(false) const [createOpen, setCreateOpen] = useState(false)
const [deleteTarget, setDeleteTarget] = useState<CarreraRow | null>(null)
// ✅ Se declara UNA SOLA VEZ
const { setOpen: setDeleteOpen, dialog: deleteDialog } = useDeleteCarreraDialog(
deleteTarget?.id ?? "",
async () => {
await qc.invalidateQueries({ queryKey: carrerasKeys.root })
router.invalidate()
// setDeleteTarget(null)
}
)
const filtered = useMemo(() => { const filtered = useMemo(() => {
const term = q.trim().toLowerCase() const term = q.trim().toLowerCase()
@@ -198,10 +209,7 @@ function RouteComponent() {
const border = tint(fac?.color, 0.28) const border = tint(fac?.color, 0.28)
const chip = tint(fac?.color, 0.1) const chip = tint(fac?.color, 0.1)
const IconComp = (fac?.icon && (Icons as any)[fac.icon]) || Icons.Building2 const IconComp = (fac?.icon && (Icons as any)[fac.icon]) || Icons.Building2
const { setOpen: setDeleteOpen, dialog: deleteDialog } = useDeleteCarreraDialog(c.id, async () => {
await qc.invalidateQueries({ queryKey: carrerasKeys.root })
router.invalidate()
})
return ( return (
<ContextMenu key={c.id}> <ContextMenu key={c.id}>
<ContextMenuTrigger onClick={(e) => openContextMenu(e)}> <ContextMenuTrigger onClick={(e) => openContextMenu(e)}>
@@ -233,11 +241,14 @@ function RouteComponent() {
<ContextMenuItem onClick={() => setEditCarrera(c)}> <ContextMenuItem onClick={() => setEditCarrera(c)}>
<Icons.Pencil className="w-4 h-4 mr-2" /> Editar <Icons.Pencil className="w-4 h-4 mr-2" /> Editar
</ContextMenuItem> </ContextMenuItem>
<ContextMenuItem onClick={() => setDeleteOpen(true)}> <ContextMenuItem onClick={() => {
setDeleteTarget(c)
setDeleteOpen(true)
}}>
<Icons.Trash className="w-4 h-4 mr-2" /> Eliminar <Icons.Trash className="w-4 h-4 mr-2" /> Eliminar
</ContextMenuItem> </ContextMenuItem>
</ContextMenuContent> </ContextMenuContent>
{deleteDialog}
</ContextMenu> </ContextMenu>
) )
})} })}
@@ -247,6 +258,8 @@ function RouteComponent() {
</CardContent> </CardContent>
</Card> </Card>
{deleteDialog}
{/* Crear / Editar */} {/* Crear / Editar */}
<CarreraFormDialog <CarreraFormDialog
open={createOpen} open={createOpen}

View File

@@ -49,7 +49,10 @@ export const Route = createFileRoute("/_authenticated/plan/$planId")({
// ...existing code... // ...existing code...
function RouteComponent() { function RouteComponent() {
const qc = useQueryClient() const qc = useQueryClient()
const { plan, asignaturas: asignaturasPreview } = Route.useLoaderData() as LoaderData //const { plan, asignaturas: asignaturasPreview } = Route.useLoaderData() as LoaderData
const { plan } = Route.useLoaderData() as LoaderData
const { data: asignaturasPreview } = useSuspenseQuery(asignaturasPreviewOptions(plan.id))
const auth = useSupabaseAuth() const auth = useSupabaseAuth()
const asignaturasCount = asignaturasPreview.length const asignaturasCount = asignaturasPreview.length