Merge remote-tracking branch 'origin/feat/wizard-plan-vista' into feature/IntegrarDetallePlan
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
import { supabaseBrowser } from '../supabase/client'
|
||||
import { invokeEdge } from '../supabase/invokeEdge'
|
||||
import { buildRange, throwIfError, requireData } from './_helpers'
|
||||
|
||||
import { buildRange, requireData, throwIfError } from './_helpers'
|
||||
|
||||
import type {
|
||||
Asignatura,
|
||||
CambioPlan,
|
||||
@@ -12,12 +14,14 @@ import type {
|
||||
TipoCiclo,
|
||||
UUID,
|
||||
} from '../types/domain'
|
||||
import type { UploadedFile } from '@/components/planes/wizard/PasoDetallesPanel/FileDropZone'
|
||||
|
||||
const EDGE = {
|
||||
plans_create_manual: 'plans_create_manual',
|
||||
ai_generate_plan: 'ai_generate_plan',
|
||||
plans_persist_from_ai: 'plans_persist_from_ai',
|
||||
plans_clone_from_existing: 'plans_clone_from_existing',
|
||||
|
||||
plans_import_from_files: 'plans_import_from_files',
|
||||
|
||||
plans_update_fields: 'plans_update_fields',
|
||||
@@ -39,40 +43,82 @@ export type PlanListFilters = {
|
||||
offset?: number
|
||||
}
|
||||
|
||||
// Helper para limpiar texto (lo movemos fuera para reutilizar o lo dejas en un utils)
|
||||
const cleanText = (text: string) => {
|
||||
return text
|
||||
.normalize('NFD')
|
||||
.replace(/[\u0300-\u036f]/g, '')
|
||||
.toLowerCase()
|
||||
}
|
||||
|
||||
export async function plans_list(
|
||||
filters: PlanListFilters = {},
|
||||
): Promise<Paged<PlanEstudio>> {
|
||||
const supabase = supabaseBrowser()
|
||||
|
||||
// 1. Construimos la query base
|
||||
// NOTA IMPORTANTE: Para filtrar planes basados en facultad (que está en carreras),
|
||||
// necesitamos hacer un INNER JOIN. En Supabase se usa "!inner".
|
||||
// Si filters.facultadId existe, forzamos el inner join, si no, lo dejamos normal.
|
||||
|
||||
const carreraModifier =
|
||||
filters.facultadId && filters.facultadId !== 'todas' ? '!inner' : ''
|
||||
|
||||
let q = supabase
|
||||
.from('planes_estudio')
|
||||
.select(
|
||||
`
|
||||
id,carrera_id,estructura_id,nombre,nivel,tipo_ciclo,numero_ciclos,datos,estado_actual_id,activo,tipo_origen,meta_origen,creado_por,actualizado_por,creado_en,actualizado_en,
|
||||
carreras(id,facultad_id,nombre,nombre_corto,clave_sep,activa, facultades(id,nombre,nombre_corto,color,icono)),
|
||||
estructuras_plan(id,nombre,tipo,version,definicion),
|
||||
estados_plan(id,clave,etiqueta,orden,es_final)
|
||||
`,
|
||||
*,
|
||||
carreras${carreraModifier} (
|
||||
*,
|
||||
facultades (*)
|
||||
),
|
||||
estructuras_plan (*),
|
||||
estados_plan (*)
|
||||
`,
|
||||
{ count: 'exact' },
|
||||
)
|
||||
.order('actualizado_en', { ascending: false })
|
||||
|
||||
if (filters.search?.trim())
|
||||
q = q.ilike('nombre', `%${filters.search.trim()}%`)
|
||||
if (filters.carreraId) q = q.eq('carrera_id', filters.carreraId)
|
||||
if (filters.estadoId) q = q.eq('estado_actual_id', filters.estadoId)
|
||||
if (typeof filters.activo === 'boolean') q = q.eq('activo', filters.activo)
|
||||
// 2. Aplicamos filtros dinámicos
|
||||
|
||||
// filtro por FK “hacia arriba” (PostgREST soporta filtros en recursos embebidos)
|
||||
if (filters.facultadId) q = q.eq('carreras.facultad_id', filters.facultadId)
|
||||
// SOLUCIÓN SEARCH: Limpiamos el input y buscamos en la columna generada
|
||||
if (filters.search?.trim()) {
|
||||
const cleanTerm = cleanText(filters.search.trim())
|
||||
// Usamos la columna nueva creada en el Paso 1
|
||||
q = q.ilike('nombre_search', `%${cleanTerm}%`)
|
||||
}
|
||||
|
||||
if (filters.carreraId && filters.carreraId !== 'todas') {
|
||||
q = q.eq('carrera_id', filters.carreraId)
|
||||
}
|
||||
|
||||
if (filters.estadoId && filters.estadoId !== 'todos') {
|
||||
q = q.eq('estado_actual_id', filters.estadoId)
|
||||
}
|
||||
|
||||
if (typeof filters.activo === 'boolean') {
|
||||
q = q.eq('activo', filters.activo)
|
||||
}
|
||||
|
||||
// Filtro por facultad (gracias al !inner arriba, esto filtrará los planes)
|
||||
if (filters.facultadId && filters.facultadId !== 'todas') {
|
||||
q = q.eq('carreras.facultad_id', filters.facultadId)
|
||||
}
|
||||
|
||||
// 3. Paginación
|
||||
const { from, to } = buildRange(filters.limit, filters.offset)
|
||||
if (typeof from === 'number' && typeof to === 'number') q = q.range(from, to)
|
||||
if (from !== undefined && to !== undefined) q = q.range(from, to)
|
||||
|
||||
const { data, error, count } = await q
|
||||
throwIfError(error)
|
||||
|
||||
return { data: data ?? [], count: count ?? null }
|
||||
return {
|
||||
// 1. Si data es null, usa [].
|
||||
// 2. Luego dile a TS que el resultado es tu Array tipado.
|
||||
data: (data ?? []) as unknown as Array<PlanEstudio>,
|
||||
count: count ?? 0,
|
||||
}
|
||||
}
|
||||
|
||||
export async function plans_get(planId: UUID): Promise<PlanEstudio> {
|
||||
@@ -82,10 +128,10 @@ export async function plans_get(planId: UUID): Promise<PlanEstudio> {
|
||||
.from('planes_estudio')
|
||||
.select(
|
||||
`
|
||||
id,carrera_id,estructura_id,nombre,nivel,tipo_ciclo,numero_ciclos,datos,estado_actual_id,activo,tipo_origen,meta_origen,creado_por,actualizado_por,creado_en,actualizado_en,
|
||||
carreras(id,facultad_id,nombre,nombre_corto,clave_sep,activa, facultades(id,nombre,nombre_corto,color,icono)),
|
||||
estructuras_plan(id,nombre,tipo,template_id,definicion),
|
||||
estados_plan(id,clave,etiqueta,orden,es_final)
|
||||
*,
|
||||
carreras (*, facultades(*)),
|
||||
estructuras_plan (*),
|
||||
estados_plan (*)
|
||||
`,
|
||||
)
|
||||
.eq('id', planId)
|
||||
@@ -95,7 +141,9 @@ export async function plans_get(planId: UUID): Promise<PlanEstudio> {
|
||||
return requireData(data, 'Plan no encontrado.')
|
||||
}
|
||||
|
||||
export async function plan_lineas_list(planId: UUID): Promise<LineaPlan[]> {
|
||||
export async function plan_lineas_list(
|
||||
planId: UUID,
|
||||
): Promise<Array<LineaPlan>> {
|
||||
const supabase = supabaseBrowser()
|
||||
const { data, error } = await supabase
|
||||
.from('lineas_plan')
|
||||
@@ -109,7 +157,7 @@ export async function plan_lineas_list(planId: UUID): Promise<LineaPlan[]> {
|
||||
|
||||
export async function plan_asignaturas_list(
|
||||
planId: UUID,
|
||||
): Promise<Asignatura[]> {
|
||||
): Promise<Array<Asignatura>> {
|
||||
const supabase = supabaseBrowser()
|
||||
const { data, error } = await supabase
|
||||
.from('asignaturas')
|
||||
@@ -125,7 +173,7 @@ export async function plan_asignaturas_list(
|
||||
return data ?? []
|
||||
}
|
||||
|
||||
export async function plans_history(planId: UUID): Promise<CambioPlan[]> {
|
||||
export async function plans_history(planId: UUID): Promise<Array<CambioPlan>> {
|
||||
const supabase = supabaseBrowser()
|
||||
const { data, error } = await supabase
|
||||
.from('cambios_plan')
|
||||
@@ -170,8 +218,9 @@ export type AIGeneratePlanInput = {
|
||||
descripcionEnfoque: string
|
||||
poblacionObjetivo?: string
|
||||
notasAdicionales?: string
|
||||
archivosReferencia?: UUID[]
|
||||
repositoriosIds?: UUID[]
|
||||
archivosReferencia?: Array<UUID>
|
||||
repositoriosIds?: Array<UUID>
|
||||
archivosAdjuntos: Array<UploadedFile>
|
||||
usarMCP?: boolean
|
||||
}
|
||||
}
|
||||
@@ -246,12 +295,12 @@ export type PlanMapOperation =
|
||||
op: 'REORDER_CELDA'
|
||||
linea_plan_id: UUID
|
||||
numero_ciclo: number
|
||||
asignaturaIdsOrdenados: UUID[]
|
||||
asignaturaIdsOrdenados: Array<UUID>
|
||||
}
|
||||
|
||||
export async function plans_update_map(
|
||||
planId: UUID,
|
||||
ops: PlanMapOperation[],
|
||||
ops: Array<PlanMapOperation>,
|
||||
): Promise<{ ok: true }> {
|
||||
return invokeEdge<{ ok: true }>(EDGE.plans_update_map, { planId, ops })
|
||||
}
|
||||
@@ -281,5 +330,28 @@ export async function plans_generate_document(
|
||||
export async function plans_get_document(
|
||||
planId: UUID,
|
||||
): Promise<DocumentoResult | null> {
|
||||
return invokeEdge<DocumentoResult | null>(EDGE.plans_get_document, { planId })
|
||||
return invokeEdge<DocumentoResult | null>(EDGE.plans_get_document, {
|
||||
planId,
|
||||
})
|
||||
}
|
||||
|
||||
export async function getCatalogos() {
|
||||
const supabase = supabaseBrowser()
|
||||
|
||||
const [facultadesRes, carrerasRes, estadosRes, estructurasPlanRes] =
|
||||
await Promise.all([
|
||||
supabase.from('facultades').select('*').order('nombre'),
|
||||
supabase.from('carreras').select('*').order('nombre'),
|
||||
supabase.from('estados_plan').select('*').order('orden'),
|
||||
supabase.from('estructuras_plan').select('*').order('creado_en', {
|
||||
ascending: true,
|
||||
}),
|
||||
])
|
||||
|
||||
return {
|
||||
facultades: facultadesRes.data ?? [],
|
||||
carreras: carrerasRes.data ?? [],
|
||||
estados: estadosRes.data ?? [],
|
||||
estructurasPlan: estructurasPlanRes.data ?? [],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
|
||||
import {
|
||||
ai_plan_chat,
|
||||
ai_plan_improve,
|
||||
|
||||
@@ -14,6 +14,7 @@ import type {
|
||||
} from '../api/plans.api'
|
||||
import {
|
||||
ai_generate_plan,
|
||||
getCatalogos,
|
||||
plan_asignaturas_list,
|
||||
plan_lineas_list,
|
||||
plans_clone_from_existing,
|
||||
@@ -33,8 +34,13 @@ import {
|
||||
export function usePlanes(filters: PlanListFilters) {
|
||||
// 🧠 Tip: memoiza "filters" (useMemo) para que queryKey sea estable.
|
||||
return useQuery({
|
||||
// Usamos la factory de keys para consistencia
|
||||
queryKey: qk.planesList(filters),
|
||||
|
||||
// La función fetch
|
||||
queryFn: () => plans_list(filters),
|
||||
|
||||
// UX: Mantiene los datos viejos mientras carga la paginación nueva
|
||||
placeholderData: keepPreviousData,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
import { QueryClient } from "@tanstack/react-query";
|
||||
|
||||
export const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
staleTime: 30_000,
|
||||
refetchOnWindowFocus: false,
|
||||
retry: (failureCount) => failureCount < 2,
|
||||
},
|
||||
mutations: {
|
||||
retry: 0,
|
||||
},
|
||||
},
|
||||
});
|
||||
33
src/data/query/queryClient.tsx
Normal file
33
src/data/query/queryClient.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||
|
||||
export function getContext() {
|
||||
const queryClient = new QueryClient(
|
||||
{
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
staleTime: 30_000,
|
||||
refetchOnWindowFocus: false,
|
||||
retry: (failureCount) => failureCount < 2,
|
||||
},
|
||||
mutations: {
|
||||
retry: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
return {
|
||||
queryClient,
|
||||
}
|
||||
}
|
||||
|
||||
export function Provider({
|
||||
children,
|
||||
queryClient,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
queryClient: QueryClient
|
||||
}) {
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
||||
)
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
import { createClient, type SupabaseClient } from "@supabase/supabase-js";
|
||||
import type { Database } from "../types/database";
|
||||
import { createClient } from "@supabase/supabase-js";
|
||||
|
||||
import { getEnv } from "./env";
|
||||
|
||||
import type { SupabaseClient } from "@supabase/supabase-js";
|
||||
import type { Database } from "src/types/supabase";
|
||||
|
||||
let _client: SupabaseClient<Database> | null = null;
|
||||
|
||||
export function supabaseBrowser(): SupabaseClient<Database> {
|
||||
@@ -10,13 +13,13 @@ export function supabaseBrowser(): SupabaseClient<Database> {
|
||||
const url = getEnv(
|
||||
"VITE_SUPABASE_URL",
|
||||
"NEXT_PUBLIC_SUPABASE_URL",
|
||||
"SUPABASE_URL"
|
||||
"SUPABASE_URL",
|
||||
);
|
||||
|
||||
const anonKey = getEnv(
|
||||
"VITE_SUPABASE_ANON_KEY",
|
||||
"NEXT_PUBLIC_SUPABASE_ANON_KEY",
|
||||
"SUPABASE_ANON_KEY"
|
||||
"SUPABASE_ANON_KEY",
|
||||
);
|
||||
|
||||
_client = createClient<Database>(url, anonKey, {
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
export type Json =
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| null
|
||||
| { [key: string]: Json }
|
||||
| Json[];
|
||||
|
||||
export type Database = any; // ✅ Reemplaza por tipos generados (supabase gen types typescript)
|
||||
@@ -1,29 +1,22 @@
|
||||
import type { Json } from "./database";
|
||||
import type { Enums, Tables } from "../../types/supabase";
|
||||
|
||||
export type UUID = string;
|
||||
|
||||
export type TipoEstructuraPlan = "CURRICULAR" | "NO_CURRICULAR";
|
||||
export type NivelPlanEstudio =
|
||||
| "LICENCIATURA"
|
||||
| "MAESTRIA"
|
||||
| "DOCTORADO"
|
||||
| "ESPECIALIDAD"
|
||||
| "DIPLOMADO"
|
||||
| "OTRO";
|
||||
export type TipoEstructuraPlan = Enums<"tipo_estructura_plan">;
|
||||
export type NivelPlanEstudio = Enums<"nivel_plan_estudio">;
|
||||
export type TipoCiclo = Enums<"tipo_ciclo">;
|
||||
|
||||
export type TipoCiclo = "SEMESTRE" | "CUATRIMESTRE" | "TRIMESTRE" | "OTRO";
|
||||
export type TipoOrigen = Enums<"tipo_origen">;
|
||||
|
||||
export type TipoOrigen = "MANUAL" | "IA" | "CLONADO_INTERNO" | "TRADICIONAL" | "OTRO";
|
||||
export type TipoAsignatura = Enums<"tipo_asignatura">;
|
||||
|
||||
export type TipoAsignatura = "OBLIGATORIA" | "OPTATIVA" | "TRONCAL" | "OTRA";
|
||||
export type TipoBibliografia = Enums<"tipo_bibliografia">;
|
||||
export type TipoFuenteBibliografia = Enums<"tipo_fuente_bibliografia">;
|
||||
|
||||
export type TipoBibliografia = "BASICA" | "COMPLEMENTARIA";
|
||||
export type TipoFuenteBibliografia = "MANUAL" | "BIBLIOTECA";
|
||||
export type EstadoTareaRevision = Enums<"estado_tarea_revision">;
|
||||
export type TipoNotificacion = Enums<"tipo_notificacion">;
|
||||
|
||||
export type EstadoTareaRevision = "PENDIENTE" | "COMPLETADA" | "OMITIDA";
|
||||
export type TipoNotificacion = "PLAN_ASIGNADO" | "ESTADO_CAMBIADO" | "TAREA_ASIGNADA" | "COMENTARIO" | "OTRA";
|
||||
|
||||
export type TipoInteraccionIA = "GENERAR" | "MEJORAR_SECCION" | "CHAT" | "OTRA";
|
||||
export type TipoInteraccionIA = Enums<"tipo_interaccion_ia">;
|
||||
|
||||
export type ModalidadEducativa = "Escolar" | "No escolarizada" | "Mixta";
|
||||
export type DisenoCurricular = "Rígido" | "Flexible";
|
||||
@@ -58,218 +51,49 @@ export type PlanDatosSep = {
|
||||
propuesta_de_evaluacion_periodica_del_plan_de_estudios?: string | null;
|
||||
};
|
||||
|
||||
export type Paged<T> = { data: T[]; count: number | null };
|
||||
export type PlanEstudioWithRel =
|
||||
& Tables<"planes_estudio">
|
||||
& {
|
||||
carreras:
|
||||
| Tables<"carreras"> & {
|
||||
facultades: Tables<"facultades"> | null;
|
||||
}
|
||||
| null;
|
||||
estados_plan: Tables<"estados_plan"> | null;
|
||||
};
|
||||
|
||||
export type Facultad = {
|
||||
id: UUID;
|
||||
nombre: string;
|
||||
nombre_corto: string | null;
|
||||
color: string | null;
|
||||
icono: string | null;
|
||||
creado_en: string;
|
||||
actualizado_en: string;
|
||||
export type Paged<T> = { data: Array<T>; count: number | null };
|
||||
|
||||
export type FacultadRow = Tables<"facultades">;
|
||||
export type CarreraRow = Tables<"carreras">;
|
||||
|
||||
export type EstructuraPlanRow = Tables<"estructuras_plan">;
|
||||
|
||||
export type EstructuraAsignatura = Tables<"estructuras_asignatura">;
|
||||
|
||||
export type EstadoPlanRow = Tables<"estados_plan">;
|
||||
export type PlanEstudioRow = Tables<"planes_estudio">;
|
||||
|
||||
export type PlanEstudio = PlanEstudioRow & {
|
||||
carreras: (CarreraRow & { facultades: FacultadRow | null }) | null;
|
||||
estructuras_plan: EstructuraPlanRow | null;
|
||||
estados_plan: EstadoPlanRow | null;
|
||||
};
|
||||
|
||||
export type Carrera = {
|
||||
id: UUID;
|
||||
facultad_id: UUID;
|
||||
nombre: string;
|
||||
nombre_corto: string | null;
|
||||
clave_sep: string | null;
|
||||
activa: boolean;
|
||||
creado_en: string;
|
||||
actualizado_en: string;
|
||||
export type LineaPlan = Tables<"lineas_plan">;
|
||||
|
||||
facultades?: Facultad | null;
|
||||
};
|
||||
export type Asignatura = Tables<"asignaturas">;
|
||||
|
||||
export type EstructuraPlan = {
|
||||
id: UUID;
|
||||
nombre: string;
|
||||
tipo: TipoEstructuraPlan;
|
||||
version: string | null;
|
||||
definicion: Json;
|
||||
};
|
||||
export type BibliografiaAsignatura = Tables<"bibliografia_asignatura">;
|
||||
|
||||
export type EstructuraAsignatura = {
|
||||
id: UUID;
|
||||
nombre: string;
|
||||
version: string | null;
|
||||
definicion: Json;
|
||||
};
|
||||
export type CambioPlan = Tables<"cambios_plan">;
|
||||
|
||||
export type EstadoPlan = {
|
||||
id: UUID;
|
||||
clave: string;
|
||||
etiqueta: string;
|
||||
orden: number;
|
||||
es_final: boolean;
|
||||
};
|
||||
export type CambioAsignatura = Tables<"cambios_asignatura">;
|
||||
|
||||
export type PlanEstudio = {
|
||||
id: UUID;
|
||||
carrera_id: UUID;
|
||||
estructura_id: UUID;
|
||||
export type InteraccionIA = Tables<"interacciones_ia">;
|
||||
|
||||
nombre: string;
|
||||
nivel: NivelPlanEstudio;
|
||||
tipo_ciclo: TipoCiclo;
|
||||
numero_ciclos: number;
|
||||
export type TareaRevision = Tables<"tareas_revision">;
|
||||
|
||||
datos: Json;
|
||||
export type Notificacion = Tables<"notificaciones">;
|
||||
|
||||
estado_actual_id: UUID | null;
|
||||
activo: boolean;
|
||||
|
||||
tipo_origen: TipoOrigen | null;
|
||||
meta_origen: Json;
|
||||
|
||||
creado_por: UUID | null;
|
||||
actualizado_por: UUID | null;
|
||||
|
||||
creado_en: string;
|
||||
actualizado_en: string;
|
||||
|
||||
carreras?: Carrera | null;
|
||||
estructuras_plan?: EstructuraPlan | null;
|
||||
estados_plan?: EstadoPlan | null;
|
||||
};
|
||||
|
||||
export type LineaPlan = {
|
||||
id: UUID;
|
||||
plan_estudio_id: UUID;
|
||||
nombre: string;
|
||||
orden: number;
|
||||
area: string | null;
|
||||
creado_en: string;
|
||||
actualizado_en: string;
|
||||
};
|
||||
|
||||
export type Asignatura = {
|
||||
id: UUID;
|
||||
plan_estudio_id: UUID;
|
||||
estructura_id: UUID | null;
|
||||
|
||||
facultad_propietaria_id: UUID | null;
|
||||
|
||||
codigo: string | null;
|
||||
nombre: string;
|
||||
|
||||
tipo: TipoAsignatura;
|
||||
creditos: number;
|
||||
horas_semana: number | null;
|
||||
|
||||
numero_ciclo: number | null;
|
||||
linea_plan_id: UUID | null;
|
||||
orden_celda: number | null;
|
||||
|
||||
datos: Json;
|
||||
contenido_tematico: Json;
|
||||
|
||||
tipo_origen: TipoOrigen | null;
|
||||
meta_origen: Json;
|
||||
|
||||
creado_por: UUID | null;
|
||||
actualizado_por: UUID | null;
|
||||
|
||||
creado_en: string;
|
||||
actualizado_en: string;
|
||||
|
||||
planes_estudio?: PlanEstudio | null;
|
||||
estructuras_asignatura?: EstructuraAsignatura | null;
|
||||
};
|
||||
|
||||
export type BibliografiaAsignatura = {
|
||||
id: UUID;
|
||||
asignatura_id: UUID;
|
||||
tipo: TipoBibliografia;
|
||||
cita: string;
|
||||
tipo_fuente: TipoFuenteBibliografia;
|
||||
biblioteca_item_id: string | null;
|
||||
|
||||
creado_por: UUID | null;
|
||||
creado_en: string;
|
||||
actualizado_en: string;
|
||||
};
|
||||
|
||||
export type CambioPlan = {
|
||||
id: UUID;
|
||||
plan_estudio_id: UUID;
|
||||
cambiado_por: UUID | null;
|
||||
cambiado_en: string;
|
||||
tipo: "ACTUALIZACION_CAMPO" | "ACTUALIZACION_MAPA" | "OTRO";
|
||||
campo: string | null;
|
||||
valor_anterior: Json | null;
|
||||
valor_nuevo: Json | null;
|
||||
interaccion_ia_id: UUID | null;
|
||||
};
|
||||
|
||||
export type CambioAsignatura = {
|
||||
id: UUID;
|
||||
asignatura_id: UUID;
|
||||
cambiado_por: UUID | null;
|
||||
cambiado_en: string;
|
||||
tipo: "ACTUALIZACION_CAMPO" | "ACTUALIZACION_MAPA" | "OTRO";
|
||||
campo: string | null;
|
||||
valor_anterior: Json | null;
|
||||
valor_nuevo: Json | null;
|
||||
fuente: "HUMANO" | "IA" | null;
|
||||
interaccion_ia_id: UUID | null;
|
||||
};
|
||||
|
||||
export type InteraccionIA = {
|
||||
id: UUID;
|
||||
usuario_id: UUID | null;
|
||||
plan_estudio_id: UUID | null;
|
||||
asignatura_id: UUID | null;
|
||||
|
||||
tipo: TipoInteraccionIA;
|
||||
modelo: string | null;
|
||||
temperatura: number | null;
|
||||
|
||||
prompt: Json;
|
||||
respuesta: Json;
|
||||
|
||||
aceptada: boolean;
|
||||
|
||||
conversacion_id: string | null;
|
||||
ids_archivos: Json;
|
||||
ids_vector_store: Json;
|
||||
|
||||
creado_en: string;
|
||||
};
|
||||
|
||||
export type TareaRevision = {
|
||||
id: UUID;
|
||||
plan_estudio_id: UUID;
|
||||
asignado_a: UUID;
|
||||
rol_id: UUID | null;
|
||||
estado_id: UUID | null;
|
||||
estatus: EstadoTareaRevision;
|
||||
fecha_limite: string | null;
|
||||
creado_en: string;
|
||||
completado_en: string | null;
|
||||
};
|
||||
|
||||
export type Notificacion = {
|
||||
id: UUID;
|
||||
usuario_id: UUID;
|
||||
tipo: TipoNotificacion;
|
||||
payload: Json;
|
||||
leida: boolean;
|
||||
creado_en: string;
|
||||
leida_en: string | null;
|
||||
};
|
||||
|
||||
export type Archivo = {
|
||||
id: UUID;
|
||||
ruta_storage: string;
|
||||
nombre: string;
|
||||
mime_type: string | null;
|
||||
bytes: number | null;
|
||||
subido_por: UUID | null;
|
||||
subido_en: string;
|
||||
temporal: boolean;
|
||||
openai_file_id: string | null;
|
||||
notas: string | null;
|
||||
};
|
||||
export type Archivo = Tables<"archivos">;
|
||||
|
||||
Reference in New Issue
Block a user