Ajustar comportamiento de seleccion de tabla/pc a datos del backend

master
Araozu 2022-10-14 19:12:10 -05:00
parent 5c2ae95031
commit 6429d25ef7
12 changed files with 46 additions and 249 deletions

View File

@ -15,7 +15,7 @@
"jest": "^29.1.2", "jest": "^29.1.2",
"normalize.css": "^8.0.1", "normalize.css": "^8.0.1",
"solid-app-router": "^0.3.2", "solid-app-router": "^0.3.2",
"solid-js": "^1.3.12", "solid-js": "1.5.7",
"ts-jest": "^29.0.3", "ts-jest": "^29.0.3",
"typescript": "^4.6.2", "typescript": "^4.6.2",
"vite": "^2.8.6", "vite": "^2.8.6",

View File

@ -13,7 +13,7 @@ specifiers:
jest: ^29.1.2 jest: ^29.1.2
normalize.css: ^8.0.1 normalize.css: ^8.0.1
solid-app-router: ^0.3.2 solid-app-router: ^0.3.2
solid-js: ^1.3.12 solid-js: 1.5.7
swiper: ^8.4.4 swiper: ^8.4.4
ts-jest: ^29.0.3 ts-jest: ^29.0.3
typescript: ^4.6.2 typescript: ^4.6.2
@ -37,8 +37,8 @@ devDependencies:
eslint-plugin-react: 7.23.1_eslint@7.22.0 eslint-plugin-react: 7.23.1_eslint@7.22.0
jest: 29.1.2_@types+node@14.14.20 jest: 29.1.2_@types+node@14.14.20
normalize.css: 8.0.1 normalize.css: 8.0.1
solid-app-router: 0.3.2_solid-js@1.3.13 solid-app-router: 0.3.2_solid-js@1.5.7
solid-js: 1.3.13 solid-js: 1.5.7
ts-jest: 29.0.3_u3tawnhld4dxccrvbviil55jbq ts-jest: 29.0.3_u3tawnhld4dxccrvbviil55jbq
typescript: 4.6.3 typescript: 4.6.3
vite: 2.9.0 vite: 2.9.0
@ -1409,6 +1409,10 @@ packages:
isobject: 3.0.1 isobject: 3.0.1
dev: true dev: true
/csstype/3.1.1:
resolution: {integrity: sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==}
dev: true
/debug/4.3.1: /debug/4.3.1:
resolution: {integrity: sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==} resolution: {integrity: sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==}
engines: {node: '>=6.0'} engines: {node: '>=6.0'}
@ -3392,19 +3396,21 @@ packages:
is-fullwidth-code-point: 3.0.0 is-fullwidth-code-point: 3.0.0
dev: true dev: true
/solid-app-router/0.3.2_solid-js@1.3.13: /solid-app-router/0.3.2_solid-js@1.5.7:
resolution: {integrity: sha512-zh6Ui87xy23JUxrH0z1xAROJPpiuxa3JRx9jP2qqjr07q2EKQOjn9BrmTFvQd/azQWzLjSMU+hN2fy6kLh5Bdw==} resolution: {integrity: sha512-zh6Ui87xy23JUxrH0z1xAROJPpiuxa3JRx9jP2qqjr07q2EKQOjn9BrmTFvQd/azQWzLjSMU+hN2fy6kLh5Bdw==}
peerDependencies: peerDependencies:
solid-js: ^1.3.5 solid-js: ^1.3.5
dependencies: dependencies:
solid-js: 1.3.13 solid-js: 1.5.7
dev: true dev: true
/solid-js/1.3.13: /solid-js/1.5.7:
resolution: {integrity: sha512-1EBEIW9u2yqT5QNjFdvz/tMAoKsDdaRA2Jbgykd2Dt13Ia0D4mV+BFvPkOaseSyu7DsMKS23+ZZofV8BVKmpuQ==} resolution: {integrity: sha512-L1UuyMuZZARAwzXo5NZDhE6yxc14aqNbVOUoGzvlcxRZo1Cm4ExhPV0diEfwDyiKG/igqNNLkNurHkXiI5sVEg==}
dependencies:
csstype: 3.1.1
dev: true dev: true
/solid-refresh/0.4.0_solid-js@1.3.13: /solid-refresh/0.4.0_solid-js@1.5.7:
resolution: {integrity: sha512-5XCUz845n/sHPzKK2i2G2EeV61tAmzv6SqzqhXcPaYhrgzVy7nKTQaBpKK8InKrriq9Z2JFF/mguIU00t/73xw==} resolution: {integrity: sha512-5XCUz845n/sHPzKK2i2G2EeV61tAmzv6SqzqhXcPaYhrgzVy7nKTQaBpKK8InKrriq9Z2JFF/mguIU00t/73xw==}
peerDependencies: peerDependencies:
solid-js: ^1.3.0 solid-js: ^1.3.0
@ -3412,7 +3418,7 @@ packages:
'@babel/generator': 7.17.7 '@babel/generator': 7.17.7
'@babel/helper-module-imports': 7.16.7 '@babel/helper-module-imports': 7.16.7
'@babel/types': 7.17.0 '@babel/types': 7.17.0
solid-js: 1.3.13 solid-js: 1.5.7
dev: true dev: true
/source-map-js/1.0.2: /source-map-js/1.0.2:
@ -3746,8 +3752,8 @@ packages:
'@babel/preset-typescript': 7.16.7_@babel+core@7.17.8 '@babel/preset-typescript': 7.16.7_@babel+core@7.17.8
babel-preset-solid: 1.3.13_@babel+core@7.17.8 babel-preset-solid: 1.3.13_@babel+core@7.17.8
merge-anything: 5.0.2 merge-anything: 5.0.2
solid-js: 1.3.13 solid-js: 1.5.7
solid-refresh: 0.4.0_solid-js@1.3.13 solid-refresh: 0.4.0_solid-js@1.5.7
vite: 2.9.0 vite: 2.9.0
transitivePeerDependencies: transitivePeerDependencies:
- less - less

