[FE] Use resources & context to load courses, instead of global state
This commit is contained in:
parent
1a236dc505
commit
16efa06639
@ -1,53 +1,11 @@
|
||||
import { Accessor, For, createSignal } from "solid-js";
|
||||
import { For, createMemo, createSignal } from "solid-js";
|
||||
import { Chip } from "../../components/Chip";
|
||||
import { getCourseMemo } from "../../utils/allCourses";
|
||||
import { Course } from "../../types/Course";
|
||||
import { RegistrationPreview } from ".";
|
||||
import { useCourses } from "..";
|
||||
|
||||
type PresetName = "None" | "2 Matpel" | "3 Matpel" | "4 Escolta" | "MD, 2 Matpel" | "3 4x4" | "2 4x4" | "6 Sibia";
|
||||
|
||||
function genPresets(): {[k: string]: Array<Accessor<Course | null>>} {
|
||||
return {
|
||||
"2 Matpel": [
|
||||
getCourseMemo("Matpel 2"),
|
||||
getCourseMemo("Matpel 1"),
|
||||
],
|
||||
"3 Matpel": [
|
||||
getCourseMemo("Matpel 3"),
|
||||
getCourseMemo("Matpel 2"),
|
||||
getCourseMemo("Matpel 1"),
|
||||
],
|
||||
"4 Escolta": [
|
||||
getCourseMemo("Sup. Escolta"),
|
||||
getCourseMemo("Matpel 3"),
|
||||
getCourseMemo("Matpel 2"),
|
||||
getCourseMemo("Matpel 1"),
|
||||
],
|
||||
"MD, 2 Matpel": [
|
||||
getCourseMemo("Matpel 2"),
|
||||
getCourseMemo("Matpel 1"),
|
||||
getCourseMemo("Manejo Defensivo"),
|
||||
],
|
||||
"3 4x4": [
|
||||
getCourseMemo("4x4"),
|
||||
getCourseMemo("Mecanica Basica"),
|
||||
getCourseMemo("Manejo Defensivo"),
|
||||
],
|
||||
"2 4x4": [
|
||||
getCourseMemo("4x4"),
|
||||
getCourseMemo("Manejo Defensivo"),
|
||||
],
|
||||
"6 Sibia": [
|
||||
getCourseMemo("Matpel 2"),
|
||||
getCourseMemo("Matpel 1"),
|
||||
getCourseMemo("Manejo Defensivo"),
|
||||
getCourseMemo("Primeros Auxilios"),
|
||||
getCourseMemo("IPERC"),
|
||||
getCourseMemo("Lucha contra Incendios"),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
export function RegisterPresets(props: {
|
||||
disableCreation: boolean,
|
||||
onAdd: (v: RegistrationPreview) => void
|
||||
@ -56,7 +14,46 @@ export function RegisterPresets(props: {
|
||||
const [selectedPreset, setSelectedPreset] = createSignal<PresetName>("None");
|
||||
const [isPreview, setIsPreview] = createSignal(false);
|
||||
const [error, setError] = createSignal("");
|
||||
const presets = genPresets();
|
||||
const courses = useCourses();
|
||||
|
||||
const presets = createMemo<{[key: string]: Array<Course>}>(() => {
|
||||
if (courses === undefined) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const coursesMap = courses.map();
|
||||
|
||||
const matpel1 = coursesMap["Matpel 1"];
|
||||
const matpel2 = coursesMap["Matpel 2"];
|
||||
const matpel3 = coursesMap["Matpel 3"];
|
||||
const escolta = coursesMap["Sup. Escolta"];
|
||||
const mecanica = coursesMap["Mecanica Basica"];
|
||||
const manejo = coursesMap["Manejo Defensivo"];
|
||||
const primerosAuxilios = coursesMap["Primeros Auxilios"];
|
||||
const iperc = coursesMap.IPERC;
|
||||
const luchaIncendios = coursesMap["Lucha contra Incendios"];
|
||||
const cuatroXcuatro = coursesMap["4x4"];
|
||||
|
||||
if (matpel1 === undefined || matpel2 === undefined || matpel3 === undefined || escolta === undefined ||
|
||||
mecanica === undefined || manejo === undefined || primerosAuxilios === undefined || iperc === undefined ||
|
||||
luchaIncendios === undefined || cuatroXcuatro === undefined
|
||||
) {
|
||||
setError("Un curso de los presets no existe. Error fatal.");
|
||||
return {};
|
||||
}
|
||||
|
||||
const data: {[key: string]: Array<Course>} = {
|
||||
"2 Matpel": [matpel2, matpel1],
|
||||
"3 Matpel": [matpel3,matpel2, matpel1],
|
||||
"4 Escolta": [escolta, matpel3,matpel2, matpel1],
|
||||
"MD, 2 Matpel": [matpel2, matpel1, manejo],
|
||||
"3 4x4": [cuatroXcuatro, mecanica, manejo],
|
||||
"2 4x4": [cuatroXcuatro, manejo],
|
||||
"6 Sibia": [matpel2, matpel1, manejo, primerosAuxilios, iperc, luchaIncendios],
|
||||
};
|
||||
|
||||
return data;
|
||||
});
|
||||
|
||||
const add = () => {
|
||||
if (datePicker === undefined) {
|
||||
@ -76,11 +73,9 @@ export function RegisterPresets(props: {
|
||||
return;
|
||||
}
|
||||
|
||||
const presetIds = presets[preset];
|
||||
const presetIds = presets()[preset];
|
||||
const currentDate = new Date(date);
|
||||
for (const courseMemo of presetIds) {
|
||||
// Get course and course duration from the memo
|
||||
const course = courseMemo();
|
||||
for (const course of presetIds) {
|
||||
if (course === null) {
|
||||
setError(`Un curso no existe. Error fatal. (${preset})`);
|
||||
return;
|
||||
@ -112,7 +107,7 @@ export function RegisterPresets(props: {
|
||||
<p>Las fechas se colocan automáticamente según la duración del curso y su jeraquía.</p>
|
||||
<br />
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<For each={Object.keys(presets)}>
|
||||
<For each={Object.keys(presets())}>
|
||||
{(key) => (
|
||||
<Chip
|
||||
selected={selectedPreset() === key}
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { FilledCard } from "../../components/FilledCard";
|
||||
import { For, Show, createSignal } from "solid-js";
|
||||
import { XIcon } from "../../icons/XIcon";
|
||||
import { allCourses } from "../../utils/allCourses";
|
||||
import { RegisterBatchCreate } from "../../types/Register";
|
||||
import { RegistrationPreview } from ".";
|
||||
import { loadCustomLabels } from "../../utils/allCustomLabels";
|
||||
import { FileDashedIcon } from "../../icons/FileDashedIcon";
|
||||
import { LoadingIcon } from "../../icons/LoadingIcon";
|
||||
import { useCourses } from "..";
|
||||
|
||||
|
||||
function isoDateToLocalDate(date: string): string {
|
||||
@ -101,9 +101,18 @@ function Register(props: {
|
||||
onDelete: (v: number) => void,
|
||||
isPreview: boolean,
|
||||
}) {
|
||||
const courseName = () => {
|
||||
const courses = allCourses();
|
||||
return courses.find((course) => course.course_id === props.courseId)?.course_name ?? `Curso invalido! (${props.courseId})`;
|
||||
const courses = useCourses();
|
||||
|
||||
const courseName = (): string => {
|
||||
if (courses === undefined || courses?.data.state !== "ready") {
|
||||
return "~~Cursos cargando~~";
|
||||
}
|
||||
|
||||
return courses
|
||||
.data()
|
||||
.find((course) => course.course_id === props.courseId)
|
||||
?.course_name ??
|
||||
`~~Curso invalido! (${props.courseId})~~`;
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -1,7 +1,5 @@
|
||||
import { createEffect, createSignal, For } from "solid-js";
|
||||
// import type {CursoGIE} from "../../../model/CursoGIE/cursoGIE.entity";
|
||||
import { isServer } from "solid-js/web";
|
||||
import { allCourses } from "../../utils/allCourses";
|
||||
import { createEffect, createSignal, For, onMount } from "solid-js";
|
||||
import { useCourses } from "..";
|
||||
|
||||
export function SearchableSelect(props: {
|
||||
onChange: (id: number | null) => void,
|
||||
@ -11,6 +9,8 @@ export function SearchableSelect(props: {
|
||||
const [selected, setSelected] = createSignal<number | null>(null);
|
||||
const [inputValue, setInputValue] = createSignal("");
|
||||
|
||||
const courses = useCourses();
|
||||
|
||||
const iHandler = (ev: KeyboardEvent & {currentTarget: HTMLInputElement, target: Element}) => {
|
||||
const inputEl = ev.target as HTMLInputElement;
|
||||
// Clear current selection
|
||||
@ -58,18 +58,22 @@ export function SearchableSelect(props: {
|
||||
/>
|
||||
);
|
||||
|
||||
if (!isServer) {
|
||||
onMount(() => {
|
||||
(inputElement as HTMLInputElement).addEventListener("keydown", (ev) => {
|
||||
if (ev.code === "Enter") {
|
||||
ev.preventDefault();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const filteredOptions = () => {
|
||||
const filterText = filter();
|
||||
|
||||
return allCourses().filter((course) => {
|
||||
if (courses === undefined || courses?.data.state !== "ready") {
|
||||
return [];
|
||||
}
|
||||
|
||||
return courses.data().filter((course) => {
|
||||
let courseText = course.course_name.toLowerCase();
|
||||
courseText = courseText.replace("á", "a");
|
||||
courseText = courseText.replace("é", "e");
|
||||
|
@ -1,21 +1,80 @@
|
||||
import { createSignal } from "solid-js";
|
||||
import { JSX, Resource, createContext, createMemo, createResource, createSignal, useContext } from "solid-js";
|
||||
import { NewRegister } from "./NewRegister";
|
||||
import { Registers } from "./Registers";
|
||||
import { Search } from "./Search";
|
||||
import { Person } from "../types/Person";
|
||||
import axios from "axios";
|
||||
import { Course } from "../types/Course";
|
||||
|
||||
export function Certs() {
|
||||
const [person, setPerson] = createSignal<Person | null>(null);
|
||||
const [count, setCount] = createSignal(0);
|
||||
|
||||
const [courses] = createResource(fetchAllCourses);
|
||||
|
||||
const coursesReady = createMemo(() => courses.state === "ready");
|
||||
|
||||
return (
|
||||
<div class="grid grid-cols-[16rem_25rem_1fr]">
|
||||
<Search setPerson={setPerson} />
|
||||
<NewRegister
|
||||
personId={person()?.person_id ?? null}
|
||||
onSuccess={() => setCount((x) => x + 1)}
|
||||
/>
|
||||
<Registers person={person()} count={count()} />
|
||||
</div>
|
||||
<>
|
||||
<div class={`top-0 left-0 w-screen h-1 ${coursesReady() ? "hidden" : "fixed"}`}>
|
||||
<div class='h-1 w-full bg-c-on-primary overflow-hidden'>
|
||||
<div class='progress w-full h-full bg-c-primary left-right' />
|
||||
</div>
|
||||
</div>
|
||||
<CoursesProvider courses={courses}>
|
||||
<div class={`grid grid-cols-[16rem_25rem_1fr] ${coursesReady() ? "" : "opacity-25"}`}>
|
||||
<Search setPerson={setPerson} />
|
||||
<NewRegister
|
||||
personId={person()?.person_id ?? null}
|
||||
onSuccess={() => setCount((x) => x + 1)}
|
||||
/>
|
||||
<Registers person={person()} count={count()} />
|
||||
</div>
|
||||
</CoursesProvider>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
const CoursesContext = createContext<{
|
||||
data: Resource<Course[]>,
|
||||
map: () => {[course_name: string]: Course},
|
||||
}>();
|
||||
|
||||
function CoursesProvider(props: {courses: Resource<Course[]>, children: JSX.Element}) {
|
||||
const courseMapMemo = createMemo(() => {
|
||||
if (props.courses === undefined || props.courses?.state !== "ready") {
|
||||
return {};
|
||||
}
|
||||
|
||||
const data = props.courses();
|
||||
|
||||
type CourseMap = {[k: string]: Course};
|
||||
const map: CourseMap = {};
|
||||
for (const course of data) {
|
||||
map[course.course_name] = course;
|
||||
}
|
||||
|
||||
return map;
|
||||
});
|
||||
|
||||
const coursesData = {
|
||||
data: props.courses,
|
||||
map: courseMapMemo,
|
||||
};
|
||||
|
||||
return (
|
||||
<CoursesContext.Provider value={coursesData}>
|
||||
{props.children}
|
||||
</CoursesContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
async function fetchAllCourses(): Promise<Array<Course>> {
|
||||
const result = await axios.get<Array<Course>>(`${import.meta.env.VITE_BACKEND_URL}/api/course`);
|
||||
return result.data;
|
||||
}
|
||||
|
||||
export function useCourses() {
|
||||
return useContext(CoursesContext);
|
||||
}
|
||||
|
@ -29,3 +29,23 @@ body {
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
.progress {
|
||||
animation: progress 1s infinite linear;
|
||||
}
|
||||
|
||||
.left-right {
|
||||
transform-origin: 0% 50%;
|
||||
}
|
||||
@keyframes progress {
|
||||
0% {
|
||||
transform: translateX(0) scaleX(0);
|
||||
}
|
||||
40% {
|
||||
transform: translateX(0) scaleX(0.4);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(100%) scaleX(0.5);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ import { Course } from "../types/Course";
|
||||
|
||||
type CourseMap = {[k: number]: Course};
|
||||
|
||||
export const [allCourses, setAllCourses] = createSignal<Array<Course>>([]);
|
||||
const [allCourses, setAllCourses] = createSignal<Array<Course>>([]);
|
||||
export const [courseMap, setCourseMap] = createSignal<CourseMap>({});
|
||||
|
||||
(() => {
|
||||
|
@ -8,6 +8,20 @@ module.exports = {
|
||||
fontFamily: {
|
||||
"mono": ["Inconsolata", "monospace"],
|
||||
},
|
||||
animation: {
|
||||
progress: "progress 1s infinite linear",
|
||||
},
|
||||
keyframes: {
|
||||
progress: {
|
||||
"0%": { transform: " translateX(0) scaleX(0)" },
|
||||
"40%": { transform: "translateX(0) scaleX(0.4)" },
|
||||
"100%": { transform: "translateX(100%) scaleX(0.5)" },
|
||||
},
|
||||
},
|
||||
transformOrigin: {
|
||||
"left-right": "0% 50%",
|
||||
},
|
||||
|
||||
},
|
||||
colors: {
|
||||
"c-primary": "var(--c-primary)",
|
||||
|
Loading…
Reference in New Issue
Block a user