Agregar linter

master
Araozu 2021-03-25 21:39:34 -05:00
parent e2503967cf
commit f325f08fc8
22 changed files with 1845 additions and 810 deletions

89
.eslintrc.yml Normal file
View File

@ -0,0 +1,89 @@
env:
browser: true
es2021: true
extends:
- 'eslint:recommended'
- 'plugin:@typescript-eslint/recommended'
parser: '@typescript-eslint/parser'
parserOptions:
ecmaVersion: 12
sourceType: module
plugins:
- '@typescript-eslint'
- react
rules:
"@typescript-eslint/ban-ts-comment": off
"@typescript-eslint/no-empty-function": off
indent:
- error
- 4
- SwitchCase: 1
linebreak-style:
- error
- unix
quotes:
- error
- double
semi:
- error
- never
react/jsx-pascal-case: error
react/jsx-closing-bracket-location: error
react/jsx-closing-tag-location: error
no-multi-spaces: error
react/jsx-tag-spacing: error
react/jsx-boolean-value: error
react/jsx-wrap-multilines: error
react/self-closing-comp: error
prefer-const: error
no-const-assign: error
no-var: error
array-callback-return: error
prefer-template: error
template-curly-spacing: error
no-useless-escape: error
wrap-iife: error
no-loop-func: error
default-param-last: error
space-before-function-paren:
- error
- never
space-before-blocks: error
no-param-reassign: error
function-paren-newline: error
comma-dangle:
- error
- always-multiline
arrow-spacing: error
arrow-parens: error
arrow-body-style: error
no-confusing-arrow: error
implicit-arrow-linebreak: error
no-duplicate-imports: error
object-curly-newline: error
dot-notation: error
one-var:
- error
- never
no-multi-assign: error
no-plusplus: error
operator-linebreak: error
eqeqeq: error
no-case-declarations: error
no-nested-ternary: error
no-unneeded-ternary: error
no-mixed-operators: error
nonblock-statement-body-position: error
brace-style: error
keyword-spacing: error
space-infix-ops: error
eol-last: error
newline-per-chained-call: error
no-whitespace-before-property: error
space-in-parens: error
array-bracket-spacing: error
key-spacing: error
no-trailing-spaces: error
comma-style: error
radix: error
no-new-wrappers: error

View File

@ -5,7 +5,11 @@
"devDependencies": {
"@types/jest": "26.0.20",
"@types/node": "14.14.20",
"@typescript-eslint/eslint-plugin": "^4.19.0",
"@typescript-eslint/parser": "^4.19.0",
"aphrodite": "^2.4.0",
"eslint": "^7.22.0",
"eslint-plugin-react": "^7.23.1",
"normalize.css": "^8.0.1",
"solid-js": "^0.23.11",
"solid-scripts": "0.0.50",

File diff suppressed because it is too large Load Diff

View File

@ -1,43 +1,44 @@
import { BarraSuperior } from "./BarraSuperior";
import { ContenedorHorarios } from "./ContenedorHorarios/ContenedorHorarios";
import { Wallpaper } from "./Wallpaper";
import { Show, createSignal } from "solid-js";
import { css } from "aphrodite";
import { estilosGlobales } from "./Estilos";
import { Creditos } from "./Creditos";
import { BarraSuperior } from "./BarraSuperior"
import { ContenedorHorarios } from "./ContenedorHorarios/ContenedorHorarios"
import { Wallpaper } from "./Wallpaper"
import { Show, createSignal } from "solid-js"
import { css } from "aphrodite"
import { estilosGlobales } from "./Estilos"
import { Creditos } from "./Creditos"
function App() {
/// @ts-ignore
const soportaBackdropFilter = document.body.style.backdropFilter !== undefined;
const mostrarMensajeBackdropFilterRaw = localStorage.getItem("mensaje-backdrop-filter-oculto") !== undefined;
const soportaBackdropFilter = document.body.style.backdropFilter !== undefined
const mostrarMensajeBackdropFilterRaw = localStorage.getItem("mensaje-backdrop-filter-oculto") !== undefined
const [mostrarMensajeBackdropFilter, setMostrarMensaje] = createSignal(mostrarMensajeBackdropFilterRaw);
const [mostrarMensajeBackdropFilter, setMostrarMensaje] = createSignal(mostrarMensajeBackdropFilterRaw)
const ocultarMensajeBackdropFilter = () => {
setMostrarMensaje(false);
localStorage.setItem("mensaje-backdrop-filter-oculto", "true");
};
setMostrarMensaje(false)
localStorage.setItem("mensaje-backdrop-filter-oculto", "true")
}
return (
<div class="App">
<Wallpaper/>
<BarraSuperior/>
<Wallpaper />
<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.&nbsp;
<span className={css(estilosGlobales.contenedorCursor, estilosGlobales.contenedorCursorSoft)}
onClick={ocultarMensajeBackdropFilter}
<span
className={css(estilosGlobales.contenedorCursor, estilosGlobales.contenedorCursorSoft)}
onClick={ocultarMensajeBackdropFilter}
>
No volver a mostrar.
</span>
</div>
</Show>
<div style={{width: "100%", height: "0.5rem"}}/>
<ContenedorHorarios/>
<Creditos/>
<div style={{width: "100%", height: "0.5rem"}} />
<ContenedorHorarios />
<Creditos />
</div>
);
)
}
export default App;
export default App

View File

@ -1,101 +1,107 @@
import { estilosGlobales } from "./Estilos";
import { StyleSheet, css } from "aphrodite";
import { numWallpaper, setNumWallpaper } from "./Store";
import { estilosGlobales } from "./Estilos"
import { StyleSheet, css } from "aphrodite"
import { numWallpaper, setNumWallpaper } from "./Store"
const totalWallpapers = 5;
const totalWallpapers = 5
const e = StyleSheet.create({
contCambiador: {
userSelect: "none"
userSelect: "none",
},
boton: {
cursor: "pointer",
textDecoration: "underline",
"::before": {
fontSize: "1rem",
transform: "translateY(0.2rem)"
}
transform: "translateY(0.2rem)",
},
},
botonDesactivado: {
cursor: "not-allowed",
textDecoration: "none"
textDecoration: "none",
},
botonLeft: {
paddingRight: "0.5rem",
marginRight: "0.25rem"
marginRight: "0.25rem",
},
botonRight: {
paddingLeft: "0.5rem",
marginRight: "0.25rem"
}
});
marginRight: "0.25rem",
},
})
const retrocederWallpaper = () => {
const num = numWallpaper();
const num = numWallpaper()
if (num > 0) {
setNumWallpaper(num - 1);
localStorage.setItem("num-img", (num - 1).toString());
setNumWallpaper(num - 1)
localStorage.setItem("num-img", (num - 1).toString())
} else {
setNumWallpaper(totalWallpapers);
localStorage.setItem("num-img", (totalWallpapers).toString());
setNumWallpaper(totalWallpapers)
localStorage.setItem("num-img", (totalWallpapers).toString())
}
};
}
const avanzarWallpaper = () => {
const num = numWallpaper();
const num = numWallpaper()
if (num < totalWallpapers) {
setNumWallpaper(num + 1);
localStorage.setItem("num-img", (num + 1).toString());
setNumWallpaper(num + 1)
localStorage.setItem("num-img", (num + 1).toString())
} else {
setNumWallpaper(0);
localStorage.setItem("num-img", (0).toString());
setNumWallpaper(0)
localStorage.setItem("num-img", (0).toString())
}
};
}
function CambiadorImg() {
return <div className={css(estilosGlobales.inlineBlock, e.contCambiador)}>
<span className={css(estilosGlobales.contenedor, estilosGlobales.inlineBlock)}>
<i
className={"ph-arrow-left " + css(e.boton, e.botonLeft, estilosGlobales.contenedorCursorSoft)}
onClick={retrocederWallpaper}
title={"Cambiar imagen de fondo"}
/>
Img. {numWallpaper() + 1}
<i
className={"ph-arrow-right " + css(e.boton, e.botonRight, estilosGlobales.contenedorCursorSoft)}
onClick={avanzarWallpaper}
title={"Cambiar imagen de fondo"}
/>
</span>
</div>;
return (
<div className={css(estilosGlobales.inlineBlock, e.contCambiador)}>
<span className={css(estilosGlobales.contenedor, estilosGlobales.inlineBlock)}>
<i
className={`ph-arrow-left ${css(e.boton, e.botonLeft, estilosGlobales.contenedorCursorSoft)}`}
onClick={retrocederWallpaper}
title={"Cambiar imagen de fondo"}
/>
Img. {numWallpaper() + 1}
<i
className={`ph-arrow-right ${css(e.boton, e.botonRight, estilosGlobales.contenedorCursorSoft)}`}
onClick={avanzarWallpaper}
title={"Cambiar imagen de fondo"}
/>
</span>
</div>
)
}
const estilos = StyleSheet.create({
tituloPrincipal: {
fontWeight: "bold"
}
});
fontWeight: "bold",
},
})
export function BarraSuperior() {
return <header>
<span className={css(
estilosGlobales.contenedor,
estilosGlobales.inlineBlock,
estilos.tituloPrincipal
)}>
Horarios Unsa
</span>
<a href="https://github.com/Araozu/horarios-unsa-2/" target="_blank" title={"Ver codigo fuente en GitHub"}
className={css(
estilosGlobales.contenedor,
estilosGlobales.inlineBlock,
estilosGlobales.contenedorCursor
)}>
GitHub
<i class="ph-arrow-up-right"/>
</a>
<CambiadorImg/>
<span className={css(estilosGlobales.contenedor, estilosGlobales.inlineBlock)}>2021-A</span>
<span className={css(estilosGlobales.contenedor, estilosGlobales.inlineBlock)}>Ingeniería de Sistemas</span>
</header>;
return (
<header>
<span className={css(
estilosGlobales.contenedor,
estilosGlobales.inlineBlock,
estilos.tituloPrincipal,
)}
>
Horarios Unsa
</span>
<a href="https://github.com/Araozu/horarios-unsa-2/" target="_blank" title={"Ver codigo fuente en GitHub"}
className={css(
estilosGlobales.contenedor,
estilosGlobales.inlineBlock,
estilosGlobales.contenedorCursor,
)}
>
GitHub
<i class="ph-arrow-up-right" />
</a>
<CambiadorImg />
<span className={css(estilosGlobales.contenedor, estilosGlobales.inlineBlock)}>2021-A</span>
<span className={css(estilosGlobales.contenedor, estilosGlobales.inlineBlock)}>Ingeniería de Sistemas</span>
</header>
)
}

