PasoResumen muestra resumen antes de crear múltiples asignaturas
This commit is contained in:
@@ -9,12 +9,13 @@ import {
|
|||||||
CardHeader,
|
CardHeader,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
} from '@/components/ui/card'
|
} from '@/components/ui/card'
|
||||||
import { usePlan, useSubjectEstructuras } from '@/data'
|
import { usePlan, usePlanLineas, useSubjectEstructuras } from '@/data'
|
||||||
import { formatFileSize } from '@/features/planes/utils/format-file-size'
|
import { formatFileSize } from '@/features/planes/utils/format-file-size'
|
||||||
|
|
||||||
export function PasoResumenCard({ wizard }: { wizard: NewSubjectWizardState }) {
|
export function PasoResumenCard({ wizard }: { wizard: NewSubjectWizardState }) {
|
||||||
const { data: plan } = usePlan(wizard.plan_estudio_id)
|
const { data: plan } = usePlan(wizard.plan_estudio_id)
|
||||||
const { data: estructuras } = useSubjectEstructuras()
|
const { data: estructuras } = useSubjectEstructuras()
|
||||||
|
const { data: lineasPlan } = usePlanLineas(wizard.plan_estudio_id)
|
||||||
|
|
||||||
const estructuraNombre = (() => {
|
const estructuraNombre = (() => {
|
||||||
const estructuraId = wizard.datosBasicos.estructuraId
|
const estructuraId = wizard.datosBasicos.estructuraId
|
||||||
@@ -26,6 +27,8 @@ export function PasoResumenCard({ wizard }: { wizard: NewSubjectWizardState }) {
|
|||||||
const modoLabel = (() => {
|
const modoLabel = (() => {
|
||||||
if (wizard.tipoOrigen === 'MANUAL') return 'Manual (Vacía)'
|
if (wizard.tipoOrigen === 'MANUAL') return 'Manual (Vacía)'
|
||||||
if (wizard.tipoOrigen === 'IA') return 'Generada con IA'
|
if (wizard.tipoOrigen === 'IA') return 'Generada con IA'
|
||||||
|
if (wizard.tipoOrigen === 'IA_SIMPLE') return 'Generada con IA (Simple)'
|
||||||
|
if (wizard.tipoOrigen === 'IA_MULTIPLE') return 'Generación múltiple (IA)'
|
||||||
if (wizard.tipoOrigen === 'CLONADO_INTERNO') return 'Clonada (Sistema)'
|
if (wizard.tipoOrigen === 'CLONADO_INTERNO') return 'Clonada (Sistema)'
|
||||||
if (wizard.tipoOrigen === 'CLONADO_TRADICIONAL') return 'Clonada (Archivo)'
|
if (wizard.tipoOrigen === 'CLONADO_TRADICIONAL') return 'Clonada (Archivo)'
|
||||||
return '—'
|
return '—'
|
||||||
@@ -41,6 +44,10 @@ export function PasoResumenCard({ wizard }: { wizard: NewSubjectWizardState }) {
|
|||||||
const repositoriosRef = wizard.iaConfig?.repositoriosReferencia ?? []
|
const repositoriosRef = wizard.iaConfig?.repositoriosReferencia ?? []
|
||||||
const adjuntos = wizard.iaConfig?.archivosAdjuntos ?? []
|
const adjuntos = wizard.iaConfig?.archivosAdjuntos ?? []
|
||||||
|
|
||||||
|
const materiasSeleccionadas = wizard.sugerencias.filter((s) => s.selected)
|
||||||
|
const iaMultipleEnfoque = wizard.iaMultiple?.enfoque.trim() ?? ''
|
||||||
|
const iaMultipleCantidad = wizard.iaMultiple?.cantidadDeSugerencias ?? 10
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
@@ -72,7 +79,9 @@ export function PasoResumenCard({ wizard }: { wizard: NewSubjectWizardState }) {
|
|||||||
{wizard.tipoOrigen === 'MANUAL' && (
|
{wizard.tipoOrigen === 'MANUAL' && (
|
||||||
<Icons.Pencil className="h-4 w-4" />
|
<Icons.Pencil className="h-4 w-4" />
|
||||||
)}
|
)}
|
||||||
{wizard.tipoOrigen === 'IA' && (
|
{(wizard.tipoOrigen === 'IA' ||
|
||||||
|
wizard.tipoOrigen === 'IA_SIMPLE' ||
|
||||||
|
wizard.tipoOrigen === 'IA_MULTIPLE') && (
|
||||||
<Icons.Sparkles className="h-4 w-4" />
|
<Icons.Sparkles className="h-4 w-4" />
|
||||||
)}
|
)}
|
||||||
{(wizard.tipoOrigen === 'CLONADO_INTERNO' ||
|
{(wizard.tipoOrigen === 'CLONADO_INTERNO' ||
|
||||||
@@ -83,6 +92,88 @@ export function PasoResumenCard({ wizard }: { wizard: NewSubjectWizardState }) {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{wizard.tipoOrigen === 'IA_MULTIPLE' ? (
|
||||||
|
<>
|
||||||
|
<div className="border-border/60 bg-muted/30 grid gap-3 rounded-xl border p-4">
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<div className="text-foreground text-base font-semibold">
|
||||||
|
Configuración
|
||||||
|
</div>
|
||||||
|
<div className="text-muted-foreground text-xs">
|
||||||
|
Se crearán {materiasSeleccionadas.length} asignatura(s) a
|
||||||
|
partir de tus selecciones.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-background/40 border-border/60 rounded-lg border p-3">
|
||||||
|
<div className="text-muted-foreground text-xs">
|
||||||
|
Estructura
|
||||||
|
</div>
|
||||||
|
<div className="text-foreground mt-1 text-sm font-medium">
|
||||||
|
{estructuraNombre}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="border-border/60 bg-muted/30 grid gap-3 rounded-xl border p-4">
|
||||||
|
<div className="flex items-end justify-between gap-2">
|
||||||
|
<div className="text-foreground text-base font-semibold">
|
||||||
|
Materias seleccionadas
|
||||||
|
</div>
|
||||||
|
<div className="text-muted-foreground text-xs">
|
||||||
|
{materiasSeleccionadas.length} en total
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{materiasSeleccionadas.length === 0 ? (
|
||||||
|
<div className="text-muted-foreground text-sm">
|
||||||
|
No hay materias seleccionadas.
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="grid gap-3">
|
||||||
|
{materiasSeleccionadas.map((m) => {
|
||||||
|
const lineaNombre = m.linea_plan_id
|
||||||
|
? (lineasPlan?.find((l) => l.id === m.linea_plan_id)
|
||||||
|
?.nombre ?? m.linea_plan_id)
|
||||||
|
: '—'
|
||||||
|
|
||||||
|
const cicloText =
|
||||||
|
typeof m.numero_ciclo === 'number' &&
|
||||||
|
Number.isFinite(m.numero_ciclo)
|
||||||
|
? String(m.numero_ciclo)
|
||||||
|
: '—'
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={m.id}
|
||||||
|
className="bg-background/40 border-border/60 grid gap-2 rounded-lg border p-3"
|
||||||
|
>
|
||||||
|
<div className="flex flex-wrap items-center justify-between gap-2">
|
||||||
|
<div className="text-foreground text-sm font-semibold">
|
||||||
|
{m.nombre}
|
||||||
|
</div>
|
||||||
|
<div className="text-muted-foreground flex flex-wrap items-center gap-2 text-xs">
|
||||||
|
<span className="bg-accent/30 text-accent-foreground rounded-full px-2 py-0.5">
|
||||||
|
Línea: {lineaNombre}
|
||||||
|
</span>
|
||||||
|
<span className="bg-accent/30 text-accent-foreground rounded-full px-2 py-0.5">
|
||||||
|
Ciclo: {cicloText}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-muted-foreground text-sm whitespace-pre-wrap">
|
||||||
|
{m.descripcion || '—'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
<div className="grid grid-cols-2 gap-4">
|
<div className="grid grid-cols-2 gap-4">
|
||||||
<div className="col-span-2">
|
<div className="col-span-2">
|
||||||
<span className="text-muted-foreground">Nombre: </span>
|
<span className="text-muted-foreground">Nombre: </span>
|
||||||
@@ -111,7 +202,9 @@ export function PasoResumenCard({ wizard }: { wizard: NewSubjectWizardState }) {
|
|||||||
<span className="font-medium">{estructuraNombre}</span>
|
<span className="font-medium">{estructuraNombre}</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span className="text-muted-foreground">Horas académicas: </span>
|
<span className="text-muted-foreground">
|
||||||
|
Horas académicas:{' '}
|
||||||
|
</span>
|
||||||
<span className="font-medium">
|
<span className="font-medium">
|
||||||
{wizard.datosBasicos.horasAcademicas ?? '—'}
|
{wizard.datosBasicos.horasAcademicas ?? '—'}
|
||||||
</span>
|
</span>
|
||||||
@@ -160,7 +253,9 @@ export function PasoResumenCard({ wizard }: { wizard: NewSubjectWizardState }) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div className="font-medium">Repositorios de referencia</div>
|
<div className="font-medium">
|
||||||
|
Repositorios de referencia
|
||||||
|
</div>
|
||||||
{repositoriosRef.length ? (
|
{repositoriosRef.length ? (
|
||||||
<ul className="text-muted-foreground list-disc pl-5 text-xs">
|
<ul className="text-muted-foreground list-disc pl-5 text-xs">
|
||||||
{repositoriosRef.map((id) => (
|
{repositoriosRef.map((id) => (
|
||||||
@@ -178,7 +273,9 @@ export function PasoResumenCard({ wizard }: { wizard: NewSubjectWizardState }) {
|
|||||||
<ul className="text-muted-foreground list-disc pl-5 text-xs">
|
<ul className="text-muted-foreground list-disc pl-5 text-xs">
|
||||||
{adjuntos.map((f) => (
|
{adjuntos.map((f) => (
|
||||||
<li key={f.id}>
|
<li key={f.id}>
|
||||||
<span className="text-foreground">{f.file.name}</span>{' '}
|
<span className="text-foreground">
|
||||||
|
{f.file.name}
|
||||||
|
</span>{' '}
|
||||||
<span>· {formatFileSize(f.file.size)}</span>
|
<span>· {formatFileSize(f.file.size)}</span>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
@@ -189,6 +286,8 @@ export function PasoResumenCard({ wizard }: { wizard: NewSubjectWizardState }) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -48,15 +48,17 @@ export function useNuevaAsignaturaWizard(planId: string) {
|
|||||||
wizard.tipoOrigen === 'CLONADO_TRADICIONAL'
|
wizard.tipoOrigen === 'CLONADO_TRADICIONAL'
|
||||||
|
|
||||||
const canContinueDesdeBasicos =
|
const canContinueDesdeBasicos =
|
||||||
!!wizard.datosBasicos.nombre &&
|
(!!wizard.datosBasicos.nombre &&
|
||||||
wizard.datosBasicos.tipo !== null &&
|
wizard.datosBasicos.tipo !== null &&
|
||||||
wizard.datosBasicos.creditos !== null &&
|
wizard.datosBasicos.creditos !== null &&
|
||||||
wizard.datosBasicos.creditos > 0 &&
|
wizard.datosBasicos.creditos > 0 &&
|
||||||
!!wizard.datosBasicos.estructuraId
|
!!wizard.datosBasicos.estructuraId) ||
|
||||||
|
(wizard.tipoOrigen === 'IA_MULTIPLE' &&
|
||||||
|
wizard.sugerencias.filter((s) => s.selected).length > 0)
|
||||||
|
|
||||||
const canContinueDesdeDetalles = (() => {
|
const canContinueDesdeDetalles = (() => {
|
||||||
if (wizard.tipoOrigen === 'MANUAL') return true
|
if (wizard.tipoOrigen === 'MANUAL') return true
|
||||||
if (wizard.tipoOrigen === 'IA') {
|
if (wizard.tipoOrigen === 'IA_SIMPLE') {
|
||||||
return !!wizard.iaConfig?.descripcionEnfoqueAcademico
|
return !!wizard.iaConfig?.descripcionEnfoqueAcademico
|
||||||
}
|
}
|
||||||
if (wizard.tipoOrigen === 'CLONADO_INTERNO') {
|
if (wizard.tipoOrigen === 'CLONADO_INTERNO') {
|
||||||
|
|||||||
@@ -7,11 +7,6 @@ export type Json =
|
|||||||
| Array<Json>
|
| Array<Json>
|
||||||
|
|
||||||
export type Database = {
|
export type Database = {
|
||||||
// Allows to automatically instantiate createClient with right options
|
|
||||||
// instead of createClient<Database, { PostgrestVersion: 'XX' }>(URL, KEY)
|
|
||||||
__InternalSupabase: {
|
|
||||||
PostgrestVersion: '12.2.3 (519615d)'
|
|
||||||
}
|
|
||||||
graphql_public: {
|
graphql_public: {
|
||||||
Tables: {
|
Tables: {
|
||||||
[_ in never]: never
|
[_ in never]: never
|
||||||
@@ -98,6 +93,7 @@ export type Database = {
|
|||||||
creado_por: string | null
|
creado_por: string | null
|
||||||
creditos: number
|
creditos: number
|
||||||
datos: Json
|
datos: Json
|
||||||
|
estado: Database['public']['Enums']['estado_asignatura']
|
||||||
estructura_id: string | null
|
estructura_id: string | null
|
||||||
horas_academicas: number | null
|
horas_academicas: number | null
|
||||||
horas_independientes: number | null
|
horas_independientes: number | null
|
||||||
@@ -122,6 +118,7 @@ export type Database = {
|
|||||||
creado_por?: string | null
|
creado_por?: string | null
|
||||||
creditos: number
|
creditos: number
|
||||||
datos?: Json
|
datos?: Json
|
||||||
|
estado?: Database['public']['Enums']['estado_asignatura']
|
||||||
estructura_id?: string | null
|
estructura_id?: string | null
|
||||||
horas_academicas?: number | null
|
horas_academicas?: number | null
|
||||||
horas_independientes?: number | null
|
horas_independientes?: number | null
|
||||||
@@ -146,6 +143,7 @@ export type Database = {
|
|||||||
creado_por?: string | null
|
creado_por?: string | null
|
||||||
creditos?: number
|
creditos?: number
|
||||||
datos?: Json
|
datos?: Json
|
||||||
|
estado?: Database['public']['Enums']['estado_asignatura']
|
||||||
estructura_id?: string | null
|
estructura_id?: string | null
|
||||||
horas_academicas?: number | null
|
horas_academicas?: number | null
|
||||||
horas_independientes?: number | null
|
horas_independientes?: number | null
|
||||||
@@ -1089,6 +1087,7 @@ export type Database = {
|
|||||||
unaccent_immutable: { Args: { '': string }; Returns: string }
|
unaccent_immutable: { Args: { '': string }; Returns: string }
|
||||||
}
|
}
|
||||||
Enums: {
|
Enums: {
|
||||||
|
estado_asignatura: 'borrador' | 'revisada' | 'aprobada' | 'generando'
|
||||||
estado_tarea_revision: 'PENDIENTE' | 'COMPLETADA' | 'OMITIDA'
|
estado_tarea_revision: 'PENDIENTE' | 'COMPLETADA' | 'OMITIDA'
|
||||||
fuente_cambio: 'HUMANO' | 'IA'
|
fuente_cambio: 'HUMANO' | 'IA'
|
||||||
nivel_plan_estudio:
|
nivel_plan_estudio:
|
||||||
@@ -1261,6 +1260,7 @@ export const Constants = {
|
|||||||
},
|
},
|
||||||
public: {
|
public: {
|
||||||
Enums: {
|
Enums: {
|
||||||
|
estado_asignatura: ['borrador', 'revisada', 'aprobada', 'generando'],
|
||||||
estado_tarea_revision: ['PENDIENTE', 'COMPLETADA', 'OMITIDA'],
|
estado_tarea_revision: ['PENDIENTE', 'COMPLETADA', 'OMITIDA'],
|
||||||
fuente_cambio: ['HUMANO', 'IA'],
|
fuente_cambio: ['HUMANO', 'IA'],
|
||||||
nivel_plan_estudio: [
|
nivel_plan_estudio: [
|
||||||
|
|||||||
Reference in New Issue
Block a user