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 type { NewSubjectWizardState } from '@/features/asignaturas/nueva/types'
import type { Database } from '@/types/supabase'
import Pagination03 from '@/components/shadcn-studio/pagination/pagination-03'
import { Button } from '@/components/ui/button'
@@ -28,12 +29,13 @@ type SourceSubjectRow = {
tipo: any
plan_estudio_id: string
estructura_id: string | null
rank?: number
}
const ALL = '__all__'
const normalizeLikeTerm = (term: string) =>
term.trim().replace(/[(),]/g, ' ').replace(/\s+/g, ' ')
type SearchAsignaturasRow =
Database['public']['Functions']['search_asignaturas']['Returns'][number]
export function PasoFuenteClonadoInterno({
wizard,
@@ -95,41 +97,146 @@ export function PasoFuenteClonadoInterno({
const from = (page - 1) * pageSize
const to = from + pageSize - 1
let q = supabase
.from('asignaturas')
.select(
'id,nombre,codigo,creditos,tipo,plan_estudio_id,estructura_id',
{
count: 'exact',
},
)
.order('nombre', { ascending: true })
const term = debouncedSearch.trim()
// Full text search (tsvector) para el campo Buscar.
// Si no hay término, conservamos el listado base por nombre.
if (term) {
const mapRow = (r: SearchAsignaturasRow): SourceSubjectRow => ({
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) {
q = q.eq('plan_estudio_id', planOrigenId)
} else if (needPlansForFilter) {
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 }
}
q = q.in('plan_estudio_id', planIds)
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 term = normalizeLikeTerm(debouncedSearch)
if (term) {
// PostgREST OR syntax
q = q.or(`nombre.ilike.%${term}%,codigo.ilike.%${term}%`)
}
q = q.range(from, to)
const { data, error, count } = await q
const { data, error, count } = await supabase.rpc(
'search_asignaturas',
args,
{ count: 'exact' },
)
if (error) throw new Error(error.message)
return {
data: data as unknown as Array<SourceSubjectRow>,
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,
}
}
// let q = supabase
// .from('asignaturas')
// .select(
// 'id,nombre,codigo,creditos,tipo,plan_estudio_id,estructura_id',
// {
// count: 'exact',
// },
// )
// .order('nombre', { ascending: true })
// if (planOrigenId) {
// 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)
// }
// q = q.range(from, to)
// 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()
}}
>
<SelectTrigger>
<SelectTrigger className="w-full min-w-0 [&>span]:block! [&>span]:truncate!">
<SelectValue placeholder="Todas" />
</SelectTrigger>
<SelectContent>
@@ -217,7 +324,7 @@ export function PasoFuenteClonadoInterno({
}}
disabled={!facultadId}
>
<SelectTrigger>
<SelectTrigger className="w-full min-w-0 [&>span]:block! [&>span]:truncate!">
<SelectValue
placeholder={facultadId ? 'Todas' : 'Selecciona facultad'}
/>
@@ -243,7 +350,10 @@ export function PasoFuenteClonadoInterno({
resetSelection()
}}
>
<SelectTrigger>
<SelectTrigger
className="w-full min-w-0 [&>span]:block! [&>span]:truncate!"
disabled={!carreraId && !facultadId}
>
<SelectValue placeholder="Todos" />
</SelectTrigger>
<SelectContent>

View File

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

View File

@@ -155,6 +155,7 @@ export type Database = {
orden_celda: number | null
plan_estudio_id: string
prerrequisito_asignatura_id: string | null
search_vector: unknown
tipo: Database['public']['Enums']['tipo_asignatura']
tipo_origen: Database['public']['Enums']['tipo_origen'] | null
}
@@ -181,6 +182,7 @@ export type Database = {
orden_celda?: number | null
plan_estudio_id: string
prerrequisito_asignatura_id?: string | null
search_vector?: unknown
tipo?: Database['public']['Enums']['tipo_asignatura']
tipo_origen?: Database['public']['Enums']['tipo_origen'] | null
}
@@ -207,6 +209,7 @@ export type Database = {
orden_celda?: number | null
plan_estudio_id?: string
prerrequisito_asignatura_id?: string | null
search_vector?: unknown
tipo?: Database['public']['Enums']['tipo_asignatura']
tipo_origen?: Database['public']['Enums']['tipo_origen'] | null
}
@@ -1393,6 +1396,31 @@ export type Database = {
Args: { p_append: Json; p_id: string }
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 }
unaccent: { Args: { '': string }; Returns: string }
unaccent_immutable: { Args: { '': string }; Returns: string }