View File

@ -1,5 +1,5 @@
import { css } from "aphrodite";
import { estilosGlobales } from "../Estilos";
import { css } from "aphrodite"
import { estilosGlobales } from "../Estilos"
interface BotonMaxMinProps {
icono: string,
@ -8,16 +8,18 @@ interface BotonMaxMinProps {
}
export function BotonIcono(props: BotonMaxMinProps) {
return <div title={props.titulo}
onClick={props.onClick}
className={css(
estilosGlobales.contenedor,
estilosGlobales.inlineBlock,
estilosGlobales.contenedorCursor,
estilosGlobales.contenedorCursorSoft,
estilosGlobales.contenedorPhospor
)}
>
<i className={css(estilosGlobales.botonPhospor) + " " + props.icono}/>
</div>
return (
<div title={props.titulo}
onClick={props.onClick}
className={css(
estilosGlobales.contenedor,
estilosGlobales.inlineBlock,
estilosGlobales.contenedorCursor,
estilosGlobales.contenedorCursorSoft,
estilosGlobales.contenedorPhospor,
)}
>
<i className={`${css(estilosGlobales.botonPhospor)} ${props.icono}`} />
</div>
)
}

View File

@ -1,6 +1,6 @@
import { css } from "aphrodite";
import { estilosGlobales } from "../Estilos";
import { EstadoLayout } from "./ContenedorHorarios";
import { css } from "aphrodite"
import { estilosGlobales } from "../Estilos"
import { EstadoLayout } from "./ContenedorHorarios"
interface BotonMaxMinProps {
fnMaximizar: () => void,
@ -10,30 +10,33 @@ interface BotonMaxMinProps {
}
export function BotonMaxMin(props: BotonMaxMinProps) {
const horariosMax = () => props.estadoActualLayout() === props.estadoLayoutMax;
const horariosMax = () => props.estadoActualLayout() === props.estadoLayoutMax
const tituloBoton = () => horariosMax() ? "Minimizar" : "Maximizar";
const iconoBoton = () => horariosMax() ? "ph-arrows-in" : "ph-arrows-out";
const tituloBoton = () => (horariosMax() ? "Minimizar" : "Maximizar")
const iconoBoton = () => (horariosMax() ? "ph-arrows-in" : "ph-arrows-out")
const funcionBoton = () => {
const estaMaximizado = horariosMax();
const estaMaximizado = horariosMax()
if (estaMaximizado) {
props.fnMinimizar();
props.fnMinimizar()
} else {
props.fnMaximizar();
props.fnMaximizar()
}
};
}
return <div title={tituloBoton()}
onClick={funcionBoton}
className={css(
estilosGlobales.contenedor,
estilosGlobales.inlineBlock,
estilosGlobales.contenedorCursor,
estilosGlobales.contenedorCursorSoft,
estilosGlobales.contenedorPhospor
)}
>
<i className={css(estilosGlobales.botonPhospor) + " " + iconoBoton()}/>
</div>
return (
<div
title={tituloBoton()}
onClick={funcionBoton}
className={css(
estilosGlobales.contenedor,
estilosGlobales.inlineBlock,
estilosGlobales.contenedorCursor,
estilosGlobales.contenedorCursorSoft,
estilosGlobales.contenedorPhospor,
)}
>
<i className={`${css(estilosGlobales.botonPhospor)} ${iconoBoton()}`} />
</div>
)
}

View File

@ -1,7 +1,7 @@
import YAML from "yaml";
import { css, StyleSheet } from "aphrodite";
import { MiHorario } from "./MiHorario";
import { Horarios } from "./Horarios";
import YAML from "yaml"
import { css, StyleSheet } from "aphrodite"
import { MiHorario } from "./MiHorario"
import { Horarios } from "./Horarios"
import {
Anios,
Cursos,
@ -10,126 +10,131 @@ import {
DatosHorario,
DatosHorarioRaw,
DatosGrupo,
ListaCursosUsuario
} from "../types/DatosHorario";
import { estilosGlobales } from "../Estilos";
import { batch, createEffect, createMemo, createSignal, createState, Show } from "solid-js";
import { useListaCursos } from "./useListaCursos";
ListaCursosUsuario,
} from "../types/DatosHorario"
import { estilosGlobales } from "../Estilos"
import { batch, createEffect, createMemo, createSignal, createState, Show } from "solid-js"
import { useListaCursos } from "./useListaCursos"
const datosPromise = (async () => {
const file = await fetch("/horarios/2020_2_fps_ingenieriadesistemas.yaml");
const text = await file.text();
const datosRaw = YAML.parse(text) as DatosHorarioRaw;
const datosPromise = (async() => {
const file = await fetch("/horarios/2020_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: {}
};
años: {},
}
const anios: Anios = {}
for (const [nombreAnio, anio] of Object.entries(datosRaw.años)) {
const anioData: Cursos = {};
const anioData: Cursos = {}
for (const [nombreCurso, curso] of Object.entries(anio)) {
const gruposTeoria: { [k: string]: DatosGrupo } = {};
const gruposTeoria: { [k: string]: DatosGrupo } = {}
for (const [key, data] of Object.entries(curso.Teoria)) {
gruposTeoria[key] = Object.assign({seleccionado: false}, data);
gruposTeoria[key] = Object.assign({seleccionado: false}, data)
}
const gruposLab: { [k: string]: DatosGrupo } = {};
const gruposLab: { [k: string]: DatosGrupo } = {}
for (const [key, data] of Object.entries(curso.Laboratorio ?? {})) {
gruposLab[key] = Object.assign({seleccionado: false}, data);
gruposLab[key] = Object.assign({seleccionado: false}, data)
}
anioData[nombreCurso] = {
...curso,
oculto: false,
Teoria: gruposTeoria,
Laboratorio: gruposLab
};
Laboratorio: gruposLab,
}
}
anios[nombreAnio] = anioData;
anios[nombreAnio] = anioData
}
datos.años = anios;
return datos;
})();
datos.años = anios
return datos
})()
const ElemCargando = () =>
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();
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 [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 = "";
let templateColumns = ""
switch (estadoLayout()) {
case "MaxHorarios": {
templateColumns = "4rem auto";
break;
templateColumns = "4rem auto"
break
}
case "MaxPersonal": {
templateColumns = "auto 4rem";
break;
templateColumns = "auto 4rem"
break
}
case "Normal": {
templateColumns = "50% 50%"
}
}
localStorage.setItem("estadoLayout", estadoLayout());
localStorage.setItem("estadoLayout", estadoLayout())
return StyleSheet.create({
contenedor: {
display: "grid",
gridTemplateColumns: templateColumns
}
});
});
gridTemplateColumns: templateColumns,
},
})
})
createEffect(async () => {
const datos = await datosPromise;
createEffect(async() => {
const datos = await datosPromise
batch(() => {
setDatos(datos);
setDatosCargados(true);
});
});
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}
return (
<div className={css(e().contenedor)}>
<div>
<MiHorario
estadoLayout={estadoLayout()}
setEstadoLayout={setEstadoLayout}
cursosUsuario={cursosUsuario}
fnAgregarCurso={agregarCursoUsuario}
setCursosUsuarios={setCursosUsuarios}
/>
</Show>
</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>
</div>;
)
}

