diff --git a/src/components/asignaturas/detalle/IAMateriaTab.tsx b/src/components/asignaturas/detalle/IAAsignaturaTab.tsx
similarity index 96%
rename from src/components/asignaturas/detalle/IAMateriaTab.tsx
rename to src/components/asignaturas/detalle/IAAsignaturaTab.tsx
index 40ce816..fc97699 100644
--- a/src/components/asignaturas/detalle/IAMateriaTab.tsx
+++ b/src/components/asignaturas/detalle/IAAsignaturaTab.tsx
@@ -13,7 +13,11 @@ import {
} from 'lucide-react'
import { useState, useEffect, useRef, useMemo } from 'react'
-import type { IAMessage, IASugerencia, CampoEstructura } from '@/types/materia'
+import type {
+ IAMessage,
+ IASugerencia,
+ CampoEstructura,
+} from '@/types/asignatura'
import { Avatar, AvatarFallback } from '@/components/ui/avatar'
import { Badge } from '@/components/ui/badge'
@@ -22,7 +26,7 @@ import { ScrollArea } from '@/components/ui/scroll-area'
import { Textarea } from '@/components/ui/textarea'
import { cn } from '@/lib/utils'
-// Tipos importados de tu archivo de materia
+// Tipos importados de tu archivo de asignatura
const PRESETS = [
{
@@ -35,7 +39,7 @@ const PRESETS = [
id: 'contenido-tematico',
label: 'Sugerir contenido',
icon: BookOpen,
- prompt: 'Genera un desglose de temas para esta materia...',
+ prompt: 'Genera un desglose de temas para esta asignatura...',
},
{
id: 'actividades',
@@ -57,7 +61,7 @@ interface SelectedField {
value: string
}
-interface IAMateriaTabProps {
+interface IAAsignaturaTabProps {
campos: Array
datosGenerales: Record
messages: Array
@@ -66,14 +70,14 @@ interface IAMateriaTabProps {
onRejectSuggestion: (messageId: string) => void
}
-export function IAMateriaTab({
+export function IAAsignaturaTab({
campos,
datosGenerales,
messages,
onSendMessage,
onAcceptSuggestion,
onRejectSuggestion,
-}: IAMateriaTabProps) {
+}: IAAsignaturaTabProps) {
const routerState = useRouterState()
// ESTADOS PRINCIPALES (Igual que en Planes)
@@ -83,7 +87,7 @@ export function IAMateriaTab({
const [isLoading, setIsLoading] = useState(false)
const scrollRef = useRef(null)
- // 1. Transformar datos de la materia para el menú
+ // 1. Transformar datos de la asignatura para el menú
const availableFields = useMemo(() => {
// Extraemos las claves directamente del objeto datosGenerales
// ["nombre", "descripcion", "perfil_de_egreso", "fines_de_aprendizaje_o_formacion"]
@@ -105,7 +109,7 @@ export function IAMateriaTab({
})
}, [campos, datosGenerales])
- // 2. Manejar el estado inicial si viene de "Datos de Materia" (Prefill)
+ // 2. Manejar el estado inicial si viene de "Datos de Asignatura" (Prefill)
useEffect(() => {
const state = routerState.location.state as any
@@ -244,7 +248,7 @@ export function IAMateriaTab({
{msg.content}
- {/* Renderizado de Sugerencias (Homologado con lógica de Materia) */}
+ {/* Renderizado de Sugerencias (Homologado con lógica de Asignatura) */}
{msg.sugerencia && !msg.sugerencia.aceptada && (
@@ -302,7 +306,7 @@ export function IAMateriaTab({
{showSuggestions && (
- Seleccionar campo de materia
+ Seleccionar campo de asignatura
{availableFields.map((field) => (
diff --git a/src/data/api/_helpers.ts b/src/data/api/_helpers.ts
index 3d2176f..d5f15f7 100644
--- a/src/data/api/_helpers.ts
+++ b/src/data/api/_helpers.ts
@@ -1,45 +1,56 @@
-import type { PostgrestError, AuthError, SupabaseClient } from "@supabase/supabase-js";
-import type { Database } from "../types/database";
+import type { Database } from '../types/database'
+import type {
+ PostgrestError,
+ AuthError,
+ SupabaseClient,
+} from '@supabase/supabase-js'
export class ApiError extends Error {
constructor(
message: string,
public readonly code?: string,
public readonly details?: unknown,
- public readonly hint?: string
+ public readonly hint?: string,
) {
- super(message);
- this.name = "ApiError";
+ super(message)
+ this.name = 'ApiError'
}
}
export function throwIfError(error: PostgrestError | AuthError | null): void {
- if (!error) return;
-
- const anyErr = error as any;
+ if (!error) return
+ const anyErr = error as any
throw new ApiError(
- anyErr.message ?? "Error inesperado",
+ anyErr.message ?? 'Error inesperado',
anyErr.code,
anyErr.details,
- anyErr.hint
- );
+ anyErr.hint,
+ )
}
-export function requireData(data: T | null | undefined, message = "Respuesta vacía"): T {
- if (data === null || data === undefined) throw new ApiError(message);
- return data;
+export function requireData(
+ data: T | null | undefined,
+ message = 'Respuesta vacía',
+): T {
+ if (data === null || data === undefined) throw new ApiError(message)
+ return data
}
-export async function getUserIdOrThrow(supabase: SupabaseClient): Promise {
- const { data, error } = await supabase.auth.getUser();
- throwIfError(error);
- if (!data?.user?.id) throw new ApiError("No hay sesión activa (auth).");
- return data.user.id;
+export async function getUserIdOrThrow(
+ supabase: SupabaseClient,
+): Promise {
+ const { data, error } = await supabase.auth.getUser()
+ throwIfError(error)
+ if (!data?.user?.id) throw new ApiError('No hay sesión activa (auth).')
+ return data.user.id
}
-export function buildRange(limit?: number, offset?: number): { from?: number; to?: number } {
- if (!limit) return {};
- const from = Math.max(0, offset ?? 0);
- const to = from + Math.max(1, limit) - 1;
- return { from, to };
+export function buildRange(
+ limit?: number,
+ offset?: number,
+): { from?: number; to?: number } {
+ if (!limit) return {}
+ const from = Math.max(0, offset ?? 0)
+ const to = from + Math.max(1, limit) - 1
+ return { from, to }
}
diff --git a/src/data/api/openaiFiles.api.ts b/src/data/api/openaiFiles.api.ts
index ad7b9e8..385914e 100644
--- a/src/data/api/openaiFiles.api.ts
+++ b/src/data/api/openaiFiles.api.ts
@@ -1,32 +1,32 @@
-import { invokeEdge } from "../supabase/invokeEdge";
-import type { UUID } from "../types/domain";
+import { invokeEdge } from '../supabase/invokeEdge'
+import type { UUID } from '../types/domain'
/**
* Metadata “canónica” para UI (archivo OpenAI + espejo en Supabase)
* Se apoya en tu tabla `archivos`.
*/
export type AppFile = {
- id: UUID; // id interno (tabla archivos)
- openai_file_id: string; // id OpenAI
- nombre: string;
- mime_type: string | null;
- bytes: number | null;
+ id: UUID // id interno (tabla archivos)
+ openai_file_id: string // id OpenAI
+ nombre: string
+ mime_type: string | null
+ bytes: number | null
// espejo Supabase para preview/descarga
- ruta_storage: string | null; // "bucket/path"
- signed_url?: string | null;
+ ruta_storage: string | null // "bucket/path"
+ signed_url?: string | null
// auditoría/evidencia
- temporal: boolean;
- notas?: string | null;
+ temporal: boolean
+ notas?: string | null
- subido_en: string;
-};
+ subido_en: string
+}
const EDGE = {
- upload: "openai_files_upload",
- remove: "openai_files_delete",
-} as const;
+ upload: 'openai_files_upload',
+ remove: 'openai_files_delete',
+} as const
/**
* Sube archivo a OpenAI y (opcional) crea espejo en Storage
@@ -37,28 +37,28 @@ export async function openai_files_upload(payload: {
* Si tu Edge soporta multipart: manda File/Blob directo.
* Si no, manda base64/bytes (según tu implementación).
*/
- file: File;
+ file: File
- /** “temporal” = evidencia usada para generar plan/materia */
- temporal?: boolean;
+ /** “temporal” = evidencia usada para generar plan/asignatura */
+ temporal?: boolean
/** contexto para auditoría */
contexto?: {
- planId?: UUID;
- asignaturaId?: UUID;
- motivo?: "WIZARD_PLAN" | "WIZARD_MATERIA" | "ADHOC";
- };
+ planId?: UUID
+ asignaturaId?: UUID
+ motivo?: 'WIZARD_PLAN' | 'WIZARD_MATERIA' | 'ADHOC'
+ }
/** si quieres forzar espejo para preview siempre */
- mirrorToSupabase?: boolean;
+ mirrorToSupabase?: boolean
}): Promise {
- return invokeEdge(EDGE.upload, payload);
+ return invokeEdge(EDGE.upload, payload)
}
export async function openai_files_delete(payload: {
- openaiFileId: string;
+ openaiFileId: string
/** si quieres borrar también espejo y registro */
- hardDelete?: boolean;
+ hardDelete?: boolean
}): Promise<{ ok: true }> {
- return invokeEdge<{ ok: true }>(EDGE.remove, payload);
+ return invokeEdge<{ ok: true }>(EDGE.remove, payload)
}
diff --git a/src/data/api/plans.api.ts b/src/data/api/plans.api.ts
index 8a47440..ff40312 100644
--- a/src/data/api/plans.api.ts
+++ b/src/data/api/plans.api.ts
@@ -333,7 +333,7 @@ export async function plans_import_from_files(payload: {
}
archivoWordPlanId: UUID
archivoMapaExcelId?: UUID | null
- archivoMateriasExcelId?: UUID | null
+ archivoAsignaturasExcelId?: UUID | null
}): Promise {
return invokeEdge(EDGE.plans_import_from_files, payload)
}
diff --git a/src/data/api/subjects.api.ts b/src/data/api/subjects.api.ts
index 61d0fdc..cfd6a67 100644
--- a/src/data/api/subjects.api.ts
+++ b/src/data/api/subjects.api.ts
@@ -1,35 +1,35 @@
-import { supabaseBrowser } from "../supabase/client";
-import { invokeEdge } from "../supabase/invokeEdge";
-import { throwIfError, requireData } from "./_helpers";
+import { supabaseBrowser } from '../supabase/client'
+import { invokeEdge } from '../supabase/invokeEdge'
+import { throwIfError, requireData } from './_helpers'
import type {
Asignatura,
BibliografiaAsignatura,
CambioAsignatura,
TipoAsignatura,
UUID,
-} from "../types/domain";
-import type { DocumentoResult } from "./plans.api";
+} from '../types/domain'
+import type { DocumentoResult } from './plans.api'
const EDGE = {
- subjects_create_manual: "subjects_create_manual",
- ai_generate_subject: "ai_generate_subject",
- subjects_persist_from_ai: "subjects_persist_from_ai",
- subjects_clone_from_existing: "subjects_clone_from_existing",
- subjects_import_from_file: "subjects_import_from_file",
+ subjects_create_manual: 'subjects_create_manual',
+ ai_generate_subject: 'ai_generate_subject',
+ subjects_persist_from_ai: 'subjects_persist_from_ai',
+ subjects_clone_from_existing: 'subjects_clone_from_existing',
+ subjects_import_from_file: 'subjects_import_from_file',
- subjects_update_fields: "subjects_update_fields",
- subjects_update_contenido: "subjects_update_contenido",
- subjects_update_bibliografia: "subjects_update_bibliografia",
+ subjects_update_fields: 'subjects_update_fields',
+ subjects_update_contenido: 'subjects_update_contenido',
+ subjects_update_bibliografia: 'subjects_update_bibliografia',
- subjects_generate_document: "subjects_generate_document",
- subjects_get_document: "subjects_get_document",
-} as const;
+ subjects_generate_document: 'subjects_generate_document',
+ subjects_get_document: 'subjects_get_document',
+} as const
export async function subjects_get(subjectId: UUID): Promise {
- const supabase = supabaseBrowser();
+ const supabase = supabaseBrowser()
const { data, error } = await supabase
- .from("asignaturas")
+ .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,
@@ -38,144 +38,170 @@ export async function subjects_get(subjectId: UUID): Promise {
carreras(id,facultad_id,nombre,nombre_corto,clave_sep,activa, facultades(id,nombre,nombre_corto,color,icono))
),
estructuras_asignatura(id,nombre,version,definicion)
- `
+ `,
)
- .eq("id", subjectId)
- .single();
+ .eq('id', subjectId)
+ .single()
- throwIfError(error);
- return requireData(data, "Materia no encontrada.");
+ throwIfError(error)
+ return requireData(data, 'Asignatura no encontrada.')
}
-export async function subjects_history(subjectId: UUID): Promise {
- const supabase = supabaseBrowser();
+export async function subjects_history(
+ subjectId: UUID,
+): Promise {
+ const supabase = supabaseBrowser()
const { data, error } = await supabase
- .from("cambios_asignatura")
+ .from('cambios_asignatura')
.select(
- "id,asignatura_id,cambiado_por,cambiado_en,tipo,campo,valor_anterior,valor_nuevo,fuente,interaccion_ia_id"
+ 'id,asignatura_id,cambiado_por,cambiado_en,tipo,campo,valor_anterior,valor_nuevo,fuente,interaccion_ia_id',
)
- .eq("asignatura_id", subjectId)
- .order("cambiado_en", { ascending: false });
+ .eq('asignatura_id', subjectId)
+ .order('cambiado_en', { ascending: false })
- throwIfError(error);
- return data ?? [];
+ throwIfError(error)
+ return data ?? []
}
-export async function subjects_bibliografia_list(subjectId: UUID): Promise {
- const supabase = supabaseBrowser();
+export async function subjects_bibliografia_list(
+ subjectId: UUID,
+): Promise {
+ const supabase = supabaseBrowser()
const { data, error } = await supabase
- .from("bibliografia_asignatura")
- .select("id,asignatura_id,tipo,cita,tipo_fuente,biblioteca_item_id,creado_por,creado_en,actualizado_en")
- .eq("asignatura_id", subjectId)
- .order("tipo", { ascending: true })
- .order("creado_en", { ascending: true });
+ .from('bibliografia_asignatura')
+ .select(
+ 'id,asignatura_id,tipo,cita,tipo_fuente,biblioteca_item_id,creado_por,creado_en,actualizado_en',
+ )
+ .eq('asignatura_id', subjectId)
+ .order('tipo', { ascending: true })
+ .order('creado_en', { ascending: true })
- throwIfError(error);
- return data ?? [];
+ throwIfError(error)
+ return data ?? []
}
-/** Wizard: crear materia manual (Edge Function) */
+/** Wizard: crear asignatura manual (Edge Function) */
export type SubjectsCreateManualInput = {
- planId: UUID;
+ planId: UUID
datosBasicos: {
- nombre: string;
- clave?: string;
- tipo: TipoAsignatura;
- creditos: number;
- horasSemana?: number;
- estructuraId: UUID;
- };
-};
+ nombre: string
+ clave?: string
+ tipo: TipoAsignatura
+ creditos: number
+ horasSemana?: number
+ estructuraId: UUID
+ }
+}
-export async function subjects_create_manual(payload: SubjectsCreateManualInput): Promise {
- return invokeEdge(EDGE.subjects_create_manual, payload);
+export async function subjects_create_manual(
+ payload: SubjectsCreateManualInput,
+): Promise {
+ return invokeEdge(EDGE.subjects_create_manual, payload)
}
export async function ai_generate_subject(payload: {
- planId: UUID;
+ planId: UUID
datosBasicos: {
- nombre: string;
- clave?: string;
- tipo: TipoAsignatura;
- creditos: number;
- horasSemana?: number;
- estructuraId: UUID;
- };
+ nombre: string
+ clave?: string
+ tipo: TipoAsignatura
+ creditos: number
+ horasSemana?: number
+ estructuraId: UUID
+ }
iaConfig: {
- descripcionEnfoque: string;
- notasAdicionales?: string;
- archivosExistentesIds?: UUID[];
- repositoriosIds?: UUID[];
- archivosAdhocIds?: UUID[];
- usarMCP?: boolean;
- };
+ descripcionEnfoque: string
+ notasAdicionales?: string
+ archivosExistentesIds?: UUID[]
+ repositoriosIds?: UUID[]
+ archivosAdhocIds?: UUID[]
+ usarMCP?: boolean
+ }
}): Promise {
- return invokeEdge(EDGE.ai_generate_subject, payload);
+ return invokeEdge(EDGE.ai_generate_subject, payload)
}
-export async function subjects_persist_from_ai(payload: { planId: UUID; jsonMateria: any }): Promise {
- return invokeEdge(EDGE.subjects_persist_from_ai, payload);
+export async function subjects_persist_from_ai(payload: {
+ planId: UUID
+ jsonAsignatura: any
+}): Promise {
+ return invokeEdge(EDGE.subjects_persist_from_ai, payload)
}
export async function subjects_clone_from_existing(payload: {
- materiaOrigenId: UUID;
- planDestinoId: UUID;
+ asignaturaOrigenId: UUID
+ planDestinoId: UUID
overrides?: Partial<{
- nombre: string;
- codigo: string;
- tipo: TipoAsignatura;
- creditos: number;
- horas_semana: number;
- }>;
+ nombre: string
+ codigo: string
+ tipo: TipoAsignatura
+ creditos: number
+ horas_semana: number
+ }>
}): Promise {
- return invokeEdge(EDGE.subjects_clone_from_existing, payload);
+ return invokeEdge(EDGE.subjects_clone_from_existing, payload)
}
export async function subjects_import_from_file(payload: {
- planId: UUID;
- archivoWordMateriaId: UUID;
- archivosAdicionalesIds?: UUID[];
+ planId: UUID
+ archivoWordAsignaturaId: UUID
+ archivosAdicionalesIds?: UUID[]
}): Promise {
- return invokeEdge(EDGE.subjects_import_from_file, payload);
+ return invokeEdge(EDGE.subjects_import_from_file, payload)
}
/** Guardado de tarjetas/fields (Edge: merge server-side en asignaturas.datos y columnas) */
export type SubjectsUpdateFieldsPatch = Partial<{
- codigo: string | null;
- nombre: string;
- tipo: TipoAsignatura;
- creditos: number;
- horas_semana: number | null;
- numero_ciclo: number | null;
- linea_plan_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
- datos: Record;
-}>;
+ datos: Record
+}>
-export async function subjects_update_fields(subjectId: UUID, patch: SubjectsUpdateFieldsPatch): Promise {
- return invokeEdge(EDGE.subjects_update_fields, { subjectId, patch });
+export async function subjects_update_fields(
+ subjectId: UUID,
+ patch: SubjectsUpdateFieldsPatch,
+): Promise {
+ return invokeEdge(EDGE.subjects_update_fields, {
+ subjectId,
+ patch,
+ })
}
-export async function subjects_update_contenido(subjectId: UUID, unidades: any[]): Promise {
- return invokeEdge(EDGE.subjects_update_contenido, { subjectId, unidades });
+export async function subjects_update_contenido(
+ subjectId: UUID,
+ unidades: any[],
+): Promise {
+ return invokeEdge(EDGE.subjects_update_contenido, {
+ subjectId,
+ unidades,
+ })
}
export type BibliografiaUpsertInput = Array<{
- id?: UUID;
- tipo: "BASICA" | "COMPLEMENTARIA";
- cita: string;
- tipo_fuente?: "MANUAL" | "BIBLIOTECA";
- biblioteca_item_id?: string | null;
-}>;
+ id?: UUID
+ tipo: 'BASICA' | 'COMPLEMENTARIA'
+ cita: string
+ tipo_fuente?: 'MANUAL' | 'BIBLIOTECA'
+ biblioteca_item_id?: string | null
+}>
export async function subjects_update_bibliografia(
subjectId: UUID,
- entries: BibliografiaUpsertInput
+ entries: BibliografiaUpsertInput,
): Promise<{ ok: true }> {
- return invokeEdge<{ ok: true }>(EDGE.subjects_update_bibliografia, { subjectId, entries });
+ return invokeEdge<{ ok: true }>(EDGE.subjects_update_bibliografia, {
+ subjectId,
+ entries,
+ })
}
-/** Documento SEP materia */
+/** Documento SEP asignatura */
/* export type DocumentoResult = {
archivoId: UUID;
signedUrl: string;
@@ -183,10 +209,18 @@ export async function subjects_update_bibliografia(
nombre?: string;
}; */
-export async function subjects_generate_document(subjectId: UUID): Promise {
- return invokeEdge(EDGE.subjects_generate_document, { subjectId });
+export async function subjects_generate_document(
+ subjectId: UUID,
+): Promise {
+ return invokeEdge(EDGE.subjects_generate_document, {
+ subjectId,
+ })
}
-export async function subjects_get_document(subjectId: UUID): Promise {
- return invokeEdge(EDGE.subjects_get_document, { subjectId });
+export async function subjects_get_document(
+ subjectId: UUID,
+): Promise {
+ return invokeEdge(EDGE.subjects_get_document, {
+ subjectId,
+ })
}
diff --git a/src/data/hooks/useSubjects.ts b/src/data/hooks/useSubjects.ts
index ebcf4ea..afeedf0 100644
--- a/src/data/hooks/useSubjects.ts
+++ b/src/data/hooks/useSubjects.ts
@@ -91,7 +91,7 @@ export function usePersistSubjectFromAI() {
const qc = useQueryClient()
return useMutation({
- mutationFn: (payload: { planId: UUID; jsonMateria: any }) =>
+ mutationFn: (payload: { planId: UUID; jsonAsignatura: any }) =>
subjects_persist_from_ai(payload),
onSuccess: (subject) => {
qc.setQueryData(qk.asignatura(subject.id), subject)
diff --git a/src/data/mockMateriaData.ts b/src/data/mockAsignaturaData.ts
similarity index 54%
rename from src/data/mockMateriaData.ts
rename to src/data/mockAsignaturaData.ts
index aa8e3fb..208d066 100644
--- a/src/data/mockMateriaData.ts
+++ b/src/data/mockAsignaturaData.ts
@@ -1,14 +1,14 @@
-import type {
- Materia,
- MateriaStructure,
- UnidadTematica,
- BibliografiaEntry,
- CambioMateria,
- DocumentoMateria,
- LibraryResource
-} from '@/types/materia';
+import type {
+ Asignatura,
+ AsignaturaStructure,
+ UnidadTematica,
+ BibliografiaEntry,
+ CambioAsignatura,
+ DocumentoAsignatura,
+ LibraryResource,
+} from '@/types/asignatura'
-export const mockMateria: Materia = {
+export const mockAsignatura: Asignatura = {
id: '1',
nombre: 'Inteligencia Artificial Aplicada',
clave: 'IAA-401',
@@ -20,9 +20,9 @@ export const mockMateria: Materia = {
carrera: 'Ingeniería en Sistemas Computacionales',
facultad: 'Facultad de Ingeniería',
estructuraId: 'estructura-1',
-};
+}
-export const mockEstructura: MateriaStructure = {
+export const mockEstructura: AsignaturaStructure = {
id: 'estructura-1',
nombre: 'Plantilla SEP Licenciatura',
campos: [
@@ -31,7 +31,7 @@ export const mockEstructura: MateriaStructure = {
nombre: 'Objetivo General',
tipo: 'texto_largo',
obligatorio: true,
- descripcion: 'Describe el propósito principal de la materia',
+ descripcion: 'Describe el propósito principal de la asignatura',
placeholder: 'Al finalizar el curso, el estudiante será capaz de...',
},
{
@@ -46,14 +46,14 @@ export const mockEstructura: MateriaStructure = {
nombre: 'Justificación',
tipo: 'texto_largo',
obligatorio: true,
- descripcion: 'Relevancia de la materia en el plan de estudios',
+ descripcion: 'Relevancia de la asignatura en el plan de estudios',
},
{
id: 'requisitos',
nombre: 'Requisitos / Seriación',
tipo: 'texto',
obligatorio: false,
- descripcion: 'Materias previas requeridas',
+ descripcion: 'Asignaturas previas requeridas',
},
{
id: 'estrategias_didacticas',
@@ -77,27 +77,49 @@ export const mockEstructura: MateriaStructure = {
descripcion: 'Características requeridas del profesor',
},
],
-};
+}
export const mockDatosGenerales: Record = {
- objetivo_general: 'Formar profesionales capaces de diseñar, implementar y evaluar sistemas de inteligencia artificial que resuelvan problemas complejos del mundo real, aplicando principios éticos y metodologías actuales en el campo.',
- competencias: '• Diseñar algoritmos de machine learning para clasificación y predicción\n• Implementar redes neuronales profundas para procesamiento de imágenes y texto\n• Evaluar y optimizar modelos de IA considerando métricas de rendimiento\n• Aplicar principios éticos en el desarrollo de sistemas inteligentes',
- justificacion: 'La inteligencia artificial es una de las tecnologías más disruptivas del siglo XXI. Su integración en diversos sectores demanda profesionales con sólidas bases teóricas y prácticas. Esta materia proporciona las competencias necesarias para que el egresado pueda innovar y contribuir al desarrollo tecnológico del país.',
- requisitos: 'Programación Avanzada (PAV-301), Matemáticas Discretas (MAT-201)',
- estrategias_didacticas: '• Aprendizaje basado en proyectos\n• Talleres prácticos con datasets reales\n• Exposiciones y discusiones grupales\n• Análisis de casos de estudio\n• Desarrollo de prototipo integrador',
- evaluacion: '• Exámenes parciales: 30%\n• Proyecto integrador: 35%\n• Prácticas de laboratorio: 20%\n• Participación y tareas: 15%',
- perfil_docente: 'Profesional con maestría o doctorado en áreas afines a la inteligencia artificial, con experiencia mínima de 3 años en docencia y desarrollo de proyectos de IA.',
-};
+ objetivo_general:
+ 'Formar profesionales capaces de diseñar, implementar y evaluar sistemas de inteligencia artificial que resuelvan problemas complejos del mundo real, aplicando principios éticos y metodologías actuales en el campo.',
+ competencias:
+ '• Diseñar algoritmos de machine learning para clasificación y predicción\n• Implementar redes neuronales profundas para procesamiento de imágenes y texto\n• Evaluar y optimizar modelos de IA considerando métricas de rendimiento\n• Aplicar principios éticos en el desarrollo de sistemas inteligentes',
+ justificacion:
+ 'La inteligencia artificial es una de las tecnologías más disruptivas del siglo XXI. Su integración en diversos sectores demanda profesionales con sólidas bases teóricas y prácticas. Esta asignatura proporciona las competencias necesarias para que el egresado pueda innovar y contribuir al desarrollo tecnológico del país.',
+ requisitos:
+ 'Programación Avanzada (PAV-301), Matemáticas Discretas (MAT-201)',
+ estrategias_didacticas:
+ '• Aprendizaje basado en proyectos\n• Talleres prácticos con datasets reales\n• Exposiciones y discusiones grupales\n• Análisis de casos de estudio\n• Desarrollo de prototipo integrador',
+ evaluacion:
+ '• Exámenes parciales: 30%\n• Proyecto integrador: 35%\n• Prácticas de laboratorio: 20%\n• Participación y tareas: 15%',
+ perfil_docente:
+ 'Profesional con maestría o doctorado en áreas afines a la inteligencia artificial, con experiencia mínima de 3 años en docencia y desarrollo de proyectos de IA.',
+}
-export const mockContenidoTematico: UnidadTematica[] = [
+export const mockContenidoTematico: Array = [
{
id: 'unidad-1',
nombre: 'Fundamentos de Inteligencia Artificial',
numero: 1,
temas: [
- { id: 'tema-1-1', nombre: 'Historia y evolución de la IA', descripcion: 'Desde los orígenes hasta la actualidad', horasEstimadas: 2 },
- { id: 'tema-1-2', nombre: 'Tipos de IA y aplicaciones', descripcion: 'IA débil, fuerte y superinteligencia', horasEstimadas: 3 },
- { id: 'tema-1-3', nombre: 'Ética en IA', descripcion: 'Consideraciones éticas y responsabilidad', horasEstimadas: 2 },
+ {
+ id: 'tema-1-1',
+ nombre: 'Historia y evolución de la IA',
+ descripcion: 'Desde los orígenes hasta la actualidad',
+ horasEstimadas: 2,
+ },
+ {
+ id: 'tema-1-2',
+ nombre: 'Tipos de IA y aplicaciones',
+ descripcion: 'IA débil, fuerte y superinteligencia',
+ horasEstimadas: 3,
+ },
+ {
+ id: 'tema-1-3',
+ nombre: 'Ética en IA',
+ descripcion: 'Consideraciones éticas y responsabilidad',
+ horasEstimadas: 2,
+ },
],
},
{
@@ -105,9 +127,24 @@ export const mockContenidoTematico: UnidadTematica[] = [
nombre: 'Machine Learning',
numero: 2,
temas: [
- { id: 'tema-2-1', nombre: 'Aprendizaje supervisado', descripcion: 'Regresión y clasificación', horasEstimadas: 6 },
- { id: 'tema-2-2', nombre: 'Aprendizaje no supervisado', descripcion: 'Clustering y reducción de dimensionalidad', horasEstimadas: 5 },
- { id: 'tema-2-3', nombre: 'Evaluación de modelos', descripcion: 'Métricas y validación cruzada', horasEstimadas: 4 },
+ {
+ id: 'tema-2-1',
+ nombre: 'Aprendizaje supervisado',
+ descripcion: 'Regresión y clasificación',
+ horasEstimadas: 6,
+ },
+ {
+ id: 'tema-2-2',
+ nombre: 'Aprendizaje no supervisado',
+ descripcion: 'Clustering y reducción de dimensionalidad',
+ horasEstimadas: 5,
+ },
+ {
+ id: 'tema-2-3',
+ nombre: 'Evaluación de modelos',
+ descripcion: 'Métricas y validación cruzada',
+ horasEstimadas: 4,
+ },
],
},
{
@@ -115,10 +152,30 @@ export const mockContenidoTematico: UnidadTematica[] = [
nombre: 'Deep Learning',
numero: 3,
temas: [
- { id: 'tema-3-1', nombre: 'Redes neuronales artificiales', descripcion: 'Perceptrón y backpropagation', horasEstimadas: 5 },
- { id: 'tema-3-2', nombre: 'Redes convolucionales (CNN)', descripcion: 'Procesamiento de imágenes', horasEstimadas: 6 },
- { id: 'tema-3-3', nombre: 'Redes recurrentes (RNN)', descripcion: 'Procesamiento de secuencias', horasEstimadas: 5 },
- { id: 'tema-3-4', nombre: 'Transformers y atención', descripcion: 'Arquitecturas modernas', horasEstimadas: 6 },
+ {
+ id: 'tema-3-1',
+ nombre: 'Redes neuronales artificiales',
+ descripcion: 'Perceptrón y backpropagation',
+ horasEstimadas: 5,
+ },
+ {
+ id: 'tema-3-2',
+ nombre: 'Redes convolucionales (CNN)',
+ descripcion: 'Procesamiento de imágenes',
+ horasEstimadas: 6,
+ },
+ {
+ id: 'tema-3-3',
+ nombre: 'Redes recurrentes (RNN)',
+ descripcion: 'Procesamiento de secuencias',
+ horasEstimadas: 5,
+ },
+ {
+ id: 'tema-3-4',
+ nombre: 'Transformers y atención',
+ descripcion: 'Arquitecturas modernas',
+ horasEstimadas: 6,
+ },
],
},
{
@@ -126,14 +183,29 @@ export const mockContenidoTematico: UnidadTematica[] = [
nombre: 'Aplicaciones Prácticas',
numero: 4,
temas: [
- { id: 'tema-4-1', nombre: 'Procesamiento de lenguaje natural', descripcion: 'NLP y chatbots', horasEstimadas: 6 },
- { id: 'tema-4-2', nombre: 'Visión por computadora', descripcion: 'Detección y reconocimiento', horasEstimadas: 5 },
- { id: 'tema-4-3', nombre: 'Sistemas de recomendación', descripcion: 'Filtrado colaborativo y contenido', horasEstimadas: 4 },
+ {
+ id: 'tema-4-1',
+ nombre: 'Procesamiento de lenguaje natural',
+ descripcion: 'NLP y chatbots',
+ horasEstimadas: 6,
+ },
+ {
+ id: 'tema-4-2',
+ nombre: 'Visión por computadora',
+ descripcion: 'Detección y reconocimiento',
+ horasEstimadas: 5,
+ },
+ {
+ id: 'tema-4-3',
+ nombre: 'Sistemas de recomendación',
+ descripcion: 'Filtrado colaborativo y contenido',
+ horasEstimadas: 4,
+ },
],
},
-];
+]
-export const mockBibliografia: BibliografiaEntry[] = [
+export const mockBibliografia: Array = [
{
id: 'bib-1',
tipo: 'BASICA',
@@ -153,13 +225,14 @@ export const mockBibliografia: BibliografiaEntry[] = [
{
id: 'bib-2',
tipo: 'BASICA',
- cita: 'Géron, A. (2022). Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow (3rd ed.). O\'Reilly Media.',
+ cita: "Géron, A. (2022). Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow (3rd ed.). O'Reilly Media.",
fuenteBibliotecaId: 'lib-2',
fuenteBiblioteca: {
id: 'lib-2',
- titulo: 'Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow',
+ titulo:
+ 'Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow',
autor: 'Aurélien Géron',
- editorial: 'O\'Reilly Media',
+ editorial: "O'Reilly Media",
anio: 2022,
isbn: '978-1098125974',
tipo: 'libro',
@@ -187,9 +260,9 @@ export const mockBibliografia: BibliografiaEntry[] = [
disponible: false,
},
},
-];
+]
-export const mockHistorial: CambioMateria[] = [
+export const mockHistorial: Array = [
{
id: 'cambio-1',
tipo: 'datos',
@@ -228,17 +301,17 @@ export const mockHistorial: CambioMateria[] = [
usuario: 'Sistema',
fecha: new Date('2024-12-06T11:30:00'),
},
-];
+]
-export const mockDocumentoSep: DocumentoMateria = {
+export const mockDocumentoSep: DocumentoAsignatura = {
id: 'doc-1',
- materiaId: '1',
+ asignaturaId: '1',
version: 3,
fechaGeneracion: new Date('2024-12-06T11:30:00'),
estado: 'listo',
-};
+}
-export const mockLibraryResources: LibraryResource[] = [
+export const mockLibraryResources: Array = [
{
id: 'lib-1',
titulo: 'Artificial Intelligence: A Modern Approach',
@@ -251,9 +324,10 @@ export const mockLibraryResources: LibraryResource[] = [
},
{
id: 'lib-2',
- titulo: 'Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow',
+ titulo:
+ 'Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow',
autor: 'Aurélien Géron',
- editorial: 'O\'Reilly Media',
+ editorial: "O'Reilly Media",
anio: 2022,
isbn: '978-1098125974',
tipo: 'libro',
@@ -299,4 +373,4 @@ export const mockLibraryResources: LibraryResource[] = [
tipo: 'libro',
disponible: true,
},
-];
+]
diff --git a/src/features/planes/nuevo/catalogs.ts b/src/features/planes/nuevo/catalogs.ts
index afe98be..4741794 100644
--- a/src/features/planes/nuevo/catalogs.ts
+++ b/src/features/planes/nuevo/catalogs.ts
@@ -1,156 +1,156 @@
-import type { NivelPlanEstudio, TipoCiclo } from "@/data/types/domain";
+import type { NivelPlanEstudio, TipoCiclo } from '@/data/types/domain'
export const FACULTADES = [
- { id: "ing", nombre: "Facultad de Ingeniería" },
+ { id: 'ing', nombre: 'Facultad de Ingeniería' },
{
- id: "med",
- nombre: "Facultad de Medicina en medicina en medicina en medicina",
+ id: 'med',
+ nombre: 'Facultad de Medicina en medicina en medicina en medicina',
},
- { id: "neg", nombre: "Facultad de Negocios" },
-];
+ { id: 'neg', nombre: 'Facultad de Negocios' },
+]
export const CARRERAS = [
- { id: "sis", nombre: "Ing. en Sistemas", facultadId: "ing" },
- { id: "ind", nombre: "Ing. Industrial", facultadId: "ing" },
- { id: "medico", nombre: "Médico Cirujano", facultadId: "med" },
- { id: "act", nombre: "Actuaría", facultadId: "neg" },
-];
+ { id: 'sis', nombre: 'Ing. en Sistemas', facultadId: 'ing' },
+ { id: 'ind', nombre: 'Ing. Industrial', facultadId: 'ing' },
+ { id: 'medico', nombre: 'Médico Cirujano', facultadId: 'med' },
+ { id: 'act', nombre: 'Actuaría', facultadId: 'neg' },
+]
export const NIVELES: Array = [
- "Licenciatura",
- "Maestría",
- "Doctorado",
- "Especialidad",
- "Diplomado",
- "Otro",
-];
+ 'Licenciatura',
+ 'Maestría',
+ 'Doctorado',
+ 'Especialidad',
+ 'Diplomado',
+ 'Otro',
+]
export const TIPOS_CICLO: Array = [
- "Semestre",
- "Cuatrimestre",
- "Trimestre",
- "Otro",
-];
+ 'Semestre',
+ 'Cuatrimestre',
+ 'Trimestre',
+ 'Otro',
+]
export const PLANES_EXISTENTES = [
{
- id: "plan-2021-sis",
- nombre: "ISC 2021",
- estado: "Aprobado",
+ id: 'plan-2021-sis',
+ nombre: 'ISC 2021',
+ estado: 'Aprobado',
anio: 2021,
- facultadId: "ing",
- carreraId: "sis",
+ facultadId: 'ing',
+ carreraId: 'sis',
},
{
- id: "plan-2020-ind",
- nombre: "I. Industrial 2020",
- estado: "Aprobado",
+ id: 'plan-2020-ind',
+ nombre: 'I. Industrial 2020',
+ estado: 'Aprobado',
anio: 2020,
- facultadId: "ing",
- carreraId: "ind",
+ facultadId: 'ing',
+ carreraId: 'ind',
},
{
- id: "plan-2019-med",
- nombre: "Medicina 2019",
- estado: "Vigente",
+ id: 'plan-2019-med',
+ nombre: 'Medicina 2019',
+ estado: 'Vigente',
anio: 2019,
- facultadId: "med",
- carreraId: "medico",
+ facultadId: 'med',
+ carreraId: 'medico',
},
-];
+]
export const ARCHIVOS = [
{
- id: "file-1",
- nombre: "Sílabo POO 2023.docx",
- tipo: "docx",
- tamaño: "245 KB",
+ id: 'file-1',
+ nombre: 'Sílabo POO 2023.docx',
+ tipo: 'docx',
+ tamaño: '245 KB',
},
{
- id: "file-2",
- nombre: "Guía de prácticas BD.pdf",
- tipo: "pdf",
- tamaño: "1.2 MB",
+ id: 'file-2',
+ nombre: 'Guía de prácticas BD.pdf',
+ tipo: 'pdf',
+ tamaño: '1.2 MB',
},
{
- id: "file-3",
- nombre: "Rúbrica evaluación proyectos.xlsx",
- tipo: "xlsx",
- tamaño: "89 KB",
+ id: 'file-3',
+ nombre: 'Rúbrica evaluación proyectos.xlsx',
+ tipo: 'xlsx',
+ tamaño: '89 KB',
},
{
- id: "file-4",
- nombre: "Banco de reactivos IA.docx",
- tipo: "docx",
- tamaño: "567 KB",
+ id: 'file-4',
+ nombre: 'Banco de reactivos IA.docx',
+ tipo: 'docx',
+ tamaño: '567 KB',
},
{
- id: "file-5",
- nombre: "Material didáctico Web.pdf",
- tipo: "pdf",
- tamaño: "3.4 MB",
+ id: 'file-5',
+ nombre: 'Asignatural didáctico Web.pdf',
+ tipo: 'pdf',
+ tamaño: '3.4 MB',
},
-];
+]
export const REPOSITORIOS = [
{
- id: "repo-1",
- nombre: "Materiales ISC 2024",
- descripcion: "Documentos de referencia para Ingeniería en Sistemas",
+ id: 'repo-1',
+ nombre: 'Asignaturales ISC 2024',
+ descripcion: 'Documentos de referencia para Ingeniería en Sistemas',
cantidadArchivos: 45,
},
{
- id: "repo-2",
- nombre: "Lineamientos SEP",
- descripcion: "Documentos oficiales y normativas SEP actualizadas",
+ id: 'repo-2',
+ nombre: 'Lineamientos SEP',
+ descripcion: 'Documentos oficiales y normativas SEP actualizadas',
cantidadArchivos: 12,
},
{
- id: "repo-3",
- nombre: "Bibliografía Digital",
- descripcion: "Recursos bibliográficos digitalizados",
+ id: 'repo-3',
+ nombre: 'Bibliografía Digital',
+ descripcion: 'Recursos bibliográficos digitalizados',
cantidadArchivos: 128,
},
{
- id: "repo-4",
- nombre: "Plantillas Institucionales",
- descripcion: "Formatos y plantillas oficiales ULSA",
+ id: 'repo-4',
+ nombre: 'Plantillas Institucionales',
+ descripcion: 'Formatos y plantillas oficiales ULSA',
cantidadArchivos: 23,
},
-];
+]
export const PLANTILLAS_ANEXO_1 = [
{
- id: "sep-2025",
- name: "Licenciatura RVOE SEP.docx",
- versions: ["v2025.2 (Vigente)", "v2025.1", "v2024.Final"],
+ id: 'sep-2025',
+ name: 'Licenciatura RVOE SEP.docx',
+ versions: ['v2025.2 (Vigente)', 'v2025.1', 'v2024.Final'],
},
{
- id: "interno-mix",
- name: "Estándar Institucional Mixto.docx",
- versions: ["v2.0", "v1.5", "v1.0-beta"],
+ id: 'interno-mix',
+ name: 'Estándar Institucional Mixto.docx',
+ versions: ['v2.0', 'v1.5', 'v1.0-beta'],
},
{
- id: "conacyt",
- name: "Formato Posgrado CONAHCYT.docx",
- versions: ["v3.0 (2025)", "v2.8"],
+ id: 'conacyt',
+ name: 'Formato Posgrado CONAHCYT.docx',
+ versions: ['v3.0 (2025)', 'v2.8'],
},
-];
+]
export const PLANTILLAS_ANEXO_2 = [
{
- id: "sep-2017-xlsx",
- name: "Licenciatura RVOE 2017.xlsx",
- versions: ["v2017.0", "v2018.1", "v2019.2", "v2020.Final"],
+ id: 'sep-2017-xlsx',
+ name: 'Licenciatura RVOE 2017.xlsx',
+ versions: ['v2017.0', 'v2018.1', 'v2019.2', 'v2020.Final'],
},
{
- id: "interno-mix-xlsx",
- name: "Estándar Institucional Mixto.xlsx",
- versions: ["v1.0", "v1.5"],
+ id: 'interno-mix-xlsx',
+ name: 'Estándar Institucional Mixto.xlsx',
+ versions: ['v1.0', 'v1.5'],
},
{
- id: "conacyt-xlsx",
- name: "Formato Posgrado CONAHCYT.xlsx",
- versions: ["v1.0", "v2.0"],
+ id: 'conacyt-xlsx',
+ name: 'Formato Posgrado CONAHCYT.xlsx',
+ versions: ['v1.0', 'v2.0'],
},
-];
+]
diff --git a/src/routes/planes/$planId/_detalle/MateriaCard.tsx b/src/routes/planes/$planId/_detalle/MateriaCard.tsx
deleted file mode 100644
index 403c7c9..0000000
--- a/src/routes/planes/$planId/_detalle/MateriaCard.tsx
+++ /dev/null
@@ -1,137 +0,0 @@
-import * as Dialog from '@radix-ui/react-dialog';
-import { Pencil, X } from 'lucide-react';
-export type Materia = {
- id: string;
- clave: string;
- nombre: string;
- creditos: number;
- hd: number; // Horas Docente
- hi: number; // Horas Independientes
- tipo: 'Obligatoria' | 'Optativa' | 'Especialidad';
- ciclo: number;
- linea: string;
- estado: string;
-};
-
-interface MateriaCardProps {
- materia: Materia;
-}
-
-export function MateriaCard({ materia }: MateriaCardProps) {
- return (
-
- {/* Trigger: La tarjeta en sí misma */}
-
-
- {materias.length} materias en total • {filteredMaterias.length}{' '}
- filtradas
+ {asignaturas.length} asignaturas en total •{' '}
+ {filteredAsignaturas.length} filtradas
@@ -132,7 +132,7 @@ function MateriasPage() {
Clonar