tipado desde supabase, primer listado de planes, ajustes en src/data
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,
|
||||
@@ -18,6 +20,7 @@ const EDGE = {
|
||||
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,23 +42,33 @@ export type PlanListFilters = {
|
||||
offset?: number;
|
||||
};
|
||||
|
||||
export async function plans_list(filters: PlanListFilters = {}): Promise<Paged<PlanEstudio>> {
|
||||
export async function plans_list(
|
||||
filters: PlanListFilters = {},
|
||||
): Promise<Paged<PlanEstudio>> {
|
||||
const supabase = supabaseBrowser();
|
||||
|
||||
// 1. Construimos la query.
|
||||
// TypeScript validará que "planes_estudio" existe en Database
|
||||
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)
|
||||
`,
|
||||
{ count: "exact" }
|
||||
*,
|
||||
carreras (
|
||||
*,
|
||||
facultades (*)
|
||||
),
|
||||
estructuras_plan (*),
|
||||
estados_plan (*)
|
||||
`,
|
||||
{ count: "exact" },
|
||||
)
|
||||
.order("actualizado_en", { ascending: false });
|
||||
|
||||
if (filters.search?.trim()) q = q.ilike("nombre", `%${filters.search.trim()}%`);
|
||||
// 2. Aplicamos filtros dinámicos
|
||||
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);
|
||||
@@ -63,13 +76,19 @@ export async function plans_list(filters: PlanListFilters = {}): Promise<Paged<P
|
||||
// filtro por FK “hacia arriba” (PostgREST soporta filtros en recursos embebidos)
|
||||
if (filters.facultadId) 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> {
|
||||
@@ -79,11 +98,11 @@ 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,version,definicion),
|
||||
estados_plan(id,clave,etiqueta,orden,es_final)
|
||||
`
|
||||
*,
|
||||
carreras (*, facultades(*)),
|
||||
estructuras_plan (*),
|
||||
estados_plan (*)
|
||||
`,
|
||||
)
|
||||
.eq("id", planId)
|
||||
.single();
|
||||
@@ -92,7 +111,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")
|
||||
@@ -104,12 +125,14 @@ export async function plan_lineas_list(planId: UUID): Promise<LineaPlan[]> {
|
||||
return data ?? [];
|
||||
}
|
||||
|
||||
export async function plan_asignaturas_list(planId: UUID): Promise<Asignatura[]> {
|
||||
export async function plan_asignaturas_list(
|
||||
planId: UUID,
|
||||
): Promise<Array<Asignatura>> {
|
||||
const supabase = supabaseBrowser();
|
||||
const { data, error } = await supabase
|
||||
.from("asignaturas")
|
||||
.select(
|
||||
"id,plan_estudio_id,estructura_id,facultad_propietaria_id,codigo,nombre,tipo,creditos,horas_semana,numero_ciclo,linea_plan_id,orden_celda,datos,contenido_tematico,tipo_origen,meta_origen,creado_por,actualizado_por,creado_en,actualizado_en"
|
||||
"id,plan_estudio_id,estructura_id,facultad_propietaria_id,codigo,nombre,tipo,creditos,horas_semana,numero_ciclo,linea_plan_id,orden_celda,datos,contenido_tematico,tipo_origen,meta_origen,creado_por,actualizado_por,creado_en,actualizado_en",
|
||||
)
|
||||
.eq("plan_estudio_id", planId)
|
||||
.order("numero_ciclo", { ascending: true, nullsFirst: false })
|
||||
@@ -120,11 +143,13 @@ export async function plan_asignaturas_list(planId: UUID): Promise<Asignatura[]>
|
||||
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")
|
||||
.select("id,plan_estudio_id,cambiado_por,cambiado_en,tipo,campo,valor_anterior,valor_nuevo,interaccion_ia_id")
|
||||
.select(
|
||||
"id,plan_estudio_id,cambiado_por,cambiado_en,tipo,campo,valor_anterior,valor_nuevo,interaccion_ia_id",
|
||||
)
|
||||
.eq("plan_estudio_id", planId)
|
||||
.order("cambiado_en", { ascending: false });
|
||||
|
||||
@@ -143,7 +168,9 @@ export type PlansCreateManualInput = {
|
||||
datos?: Partial<PlanDatosSep> & Record<string, any>;
|
||||
};
|
||||
|
||||
export async function plans_create_manual(input: PlansCreateManualInput): Promise<PlanEstudio> {
|
||||
export async function plans_create_manual(
|
||||
input: PlansCreateManualInput,
|
||||
): Promise<PlanEstudio> {
|
||||
return invokeEdge<PlanEstudio>(EDGE.plans_create_manual, input);
|
||||
}
|
||||
|
||||
@@ -161,27 +188,35 @@ export type AIGeneratePlanInput = {
|
||||
descripcionEnfoque: string;
|
||||
poblacionObjetivo?: string;
|
||||
notasAdicionales?: string;
|
||||
archivosReferencia?: UUID[];
|
||||
repositoriosIds?: UUID[];
|
||||
archivosReferencia?: Array<UUID>;
|
||||
repositoriosIds?: Array<UUID>;
|
||||
usarMCP?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
export async function ai_generate_plan(input: AIGeneratePlanInput): Promise<any> {
|
||||
export async function ai_generate_plan(
|
||||
input: AIGeneratePlanInput,
|
||||
): Promise<any> {
|
||||
return invokeEdge<any>(EDGE.ai_generate_plan, input);
|
||||
}
|
||||
|
||||
export async function plans_persist_from_ai(payload: { jsonPlan: any }): Promise<PlanEstudio> {
|
||||
export async function plans_persist_from_ai(
|
||||
payload: { jsonPlan: any },
|
||||
): Promise<PlanEstudio> {
|
||||
return invokeEdge<PlanEstudio>(EDGE.plans_persist_from_ai, payload);
|
||||
}
|
||||
|
||||
export async function plans_clone_from_existing(payload: {
|
||||
planOrigenId: UUID;
|
||||
overrides: Partial<Pick<PlanEstudio, "nombre" | "nivel" | "tipo_ciclo" | "numero_ciclos">> & {
|
||||
carrera_id?: UUID;
|
||||
estructura_id?: UUID;
|
||||
datos?: Partial<PlanDatosSep> & Record<string, any>;
|
||||
};
|
||||
overrides:
|
||||
& Partial<
|
||||
Pick<PlanEstudio, "nombre" | "nivel" | "tipo_ciclo" | "numero_ciclos">
|
||||
>
|
||||
& {
|
||||
carrera_id?: UUID;
|
||||
estructura_id?: UUID;
|
||||
datos?: Partial<PlanDatosSep> & Record<string, any>;
|
||||
};
|
||||
}): Promise<PlanEstudio> {
|
||||
return invokeEdge<PlanEstudio>(EDGE.plans_clone_from_existing, payload);
|
||||
}
|
||||
@@ -211,27 +246,33 @@ export type PlansUpdateFieldsPatch = {
|
||||
datos?: Partial<PlanDatosSep> & Record<string, any>;
|
||||
};
|
||||
|
||||
export async function plans_update_fields(planId: UUID, patch: PlansUpdateFieldsPatch): Promise<PlanEstudio> {
|
||||
export async function plans_update_fields(
|
||||
planId: UUID,
|
||||
patch: PlansUpdateFieldsPatch,
|
||||
): Promise<PlanEstudio> {
|
||||
return invokeEdge<PlanEstudio>(EDGE.plans_update_fields, { planId, patch });
|
||||
}
|
||||
|
||||
/** Operaciones del mapa curricular (mover/reordenar) */
|
||||
export type PlanMapOperation =
|
||||
| {
|
||||
op: "MOVE_ASIGNATURA";
|
||||
asignaturaId: UUID;
|
||||
numero_ciclo: number | null;
|
||||
linea_plan_id: UUID | null;
|
||||
orden_celda?: number | null;
|
||||
}
|
||||
op: "MOVE_ASIGNATURA";
|
||||
asignaturaId: UUID;
|
||||
numero_ciclo: number | null;
|
||||
linea_plan_id: UUID | null;
|
||||
orden_celda?: number | null;
|
||||
}
|
||||
| {
|
||||
op: "REORDER_CELDA";
|
||||
linea_plan_id: UUID;
|
||||
numero_ciclo: number;
|
||||
asignaturaIdsOrdenados: UUID[];
|
||||
};
|
||||
op: "REORDER_CELDA";
|
||||
linea_plan_id: UUID;
|
||||
numero_ciclo: number;
|
||||
asignaturaIdsOrdenados: Array<UUID>;
|
||||
};
|
||||
|
||||
export async function plans_update_map(planId: UUID, ops: PlanMapOperation[]): Promise<{ ok: true }> {
|
||||
export async function plans_update_map(
|
||||
planId: UUID,
|
||||
ops: Array<PlanMapOperation>,
|
||||
): Promise<{ ok: true }> {
|
||||
return invokeEdge<{ ok: true }>(EDGE.plans_update_map, { planId, ops });
|
||||
}
|
||||
|
||||
@@ -251,10 +292,16 @@ export type DocumentoResult = {
|
||||
nombre?: string;
|
||||
};
|
||||
|
||||
export async function plans_generate_document(planId: UUID): Promise<DocumentoResult> {
|
||||
export async function plans_generate_document(
|
||||
planId: UUID,
|
||||
): Promise<DocumentoResult> {
|
||||
return invokeEdge<DocumentoResult>(EDGE.plans_generate_document, { planId });
|
||||
}
|
||||
|
||||
export async function plans_get_document(planId: UUID): Promise<DocumentoResult | null> {
|
||||
return invokeEdge<DocumentoResult | null>(EDGE.plans_get_document, { planId });
|
||||
export async function plans_get_document(
|
||||
planId: UUID,
|
||||
): Promise<DocumentoResult | null> {
|
||||
return invokeEdge<DocumentoResult | null>(EDGE.plans_get_document, {
|
||||
planId,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
|
||||
import {
|
||||
ai_plan_chat,
|
||||
ai_plan_improve,
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { keepPreviousData, useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { qk } from "../query/keys";
|
||||
import type { PlanEstudio, UUID } from "../types/domain";
|
||||
import type { PlanListFilters, PlanMapOperation, PlansCreateManualInput, PlansUpdateFieldsPatch } from "../api/plans.api";
|
||||
import {
|
||||
keepPreviousData,
|
||||
useMutation,
|
||||
useQuery,
|
||||
useQueryClient,
|
||||
} from "@tanstack/react-query";
|
||||
|
||||
import {
|
||||
ai_generate_plan,
|
||||
plan_asignaturas_list,
|
||||
@@ -19,13 +22,30 @@ import {
|
||||
plans_update_fields,
|
||||
plans_update_map,
|
||||
} from "../api/plans.api";
|
||||
import { qk } from "../query/keys";
|
||||
|
||||
import type {
|
||||
PlanListFilters,
|
||||
PlanMapOperation,
|
||||
PlansCreateManualInput,
|
||||
PlansUpdateFieldsPatch,
|
||||
} from "../api/plans.api";
|
||||
import type { UUID } from "../types/domain";
|
||||
|
||||
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,
|
||||
|
||||
// Opcional: Tiempo que la data se considera fresca
|
||||
staleTime: 1000 * 60 * 5, // 5 minutos
|
||||
});
|
||||
}
|
||||
|
||||
@@ -47,7 +67,9 @@ export function usePlanLineas(planId: UUID | null | undefined) {
|
||||
|
||||
export function usePlanAsignaturas(planId: UUID | null | undefined) {
|
||||
return useQuery({
|
||||
queryKey: planId ? qk.planAsignaturas(planId) : ["planes", "asignaturas", null],
|
||||
queryKey: planId
|
||||
? qk.planAsignaturas(planId)
|
||||
: ["planes", "asignaturas", null],
|
||||
queryFn: () => plan_asignaturas_list(planId as UUID),
|
||||
enabled: Boolean(planId),
|
||||
});
|
||||
@@ -144,7 +166,8 @@ export function useUpdatePlanMapa() {
|
||||
const qc = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (vars: { planId: UUID; ops: PlanMapOperation[] }) => plans_update_map(vars.planId, vars.ops),
|
||||
mutationFn: (vars: { planId: UUID; ops: Array<PlanMapOperation> }) =>
|
||||
plans_update_map(vars.planId, vars.ops),
|
||||
|
||||
// ✅ Optimista (rápida) para el caso MOVE_ASIGNATURA
|
||||
onMutate: async (vars) => {
|
||||
@@ -152,9 +175,7 @@ export function useUpdatePlanMapa() {
|
||||
const prev = qc.getQueryData<any>(qk.planAsignaturas(vars.planId));
|
||||
|
||||
// solo optimizamos MOVEs simples
|
||||
const moves = vars.ops.filter((x) => x.op === "MOVE_ASIGNATURA") as Array<
|
||||
Extract<PlanMapOperation, { op: "MOVE_ASIGNATURA" }>
|
||||
>;
|
||||
const moves = vars.ops.filter((x) => x.op === "MOVE_ASIGNATURA");
|
||||
|
||||
if (prev && Array.isArray(prev) && moves.length) {
|
||||
const next = prev.map((a: any) => {
|
||||
|
||||
@@ -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.js";
|
||||
|
||||
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,27 +1,26 @@
|
||||
import type { Json } from "./database";
|
||||
import type { Enums, Tables } from "../../types/supabase";
|
||||
import type { Json } from "src/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 = "OBLIGATORIA" | "OPTATIVA" | "TRONCAL" | "OTRA";
|
||||
export type TipoAsignatura = Enums<"tipo_asignatura">;
|
||||
|
||||
export type TipoBibliografia = "BASICA" | "COMPLEMENTARIA";
|
||||
export type TipoFuenteBibliografia = "MANUAL" | "BIBLIOTECA";
|
||||
|
||||
export type EstadoTareaRevision = "PENDIENTE" | "COMPLETADA" | "OMITIDA";
|
||||
export type TipoNotificacion = "PLAN_ASIGNADO" | "ESTADO_CAMBIADO" | "TAREA_ASIGNADA" | "COMENTARIO" | "OTRA";
|
||||
export type TipoNotificacion =
|
||||
| "PLAN_ASIGNADO"
|
||||
| "ESTADO_CAMBIADO"
|
||||
| "TAREA_ASIGNADA"
|
||||
| "COMENTARIO"
|
||||
| "OTRA";
|
||||
|
||||
export type TipoInteraccionIA = "GENERAR" | "MEJORAR_SECCION" | "CHAT" | "OTRA";
|
||||
|
||||
@@ -58,38 +57,12 @@ export type PlanDatosSep = {
|
||||
propuesta_de_evaluacion_periodica_del_plan_de_estudios?: string | null;
|
||||
};
|
||||
|
||||
export type Paged<T> = { data: T[]; count: number | null };
|
||||
export type Paged<T> = { data: Array<T>; count: number | 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 FacultadRow = Tables<"facultades">;
|
||||
export type CarreraRow = Tables<"carreras">;
|
||||
|
||||
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;
|
||||
|
||||
facultades?: Facultad | null;
|
||||
};
|
||||
|
||||
export type EstructuraPlan = {
|
||||
id: UUID;
|
||||
nombre: string;
|
||||
tipo: TipoEstructuraPlan;
|
||||
version: string | null;
|
||||
definicion: Json;
|
||||
};
|
||||
export type EstructuraPlanRow = Tables<"estructuras_plan">;
|
||||
|
||||
export type EstructuraAsignatura = {
|
||||
id: UUID;
|
||||
@@ -98,41 +71,13 @@ export type EstructuraAsignatura = {
|
||||
definicion: Json;
|
||||
};
|
||||
|
||||
export type EstadoPlan = {
|
||||
id: UUID;
|
||||
clave: string;
|
||||
etiqueta: string;
|
||||
orden: number;
|
||||
es_final: boolean;
|
||||
};
|
||||
export type EstadoPlanRow = Tables<"estados_plan">;
|
||||
export type PlanEstudioRow = Tables<"planes_estudio">;
|
||||
|
||||
export type PlanEstudio = {
|
||||
id: UUID;
|
||||
carrera_id: UUID;
|
||||
estructura_id: UUID;
|
||||
|
||||
nombre: string;
|
||||
nivel: NivelPlanEstudio;
|
||||
tipo_ciclo: TipoCiclo;
|
||||
numero_ciclos: number;
|
||||
|
||||
datos: Json;
|
||||
|
||||
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 PlanEstudio = PlanEstudioRow & {
|
||||
carreras: (CarreraRow & { facultades: FacultadRow | null }) | null;
|
||||
estructuras_plan: EstructuraPlanRow | null;
|
||||
estados_plan: EstadoPlanRow | null;
|
||||
};
|
||||
|
||||
export type LineaPlan = {
|
||||
|
||||
Reference in New Issue
Block a user