View File

@ -1,32 +1,32 @@
import { Cursos, CursoRaw, DatosGrupo, ListaCursosUsuario, Curso } from "../types/DatosHorario";
import { createEffect, createMemo, For, SetStateFunction } from "solid-js";
import { StyleSheet, css } from "aphrodite";
import { estilosGlobales } from "../Estilos";
import { Cursos, CursoRaw, DatosGrupo, ListaCursosUsuario, Curso } from "../types/DatosHorario"
import { createEffect, createMemo, For, SetStateFunction } from "solid-js"
import { StyleSheet, css } from "aphrodite"
import { estilosGlobales } from "../Estilos"
const e = StyleSheet.create({
inline: {
display: "inline-block"
display: "inline-block",
},
lineaTexto: {
marginBottom: "0.5rem"
marginBottom: "0.5rem",
},
tablaGrupos: {
whiteSpace: "pre",
borderCollapse: "collapse",
borderSpacing: 0
borderSpacing: 0,
},
contenedorCurso: {
display: "inline-block",
verticalAlign: "top"
verticalAlign: "top",
},
cursoOculto: {
display: "none"
display: "none",
},
botonTexto: {
padding: "0.25rem 0.35rem",
borderRadius: "5px"
}
});
borderRadius: "5px",
},
})
interface Props {
dataAnio: Cursos,
@ -50,23 +50,30 @@ interface PropsIndicadorGrupo {
}
function IndicadorGrupo(props: PropsIndicadorGrupo) {
const id = `${props.idParcial}_${props.esLab ? 'L' : 'T'}_${props.nombre}`;
return <span className={css(e.botonTexto, estilosGlobales.contenedorCursor, estilosGlobales.contenedorCursorSoft)}
style={props.esLab ? {"font-style": "italic"} : {"font-weight": "bold"}}
onMouseEnter={() => props.setIdHover(id)}
onMouseLeave={() => props.setIdHover("")}
onClick={props.onClick}
>
{props.esLab ? "L" : ""}{props.nombre}
</span>
const id = `${props.idParcial}_${props.esLab ? "L" : "T"}_${props.nombre}`
return (
<span className={css(e.botonTexto, estilosGlobales.contenedorCursor, estilosGlobales.contenedorCursorSoft)}
style={props.esLab ? {"font-style": "italic"} : {"font-weight": "bold"}}
onMouseEnter={() => props.setIdHover(id)}
onMouseLeave={() => props.setIdHover("")}
onClick={props.onClick}
>
{props.esLab ? "L" : ""}{props.nombre}
</span>
)
}
const agruparProfesores = (datos: { [k: string]: DatosGrupo }, indiceCurso: number, esLab: boolean, setCursosUsuarios: FnSetCursosUsuarios) => {
const profesores: { [k: string]: [string, () => void][] } = {};
const agruparProfesores = (
datos: { [k: string]: DatosGrupo },
indiceCurso: number,
esLab: boolean,
setCursosUsuarios: FnSetCursosUsuarios,
) => {
const profesores: { [k: string]: [string, () => void][] } = {}
for (const [grupo, datosGrupo] of Object.entries(datos)) {
const nombreProfesor = datosGrupo.Docente;
const nombreProfesor = datosGrupo.Docente
if (!profesores[nombreProfesor]) {
profesores[nombreProfesor] = [];
profesores[nombreProfesor] = []
}
profesores[nombreProfesor].push([
grupo,
@ -78,133 +85,136 @@ const agruparProfesores = (datos: { [k: string]: DatosGrupo }, indiceCurso: numb
/// @ts-ignore
grupo,
"seleccionado",
x => !x
);
}
]);
(x) => !x,
)
},
])
}
return profesores;
};
return profesores
}
export function CursosElem(props: Props) {
const anio = () => props.anioActual().substring(0, props.anioActual().indexOf(" "));
const anio = () => props.anioActual().substring(0, props.anioActual().indexOf(" "))
const claseCursoNoAgregado = css(
e.contenedorCurso,
estilosGlobales.contenedor
);
estilosGlobales.contenedor,
)
const claseCursoAgregado = css(
e.contenedorCurso,
estilosGlobales.contenedor,
!props.esCursoMiHorario && estilosGlobales.contenedorCursorActivo,
);
)
const claseCursoOculto = css(e.cursoOculto);
const claseCursoOculto = css(e.cursoOculto)
return <>
<For each={Object.entries(props.dataAnio)}>
{([indiceCurso, datosCurso]) => {
return (
<>
<For each={Object.entries(props.dataAnio)}>
{([indiceCurso, datosCurso]) => {
const idCurso = `${anio()}_${datosCurso.abreviado}`;
const idCurso = `${anio()}_${datosCurso.abreviado}`
const cursoAgregadoMemo = createMemo(
() => props.listaCursosUsuario.cursos.find(x => {
return x.nombre === datosCurso.nombre && !x.oculto
}) !== undefined,
undefined,
(x, y) => x === y
);
const cursoAgregadoMemo = createMemo(
() => props.listaCursosUsuario.cursos.find((x) => x.nombre === datosCurso.nombre && !x.oculto) !== undefined,
undefined,
(x, y) => x === y,
)
const tituloMemo = createMemo(() => cursoAgregadoMemo()
? `Remover de mi horario`
: `Agregar a mi horario`
);
const tituloMemo = createMemo(() => (cursoAgregadoMemo()
? "Remover de mi horario"
: "Agregar a mi horario"))
const claseMemo = createMemo(() => {
if (props.esCursoMiHorario && datosCurso.oculto) {
return claseCursoOculto
}
return cursoAgregadoMemo()
? claseCursoAgregado
: claseCursoNoAgregado
});
const claseMemo = createMemo(() => {
if (props.esCursoMiHorario && datosCurso.oculto) {
return claseCursoOculto
}
return cursoAgregadoMemo()
? claseCursoAgregado
: claseCursoNoAgregado
})
const profesoresTeoria = createMemo(() => agruparProfesores(
datosCurso.Teoria,
parseInt(indiceCurso),
false,
props.setCursosUsuarios
));
const profesoresLab = createMemo(() => agruparProfesores(
datosCurso.Laboratorio ?? {},
parseInt(indiceCurso),
true,
props.setCursosUsuarios
));
const profesoresTeoria = createMemo(() => agruparProfesores(
datosCurso.Teoria,
Number(indiceCurso),
false,
props.setCursosUsuarios,
))
const profesoresLab = createMemo(() => agruparProfesores(
datosCurso.Laboratorio ?? {},
Number(indiceCurso),
true,
props.setCursosUsuarios,
))
return <div className={claseMemo()}>
<div
className={css(e.inline, e.lineaTexto, e.botonTexto, estilosGlobales.contenedorCursor, estilosGlobales.contenedorCursorSoft)}
onMouseEnter={() => props.setIdHover(idCurso)}
onMouseLeave={() => props.setIdHover("")}
>
{datosCurso.abreviado} - {datosCurso.nombre}
</div>
<table>
<tbody>
<tr>
<For each={Object.entries(profesoresTeoria())}>
{([profesor, grupos]) => {
return <td style={{"padding-bottom": "0.5rem", "padding-right": "0.75rem"}}>
<span>
{profesor}&nbsp;
</span>
<For each={grupos}>
{([x, fnOnClick]) =>
<IndicadorGrupo nombre={x}
return (
<div className={claseMemo()}>
<div
className={css(e.inline, e.lineaTexto, e.botonTexto, estilosGlobales.contenedorCursor, estilosGlobales.contenedorCursorSoft)}
onMouseEnter={() => props.setIdHover(idCurso)}
onMouseLeave={() => props.setIdHover("")}
>
{datosCurso.abreviado} - {datosCurso.nombre}
</div>
<table>
<tbody>
<tr>
<For each={Object.entries(profesoresTeoria())}>
{([profesor, grupos]) => (
<td style={{"padding-bottom": "0.5rem", "padding-right": "0.75rem"}}>
<span>
{profesor}&nbsp;
</span>
<For each={grupos}>
{([x, fnOnClick]) => (
<IndicadorGrupo nombre={x}
esLab={false}
idParcial={idCurso}
setIdHover={props.setIdHover}
onClick={fnOnClick}
/>
}
/>
)
}
</For>
</td>
)}
</For>
</td>
}}
</For>
</tr>
<tr>
<For each={Object.entries(profesoresLab())}>
{([profesor, grupos]) => {
return <td style={{"padding-bottom": "0.5rem", "padding-right": "0.75rem"}}>
<span>
{profesor}&nbsp;
</span>
<For each={grupos}>
{([x, fnOnClick]) =>
<IndicadorGrupo nombre={x}
esLab={true}
</tr>
<tr>
<For each={Object.entries(profesoresLab())}>
{([profesor, grupos]) => (
<td style={{"padding-bottom": "0.5rem", "padding-right": "0.75rem"}}>
<span>
{profesor}&nbsp;
</span>
<For each={grupos}>
{([x, fnOnClick]) => (
<IndicadorGrupo nombre={x}
esLab
idParcial={idCurso}
setIdHover={props.setIdHover}
onClick={fnOnClick}
/>
}
/>
)
}
</For>
</td>
)}
</For>
</td>
}}
</For>
</tr>
</tbody>
</table>
<span
className={css(e.botonTexto, estilosGlobales.contenedorCursor, estilosGlobales.contenedorCursorSoft)}
onClick={() => props.fnAgregarCurso(datosCurso)}
>
{tituloMemo}
</span>
</div>
}}
</For>
</>;
</tr>
</tbody>
</table>
<span
className={css(e.botonTexto, estilosGlobales.contenedorCursor, estilosGlobales.contenedorCursorSoft)}
onClick={() => props.fnAgregarCurso(datosCurso)}
>
{tituloMemo}
</span>
</div>
)
}}
</For>
</>
)
}