View File

@ -159,7 +159,7 @@ export const getHorariosMock: GetHorariosFn = async(_) => {
id_horario: 4, id_horario: 4,
id_laboratorio: 2, id_laboratorio: 2,
hora_inicio: "0850", hora_inicio: "0850",
hora_fin: "0940", hora_fin: "1040",
dia: "Jueves", dia: "Jueves",
}, },
], ],
@ -174,7 +174,7 @@ export const getHorariosMock: GetHorariosFn = async(_) => {
id_horario: 5, id_horario: 5,
id_laboratorio: 3, id_laboratorio: 3,
hora_inicio: "1740", hora_inicio: "1740",
hora_fin: "1830", hora_fin: "1920",
dia: "Martes", dia: "Martes",
}, },
], ],

View File

@ -37,6 +37,7 @@ function listaCursosADatos(cursosEntrada: ListaCursosCompleto): Cursos {
const gruposLab: {[grupo: string]: DatosGrupo} = {}; const gruposLab: {[grupo: string]: DatosGrupo} = {};
for (const lab of curso.laboratorios) { for (const lab of curso.laboratorios) {
gruposLab[lab.grupo] = { gruposLab[lab.grupo] = {
id_laboratorio: lab.id_laboratorio,
Docente: lab.docente, Docente: lab.docente,
Horas: infoDiaAListaHoras(lab.horario), Horas: infoDiaAListaHoras(lab.horario),
seleccionado: false, seleccionado: false,

View File

@ -1,7 +1,6 @@
import YAML from "yaml"; import YAML from "yaml";
import { css, StyleSheet } from "aphrodite"; import { css, StyleSheet } from "aphrodite";
import { MiHorario } from "./ContenedorHorarios/MiHorario"; import { MiHorario } from "./ContenedorHorarios/MiHorario";
import { Horarios } from "./ContenedorHorarios/Horarios";
import { import {
Anios, Anios,
Cursos, Cursos,
@ -20,18 +19,17 @@ const {
} = useListaCursos(); } = useListaCursos();
export function ContenedorHorarios(props: {datos: Cursos}) { export function ContenedorHorarios(props: {datos: Cursos}) {
const [datos, setDatos] = createSignal<Cursos>({});
createEffect(async() => { createEffect(async() => {
const datos = props.datos; const d2 = props.datos;
batch(() => { batch(() => {
setDatos(datos); Object.entries(d2).forEach(([_, curso]) => agregarCursoUsuario(curso));
}); });
}); });
return ( return (
<MiHorario <MiHorario
cursos={datos()} cursos={props.datos}
fnAgregarCurso={agregarCursoUsuario} fnAgregarCurso={agregarCursoUsuario}
setCursosUsuarios={setCursosUsuarios} setCursosUsuarios={setCursosUsuarios}
/> />

View File

@ -4,6 +4,7 @@ import { produce, SetStoreFunction } from "solid-js/store";
import { StyleSheet, css } from "aphrodite"; import { StyleSheet, css } from "aphrodite";
import { estilosGlobales } from "../../../../Estilos"; import { estilosGlobales } from "../../../../Estilos";
import { TablaObserver } from "./TablaObserver"; import { TablaObserver } from "./TablaObserver";
import { setGruposSeleccionados } from "../../../../Store";
const e = StyleSheet.create({ const e = StyleSheet.create({
inline: { inline: {
@ -47,7 +48,6 @@ interface Props {
dataAnio: Cursos, dataAnio: Cursos,
anioActual: () => string, anioActual: () => string,
fnAgregarCurso: (c: Curso) => void, fnAgregarCurso: (c: Curso) => void,
listaCursosUsuario: ListaCursosUsuario,
esCursoMiHorario: boolean, esCursoMiHorario: boolean,
setCursosUsuarios: SetStoreFunction<ListaCursosUsuario>, setCursosUsuarios: SetStoreFunction<ListaCursosUsuario>,
tablaObserver: TablaObserver, tablaObserver: TablaObserver,
@ -93,17 +93,7 @@ const agruparProfesores = (
profesores[nombreProfesor].push([ profesores[nombreProfesor].push([
grupo, grupo,
() => { () => {
setCursosUsuarios("cursos", Number(indiceCurso), "Teoria", produce<{ [p: string]: DatosGrupo }>((x) => { setGruposSeleccionados(datosGrupo.id_laboratorio, (x) => !x);
const grupoActualSeleccionado = x[grupo].seleccionado;
if (grupoActualSeleccionado) {
x[grupo].seleccionado = false;
} else {
for (const xKey in x) {
x[xKey].seleccionado = xKey === grupo;
}
}
}));
}, },
]); ]);
} }
@ -119,26 +109,11 @@ function CursoE(
) { ) {
const idCurso = `${props.version}_${anio()}_${datosCurso.abreviado}`; const idCurso = `${props.version}_${anio()}_${datosCurso.abreviado}`;
const cursoAgregadoMemo = createMemo(
() => props.listaCursosUsuario.cursos.find((x) => x.nombre === datosCurso.nombre && !x.oculto) !== undefined,
undefined,
{
equals:
(x, y) => x === y,
},
);
const tituloMemo = createMemo(() => (cursoAgregadoMemo()
? "Remover de mi horario"
: "Agregar a mi horario"));
const claseMemo = createMemo(() => { const claseMemo = createMemo(() => {
if (props.esCursoMiHorario && datosCurso.oculto) { if (props.esCursoMiHorario && datosCurso.oculto) {
return claseCursoOculto; return claseCursoOculto;
} }
return cursoAgregadoMemo() return claseCursoNoAgregado;
? claseCursoAgregado
: claseCursoNoAgregado;
}); });
const profesoresTeoria = createMemo(() => agruparProfesores( const profesoresTeoria = createMemo(() => agruparProfesores(
@ -197,12 +172,6 @@ function CursoE(
</tr> </tr>
</tbody> </tbody>
</table> </table>
<button
className={css(e.botonTexto, estilosGlobales.contenedorCursor, estilosGlobales.contenedorCursorSoft)}
onClick={() => props.fnAgregarCurso(datosCurso)}
>
{tituloMemo}
</button>
</div> </div>
); );
} }

View File

@ -1,139 +0,0 @@
import { Curso, Cursos, DatosHorario, ListaCursosUsuario } from "../../../../types/DatosHorario";
import { batch, createMemo, createSignal, For, Match, Switch, untrack } from "solid-js";
import {SetStoreFunction} from "solid-js/store";
import { css } from "aphrodite";
import { estilosGlobales } from "../../../../Estilos";
import { Tabla } from "./Tabla";
import { CursosElem } from "./CursosElem";
import { EstadoLayout } from "../ContenedorHorarios";
import { BotonMaxMin } from "./BotonMaxMin";
import { useListaCursos } from "./useListaCursos";
import { TablaObserver } from "./TablaObserver";
interface HorariosProps {
data: DatosHorario,
estadoLayout: EstadoLayout,
setEstadoLayout: (v: EstadoLayout) => EstadoLayout,
fnAgregarCurso: (c: Curso) => void,
listaCursosUsuario: ListaCursosUsuario,
setCursosUsuarios: SetStoreFunction<ListaCursosUsuario>
}
const {
setListaCursos,
agregarCursoALista,
eliminarCursosDeLista,
} = useListaCursos();
export function Horarios(props: HorariosProps) {
const [anioActual, setAnioActual] = createSignal("1er año");
const tablaObserver = new TablaObserver();
const elAnios = (
<For each={Object.entries(props.data.años)}>
{([nombre]) => {
const clases = createMemo(() => {
const vAnio = anioActual();
return css(
estilosGlobales.contenedor,
estilosGlobales.inlineBlock,
estilosGlobales.contenedorCursor,
estilosGlobales.contenedorCursorSoft,
nombre === vAnio && estilosGlobales.contenedorCursorActivo,
);
});
return (
<button className={clases()} title={`Cambiar a ${nombre}`} onClick={() => setAnioActual(nombre)}>
{nombre}
</button>
);
}}
</For>
);
const dataTabla = createMemo(() => {
const anio = anioActual();
const obj: Cursos = {};
untrack(() => {
const cursos = props.data.años[anio];
batch(() => {
eliminarCursosDeLista();
let i = 0;
for (const [, curso] of Object.entries(cursos)) {
// El curso devuelto por esta fun. es reactivo
obj[i] = agregarCursoALista(curso);
i += 1;
}
});
});
return obj;
});
const fnMaximizar = () => props.setEstadoLayout("MaxHorarios");
const fnMinimizar = () => props.setEstadoLayout("Normal");
const estadoActualLayout = () => props.estadoLayout;
return (
<div>
<Switch>
<Match when={props.estadoLayout === "Normal" || props.estadoLayout === "MaxHorarios"}>
<div>
<div className={css(
estilosGlobales.inlineBlock,
estilosGlobales.contenedor,
)}
>
Horarios disponibles
</div>
</div>
{elAnios}
|
<BotonMaxMin
fnMaximizar={fnMaximizar}
fnMinimizar={fnMinimizar}
estadoActualLayout={estadoActualLayout}
estadoLayoutMax={"MaxHorarios"}
/>
<br />
<div className={css(estilosGlobales.contenedor)}>
<Tabla
data={dataTabla()}
version={props.data.version}
anio={anioActual()}
setCursosUsuarios={setListaCursos}
tablaObserver={tablaObserver}
/>
</div>
<div>
<CursosElem
version={props.data.version}
dataAnio={dataTabla()}
anioActual={anioActual}
fnAgregarCurso={props.fnAgregarCurso}
listaCursosUsuario={props.listaCursosUsuario}
esCursoMiHorario={false}
setCursosUsuarios={setListaCursos}
tablaObserver={tablaObserver}
/>
</div>
</Match>
<Match when={props.estadoLayout === "MaxPersonal"}>
{/*
<BotonMaxMin
fnMaximizar={fnMaximizar}
fnMinimizar={fnMinimizar}
estadoActualLayout={estadoActualLayout}
estadoLayoutMax={"MaxHorarios"}
/>
*/}
<div />
</Match>
</Switch>
</div>
);
}

View File

@ -11,7 +11,7 @@ import { CursosElem } from "./CursosElem";
import { TablaObserver } from "./TablaObserver"; import { TablaObserver } from "./TablaObserver";
interface MiHorarioProps { interface MiHorarioProps {
cursosUsuario: ListaCursosUsuario, cursos: Cursos,
fnAgregarCurso: (c: Curso) => void, fnAgregarCurso: (c: Curso) => void,
setCursosUsuarios: SetStoreFunction<ListaCursosUsuario> setCursosUsuarios: SetStoreFunction<ListaCursosUsuario>
} }
@ -34,7 +34,7 @@ export function MiHorario(props: MiHorarioProps) {
const datosMiHorario = createMemo(() => { const datosMiHorario = createMemo(() => {
const obj: Cursos = {}; const obj: Cursos = {};
props.cursosUsuario.cursos.forEach((x, i) => { Object.entries(props.cursos).forEach(([_, x], i) => {
obj[i] = x; obj[i] = x;
}); });
return obj; return obj;
@ -69,7 +69,6 @@ export function MiHorario(props: MiHorarioProps) {
anioActual={() => "Mi horario"} anioActual={() => "Mi horario"}
dataAnio={datosMiHorario()} dataAnio={datosMiHorario()}
fnAgregarCurso={props.fnAgregarCurso} fnAgregarCurso={props.fnAgregarCurso}
listaCursosUsuario={props.cursosUsuario}
esCursoMiHorario esCursoMiHorario
setCursosUsuarios={props.setCursosUsuarios} setCursosUsuarios={props.setCursosUsuarios}
tablaObserver={tablaObserver} tablaObserver={tablaObserver}

View File

@ -3,7 +3,7 @@ import { batch, createMemo, For } from "solid-js";
import { produce, SetStoreFunction } from "solid-js/store"; import { produce, SetStoreFunction } from "solid-js/store";
import {estilosGlobales} from "../../../../Estilos"; import {estilosGlobales} from "../../../../Estilos";
import { Cursos, ListaCursosUsuario, DataProcesada, DatosGrupo } from "../../../../types/DatosHorario"; import { Cursos, ListaCursosUsuario, DataProcesada, DatosGrupo } from "../../../../types/DatosHorario";
import { Dia, dias, horas } from "../../../../Store"; import { Dia, dias, gruposSeleccionados, horas, setGruposSeleccionados } from "../../../../Store";
import { FilaTabla } from "./Tabla/FilaTabla"; import { FilaTabla } from "./Tabla/FilaTabla";
import { TablaObserver } from "./TablaObserver"; import { TablaObserver } from "./TablaObserver";
@ -117,17 +117,7 @@ const procesarAnio = (data: Cursos, anio: string, version: number, setCursosUsua
esLab: false, esLab: false,
datosGrupo: grupo, datosGrupo: grupo,
fnSeleccionar: () => { fnSeleccionar: () => {
setCursosUsuarios("cursos", Number(indiceCurso), "Teoria", produce<{ [p: string]: DatosGrupo }>((x) => {
const grupoActualSeleccionado = x[grupoStr].seleccionado;
if (grupoActualSeleccionado) {
x[grupoStr].seleccionado = false;
} else {
for (const xKey in x) {
x[xKey].seleccionado = xKey === grupoStr;
}
}
}));
}, },
}); });
} }
@ -157,23 +147,7 @@ const procesarAnio = (data: Cursos, anio: string, version: number, setCursosUsua
esLab: true, esLab: true,
datosGrupo: grupo, datosGrupo: grupo,
fnSeleccionar: () => { fnSeleccionar: () => {
setCursosUsuarios( setGruposSeleccionados(grupo.id_laboratorio, (x) => !x);
"cursos",
Number(indiceCurso),
"Laboratorio",
/// @ts-ignore
produce<{ [p: string]: DatosGrupo }>((x) => {
const grupoActualSeleccionado = x[grupoStr].seleccionado;
if (grupoActualSeleccionado) {
x[grupoStr].seleccionado = false;
} else {
for (const xKey in x) {
x[xKey].seleccionado = xKey === grupoStr;
}
}
}),
);
}, },
}); });
} }

View File

@ -1,6 +1,7 @@
import { createMemo, createEffect, untrack } from "solid-js"; import { createMemo, createEffect, untrack } from "solid-js";
import {createStore, SetStoreFunction, Store, produce} from "solid-js/store"; import {createStore, SetStoreFunction, Store, produce} from "solid-js/store";
import { DatosGrupo } from "../../../../types/DatosHorario"; import { DatosGrupo } from "../../../../types/DatosHorario";
import { gruposSeleccionados } from "../../../../Store";
const createMemoDefault = <T>(f: () => T) => createMemo<T>( const createMemoDefault = <T>(f: () => T) => createMemo<T>(
f, f,
@ -73,6 +74,7 @@ export class TablaObserver {
/** /**
* Crea un memo que indica el estado de la celda * Crea un memo que indica el estado de la celda
* @param id_laboratorio Id del laboratorio
* @param anio El año * @param anio El año
* @param curso Curso abreviado * @param curso Curso abreviado
* @param esLab Si es laboratorio * @param esLab Si es laboratorio
@ -80,6 +82,7 @@ export class TablaObserver {
* @param datosGrupo Contiene `seleccionado`, se usa ese valor reactivo * @param datosGrupo Contiene `seleccionado`, se usa ese valor reactivo
*/ */
private registrar( private registrar(
id_laboratorio: number,
anio: string, anio: string,
curso: string, curso: string,
esLab: boolean, esLab: boolean,
@ -131,15 +134,7 @@ export class TablaObserver {
} }
}); });
const seleccionadoMemo = createMemoDefault<EstadoSeleccionado>(() => { const seleccionadoMemo = createMemoDefault<EstadoSeleccionado>(() => ((gruposSeleccionados[id_laboratorio]) ? "Seleccionado" : "Normal"));
const gruposSeleccionados = this.seleccionado[anio][curso][esLab ? "Laboratorio" : "Teoria"];
if (gruposSeleccionados.length > 0) {
return gruposSeleccionados.find((x) => x === grupo) ? "Seleccionado" : "Oculto";
} else {
return "Normal";
}
});
return createMemoDefault((): EstadoCelda => { return createMemoDefault((): EstadoCelda => {
const resaltado = resaltadoMemo(); const resaltado = resaltadoMemo();
@ -176,7 +171,7 @@ export class TablaObserver {
} }
const [, anio, curso, lab, grupo] = id.split("_"); const [, anio, curso, lab, grupo] = id.split("_");
const memo = this.registrar(anio, curso, lab === "L", grupo, datosGrupo); const memo = this.registrar(datosGrupo.id_laboratorio, anio, curso, lab === "L", grupo, datosGrupo);
this.memos[id] = memo; this.memos[id] = memo;
return memo; return memo;
} }

View File

@ -9,27 +9,21 @@ interface ReturnType {
} }
export const useListaCursos = (): ReturnType => { export const useListaCursos = (): ReturnType => {
const [listaCursos, setListaCursos] = createStore<ListaCursosUsuario>({ const [listaCursos, setListaCursos] = createStore<ListaCursosUsuario>({});
sigIndice: 0,
cursos: [],
});
const agregarCursoALista = (curso: Curso): Curso => { const agregarCursoALista = (curso: Curso): Curso => {
// Si el horario ya se habia agregado, ocultarlo // Si el horario ya se habia agregado, ocultarlo
const cursoActualIndex = listaCursos.cursos.findIndex((x) => x.nombre === curso.nombre); if (listaCursos[curso.nombre]) {
if (cursoActualIndex !== -1) { setListaCursos(curso.nombre, "oculto", (x) => !x);
setListaCursos("cursos", cursoActualIndex, "oculto", (x) => !x); return listaCursos[curso.nombre];
return listaCursos.cursos[cursoActualIndex];
} else { } else {
setListaCursos("cursos", listaCursos.sigIndice, curso); setListaCursos(curso.nombre, curso);
setListaCursos("sigIndice", (x) => x + 1); return listaCursos[curso.nombre];
return listaCursos.cursos[listaCursos.sigIndice - 1];
} }
}; };
const eliminarCursosDeLista = () => { const eliminarCursosDeLista = () => {
setListaCursos("cursos", []); setListaCursos({});
setListaCursos("sigIndice", 0);
}; };
return { return {

View File

@ -27,6 +27,7 @@ export interface DatosHorarioRaw {
} }
export interface DatosGrupo { export interface DatosGrupo {
id_laboratorio: number,
Docente: string, Docente: string,
Horas: string[] Horas: string[]
seleccionado: boolean seleccionado: boolean
@ -44,9 +45,8 @@ export interface Curso {
} }
} }
export interface ListaCursosUsuario { export type ListaCursosUsuario = {
sigIndice: number, [key: string]: Curso
cursos: Curso[]
} }
export interface Cursos { export interface Cursos {