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
|
// Exclusivo de un unico dia
|
||||||
import { GrupoDia, TableInput } from "../src/Views/SistemasMovil/Table";
|
|
||||||
import { generarMapaCeldas, MapaCeldas } from "../src/Views/SistemasMovil/mapaCeldas";
|
|
||||||
|
|
||||||
type Input = {
|
type Input = {
|
||||||
offsetVertical: number,
|
horaInicio: number,
|
||||||
nroHoras: 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", () => {
|
describe("generarMapaCeldas", () => {
|
||||||
it("vacio si input es vacio", () => {
|
it("vacio si input es vacio", () => {
|
||||||
const input: Array<GrupoDia> = [];
|
const input: Array<Input> = [];
|
||||||
const output = generarMapaCeldas(input);
|
const output = generarMapaCeldas(input);
|
||||||
expect(output.length).toBe(0);
|
expect(output.length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("funciona con 1 curso", () => {
|
it("funciona con 1 curso", () => {
|
||||||
const input: Array<any> = [
|
const input: Array<Input> = [
|
||||||
{
|
{
|
||||||
offsetVertical: 0,
|
horaInicio: 0,
|
||||||
nroHoras: 2,
|
nroHoras: 2,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const output = generarMapaCeldas(input)[0];
|
const output = generarMapaCeldas(input)[0];
|
||||||
expect(output).not.toBeUndefined();
|
expect(output).not.toBeUndefined();
|
||||||
expect(output.offsetHorizontal).toBe(0);
|
expect(output.offset).toBe(0);
|
||||||
expect(output.fraccion).toBe(1);
|
expect(output.fraccion).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("funciona con 2 cursos", () => {
|
it("funciona con 2 cursos", () => {
|
||||||
const input: Array<any> = [
|
const input: Array<Input> = [
|
||||||
{
|
{
|
||||||
offsetVertical: 0,
|
horaInicio: 0,
|
||||||
nroHoras: 2,
|
nroHoras: 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
offsetVertical: 1,
|
horaInicio: 1,
|
||||||
nroHoras: 3,
|
nroHoras: 3,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const output1 = generarMapaCeldas(input)[0];
|
const output1 = generarMapaCeldas(input)[0];
|
||||||
expect(output1.offsetHorizontal).toBe(0);
|
expect(output1.offset).toBe(0);
|
||||||
expect(output1.fraccion).toBe(2);
|
expect(output1.fraccion).toBe(2);
|
||||||
|
|
||||||
const output2 = generarMapaCeldas(input)[1];
|
const output2 = generarMapaCeldas(input)[1];
|
||||||
expect(output2.offsetHorizontal).toBe(1);
|
expect(output2.offset).toBe(1);
|
||||||
expect(output2.fraccion).toBe(2);
|
expect(output2.fraccion).toBe(2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -55,29 +174,29 @@ describe("MapaCeldas", () => {
|
|||||||
it("crea 1", () => {
|
it("crea 1", () => {
|
||||||
const mapa = new MapaCeldas();
|
const mapa = new MapaCeldas();
|
||||||
const input = {} as unknown as Input;
|
const input = {} as unknown as Input;
|
||||||
const offset = mapa.solicitar(0, 2);
|
const offset = mapa.solicitar(0, 2, input);
|
||||||
expect(offset).toBe(0);
|
expect(offset).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("crea varios que no se solapan", () => {
|
it("crea varios que no se solapan", () => {
|
||||||
const mapa = new MapaCeldas();
|
const mapa = new MapaCeldas();
|
||||||
const input = {} as unknown as Input;
|
const input = {} as unknown as Input;
|
||||||
let offset = mapa.solicitar(0, 2);
|
let offset = mapa.solicitar(0, 2, input);
|
||||||
expect(offset).toBe(0);
|
expect(offset).toBe(0);
|
||||||
offset = mapa.solicitar(4, 3);
|
offset = mapa.solicitar(4, 3, input);
|
||||||
expect(offset).toBe(0);
|
expect(offset).toBe(0);
|
||||||
offset = mapa.solicitar(7, 4);
|
offset = mapa.solicitar(7, 4, input);
|
||||||
expect(offset).toBe(0);
|
expect(offset).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("crea varios que se solapan", () => {
|
it("crea varios que se solapan", () => {
|
||||||
const mapa = new MapaCeldas();
|
const mapa = new MapaCeldas();
|
||||||
const input = {} as unknown as Input;
|
const input = {} as unknown as Input;
|
||||||
let offset = mapa.solicitar(0, 2);
|
let offset = mapa.solicitar(0, 2, input);
|
||||||
expect(offset).toBe(0);
|
expect(offset).toBe(0);
|
||||||
offset = mapa.solicitar(0, 2);
|
offset = mapa.solicitar(0, 2, input);
|
||||||
expect(offset).toBe(1);
|
expect(offset).toBe(1);
|
||||||
offset = mapa.solicitar(0, 4);
|
offset = mapa.solicitar(0, 4, input);
|
||||||
expect(offset).toBe(2);
|
expect(offset).toBe(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -93,33 +212,33 @@ describe("MapaCeldas", () => {
|
|||||||
*/
|
*/
|
||||||
const mapa = new MapaCeldas();
|
const mapa = new MapaCeldas();
|
||||||
const input = {} as unknown as Input;
|
const input = {} as unknown as Input;
|
||||||
let offset = mapa.solicitar(0, 2);
|
let offset = mapa.solicitar(0, 2, input);
|
||||||
expect(offset).toBe(0);
|
expect(offset).toBe(0);
|
||||||
offset = mapa.solicitar(1, 3);
|
offset = mapa.solicitar(1, 3, input);
|
||||||
expect(offset).toBe(1);
|
expect(offset).toBe(1);
|
||||||
offset = mapa.solicitar(1, 4);
|
offset = mapa.solicitar(1, 4, input);
|
||||||
expect(offset).toBe(2);
|
expect(offset).toBe(2);
|
||||||
offset = mapa.solicitar(2, 3);
|
offset = mapa.solicitar(2, 3, input);
|
||||||
expect(offset).toBe(0);
|
expect(offset).toBe(0);
|
||||||
offset = mapa.solicitar(4, 2);
|
offset = mapa.solicitar(4, 2, input);
|
||||||
expect(offset).toBe(1);
|
expect(offset).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("genera offsets", () => {
|
it("genera offsets", () => {
|
||||||
const mapa = new MapaCeldas();
|
const mapa = new MapaCeldas();
|
||||||
const input = {} as unknown as Input;
|
const input = {} as unknown as Input;
|
||||||
let offset = mapa.solicitar(0, 2);
|
let offset = mapa.solicitar(0, 2, input);
|
||||||
expect(offset).toBe(0);
|
expect(offset).toBe(0);
|
||||||
let fraccion = mapa.generarFraccion(0, offset, 2);
|
let fraccion = mapa.generarFraccion(0, offset, 2);
|
||||||
expect(fraccion).toBe(1);
|
expect(fraccion).toBe(1);
|
||||||
|
|
||||||
offset = mapa.solicitar(1, 3);
|
offset = mapa.solicitar(1, 3, input);
|
||||||
fraccion = mapa.generarFraccion(1, offset, 3);
|
fraccion = mapa.generarFraccion(1, offset, 3);
|
||||||
expect(fraccion).toBe(2);
|
expect(fraccion).toBe(2);
|
||||||
|
|
||||||
mapa.solicitar(1, 4);
|
mapa.solicitar(1, 4, input);
|
||||||
mapa.solicitar(2, 3);
|
mapa.solicitar(2, 3, input);
|
||||||
offset = mapa.solicitar(4, 2);
|
offset = mapa.solicitar(4, 2, input);
|
||||||
fraccion = mapa.generarFraccion(4, offset, 2);
|
fraccion = mapa.generarFraccion(4, offset, 2);
|
||||||
expect(fraccion).toBe(3);
|
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",
|
"jest": "^29.1.2",
|
||||||
"normalize.css": "^8.0.1",
|
"normalize.css": "^8.0.1",
|
||||||
"solid-app-router": "^0.3.2",
|
"solid-app-router": "^0.3.2",
|
||||||
"solid-js": "1.5.7",
|
"solid-js": "^1.3.12",
|
||||||
"ts-jest": "^29.0.3",
|
"ts-jest": "^29.0.3",
|
||||||
"typescript": "^4.6.2",
|
"typescript": "^4.6.2",
|
||||||
"vite": "^2.8.6",
|
"vite": "^2.8.6",
|
||||||
@ -35,7 +35,6 @@
|
|||||||
"Node 10"
|
"Node 10"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"swiper": "^8.4.4",
|
|
||||||
"yaml": "^1.10.0"
|
"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 { Index } from "./Views/Index";
|
||||||
import { Editor } from "./Views/Editor";
|
import { Editor } from "./Views/Editor";
|
||||||
import { useRouter } from "./Router";
|
import { useRouter } from "./Router";
|
||||||
import { Switch, Match, Show } from "solid-js";
|
import { Switch, Match, Show } from "solid-js";
|
||||||
import { Wallpaper } from "./Wallpaper";
|
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() {
|
function App() {
|
||||||
const route = useRouter();
|
const route = useRouter();
|
||||||
const isMobile = screen.width <= 500;
|
const isMobile = screen.width <= 500;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="App" style={isMobile ? "--color-texto: #202020;" : ""}>
|
<div className="App" style={isMobile ? "--color-texto: #202020;" : ""}>
|
||||||
<Show when={!isMobile}>
|
<Show when={!isMobile}>
|
||||||
<Wallpaper />
|
<Wallpaper />
|
||||||
</Show>
|
</Show>
|
||||||
@ -26,24 +21,8 @@ function App() {
|
|||||||
<Match when={route() === "/editor/"}>
|
<Match when={route() === "/editor/"}>
|
||||||
<Editor />
|
<Editor />
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={route() === "/seleccion-cursos/"}>
|
<Match when={route() === "/sistemas/"}>
|
||||||
<SeleccionCursos />
|
<Main />
|
||||||
</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>
|
</Match>
|
||||||
</Switch>
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { css } from "aphrodite";
|
import { css } from "aphrodite"
|
||||||
import {estilosGlobales} from "../../../../Estilos";
|
import { estilosGlobales } from "../Estilos"
|
||||||
|
|
||||||
interface BotonMaxMinProps {
|
interface BotonMaxMinProps {
|
||||||
icono: string,
|
icono: string,
|
||||||
@ -21,5 +21,5 @@ export function BotonIcono(props: BotonMaxMinProps) {
|
|||||||
>
|
>
|
||||||
<i className={`${css(estilosGlobales.botonPhospor)} ${props.icono}`} />
|
<i className={`${css(estilosGlobales.botonPhospor)} ${props.icono}`} />
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import { css } from "aphrodite";
|
import { css } from "aphrodite"
|
||||||
import { estilosGlobales } from "../../../../Estilos";
|
import { estilosGlobales } from "../Estilos"
|
||||||
import {EstadoLayout} from "../ContenedorHorarios";
|
import { EstadoLayout } from "./ContenedorHorarios"
|
||||||
|
|
||||||
interface BotonMaxMinProps {
|
interface BotonMaxMinProps {
|
||||||
fnMaximizar: () => void,
|
fnMaximizar: () => void,
|
||||||
@ -10,19 +10,19 @@ interface BotonMaxMinProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function BotonMaxMin(props: BotonMaxMinProps) {
|
export function BotonMaxMin(props: BotonMaxMinProps) {
|
||||||
const horariosMax = () => props.estadoActualLayout() === props.estadoLayoutMax;
|
const horariosMax = () => props.estadoActualLayout() === props.estadoLayoutMax
|
||||||
|
|
||||||
const tituloBoton = () => (horariosMax() ? "Minimizar" : "Maximizar");
|
const tituloBoton = () => (horariosMax() ? "Minimizar" : "Maximizar")
|
||||||
const iconoBoton = () => (horariosMax() ? "ph-arrows-in" : "ph-arrows-out");
|
const iconoBoton = () => (horariosMax() ? "ph-arrows-in" : "ph-arrows-out")
|
||||||
|
|
||||||
const funcionBoton = () => {
|
const funcionBoton = () => {
|
||||||
const estaMaximizado = horariosMax();
|
const estaMaximizado = horariosMax()
|
||||||
if (estaMaximizado) {
|
if (estaMaximizado) {
|
||||||
props.fnMinimizar();
|
props.fnMinimizar()
|
||||||
} else {
|
} else {
|
||||||
props.fnMaximizar();
|
props.fnMaximizar()
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
@ -38,5 +38,5 @@ export function BotonMaxMin(props: BotonMaxMinProps) {
|
|||||||
>
|
>
|
||||||
<i className={`${css(estilosGlobales.botonPhospor)} ${iconoBoton()}`} />
|
<i className={`${css(estilosGlobales.botonPhospor)} ${iconoBoton()}`} />
|
||||||
</button>
|
</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 { Cursos, DatosGrupo, ListaCursosUsuario, Curso } from "../types/DatosHorario"
|
||||||
import { createMemo, For } from "solid-js";
|
import { createMemo, For } from "solid-js"
|
||||||
import { produce, SetStoreFunction } from "solid-js/store";
|
import { produce, SetStoreFunction } from "solid-js/store"
|
||||||
import { StyleSheet, css } from "aphrodite";
|
import { StyleSheet, css } from "aphrodite"
|
||||||
import { estilosGlobales } from "../../../../Estilos";
|
import { estilosGlobales } from "../Estilos"
|
||||||
import { TablaObserver } from "./TablaObserver";
|
import { TablaObserver } from "./TablaObserver"
|
||||||
import { setGruposSeleccionados } from "../../../../Store";
|
|
||||||
|
|
||||||
const e = StyleSheet.create({
|
const e = StyleSheet.create({
|
||||||
inline: {
|
inline: {
|
||||||
@ -34,20 +33,21 @@ const e = StyleSheet.create({
|
|||||||
border: "none",
|
border: "none",
|
||||||
color: "var(--color)",
|
color: "var(--color)",
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
const claseCursoNoAgregado = css(
|
const claseCursoNoAgregado = css(
|
||||||
e.contenedorCurso,
|
e.contenedorCurso,
|
||||||
estilosGlobales.contenedor,
|
estilosGlobales.contenedor,
|
||||||
);
|
)
|
||||||
|
|
||||||
const claseCursoOculto = css(e.cursoOculto);
|
const claseCursoOculto = css(e.cursoOculto)
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
version: number,
|
version: number,
|
||||||
dataAnio: Cursos,
|
dataAnio: Cursos,
|
||||||
anioActual: () => string,
|
anioActual: () => string,
|
||||||
fnAgregarCurso: (c: Curso) => void,
|
fnAgregarCurso: (c: Curso) => void,
|
||||||
|
listaCursosUsuario: ListaCursosUsuario,
|
||||||
esCursoMiHorario: boolean,
|
esCursoMiHorario: boolean,
|
||||||
setCursosUsuarios: SetStoreFunction<ListaCursosUsuario>,
|
setCursosUsuarios: SetStoreFunction<ListaCursosUsuario>,
|
||||||
tablaObserver: TablaObserver,
|
tablaObserver: TablaObserver,
|
||||||
@ -64,7 +64,7 @@ interface PropsIndicadorGrupo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function IndicadorGrupo(props: 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 (
|
return (
|
||||||
<span
|
<span
|
||||||
className={css(e.botonTexto, estilosGlobales.contenedorCursor, estilosGlobales.contenedorCursorSoft)}
|
className={css(e.botonTexto, estilosGlobales.contenedorCursor, estilosGlobales.contenedorCursorSoft)}
|
||||||
@ -75,7 +75,7 @@ function IndicadorGrupo(props: PropsIndicadorGrupo) {
|
|||||||
>
|
>
|
||||||
{props.esLab ? "L" : ""}{props.nombre}
|
{props.esLab ? "L" : ""}{props.nombre}
|
||||||
</span>
|
</span>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const agruparProfesores = (
|
const agruparProfesores = (
|
||||||
@ -84,21 +84,31 @@ const agruparProfesores = (
|
|||||||
esLab: boolean,
|
esLab: boolean,
|
||||||
setCursosUsuarios: FnSetCursosUsuarios,
|
setCursosUsuarios: FnSetCursosUsuarios,
|
||||||
) => {
|
) => {
|
||||||
const profesores: { [k: string]: [string, () => void][] } = {};
|
const profesores: { [k: string]: [string, () => void][] } = {}
|
||||||
for (const [grupo, datosGrupo] of Object.entries(datos)) {
|
for (const [grupo, datosGrupo] of Object.entries(datos)) {
|
||||||
const nombreProfesor = datosGrupo.Docente;
|
const nombreProfesor = datosGrupo.Docente
|
||||||
if (!profesores[nombreProfesor]) {
|
if (!profesores[nombreProfesor]) {
|
||||||
profesores[nombreProfesor] = [];
|
profesores[nombreProfesor] = []
|
||||||
}
|
}
|
||||||
profesores[nombreProfesor].push([
|
profesores[nombreProfesor].push([
|
||||||
grupo,
|
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(
|
function CursoE(
|
||||||
indiceCurso: string,
|
indiceCurso: string,
|
||||||
@ -107,27 +117,42 @@ function CursoE(
|
|||||||
claseCursoAgregado: string,
|
claseCursoAgregado: string,
|
||||||
props: Props,
|
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(() => {
|
const claseMemo = createMemo(() => {
|
||||||
if (props.esCursoMiHorario && datosCurso.oculto) {
|
if (props.esCursoMiHorario && datosCurso.oculto) {
|
||||||
return claseCursoOculto;
|
return claseCursoOculto
|
||||||
}
|
}
|
||||||
return claseCursoNoAgregado;
|
return cursoAgregadoMemo()
|
||||||
});
|
? claseCursoAgregado
|
||||||
|
: claseCursoNoAgregado
|
||||||
|
})
|
||||||
|
|
||||||
const profesoresTeoria = createMemo(() => agruparProfesores(
|
const profesoresTeoria = createMemo(() => agruparProfesores(
|
||||||
datosCurso.Teoria,
|
datosCurso.Teoria,
|
||||||
Number(indiceCurso),
|
Number(indiceCurso),
|
||||||
false,
|
false,
|
||||||
props.setCursosUsuarios,
|
props.setCursosUsuarios,
|
||||||
));
|
))
|
||||||
const profesoresLab = createMemo(() => agruparProfesores(
|
const profesoresLab = createMemo(() => agruparProfesores(
|
||||||
datosCurso.Laboratorio ?? {},
|
datosCurso.Laboratorio ?? {},
|
||||||
Number(indiceCurso),
|
Number(indiceCurso),
|
||||||
true,
|
true,
|
||||||
props.setCursosUsuarios,
|
props.setCursosUsuarios,
|
||||||
));
|
))
|
||||||
|
|
||||||
const IndicadorGrupos = (profesor: string, grupos: [string, () => void][], esLab: boolean) => (
|
const IndicadorGrupos = (profesor: string, grupos: [string, () => void][], esLab: boolean) => (
|
||||||
<td style={{"padding-bottom": "0.5rem", "padding-right": "0.75rem"}}>
|
<td style={{"padding-bottom": "0.5rem", "padding-right": "0.75rem"}}>
|
||||||
@ -147,7 +172,7 @@ function CursoE(
|
|||||||
}
|
}
|
||||||
</For>
|
</For>
|
||||||
</td>
|
</td>
|
||||||
);
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={claseMemo()}>
|
<div className={claseMemo()}>
|
||||||
@ -172,18 +197,24 @@ function CursoE(
|
|||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
<button
|
||||||
|
className={css(e.botonTexto, estilosGlobales.contenedorCursor, estilosGlobales.contenedorCursorSoft)}
|
||||||
|
onClick={() => props.fnAgregarCurso(datosCurso)}
|
||||||
|
>
|
||||||
|
{tituloMemo}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CursosElem(props: Props) {
|
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(
|
const claseCursoAgregado = css(
|
||||||
e.contenedorCurso,
|
e.contenedorCurso,
|
||||||
estilosGlobales.contenedor,
|
estilosGlobales.contenedor,
|
||||||
!props.esCursoMiHorario && estilosGlobales.contenedorCursorActivo,
|
!props.esCursoMiHorario && estilosGlobales.contenedorCursorActivo,
|
||||||
);
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -191,5 +222,5 @@ export function CursosElem(props: Props) {
|
|||||||
{([indiceCurso, datosCurso]) => CursoE(indiceCurso, datosCurso, anio, claseCursoAgregado, props)}
|
{([indiceCurso, datosCurso]) => CursoE(indiceCurso, datosCurso, anio, claseCursoAgregado, props)}
|
||||||
</For>
|
</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 { StyleSheet, css } from "aphrodite"
|
||||||
import { batch, createMemo, For } from "solid-js";
|
import { batch, createMemo, For } from "solid-js"
|
||||||
import { produce, SetStoreFunction } from "solid-js/store";
|
import { produce, SetStoreFunction } from "solid-js/store"
|
||||||
import {estilosGlobales} from "../../../../Estilos";
|
import { estilosGlobales } from "../Estilos"
|
||||||
import { Cursos, ListaCursosUsuario, DataProcesada, DatosGrupo } from "../../../../types/DatosHorario";
|
import { Cursos, ListaCursosUsuario, DataProcesada, DatosGrupo } from "../types/DatosHorario"
|
||||||
import { Dia, dias, gruposSeleccionados, horas, setGruposSeleccionados } from "../../../../Store";
|
import { Dia, dias, horas } from "../Store"
|
||||||
import { FilaTabla } from "./Tabla/FilaTabla";
|
import { FilaTabla } from "./Tabla/FilaTabla"
|
||||||
import { TablaObserver } from "./TablaObserver";
|
import { TablaObserver } from "./TablaObserver"
|
||||||
|
|
||||||
export const coloresBorde = Object.freeze([
|
export const coloresBorde = Object.freeze([
|
||||||
"rgba(33,150,243,1)",
|
"rgba(33,150,243,1)",
|
||||||
@ -13,22 +13,22 @@ export const coloresBorde = Object.freeze([
|
|||||||
"rgba(236,64,122 ,1)",
|
"rgba(236,64,122 ,1)",
|
||||||
"rgba(29,233,182 ,1)",
|
"rgba(29,233,182 ,1)",
|
||||||
"rgba(244,67,54,1)",
|
"rgba(244,67,54,1)",
|
||||||
]);
|
])
|
||||||
|
|
||||||
export const diaANum = (d: Dia) => {
|
export const diaANum = (d: Dia) => {
|
||||||
switch (d) {
|
switch (d) {
|
||||||
case "Lunes":
|
case "Lunes":
|
||||||
return 0;
|
return 0
|
||||||
case "Martes":
|
case "Martes":
|
||||||
return 1;
|
return 1
|
||||||
case "Miercoles":
|
case "Miercoles":
|
||||||
return 2;
|
return 2
|
||||||
case "Jueves":
|
case "Jueves":
|
||||||
return 3;
|
return 3
|
||||||
case "Viernes":
|
case "Viernes":
|
||||||
return 4;
|
return 4
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const e = StyleSheet.create({
|
const e = StyleSheet.create({
|
||||||
fila: {
|
fila: {
|
||||||
@ -81,34 +81,34 @@ const e = StyleSheet.create({
|
|||||||
celdaCursoTeoria: {
|
celdaCursoTeoria: {
|
||||||
fontWeight: "bold",
|
fontWeight: "bold",
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
type FnSetCursosUsuarios = SetStoreFunction<ListaCursosUsuario>;
|
type FnSetCursosUsuarios = SetStoreFunction<ListaCursosUsuario>;
|
||||||
|
|
||||||
const procesarAnio = (data: Cursos, anio: string, version: number, setCursosUsuarios: FnSetCursosUsuarios) => {
|
const procesarAnio = (data: Cursos, anio: string, version: number, setCursosUsuarios: FnSetCursosUsuarios) => {
|
||||||
const obj: DataProcesada = {};
|
const obj: DataProcesada = {}
|
||||||
|
|
||||||
for (const [indiceCurso, curso] of Object.entries(data)) {
|
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 [grupoStr, grupo] of Object.entries(curso.Teoria)) {
|
||||||
for (const hora of grupo.Horas) {
|
for (const hora of grupo.Horas) {
|
||||||
const dia = hora.substring(0, 2);
|
const dia = hora.substring(0, 2)
|
||||||
const horas = hora.substring(2, 4);
|
const horas = hora.substring(2, 4)
|
||||||
const minutos = hora.substr(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)) {
|
if (!(horaCompleta in obj)) {
|
||||||
obj[horaCompleta] = {};
|
obj[horaCompleta] = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(dia in obj[horaCompleta])) {
|
if (!(dia in obj[horaCompleta])) {
|
||||||
obj[horaCompleta][dia] = [];
|
obj[horaCompleta][dia] = []
|
||||||
}
|
}
|
||||||
|
|
||||||
obj[horaCompleta][dia].push({
|
obj[horaCompleta][dia].push({
|
||||||
@ -117,28 +117,38 @@ const procesarAnio = (data: Cursos, anio: string, version: number, setCursosUsua
|
|||||||
esLab: false,
|
esLab: false,
|
||||||
datosGrupo: grupo,
|
datosGrupo: grupo,
|
||||||
fnSeleccionar: () => {
|
fnSeleccionar: () => {
|
||||||
|
setCursosUsuarios("cursos", Number(indiceCurso), "Teoria", produce<{ [p: string]: DatosGrupo }>((x) => {
|
||||||
|
const grupoActualSeleccionado = x[grupoStr].seleccionado
|
||||||
|
|
||||||
|
if (grupoActualSeleccionado) {
|
||||||
|
x[grupoStr].seleccionado = false
|
||||||
|
} else {
|
||||||
|
for (const xKey in x) {
|
||||||
|
x[xKey].seleccionado = xKey === grupoStr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const [grupoStr, grupo] of Object.entries(curso.Laboratorio ?? {})) {
|
for (const [grupoStr, grupo] of Object.entries(curso.Laboratorio ?? {})) {
|
||||||
for (const hora of grupo.Horas) {
|
for (const hora of grupo.Horas) {
|
||||||
const dia = hora.substring(0, 2);
|
const dia = hora.substring(0, 2)
|
||||||
const horas = hora.substring(2, 4);
|
const horas = hora.substring(2, 4)
|
||||||
const minutos = hora.substr(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)) {
|
if (!(horaCompleta in obj)) {
|
||||||
obj[horaCompleta] = {};
|
obj[horaCompleta] = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(dia in obj[horaCompleta])) {
|
if (!(dia in obj[horaCompleta])) {
|
||||||
obj[horaCompleta][dia] = [];
|
obj[horaCompleta][dia] = []
|
||||||
}
|
}
|
||||||
|
|
||||||
obj[horaCompleta][dia].push({
|
obj[horaCompleta][dia].push({
|
||||||
@ -147,15 +157,30 @@ const procesarAnio = (data: Cursos, anio: string, version: number, setCursosUsua
|
|||||||
esLab: true,
|
esLab: true,
|
||||||
datosGrupo: grupo,
|
datosGrupo: grupo,
|
||||||
fnSeleccionar: () => {
|
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 {
|
interface Props {
|
||||||
data: Cursos,
|
data: Cursos,
|
||||||
@ -166,12 +191,12 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function Tabla(props: Props) {
|
export function Tabla(props: Props) {
|
||||||
const anio = () => props.anio.substring(0, props.anio.indexOf(" "));
|
const anio = () => props.anio.substring(0, props.anio.indexOf(" "))
|
||||||
const data = createMemo(() => procesarAnio(props.data, anio(), props.version, props.setCursosUsuarios));
|
const data = createMemo(() => procesarAnio(props.data, anio(), props.version, props.setCursosUsuarios))
|
||||||
|
|
||||||
const celdas = createMemo(() => {
|
const celdas = createMemo(() => {
|
||||||
// Hace reaccionar a la reactividad de Solid
|
// Hace reaccionar a la reactividad de Solid
|
||||||
const d = data();
|
const d = data()
|
||||||
return (
|
return (
|
||||||
<For each={horas}>
|
<For each={horas}>
|
||||||
{(hora) => (
|
{(hora) => (
|
||||||
@ -182,8 +207,8 @@ export function Tabla(props: Props) {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
);
|
)
|
||||||
});
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@ -198,5 +223,5 @@ export function Tabla(props: Props) {
|
|||||||
</div>
|
</div>
|
||||||
{celdas()}
|
{celdas()}
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
@ -1,9 +1,9 @@
|
|||||||
import { StyleSheet, css } from "aphrodite";
|
import { StyleSheet, css } from "aphrodite"
|
||||||
import { estilosGlobales } from "../../../../../Estilos";
|
import { estilosGlobales } from "../../Estilos"
|
||||||
import { For, createSignal, createMemo, createEffect, onCleanup } from "solid-js";
|
import { For, createSignal, createMemo, createEffect, onCleanup } from "solid-js"
|
||||||
import { Dia } from "../../../../../Store";
|
import { Dia } from "../../Store"
|
||||||
import { DatosGrupo } from "../../../../../types/DatosHorario";
|
import { DatosGrupo } from "../../types/DatosHorario"
|
||||||
import {TablaObserver} from "../TablaObserver";
|
import { TablaObserver } from "../TablaObserver"
|
||||||
|
|
||||||
const e = StyleSheet.create({
|
const e = StyleSheet.create({
|
||||||
celdaComun: {
|
celdaComun: {
|
||||||
@ -42,7 +42,7 @@ const e = StyleSheet.create({
|
|||||||
celdaResaltadoSeleccionado: {
|
celdaResaltadoSeleccionado: {
|
||||||
textDecoration: "underline",
|
textDecoration: "underline",
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
const eColores = StyleSheet.create({
|
const eColores = StyleSheet.create({
|
||||||
lunes: {
|
lunes: {
|
||||||
@ -62,7 +62,7 @@ const eColores = StyleSheet.create({
|
|||||||
viernes: {
|
viernes: {
|
||||||
backgroundColor: "rgba(244,67,54,1)",
|
backgroundColor: "rgba(244,67,54,1)",
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
const clasesColores = {
|
const clasesColores = {
|
||||||
Lunes: css(eColores.lunes),
|
Lunes: css(eColores.lunes),
|
||||||
@ -70,7 +70,7 @@ const clasesColores = {
|
|||||||
Miercoles: css(eColores.miercoles),
|
Miercoles: css(eColores.miercoles),
|
||||||
Jueves: css(eColores.jueves),
|
Jueves: css(eColores.jueves),
|
||||||
Viernes: css(eColores.viernes),
|
Viernes: css(eColores.viernes),
|
||||||
};
|
}
|
||||||
|
|
||||||
interface DatosProps {
|
interface DatosProps {
|
||||||
id: string,
|
id: string,
|
||||||
@ -97,110 +97,108 @@ interface Props {
|
|||||||
tablaObserver: TablaObserver,
|
tablaObserver: TablaObserver,
|
||||||
}
|
}
|
||||||
|
|
||||||
const claseSeldaSeleccionada = css(e.celdaSeleccionado);
|
const claseSeldaSeleccionada = css(e.celdaSeleccionado)
|
||||||
|
|
||||||
function RenderFila(datos: DatosProps, props: Props) {
|
function RenderFila(datos: DatosProps, props: Props) {
|
||||||
const id = datos.id;
|
const id = datos.id
|
||||||
const txt = datos.txt;
|
const txt = datos.txt
|
||||||
const esLab = datos.esLab;
|
const esLab = datos.esLab
|
||||||
const fnSeleccionar = datos.fnSeleccionar;
|
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
|
// Limpiar los memos, porque cuando se desmonta la celda esos memos quedan sin efecto
|
||||||
onCleanup(() => {
|
onCleanup(() => {
|
||||||
props.tablaObserver.limpiar(id);
|
props.tablaObserver.limpiar(id)
|
||||||
});
|
})
|
||||||
|
|
||||||
const clases = createMemo(
|
const clases = createMemo(
|
||||||
() => {
|
() => {
|
||||||
const clases = [
|
const clases = [
|
||||||
e.celdaCurso,
|
e.celdaCurso,
|
||||||
esLab ? e.celdaCursoLab : e.celdaCursoTeoria,
|
esLab ? e.celdaCursoLab : e.celdaCursoTeoria,
|
||||||
];
|
]
|
||||||
let adicional = "";
|
let adicional = ""
|
||||||
|
|
||||||
const estadoCelda = estadoCeldaMemo();
|
const estadoCelda = estadoCeldaMemo()
|
||||||
|
|
||||||
switch (estadoCelda) {
|
switch (estadoCelda) {
|
||||||
case "Normal": {
|
case "Normal": {
|
||||||
if (estabaResaltado()) {
|
if (estabaResaltado()) {
|
||||||
props.fnDesresaltarFila();
|
props.fnDesresaltarFila()
|
||||||
setEstabaResaltado(false);
|
setEstabaResaltado(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
case "Oculto": {
|
case "Oculto": {
|
||||||
if (estabaResaltado()) {
|
if (estabaResaltado()) {
|
||||||
props.fnDesresaltarFila();
|
props.fnDesresaltarFila()
|
||||||
setEstabaResaltado(false);
|
setEstabaResaltado(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
clases.push(e.celdaOculto);
|
clases.push(e.celdaOculto)
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
case "Resaltado": {
|
case "Resaltado": {
|
||||||
props.fnResaltarFila();
|
props.fnResaltarFila()
|
||||||
setEstabaResaltado(true);
|
setEstabaResaltado(true)
|
||||||
clases.push(e.celdaResaltado);
|
clases.push(e.celdaResaltado)
|
||||||
adicional = clasesColores[props.dia];
|
adicional = clasesColores[props.dia]
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
case "Seleccionado": {
|
case "Seleccionado": {
|
||||||
if (estabaResaltado()) {
|
if (estabaResaltado()) {
|
||||||
props.fnDesresaltarFila();
|
props.fnDesresaltarFila()
|
||||||
setEstabaResaltado(false);
|
setEstabaResaltado(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
clases.push(e.celdaSeleccionado);
|
clases.push(e.celdaSeleccionado)
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
case "ResaltadoOculto": {
|
case "ResaltadoOculto": {
|
||||||
props.fnResaltarFila();
|
props.fnResaltarFila()
|
||||||
setEstabaResaltado(true);
|
setEstabaResaltado(true)
|
||||||
|
|
||||||
clases.push(e.celdaResaltadoOculto);
|
clases.push(e.celdaResaltadoOculto)
|
||||||
adicional = clasesColores[props.dia];
|
adicional = clasesColores[props.dia]
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
case "ResaltadoSeleccionado": {
|
case "ResaltadoSeleccionado": {
|
||||||
props.fnResaltarFila();
|
props.fnResaltarFila()
|
||||||
setEstabaResaltado(true);
|
setEstabaResaltado(true)
|
||||||
|
|
||||||
clases.push(e.celdaResaltadoSeleccionado);
|
clases.push(e.celdaResaltadoSeleccionado)
|
||||||
adicional = clasesColores[props.dia];
|
adicional = clasesColores[props.dia]
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${css(...clases)} ${adicional}`;
|
return `${css(...clases)} ${adicional}`
|
||||||
},
|
},
|
||||||
undefined,
|
undefined,
|
||||||
{
|
(x, y) => x === y,
|
||||||
equals: (x, y) => x === y,
|
)
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button className={clases()}
|
<button className={clases()}
|
||||||
onMouseEnter={() => {
|
onMouseEnter={() => {
|
||||||
props.tablaObserver.resaltar(id);
|
props.tablaObserver.resaltar(id)
|
||||||
}}
|
}}
|
||||||
onMouseLeave={() => {
|
onMouseLeave={() => {
|
||||||
props.tablaObserver.quitarResaltado();
|
props.tablaObserver.quitarResaltado()
|
||||||
}}
|
}}
|
||||||
onClick={fnSeleccionar}
|
onClick={fnSeleccionar}
|
||||||
>
|
>
|
||||||
{txt}
|
{txt}
|
||||||
</button>
|
</button>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CeldaFila(props: Props) {
|
export function CeldaFila(props: Props) {
|
||||||
const datos = props.datos;
|
const datos = props.datos
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={css(e.celdaComun, estilosGlobales.inlineBlock)}>
|
<div className={css(e.celdaComun, estilosGlobales.inlineBlock)}>
|
||||||
@ -208,5 +206,5 @@ export function CeldaFila(props: Props) {
|
|||||||
{(datos) => RenderFila(datos, props)}
|
{(datos) => RenderFila(datos, props)}
|
||||||
</For>
|
</For>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
@ -1,12 +1,12 @@
|
|||||||
import { StyleSheet, css } from "aphrodite";
|
import { StyleSheet, css } from "aphrodite"
|
||||||
import { estilosGlobales } from "../../../../../Estilos";
|
import { estilosGlobales } from "../../Estilos"
|
||||||
import { For, createMemo } from "solid-js";
|
import { For, createMemo } from "solid-js"
|
||||||
import {createStore, Store} from "solid-js/store";
|
import {createStore, Store} from "solid-js/store"
|
||||||
import { Dia, dias } from "../../../../../Store";
|
import { Dia, dias } from "../../Store"
|
||||||
import { CeldaFila } from "./CeldaFila";
|
import { CeldaFila } from "./CeldaFila"
|
||||||
import { DataProcesada } from "../../../../../types/DatosHorario";
|
import { DataProcesada } from "../../types/DatosHorario"
|
||||||
import { coloresBorde, diaANum } from "../Tabla";
|
import { coloresBorde, diaANum } from "../Tabla"
|
||||||
import { TablaObserver } from "../TablaObserver";
|
import { TablaObserver } from "../TablaObserver"
|
||||||
|
|
||||||
const e = StyleSheet.create({
|
const e = StyleSheet.create({
|
||||||
celdaHora: {
|
celdaHora: {
|
||||||
@ -50,7 +50,7 @@ const e = StyleSheet.create({
|
|||||||
celdaResaltadoTransparente: {
|
celdaResaltadoTransparente: {
|
||||||
backgroundColor: "transparent",
|
backgroundColor: "transparent",
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
const [diasResaltados, setDiasResaltados] = createStore({
|
const [diasResaltados, setDiasResaltados] = createStore({
|
||||||
Lunes: 0,
|
Lunes: 0,
|
||||||
@ -58,7 +58,7 @@ const [diasResaltados, setDiasResaltados] = createStore({
|
|||||||
Miercoles: 0,
|
Miercoles: 0,
|
||||||
Jueves: 0,
|
Jueves: 0,
|
||||||
Viernes: 0,
|
Viernes: 0,
|
||||||
} as { [k: string]: number });
|
} as { [k: string]: number })
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
hora: string,
|
hora: string,
|
||||||
@ -69,7 +69,7 @@ interface Props {
|
|||||||
const diasFilter = createMemo(() => Object.entries(diasResaltados)
|
const diasFilter = createMemo(() => Object.entries(diasResaltados)
|
||||||
.filter((x) => x[1] > 0)
|
.filter((x) => x[1] > 0)
|
||||||
.map((x) => x[0] as Dia)
|
.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: () => [
|
const useDiasResaltados: () => [
|
||||||
Store<{ [k: string]: boolean }>,
|
Store<{ [k: string]: boolean }>,
|
||||||
@ -82,26 +82,26 @@ const useDiasResaltados: () => [
|
|||||||
Miercoles: false,
|
Miercoles: false,
|
||||||
Jueves: false,
|
Jueves: false,
|
||||||
Viernes: false,
|
Viernes: false,
|
||||||
} as { [k: string]: boolean });
|
} as { [k: string]: boolean })
|
||||||
|
|
||||||
const fnResaltar = (d: Dia) => {
|
const fnResaltar = (d: Dia) => {
|
||||||
setDiasResaltadosLocal(d, true);
|
setDiasResaltadosLocal(d, true)
|
||||||
setDiasResaltados(d, (v) => v + 1);
|
setDiasResaltados(d, (v) => v + 1)
|
||||||
};
|
}
|
||||||
|
|
||||||
const fnDesresaltar = (d: Dia) => {
|
const fnDesresaltar = (d: Dia) => {
|
||||||
setDiasResaltadosLocal(d, false);
|
setDiasResaltadosLocal(d, false)
|
||||||
setDiasResaltados(d, (v) => v - 1);
|
setDiasResaltados(d, (v) => v - 1)
|
||||||
};
|
}
|
||||||
|
|
||||||
return [diasResaltadosLocal, fnResaltar, fnDesresaltar];
|
return [diasResaltadosLocal, fnResaltar, fnDesresaltar]
|
||||||
};
|
}
|
||||||
|
|
||||||
export function FilaTabla(props: Props) {
|
export function FilaTabla(props: Props) {
|
||||||
const [diasResaltadosLocal, fnResaltar, fnDesresaltar] = useDiasResaltados();
|
const [diasResaltadosLocal, fnResaltar, fnDesresaltar] = useDiasResaltados()
|
||||||
|
|
||||||
const hora = props.hora;
|
const hora = props.hora
|
||||||
const data = props.data;
|
const data = props.data
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{position: "relative"}}>
|
<div style={{position: "relative"}}>
|
||||||
@ -133,10 +133,10 @@ export function FilaTabla(props: Props) {
|
|||||||
</div>
|
</div>
|
||||||
<For each={dias}>
|
<For each={dias}>
|
||||||
{(dia) => {
|
{(dia) => {
|
||||||
const diaStr = dia.substring(0, 2);
|
const diaStr = dia.substring(0, 2)
|
||||||
const horaStr = hora.substring(0, 5);
|
const horaStr = hora.substring(0, 5)
|
||||||
|
|
||||||
const datos = data?.[horaStr]?.[diaStr] ?? [];
|
const datos = data?.[horaStr]?.[diaStr] ?? []
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CeldaFila
|
<CeldaFila
|
||||||
@ -146,11 +146,11 @@ export function FilaTabla(props: Props) {
|
|||||||
dia={dia}
|
dia={dia}
|
||||||
tablaObserver={props.tablaObserver}
|
tablaObserver={props.tablaObserver}
|
||||||
/>
|
/>
|
||||||
);
|
)
|
||||||
}}
|
}}
|
||||||
</For>
|
</For>
|
||||||
<div className={css(e.filaBorde)} />
|
<div className={css(e.filaBorde)} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
@ -1,7 +1,6 @@
|
|||||||
import { createMemo, createEffect, untrack } from "solid-js";
|
import { createMemo, createEffect, untrack } from "solid-js"
|
||||||
import {createStore, SetStoreFunction, Store, produce} from "solid-js/store";
|
import {createStore, SetStoreFunction, Store, produce} from "solid-js/store"
|
||||||
import { DatosGrupo } from "../../../../types/DatosHorario";
|
import { DatosGrupo } from "../types/DatosHorario"
|
||||||
import { gruposSeleccionados } from "../../../../Store";
|
|
||||||
|
|
||||||
const createMemoDefault = <T>(f: () => T) => createMemo<T>(
|
const createMemoDefault = <T>(f: () => T) => createMemo<T>(
|
||||||
f,
|
f,
|
||||||
@ -9,7 +8,7 @@ const createMemoDefault = <T>(f: () => T) => createMemo<T>(
|
|||||||
{
|
{
|
||||||
equals: (x, y) => x === y,
|
equals: (x, y) => x === y,
|
||||||
},
|
},
|
||||||
);
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* - Normal
|
* - Normal
|
||||||
@ -63,18 +62,17 @@ export class TablaObserver {
|
|||||||
curso: undefined,
|
curso: undefined,
|
||||||
esLab: undefined,
|
esLab: undefined,
|
||||||
grupo: undefined,
|
grupo: undefined,
|
||||||
});
|
})
|
||||||
this.resaltado = resaltado;
|
this.resaltado = resaltado
|
||||||
this.setResaltado = setResaltado;
|
this.setResaltado = setResaltado
|
||||||
|
|
||||||
const [seleccionado, setSeleccionado] = createStore<ISeleccionado>({});
|
const [seleccionado, setSeleccionado] = createStore<ISeleccionado>({})
|
||||||
this.seleccionado = seleccionado;
|
this.seleccionado = seleccionado
|
||||||
this.setSeleccionado = setSeleccionado;
|
this.setSeleccionado = setSeleccionado
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Crea un memo que indica el estado de la celda
|
* Crea un memo que indica el estado de la celda
|
||||||
* @param id_laboratorio Id del laboratorio
|
|
||||||
* @param anio El año
|
* @param anio El año
|
||||||
* @param curso Curso abreviado
|
* @param curso Curso abreviado
|
||||||
* @param esLab Si es laboratorio
|
* @param esLab Si es laboratorio
|
||||||
@ -82,82 +80,89 @@ export class TablaObserver {
|
|||||||
* @param datosGrupo Contiene `seleccionado`, se usa ese valor reactivo
|
* @param datosGrupo Contiene `seleccionado`, se usa ese valor reactivo
|
||||||
*/
|
*/
|
||||||
private registrar(
|
private registrar(
|
||||||
id_laboratorio: number,
|
|
||||||
anio: string,
|
anio: string,
|
||||||
curso: string,
|
curso: string,
|
||||||
esLab: boolean,
|
esLab: boolean,
|
||||||
grupo: string,
|
grupo: string,
|
||||||
datosGrupo: DatosGrupo,
|
datosGrupo: DatosGrupo,
|
||||||
): () => EstadoCelda {
|
): () => EstadoCelda {
|
||||||
const resaltado = this.resaltado;
|
const resaltado = this.resaltado
|
||||||
const resaltadoMemo = createMemoDefault(() => {
|
const resaltadoMemo = createMemoDefault(() => {
|
||||||
if (resaltado.anio === anio && resaltado.curso === curso) {
|
if (resaltado.anio === anio && resaltado.curso === curso) {
|
||||||
if (resaltado.esLab === undefined) {
|
if (resaltado.esLab === undefined) {
|
||||||
return true;
|
return true
|
||||||
} else if (resaltado.esLab !== esLab) {
|
} else if (resaltado.esLab !== esLab) {
|
||||||
return false;
|
return false
|
||||||
} else {
|
} else {
|
||||||
if (resaltado.grupo === undefined) {
|
if (resaltado.grupo === undefined) {
|
||||||
return true;
|
return true
|
||||||
} else return resaltado.grupo === grupo;
|
} else return resaltado.grupo === grupo
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
// Registrar curso en `seleccionado`
|
// Registrar curso en `seleccionado`
|
||||||
this.setSeleccionado((obj: Store<ISeleccionado>) => {
|
this.setSeleccionado((obj: Store<ISeleccionado>) => {
|
||||||
const nuevoObj = {...obj};
|
const nuevoObj = {...obj}
|
||||||
|
|
||||||
if (!nuevoObj[anio]) {
|
if (!nuevoObj[anio]) {
|
||||||
nuevoObj[anio] = {};
|
nuevoObj[anio] = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!nuevoObj[anio][curso]) {
|
if (!nuevoObj[anio][curso]) {
|
||||||
nuevoObj[anio][curso] = {
|
nuevoObj[anio][curso] = {
|
||||||
Laboratorio: [],
|
Laboratorio: [],
|
||||||
Teoria: [],
|
Teoria: [],
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nuevoObj;
|
return nuevoObj
|
||||||
});
|
})
|
||||||
|
|
||||||
// Crear un effect para que cada vez que la celda se seleccione se actualize `seleccionado`
|
// Crear un effect para que cada vez que la celda se seleccione se actualize `seleccionado`
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
const seleccionado = datosGrupo.seleccionado;
|
const seleccionado = datosGrupo.seleccionado
|
||||||
if (seleccionado) {
|
if (seleccionado) {
|
||||||
this.setSeleccionado(anio, curso, esLab ? "Laboratorio" : "Teoria", (x) => [...x, grupo]);
|
this.setSeleccionado(anio, curso, esLab ? "Laboratorio" : "Teoria", (x) => [...x, grupo])
|
||||||
} else {
|
} 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 => {
|
return createMemoDefault((): EstadoCelda => {
|
||||||
const resaltado = resaltadoMemo();
|
const resaltado = resaltadoMemo()
|
||||||
const seleccionado = seleccionadoMemo();
|
const seleccionado = seleccionadoMemo()
|
||||||
|
|
||||||
switch (seleccionado) {
|
switch (seleccionado) {
|
||||||
case "Normal": {
|
case "Normal": {
|
||||||
return resaltado ? "Resaltado" : "Normal";
|
return resaltado ? "Resaltado" : "Normal"
|
||||||
}
|
}
|
||||||
case "Oculto": {
|
case "Oculto": {
|
||||||
return resaltado ? "ResaltadoOculto" : "Oculto";
|
return resaltado ? "ResaltadoOculto" : "Oculto"
|
||||||
}
|
}
|
||||||
case "Seleccionado": {
|
case "Seleccionado": {
|
||||||
return resaltado ? "ResaltadoSeleccionado" : "Seleccionado";
|
return resaltado ? "ResaltadoSeleccionado" : "Seleccionado"
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
let _: never;
|
let _: never
|
||||||
// eslint-disable-next-line prefer-const
|
// eslint-disable-next-line prefer-const
|
||||||
_ = seleccionado;
|
_ = seleccionado
|
||||||
return _;
|
return _
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -167,13 +172,13 @@ export class TablaObserver {
|
|||||||
*/
|
*/
|
||||||
registrarConId(id: string, datosGrupo: DatosGrupo): () => EstadoCelda {
|
registrarConId(id: string, datosGrupo: DatosGrupo): () => EstadoCelda {
|
||||||
if (this.memos[id]) {
|
if (this.memos[id]) {
|
||||||
return this.memos[id];
|
return this.memos[id]
|
||||||
}
|
}
|
||||||
|
|
||||||
const [, anio, curso, lab, grupo] = id.split("_");
|
const [, anio, curso, lab, grupo] = id.split("_")
|
||||||
const memo = this.registrar(datosGrupo.id_laboratorio, anio, curso, lab === "L", grupo, datosGrupo);
|
const memo = this.registrar(anio, curso, lab === "L", grupo, datosGrupo)
|
||||||
this.memos[id] = memo;
|
this.memos[id] = memo
|
||||||
return memo;
|
return memo
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -181,24 +186,24 @@ export class TablaObserver {
|
|||||||
* @param id Id a resaltar - YYYYMMDD_Año_Curso[\_Lab[_Grupo]]
|
* @param id Id a resaltar - YYYYMMDD_Año_Curso[\_Lab[_Grupo]]
|
||||||
*/
|
*/
|
||||||
resaltar(id: string) {
|
resaltar(id: string) {
|
||||||
const [, anio, curso, lab, grupo] = id.split("_");
|
const [, anio, curso, lab, grupo] = id.split("_")
|
||||||
if (anio === undefined || curso === undefined) {
|
if (anio === undefined || curso === undefined) {
|
||||||
console.error("Error al intentar resaltar celda: anio o curso son undefined:", anio, curso);
|
console.error("Error al intentar resaltar celda: anio o curso son undefined:", anio, curso)
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let esLab: boolean | undefined;
|
let esLab: boolean | undefined
|
||||||
if (lab === undefined) {
|
if (lab === undefined) {
|
||||||
esLab = undefined;
|
esLab = undefined
|
||||||
} else {
|
} else {
|
||||||
esLab = lab === "L";
|
esLab = lab === "L"
|
||||||
}
|
}
|
||||||
this.setResaltado({
|
this.setResaltado({
|
||||||
anio,
|
anio,
|
||||||
curso,
|
curso,
|
||||||
esLab,
|
esLab,
|
||||||
grupo,
|
grupo,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
quitarResaltado() {
|
quitarResaltado() {
|
||||||
@ -207,10 +212,10 @@ export class TablaObserver {
|
|||||||
curso: undefined,
|
curso: undefined,
|
||||||
esLab: undefined,
|
esLab: undefined,
|
||||||
grupo: undefined,
|
grupo: undefined,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
limpiar(id: string) {
|
limpiar(id: string) {
|
||||||
delete this.memos[id];
|
delete this.memos[id]
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,5 +1,5 @@
|
|||||||
import {createStore, SetStoreFunction, Store} from "solid-js/store";
|
import {createStore, SetStoreFunction, Store} from "solid-js/store"
|
||||||
import { Curso, ListaCursosUsuario } from "../../../../types/DatosHorario";
|
import { Curso, ListaCursosUsuario } from "../types/DatosHorario"
|
||||||
|
|
||||||
interface ReturnType {
|
interface ReturnType {
|
||||||
listaCursos: Store<ListaCursosUsuario>,
|
listaCursos: Store<ListaCursosUsuario>,
|
||||||
@ -9,27 +9,33 @@ interface ReturnType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const useListaCursos = (): ReturnType => {
|
export const useListaCursos = (): ReturnType => {
|
||||||
const [listaCursos, setListaCursos] = createStore<ListaCursosUsuario>({});
|
const [listaCursos, setListaCursos] = createStore<ListaCursosUsuario>({
|
||||||
|
sigIndice: 0,
|
||||||
|
cursos: [],
|
||||||
|
})
|
||||||
|
|
||||||
const agregarCursoALista = (curso: Curso): Curso => {
|
const agregarCursoALista = (curso: Curso): Curso => {
|
||||||
// Si el horario ya se habia agregado, ocultarlo
|
// Si el horario ya se habia agregado, ocultarlo
|
||||||
if (listaCursos[curso.nombre]) {
|
const cursoActualIndex = listaCursos.cursos.findIndex((x) => x.nombre === curso.nombre)
|
||||||
setListaCursos(curso.nombre, "oculto", (x) => !x);
|
if (cursoActualIndex !== -1) {
|
||||||
return listaCursos[curso.nombre];
|
setListaCursos("cursos", cursoActualIndex, "oculto", (x) => !x)
|
||||||
|
return listaCursos.cursos[cursoActualIndex]
|
||||||
} else {
|
} else {
|
||||||
setListaCursos(curso.nombre, curso);
|
setListaCursos("cursos", listaCursos.sigIndice, curso)
|
||||||
return listaCursos[curso.nombre];
|
setListaCursos("sigIndice", (x) => x + 1)
|
||||||
|
return listaCursos.cursos[listaCursos.sigIndice - 1]
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const eliminarCursosDeLista = () => {
|
const eliminarCursosDeLista = () => {
|
||||||
setListaCursos({});
|
setListaCursos("cursos", [])
|
||||||
};
|
setListaCursos("sigIndice", 0)
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
listaCursos,
|
listaCursos,
|
||||||
setListaCursos,
|
setListaCursos,
|
||||||
agregarCursoALista,
|
agregarCursoALista,
|
||||||
eliminarCursosDeLista,
|
eliminarCursosDeLista,
|
||||||
};
|
}
|
||||||
};
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import { StyleSheet } from "aphrodite";
|
import { StyleSheet } from "aphrodite"
|
||||||
|
|
||||||
export const estilosGlobales = StyleSheet.create({
|
export const estilosGlobales = StyleSheet.create({
|
||||||
contenedor: {
|
contenedor: {
|
||||||
@ -58,4 +58,4 @@ export const estilosGlobales = StyleSheet.create({
|
|||||||
padding: "0.25rem 0.35rem",
|
padding: "0.25rem 0.35rem",
|
||||||
borderRadius: "5px",
|
borderRadius: "5px",
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
@ -1,31 +1,31 @@
|
|||||||
import { createSignal, JSX } from "solid-js";
|
import { createSignal, JSX } from "solid-js"
|
||||||
|
|
||||||
export const useRouter = (): () => string => {
|
export const useRouter = (): () => string => {
|
||||||
let rutaPrevia = window.location.hash;
|
let rutaPrevia = window.location.hash
|
||||||
|
|
||||||
if (rutaPrevia === "") {
|
if (rutaPrevia === "") {
|
||||||
window.history.pushState({}, "Horarios UNSA", "#/");
|
window.history.pushState({}, "Horarios UNSA", "#/")
|
||||||
rutaPrevia = "/";
|
rutaPrevia = "/"
|
||||||
} else {
|
} else {
|
||||||
rutaPrevia = rutaPrevia.substr(1);
|
rutaPrevia = rutaPrevia.substr(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
const [rutaActual, setRutaActual] = createSignal(rutaPrevia);
|
const [rutaActual, setRutaActual] = createSignal(rutaPrevia)
|
||||||
|
|
||||||
const fnEffect = () => {
|
const fnEffect = () => {
|
||||||
const nuevaRuta = window.location.hash.substr(1);
|
const nuevaRuta = window.location.hash.substr(1)
|
||||||
setRutaActual(nuevaRuta);
|
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 }) {
|
export function RouterLink(props: { to: string, className?: string, children: JSX.Element }) {
|
||||||
return (
|
return (
|
||||||
<a className={props.className} href={`/#${props.to}`}>
|
<a className={props.className} href={`/#${props.to}`}>
|
||||||
{props.children}
|
{props.children}
|
||||||
</a>
|
</a>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { createSignal } from "solid-js";
|
import { createSignal } from "solid-js";
|
||||||
import { createStore } from "solid-js/store";
|
|
||||||
|
|
||||||
export type Dia = "Lunes" | "Martes" | "Miercoles" | "Jueves" | "Viernes";
|
export type Dia = "Lunes" | "Martes" | "Miercoles" | "Jueves" | "Viernes";
|
||||||
|
|
||||||
@ -29,7 +28,6 @@ export const horasDescanso = [
|
|||||||
"15:40 - 15:50",
|
"15:40 - 15:50",
|
||||||
"17:30 - 17:40",
|
"17:30 - 17:40",
|
||||||
];
|
];
|
||||||
export const SERVER_PATH = "https://matriculas.fly.dev/sistema";
|
|
||||||
|
|
||||||
const numImgGuardado = Number(localStorage.getItem("num-img") ?? "0");
|
const numImgGuardado = Number(localStorage.getItem("num-img") ?? "0");
|
||||||
const tamanoLetraGuardado = Number(/* localStorage.getItem("tamano-letra") ?? */ "16");
|
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 [numWallpaper, setNumWallpaper] = createSignal(numImgGuardado);
|
||||||
export const [tamanoLetra, setTamanoLetra] = createSignal(tamanoLetraGuardado);
|
export const [tamanoLetra, setTamanoLetra] = createSignal(tamanoLetraGuardado);
|
||||||
export const [isMobile, setIsMobile] = createSignal(screen.width < 500);
|
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 { estilosGlobales } from "../Estilos";
|
||||||
import { StyleSheet, css } from "aphrodite";
|
import { StyleSheet, css } from "aphrodite";
|
||||||
import { Separador } from "../Separador";
|
import { Separador } from "../Separador";
|
||||||
import { Tabla } from "./pc/Sistemas/ContenedorHorarios/Tabla";
|
import { Tabla } from "../ContenedorHorarios/Tabla";
|
||||||
import { TablaObserver } from "./pc/Sistemas/ContenedorHorarios/TablaObserver";
|
import { TablaObserver } from "../ContenedorHorarios/TablaObserver";
|
||||||
import { Curso, Cursos } from "../types/DatosHorario";
|
import { Curso, Cursos } from "../types/DatosHorario";
|
||||||
import { For, createMemo } from "solid-js";
|
import { For, createMemo } from "solid-js";
|
||||||
import {createStore} from "solid-js/store";
|
import {createStore} from "solid-js/store";
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
import { estilosGlobales } from "../Estilos";
|
import { estilosGlobales } from "../Estilos";
|
||||||
import { StyleSheet, css } from "aphrodite/no-important";
|
import { StyleSheet, css } from "aphrodite/no-important";
|
||||||
import { RouterLink } from "../Router";
|
import { RouterLink } from "../Router";
|
||||||
import { batch, createSignal, Show } from "solid-js";
|
import { Show } from "solid-js";
|
||||||
import { isMobile, setGruposSeleccionados } from "../Store";
|
import { isMobile } from "../Store";
|
||||||
import { MobileIndex } from "./MobileIndex";
|
|
||||||
import { loginFn } from "../API/Login";
|
|
||||||
|
|
||||||
const e = StyleSheet.create({
|
const e = StyleSheet.create({
|
||||||
contenedorGlobal: {
|
contenedorGlobal: {
|
||||||
@ -31,51 +29,55 @@ const e = StyleSheet.create({
|
|||||||
verticalAlign: "bottom",
|
verticalAlign: "bottom",
|
||||||
marginRight: "0.5rem",
|
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",
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
export function Index() {
|
const inputElement = <input type="email" placeholder="Correo electronico" className={css(s.entrada)} />;
|
||||||
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) => {
|
const login = () => {
|
||||||
ev.preventDefault();
|
console.log((inputElement as HTMLInputElement).value);
|
||||||
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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
window.location.href = "#/pc/ver-matricula/";
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<Show when={!isMobile()}>
|
<Show when={!isMobile()}>
|
||||||
<div class={css(e.contenedorGlobal)}>
|
<div className={css(e.contenedorGlobal)}>
|
||||||
<div class={css(e.cont)}>
|
<div className={css(e.cont)}>
|
||||||
<div class={css(estilosGlobales.contenedor, estilosGlobales.inlineBlock, e.cont)}>
|
<div className={css(estilosGlobales.contenedor, estilosGlobales.inlineBlock, e.cont)}>
|
||||||
<h1 style={{
|
<h1 style={{
|
||||||
"text-align": "center",
|
"text-align": "center",
|
||||||
"font-size": "1.75rem",
|
"font-size": "1.75rem",
|
||||||
@ -83,26 +85,43 @@ export function Index() {
|
|||||||
>
|
>
|
||||||
Horarios UNSA
|
Horarios UNSA
|
||||||
</h1>
|
</h1>
|
||||||
<p class={css(e.parrafo)}>
|
<p className={css(e.parrafo)}>
|
||||||
Inicia sesión con tu correo institucional.
|
Esta página te permite crear tu horario fácilmente, sin importar de que
|
||||||
<br />
|
año son los cursos.
|
||||||
{inputElement}
|
</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>
|
</p>
|
||||||
<span style={msgErrorVisible() ? "opacity: 1; color: red;" : "opacity: 0;"}>
|
|
||||||
El correo es invalido
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<button onClick={login} class={css(estilosGlobales.contenedor, estilosGlobales.contenedorCursor, e.botonAccion)}>
|
<RouterLink
|
||||||
Iniciar sesion
|
to={"/sistemas/"}
|
||||||
</button>
|
className={css(estilosGlobales.contenedor, estilosGlobales.contenedorCursor, e.botonAccion)}
|
||||||
<br />
|
>
|
||||||
<br />
|
Ing. de Sistemas
|
||||||
|
</RouterLink>
|
||||||
|
{/*
|
||||||
|
<button disabled className={css(estilosGlobales.contenedor, estilosGlobales.contenedorCursor, e.botonAccion)}>
|
||||||
|
Otras carreras
|
||||||
|
</button>
|
||||||
|
*/}
|
||||||
|
<RouterLink
|
||||||
|
to={"/editor/"}
|
||||||
|
className={css(estilosGlobales.contenedor, estilosGlobales.contenedorCursor, e.botonAccion)}
|
||||||
|
>
|
||||||
|
<i className={`${css(e.iconoGitHub)} ph-pencil`} />
|
||||||
|
Editor
|
||||||
|
</RouterLink>
|
||||||
<a
|
<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/"
|
href="https://github.com/Araozu/horarios-unsa-2/"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
<i class={`${css(e.iconoGitHub)} ph-code`} />
|
<i className={`${css(e.iconoGitHub)} ph-code`} />
|
||||||
Código fuente en GitHub
|
Código fuente en GitHub
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</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 {
|
:root {
|
||||||
--color-texto: white;
|
--color-texto: white;
|
||||||
--color-primario: #531925;
|
--color-primario: #531925;
|
||||||
--color-borde: rgba(83, 25, 37, 0.49);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
|
@ -27,7 +27,6 @@ export interface DatosHorarioRaw {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface DatosGrupo {
|
export interface DatosGrupo {
|
||||||
id_laboratorio: number,
|
|
||||||
Docente: string,
|
Docente: string,
|
||||||
Horas: string[]
|
Horas: string[]
|
||||||
seleccionado: boolean
|
seleccionado: boolean
|
||||||
@ -45,8 +44,9 @@ export interface Curso {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ListaCursosUsuario = {
|
export interface ListaCursosUsuario {
|
||||||
[key: string]: Curso
|
sigIndice: number,
|
||||||
|
cursos: Curso[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Cursos {
|
export interface Cursos {
|
||||||
|
Loading…
Reference in New Issue
Block a user