View File

@ -1,12 +1,12 @@
import { Curso, Cursos, DatosHorario, ListaCursosUsuario } from "../types/DatosHorario";
import { batch, createMemo, createSignal, For, Match, SetStateFunction, Switch, untrack } from "solid-js";
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 { Curso, Cursos, DatosHorario, ListaCursosUsuario } from "../types/DatosHorario"
import { batch, createMemo, createSignal, For, Match, SetStateFunction, Switch, untrack } from "solid-js"
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"
interface HorariosProps {
data: DatosHorario,
@ -20,107 +20,116 @@ interface HorariosProps {
const {
setListaCursos,
agregarCursoALista,
eliminarCursosDeLista
} = useListaCursos();
eliminarCursosDeLista,
} = useListaCursos()
export function Horarios(props: HorariosProps) {
const [anioActual, setAnioActual] = createSignal("1er año");
const [anioActual, setAnioActual] = createSignal("1er año")
// ID que indica cuales celdas resaltar.
const [idHover, setIdHover] = createSignal("");
const [idHover, setIdHover] = createSignal("")
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
);
});
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 <div className={clases()} title={"Cambiar a " + nombre} onClick={() => setAnioActual(nombre)}>
{nombre}
</div>
}}
</For>;
return (
<div className={clases()} title={`Cambiar a ${nombre}`} onClick={() => setAnioActual(nombre)}>
{nombre}
</div>
)
}}
</For>
)
const dataTabla = createMemo(() => {
const anio = anioActual();
const obj: Cursos = {};
const anio = anioActual()
const obj: Cursos = {}
untrack(() => {
const cursos = props.data.años[anio];
const cursos = props.data.años[anio]
batch(() => {
eliminarCursosDeLista();
eliminarCursosDeLista()
let i = 0;
let i = 0
for (const [, curso] of Object.entries(cursos)) {
// El curso devuelto por esta fun. es reactivo
obj[i] = agregarCursoALista(curso);
i++;
obj[i] = agregarCursoALista(curso)
i += 1
}
});
});
})
})
return obj;
});
return obj
})
const fnMaximizar = () => props.setEstadoLayout("MaxHorarios");
const fnMinimizar = () => props.setEstadoLayout("Normal");
const estadoActualLayout = () => props.estadoLayout;
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
return (
<div>
<Switch>
<Match when={props.estadoLayout === "Normal" || props.estadoLayout === "MaxHorarios"}>
<div>
<div className={css(
estilosGlobales.inlineBlock,
estilosGlobales.contenedor,
)}
>
Horarios disponibles
</div>
</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()}
idHover={idHover}
setIdHover={setIdHover}
setCursosUsuarios={setListaCursos}
{elAnios}
|
<BotonMaxMin
fnMaximizar={fnMaximizar}
fnMinimizar={fnMinimizar}
estadoActualLayout={estadoActualLayout}
estadoLayoutMax={"MaxHorarios"}
/>
</div>
<div>
<CursosElem dataAnio={dataTabla()}
anioActual={anioActual}
fnAgregarCurso={props.fnAgregarCurso}
listaCursosUsuario={props.listaCursosUsuario}
idHover={idHover}
setIdHover={setIdHover}
esCursoMiHorario={false}
setCursosUsuarios={setListaCursos}
<br />
<div className={css(estilosGlobales.contenedor)}>
<Tabla
data={dataTabla()}
version={props.data.version}
anio={anioActual()}
idHover={idHover}
setIdHover={setIdHover}
setCursosUsuarios={setListaCursos}
/>
</div>
<div>
<CursosElem
dataAnio={dataTabla()}
anioActual={anioActual}
fnAgregarCurso={props.fnAgregarCurso}
listaCursosUsuario={props.listaCursosUsuario}
idHover={idHover}
setIdHover={setIdHover}
esCursoMiHorario={false}
setCursosUsuarios={setListaCursos}
/>
</div>
</Match>
<Match when={props.estadoLayout === "MaxPersonal"}>
<BotonMaxMin
fnMaximizar={fnMaximizar}
fnMinimizar={fnMinimizar}
estadoActualLayout={estadoActualLayout}
estadoLayoutMax={"MaxHorarios"}
/>
</div>
</Match>
<Match when={props.estadoLayout === "MaxPersonal"}>
<BotonMaxMin
fnMaximizar={fnMaximizar}
fnMinimizar={fnMinimizar}
estadoActualLayout={estadoActualLayout}
estadoLayoutMax={"MaxHorarios"}
/>
</Match>
</Switch>
</Match>
</Switch>
</div>;
</div>
)
}

View File

