Revert to 76f723a097
This commit is contained in:
parent
0f26ae732d
commit
3fd0baad96
217
API.md
217
API.md
@ -1,217 +0,0 @@
|
||||
# Inicio de sesion
|
||||
|
||||
```ts
|
||||
// HTTP POST
|
||||
// Url: /login
|
||||
|
||||
// Frontend envia el correo del usuario
|
||||
{
|
||||
correo_usuario: string
|
||||
}
|
||||
|
||||
// Backend responde con una lista de matriculas
|
||||
// Si el correo es valido y el usuario tiene alguna matricula
|
||||
{
|
||||
matriculas: Array<number> // Un array de id_laboratorio
|
||||
}
|
||||
// Si el correo es valido pero el usuario no tiene matriculas
|
||||
{
|
||||
matriculas: [] // Un array vacio
|
||||
}
|
||||
// Si el correo es invalido: Se envia HTTP 401
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Lista cursos
|
||||
|
||||
```ts
|
||||
// HTTP GET
|
||||
// Url: /cursos
|
||||
|
||||
// El frontend pide una lista de la informacion de todos los cursos
|
||||
|
||||
// Backend responde con una lista de todos los cursos
|
||||
[
|
||||
{
|
||||
id_curso: number,
|
||||
id_datos_carrera: any, // Opcional
|
||||
nombre_curso: string,
|
||||
curso_anio: number | string, // Numero o string, dependiendo de como este en DB
|
||||
abreviado: string,
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Carga de horarios
|
||||
|
||||
```ts
|
||||
// HTTP POST
|
||||
// Url: /horarios
|
||||
|
||||
// El frontend envia una lista de cursos, de los cuales recuperar sus datos
|
||||
{
|
||||
cursos: Array<number> // Un array de id_curso
|
||||
}
|
||||
|
||||
// Backend responde con los cursos especificados y sus horarios
|
||||
[
|
||||
// Cada objeto dentro del array sera un Curso
|
||||
{
|
||||
id_curso: number,
|
||||
id_datos_carrera: any, // Opcional
|
||||
nombre_curso: string,
|
||||
curso_anio: number | string,
|
||||
abreviado: string,
|
||||
// Un array de objetos, estos objetos son de la entidad Laboratorio
|
||||
laboratorios: [
|
||||
{
|
||||
id_laboratorio: number,
|
||||
id_curso: number,
|
||||
grupo: string,
|
||||
docente: string,
|
||||
// Array de objetos de la entidad Horario
|
||||
horario: [
|
||||
{
|
||||
id_horario: number,
|
||||
id_laboratorio: number,
|
||||
dia: string,
|
||||
hora_inicio: string,
|
||||
hora_fin: string,
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
# Matricula
|
||||
|
||||
```ts
|
||||
// HTTP POST
|
||||
// Url: /matricula
|
||||
|
||||
// Frontend envia una lista de horarios en los cuales se matricula y el usuario
|
||||
{
|
||||
correo_usuario: string, // Correo del usuario
|
||||
horarios: Array<number> // Array de id_laboratorio
|
||||
}
|
||||
|
||||
// Backend devuelve HTTP 200 si exitoso, HTTP 400 si hay error (no hay cupos)
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Recuperacion de matricula
|
||||
|
||||
```ts
|
||||
// HTTP POST
|
||||
// Url: /recuperacion
|
||||
|
||||
// Frontend envia una lista de id_laboratorio
|
||||
{
|
||||
matriculas: Array<number> // Array de id_laboratorio
|
||||
}
|
||||
|
||||
// Backend devuelve:
|
||||
// Por cada id_laboratorio: datos del curso y el laboratorio
|
||||
[
|
||||
// Un objeto asi por cada id_laboratorio
|
||||
{
|
||||
nombre_curso: string, // Este es el campo `nombre_curso` de la entidad Curso
|
||||
grupo: string, // Campo `grupo` de la entidad Laboratorio
|
||||
docente: string, // Campo `docente` de la entidad Laboratorio
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Creacion de datos
|
||||
|
||||
Endpoints para insertar datos a la db
|
||||
|
||||
```ts
|
||||
// POST /crear-carrera
|
||||
|
||||
// Front
|
||||
{
|
||||
nombre_carrera: string
|
||||
}
|
||||
|
||||
// Backend
|
||||
{
|
||||
id_carrera: number // id de la carrera creada
|
||||
}
|
||||
```
|
||||
|
||||
```ts
|
||||
// POST /crear-curso
|
||||
|
||||
// Front
|
||||
{
|
||||
id_datos_carrera: number,
|
||||
nombre_curso: string,
|
||||
curso_anio: string, // Ejm: "1er", "2do", etc
|
||||
abreviado: string
|
||||
}
|
||||
|
||||
// Backend
|
||||
{
|
||||
id_curso: number // id del curso creado
|
||||
}
|
||||
```
|
||||
|
||||
```ts
|
||||
// POST /crear-laboratorio
|
||||
|
||||
// Front
|
||||
{
|
||||
id_curso: number,
|
||||
grupo: string, // Una letra
|
||||
docente: string,
|
||||
max_estudiantes: number
|
||||
}
|
||||
|
||||
// Backend
|
||||
{
|
||||
id_laboratorio: number // id del lab creado
|
||||
}
|
||||
```
|
||||
|
||||
```ts
|
||||
// POST /crear-horario
|
||||
|
||||
// Front
|
||||
{
|
||||
id_laboratorio: number,
|
||||
dia: string, // "Lunes", "Martes", etc
|
||||
hora_inicio: string, // "07:00"
|
||||
hora_fin: string, // "08:50"
|
||||
}
|
||||
|
||||
// Backend
|
||||
{
|
||||
id_horario: number // id del horario creado
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,52 +1,171 @@
|
||||
|
||||
interface Curso {
|
||||
// Nombre completo del curso
|
||||
nombre: string,
|
||||
// Nombre del curso abreviado
|
||||
abreviado: string,
|
||||
// Información de las horas de teoria
|
||||
Teoria: {
|
||||
// grupo es una letra: A, B, C, D
|
||||
[grupo: string]: DatosGrupo,
|
||||
},
|
||||
// Información de las horas de laboratorio
|
||||
Laboratorio?: {
|
||||
// grupo es una letra: A, B, C, D
|
||||
[grupo: string]: DatosGrupo,
|
||||
},
|
||||
}
|
||||
|
||||
interface DatosGrupo {
|
||||
// Nombre del docente de este grupo
|
||||
Docente: string,
|
||||
/*
|
||||
Las horas del curso en el siguiente formato: DD_HHMM
|
||||
DD puede ser Lu, Ma, Mi, Ju, Vi
|
||||
Ejm: Ma0850, Vi1640, Ju1550
|
||||
*/
|
||||
Horas: string[]
|
||||
}
|
||||
|
||||
// Exclusivo de un unico dia
|
||||
import { GrupoDia, TableInput } from "../src/Views/SistemasMovil/Table";
|
||||
import { generarMapaCeldas, MapaCeldas } from "../src/Views/SistemasMovil/mapaCeldas";
|
||||
|
||||
type Input = {
|
||||
offsetVertical: number,
|
||||
horaInicio: number,
|
||||
nroHoras: number,
|
||||
}
|
||||
|
||||
type Output = {
|
||||
horaInicio: number,
|
||||
nroHoras: number,
|
||||
offset: number, // 0, 1, 2
|
||||
fraccion: number, // por cuanto dividir la celda. 1, 2, 3, ...
|
||||
}
|
||||
|
||||
class MapaCeldas {
|
||||
// Almacena referencias a input
|
||||
private mapa: Map<number, Map<number, Input>> = new Map();
|
||||
|
||||
private disponible(nroFila: number, nroColumna: number): boolean {
|
||||
if (!this.mapa.has(nroFila)) return true;
|
||||
|
||||
const fila = this.mapa.get(nroFila)!;
|
||||
|
||||
return fila.has(nroColumna) === false;
|
||||
}
|
||||
|
||||
private obtenerFilaOCrear(nro: number): Map<number, Input> {
|
||||
if (!this.mapa.has(nro)) {
|
||||
const m = new Map<number, Input>();
|
||||
this.mapa.set(nro, m);
|
||||
return m;
|
||||
}
|
||||
|
||||
return this.mapa.get(nro)!;
|
||||
}
|
||||
|
||||
// Devuelve el offset
|
||||
public solicitar(inicio: number, cantidad: number, input: Input): number {
|
||||
const filas = [];
|
||||
for (let i = 0; i < cantidad; i += 1) filas.push(inicio + i);
|
||||
|
||||
for (let offsetActual = 0; offsetActual < 8; offsetActual += 1) {
|
||||
let todasCeldasDisponibles = true;
|
||||
for (const fila of filas) {
|
||||
if (!this.disponible(fila, offsetActual)) {
|
||||
todasCeldasDisponibles = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (todasCeldasDisponibles) {
|
||||
// Crear estas celdas y almacenar
|
||||
filas.forEach((nroFila) => {
|
||||
const fila = this.obtenerFilaOCrear(nroFila);
|
||||
fila.set(offsetActual, input);
|
||||
});
|
||||
|
||||
// Devolver nro de offset
|
||||
return offsetActual;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error("Limite de celdas alcanzado");
|
||||
}
|
||||
|
||||
public generarFraccion(nroFila: number, nroColumna: number, cantidad: number): number {
|
||||
let fraccionActual = 1;
|
||||
for (let i = 0; i < cantidad; i += 1) {
|
||||
const nroFilaActual = nroFila + i;
|
||||
const filaActual = this.mapa.get(nroFilaActual)!;
|
||||
const numeroColumnas = filaActual.size;
|
||||
if (numeroColumnas > fraccionActual) {
|
||||
fraccionActual = numeroColumnas;
|
||||
}
|
||||
}
|
||||
|
||||
return fraccionActual;
|
||||
}
|
||||
}
|
||||
|
||||
function generarMapaCeldas(entrada: Readonly<Array<Input>>): Array<Output> {
|
||||
const mapa = new MapaCeldas();
|
||||
const salida: Array<Output> = [];
|
||||
|
||||
// Obtener los offsets de cada curso
|
||||
for (const input of entrada) {
|
||||
const offset = mapa.solicitar(input.horaInicio, input.nroHoras, input);
|
||||
salida.push({
|
||||
...input,
|
||||
offset,
|
||||
fraccion: -1,
|
||||
});
|
||||
}
|
||||
|
||||
// Generar las fracciones de cada curso
|
||||
for (const output of salida) {
|
||||
output.fraccion = mapa.generarFraccion(output.horaInicio, output.offset, output.nroHoras);
|
||||
}
|
||||
|
||||
return salida;
|
||||
}
|
||||
|
||||
describe("generarMapaCeldas", () => {
|
||||
it("vacio si input es vacio", () => {
|
||||
const input: Array<GrupoDia> = [];
|
||||
const input: Array<Input> = [];
|
||||
const output = generarMapaCeldas(input);
|
||||
expect(output.length).toBe(0);
|
||||
});
|
||||
|
||||
it("funciona con 1 curso", () => {
|
||||
const input: Array<any> = [
|
||||
const input: Array<Input> = [
|
||||
{
|
||||
offsetVertical: 0,
|
||||
horaInicio: 0,
|
||||
nroHoras: 2,
|
||||
},
|
||||
];
|
||||
const output = generarMapaCeldas(input)[0];
|
||||
expect(output).not.toBeUndefined();
|
||||
expect(output.offsetHorizontal).toBe(0);
|
||||
expect(output.offset).toBe(0);
|
||||
expect(output.fraccion).toBe(1);
|
||||
});
|
||||
|
||||
it("funciona con 2 cursos", () => {
|
||||
const input: Array<any> = [
|
||||
const input: Array<Input> = [
|
||||
{
|
||||
offsetVertical: 0,
|
||||
horaInicio: 0,
|
||||
nroHoras: 2,
|
||||
},
|
||||
{
|
||||
offsetVertical: 1,
|
||||
horaInicio: 1,
|
||||
nroHoras: 3,
|
||||
},
|
||||
];
|
||||
|
||||
const output1 = generarMapaCeldas(input)[0];
|
||||
expect(output1.offsetHorizontal).toBe(0);
|
||||
expect(output1.offset).toBe(0);
|
||||
expect(output1.fraccion).toBe(2);
|
||||
|
||||
const output2 = generarMapaCeldas(input)[1];
|
||||
expect(output2.offsetHorizontal).toBe(1);
|
||||
expect(output2.offset).toBe(1);
|
||||
expect(output2.fraccion).toBe(2);
|
||||
});
|
||||
});
|
||||
@ -55,29 +174,29 @@ describe("MapaCeldas", () => {
|
||||
it("crea 1", () => {
|
||||
const mapa = new MapaCeldas();
|
||||
const input = {} as unknown as Input;
|
||||
const offset = mapa.solicitar(0, 2);
|
||||
const offset = mapa.solicitar(0, 2, input);
|
||||
expect(offset).toBe(0);
|
||||
});
|
||||
|
||||
it("crea varios que no se solapan", () => {
|
||||
const mapa = new MapaCeldas();
|
||||
const input = {} as unknown as Input;
|
||||
let offset = mapa.solicitar(0, 2);
|
||||
let offset = mapa.solicitar(0, 2, input);
|
||||
expect(offset).toBe(0);
|
||||
offset = mapa.solicitar(4, 3);
|
||||
offset = mapa.solicitar(4, 3, input);
|
||||
expect(offset).toBe(0);
|
||||
offset = mapa.solicitar(7, 4);
|
||||
offset = mapa.solicitar(7, 4, input);
|
||||
expect(offset).toBe(0);
|
||||
});
|
||||
|
||||
it("crea varios que se solapan", () => {
|
||||
const mapa = new MapaCeldas();
|
||||
const input = {} as unknown as Input;
|
||||
let offset = mapa.solicitar(0, 2);
|
||||
let offset = mapa.solicitar(0, 2, input);
|
||||
expect(offset).toBe(0);
|
||||
offset = mapa.solicitar(0, 2);
|
||||
offset = mapa.solicitar(0, 2, input);
|
||||
expect(offset).toBe(1);
|
||||
offset = mapa.solicitar(0, 4);
|
||||
offset = mapa.solicitar(0, 4, input);
|
||||
expect(offset).toBe(2);
|
||||
});
|
||||
|
||||
@ -93,33 +212,33 @@ describe("MapaCeldas", () => {
|
||||
*/
|
||||
const mapa = new MapaCeldas();
|
||||
const input = {} as unknown as Input;
|
||||
let offset = mapa.solicitar(0, 2);
|
||||
let offset = mapa.solicitar(0, 2, input);
|
||||
expect(offset).toBe(0);
|
||||
offset = mapa.solicitar(1, 3);
|
||||
offset = mapa.solicitar(1, 3, input);
|
||||
expect(offset).toBe(1);
|
||||
offset = mapa.solicitar(1, 4);
|
||||
offset = mapa.solicitar(1, 4, input);
|
||||
expect(offset).toBe(2);
|
||||
offset = mapa.solicitar(2, 3);
|
||||
offset = mapa.solicitar(2, 3, input);
|
||||
expect(offset).toBe(0);
|
||||
offset = mapa.solicitar(4, 2);
|
||||
offset = mapa.solicitar(4, 2, input);
|
||||
expect(offset).toBe(1);
|
||||
});
|
||||
|
||||
it("genera offsets", () => {
|
||||
const mapa = new MapaCeldas();
|
||||
const input = {} as unknown as Input;
|
||||
let offset = mapa.solicitar(0, 2);
|
||||
let offset = mapa.solicitar(0, 2, input);
|
||||
expect(offset).toBe(0);
|
||||
let fraccion = mapa.generarFraccion(0, offset, 2);
|
||||
expect(fraccion).toBe(1);
|
||||
|
||||
offset = mapa.solicitar(1, 3);
|
||||
offset = mapa.solicitar(1, 3, input);
|
||||
fraccion = mapa.generarFraccion(1, offset, 3);
|
||||
expect(fraccion).toBe(2);
|
||||
|
||||
mapa.solicitar(1, 4);
|
||||
mapa.solicitar(2, 3);
|
||||
offset = mapa.solicitar(4, 2);
|
||||
mapa.solicitar(1, 4, input);
|
||||
mapa.solicitar(2, 3, input);
|
||||
offset = mapa.solicitar(4, 2, input);
|
||||
fraccion = mapa.generarFraccion(4, offset, 2);
|
||||
expect(fraccion).toBe(3);
|
||||
});
|
||||
|
@ -1,5 +0,0 @@
|
||||
/** @type {import('ts-jest').JestConfigWithTsJest} */
|
||||
module.exports = {
|
||||
preset: "ts-jest",
|
||||
testEnvironment: "node",
|
||||
};
|
@ -1,6 +0,0 @@
|
||||
[build.environment]
|
||||
NPM_FLAGS="--version"
|
||||
NODE_VERSION="16"
|
||||
[build]
|
||||
command = "npx pnpm install --store=node_modules/.pnpm-store && npx pnpm build"
|
||||
|
@ -15,7 +15,7 @@
|
||||
"jest": "^29.1.2",
|
||||
"normalize.css": "^8.0.1",
|
||||
"solid-app-router": "^0.3.2",
|
||||
"solid-js": "1.5.7",
|
||||
"solid-js": "^1.3.12",
|
||||
"ts-jest": "^29.0.3",
|
||||
"typescript": "^4.6.2",
|
||||
"vite": "^2.8.6",
|
||||
@ -35,7 +35,6 @@
|
||||
"Node 10"
|
||||
],
|
||||
"dependencies": {
|
||||
"swiper": "^8.4.4",
|
||||
"yaml": "^1.10.0"
|
||||
}
|
||||
}
|
||||
|
1180
pnpm-lock.yaml
1180
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Before Width: | Height: | Size: 204 KiB |
@ -1,186 +0,0 @@
|
||||
/*
|
||||
// HTTP POST
|
||||
// Url: /horarios
|
||||
|
||||
// El frontend envia una lista de cursos, de los cuales recuperar sus datos
|
||||
{
|
||||
cursos: Array<number> // Un array de id_curso
|
||||
}
|
||||
|
||||
// Backend responde con los cursos especificados y sus horarios
|
||||
[
|
||||
// Cada objeto dentro del array sera un Curso
|
||||
{
|
||||
id_curso: number,
|
||||
id_datos_carrera: any, // Opcional
|
||||
nombre_curso: string,
|
||||
curso_anio: number | string,
|
||||
abreviado: string,
|
||||
// Un array de objetos, estos objetos son de la entidad Laboratorio
|
||||
laboratorios: [
|
||||
{
|
||||
id_laboratorio: number,
|
||||
id_curso: number,
|
||||
grupo: string,
|
||||
docente: string,
|
||||
// Array de objetos de la entidad Horario
|
||||
horario: [
|
||||
{
|
||||
id_horario: number,
|
||||
dia: string,
|
||||
hora_inicio: string,
|
||||
hora_fin: string,
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
*/
|
||||
|
||||
|
||||
import { SERVER_PATH } from "../Store";
|
||||
|
||||
export type Horario = {
|
||||
id_horario: number,
|
||||
id_laboratorio: number,
|
||||
dia: string,
|
||||
hora_inicio: string,
|
||||
hora_fin: string,
|
||||
}
|
||||
export type Laboratorio = {
|
||||
id_laboratorio: number,
|
||||
id_curso: number,
|
||||
grupo: string,
|
||||
docente: string,
|
||||
// Array de objetos de la entidad Horario
|
||||
horarios: Array<Horario>
|
||||
}
|
||||
export type CursoCompleto = {
|
||||
id_curso: number,
|
||||
nombre_curso: string,
|
||||
curso_anio: number | string,
|
||||
abreviado: string,
|
||||
// Un array de objetos, estos objetos son de la entidad Laboratorio
|
||||
laboratorios: Array<Laboratorio>
|
||||
}
|
||||
|
||||
type InputData = {
|
||||
cursos: Array<number>
|
||||
}
|
||||
export type ListaCursosCompleto = Array<CursoCompleto>
|
||||
|
||||
type GetHorariosFn = (_: InputData) => Promise<ListaCursosCompleto>
|
||||
|
||||
export const getHorarios: GetHorariosFn = async(data) => {
|
||||
const response = await fetch(`${SERVER_PATH}/horarios`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
return await response.json();
|
||||
};
|
||||
|
||||
export const getHorariosMock: GetHorariosFn = async(_) => {
|
||||
const c1: CursoCompleto = {
|
||||
id_curso: 0,
|
||||
nombre_curso: "Gestion de Sistemas y Tecnologias de Informacion",
|
||||
curso_anio: "5to",
|
||||
abreviado: "GSTI",
|
||||
laboratorios: [
|
||||
{
|
||||
id_laboratorio: 0,
|
||||
id_curso: 0,
|
||||
grupo: "A",
|
||||
docente: "Luis Rocha",
|
||||
horarios: [
|
||||
{
|
||||
id_horario: 0,
|
||||
id_laboratorio: 0,
|
||||
hora_inicio: "1830",
|
||||
hora_fin: "1920",
|
||||
dia: "Jueves",
|
||||
},
|
||||
{
|
||||
id_horario: 1,
|
||||
id_laboratorio: 0,
|
||||
hora_inicio: "1550",
|
||||
hora_fin: "1740",
|
||||
dia: "Viernes",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id_laboratorio: 1,
|
||||
id_curso: 0,
|
||||
grupo: "B",
|
||||
docente: "Luis Rocha",
|
||||
horarios: [
|
||||
{
|
||||
id_horario: 2,
|
||||
id_laboratorio: 1,
|
||||
hora_inicio: "0700",
|
||||
hora_fin: "0850",
|
||||
dia: "Lunes",
|
||||
},
|
||||
{
|
||||
id_horario: 3,
|
||||
id_laboratorio: 1,
|
||||
hora_inicio: "1400",
|
||||
hora_fin: "1640",
|
||||
dia: "Miercoles",
|
||||
},
|
||||
{
|
||||
id_horario: 6,
|
||||
id_laboratorio: 1,
|
||||
hora_inicio: "1400",
|
||||
hora_fin: "1640",
|
||||
dia: "Viernes",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
const c2: CursoCompleto = {
|
||||
id_curso: 1,
|
||||
nombre_curso: "Plataformas Emergentes",
|
||||
curso_anio: "5to",
|
||||
abreviado: "PE",
|
||||
laboratorios: [
|
||||
{
|
||||
id_laboratorio: 2,
|
||||
id_curso: 1,
|
||||
grupo: "A",
|
||||
docente: "Diego Iquira",
|
||||
horarios: [
|
||||
{
|
||||
id_horario: 4,
|
||||
id_laboratorio: 2,
|
||||
hora_inicio: "0850",
|
||||
hora_fin: "1040",
|
||||
dia: "Jueves",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id_laboratorio: 3,
|
||||
id_curso: 1,
|
||||
grupo: "B",
|
||||
docente: "Diego Iquira",
|
||||
horarios: [
|
||||
{
|
||||
id_horario: 5,
|
||||
id_laboratorio: 3,
|
||||
hora_inicio: "1740",
|
||||
hora_fin: "1920",
|
||||
dia: "Martes",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
return [c1, c2];
|
||||
};
|
@ -1,82 +0,0 @@
|
||||
/*
|
||||
// HTTP GET
|
||||
// Url: /cursos
|
||||
|
||||
// El frontend pide una lista de la informacion de todos los cursos
|
||||
|
||||
// Backend responde con una lista de todos los cursos
|
||||
[
|
||||
{
|
||||
id_curso: number,
|
||||
id_datos_carrera: any, // Opcional
|
||||
nombre_curso: string,
|
||||
curso_anio: number | string, // Numero o string, dependiendo de como este en DB
|
||||
abreviado: string,
|
||||
}
|
||||
]
|
||||
*/
|
||||
|
||||
import { SERVER_PATH } from "../Store";
|
||||
|
||||
export type InfoCurso = {
|
||||
id_curso: number,
|
||||
nombre_curso: string,
|
||||
curso_anio: string,
|
||||
abreviado: string,
|
||||
}
|
||||
// `"1er"`, `"2do"`, etc
|
||||
type NombreAnio = string
|
||||
export type RespuestaListaCursos = {[key: NombreAnio]: Array<InfoCurso>}
|
||||
type ListaCursosFn = () => Promise<RespuestaListaCursos>
|
||||
|
||||
export const getAllListaCursos: ListaCursosFn = async() => {
|
||||
const response = await fetch(`${SERVER_PATH}/cursos`);
|
||||
const data = await response.json() as Array<InfoCurso>;
|
||||
|
||||
const resultMap: RespuestaListaCursos = {};
|
||||
data.forEach((curso) => {
|
||||
if (resultMap[curso.curso_anio] === undefined) {
|
||||
resultMap[curso.curso_anio] = [];
|
||||
}
|
||||
|
||||
resultMap[curso.curso_anio]?.push(curso);
|
||||
});
|
||||
|
||||
return resultMap;
|
||||
};
|
||||
|
||||
export const getAllListaCursosMock: ListaCursosFn = async() => {
|
||||
const arr5to: Array<InfoCurso> = [
|
||||
{
|
||||
id_curso: 0,
|
||||
nombre_curso: "Gestion de Sistemas y Tecnologias de Informacion",
|
||||
curso_anio: "5to",
|
||||
abreviado: "GSTI",
|
||||
},
|
||||
{
|
||||
id_curso: 1,
|
||||
nombre_curso: "Practicas Pre Profesionales",
|
||||
curso_anio: "5to",
|
||||
abreviado: "PPP",
|
||||
},
|
||||
];
|
||||
const arr4to: Array<InfoCurso> = [
|
||||
{
|
||||
id_curso: 2,
|
||||
nombre_curso: "Diseño y Arquitectura de Software",
|
||||
curso_anio: "4to",
|
||||
abreviado: "DAS",
|
||||
},
|
||||
{
|
||||
id_curso: 3,
|
||||
nombre_curso: "Gestion de Proyectos de Software",
|
||||
curso_anio: "4to",
|
||||
abreviado: "GPS",
|
||||
},
|
||||
];
|
||||
|
||||
return {
|
||||
"5to": arr5to,
|
||||
"4to": arr4to,
|
||||
};
|
||||
};
|
@ -1,34 +0,0 @@
|
||||
import { SERVER_PATH } from "../Store";
|
||||
|
||||
type IdLaboratorio = number;
|
||||
type LoginData = {correo_usuario: string};
|
||||
type LoginResponse = Promise<{matriculas: Array<IdLaboratorio>} | null>;
|
||||
export type LoginFunction = (data: LoginData) => LoginResponse;
|
||||
|
||||
// Mock for a login without courses
|
||||
export const mockLoginEmpty: LoginFunction = async(data) => ({matriculas: []});
|
||||
|
||||
// Mock for a login with courses
|
||||
export const mockLoginNotEmpty: LoginFunction = async(_) => ({
|
||||
matriculas: [0, 1, 2, 3],
|
||||
});
|
||||
|
||||
// Error login mock
|
||||
export const mockLoginWithError: LoginFunction = async(_) => null;
|
||||
|
||||
// Standard login
|
||||
export const loginFn: LoginFunction = async(data) => {
|
||||
const petition = await fetch(`${SERVER_PATH}/login`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
correo_usuario: data.correo_usuario,
|
||||
}),
|
||||
});
|
||||
if (!petition.ok) return null;
|
||||
return await petition.json() as {matriculas: Array<IdLaboratorio>};
|
||||
};
|
||||
|
@ -1,36 +0,0 @@
|
||||
import {SERVER_PATH} from "../Store";
|
||||
|
||||
type Input = {
|
||||
matriculas: Array<number>
|
||||
}
|
||||
export type InfoMatricula = {
|
||||
nombre_curso: string,
|
||||
grupo: string,
|
||||
docente: string,
|
||||
}
|
||||
type VerMatriculaFn = (_: Input) => Promise<Array<InfoMatricula>>;
|
||||
|
||||
export const getMatricula: VerMatriculaFn = async(input) => {
|
||||
const response = await fetch(`${SERVER_PATH}/recuperacion`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(input),
|
||||
});
|
||||
return await response.json();
|
||||
};
|
||||
|
||||
export const getMatriculaMock: VerMatriculaFn = async(_) => [
|
||||
{
|
||||
nombre_curso: "Plataformas Emergentes",
|
||||
grupo: "LA",
|
||||
docente: "Diego Iquira",
|
||||
},
|
||||
{
|
||||
nombre_curso: "Gestión de Proyectos de Software",
|
||||
grupo: "LB",
|
||||
docente: "Luis Rocha",
|
||||
},
|
||||
];
|
||||
|
29
src/App.tsx
29
src/App.tsx
@ -1,21 +1,16 @@
|
||||
import { Sistemas } from "./Views/pc/Sistemas";
|
||||
import { Main } from "./Views/Main";
|
||||
import { Index } from "./Views/Index";
|
||||
import { Editor } from "./Views/Editor";
|
||||
import { useRouter } from "./Router";
|
||||
import { Switch, Match, Show } from "solid-js";
|
||||
import { Wallpaper } from "./Wallpaper";
|
||||
import { SistemasMovil } from "./Views/SistemasMovil";
|
||||
import { SeleccionCursos } from "./Views/SeleccionCursos";
|
||||
import { VerMatricula } from "./Views/VerMatricula";
|
||||
import {SeleccionCursos as SeleccionCursosPC} from "./Views/pc/SeleccionCursos";
|
||||
import { VerMatricula as VerMatriculaPC } from "./Views/pc/VerMatricula";
|
||||
|
||||
function App() {
|
||||
const route = useRouter();
|
||||
const isMobile = screen.width <= 500;
|
||||
|
||||
return (
|
||||
<div class="App" style={isMobile ? "--color-texto: #202020;" : ""}>
|
||||
<div className="App" style={isMobile ? "--color-texto: #202020;" : ""}>
|
||||
<Show when={!isMobile}>
|
||||
<Wallpaper />
|
||||
</Show>
|
||||
@ -26,24 +21,8 @@ function App() {
|
||||
<Match when={route() === "/editor/"}>
|
||||
<Editor />
|
||||
</Match>
|
||||
<Match when={route() === "/seleccion-cursos/"}>
|
||||
<SeleccionCursos />
|
||||
</Match>
|
||||
<Match when={route() === "/sistemas-movil/"}>
|
||||
<SistemasMovil />
|
||||
</Match>
|
||||
<Match when={route() === "/ver-matricula/"}>
|
||||
<VerMatricula />
|
||||
</Match>
|
||||
|
||||
<Match when={route() === "/pc/seleccion-cursos/"}>
|
||||
<SeleccionCursosPC />
|
||||
</Match>
|
||||
<Match when={route() === "/pc/sistemas/"}>
|
||||
<Sistemas />
|
||||
</Match>
|
||||
<Match when={route() === "/pc/ver-matricula/"}>
|
||||
<VerMatriculaPC />
|
||||
<Match when={route() === "/sistemas/"}>
|
||||
<Main />
|
||||
</Match>
|
||||
</Switch>
|
||||
</div>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { css } from "aphrodite";
|
||||
import {estilosGlobales} from "../../../../Estilos";
|
||||
import { css } from "aphrodite"
|
||||
import { estilosGlobales } from "../Estilos"
|
||||
|
||||
interface BotonMaxMinProps {
|
||||
icono: string,
|
||||
@ -21,5 +21,5 @@ export function BotonIcono(props: BotonMaxMinProps) {
|
||||
>
|
||||
<i className={`${css(estilosGlobales.botonPhospor)} ${props.icono}`} />
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import { css } from "aphrodite";
|
||||
import { estilosGlobales } from "../../../../Estilos";
|
||||
import {EstadoLayout} from "../ContenedorHorarios";
|
||||
import { css } from "aphrodite"
|
||||
import { estilosGlobales } from "../Estilos"
|
||||
import { EstadoLayout } from "./ContenedorHorarios"
|
||||
|
||||
interface BotonMaxMinProps {
|
||||
fnMaximizar: () => void,
|
||||
@ -10,19 +10,19 @@ interface BotonMaxMinProps {
|
||||
}
|
||||
|
||||
export function BotonMaxMin(props: BotonMaxMinProps) {
|
||||
const horariosMax = () => props.estadoActualLayout() === props.estadoLayoutMax;
|
||||
const horariosMax = () => props.estadoActualLayout() === props.estadoLayoutMax
|
||||
|
||||
const tituloBoton = () => (horariosMax() ? "Minimizar" : "Maximizar");
|
||||
const iconoBoton = () => (horariosMax() ? "ph-arrows-in" : "ph-arrows-out");
|
||||
const tituloBoton = () => (horariosMax() ? "Minimizar" : "Maximizar")
|
||||
const iconoBoton = () => (horariosMax() ? "ph-arrows-in" : "ph-arrows-out")
|
||||
|
||||
const funcionBoton = () => {
|
||||
const estaMaximizado = horariosMax();
|
||||
const estaMaximizado = horariosMax()
|
||||
if (estaMaximizado) {
|
||||
props.fnMinimizar();
|
||||
props.fnMinimizar()
|
||||
} else {
|
||||
props.fnMaximizar();
|
||||
props.fnMaximizar()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
@ -38,5 +38,5 @@ export function BotonMaxMin(props: BotonMaxMinProps) {
|
||||
>
|
||||
<i className={`${css(estilosGlobales.botonPhospor)} ${iconoBoton()}`} />
|
||||
</button>
|
||||
);
|
||||
)
|
||||
}
|
137
src/ContenedorHorarios/ContenedorHorarios.tsx
Executable file
137
src/ContenedorHorarios/ContenedorHorarios.tsx
Executable file
@ -0,0 +1,137 @@
|
||||
import YAML from "yaml"
|
||||
import { css, StyleSheet } from "aphrodite"
|
||||
import { MiHorario } from "./MiHorario"
|
||||
import { Horarios } from "./Horarios"
|
||||
import {
|
||||
Anios,
|
||||
Cursos,
|
||||
DatosHorario,
|
||||
DatosHorarioRaw,
|
||||
DatosGrupo,
|
||||
} from "../types/DatosHorario"
|
||||
import { estilosGlobales } from "../Estilos"
|
||||
import { batch, createEffect, createMemo, createSignal, Show } from "solid-js"
|
||||
import { useListaCursos } from "./useListaCursos"
|
||||
|
||||
const datosPromise = (async() => {
|
||||
const file = await fetch("/horarios/2022_2_fps_ingenieriadesistemas.yaml")
|
||||
const text = await file.text()
|
||||
const datosRaw = YAML.parse(text) as DatosHorarioRaw
|
||||
|
||||
// Agregar los campos faltantes a DatosHorarioRaw para que sea DatosHorario
|
||||
const datos: DatosHorario = {
|
||||
...datosRaw,
|
||||
años: {},
|
||||
}
|
||||
|
||||
const anios: Anios = {}
|
||||
for (const [nombreAnio, anio] of Object.entries(datosRaw.años)) {
|
||||
const anioData: Cursos = {}
|
||||
for (const [nombreCurso, curso] of Object.entries(anio)) {
|
||||
|
||||
const gruposTeoria: { [k: string]: DatosGrupo } = {}
|
||||
for (const [key, data] of Object.entries(curso.Teoria)) {
|
||||
gruposTeoria[key] = Object.assign({seleccionado: false}, data)
|
||||
}
|
||||
|
||||
const gruposLab: { [k: string]: DatosGrupo } = {}
|
||||
for (const [key, data] of Object.entries(curso.Laboratorio ?? {})) {
|
||||
gruposLab[key] = Object.assign({seleccionado: false}, data)
|
||||
}
|
||||
|
||||
anioData[nombreCurso] = {
|
||||
...curso,
|
||||
oculto: false,
|
||||
Teoria: gruposTeoria,
|
||||
Laboratorio: gruposLab,
|
||||
}
|
||||
}
|
||||
|
||||
anios[nombreAnio] = anioData
|
||||
}
|
||||
|
||||
datos.años = anios
|
||||
return datos
|
||||
})()
|
||||
|
||||
const ElemCargando = () => (
|
||||
<div className={css(estilosGlobales.contenedor, estilosGlobales.inlineBlock)}>
|
||||
Recuperando horarios...
|
||||
</div>
|
||||
)
|
||||
|
||||
export type EstadoLayout = "MaxPersonal" | "Normal" | "MaxHorarios";
|
||||
|
||||
const {
|
||||
listaCursos: cursosUsuario,
|
||||
setListaCursos: setCursosUsuarios,
|
||||
agregarCursoALista: agregarCursoUsuario,
|
||||
} = useListaCursos()
|
||||
|
||||
export function ContenedorHorarios() {
|
||||
const [datosCargados, setDatosCargados] = createSignal(false)
|
||||
const [datos, setDatos] = createSignal<DatosHorario | null>(null)
|
||||
const [estadoLayout, setEstadoLayout] = (
|
||||
createSignal<EstadoLayout>(localStorage.getItem("estadoLayout") as EstadoLayout || "Normal")
|
||||
)
|
||||
|
||||
const e = createMemo(() => {
|
||||
let templateColumns = ""
|
||||
switch (estadoLayout()) {
|
||||
case "MaxHorarios": {
|
||||
templateColumns = "0 auto"
|
||||
break
|
||||
}
|
||||
case "MaxPersonal": {
|
||||
templateColumns = "auto 0m"
|
||||
break
|
||||
}
|
||||
case "Normal": {
|
||||
templateColumns = "50% 50%"
|
||||
}
|
||||
}
|
||||
|
||||
localStorage.setItem("estadoLayout", estadoLayout())
|
||||
|
||||
return StyleSheet.create({
|
||||
contenedor: {
|
||||
display: "grid",
|
||||
gridTemplateColumns: templateColumns,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
createEffect(async() => {
|
||||
const datos = await datosPromise
|
||||
batch(() => {
|
||||
setDatos(datos)
|
||||
setDatosCargados(true)
|
||||
})
|
||||
})
|
||||
|
||||
return (
|
||||
<div className={css(e().contenedor)}>
|
||||
<div>
|
||||
<MiHorario
|
||||
estadoLayout={estadoLayout()}
|
||||
setEstadoLayout={setEstadoLayout}
|
||||
cursosUsuario={cursosUsuario}
|
||||
fnAgregarCurso={agregarCursoUsuario}
|
||||
setCursosUsuarios={setCursosUsuarios}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Show when={datosCargados()}>
|
||||
<Horarios
|
||||
data={datos()!}
|
||||
estadoLayout={estadoLayout()}
|
||||
setEstadoLayout={setEstadoLayout}
|
||||
fnAgregarCurso={(c) => agregarCursoUsuario(JSON.parse(JSON.stringify(c)))}
|
||||
listaCursosUsuario={cursosUsuario}
|
||||
setCursosUsuarios={setCursosUsuarios}
|
||||
/>
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -1,10 +1,9 @@
|
||||
import { Cursos, DatosGrupo, ListaCursosUsuario, Curso } from "../../../../types/DatosHorario";
|
||||
import { createMemo, For } from "solid-js";
|
||||
import { produce, SetStoreFunction } from "solid-js/store";
|
||||
import { StyleSheet, css } from "aphrodite";
|
||||
import { estilosGlobales } from "../../../../Estilos";
|
||||
import { TablaObserver } from "./TablaObserver";
|
||||
import { setGruposSeleccionados } from "../../../../Store";
|
||||
import { Cursos, DatosGrupo, ListaCursosUsuario, Curso } from "../types/DatosHorario"
|
||||
import { createMemo, For } from "solid-js"
|
||||
import { produce, SetStoreFunction } from "solid-js/store"
|
||||
import { StyleSheet, css } from "aphrodite"
|
||||
import { estilosGlobales } from "../Estilos"
|
||||
import { TablaObserver } from "./TablaObserver"
|
||||
|
||||
const e = StyleSheet.create({
|
||||
inline: {
|
||||
@ -34,20 +33,21 @@ const e = StyleSheet.create({
|
||||
border: "none",
|
||||
color: "var(--color)",
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
const claseCursoNoAgregado = css(
|
||||
e.contenedorCurso,
|
||||
estilosGlobales.contenedor,
|
||||
);
|
||||
)
|
||||
|
||||
const claseCursoOculto = css(e.cursoOculto);
|
||||
const claseCursoOculto = css(e.cursoOculto)
|
||||
|
||||
interface Props {
|
||||
version: number,
|
||||
dataAnio: Cursos,
|
||||
anioActual: () => string,
|
||||
fnAgregarCurso: (c: Curso) => void,
|
||||
listaCursosUsuario: ListaCursosUsuario,
|
||||
esCursoMiHorario: boolean,
|
||||
setCursosUsuarios: SetStoreFunction<ListaCursosUsuario>,
|
||||
tablaObserver: TablaObserver,
|
||||
@ -64,7 +64,7 @@ interface PropsIndicadorGrupo {
|
||||
}
|
||||
|
||||
function IndicadorGrupo(props: PropsIndicadorGrupo) {
|
||||
const id = `${props.idParcial}_${props.esLab ? "L" : "T"}_${props.nombre}`;
|
||||
const id = `${props.idParcial}_${props.esLab ? "L" : "T"}_${props.nombre}`
|
||||
return (
|
||||
<span
|
||||
className={css(e.botonTexto, estilosGlobales.contenedorCursor, estilosGlobales.contenedorCursorSoft)}
|
||||
@ -75,7 +75,7 @@ function IndicadorGrupo(props: PropsIndicadorGrupo) {
|
||||
>
|
||||
{props.esLab ? "L" : ""}{props.nombre}
|
||||
</span>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
const agruparProfesores = (
|
||||
@ -84,21 +84,31 @@ const agruparProfesores = (
|
||||
esLab: boolean,
|
||||
setCursosUsuarios: FnSetCursosUsuarios,
|
||||
) => {
|
||||
const profesores: { [k: string]: [string, () => void][] } = {};
|
||||
const profesores: { [k: string]: [string, () => void][] } = {}
|
||||
for (const [grupo, datosGrupo] of Object.entries(datos)) {
|
||||
const nombreProfesor = datosGrupo.Docente;
|
||||
const nombreProfesor = datosGrupo.Docente
|
||||
if (!profesores[nombreProfesor]) {
|
||||
profesores[nombreProfesor] = [];
|
||||
profesores[nombreProfesor] = []
|
||||
}
|
||||
profesores[nombreProfesor].push([
|
||||
grupo,
|
||||
() => {
|
||||
setGruposSeleccionados(datosGrupo.id_laboratorio, (x) => !x);
|
||||
},
|
||||
]);
|
||||
setCursosUsuarios("cursos", Number(indiceCurso), "Teoria", produce<{ [p: string]: DatosGrupo }>((x) => {
|
||||
const grupoActualSeleccionado = x[grupo].seleccionado
|
||||
|
||||
if (grupoActualSeleccionado) {
|
||||
x[grupo].seleccionado = false
|
||||
} else {
|
||||
for (const xKey in x) {
|
||||
x[xKey].seleccionado = xKey === grupo
|
||||
}
|
||||
return profesores;
|
||||
};
|
||||
}
|
||||
}))
|
||||
},
|
||||
])
|
||||
}
|
||||
return profesores
|
||||
}
|
||||
|
||||
function CursoE(
|
||||
indiceCurso: string,
|
||||
@ -107,27 +117,42 @@ function CursoE(
|
||||
claseCursoAgregado: string,
|
||||
props: Props,
|
||||
) {
|
||||
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(() => {
|
||||
if (props.esCursoMiHorario && datosCurso.oculto) {
|
||||
return claseCursoOculto;
|
||||
return claseCursoOculto
|
||||
}
|
||||
return claseCursoNoAgregado;
|
||||
});
|
||||
return cursoAgregadoMemo()
|
||||
? claseCursoAgregado
|
||||
: claseCursoNoAgregado
|
||||
})
|
||||
|
||||
const profesoresTeoria = createMemo(() => agruparProfesores(
|
||||
datosCurso.Teoria,
|
||||
Number(indiceCurso),
|
||||
false,
|
||||
props.setCursosUsuarios,
|
||||
));
|
||||
))
|
||||
const profesoresLab = createMemo(() => agruparProfesores(
|
||||
datosCurso.Laboratorio ?? {},
|
||||
Number(indiceCurso),
|
||||
true,
|
||||
props.setCursosUsuarios,
|
||||
));
|
||||
))
|
||||
|
||||
const IndicadorGrupos = (profesor: string, grupos: [string, () => void][], esLab: boolean) => (
|
||||
<td style={{"padding-bottom": "0.5rem", "padding-right": "0.75rem"}}>
|
||||
@ -147,7 +172,7 @@ function CursoE(
|
||||
}
|
||||
</For>
|
||||
</td>
|
||||
);
|
||||
)
|
||||
|
||||
return (
|
||||
<div className={claseMemo()}>
|
||||
@ -172,18 +197,24 @@ function CursoE(
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<button
|
||||
className={css(e.botonTexto, estilosGlobales.contenedorCursor, estilosGlobales.contenedorCursorSoft)}
|
||||
onClick={() => props.fnAgregarCurso(datosCurso)}
|
||||
>
|
||||
{tituloMemo}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
export function CursosElem(props: Props) {
|
||||
const anio = () => props.anioActual().substring(0, props.anioActual().indexOf(" "));
|
||||
const anio = () => props.anioActual().substring(0, props.anioActual().indexOf(" "))
|
||||
|
||||
const claseCursoAgregado = css(
|
||||
e.contenedorCurso,
|
||||
estilosGlobales.contenedor,
|
||||
!props.esCursoMiHorario && estilosGlobales.contenedorCursorActivo,
|
||||
);
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -191,5 +222,5 @@ export function CursosElem(props: Props) {
|
||||
{([indiceCurso, datosCurso]) => CursoE(indiceCurso, datosCurso, anio, claseCursoAgregado, props)}
|
||||
</For>
|
||||
</>
|
||||
);
|
||||
)
|
||||
}
|
139
src/ContenedorHorarios/Horarios.tsx
Executable file
139
src/ContenedorHorarios/Horarios.tsx
Executable file
@ -0,0 +1,139 @@
|
||||
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>
|
||||
)
|
||||
}
|
146
src/ContenedorHorarios/MiHorario.tsx
Executable file
146
src/ContenedorHorarios/MiHorario.tsx
Executable file
@ -0,0 +1,146 @@
|
||||
import { estilosGlobales } from "../Estilos"
|
||||
import { StyleSheet, css } from "aphrodite"
|
||||
import { Tabla } from "./Tabla"
|
||||
import { EstadoLayout } from "./ContenedorHorarios"
|
||||
import { Switch, Match, createMemo } from "solid-js"
|
||||
import {SetStoreFunction} from "solid-js/store"
|
||||
import { BotonMaxMin } from "./BotonMaxMin"
|
||||
import { BotonIcono } from "./BotonIcono"
|
||||
import { Curso, Cursos, ListaCursosUsuario } from "../types/DatosHorario"
|
||||
import { CursosElem } from "./CursosElem"
|
||||
import { TablaObserver } from "./TablaObserver"
|
||||
|
||||
interface MiHorarioProps {
|
||||
estadoLayout: EstadoLayout,
|
||||
setEstadoLayout: (v: EstadoLayout) => EstadoLayout,
|
||||
cursosUsuario: ListaCursosUsuario,
|
||||
fnAgregarCurso: (c: Curso) => void,
|
||||
setCursosUsuarios: SetStoreFunction<ListaCursosUsuario>
|
||||
}
|
||||
|
||||
const e = StyleSheet.create({
|
||||
horario: {},
|
||||
boton: {
|
||||
textDecoration: "none",
|
||||
// paddingRight: "0.5rem",
|
||||
"::before": {
|
||||
fontSize: "1rem",
|
||||
// transform: "translateY(0.2rem)",
|
||||
textDecoration: "none",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export function MiHorario(props: MiHorarioProps) {
|
||||
const tablaObserver = new TablaObserver()
|
||||
|
||||
const datosMiHorario = createMemo(() => {
|
||||
const obj: Cursos = {}
|
||||
props.cursosUsuario.cursos.forEach((x, i) => {
|
||||
obj[i] = x
|
||||
})
|
||||
return obj
|
||||
})
|
||||
|
||||
const fnMaximizar = () => props.setEstadoLayout("MaxPersonal")
|
||||
const fnMinimizar = () => props.setEstadoLayout("Normal")
|
||||
const estadoActualLayout = () => props.estadoLayout
|
||||
|
||||
/* TODO: En barra superior colocar todos los horarios. En barra inferior el horario
|
||||
actual.
|
||||
Al hacer click en un horario de la barra superior, llevarlo al inicio de la lista.
|
||||
*/
|
||||
return (
|
||||
<div>
|
||||
<Switch>
|
||||
<Match when={props.estadoLayout === "Normal" || props.estadoLayout === "MaxPersonal"}>
|
||||
|
||||
<div>
|
||||
<div className={css(
|
||||
estilosGlobales.inlineBlock,
|
||||
estilosGlobales.contenedor,
|
||||
)}
|
||||
>
|
||||
Mi horario
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className={css(
|
||||
estilosGlobales.inlineBlock,
|
||||
estilosGlobales.contenedor,
|
||||
)}
|
||||
>
|
||||
Mi horario
|
||||
</div>
|
||||
|
|
||||
{/*
|
||||
<BotonIcono
|
||||
titulo={"Nuevo horario en blanco"}
|
||||
icono={"ph-plus"}
|
||||
onClick={() => {}}
|
||||
/>
|
||||
<BotonIcono
|
||||
titulo={"Reiniciar horario"}
|
||||
icono={"ph-arrow-counter-clockwise"}
|
||||
onClick={() => {}}
|
||||
/>
|
||||
<BotonIcono
|
||||
titulo={"Duplicar horario"}
|
||||
icono={"ph-copy"}
|
||||
onClick={() => {}}
|
||||
/>
|
||||
<BotonIcono titulo={"Eliminar horario"}
|
||||
icono={"ph-trash"}
|
||||
onClick={() => {}}
|
||||
/>
|
||||
|
|
||||
*/}
|
||||
<BotonMaxMin
|
||||
fnMaximizar={fnMaximizar}
|
||||
fnMinimizar={fnMinimizar}
|
||||
estadoActualLayout={estadoActualLayout}
|
||||
estadoLayoutMax={"MaxPersonal"}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={css(
|
||||
e.horario,
|
||||
estilosGlobales.contenedor,
|
||||
)}
|
||||
>
|
||||
<Tabla
|
||||
data={datosMiHorario()}
|
||||
anio={"Mi horario"}
|
||||
version={1}
|
||||
setCursosUsuarios={props.setCursosUsuarios}
|
||||
tablaObserver={tablaObserver}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<CursosElem
|
||||
version={Math.floor(Math.random() * 1_000_000)}
|
||||
anioActual={() => "Mi horario"}
|
||||
dataAnio={datosMiHorario()}
|
||||
fnAgregarCurso={props.fnAgregarCurso}
|
||||
listaCursosUsuario={props.cursosUsuario}
|
||||
esCursoMiHorario
|
||||
setCursosUsuarios={props.setCursosUsuarios}
|
||||
tablaObserver={tablaObserver}
|
||||
/>
|
||||
</Match>
|
||||
<Match when={props.estadoLayout === "MaxHorarios"}>
|
||||
{/*
|
||||
<BotonMaxMin
|
||||
fnMaximizar={fnMaximizar}
|
||||
fnMinimizar={fnMinimizar}
|
||||
estadoActualLayout={estadoActualLayout}
|
||||
estadoLayoutMax={"MaxPersonal"}
|
||||
/>
|
||||
*/}
|
||||
<div />
|
||||
</Match>
|
||||
</Switch>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
import { StyleSheet, css } from "aphrodite";
|
||||
import { batch, createMemo, For } from "solid-js";
|
||||
import { produce, SetStoreFunction } from "solid-js/store";
|
||||
import {estilosGlobales} from "../../../../Estilos";
|
||||
import { Cursos, ListaCursosUsuario, DataProcesada, DatosGrupo } from "../../../../types/DatosHorario";
|
||||
import { Dia, dias, gruposSeleccionados, horas, setGruposSeleccionados } from "../../../../Store";
|
||||
import { FilaTabla } from "./Tabla/FilaTabla";
|
||||
import { TablaObserver } from "./TablaObserver";
|
||||
import { StyleSheet, css } from "aphrodite"
|
||||
import { batch, createMemo, For } from "solid-js"
|
||||
import { produce, SetStoreFunction } from "solid-js/store"
|
||||
import { estilosGlobales } from "../Estilos"
|
||||
import { Cursos, ListaCursosUsuario, DataProcesada, DatosGrupo } from "../types/DatosHorario"
|
||||
import { Dia, dias, horas } from "../Store"
|
||||
import { FilaTabla } from "./Tabla/FilaTabla"
|
||||
import { TablaObserver } from "./TablaObserver"
|
||||
|
||||
export const coloresBorde = Object.freeze([
|
||||
"rgba(33,150,243,1)",
|
||||
@ -13,22 +13,22 @@ export const coloresBorde = Object.freeze([
|
||||
"rgba(236,64,122 ,1)",
|
||||
"rgba(29,233,182 ,1)",
|
||||
"rgba(244,67,54,1)",
|
||||
]);
|
||||
])
|
||||
|
||||
export const diaANum = (d: Dia) => {
|
||||
switch (d) {
|
||||
case "Lunes":
|
||||
return 0;
|
||||
return 0
|
||||
case "Martes":
|
||||
return 1;
|
||||
return 1
|
||||
case "Miercoles":
|
||||
return 2;
|
||||
return 2
|
||||
case "Jueves":
|
||||
return 3;
|
||||
return 3
|
||||
case "Viernes":
|
||||
return 4;
|
||||
return 4
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const e = StyleSheet.create({
|
||||
fila: {
|
||||
@ -81,34 +81,34 @@ const e = StyleSheet.create({
|
||||
celdaCursoTeoria: {
|
||||
fontWeight: "bold",
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
type FnSetCursosUsuarios = SetStoreFunction<ListaCursosUsuario>;
|
||||
|
||||
const procesarAnio = (data: Cursos, anio: string, version: number, setCursosUsuarios: FnSetCursosUsuarios) => {
|
||||
const obj: DataProcesada = {};
|
||||
const obj: DataProcesada = {}
|
||||
|
||||
for (const [indiceCurso, curso] of Object.entries(data)) {
|
||||
if (curso.oculto) continue;
|
||||
if (curso.oculto) continue
|
||||
|
||||
const nombreAbreviado = curso.abreviado;
|
||||
const nombreAbreviado = curso.abreviado
|
||||
|
||||
for (const [grupoStr, grupo] of Object.entries(curso.Teoria)) {
|
||||
for (const hora of grupo.Horas) {
|
||||
const dia = hora.substring(0, 2);
|
||||
const horas = hora.substring(2, 4);
|
||||
const minutos = hora.substr(4);
|
||||
const dia = hora.substring(0, 2)
|
||||
const horas = hora.substring(2, 4)
|
||||
const minutos = hora.substr(4)
|
||||
|
||||
const horaCompleta = `${horas}:${minutos}`;
|
||||
const horaCompleta = `${horas}:${minutos}`
|
||||
|
||||
const id = `${version}_${anio}_${nombreAbreviado}_T_${grupoStr}`;
|
||||
const id = `${version}_${anio}_${nombreAbreviado}_T_${grupoStr}`
|
||||
|
||||
if (!(horaCompleta in obj)) {
|
||||
obj[horaCompleta] = {};
|
||||
obj[horaCompleta] = {}
|
||||
}
|
||||
|
||||
if (!(dia in obj[horaCompleta])) {
|
||||
obj[horaCompleta][dia] = [];
|
||||
obj[horaCompleta][dia] = []
|
||||
}
|
||||
|
||||
obj[horaCompleta][dia].push({
|
||||
@ -117,28 +117,38 @@ const procesarAnio = (data: Cursos, anio: string, version: number, setCursosUsua
|
||||
esLab: false,
|
||||
datosGrupo: grupo,
|
||||
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
|
||||
}
|
||||
}
|
||||
}))
|
||||
},
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for (const [grupoStr, grupo] of Object.entries(curso.Laboratorio ?? {})) {
|
||||
for (const hora of grupo.Horas) {
|
||||
const dia = hora.substring(0, 2);
|
||||
const horas = hora.substring(2, 4);
|
||||
const minutos = hora.substr(4);
|
||||
const dia = hora.substring(0, 2)
|
||||
const horas = hora.substring(2, 4)
|
||||
const minutos = hora.substr(4)
|
||||
|
||||
const horaCompleta = `${horas}:${minutos}`;
|
||||
const horaCompleta = `${horas}:${minutos}`
|
||||
|
||||
const id = `${version}_${anio}_${nombreAbreviado}_L_${grupoStr}`;
|
||||
const id = `${version}_${anio}_${nombreAbreviado}_L_${grupoStr}`
|
||||
|
||||
if (!(horaCompleta in obj)) {
|
||||
obj[horaCompleta] = {};
|
||||
obj[horaCompleta] = {}
|
||||
}
|
||||
|
||||
if (!(dia in obj[horaCompleta])) {
|
||||
obj[horaCompleta][dia] = [];
|
||||
obj[horaCompleta][dia] = []
|
||||
}
|
||||
|
||||
obj[horaCompleta][dia].push({
|
||||
@ -147,15 +157,30 @@ const procesarAnio = (data: Cursos, anio: string, version: number, setCursosUsua
|
||||
esLab: true,
|
||||
datosGrupo: grupo,
|
||||
fnSeleccionar: () => {
|
||||
setGruposSeleccionados(grupo.id_laboratorio, (x) => !x);
|
||||
setCursosUsuarios(
|
||||
"cursos",
|
||||
Number(indiceCurso),
|
||||
"Laboratorio",
|
||||
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
|
||||
}
|
||||
}
|
||||
}),
|
||||
)
|
||||
},
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return obj;
|
||||
};
|
||||
return obj
|
||||
}
|
||||
|
||||
interface Props {
|
||||
data: Cursos,
|
||||
@ -166,12 +191,12 @@ interface Props {
|
||||
}
|
||||
|
||||
export function Tabla(props: Props) {
|
||||
const anio = () => props.anio.substring(0, props.anio.indexOf(" "));
|
||||
const data = createMemo(() => procesarAnio(props.data, anio(), props.version, props.setCursosUsuarios));
|
||||
const anio = () => props.anio.substring(0, props.anio.indexOf(" "))
|
||||
const data = createMemo(() => procesarAnio(props.data, anio(), props.version, props.setCursosUsuarios))
|
||||
|
||||
const celdas = createMemo(() => {
|
||||
// Hace reaccionar a la reactividad de Solid
|
||||
const d = data();
|
||||
const d = data()
|
||||
return (
|
||||
<For each={horas}>
|
||||
{(hora) => (
|
||||
@ -182,8 +207,8 @@ export function Tabla(props: Props) {
|
||||
/>
|
||||
)}
|
||||
</For>
|
||||
);
|
||||
});
|
||||
)
|
||||
})
|
||||
|
||||
return (
|
||||
<div>
|
||||
@ -198,5 +223,5 @@ export function Tabla(props: Props) {
|
||||
</div>
|
||||
{celdas()}
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
import { StyleSheet, css } from "aphrodite";
|
||||
import { estilosGlobales } from "../../../../../Estilos";
|
||||
import { For, createSignal, createMemo, createEffect, onCleanup } from "solid-js";
|
||||
import { Dia } from "../../../../../Store";
|
||||
import { DatosGrupo } from "../../../../../types/DatosHorario";
|
||||
import {TablaObserver} from "../TablaObserver";
|
||||
import { StyleSheet, css } from "aphrodite"
|
||||
import { estilosGlobales } from "../../Estilos"
|
||||
import { For, createSignal, createMemo, createEffect, onCleanup } from "solid-js"
|
||||
import { Dia } from "../../Store"
|
||||
import { DatosGrupo } from "../../types/DatosHorario"
|
||||
import { TablaObserver } from "../TablaObserver"
|
||||
|
||||
const e = StyleSheet.create({
|
||||
celdaComun: {
|
||||
@ -42,7 +42,7 @@ const e = StyleSheet.create({
|
||||
celdaResaltadoSeleccionado: {
|
||||
textDecoration: "underline",
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
const eColores = StyleSheet.create({
|
||||
lunes: {
|
||||
@ -62,7 +62,7 @@ const eColores = StyleSheet.create({
|
||||
viernes: {
|
||||
backgroundColor: "rgba(244,67,54,1)",
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
const clasesColores = {
|
||||
Lunes: css(eColores.lunes),
|
||||
@ -70,7 +70,7 @@ const clasesColores = {
|
||||
Miercoles: css(eColores.miercoles),
|
||||
Jueves: css(eColores.jueves),
|
||||
Viernes: css(eColores.viernes),
|
||||
};
|
||||
}
|
||||
|
||||
interface DatosProps {
|
||||
id: string,
|
||||
@ -97,110 +97,108 @@ interface Props {
|
||||
tablaObserver: TablaObserver,
|
||||
}
|
||||
|
||||
const claseSeldaSeleccionada = css(e.celdaSeleccionado);
|
||||
const claseSeldaSeleccionada = css(e.celdaSeleccionado)
|
||||
|
||||
function RenderFila(datos: DatosProps, props: Props) {
|
||||
const id = datos.id;
|
||||
const txt = datos.txt;
|
||||
const esLab = datos.esLab;
|
||||
const fnSeleccionar = datos.fnSeleccionar;
|
||||
const id = datos.id
|
||||
const txt = datos.txt
|
||||
const esLab = datos.esLab
|
||||
const fnSeleccionar = datos.fnSeleccionar
|
||||
|
||||
const estadoCeldaMemo = props.tablaObserver.registrarConId(id, datos.datosGrupo);
|
||||
const estadoCeldaMemo = props.tablaObserver.registrarConId(id, datos.datosGrupo)
|
||||
|
||||
const [estabaResaltado, setEstabaResaltado] = createSignal(false);
|
||||
const [estabaResaltado, setEstabaResaltado] = createSignal(false)
|
||||
|
||||
// Limpiar los memos, porque cuando se desmonta la celda esos memos quedan sin efecto
|
||||
onCleanup(() => {
|
||||
props.tablaObserver.limpiar(id);
|
||||
});
|
||||
props.tablaObserver.limpiar(id)
|
||||
})
|
||||
|
||||
const clases = createMemo(
|
||||
() => {
|
||||
const clases = [
|
||||
e.celdaCurso,
|
||||
esLab ? e.celdaCursoLab : e.celdaCursoTeoria,
|
||||
];
|
||||
let adicional = "";
|
||||
]
|
||||
let adicional = ""
|
||||
|
||||
const estadoCelda = estadoCeldaMemo();
|
||||
const estadoCelda = estadoCeldaMemo()
|
||||
|
||||
switch (estadoCelda) {
|
||||
case "Normal": {
|
||||
if (estabaResaltado()) {
|
||||
props.fnDesresaltarFila();
|
||||
setEstabaResaltado(false);
|
||||
props.fnDesresaltarFila()
|
||||
setEstabaResaltado(false)
|
||||
}
|
||||
|
||||
break;
|
||||
break
|
||||
}
|
||||
case "Oculto": {
|
||||
if (estabaResaltado()) {
|
||||
props.fnDesresaltarFila();
|
||||
setEstabaResaltado(false);
|
||||
props.fnDesresaltarFila()
|
||||
setEstabaResaltado(false)
|
||||
}
|
||||
|
||||
clases.push(e.celdaOculto);
|
||||
break;
|
||||
clases.push(e.celdaOculto)
|
||||
break
|
||||
}
|
||||
case "Resaltado": {
|
||||
props.fnResaltarFila();
|
||||
setEstabaResaltado(true);
|
||||
clases.push(e.celdaResaltado);
|
||||
adicional = clasesColores[props.dia];
|
||||
break;
|
||||
props.fnResaltarFila()
|
||||
setEstabaResaltado(true)
|
||||
clases.push(e.celdaResaltado)
|
||||
adicional = clasesColores[props.dia]
|
||||
break
|
||||
}
|
||||
case "Seleccionado": {
|
||||
if (estabaResaltado()) {
|
||||
props.fnDesresaltarFila();
|
||||
setEstabaResaltado(false);
|
||||
props.fnDesresaltarFila()
|
||||
setEstabaResaltado(false)
|
||||
}
|
||||
|
||||
clases.push(e.celdaSeleccionado);
|
||||
break;
|
||||
clases.push(e.celdaSeleccionado)
|
||||
break
|
||||
}
|
||||
case "ResaltadoOculto": {
|
||||
props.fnResaltarFila();
|
||||
setEstabaResaltado(true);
|
||||
props.fnResaltarFila()
|
||||
setEstabaResaltado(true)
|
||||
|
||||
clases.push(e.celdaResaltadoOculto);
|
||||
adicional = clasesColores[props.dia];
|
||||
break;
|
||||
clases.push(e.celdaResaltadoOculto)
|
||||
adicional = clasesColores[props.dia]
|
||||
break
|
||||
}
|
||||
case "ResaltadoSeleccionado": {
|
||||
props.fnResaltarFila();
|
||||
setEstabaResaltado(true);
|
||||
props.fnResaltarFila()
|
||||
setEstabaResaltado(true)
|
||||
|
||||
clases.push(e.celdaResaltadoSeleccionado);
|
||||
adicional = clasesColores[props.dia];
|
||||
break;
|
||||
clases.push(e.celdaResaltadoSeleccionado)
|
||||
adicional = clasesColores[props.dia]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return `${css(...clases)} ${adicional}`;
|
||||
return `${css(...clases)} ${adicional}`
|
||||
},
|
||||
undefined,
|
||||
{
|
||||
equals: (x, y) => x === y,
|
||||
},
|
||||
);
|
||||
(x, y) => x === y,
|
||||
)
|
||||
|
||||
return (
|
||||
<button className={clases()}
|
||||
onMouseEnter={() => {
|
||||
props.tablaObserver.resaltar(id);
|
||||
props.tablaObserver.resaltar(id)
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
props.tablaObserver.quitarResaltado();
|
||||
props.tablaObserver.quitarResaltado()
|
||||
}}
|
||||
onClick={fnSeleccionar}
|
||||
>
|
||||
{txt}
|
||||
</button>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
export function CeldaFila(props: Props) {
|
||||
const datos = props.datos;
|
||||
const datos = props.datos
|
||||
|
||||
return (
|
||||
<div className={css(e.celdaComun, estilosGlobales.inlineBlock)}>
|
||||
@ -208,5 +206,5 @@ export function CeldaFila(props: Props) {
|
||||
{(datos) => RenderFila(datos, props)}
|
||||
</For>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
@ -1,12 +1,12 @@
|
||||
import { StyleSheet, css } from "aphrodite";
|
||||
import { estilosGlobales } from "../../../../../Estilos";
|
||||
import { For, createMemo } from "solid-js";
|
||||
import {createStore, Store} from "solid-js/store";
|
||||
import { Dia, dias } from "../../../../../Store";
|
||||
import { CeldaFila } from "./CeldaFila";
|
||||
import { DataProcesada } from "../../../../../types/DatosHorario";
|
||||
import { coloresBorde, diaANum } from "../Tabla";
|
||||
import { TablaObserver } from "../TablaObserver";
|
||||
import { StyleSheet, css } from "aphrodite"
|
||||
import { estilosGlobales } from "../../Estilos"
|
||||
import { For, createMemo } from "solid-js"
|
||||
import {createStore, Store} from "solid-js/store"
|
||||
import { Dia, dias } from "../../Store"
|
||||
import { CeldaFila } from "./CeldaFila"
|
||||
import { DataProcesada } from "../../types/DatosHorario"
|
||||
import { coloresBorde, diaANum } from "../Tabla"
|
||||
import { TablaObserver } from "../TablaObserver"
|
||||
|
||||
const e = StyleSheet.create({
|
||||
celdaHora: {
|
||||
@ -50,7 +50,7 @@ const e = StyleSheet.create({
|
||||
celdaResaltadoTransparente: {
|
||||
backgroundColor: "transparent",
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
const [diasResaltados, setDiasResaltados] = createStore({
|
||||
Lunes: 0,
|
||||
@ -58,7 +58,7 @@ const [diasResaltados, setDiasResaltados] = createStore({
|
||||
Miercoles: 0,
|
||||
Jueves: 0,
|
||||
Viernes: 0,
|
||||
} as { [k: string]: number });
|
||||
} as { [k: string]: number })
|
||||
|
||||
interface Props {
|
||||
hora: string,
|
||||
@ -69,7 +69,7 @@ interface Props {
|
||||
const diasFilter = createMemo(() => Object.entries(diasResaltados)
|
||||
.filter((x) => x[1] > 0)
|
||||
.map((x) => x[0] as Dia)
|
||||
.sort((x, y) => (diaANum(x) > diaANum(y) ? 1 : -1)));
|
||||
.sort((x, y) => (diaANum(x) > diaANum(y) ? 1 : -1)))
|
||||
|
||||
const useDiasResaltados: () => [
|
||||
Store<{ [k: string]: boolean }>,
|
||||
@ -82,26 +82,26 @@ const useDiasResaltados: () => [
|
||||
Miercoles: false,
|
||||
Jueves: false,
|
||||
Viernes: false,
|
||||
} as { [k: string]: boolean });
|
||||
} as { [k: string]: boolean })
|
||||
|
||||
const fnResaltar = (d: Dia) => {
|
||||
setDiasResaltadosLocal(d, true);
|
||||
setDiasResaltados(d, (v) => v + 1);
|
||||
};
|
||||
setDiasResaltadosLocal(d, true)
|
||||
setDiasResaltados(d, (v) => v + 1)
|
||||
}
|
||||
|
||||
const fnDesresaltar = (d: Dia) => {
|
||||
setDiasResaltadosLocal(d, false);
|
||||
setDiasResaltados(d, (v) => v - 1);
|
||||
};
|
||||
setDiasResaltadosLocal(d, false)
|
||||
setDiasResaltados(d, (v) => v - 1)
|
||||
}
|
||||
|
||||
return [diasResaltadosLocal, fnResaltar, fnDesresaltar];
|
||||
};
|
||||
return [diasResaltadosLocal, fnResaltar, fnDesresaltar]
|
||||
}
|
||||
|
||||
export function FilaTabla(props: Props) {
|
||||
const [diasResaltadosLocal, fnResaltar, fnDesresaltar] = useDiasResaltados();
|
||||
const [diasResaltadosLocal, fnResaltar, fnDesresaltar] = useDiasResaltados()
|
||||
|
||||
const hora = props.hora;
|
||||
const data = props.data;
|
||||
const hora = props.hora
|
||||
const data = props.data
|
||||
|
||||
return (
|
||||
<div style={{position: "relative"}}>
|
||||
@ -133,10 +133,10 @@ export function FilaTabla(props: Props) {
|
||||
</div>
|
||||
<For each={dias}>
|
||||
{(dia) => {
|
||||
const diaStr = dia.substring(0, 2);
|
||||
const horaStr = hora.substring(0, 5);
|
||||
const diaStr = dia.substring(0, 2)
|
||||
const horaStr = hora.substring(0, 5)
|
||||
|
||||
const datos = data?.[horaStr]?.[diaStr] ?? [];
|
||||
const datos = data?.[horaStr]?.[diaStr] ?? []
|
||||
|
||||
return (
|
||||
<CeldaFila
|
||||
@ -146,11 +146,11 @@ export function FilaTabla(props: Props) {
|
||||
dia={dia}
|
||||
tablaObserver={props.tablaObserver}
|
||||
/>
|
||||
);
|
||||
)
|
||||
}}
|
||||
</For>
|
||||
<div className={css(e.filaBorde)} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
import { createMemo, createEffect, untrack } from "solid-js";
|
||||
import {createStore, SetStoreFunction, Store, produce} from "solid-js/store";
|
||||
import { DatosGrupo } from "../../../../types/DatosHorario";
|
||||
import { gruposSeleccionados } from "../../../../Store";
|
||||
import { createMemo, createEffect, untrack } from "solid-js"
|
||||
import {createStore, SetStoreFunction, Store, produce} from "solid-js/store"
|
||||
import { DatosGrupo } from "../types/DatosHorario"
|
||||
|
||||
const createMemoDefault = <T>(f: () => T) => createMemo<T>(
|
||||
f,
|
||||
@ -9,7 +8,7 @@ const createMemoDefault = <T>(f: () => T) => createMemo<T>(
|
||||
{
|
||||
equals: (x, y) => x === y,
|
||||
},
|
||||
);
|
||||
)
|
||||
|
||||
/**
|
||||
* - Normal
|
||||
@ -63,18 +62,17 @@ export class TablaObserver {
|
||||
curso: undefined,
|
||||
esLab: undefined,
|
||||
grupo: undefined,
|
||||
});
|
||||
this.resaltado = resaltado;
|
||||
this.setResaltado = setResaltado;
|
||||
})
|
||||
this.resaltado = resaltado
|
||||
this.setResaltado = setResaltado
|
||||
|
||||
const [seleccionado, setSeleccionado] = createStore<ISeleccionado>({});
|
||||
this.seleccionado = seleccionado;
|
||||
this.setSeleccionado = setSeleccionado;
|
||||
const [seleccionado, setSeleccionado] = createStore<ISeleccionado>({})
|
||||
this.seleccionado = seleccionado
|
||||
this.setSeleccionado = setSeleccionado
|
||||
}
|
||||
|
||||
/**
|
||||
* Crea un memo que indica el estado de la celda
|
||||
* @param id_laboratorio Id del laboratorio
|
||||
* @param anio El año
|
||||
* @param curso Curso abreviado
|
||||
* @param esLab Si es laboratorio
|
||||
@ -82,82 +80,89 @@ export class TablaObserver {
|
||||
* @param datosGrupo Contiene `seleccionado`, se usa ese valor reactivo
|
||||
*/
|
||||
private registrar(
|
||||
id_laboratorio: number,
|
||||
anio: string,
|
||||
curso: string,
|
||||
esLab: boolean,
|
||||
grupo: string,
|
||||
datosGrupo: DatosGrupo,
|
||||
): () => EstadoCelda {
|
||||
const resaltado = this.resaltado;
|
||||
const resaltado = this.resaltado
|
||||
const resaltadoMemo = createMemoDefault(() => {
|
||||
if (resaltado.anio === anio && resaltado.curso === curso) {
|
||||
if (resaltado.esLab === undefined) {
|
||||
return true;
|
||||
return true
|
||||
} else if (resaltado.esLab !== esLab) {
|
||||
return false;
|
||||
return false
|
||||
} else {
|
||||
if (resaltado.grupo === undefined) {
|
||||
return true;
|
||||
} else return resaltado.grupo === grupo;
|
||||
return true
|
||||
} else return resaltado.grupo === grupo
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
// Registrar curso en `seleccionado`
|
||||
this.setSeleccionado((obj: Store<ISeleccionado>) => {
|
||||
const nuevoObj = {...obj};
|
||||
const nuevoObj = {...obj}
|
||||
|
||||
if (!nuevoObj[anio]) {
|
||||
nuevoObj[anio] = {};
|
||||
nuevoObj[anio] = {}
|
||||
}
|
||||
|
||||
if (!nuevoObj[anio][curso]) {
|
||||
nuevoObj[anio][curso] = {
|
||||
Laboratorio: [],
|
||||
Teoria: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return nuevoObj;
|
||||
});
|
||||
return nuevoObj
|
||||
})
|
||||
|
||||
// Crear un effect para que cada vez que la celda se seleccione se actualize `seleccionado`
|
||||
createEffect(() => {
|
||||
const seleccionado = datosGrupo.seleccionado;
|
||||
const seleccionado = datosGrupo.seleccionado
|
||||
if (seleccionado) {
|
||||
this.setSeleccionado(anio, curso, esLab ? "Laboratorio" : "Teoria", (x) => [...x, grupo]);
|
||||
this.setSeleccionado(anio, curso, esLab ? "Laboratorio" : "Teoria", (x) => [...x, grupo])
|
||||
} else {
|
||||
this.setSeleccionado(anio, curso, esLab ? "Laboratorio" : "Teoria", (x) => x.filter((x) => x !== grupo));
|
||||
this.setSeleccionado(anio, curso, esLab ? "Laboratorio" : "Teoria", (x) => x.filter((x) => x !== grupo))
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
const seleccionadoMemo = createMemoDefault<EstadoSeleccionado>(() => ((gruposSeleccionados[id_laboratorio]) ? "Seleccionado" : "Normal"));
|
||||
const seleccionadoMemo = createMemoDefault<EstadoSeleccionado>(() => {
|
||||
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 => {
|
||||
const resaltado = resaltadoMemo();
|
||||
const seleccionado = seleccionadoMemo();
|
||||
const resaltado = resaltadoMemo()
|
||||
const seleccionado = seleccionadoMemo()
|
||||
|
||||
switch (seleccionado) {
|
||||
case "Normal": {
|
||||
return resaltado ? "Resaltado" : "Normal";
|
||||
return resaltado ? "Resaltado" : "Normal"
|
||||
}
|
||||
case "Oculto": {
|
||||
return resaltado ? "ResaltadoOculto" : "Oculto";
|
||||
return resaltado ? "ResaltadoOculto" : "Oculto"
|
||||
}
|
||||
case "Seleccionado": {
|
||||
return resaltado ? "ResaltadoSeleccionado" : "Seleccionado";
|
||||
return resaltado ? "ResaltadoSeleccionado" : "Seleccionado"
|
||||
}
|
||||
default: {
|
||||
let _: never;
|
||||
let _: never
|
||||
// eslint-disable-next-line prefer-const
|
||||
_ = seleccionado;
|
||||
return _;
|
||||
_ = seleccionado
|
||||
return _
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -167,13 +172,13 @@ export class TablaObserver {
|
||||
*/
|
||||
registrarConId(id: string, datosGrupo: DatosGrupo): () => EstadoCelda {
|
||||
if (this.memos[id]) {
|
||||
return this.memos[id];
|
||||
return this.memos[id]
|
||||
}
|
||||
|
||||
const [, anio, curso, lab, grupo] = id.split("_");
|
||||
const memo = this.registrar(datosGrupo.id_laboratorio, anio, curso, lab === "L", grupo, datosGrupo);
|
||||
this.memos[id] = memo;
|
||||
return memo;
|
||||
const [, anio, curso, lab, grupo] = id.split("_")
|
||||
const memo = this.registrar(anio, curso, lab === "L", grupo, datosGrupo)
|
||||
this.memos[id] = memo
|
||||
return memo
|
||||
}
|
||||
|
||||
/**
|
||||
@ -181,24 +186,24 @@ export class TablaObserver {
|
||||
* @param id Id a resaltar - YYYYMMDD_Año_Curso[\_Lab[_Grupo]]
|
||||
*/
|
||||
resaltar(id: string) {
|
||||
const [, anio, curso, lab, grupo] = id.split("_");
|
||||
const [, anio, curso, lab, grupo] = id.split("_")
|
||||
if (anio === undefined || curso === undefined) {
|
||||
console.error("Error al intentar resaltar celda: anio o curso son undefined:", anio, curso);
|
||||
return;
|
||||
console.error("Error al intentar resaltar celda: anio o curso son undefined:", anio, curso)
|
||||
return
|
||||
}
|
||||
|
||||
let esLab: boolean | undefined;
|
||||
let esLab: boolean | undefined
|
||||
if (lab === undefined) {
|
||||
esLab = undefined;
|
||||
esLab = undefined
|
||||
} else {
|
||||
esLab = lab === "L";
|
||||
esLab = lab === "L"
|
||||
}
|
||||
this.setResaltado({
|
||||
anio,
|
||||
curso,
|
||||
esLab,
|
||||
grupo,
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
quitarResaltado() {
|
||||
@ -207,10 +212,10 @@ export class TablaObserver {
|
||||
curso: undefined,
|
||||
esLab: undefined,
|
||||
grupo: undefined,
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
limpiar(id: string) {
|
||||
delete this.memos[id];
|
||||
delete this.memos[id]
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import {createStore, SetStoreFunction, Store} from "solid-js/store";
|
||||
import { Curso, ListaCursosUsuario } from "../../../../types/DatosHorario";
|
||||
import {createStore, SetStoreFunction, Store} from "solid-js/store"
|
||||
import { Curso, ListaCursosUsuario } from "../types/DatosHorario"
|
||||
|
||||
interface ReturnType {
|
||||
listaCursos: Store<ListaCursosUsuario>,
|
||||
@ -9,27 +9,33 @@ interface ReturnType {
|
||||
}
|
||||
|
||||
export const useListaCursos = (): ReturnType => {
|
||||
const [listaCursos, setListaCursos] = createStore<ListaCursosUsuario>({});
|
||||
const [listaCursos, setListaCursos] = createStore<ListaCursosUsuario>({
|
||||
sigIndice: 0,
|
||||
cursos: [],
|
||||
})
|
||||
|
||||
const agregarCursoALista = (curso: Curso): Curso => {
|
||||
// Si el horario ya se habia agregado, ocultarlo
|
||||
if (listaCursos[curso.nombre]) {
|
||||
setListaCursos(curso.nombre, "oculto", (x) => !x);
|
||||
return listaCursos[curso.nombre];
|
||||
const cursoActualIndex = listaCursos.cursos.findIndex((x) => x.nombre === curso.nombre)
|
||||
if (cursoActualIndex !== -1) {
|
||||
setListaCursos("cursos", cursoActualIndex, "oculto", (x) => !x)
|
||||
return listaCursos.cursos[cursoActualIndex]
|
||||
} else {
|
||||
setListaCursos(curso.nombre, curso);
|
||||
return listaCursos[curso.nombre];
|
||||
setListaCursos("cursos", listaCursos.sigIndice, curso)
|
||||
setListaCursos("sigIndice", (x) => x + 1)
|
||||
return listaCursos.cursos[listaCursos.sigIndice - 1]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const eliminarCursosDeLista = () => {
|
||||
setListaCursos({});
|
||||
};
|
||||
setListaCursos("cursos", [])
|
||||
setListaCursos("sigIndice", 0)
|
||||
}
|
||||
|
||||
return {
|
||||
listaCursos,
|
||||
setListaCursos,
|
||||
agregarCursoALista,
|
||||
eliminarCursosDeLista,
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import { StyleSheet } from "aphrodite";
|
||||
import { StyleSheet } from "aphrodite"
|
||||
|
||||
export const estilosGlobales = StyleSheet.create({
|
||||
contenedor: {
|
||||
@ -58,4 +58,4 @@ export const estilosGlobales = StyleSheet.create({
|
||||
padding: "0.25rem 0.35rem",
|
||||
borderRadius: "5px",
|
||||
},
|
||||
});
|
||||
})
|
||||
|
@ -1,31 +1,31 @@
|
||||
import { createSignal, JSX } from "solid-js";
|
||||
import { createSignal, JSX } from "solid-js"
|
||||
|
||||
export const useRouter = (): () => string => {
|
||||
let rutaPrevia = window.location.hash;
|
||||
let rutaPrevia = window.location.hash
|
||||
|
||||
if (rutaPrevia === "") {
|
||||
window.history.pushState({}, "Horarios UNSA", "#/");
|
||||
rutaPrevia = "/";
|
||||
window.history.pushState({}, "Horarios UNSA", "#/")
|
||||
rutaPrevia = "/"
|
||||
} else {
|
||||
rutaPrevia = rutaPrevia.substr(1);
|
||||
rutaPrevia = rutaPrevia.substr(1)
|
||||
}
|
||||
|
||||
const [rutaActual, setRutaActual] = createSignal(rutaPrevia);
|
||||
const [rutaActual, setRutaActual] = createSignal(rutaPrevia)
|
||||
|
||||
const fnEffect = () => {
|
||||
const nuevaRuta = window.location.hash.substr(1);
|
||||
setRutaActual(nuevaRuta);
|
||||
};
|
||||
const nuevaRuta = window.location.hash.substr(1)
|
||||
setRutaActual(nuevaRuta)
|
||||
}
|
||||
|
||||
window.addEventListener("hashchange", fnEffect);
|
||||
window.addEventListener("hashchange", fnEffect)
|
||||
|
||||
return rutaActual;
|
||||
};
|
||||
return rutaActual
|
||||
}
|
||||
|
||||
export function RouterLink(props: { to: string, className?: string, children: JSX.Element }) {
|
||||
return (
|
||||
<a className={props.className} href={`/#${props.to}`}>
|
||||
{props.children}
|
||||
</a>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { createSignal } from "solid-js";
|
||||
import { createStore } from "solid-js/store";
|
||||
|
||||
export type Dia = "Lunes" | "Martes" | "Miercoles" | "Jueves" | "Viernes";
|
||||
|
||||
@ -29,7 +28,6 @@ export const horasDescanso = [
|
||||
"15:40 - 15:50",
|
||||
"17:30 - 17:40",
|
||||
];
|
||||
export const SERVER_PATH = "https://matriculas.fly.dev/sistema";
|
||||
|
||||
const numImgGuardado = Number(localStorage.getItem("num-img") ?? "0");
|
||||
const tamanoLetraGuardado = Number(/* localStorage.getItem("tamano-letra") ?? */ "16");
|
||||
@ -37,5 +35,3 @@ const tamanoLetraGuardado = Number(/* localStorage.getItem("tamano-letra") ?? */
|
||||
export const [numWallpaper, setNumWallpaper] = createSignal(numImgGuardado);
|
||||
export const [tamanoLetra, setTamanoLetra] = createSignal(tamanoLetraGuardado);
|
||||
export const [isMobile, setIsMobile] = createSignal(screen.width < 500);
|
||||
|
||||
export const [gruposSeleccionados, setGruposSeleccionados] = createStore<{[k: number]: boolean}>({});
|
||||
|
@ -2,8 +2,8 @@ import { BarraSuperior } from "../BarraSuperior";
|
||||
import { estilosGlobales } from "../Estilos";
|
||||
import { StyleSheet, css } from "aphrodite";
|
||||
import { Separador } from "../Separador";
|
||||
import { Tabla } from "./pc/Sistemas/ContenedorHorarios/Tabla";
|
||||
import { TablaObserver } from "./pc/Sistemas/ContenedorHorarios/TablaObserver";
|
||||
import { Tabla } from "../ContenedorHorarios/Tabla";
|
||||
import { TablaObserver } from "../ContenedorHorarios/TablaObserver";
|
||||
import { Curso, Cursos } from "../types/DatosHorario";
|
||||
import { For, createMemo } from "solid-js";
|
||||
import {createStore} from "solid-js/store";
|
||||
|
@ -1,10 +1,8 @@
|
||||
import { estilosGlobales } from "../Estilos";
|
||||
import { StyleSheet, css } from "aphrodite/no-important";
|
||||
import { RouterLink } from "../Router";
|
||||
import { batch, createSignal, Show } from "solid-js";
|
||||
import { isMobile, setGruposSeleccionados } from "../Store";
|
||||
import { MobileIndex } from "./MobileIndex";
|
||||
import { loginFn } from "../API/Login";
|
||||
import { Show } from "solid-js";
|
||||
import { isMobile } from "../Store";
|
||||
|
||||
const e = StyleSheet.create({
|
||||
contenedorGlobal: {
|
||||
@ -31,51 +29,55 @@ const e = StyleSheet.create({
|
||||
verticalAlign: "bottom",
|
||||
marginRight: "0.5rem",
|
||||
},
|
||||
inputCorreo: {
|
||||
width: "100%",
|
||||
backgroundColor: "rgba(159,159,159,0.44)",
|
||||
border: "none",
|
||||
borderBottom: "solid 2px var(--color-texto)",
|
||||
padding: "0.5rem 1rem",
|
||||
boxSizing: "border-box",
|
||||
marginTop: "1rem",
|
||||
borderRadius: "5px",
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
export function Index() {
|
||||
const [msgErrorVisible, setMsgErrorVisible] = createSignal(false);
|
||||
const inputElement = <input class={css(e.inputCorreo)} type="email" required placeholder="correo@unsa.edu.pe" />;
|
||||
|
||||
const login = async(ev: Event) => {
|
||||
ev.preventDefault();
|
||||
const email = (inputElement as HTMLInputElement).value;
|
||||
const response = await loginFn({correo_usuario: email});
|
||||
|
||||
if (response === null) {
|
||||
setMsgErrorVisible(true);
|
||||
setTimeout(() => setMsgErrorVisible(false), 2500);
|
||||
} else if (!response.matriculas || response.matriculas.length === 0) {
|
||||
localStorage.setItem("correo", email);
|
||||
window.location.href = "#/pc/seleccion-cursos/";
|
||||
} else if (response.matriculas.length > 0) {
|
||||
localStorage.setItem("correo", email);
|
||||
batch(() => {
|
||||
for (const id_lab of response.matriculas) {
|
||||
setGruposSeleccionados(id_lab, true);
|
||||
}
|
||||
function MobileIndex() {
|
||||
const s = StyleSheet.create({
|
||||
boton: {
|
||||
backgroundColor: "var(--color-primario)",
|
||||
color: "white",
|
||||
padding: "1rem 5rem",
|
||||
borderRadius: "25px",
|
||||
margin: "1.5rem 0",
|
||||
boxShadow: "2px 2px 2px 0 gray",
|
||||
cursor: "pointer",
|
||||
},
|
||||
entrada: {
|
||||
borderTop: "none",
|
||||
borderRight: "none",
|
||||
borderLeft: "none",
|
||||
borderBottom: "solid 2px gray",
|
||||
padding: "0.75rem 1rem",
|
||||
},
|
||||
});
|
||||
window.location.href = "#/pc/ver-matricula/";
|
||||
}
|
||||
|
||||
const inputElement = <input type="email" placeholder="Correo electronico" className={css(s.entrada)} />;
|
||||
|
||||
const login = () => {
|
||||
console.log((inputElement as HTMLInputElement).value);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={css(e.contenedorGlobal)}>
|
||||
<div style="text-align: center;">
|
||||
<h1>Iniciar sesión</h1>
|
||||
<br />
|
||||
<br />
|
||||
{inputElement}
|
||||
<br />
|
||||
<button className={css(s.boton)} onClick={login}>Iniciar Sesion</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function Index() {
|
||||
return (
|
||||
<>
|
||||
<Show when={!isMobile()}>
|
||||
<div class={css(e.contenedorGlobal)}>
|
||||
<div class={css(e.cont)}>
|
||||
<div class={css(estilosGlobales.contenedor, estilosGlobales.inlineBlock, e.cont)}>
|
||||
<div className={css(e.contenedorGlobal)}>
|
||||
<div className={css(e.cont)}>
|
||||
<div className={css(estilosGlobales.contenedor, estilosGlobales.inlineBlock, e.cont)}>
|
||||
<h1 style={{
|
||||
"text-align": "center",
|
||||
"font-size": "1.75rem",
|
||||
@ -83,26 +85,43 @@ export function Index() {
|
||||
>
|
||||
Horarios UNSA
|
||||
</h1>
|
||||
<p class={css(e.parrafo)}>
|
||||
Inicia sesión con tu correo institucional.
|
||||
<br />
|
||||
{inputElement}
|
||||
<p className={css(e.parrafo)}>
|
||||
Esta página te permite crear tu horario fácilmente, sin importar de que
|
||||
año son los cursos.
|
||||
</p>
|
||||
<p className={css(e.parrafo)}>
|
||||
Por ahora solo está disponible para ing. de sistemas. Proximamente se habilitarán
|
||||
otras carreras.
|
||||
</p>
|
||||
<p className={css(e.parrafo)}>
|
||||
Se recomienda usar un computador/laptop y un navegador actualizado (Firefox, Chrome,
|
||||
Qutebrowser).
|
||||
</p>
|
||||
<span style={msgErrorVisible() ? "opacity: 1; color: red;" : "opacity: 0;"}>
|
||||
El correo es invalido
|
||||
</span>
|
||||
</div>
|
||||
<button onClick={login} class={css(estilosGlobales.contenedor, estilosGlobales.contenedorCursor, e.botonAccion)}>
|
||||
Iniciar sesion
|
||||
<RouterLink
|
||||
to={"/sistemas/"}
|
||||
className={css(estilosGlobales.contenedor, estilosGlobales.contenedorCursor, e.botonAccion)}
|
||||
>
|
||||
Ing. de Sistemas
|
||||
</RouterLink>
|
||||
{/*
|
||||
<button disabled className={css(estilosGlobales.contenedor, estilosGlobales.contenedorCursor, e.botonAccion)}>
|
||||
Otras carreras
|
||||
</button>
|
||||
<br />
|
||||
<br />
|
||||
*/}
|
||||
<RouterLink
|
||||
to={"/editor/"}
|
||||
className={css(estilosGlobales.contenedor, estilosGlobales.contenedorCursor, e.botonAccion)}
|
||||
>
|
||||
<i className={`${css(e.iconoGitHub)} ph-pencil`} />
|
||||
Editor
|
||||
</RouterLink>
|
||||
<a
|
||||
class={css(estilosGlobales.contenedor, estilosGlobales.contenedorCursor, e.botonAccion)}
|
||||
className={css(estilosGlobales.contenedor, estilosGlobales.contenedorCursor, e.botonAccion)}
|
||||
href="https://github.com/Araozu/horarios-unsa-2/"
|
||||
target="_blank"
|
||||
>
|
||||
<i class={`${css(e.iconoGitHub)} ph-code`} />
|
||||
<i className={`${css(e.iconoGitHub)} ph-code`} />
|
||||
Código fuente en GitHub
|
||||
</a>
|
||||
</div>
|
||||
|
49
src/Views/Main.tsx
Normal file
49
src/Views/Main.tsx
Normal file
@ -0,0 +1,49 @@
|
||||
import { BarraSuperior } from "../BarraSuperior"
|
||||
import { ContenedorHorarios } from "../ContenedorHorarios/ContenedorHorarios"
|
||||
import { Show, createSignal } from "solid-js"
|
||||
import { css } from "aphrodite"
|
||||
import { estilosGlobales } from "../Estilos"
|
||||
import { Creditos } from "../Creditos"
|
||||
import { Separador } from "../Separador"
|
||||
|
||||
export function Main() {
|
||||
/// @ts-ignore
|
||||
const soportaBackdropFilter = document.body.style.backdropFilter !== undefined
|
||||
const mostrarMensajeBackdropFilterRaw = !localStorage.getItem("mensaje-backdrop-filter-oculto")
|
||||
|
||||
const [mostrarMensajeBackdropFilter, setMostrarMensaje] = createSignal(mostrarMensajeBackdropFilterRaw)
|
||||
|
||||
const ocultarMensajeBackdropFilter = () => {
|
||||
setMostrarMensaje(false)
|
||||
localStorage.setItem("mensaje-backdrop-filter-oculto", "true")
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<BarraSuperior />
|
||||
<Show when={!soportaBackdropFilter && mostrarMensajeBackdropFilter()}>
|
||||
<div className={css(estilosGlobales.contenedor)}>
|
||||
Tu navegador no soporta "backdrop-filter". Este es solo un efecto
|
||||
visual, no afecta la funcionalidad de la página.
|
||||
<span
|
||||
className={css(estilosGlobales.contenedorCursor, estilosGlobales.contenedorCursorSoft)}
|
||||
onClick={ocultarMensajeBackdropFilter}
|
||||
>
|
||||
No volver a mostrar.
|
||||
</span>
|
||||
</div>
|
||||
</Show>
|
||||
{/*
|
||||
<div className={css(estilosGlobales.contenedor)}>
|
||||
Solo teoria por ahora. Actualizado el 2021/03/28. Fuente:
|
||||
<a className={css(estilosGlobales.linkGenerico)} target="_blank" href="https://www.facebook.com/Escuela-Profesional-de-Ingenieria-de-Sistemas-171720913528/photos/pcb.10159175240878529/10159175239403529">
|
||||
Página de Facebook de la escuela.
|
||||
</a>
|
||||
</div>
|
||||
*/}
|
||||
<Separador />
|
||||
<ContenedorHorarios />
|
||||
<Creditos />
|
||||
</div>
|
||||
)
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
import { css, StyleSheet } from "aphrodite/no-important";
|
||||
import { batch, createSignal } from "solid-js";
|
||||
import { SERVER_PATH, setGruposSeleccionados } from "../Store";
|
||||
import { loginFn } from "../API/Login";
|
||||
|
||||
const e = StyleSheet.create({
|
||||
contenedorGlobal: {
|
||||
width: "100vw",
|
||||
height: "100vh",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
},
|
||||
cont: {
|
||||
width: "30rem",
|
||||
},
|
||||
parrafo: {
|
||||
textAlign: "justify",
|
||||
lineHeight: "1.4rem",
|
||||
},
|
||||
botonAccion: {
|
||||
width: "30rem",
|
||||
display: "inline-block",
|
||||
textAlign: "center",
|
||||
},
|
||||
iconoGitHub: {
|
||||
fontSize: "1.25rem",
|
||||
verticalAlign: "bottom",
|
||||
marginRight: "0.5rem",
|
||||
},
|
||||
});
|
||||
|
||||
export function MobileIndex() {
|
||||
const s = StyleSheet.create({
|
||||
boton: {
|
||||
backgroundColor: "var(--color-primario)",
|
||||
color: "white",
|
||||
padding: "1rem 5rem",
|
||||
borderRadius: "25px",
|
||||
margin: "1.5rem 0",
|
||||
boxShadow: "2px 2px 2px 0 gray",
|
||||
cursor: "pointer",
|
||||
},
|
||||
entrada: {
|
||||
borderTop: "none",
|
||||
borderRight: "none",
|
||||
borderLeft: "none",
|
||||
borderBottom: "solid 2px gray",
|
||||
padding: "0.75rem 1rem",
|
||||
},
|
||||
});
|
||||
const [msgErrorVisible, setMsgErrorVisible] = createSignal(false);
|
||||
|
||||
const inputElement = <input required type="email" placeholder="correo@unsa.edu.pe" class={css(s.entrada)} />;
|
||||
|
||||
const login = async(ev: Event) => {
|
||||
ev.preventDefault();
|
||||
const email = (inputElement as HTMLInputElement).value;
|
||||
const response = await loginFn({correo_usuario: email});
|
||||
|
||||
if (response === null) {
|
||||
setMsgErrorVisible(true);
|
||||
setTimeout(() => setMsgErrorVisible(false), 2500);
|
||||
} else {
|
||||
localStorage.setItem("correo", email);
|
||||
window.location.href = "#/seleccion-cursos/";
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div class={css(e.contenedorGlobal)}>
|
||||
<div style="text-align: center;">
|
||||
<h1>Iniciar sesión</h1>
|
||||
<br />
|
||||
<br />
|
||||
<form onSubmit={(ev) => login(ev)}>
|
||||
{inputElement}
|
||||
<br />
|
||||
<button type="submit" class={css(s.boton)}>Iniciar Sesion</button>
|
||||
</form>
|
||||
<span style={{opacity: msgErrorVisible() ? 1 : 0}}>El correo es invalido</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
import { TopBar } from "./SistemasMovil/TopBar";
|
||||
import { StyleSheet, css } from "aphrodite/no-important";
|
||||
import { Card } from "../components/Card";
|
||||
import { createSignal, For } from "solid-js";
|
||||
import { getAllListaCursos, RespuestaListaCursos } from "../API/ListaCursos";
|
||||
import { Button } from "../components/Button";
|
||||
|
||||
const s = StyleSheet.create({
|
||||
checkbox: {
|
||||
width: "1.25rem",
|
||||
height: "1.25rem",
|
||||
margin: "0 0.5rem",
|
||||
},
|
||||
grid: {
|
||||
display: "grid",
|
||||
gridTemplateColumns: "3rem auto",
|
||||
gridRowGap: "1rem",
|
||||
},
|
||||
});
|
||||
|
||||
export function SeleccionCursos() {
|
||||
const [cursos, setCursos] = createSignal<RespuestaListaCursos>({});
|
||||
const [msjErr, setMsjError] = createSignal(false);
|
||||
|
||||
// Recuperar cursos de back
|
||||
(async() => setCursos(await getAllListaCursos()))();
|
||||
|
||||
const submit = (ev: Event) => {
|
||||
ev.preventDefault();
|
||||
const form = ev.target as HTMLFormElement;
|
||||
// Los checkboxes
|
||||
const elements = form.elements;
|
||||
const idsAEnviar: Array<string> = [];
|
||||
for (let i = 0; i < elements.length; i += 1) {
|
||||
const inputBox = elements[i] as HTMLInputElement;
|
||||
if (inputBox.checked) {
|
||||
idsAEnviar.push(inputBox.value);
|
||||
}
|
||||
}
|
||||
|
||||
if (idsAEnviar.length === 0) {
|
||||
setMsjError(true);
|
||||
setTimeout(() => setMsjError(false), 2500);
|
||||
return;
|
||||
}
|
||||
|
||||
// Almacenar en localStorage
|
||||
localStorage.setItem("cursos-seleccionados", JSON.stringify(idsAEnviar));
|
||||
// Ir a sig pantalla
|
||||
window.location.href = "#/sistemas-movil/";
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<TopBar tituloBarra="Selección de Cursos" />
|
||||
|
||||
<Card>
|
||||
<p>Escoge los cursos en los que matricularte</p>
|
||||
</Card>
|
||||
<form onSubmit={(ev) => submit(ev)}>
|
||||
<For each={Object.entries(cursos())}>
|
||||
{([nombreAnio, infoCurso]) => (
|
||||
<Card>
|
||||
<h2>{nombreAnio} año</h2>
|
||||
<div class={css(s.grid)}>
|
||||
<For each={infoCurso}>
|
||||
{(curso) => (
|
||||
<>
|
||||
<input
|
||||
type="checkbox"
|
||||
value={curso.id_curso}
|
||||
class={css(s.checkbox)}
|
||||
/>
|
||||
<span>{curso.nombre_curso}</span>
|
||||
</>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
</Card>
|
||||
)}
|
||||
</For>
|
||||
<div style="text-align: center">
|
||||
<span style={msjErr() ? "opacity: 1; color: red" : "opacity: 0"}>Selecciona al menos 1 curso</span>
|
||||
<br />
|
||||
<Button texto={"Continuar"} />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,167 +0,0 @@
|
||||
import { TopBar } from "./SistemasMovil/TopBar";
|
||||
import { GrupoDia, Table, TableInput } from "./SistemasMovil/Table";
|
||||
import { getHorarios, Horario, ListaCursosCompleto } from "../API/CargaHorarios";
|
||||
import { createSignal } from "solid-js";
|
||||
import { generarMapaCeldas } from "./SistemasMovil/mapaCeldas";
|
||||
import { Button } from "../components/Button";
|
||||
import { gruposSeleccionados, SERVER_PATH } from "../Store";
|
||||
|
||||
export function SistemasMovil() {
|
||||
const [rawData, setRawData] = createSignal<ListaCursosCompleto>([]);
|
||||
|
||||
// Obtener cursos seleccionados del servidor
|
||||
(async() => {
|
||||
const cursos: Array<string> = JSON.parse(localStorage.getItem("cursos-seleccionados") ?? "[]");
|
||||
const data = await getHorarios({
|
||||
cursos: cursos.map((x) => parseInt(x, 10)),
|
||||
});
|
||||
setRawData(data);
|
||||
})();
|
||||
|
||||
const matricular = async() => {
|
||||
const laboratoriosAMatricular = Object.entries(gruposSeleccionados)
|
||||
.filter((x) => x[1] === true)
|
||||
.map((x) => x[0]);
|
||||
|
||||
const response = await fetch(`${SERVER_PATH}/matricula`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
correo_usuario: localStorage.getItem("correo"),
|
||||
horarios: laboratoriosAMatricular,
|
||||
}),
|
||||
});
|
||||
if (response.ok) {
|
||||
window.location.href = "#/ver-matricula/";
|
||||
} else {
|
||||
alert("No se pudo procesar la matricula");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<TopBar tituloBarra="Mi Horario" />
|
||||
<Table datos={transformar(rawData())} />
|
||||
<hr />
|
||||
<div style="text-align: center;">
|
||||
<Button texto={"Matricular"} onClick={matricular} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function transformar(input: ListaCursosCompleto): TableInput {
|
||||
const data: TableInput = {
|
||||
lunes: [],
|
||||
martes: [],
|
||||
miercoles: [],
|
||||
jueves: [],
|
||||
viernes: [],
|
||||
};
|
||||
|
||||
// Organizar por dias
|
||||
for (const curso of input) {
|
||||
for (const lab of curso.laboratorios) {
|
||||
for (const horas of lab.horarios) {
|
||||
const dia = horas.dia;
|
||||
const [idx, nroHoras] = infoDiaAOffsets(horas.hora_inicio, horas.hora_fin);
|
||||
const datos = {
|
||||
id_horario: horas.id_horario,
|
||||
id_laboratorio: lab.id_laboratorio,
|
||||
abreviado: curso.abreviado,
|
||||
grupo: lab.grupo,
|
||||
offsetVertical: idx,
|
||||
nroHoras: nroHoras,
|
||||
offsetHorizontal: 0,
|
||||
fraccion: 0,
|
||||
};
|
||||
|
||||
if (dia === "Lunes") {
|
||||
data.lunes.push(datos);
|
||||
} else if (dia === "Martes") {
|
||||
data.martes.push(datos);
|
||||
} else if (dia === "Miercoles") {
|
||||
data.miercoles.push(datos);
|
||||
} else if (dia === "Jueves") {
|
||||
data.jueves.push(datos);
|
||||
} else if (dia === "Viernes") {
|
||||
data.viernes.push(datos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Procesar cada dia y devolver
|
||||
return {
|
||||
lunes: generarMapaCeldas(data.lunes),
|
||||
martes: generarMapaCeldas(data.martes),
|
||||
miercoles: generarMapaCeldas(data.miercoles),
|
||||
jueves: generarMapaCeldas(data.jueves),
|
||||
viernes: generarMapaCeldas(data.viernes),
|
||||
};
|
||||
}
|
||||
|
||||
const horasStr = ["0700","0750","0850","0940","1040","1130","1220","1310","1400",
|
||||
"1450","1550","1640","1740","1830","1920","2010","2100","2150"];
|
||||
|
||||
const horas = [
|
||||
700,
|
||||
750,
|
||||
850,
|
||||
940,
|
||||
1040,
|
||||
1130,
|
||||
1220,
|
||||
1310,
|
||||
1400,
|
||||
1450,
|
||||
1550,
|
||||
1640,
|
||||
1740,
|
||||
1830,
|
||||
1920,
|
||||
2010,
|
||||
2100,
|
||||
2150,
|
||||
];
|
||||
|
||||
/**
|
||||
* Convierte horas en texto a offsets
|
||||
*/
|
||||
// Ejm: 0700, 0850 -> 0, 2
|
||||
function infoDiaAOffsets(horaInicio: string, horaFinal: string): [number, number] {
|
||||
const inicio = parseInt(horaInicio, 10);
|
||||
const final = parseInt(horaFinal, 10);
|
||||
|
||||
const idxInicio = horas.findIndex((x) => x === inicio);
|
||||
let nroHoras = 0;
|
||||
|
||||
for (let i = idxInicio; i < horas.length; i += 1) {
|
||||
if (final > horas[i]) {
|
||||
nroHoras += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return [idxInicio, nroHoras];
|
||||
}
|
||||
|
||||
// inicio: 1740 fin 2010 -> 1740,1830,1920
|
||||
export function infoDiaAListaHoras(horas: Array<Horario>): Array<string> {
|
||||
const horasFin: Array<string> = [];
|
||||
|
||||
for (const grupoHoras of horas) {
|
||||
const [idx, cantidad] = infoDiaAOffsets(grupoHoras.hora_inicio, grupoHoras.hora_fin);
|
||||
const strDia = grupoHoras.dia.substring(0, 2);
|
||||
|
||||
for (let i = 0; i < cantidad; i += 1) {
|
||||
horasFin.push(`${strDia}${horasStr[idx + i]}`);
|
||||
}
|
||||
}
|
||||
|
||||
return horasFin;
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
import { css, StyleSheet } from "aphrodite/no-important";
|
||||
import { GrupoDia } from "./Table";
|
||||
import { gruposSeleccionados, setGruposSeleccionados } from "../../Store";
|
||||
|
||||
const colores: Array<[string, string]> = [
|
||||
["#FFEBEE", "#F44336"],
|
||||
["#F3E5F5", "#9C27B0"],
|
||||
["#E8EAF6", "#3F51B5"],
|
||||
["#E1F5FE", "#03A9F4"],
|
||||
["#E0F2F1", "#009688"],
|
||||
["#F1F8E9", "#689F38"],
|
||||
["#FFF9C4", "#FBC02D"],
|
||||
["#FBE9E7", "#F4511E"],
|
||||
["#EFEBE9", "#795548"],
|
||||
];
|
||||
|
||||
export function Grupo(props: { data: GrupoDia }) {
|
||||
const [colorDesactivado, colorActivado] = colores[props.data.id_laboratorio % 9];
|
||||
const ss = StyleSheet.create({
|
||||
button: {
|
||||
display: "inline-block",
|
||||
padding: "0.2rem 1rem",
|
||||
textAlign: "left",
|
||||
borderRadius: "10px",
|
||||
border: `solid 2px ${colorActivado}`,
|
||||
position: "absolute",
|
||||
},
|
||||
});
|
||||
|
||||
const estiloFondo = () => {
|
||||
if (gruposSeleccionados[props.data.id_laboratorio]) {
|
||||
return `background-color: ${colorActivado}; color: white; font-weight: 600;`;
|
||||
} else {
|
||||
return `background-color: ${colorDesactivado};`;
|
||||
}
|
||||
};
|
||||
|
||||
const estilo = () => {
|
||||
const fraccion = props.data.fraccion;
|
||||
const offsetHorizontal = props.data.offsetHorizontal;
|
||||
const offsetVertical = props.data.offsetVertical;
|
||||
const nroHoras = props.data.nroHoras;
|
||||
|
||||
return `left: calc((43vw / ${fraccion}) * ${offsetHorizontal}); top: ${offsetVertical * 3}rem;` +
|
||||
`height: ${nroHoras * 3}rem; width: calc(100% / ${fraccion});`;
|
||||
};
|
||||
|
||||
const handleClick = () => {
|
||||
setGruposSeleccionados(props.data.id_laboratorio, (x) => !x);
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className={css(ss.button)}
|
||||
style={`${estilo()}${estiloFondo()}`}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{props.data.abreviado}
|
||||
<br />
|
||||
{props.data.grupo}
|
||||
</button>
|
||||
);
|
||||
}
|
@ -1,100 +0,0 @@
|
||||
import { StyleSheet, css } from "aphrodite/no-important";
|
||||
import { createSignal, For } from "solid-js";
|
||||
import { Swiper, SwiperSlide } from "swiper/solid";
|
||||
import { horas } from "../../Store";
|
||||
|
||||
import "swiper/css";
|
||||
import { Grupo } from "./Grupo";
|
||||
|
||||
const s = StyleSheet.create({
|
||||
container: {
|
||||
display: "grid",
|
||||
gridTemplateColumns: "13vw 1fr",
|
||||
textAlign: "center",
|
||||
fontSize: "0.9rem",
|
||||
},
|
||||
tableIndex: {
|
||||
backgroundColor: "rgb(108,67,75)",
|
||||
color: "white",
|
||||
padding: "0.5rem 0.25rem",
|
||||
textAlign: "center",
|
||||
width: "42vw",
|
||||
},
|
||||
columna: {
|
||||
borderRight: "solid 2px var(--color-borde)",
|
||||
},
|
||||
celdaHora: {
|
||||
position: "relative",
|
||||
top: "-0.75rem",
|
||||
height: "3rem",
|
||||
},
|
||||
});
|
||||
|
||||
export type GrupoDia = {
|
||||
id_horario: number,
|
||||
id_laboratorio: number,
|
||||
abreviado: string,
|
||||
grupo: string,
|
||||
offsetVertical: number, // 07:00 -> 0, 07:50 -> 1
|
||||
nroHoras: number,
|
||||
offsetHorizontal: number, // 0, 1, 2
|
||||
fraccion: number, // por cuanto dividir la celda. 1, 2, 3, ...
|
||||
}
|
||||
|
||||
function Dia(props: { dia: string, grupos: Array<GrupoDia> }) {
|
||||
const ss = StyleSheet.create({
|
||||
contenedorDia: {
|
||||
position: "relative",
|
||||
width: "42vw",
|
||||
},
|
||||
});
|
||||
return (
|
||||
<div className={css(s.columna)}>
|
||||
<div className={css(s.tableIndex)}>{props.dia}</div>
|
||||
<div className={css(ss.contenedorDia)}>
|
||||
<For each={props.grupos}>
|
||||
{(grupo) => (
|
||||
<Grupo data={grupo} />
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export type TableInput = {
|
||||
lunes: Array<GrupoDia>,
|
||||
martes: Array<GrupoDia>,
|
||||
miercoles: Array<GrupoDia>,
|
||||
jueves: Array<GrupoDia>,
|
||||
viernes: Array<GrupoDia>,
|
||||
}
|
||||
|
||||
export function Table(props: { datos: TableInput }) {
|
||||
const lunes = <Dia dia={"Lunes"} grupos={props.datos.lunes} />;
|
||||
const martes = <Dia dia={"Martes"} grupos={props.datos.martes} />;
|
||||
const miercoles = <Dia dia={"Miercoles"} grupos={props.datos.miercoles} />;
|
||||
const jueves = <Dia dia={"Jueves"} grupos={props.datos.jueves} />;
|
||||
const viernes = <Dia dia={"Viernes"} grupos={props.datos.viernes} />;
|
||||
|
||||
return (
|
||||
<div className={css(s.container)}>
|
||||
<div className={css(s.columna)}>
|
||||
<div className={css(s.tableIndex)} style="border: none; background: transparent;"> </div>
|
||||
<For each={horas}>
|
||||
{(hora) => <div className={css(s.celdaHora)}>{hora.substring(0, 5)}</div>}
|
||||
</For>
|
||||
</div>
|
||||
<Swiper
|
||||
slidesPerView={2}
|
||||
style="width: 86vw"
|
||||
>
|
||||
<SwiperSlide>{lunes}</SwiperSlide>
|
||||
<SwiperSlide>{martes}</SwiperSlide>
|
||||
<SwiperSlide>{miercoles}</SwiperSlide>
|
||||
<SwiperSlide>{jueves}</SwiperSlide>
|
||||
<SwiperSlide>{viernes}</SwiperSlide>
|
||||
</Swiper>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
import { StyleSheet, css } from "aphrodite/no-important";
|
||||
|
||||
const s = StyleSheet.create({
|
||||
bar: {
|
||||
backgroundColor: "var(--color-primario)",
|
||||
color: "white",
|
||||
height: "3.5rem",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
position: "sticky",
|
||||
top: 0,
|
||||
zIndex: 100,
|
||||
},
|
||||
icon: {
|
||||
display: "inline-block",
|
||||
color: "white",
|
||||
fontSize: "1.5rem",
|
||||
verticalAlign: "bottom",
|
||||
cursor: "pointer",
|
||||
height: "1.5rem",
|
||||
padding: "0 0.5rem",
|
||||
},
|
||||
barLabel: {
|
||||
color: "white",
|
||||
padding: "0 1rem",
|
||||
fontWeight: 500,
|
||||
fontSize: "1.25rem",
|
||||
},
|
||||
});
|
||||
|
||||
export function TopBar(props: {tituloBarra: string}) {
|
||||
return (
|
||||
<nav className={css(s.bar)}>
|
||||
<button>
|
||||
<i
|
||||
className={`ph-list ${css(s.icon)}`}
|
||||
title={"Cambiar imagen de fondo"}
|
||||
/>
|
||||
</button>
|
||||
<p className={css(s.barLabel)}>{props.tituloBarra}</p>
|
||||
</nav>
|
||||
);
|
||||
}
|
@ -1,89 +0,0 @@
|
||||
import { GrupoDia } from "./Table";
|
||||
|
||||
export class MapaCeldas {
|
||||
// Almacena referencias a input
|
||||
private mapa: Map<number, Map<number, null>> = new Map();
|
||||
|
||||
private disponible(nroFila: number, nroColumna: number): boolean {
|
||||
if (!this.mapa.has(nroFila)) return true;
|
||||
|
||||
const fila = this.mapa.get(nroFila)!;
|
||||
|
||||
return fila.has(nroColumna) === false;
|
||||
}
|
||||
|
||||
private obtenerFilaOCrear(nro: number): Map<number, null> {
|
||||
if (!this.mapa.has(nro)) {
|
||||
const m = new Map<number, null>();
|
||||
this.mapa.set(nro, m);
|
||||
return m;
|
||||
}
|
||||
|
||||
return this.mapa.get(nro)!;
|
||||
}
|
||||
|
||||
// Devuelve el offset
|
||||
public solicitar(inicio: number, cantidad: number): number {
|
||||
const filas = [];
|
||||
for (let i = 0; i < cantidad; i += 1) filas.push(inicio + i);
|
||||
|
||||
for (let offsetActual = 0; offsetActual < 8; offsetActual += 1) {
|
||||
let todasCeldasDisponibles = true;
|
||||
for (const fila of filas) {
|
||||
if (!this.disponible(fila, offsetActual)) {
|
||||
todasCeldasDisponibles = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (todasCeldasDisponibles) {
|
||||
// Crear estas celdas y almacenar
|
||||
filas.forEach((nroFila) => {
|
||||
const fila = this.obtenerFilaOCrear(nroFila);
|
||||
fila.set(offsetActual, null);
|
||||
});
|
||||
|
||||
// Devolver nro de offset
|
||||
return offsetActual;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error("Limite de celdas alcanzado");
|
||||
}
|
||||
|
||||
public generarFraccion(nroFila: number, nroColumna: number, cantidad: number): number {
|
||||
let fraccionActual = 1;
|
||||
for (let i = 0; i < cantidad; i += 1) {
|
||||
const nroFilaActual = nroFila + i;
|
||||
const filaActual = this.mapa.get(nroFilaActual)!;
|
||||
const numeroColumnas = filaActual.size;
|
||||
if (numeroColumnas > fraccionActual) {
|
||||
fraccionActual = numeroColumnas;
|
||||
}
|
||||
}
|
||||
|
||||
return fraccionActual;
|
||||
}
|
||||
}
|
||||
|
||||
export function generarMapaCeldas(entrada: Readonly<Array<GrupoDia>>): Array<GrupoDia> {
|
||||
const mapa = new MapaCeldas();
|
||||
const salida: Array<GrupoDia> = [];
|
||||
|
||||
// Obtener los offsets de cada curso
|
||||
for (const input of entrada) {
|
||||
const offset = mapa.solicitar(input.offsetVertical, input.nroHoras);
|
||||
salida.push({
|
||||
...input,
|
||||
offsetHorizontal: offset,
|
||||
fraccion: -1,
|
||||
});
|
||||
}
|
||||
|
||||
// Generar las fracciones de cada curso
|
||||
for (const output of salida) {
|
||||
output.fraccion = mapa.generarFraccion(output.offsetVertical, output.offsetHorizontal, output.nroHoras);
|
||||
}
|
||||
|
||||
return salida;
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
import { TopBar } from "./SistemasMovil/TopBar";
|
||||
import { Card } from "../components/Card";
|
||||
import { createSignal, For } from "solid-js";
|
||||
import { getMatricula, InfoMatricula } from "../API/VerMatricula";
|
||||
import { gruposSeleccionados } from "../Store";
|
||||
|
||||
export function VerMatricula() {
|
||||
const [infoMatriculas, setInfoMatriculas] = createSignal<Array<InfoMatricula>>([]);
|
||||
|
||||
(async() => {
|
||||
const laboratorios = Object.entries(gruposSeleccionados)
|
||||
.filter((x) => x[1] === true)
|
||||
.map((x) => parseInt(x[0], 10));
|
||||
setInfoMatriculas(await getMatricula({matriculas: laboratorios}));
|
||||
})();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<TopBar tituloBarra={"Ver Matricula"} />
|
||||
<Card>
|
||||
<h2>Tu matrícula</h2>
|
||||
<For each={infoMatriculas()}>
|
||||
{(matricula) => (
|
||||
<div>
|
||||
<h3>{matricula.nombre_curso}</h3>
|
||||
<p>Grupo: {matricula.grupo}</p>
|
||||
<p>Docente: {matricula.docente}</p>
|
||||
</div>
|
||||
)}
|
||||
</For>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,135 +0,0 @@
|
||||
import { css, StyleSheet } from "aphrodite/no-important";
|
||||
import { estilosGlobales } from "../../Estilos";
|
||||
import { createSignal, For } from "solid-js";
|
||||
import { getAllListaCursos, RespuestaListaCursos } from "../../API/ListaCursos";
|
||||
|
||||
const e = StyleSheet.create({
|
||||
contenedorGlobal: {
|
||||
width: "100vw",
|
||||
height: "100vh",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
},
|
||||
cont: {
|
||||
width: "30rem",
|
||||
},
|
||||
parrafo: {
|
||||
textAlign: "justify",
|
||||
lineHeight: "1.4rem",
|
||||
},
|
||||
botonAccion: {
|
||||
width: "30rem",
|
||||
display: "inline-block",
|
||||
textAlign: "center",
|
||||
},
|
||||
iconoGitHub: {
|
||||
fontSize: "1.25rem",
|
||||
verticalAlign: "bottom",
|
||||
marginRight: "0.5rem",
|
||||
},
|
||||
inputCorreo: {
|
||||
width: "100%",
|
||||
backgroundColor: "rgba(159,159,159,0.44)",
|
||||
border: "none",
|
||||
borderBottom: "solid 2px var(--color-texto)",
|
||||
padding: "0.5rem 1rem",
|
||||
boxSizing: "border-box",
|
||||
marginTop: "1rem",
|
||||
borderRadius: "5px",
|
||||
},
|
||||
checkbox: {
|
||||
width: "1.25rem",
|
||||
height: "1.25rem",
|
||||
margin: "0 0.5rem",
|
||||
},
|
||||
grid: {
|
||||
display: "grid",
|
||||
gridTemplateColumns: "3rem auto",
|
||||
gridRowGap: "1rem",
|
||||
},
|
||||
});
|
||||
|
||||
export function SeleccionCursos() {
|
||||
const [cursos, setCursos] = createSignal<RespuestaListaCursos>({});
|
||||
const [msgErr, setMsgError] = createSignal(false);
|
||||
|
||||
// Recuperar cursos de back
|
||||
(async() => setCursos(await getAllListaCursos()))();
|
||||
|
||||
const submit = (ev: Event) => {
|
||||
ev.preventDefault();
|
||||
const form = ev.target as HTMLFormElement;
|
||||
// Los checkboxes
|
||||
const elements = form.elements;
|
||||
const idsAEnviar: Array<string> = [];
|
||||
for (let i = 0; i < elements.length; i += 1) {
|
||||
const inputBox = elements[i] as HTMLInputElement;
|
||||
if (inputBox.checked) {
|
||||
idsAEnviar.push(inputBox.value);
|
||||
}
|
||||
}
|
||||
|
||||
if (idsAEnviar.length === 0) {
|
||||
setMsgError(true);
|
||||
setTimeout(() => setMsgError(false), 2500);
|
||||
return;
|
||||
}
|
||||
|
||||
// Almacenar en localStorage
|
||||
localStorage.setItem("cursos-seleccionados", JSON.stringify(idsAEnviar));
|
||||
// Ir a sig pantalla
|
||||
window.location.href = "#/pc/sistemas/";
|
||||
};
|
||||
|
||||
return (
|
||||
<div class={css(e.contenedorGlobal)}>
|
||||
<div class={css(e.cont)}>
|
||||
<form onSubmit={submit}>
|
||||
<div class={css(estilosGlobales.contenedor, estilosGlobales.inlineBlock, e.cont)}>
|
||||
<h1 style={{
|
||||
"text-align": "center",
|
||||
"font-size": "1.75rem",
|
||||
}}
|
||||
>
|
||||
Seleccion de cursos
|
||||
</h1>
|
||||
<p>Selecciona los cursos en los que matricularte</p>
|
||||
|
||||
<For each={Object.entries(cursos())}>
|
||||
{([nombreAnio, infoCurso]) => (
|
||||
<>
|
||||
<h2>{nombreAnio} año</h2>
|
||||
<div class={css(e.grid)}>
|
||||
<For each={infoCurso}>
|
||||
{(curso) => (
|
||||
<>
|
||||
<input
|
||||
type="checkbox"
|
||||
value={curso.id_curso}
|
||||
class={css(e.checkbox)}
|
||||
/>
|
||||
<span>{curso.nombre_curso}</span>
|
||||
</>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</For>
|
||||
<br />
|
||||
<span style={msgErr() ? "opacity: 1; color: red;" : "opacity: 0;"}>
|
||||
Selecciona al menos un curso
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
class={css(estilosGlobales.contenedor, estilosGlobales.contenedorCursor, e.botonAccion)}
|
||||
>
|
||||
Continuar
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,101 +0,0 @@
|
||||
import { BarraSuperior } from "../../BarraSuperior";
|
||||
import { ContenedorHorarios } from "./Sistemas/ContenedorHorarios";
|
||||
import { Creditos } from "../../Creditos";
|
||||
import { Separador } from "../../Separador";
|
||||
import { createSignal } from "solid-js";
|
||||
import { getHorarios, ListaCursosCompleto } from "../../API/CargaHorarios";
|
||||
import { Cursos, DatosGrupo } from "../../types/DatosHorario";
|
||||
import { infoDiaAListaHoras } from "../SistemasMovil";
|
||||
import { StyleSheet, css } from "aphrodite/no-important";
|
||||
import { estilosGlobales } from "../../Estilos";
|
||||
import { gruposSeleccionados, SERVER_PATH } from "../../Store";
|
||||
|
||||
const s = StyleSheet.create({
|
||||
botonAccion: {
|
||||
width: "50%",
|
||||
display: "inline-block",
|
||||
textAlign: "center",
|
||||
backgroundColor: "var(--color-primario)",
|
||||
},
|
||||
});
|
||||
|
||||
export function Sistemas() {
|
||||
const [data, setData] = createSignal<Cursos>({});
|
||||
|
||||
// Obtener cursos seleccionados del servidor
|
||||
(async() => {
|
||||
const cursos: Array<string> = JSON.parse(localStorage.getItem("cursos-seleccionados") ?? "[]");
|
||||
const data = await getHorarios({
|
||||
cursos: cursos.map((x) => parseInt(x, 10)),
|
||||
});
|
||||
setData(listaCursosADatos(data));
|
||||
})();
|
||||
|
||||
const matricular = async() => {
|
||||
const laboratoriosAMatricular = Object.entries(gruposSeleccionados)
|
||||
.filter((x) => x[1] === true)
|
||||
.map((x) => x[0]);
|
||||
|
||||
const response = await fetch(`${SERVER_PATH}/matricula`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
correo_usuario: localStorage.getItem("correo"),
|
||||
horarios: laboratoriosAMatricular,
|
||||
}),
|
||||
});
|
||||
if (response.ok) {
|
||||
window.location.href = "#/pc/ver-matricula/";
|
||||
} else {
|
||||
alert("No se pudo procesar la matricula");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<BarraSuperior />
|
||||
<Separador />
|
||||
<Separador />
|
||||
<ContenedorHorarios datos={data()} />
|
||||
<Separador />
|
||||
<div style="text-align: center;">
|
||||
<button
|
||||
class={css(estilosGlobales.contenedor, estilosGlobales.contenedorCursor, s.botonAccion)}
|
||||
onclick={matricular}
|
||||
>
|
||||
Matricular
|
||||
</button>
|
||||
</div>
|
||||
<Creditos />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function listaCursosADatos(cursosEntrada: ListaCursosCompleto): Cursos {
|
||||
const result: Cursos = {};
|
||||
|
||||
for (const curso of cursosEntrada) {
|
||||
const gruposLab: {[grupo: string]: DatosGrupo} = {};
|
||||
for (const lab of curso.laboratorios) {
|
||||
gruposLab[lab.grupo] = {
|
||||
id_laboratorio: lab.id_laboratorio,
|
||||
Docente: lab.docente,
|
||||
Horas: infoDiaAListaHoras(lab.horarios),
|
||||
seleccionado: false,
|
||||
};
|
||||
}
|
||||
|
||||
result[curso.nombre_curso] = {
|
||||
nombre: curso.nombre_curso,
|
||||
abreviado: curso.abreviado,
|
||||
oculto: false,
|
||||
Teoria: {},
|
||||
Laboratorio: gruposLab,
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -1,37 +0,0 @@
|
||||
import YAML from "yaml";
|
||||
import { css, StyleSheet } from "aphrodite";
|
||||
import { MiHorario } from "./ContenedorHorarios/MiHorario";
|
||||
import {
|
||||
Anios,
|
||||
Cursos,
|
||||
DatosHorario,
|
||||
DatosGrupo,
|
||||
} from "../../../types/DatosHorario";
|
||||
import { batch, createEffect, createMemo, createSignal, Show } from "solid-js";
|
||||
import { useListaCursos } from "./ContenedorHorarios/useListaCursos";
|
||||
|
||||
export type EstadoLayout = "MaxPersonal" | "Normal" | "MaxHorarios";
|
||||
|
||||
const {
|
||||
listaCursos: cursosUsuario,
|
||||
setListaCursos: setCursosUsuarios,
|
||||
agregarCursoALista: agregarCursoUsuario,
|
||||
} = useListaCursos();
|
||||
|
||||
export function ContenedorHorarios(props: {datos: Cursos}) {
|
||||
|
||||
createEffect(async() => {
|
||||
const d2 = props.datos;
|
||||
batch(() => {
|
||||
Object.entries(d2).forEach(([_, curso]) => agregarCursoUsuario(curso));
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
<MiHorario
|
||||
cursos={props.datos}
|
||||
fnAgregarCurso={agregarCursoUsuario}
|
||||
setCursosUsuarios={setCursosUsuarios}
|
||||
/>
|
||||
);
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
import { estilosGlobales } from "../../../../Estilos";
|
||||
import { StyleSheet, css } from "aphrodite";
|
||||
import {Tabla} from "./Tabla";
|
||||
import { EstadoLayout } from "../ContenedorHorarios";
|
||||
import { Switch, Match, createMemo } from "solid-js";
|
||||
import {SetStoreFunction} from "solid-js/store";
|
||||
import { BotonMaxMin } from "./BotonMaxMin";
|
||||
import { BotonIcono } from "./BotonIcono";
|
||||
import { Curso, Cursos, ListaCursosUsuario } from "../../../../types/DatosHorario";
|
||||
import { CursosElem } from "./CursosElem";
|
||||
import { TablaObserver } from "./TablaObserver";
|
||||
|
||||
interface MiHorarioProps {
|
||||
cursos: Cursos,
|
||||
fnAgregarCurso: (c: Curso) => void,
|
||||
setCursosUsuarios: SetStoreFunction<ListaCursosUsuario>
|
||||
}
|
||||
|
||||
const e = StyleSheet.create({
|
||||
horario: {},
|
||||
boton: {
|
||||
textDecoration: "none",
|
||||
// paddingRight: "0.5rem",
|
||||
"::before": {
|
||||
fontSize: "1rem",
|
||||
// transform: "translateY(0.2rem)",
|
||||
textDecoration: "none",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export function MiHorario(props: MiHorarioProps) {
|
||||
const tablaObserver = new TablaObserver();
|
||||
|
||||
const datosMiHorario = createMemo(() => {
|
||||
const obj: Cursos = {};
|
||||
Object.entries(props.cursos).forEach(([_, x], i) => {
|
||||
obj[i] = x;
|
||||
});
|
||||
return obj;
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={css(
|
||||
estilosGlobales.inlineBlock,
|
||||
estilosGlobales.contenedor,
|
||||
)}
|
||||
>
|
||||
Mi horario
|
||||
</div>
|
||||
|
||||
<div className={css(
|
||||
e.horario,
|
||||
estilosGlobales.contenedor,
|
||||
)}
|
||||
>
|
||||
<Tabla
|
||||
data={datosMiHorario()}
|
||||
anio={"Mi horario"}
|
||||
version={1}
|
||||
setCursosUsuarios={props.setCursosUsuarios}
|
||||
tablaObserver={tablaObserver}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<CursosElem
|
||||
version={Math.floor(Math.random() * 1_000_000)}
|
||||
anioActual={() => "Mi horario"}
|
||||
dataAnio={datosMiHorario()}
|
||||
fnAgregarCurso={props.fnAgregarCurso}
|
||||
esCursoMiHorario
|
||||
setCursosUsuarios={props.setCursosUsuarios}
|
||||
tablaObserver={tablaObserver}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,90 +0,0 @@
|
||||
import { css, StyleSheet } from "aphrodite/no-important";
|
||||
import { estilosGlobales } from "../../Estilos";
|
||||
import { createSignal, For } from "solid-js";
|
||||
import { getMatricula, InfoMatricula } from "../../API/VerMatricula";
|
||||
import { gruposSeleccionados } from "../../Store";
|
||||
|
||||
const e = StyleSheet.create({
|
||||
contenedorGlobal: {
|
||||
width: "100vw",
|
||||
height: "100vh",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
},
|
||||
cont: {
|
||||
width: "30rem",
|
||||
},
|
||||
parrafo: {
|
||||
textAlign: "justify",
|
||||
lineHeight: "1.4rem",
|
||||
},
|
||||
botonAccion: {
|
||||
width: "30rem",
|
||||
display: "inline-block",
|
||||
textAlign: "center",
|
||||
},
|
||||
iconoGitHub: {
|
||||
fontSize: "1.25rem",
|
||||
verticalAlign: "bottom",
|
||||
marginRight: "0.5rem",
|
||||
},
|
||||
inputCorreo: {
|
||||
width: "100%",
|
||||
backgroundColor: "rgba(159,159,159,0.44)",
|
||||
border: "none",
|
||||
borderBottom: "solid 2px var(--color-texto)",
|
||||
padding: "0.5rem 1rem",
|
||||
boxSizing: "border-box",
|
||||
marginTop: "1rem",
|
||||
borderRadius: "5px",
|
||||
},
|
||||
checkbox: {
|
||||
width: "1.25rem",
|
||||
height: "1.25rem",
|
||||
margin: "0 0.5rem",
|
||||
},
|
||||
grid: {
|
||||
display: "grid",
|
||||
gridTemplateColumns: "3rem auto",
|
||||
gridRowGap: "1rem",
|
||||
},
|
||||
});
|
||||
|
||||
export function VerMatricula() {
|
||||
const [infoMatriculas, setInfoMatriculas] = createSignal<Array<InfoMatricula>>([]);
|
||||
|
||||
(async() => {
|
||||
const laboratorios = Object.entries(gruposSeleccionados)
|
||||
.filter((x) => x[1] === true)
|
||||
.map((x) => parseInt(x[0], 10));
|
||||
setInfoMatriculas(await getMatricula({matriculas: laboratorios}));
|
||||
})();
|
||||
|
||||
return (
|
||||
<div class={css(e.contenedorGlobal)}>
|
||||
<div class={css(e.cont)}>
|
||||
|
||||
<div class={css(estilosGlobales.contenedor, estilosGlobales.inlineBlock, e.cont)}>
|
||||
<h1 style={{
|
||||
"text-align": "center",
|
||||
"font-size": "1.75rem",
|
||||
}}
|
||||
>
|
||||
Matricula realizada
|
||||
</h1>
|
||||
<For each={infoMatriculas()}>
|
||||
{(matricula) => (
|
||||
<div>
|
||||
<h3>{matricula.nombre_curso}</h3>
|
||||
<p>Grupo: {matricula.grupo}</p>
|
||||
<p>Docente: {matricula.docente}</p>
|
||||
</div>
|
||||
)}
|
||||
</For>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
import { StyleSheet, css } from "aphrodite/no-important";
|
||||
|
||||
export function Button(props: {texto: string, onClick?: () => void}) {
|
||||
const s = StyleSheet.create({
|
||||
boton: {
|
||||
backgroundColor: "var(--color-primario)",
|
||||
color: "white",
|
||||
padding: "1rem 5rem",
|
||||
borderRadius: "25px",
|
||||
margin: "1.5rem 0",
|
||||
boxShadow: "2px 2px 2px 0 gray",
|
||||
cursor: "pointer",
|
||||
},
|
||||
});
|
||||
return (
|
||||
<button type="submit" className={css(s.boton)} onClick={() => props.onClick?.()}>
|
||||
{props.texto}
|
||||
</button>
|
||||
);
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
import { JSX } from "solid-js";
|
||||
import { StyleSheet, css } from "aphrodite/no-important";
|
||||
|
||||
const s = StyleSheet.create({
|
||||
card: {
|
||||
padding: "0.5rem",
|
||||
border: "solid 2px var(--color-borde)",
|
||||
borderRadius: "10px",
|
||||
margin: "0.5rem",
|
||||
},
|
||||
});
|
||||
|
||||
export function Card(props: {children?: JSX.Element}) {
|
||||
return (
|
||||
<div className={css(s.card)}>
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
:root {
|
||||
--color-texto: white;
|
||||
--color-primario: #531925;
|
||||
--color-borde: rgba(83, 25, 37, 0.49);
|
||||
}
|
||||
|
||||
body {
|
||||
|
@ -27,7 +27,6 @@ export interface DatosHorarioRaw {
|
||||
}
|
||||
|
||||
export interface DatosGrupo {
|
||||
id_laboratorio: number,
|
||||
Docente: string,
|
||||
Horas: string[]
|
||||
seleccionado: boolean
|
||||
@ -45,8 +44,9 @@ export interface Curso {
|
||||
}
|
||||
}
|
||||
|
||||
export type ListaCursosUsuario = {
|
||||
[key: string]: Curso
|
||||
export interface ListaCursosUsuario {
|
||||
sigIndice: number,
|
||||
cursos: Curso[]
|
||||
}
|
||||
|
||||
export interface Cursos {
|
||||
|
Loading…
Reference in New Issue
Block a user