ya funciona la busqueda de asignaturas, pero ocurre un bug en la insercion de la asignatura clonada
This commit is contained in:
2026-03-24 20:43:29 -06:00
parent ed318fa67b
commit 3bb3a11779
3 changed files with 174 additions and 34 deletions

View File

@@ -4,6 +4,7 @@ import { useEffect, useMemo } from 'react'
import { useDebounce } from 'use-debounce' import { useDebounce } from 'use-debounce'
import type { NewSubjectWizardState } from '@/features/asignaturas/nueva/types' import type { NewSubjectWizardState } from '@/features/asignaturas/nueva/types'
import type { Database } from '@/types/supabase'
import Pagination03 from '@/components/shadcn-studio/pagination/pagination-03' import Pagination03 from '@/components/shadcn-studio/pagination/pagination-03'
import { Button } from '@/components/ui/button' import { Button } from '@/components/ui/button'
@@ -28,12 +29,13 @@ type SourceSubjectRow = {
tipo: any tipo: any
plan_estudio_id: string plan_estudio_id: string
estructura_id: string | null estructura_id: string | null
rank?: number
} }
const ALL = '__all__' const ALL = '__all__'
const normalizeLikeTerm = (term: string) => type SearchAsignaturasRow =
term.trim().replace(/[(),]/g, ' ').replace(/\s+/g, ' ') Database['public']['Functions']['search_asignaturas']['Returns'][number]
export function PasoFuenteClonadoInterno({ export function PasoFuenteClonadoInterno({
wizard, wizard,
@@ -95,41 +97,146 @@ export function PasoFuenteClonadoInterno({
const from = (page - 1) * pageSize const from = (page - 1) * pageSize
const to = from + pageSize - 1 const to = from + pageSize - 1
let q = supabase const term = debouncedSearch.trim()
.from('asignaturas')
.select(
'id,nombre,codigo,creditos,tipo,plan_estudio_id,estructura_id',
{
count: 'exact',
},
)
.order('nombre', { ascending: true })
if (planOrigenId) { // Full text search (tsvector) para el campo Buscar.
q = q.eq('plan_estudio_id', planOrigenId) // Si no hay término, conservamos el listado base por nombre.
} else if (needPlansForFilter) {
const planIds = plansForFilter.map((p) => p.id)
if (!planIds.length) {
return { data: [] as Array<SourceSubjectRow>, count: 0 }
}
q = q.in('plan_estudio_id', planIds)
}
const term = normalizeLikeTerm(debouncedSearch)
if (term) { if (term) {
// PostgREST OR syntax const mapRow = (r: SearchAsignaturasRow): SourceSubjectRow => ({
q = q.or(`nombre.ilike.%${term}%,codigo.ilike.%${term}%`) id: r.id,
nombre: r.nombre,
codigo: r.codigo,
creditos: Number(r.creditos),
tipo: r.tipo,
plan_estudio_id: r.plan_estudio_id,
estructura_id: null,
rank: r.rank,
})
if (planOrigenId) {
const args: Database['public']['Functions']['search_asignaturas']['Args'] =
{
p_search: term,
p_plan_estudio_id: planOrigenId,
p_limit: pageSize,
p_offset: from,
}
const { data, error, count } = await supabase.rpc(
'search_asignaturas',
args,
{ count: 'exact' },
)
if (error) throw new Error(error.message)
return {
data: data.map(mapRow),
count: count ?? 0,
}
}
if (needPlansForFilter) {
const planIds = plansForFilter.map((p) => p.id)
if (!planIds.length) {
return { data: [] as Array<SourceSubjectRow>, count: 0 }
}
const perPlanLimit = pageSize * page
const perPlan = await Promise.all(
planIds.map(async (planId) => {
const args: Database['public']['Functions']['search_asignaturas']['Args'] =
{
p_search: term,
p_plan_estudio_id: planId,
p_limit: perPlanLimit,
p_offset: 0,
}
const { data, error, count } = await supabase.rpc(
'search_asignaturas',
args,
{ count: 'exact' },
)
if (error) throw new Error(error.message)
return {
data,
count: count ?? 0,
}
}),
)
const merged = perPlan
.flatMap((p) => p.data)
.map(mapRow)
.sort((a, b) => {
const ar = a.rank ?? 0
const br = b.rank ?? 0
if (br !== ar) return br - ar
const byName = a.nombre.localeCompare(b.nombre, 'es')
if (byName !== 0) return byName
return String(a.id).localeCompare(String(b.id))
})
const pageData = merged.slice(from, to + 1)
const totalCount = perPlan.reduce((acc, p) => acc + p.count, 0)
return {
data: pageData,
count: totalCount,
}
}
const args: Database['public']['Functions']['search_asignaturas']['Args'] =
{
p_search: term,
p_limit: pageSize,
p_offset: from,
}
const { data, error, count } = await supabase.rpc(
'search_asignaturas',
args,
{ count: 'exact' },
)
if (error) throw new Error(error.message)
return {
data: data.map(mapRow),
count: count ?? 0,
}
} }
q = q.range(from, to) // let q = supabase
// .from('asignaturas')
// .select(
// 'id,nombre,codigo,creditos,tipo,plan_estudio_id,estructura_id',
// {
// count: 'exact',
// },
// )
// .order('nombre', { ascending: true })
const { data, error, count } = await q // if (planOrigenId) {
if (error) throw new Error(error.message) // q = q.eq('plan_estudio_id', planOrigenId)
// } else if (needPlansForFilter) {
// const planIds = plansForFilter.map((p) => p.id)
// if (!planIds.length) {
// return { data: [] as Array<SourceSubjectRow>, count: 0 }
// }
// q = q.in('plan_estudio_id', planIds)
// }
return { // q = q.range(from, to)
data: data as unknown as Array<SourceSubjectRow>,
count: count ?? 0, // const { data, error, count } = await q
} // if (error) throw new Error(error.message)
// return {
// data: data as unknown as Array<SourceSubjectRow>,
// count: count ?? 0,
// }
}, },
}) })
@@ -188,7 +295,7 @@ export function PasoFuenteClonadoInterno({
resetSelection() resetSelection()
}} }}
> >
<SelectTrigger> <SelectTrigger className="w-full min-w-0 [&>span]:block! [&>span]:truncate!">
<SelectValue placeholder="Todas" /> <SelectValue placeholder="Todas" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@@ -217,7 +324,7 @@ export function PasoFuenteClonadoInterno({
}} }}
disabled={!facultadId} disabled={!facultadId}
> >
<SelectTrigger> <SelectTrigger className="w-full min-w-0 [&>span]:block! [&>span]:truncate!">
<SelectValue <SelectValue
placeholder={facultadId ? 'Todas' : 'Selecciona facultad'} placeholder={facultadId ? 'Todas' : 'Selecciona facultad'}
/> />
@@ -243,7 +350,10 @@ export function PasoFuenteClonadoInterno({
resetSelection() resetSelection()
}} }}
> >
<SelectTrigger> <SelectTrigger
className="w-full min-w-0 [&>span]:block! [&>span]:truncate!"
disabled={!carreraId && !facultadId}
>
<SelectValue placeholder="Todos" /> <SelectValue placeholder="Todos" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>