@ -1,13 +1,13 @@
import { estilosGlobales } from "../Estilos";
import { StyleSheet, css } from "aphrodite";
import { Tabla } from "./Tabla";
import { mostrarDescansos } from "../Store";
import { EstadoLayout } from "./ContenedorHorarios";
import { Switch, Match, For, createMemo, createSignal, SetStateFunction } from "solid-js";
import { BotonMaxMin } from "./BotonMaxMin";
import { BotonIcono } from "./BotonIcono";
import { Curso, Cursos, ListaCursosUsuario } from "../types/DatosHorario";
import { CursosElem } from "./CursosElem";
import { estilosGlobales } from "../Estilos"
import { StyleSheet, css } from "aphrodite"
import { Tabla } from "./Tabla"
import { mostrarDescansos } from "../Store"
import { EstadoLayout } from "./ContenedorHorarios"
import { Switch, Match, For, createMemo, createSignal, SetStateFunction } from "solid-js"
import { BotonMaxMin } from "./BotonMaxMin"
import { BotonIcono } from "./BotonIcono"
import { Curso, Cursos, ListaCursosUsuario } from "../types/DatosHorario"
import { CursosElem } from "./CursosElem"
interface MiHorarioProps {
estadoLayout: EstadoLayout,
@ -25,111 +25,117 @@ const e = StyleSheet.create({
"::before": {
fontSize: "1rem",
// transform: "translateY(0.2rem)",
textDecoration: "none"
}
}
});
textDecoration: "none",
},
},
})
export function MiHorario(props: MiHorarioProps) {
const [idHover, setIdHover] = createSignal("");
const [idHover, setIdHover] = createSignal("")
const datosMiHorario = createMemo(() => {
const obj: Cursos = {};
const obj: Cursos = {}
props.cursosUsuario.cursos.forEach((x, i) => {
obj[i] = x;
});
return obj;
});
obj[i] = x
})
return obj
})
const fnMaximizar = () => props.setEstadoLayout("MaxPersonal");
const fnMinimizar = () => props.setEstadoLayout("Normal");
const estadoActualLayout = () => props.estadoLayout;
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"}>
return (
<div>
<Switch>
<Match when={props.estadoLayout === "Normal" || props.estadoLayout === "MaxPersonal"}>
<div>
<div className={css(
estilosGlobales.inlineBlock,
estilosGlobales.contenedor
)}>
<div>
<div className={css(
estilosGlobales.inlineBlock,
estilosGlobales.contenedor,
)}
>
Mi horario
</div>
</div>
</div>
<div>
<div className={css(
estilosGlobales.inlineBlock,
estilosGlobales.contenedor
)}>
Mi horario
<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>
|
<BotonIcono titulo={"Nuevo horario en blanco"}
icono={"ph-plus"}
onClick={() => {
}}
<div className={css(
e.horario,
estilosGlobales.contenedor,
)}
>
<Tabla
data={datosMiHorario()}
anio={"Mi horario"}
version={1}
idHover={idHover}
setIdHover={setIdHover}
setCursosUsuarios={props.setCursosUsuarios}
/>
</div>
<CursosElem
anioActual={() => "Mi horario"}
dataAnio={datosMiHorario()}
fnAgregarCurso={props.fnAgregarCurso}
listaCursosUsuario={props.cursosUsuario}
idHover={idHover}
setIdHover={setIdHover}
esCursoMiHorario
setCursosUsuarios={props.setCursosUsuarios}
/>
<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={() => {
}}
/>
|
</Match>
<Match when={props.estadoLayout === "MaxHorarios"}>
<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}
idHover={idHover}
setIdHover={setIdHover}
setCursosUsuarios={props.setCursosUsuarios}
/>
</div>
<CursosElem anioActual={() => "Mi horario"}
dataAnio={datosMiHorario()}
fnAgregarCurso={props.fnAgregarCurso}
listaCursosUsuario={props.cursosUsuario}
idHover={idHover}
setIdHover={setIdHover}
esCursoMiHorario={true}
setCursosUsuarios={props.setCursosUsuarios}
/>
</Match>
<Match when={props.estadoLayout === "MaxHorarios"}>
<BotonMaxMin
fnMaximizar={fnMaximizar}
fnMinimizar={fnMinimizar}
estadoActualLayout={estadoActualLayout}
estadoLayoutMax={"MaxPersonal"}
/>
</Match>
</Switch>
</div>;
</Match>
</Switch>
</div>
)
}

View File

@ -1,31 +1,30 @@
import { StyleSheet, css } from "aphrodite";
import { createEffect, createMemo, createSignal, createState, For, SetStateFunction } from "solid-js";
import { estilosGlobales } from "../Estilos";
import { Cursos, Curso, ListaCursosUsuario } from "../types/DatosHorario";
import { Dia, dias, horas } from "../Store";
import { DataProcesada } from "../types/DatosHorario";
import { FilaTabla } from "./Tabla/FilaTabla";
import { StyleSheet, css } from "aphrodite"
import { createMemo, For, SetStateFunction } from "solid-js"
import { estilosGlobales } from "../Estilos"
import { Cursos, ListaCursosUsuario, DataProcesada } from "../types/DatosHorario"
import { Dia, dias, horas } from "../Store"
import { FilaTabla } from "./Tabla/FilaTabla"
export const coloresBorde = Object.freeze([
"rgba(33,150,243,1)",
"rgba(255,214,0 ,1)",
"rgba(236,64,122 ,1)",
"rgba(29,233,182 ,1)",
"rgba(244,67,54,1)"
]);
"rgba(244,67,54,1)",
])
export const diaANum = (d: Dia) => {
switch (d) {
case "Lunes":
return 0;
return 0
case "Martes":
return 1;
return 1
case "Miercoles":
return 2;
return 2
case "Jueves":
return 3;
return 3
case "Viernes":
return 4;
return 4
}
}
@ -40,7 +39,7 @@ const e = StyleSheet.create({
minHeight: "1.5rem",
":hover": {
// backgroundColor: "rgba(200, 200, 200, 0.25)"
}
},
},
filaBorde: {
position: "absolute",
@ -48,65 +47,65 @@ const e = StyleSheet.create({
height: "1px",
width: "100%",
backgroundColor: "rgba(200, 200, 200, 0.25)",
zIndex: 1
zIndex: 1,
},
celdaHora: {
textAlign: "center",
width: "4rem",
padding: "0.25rem 0",
position: "absolute",
top: "-0.75rem"
top: "-0.75rem",
},
celdaComun: {
width: "20%",
textAlign: "center",
padding: "0 0.5rem",
boxSizing: "border-box"
boxSizing: "border-box",
},
celdaDia: {
padding: "0.3rem 0"
padding: "0.3rem 0",
},
celdaCurso: {
display: "inline-block",
padding: "0.25rem 0.35rem",
cursor: "pointer",
borderRadius: "5px",
transition: "background-color 100ms"
transition: "background-color 100ms",
},
celdaCursoActiva: {
backgroundColor: "rgba(200, 200, 200, 0.25)"
backgroundColor: "rgba(200, 200, 200, 0.25)",
},
celdaCursoTeoria: {
fontWeight: "bold"
}
});
fontWeight: "bold",
},
})
type FnSetCursosUsuarios = SetStateFunction<ListaCursosUsuario>;
const procesarAnio = (data: Cursos, anio: string, version: number, setCursosUsuarios: FnSetCursosUsuarios) => {
const obj: DataProcesada = {};
const obj: DataProcesada = {}
for (const [indiceCurso, curso] of Object.entries(data)) {
if (curso.oculto) continue;
if (curso.oculto) continue
const nombreAbreviado = curso.abreviado;
const nombreAbreviado = curso.abreviado
for (const [grupoStr, grupo] of Object.entries(curso.Teoria)) {
for (const hora of grupo.Horas) {
const dia = hora.substring(0, 2);
const horas = hora.substring(2, 4);
const minutos = hora.substr(4);
const dia = hora.substring(0, 2)
const horas = hora.substring(2, 4)
const minutos = hora.substr(4)
const horaCompleta = horas + ":" + minutos;
const horaCompleta = `${horas}:${minutos}`
const id = `${version}_${anio}_${nombreAbreviado}_T_${grupoStr}`;
const id = `${version}_${anio}_${nombreAbreviado}_T_${grupoStr}`
if (!(horaCompleta in obj)) {
obj[horaCompleta] = {};
obj[horaCompleta] = {}
}
if (!(dia in obj[horaCompleta])) {
obj[horaCompleta][dia] = [];
obj[horaCompleta][dia] = []
}
obj[horaCompleta][dia].push({
@ -116,28 +115,28 @@ const procesarAnio = (data: Cursos, anio: string, version: number, setCursosUsua
datosGrupo: grupo,
fnSeleccionar: () => {
/// @ts-ignore
setCursosUsuarios("cursos", indiceCurso, "Teoria", grupoStr, "seleccionado", x => !x);
}
});
setCursosUsuarios("cursos", indiceCurso, "Teoria", grupoStr, "seleccionado", (x) => !x)
},
})
}
}
for (const [grupoStr, grupo] of Object.entries(curso.Laboratorio ?? {})) {
for (const hora of grupo.Horas) {
const dia = hora.substring(0, 2);
const horas = hora.substring(2, 4);
const minutos = hora.substr(4);
const dia = hora.substring(0, 2)
const horas = hora.substring(2, 4)
const minutos = hora.substr(4)
const horaCompleta = horas + ":" + minutos;
const horaCompleta = `${horas}:${minutos}`
const id = `${version}_${anio}_${nombreAbreviado}_L_${grupoStr}`;
const id = `${version}_${anio}_${nombreAbreviado}_L_${grupoStr}`
if (!(horaCompleta in obj)) {
obj[horaCompleta] = {};
obj[horaCompleta] = {}
}
if (!(dia in obj[horaCompleta])) {
obj[horaCompleta][dia] = [];
obj[horaCompleta][dia] = []
}
obj[horaCompleta][dia].push({
@ -147,14 +146,14 @@ const procesarAnio = (data: Cursos, anio: string, version: number, setCursosUsua
datosGrupo: grupo,
fnSeleccionar: () => {
/// @ts-ignore
setCursosUsuarios("cursos", indiceCurso, "Laboratorio", grupoStr, "seleccionado", x => !x);
}
});
setCursosUsuarios("cursos", indiceCurso, "Laboratorio", grupoStr, "seleccionado", (x) => !x)
},
})
}
}
}
return obj;
return obj
}
interface Props {
@ -167,35 +166,40 @@ interface Props {
}
export function Tabla(props: Props) {
const anio = () => props.anio.substring(0, props.anio.indexOf(" "));
const data = createMemo(() => procesarAnio(props.data, anio(), props.version, props.setCursosUsuarios));
const idHover = props.idHover;
const setIdHover = props.setIdHover;
const anio = () => props.anio.substring(0, props.anio.indexOf(" "))
const data = createMemo(() => procesarAnio(props.data, anio(), props.version, props.setCursosUsuarios))
const idHover = props.idHover
const setIdHover = props.setIdHover
const celdas = createMemo(() => {
// Hace reaccionar a la reactividad de Solid
props.data;
return <For each={horas}>
{hora => {
return <FilaTabla data={data()}
hora={hora}
idHover={idHover}
setIdHover={setIdHover}
/>
}}
</For>
});
return <div>
<div className={css(e.fila)}>
<For each={dias}>
{dia =>
<div className={css(e.celdaComun, estilosGlobales.inlineBlock, e.celdaDia)}>
{dia}
</div>
}
props.data
return (
<For each={horas}>
{(hora) => (
<FilaTabla
data={data()}
hora={hora}
idHover={idHover}
setIdHover={setIdHover}
/>
)}
</For>
)
})
return (
<div>
<div className={css(e.fila)}>
<For each={dias}>
{(dia) => (
<div className={css(e.celdaComun, estilosGlobales.inlineBlock, e.celdaDia)}>
{dia}
</div>
)}
</For>
</div>
{celdas()}
</div>
{celdas()}
</div>
}
)
}

View File

@ -1,8 +1,8 @@
import { StyleSheet, css } from "aphrodite";
import { estilosGlobales } from "../../Estilos";
import { For, createSignal, createMemo, createEffect, SetStateFunction } from "solid-js";
import { Dia } from "../../Store";
import { DatosGrupo, ListaCursosUsuario } from "../../types/DatosHorario";
import { StyleSheet, css } from "aphrodite"
import { estilosGlobales } from "../../Estilos"
import { For, createSignal, createMemo, createEffect, SetStateFunction } from "solid-js"
import { Dia } from "../../Store"
import { DatosGrupo, ListaCursosUsuario } from "../../types/DatosHorario"
const e = StyleSheet.create({
celdaComun: {
@ -10,7 +10,7 @@ const e = StyleSheet.create({
textAlign: "center",
padding: "0 0.7rem",
boxSizing: "border-box",
userSelect: "none"
userSelect: "none",
},
celdaCurso: {
display: "inline-block",
@ -23,39 +23,42 @@ const e = StyleSheet.create({
// color: "#151515"
},
celdaCursoTeoria: {
fontWeight: "bold"
fontWeight: "bold",
},
celdaCursoLab: {
fontStyle: "italic"
}
});
fontStyle: "italic",
},
celdaSeleccionada: {
textDecoration: "underline",
},
})
const eColores = StyleSheet.create({
lunes: {
backgroundColor: "rgba(33,150,243,1)"
backgroundColor: "rgba(33,150,243,1)",
},
martes: {
backgroundColor: "rgba(255,214,0 ,1)",
color: "#151515"
color: "#151515",
},
miercoles: {
backgroundColor: "rgba(236,64,122 ,1)"
backgroundColor: "rgba(236,64,122 ,1)",
},
jueves: {
backgroundColor: "rgba(29,233,182 ,1)",
color: "#151515"
color: "#151515",
},
viernes: {
backgroundColor: "rgba(244,67,54,1)"
}
});
backgroundColor: "rgba(244,67,54,1)",
},
})
const clasesColores = {
Lunes: css(eColores.lunes),
Martes: css(eColores.martes),
Miercoles: css(eColores.miercoles),
Jueves: css(eColores.jueves),
Viernes: css(eColores.viernes)
Viernes: css(eColores.viernes),
}
interface Props {
@ -79,64 +82,68 @@ interface Props {
setIdHover: (v: string) => string,
fnResaltarFila: () => void,
fnDesresaltarFila: () => void,
dia: Dia
dia: Dia,
}
const claseSeldaSeleccionada = css(e.celdaSeleccionada)
export function CeldaFila(props: Props) {
const datos = props.datos;
const idHover = props.idHover;
const setIdHover = props.setIdHover;
const datos = props.datos
const idHover = props.idHover
const setIdHover = props.setIdHover
const fnOnMouseEnter = (id: string) => setIdHover(id);
const fnOnMouseLeave = () => setIdHover("");
const fnOnMouseEnter = (id: string) => setIdHover(id)
const fnOnMouseLeave = () => setIdHover("")
return <div className={css(e.celdaComun, estilosGlobales.inlineBlock)}>
<For each={datos}>
{datos => {
const id = datos.id;
const txt = datos.txt;
const esLab = datos.esLab;
const fnSeleccionar = datos.fnSeleccionar;
return (
<div className={css(e.celdaComun, estilosGlobales.inlineBlock)}>
<For each={datos}>
{(datos) => {
const id = datos.id
const txt = datos.txt
const esLab = datos.esLab
const fnSeleccionar = datos.fnSeleccionar
const [estabaResaltado, setEstabaResaltado] = createSignal(false);
const [estabaResaltado, setEstabaResaltado] = createSignal(false)
const estaSeleccionado = createMemo(() => {
return datos.datosGrupo.seleccionado;
});
const estaSeleccionado = createMemo(() => datos.datosGrupo.seleccionado)
const clases = createMemo(
() => {
const clases = [
e.celdaCurso,
esLab ? e.celdaCursoLab : e.celdaCursoTeoria,
estaSeleccionado() && estilosGlobales.contenedorCursorActivo
];
let adicional = "";
const idHoverS = idHover();
if (idHoverS !== "" && id.search(idHoverS) !== -1) {
props.fnResaltarFila();
clases.push(e.celdaCursoActiva);
adicional = clasesColores[props.dia];
const clases = createMemo(
() => {
const clases = [
e.celdaCurso,
esLab ? e.celdaCursoLab : e.celdaCursoTeoria,
estaSeleccionado() && e.celdaSeleccionada,
]
let adicional = ""
const idHoverS = idHover()
if (idHoverS !== "" && id.search(idHoverS) !== -1) {
props.fnResaltarFila()
clases.push(e.celdaCursoActiva)
adicional = clasesColores[props.dia]
setEstabaResaltado(true);
} else if (estabaResaltado()) {
props.fnDesresaltarFila();
setEstabaResaltado(false);
}
return css(...clases) + " " + adicional;
},
undefined,
(x, y) => x === y
);
setEstabaResaltado(true)
} else if (estabaResaltado()) {
props.fnDesresaltarFila()
setEstabaResaltado(false)
}
return `${css(...clases)} ${adicional}`
},
undefined,
(x, y) => x === y,
)
return <span className={clases()}
onMouseEnter={() => fnOnMouseEnter(id)}
onMouseLeave={fnOnMouseLeave}
onClick={fnSeleccionar}
>
{txt}
</span>;
}}
</For>
</div>
}
return (
<span className={clases()}
onMouseEnter={() => fnOnMouseEnter(id)}
onMouseLeave={fnOnMouseLeave}
onClick={fnSeleccionar}
>
{txt}
</span>
)
}}
</For>
</div>
)
}

View File