View File

@@ -257,6 +257,8 @@ export function WizardControls({
null, null,
} }
console.log('payload:', payload)
const { data: inserted, error: insertError } = await supabase const { data: inserted, error: insertError } = await supabase
.from('asignaturas') .from('asignaturas')
.insert(payload) .insert(payload)

View File

@@ -155,6 +155,7 @@ export type Database = {
orden_celda: number | null orden_celda: number | null
plan_estudio_id: string plan_estudio_id: string
prerrequisito_asignatura_id: string | null prerrequisito_asignatura_id: string | null
search_vector: unknown
tipo: Database['public']['Enums']['tipo_asignatura'] tipo: Database['public']['Enums']['tipo_asignatura']
tipo_origen: Database['public']['Enums']['tipo_origen'] | null tipo_origen: Database['public']['Enums']['tipo_origen'] | null
} }
@@ -181,6 +182,7 @@ export type Database = {
orden_celda?: number | null orden_celda?: number | null
plan_estudio_id: string plan_estudio_id: string
prerrequisito_asignatura_id?: string | null prerrequisito_asignatura_id?: string | null
search_vector?: unknown
tipo?: Database['public']['Enums']['tipo_asignatura'] tipo?: Database['public']['Enums']['tipo_asignatura']
tipo_origen?: Database['public']['Enums']['tipo_origen'] | null tipo_origen?: Database['public']['Enums']['tipo_origen'] | null
} }
@@ -207,6 +209,7 @@ export type Database = {
orden_celda?: number | null orden_celda?: number | null
plan_estudio_id?: string plan_estudio_id?: string
prerrequisito_asignatura_id?: string | null prerrequisito_asignatura_id?: string | null
search_vector?: unknown
tipo?: Database['public']['Enums']['tipo_asignatura'] tipo?: Database['public']['Enums']['tipo_asignatura']
tipo_origen?: Database['public']['Enums']['tipo_origen'] | null tipo_origen?: Database['public']['Enums']['tipo_origen'] | null
} }
@@ -1393,6 +1396,31 @@ export type Database = {
Args: { p_append: Json; p_id: string } Args: { p_append: Json; p_id: string }
Returns: undefined Returns: undefined
} }
build_asignaturas_prefix_tsquery: {
Args: { p_search: string }
Returns: unknown
}
search_asignaturas: {
Args: {
p_limit?: number
p_offset?: number
p_plan_estudio_id?: string
p_search: string
}
Returns: Array<{
codigo: string
contenido_tematico: Json
creditos: number
datos: Json
estado: Database['public']['Enums']['estado_asignatura']
id: string
nombre: string
numero_ciclo: number
plan_estudio_id: string
rank: number
tipo: Database['public']['Enums']['tipo_asignatura']
}>
}
suma_porcentajes: { Args: { '': Json }; Returns: number } suma_porcentajes: { Args: { '': Json }; Returns: number }
unaccent: { Args: { '': string }; Returns: string } unaccent: { Args: { '': string }; Returns: string }
unaccent_immutable: { Args: { '': string }; Returns: string } unaccent_immutable: { Args: { '': string }; Returns: string }