@ -1,10 +1,10 @@
import { StyleSheet, css } from "aphrodite";
import { estilosGlobales } from "../../Estilos";
import { For, createSignal, createMemo, createState, createEffect, State, SetStateFunction } from "solid-js";
import { Dia, dias } from "../../Store";
import { CeldaFila } from "./CeldaFila";
import { DataProcesada, ListaCursosUsuario } from "../../types/DatosHorario";
import { coloresBorde, diaANum } from "../Tabla";
import { StyleSheet, css } from "aphrodite"
import { estilosGlobales } from "../../Estilos"
import { For, createSignal, createMemo, createState, createEffect, State, SetStateFunction } from "solid-js"
import { Dia, dias } from "../../Store"
import { CeldaFila } from "./CeldaFila"
import { DataProcesada, ListaCursosUsuario } from "../../types/DatosHorario"
import { coloresBorde, diaANum } from "../Tabla"
const e = StyleSheet.create({
celdaHora: {
@ -12,13 +12,13 @@ const e = StyleSheet.create({
width: "3rem",
padding: "0.25rem 0",
position: "absolute",
top: "-0.75rem"
top: "-0.75rem",
},
filaResaltado: {
position: "absolute",
zIndex: -1,
height: "100%",
transform: "translateX(-1.5rem)"
transform: "translateX(-1.5rem)",
},
fila: {
position: "relative",
@ -30,7 +30,7 @@ const e = StyleSheet.create({
minHeight: "1.2rem",
":hover": {
// backgroundColor: "rgba(200, 200, 200, 0.25)"
}
},
},
filaBorde: {
position: "absolute",
@ -38,17 +38,17 @@ const e = StyleSheet.create({
height: "1px",
width: "100%",
backgroundColor: "rgba(200, 200, 200, 0.25)",
zIndex: -1
zIndex: -1,
},
celdaResaltado: {
height: "101%",
width: "5px",
display: "inline-block"
display: "inline-block",
},
celdaResaltadoTransparente: {
backgroundColor: "transparent"
}
});
backgroundColor: "transparent",
},
})
const [diasResaltados, setDiasResaltados] = createState({
Lunes: 0,
@ -56,7 +56,7 @@ const [diasResaltados, setDiasResaltados] = createState({
Miercoles: 0,
Jueves: 0,
Viernes: 0,
} as { [k: string]: number });
} as { [k: string]: number })
interface Props {
hora: string,
@ -65,12 +65,10 @@ interface Props {
setIdHover: (v: string) => string
}
const diasFilter = createMemo(() => {
return Object.entries(diasResaltados)
.filter(x => x[1] > 0)
.map(x => x[0] as Dia)
.sort((x, y) => diaANum(x) > diaANum(y) ? 1 : -1);
});
const diasFilter = createMemo(() => Object.entries(diasResaltados)
.filter((x) => x[1] > 0)
.map((x) => x[0] as Dia)
.sort((x, y) => (diaANum(x) > diaANum(y) ? 1 : -1)))
const useDiasResaltados: () => [
State<{ [k: string]: boolean }>,
@ -83,64 +81,76 @@ const useDiasResaltados: () => [
Miercoles: false,
Jueves: false,
Viernes: false,
} as { [k: string]: boolean });
} as { [k: string]: boolean })
const fnResaltar = (d: Dia) => {
setDiasResaltadosLocal(d, true);
setDiasResaltados(d, v => v + 1);
};
setDiasResaltadosLocal(d, true)
setDiasResaltados(d, (v) => v + 1)
}
const fnDesresaltar = (d: Dia) => {
setDiasResaltadosLocal(d, false);
setDiasResaltados(d, v => v - 1);
};
setDiasResaltadosLocal(d, false)
setDiasResaltados(d, (v) => v - 1)
}
return [diasResaltadosLocal, fnResaltar, fnDesresaltar];
};
return [diasResaltadosLocal, fnResaltar, fnDesresaltar]
}
export function FilaTabla(props: Props) {
const [diasResaltadosLocal, fnResaltar, fnDesresaltar] = useDiasResaltados();
const [diasResaltadosLocal, fnResaltar, fnDesresaltar] = useDiasResaltados()
const hora = props.hora;
const data = props.data;
const hora = props.hora
const data = props.data
return <div style={{position: "relative"}}>
<div className={css(e.celdaHora, estilosGlobales.inlineBlock)}>
{hora.substring(0, 5)}
</div>
<div className={css(e.fila)}>
<div className={css(estilosGlobales.inlineBlock, e.filaResaltado)}>
<div className={css(e.celdaResaltado, diasResaltadosLocal.Lunes ? null : e.celdaResaltadoTransparente)}
style={{"background-color": coloresBorde[0]}}/>
<div className={css(e.celdaResaltado, diasResaltadosLocal.Martes ? null : e.celdaResaltadoTransparente)}
style={{"background-color": coloresBorde[1]}}/>
<div
className={css(e.celdaResaltado, diasResaltadosLocal.Miercoles ? null : e.celdaResaltadoTransparente)}
style={{"background-color": coloresBorde[2]}}/>
<div className={css(e.celdaResaltado, diasResaltadosLocal.Jueves ? null : e.celdaResaltadoTransparente)}
style={{"background-color": coloresBorde[3]}}/>
<div
className={css(e.celdaResaltado, diasResaltadosLocal.Viernes ? null : e.celdaResaltadoTransparente)}
style={{"background-color": coloresBorde[4]}}/>
return (
<div style={{position: "relative"}}>
<div className={css(e.celdaHora, estilosGlobales.inlineBlock)}>
{hora.substring(0, 5)}
</div>
<For each={dias}>
{dia => {
const diaStr = dia.substring(0, 2);
const horaStr = hora.substring(0, 5);
const datos = data?.[horaStr]?.[diaStr] ?? [];
return <CeldaFila
datos={datos}
idHover={props.idHover}
setIdHover={props.setIdHover}
fnResaltarFila={() => fnResaltar(dia)}
fnDesresaltarFila={() => fnDesresaltar(dia)}
dia={dia}
<div className={css(e.fila)}>
<div className={css(estilosGlobales.inlineBlock, e.filaResaltado)}>
<div
className={css(e.celdaResaltado, diasResaltadosLocal.Lunes ? null : e.celdaResaltadoTransparente)}
style={{"background-color": coloresBorde[0]}}
/>
}}
</For>
<div className={css(e.filaBorde)}/>
<div
className={css(e.celdaResaltado, diasResaltadosLocal.Martes ? null : e.celdaResaltadoTransparente)}
style={{"background-color": coloresBorde[1]}}
/>
<div
className={css(e.celdaResaltado, diasResaltadosLocal.Miercoles ? null : e.celdaResaltadoTransparente)}
style={{"background-color": coloresBorde[2]}}
/>
<div
className={css(e.celdaResaltado, diasResaltadosLocal.Jueves ? null : e.celdaResaltadoTransparente)}
style={{"background-color": coloresBorde[3]}}
/>
<div
className={css(e.celdaResaltado, diasResaltadosLocal.Viernes ? null : e.celdaResaltadoTransparente)}
style={{"background-color": coloresBorde[4]}}
/>
</div>
<For each={dias}>
{(dia) => {
const diaStr = dia.substring(0, 2)
const horaStr = hora.substring(0, 5)
const datos = data?.[horaStr]?.[diaStr] ?? []
return (
<CeldaFila
datos={datos}
idHover={props.idHover}
setIdHover={props.setIdHover}
fnResaltarFila={() => fnResaltar(dia)}
fnDesresaltarFila={() => fnDesresaltar(dia)}
dia={dia}
/>
)
}}
</For>
<div className={css(e.filaBorde)} />
</div>
</div>
</div>;
}
)
}

View File

@ -0,0 +1,70 @@
/**
* - Normal
* - Oculto - Otro grupo seleccionado
* - Seleccionado - Cursor ensima
* - Resaltado - Grupo seleccionado
* - ResaltadoSeleccionado - Grupo seleccionado y cursor encima
* - ResaltadoOculto - Otro grupo seleccionado y cursor encima
*/
import { createMemo, createState, SetStateFunction, State } from "solid-js";
type EstadoCelda =
| "Normal"
| "Oculto"
| "Seleccionado"
| "Resaltado"
| "ResaltadoSeleccionado"
| "ResaltadoOculto"
interface Datos {
anio?: string,
curso?: string,
esLab?: boolean,
grupo?: string
}
export class TablaObserver {
private readonly resaltado: State<Datos>
private readonly setResaltado: SetStateFunction<Datos>
private gruposSeleccionados = {}
constructor() {
const [resaltado, setResaltado] = createState<Datos>({
anio: undefined,
curso: undefined,
esLab: undefined,
grupo: undefined
});
this.resaltado = resaltado;
this.setResaltado = setResaltado;
}
// Cada celda se registra dando estos datos
// Devuelve un memo con un valor de EstadoCelda,
// el cual cada celda sabra como manejar
registrar(anio: string, cursoAbreviado: string, esLab: boolean, grupo: string) {
const fn = () => {
};
const memo = createMemo(
fn,
undefined,
(x, y) => x === y
);
}
resaltar(id: string) {
}
quitarResaltado() {
this.setResaltado({
anio: undefined,
curso: undefined,
esLab: undefined,
grupo: undefined
});
}
}

View File

@ -1,5 +1,5 @@
import { createState, SetStateFunction, State } from "solid-js";
import { Curso, ListaCursosUsuario } from "../types/DatosHorario";
import { createState, SetStateFunction, State } from "solid-js"
import { Curso, ListaCursosUsuario } from "../types/DatosHorario"
interface ReturnType {
listaCursos: State<ListaCursosUsuario>,
@ -11,31 +11,31 @@ interface ReturnType {
export const useListaCursos = (): ReturnType => {
const [listaCursos, setListaCursos] = createState<ListaCursosUsuario>({
sigIndice: 0,
cursos: []
});
cursos: [],
})
const agregarCursoALista = (curso: Curso): Curso => {
// Si el horario ya se habia agregado, ocultarlo
const cursoActualIndex = listaCursos.cursos.findIndex(x => x.nombre === curso.nombre);
const cursoActualIndex = listaCursos.cursos.findIndex((x) => x.nombre === curso.nombre)
if (cursoActualIndex !== -1) {
setListaCursos("cursos", cursoActualIndex, "oculto", x => !x);
return listaCursos.cursos[cursoActualIndex];
setListaCursos("cursos", cursoActualIndex, "oculto", (x) => !x)
return listaCursos.cursos[cursoActualIndex]
} else {
setListaCursos("cursos", listaCursos.sigIndice, curso);
setListaCursos("sigIndice", x => x + 1);
return listaCursos.cursos[listaCursos.sigIndice - 1];
setListaCursos("cursos", listaCursos.sigIndice, curso)
setListaCursos("sigIndice", (x) => x + 1)
return listaCursos.cursos[listaCursos.sigIndice - 1]
}
};
}
const eliminarCursosDeLista = () => {
setListaCursos("cursos", []);
setListaCursos("sigIndice", 0);
};
setListaCursos("cursos", [])
setListaCursos("sigIndice", 0)
}
return {
listaCursos,
setListaCursos,
agregarCursoALista,
eliminarCursosDeLista
};
};
eliminarCursosDeLista,
}
}

View File

@ -1,15 +1,17 @@
import { StyleSheet, css } from "aphrodite";
import { StyleSheet, css } from "aphrodite"
const e = StyleSheet.create({
creditos: {
textAlign: "center",
paddingTop: "7.5rem",
paddingBottom: "1rem"
}
});
paddingBottom: "1rem",
},
})
export function Creditos() {
return <div className={css(e.creditos)}>
Desarrollado por Fernando Araoz con TypeScript, JSX y Solid.js.
</div>
}
return (
<div className={css(e.creditos)}>
Desarrollado por Fernando Araoz con TypeScript, JSX y Solid.js.
</div>
)
}

View File

@ -1,4 +1,4 @@
import {StyleSheet} from "aphrodite";
import {StyleSheet} from "aphrodite"
export const estilosGlobales = StyleSheet.create({
contenedor: {
@ -7,7 +7,7 @@ export const estilosGlobales = StyleSheet.create({
borderRadius: "10px",
backdropFilter: "blur(40px)",
backgroundColor: "rgba(100, 100, 100, 0.25)",
color: "var(--color-texto)"
color: "var(--color-texto)",
},
contenedorCursor: {
cursor: "pointer",
@ -15,27 +15,27 @@ export const estilosGlobales = StyleSheet.create({
transition: "background-color 200ms",
textDecoration: "underline solid white 2px",
":hover": {
backgroundColor: "rgba(200, 200, 200, 0.3)"
}
backgroundColor: "rgba(200, 200, 200, 0.3)",
},
},
contenedorCursorSoft: {
textDecoration: "underline rgba(255, 255, 255, 0.4)"
textDecoration: "underline rgba(255, 255, 255, 0.4)",
},
contenedorCursorActivo: {
backgroundColor: "rgba(200, 200, 200, 0.3)"
backgroundColor: "rgba(200, 200, 200, 0.3)",
},
contenedorPhospor: {
padding: "0.5rem 0.65rem",
transform: "translateY(0.2rem)"
transform: "translateY(0.2rem)",
},
inlineBlock: {
display: "inline-block"
display: "inline-block",
},
botonPhospor: {
"::before": {
fontSize: "1.25rem",
// transform: "translateY(0.2rem)",
textDecoration: "underline rgba(255, 255, 255, 0.4)",
}
},
},
});
})

View File

@ -1,4 +1,4 @@
import { createSignal, createEffect } from "solid-js";
import { createSignal} from "solid-js"
enum ModoColor {
Claro,
@ -8,7 +8,7 @@ enum ModoColor {
export type Dia = "Lunes" | "Martes" | "Miercoles" | "Jueves" | "Viernes";
export const dias: Dia[] = ["Lunes", "Martes", "Miercoles", "Jueves", "Viernes"];
export const dias: Dia[] = ["Lunes", "Martes", "Miercoles", "Jueves", "Viernes"]
export const horas = [
"07:00 - 07:50",
"07:50 - 08:40",
@ -30,17 +30,17 @@ export const horas = [
"18:30 - 19:20",
"19:20 - 20:10",
"20:10 - 21:00",
"21:00 - 21:00"
];
"21:00 - 21:00",
]
export const horasDescanso = [
"08:40 - 08:50",
"10:30 - 10:40",
"15:40 - 15:50",
"17:30 - 17:40"
];
"17:30 - 17:40",
]
const numImgGuardado = parseInt(localStorage.getItem("num-img") ?? "3");
const numImgGuardado = Number(localStorage.getItem("num-img") ?? "3")
export const [modoColor, setModoColor] = createSignal(ModoColor.Oscuro);
export const [numWallpaper, setNumWallpaper] = createSignal(numImgGuardado);
export const [mostrarDescansos, setMostrarDescansos] = createSignal(true);
export const [modoColor, setModoColor] = createSignal(ModoColor.Oscuro)
export const [numWallpaper, setNumWallpaper] = createSignal(numImgGuardado)
export const [mostrarDescansos, setMostrarDescansos] = createSignal(true)

View File

@ -1,12 +1,12 @@
import { StyleSheet, css } from "aphrodite";
import { numWallpaper } from "./Store";
import { createEffect, createState } from "solid-js";
import { StyleSheet, css } from "aphrodite"
import { numWallpaper } from "./Store"
import { createEffect, createState } from "solid-js"
const duracionTransicion = 250;
const duracionTransicion = 250
export function Wallpaper() {
/// @ts-ignore
const soportaBackdropFilter = document.body.style.backdropFilter !== undefined;
const soportaBackdropFilter = document.body.style.backdropFilter !== undefined
const estilos = StyleSheet.create({
contenedorCover: {
@ -16,7 +16,7 @@ export function Wallpaper() {
top: "0",
left: "0",
backgroundColor: "#212121",
zIndex: -1
zIndex: -1,
},
cover: {
width: "100vw",
@ -25,43 +25,45 @@ export function Wallpaper() {
backgroundSize: "cover",
zIndex: -1,
transition: `opacity ${duracionTransicion}ms`,
filter: soportaBackdropFilter? "": "blur(40px)"
filter: soportaBackdropFilter ? "" : "blur(40px)",
},
coverTransicion: {
opacity: 0
}
});
opacity: 0,
},
})
const [estilosRaw, setEstilosRaw] = createState({
"background-image": `none`,
opacity: 1
});
"background-image": "none",
opacity: 1,
})
createEffect(() => {
const numImg = numWallpaper();
setEstilosRaw("opacity", 0);
const numImg = numWallpaper()
setEstilosRaw("opacity", 0)
const promesa250ms = new Promise(resolve => {
setTimeout(resolve, duracionTransicion);
});
const promesa250ms = new Promise((resolve) => {
setTimeout(resolve, duracionTransicion)
})
const url = `/img/wall${numImg}.webp`;
const img = new Image();
img.addEventListener("load", async () => {
await promesa250ms;
const url = `/img/wall${numImg}.webp`
const img = new Image()
img.addEventListener("load", async() => {
await promesa250ms
setEstilosRaw({
"background-image": `url('${url}')`,
opacity: 1
});
});
img.src = url;
});
opacity: 1,
})
})
img.src = url
})
return <div className={css(estilos.contenedorCover)}>
<div
className={css(estilos.cover)}
style={{"background-image": estilosRaw["background-image"], opacity: estilosRaw.opacity}}
/>
</div>
return (
<div className={css(estilos.contenedorCover)}>
<div
className={css(estilos.cover)}
style={{"background-image": estilosRaw["background-image"], opacity: estilosRaw.opacity}}
/>
</div>
)
}

View File

@ -1,7 +1,7 @@
import "solid-js";
import { render } from 'solid-js/web';
import App from './App';
import "normalize.css";
import "./styles/global.css";
import "solid-js"
import { render } from "solid-js/web"
import App from "./App"
import "normalize.css"
import "./styles/global.css"
render(App, document.getElementById('root') as Node);
render(App, document.getElementById("root") as Node)

View File

@ -74,4 +74,4 @@ export interface DataProcesada {
fnSeleccionar: () => void
}[]
}
}
}