[FE][Certs] Refactor courses context to its own file

This commit is contained in:
Araozu 2023-12-16 10:29:33 -05:00
parent 1feeb70af8
commit b3bfa4a82c
10 changed files with 137 additions and 122 deletions

View File

@ -0,0 +1,44 @@
import { Show, createSignal } from "solid-js";
import { NewRegister } from "./NewRegister";
import { Registers } from "./Registers";
import { Search } from "./Search";
import { Person } from "../types/Person";
import { useCourses } from "./CourseContext";
export function CertsImpl() {
const [person, setPerson] = createSignal<Person | null>(null);
const [count, setCount] = createSignal(0);
const courses = useCourses()!;
return (
<>
<div class={`top-0 left-0 w-screen h-1 ${courses.loading() ? "fixed" : "hidden"}`}>
<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>
<Show when={!courses.errored()}>
<div class={`grid grid-cols-[16rem_25rem_1fr] ${courses.ready() ? "" : "opacity-25"}`}>
<Search setPerson={setPerson} />
<NewRegister
personId={person()?.person_id ?? null}
onSuccess={() => setCount((x) => x + 1)}
/>
<Registers person={person()} count={count()} />
</div>
</Show>
<Show when={courses.errored()}>
<div>
<div class="p-2 m-8 text-c-on-error bg-c-error rounded-md">
Error al cargar recursos vitales del sistema (lista de cursos)
<br />
Recargue la página para intentar de nuevo
<br />
<br />
{courses?.errorMessage()}
</div>
</div>
</Show>
</>
);
}

View File

@ -0,0 +1,81 @@
import { JSX, Resource, createContext, createMemo, createResource, useContext } from "solid-js";
import axios from "axios";
import { Course } from "../types/Course";
const CoursesContext = createContext<{
data: Resource<Course[]>,
nameMap: () => {[course_name: string]: Course},
idMap: () => {[course_name: string]: Course},
loading: () => boolean,
ready: () => boolean,
errored: () => boolean,
errorMessage: () => string,
}>();
export function CoursesProvider(props: {children: JSX.Element}) {
const [courses] = createResource(fetchAllCourses);
const courseMapMemo = createMemo(() => {
if (courses === undefined || courses?.state !== "ready") {
return {};
}
const data = courses();
type CourseMap = {[course_name: string]: Course};
const map: CourseMap = {};
for (const course of data) {
map[course.course_name] = course;
}
return map;
});
const courseIdMapMemo = createMemo(() => {
if (courses === undefined || courses?.state !== "ready") {
return {};
}
const data = courses();
type CourseMap = {[course_id: number]: Course};
const map: CourseMap = {};
for (const course of data) {
map[course.course_id] = course;
}
return map;
});
const coursesData = {
data: courses,
nameMap: courseMapMemo,
idMap: courseIdMapMemo,
loading: createMemo(() => courses.state === "pending"),
ready: createMemo(() => courses.state === "ready"),
errored: createMemo(() => courses.state === "errored"),
errorMessage: createMemo(() => courses.error?.message ?? ""),
};
return (
<CoursesContext.Provider value={coursesData}>
{props.children}
</CoursesContext.Provider>
);
}
let cachedCourses: Array<Course> | null = null;
async function fetchAllCourses(): Promise<Array<Course>> {
if (cachedCourses !== null) {
return cachedCourses;
}
const result = await axios.get<Array<Course>>(`${import.meta.env.VITE_BACKEND_URL}/api/course`);
cachedCourses = result.data;
return result.data;
}
export function useCourses() {
return useContext(CoursesContext);
}

View File

@ -3,7 +3,7 @@ import { SearchableSelect } from "./SearchableSelect";
import { CustomLabelSelect } from "./CustomLabelSelect"; import { CustomLabelSelect } from "./CustomLabelSelect";
import { RegistrationPreview } from "."; import { RegistrationPreview } from ".";
import { customLabelsMap } from "../../utils/allCustomLabels"; import { customLabelsMap } from "../../utils/allCustomLabels";
import { useCourses } from ".."; import { useCourses } from "../CourseContext";
type HTMLEventFn = JSX.EventHandlerUnion<HTMLFormElement, Event & { type HTMLEventFn = JSX.EventHandlerUnion<HTMLFormElement, Event & {
submitter: HTMLElement; submitter: HTMLElement;

View File

@ -2,7 +2,7 @@ import { For, createMemo, createSignal } from "solid-js";
import { Chip } from "../../components/Chip"; import { Chip } from "../../components/Chip";
import { Course } from "../../types/Course"; import { Course } from "../../types/Course";
import { RegistrationPreview } from "."; import { RegistrationPreview } from ".";
import { useCourses } from ".."; import { useCourses } from "../CourseContext";
type PresetName = "None" | "2 Matpel" | "3 Matpel" | "4 Escolta" | "MD, 2 Matpel" | "3 4x4" | "2 4x4" | "6 Sibia"; type PresetName = "None" | "2 Matpel" | "3 Matpel" | "4 Escolta" | "MD, 2 Matpel" | "3 4x4" | "2 4x4" | "6 Sibia";

View File

@ -6,7 +6,7 @@ import { RegistrationPreview } from ".";
import { loadCustomLabels } from "../../utils/allCustomLabels"; import { loadCustomLabels } from "../../utils/allCustomLabels";
import { FileDashedIcon } from "../../icons/FileDashedIcon"; import { FileDashedIcon } from "../../icons/FileDashedIcon";
import { LoadingIcon } from "../../icons/LoadingIcon"; import { LoadingIcon } from "../../icons/LoadingIcon";
import { useCourses } from ".."; import { useCourses } from "../CourseContext";
function isoDateToLocalDate(date: string): string { function isoDateToLocalDate(date: string): string {

View File

@ -1,5 +1,5 @@
import { createEffect, createSignal, For, onMount } from "solid-js"; import { createEffect, createSignal, For, onMount } from "solid-js";
import { useCourses } from ".."; import { useCourses } from "../CourseContext";
export function SearchableSelect(props: { export function SearchableSelect(props: {
onChange: (id: number | null) => void, onChange: (id: number | null) => void,

View File

@ -12,8 +12,8 @@ import QR from "qrcode";
import saveAs from "file-saver"; import saveAs from "file-saver";
import { genMatpelCarnet } from "../../carnetGenerator/matpel"; import { genMatpelCarnet } from "../../carnetGenerator/matpel";
import { genMachineryCarnet } from "../../carnetGenerator/machinery"; import { genMachineryCarnet } from "../../carnetGenerator/machinery";
import { useCourses } from "..";
import { Course } from "../../types/Course"; import { Course } from "../../types/Course";
import { useCourses } from "../CourseContext";
const carnetEnabled = [ const carnetEnabled = [
"Matpel 3", "Matpel 3",

View File

@ -1,7 +1,7 @@
import { createMemo } from "solid-js"; import { createMemo } from "solid-js";
import { useCourses } from "..";
import { Register } from "../../types/Register"; import { Register } from "../../types/Register";
import { formatDate } from "../../utils/functions"; import { formatDate } from "../../utils/functions";
import { useCourses } from "../CourseContext";
export function RegisterSidebar(props: { export function RegisterSidebar(props: {
register: Register, register: Register,

View File

@ -7,7 +7,7 @@ import { backend, wait } from "../../utils/functions";
import { JsonResult } from "../../types/JsonResult"; import { JsonResult } from "../../types/JsonResult";
import { LoadingIcon } from "../../icons/LoadingIcon"; import { LoadingIcon } from "../../icons/LoadingIcon";
import { RegisterElement, generateCert } from "./RegisterElement"; import { RegisterElement, generateCert } from "./RegisterElement";
import { useCourses } from ".."; import { useCourses } from "../CourseContext";
export function Registers(props: {person: Person | null, count: number}) { export function Registers(props: {person: Person | null, count: number}) {
const [registers, setRegisters] = createSignal<Array<Register>>([]); const [registers, setRegisters] = createSignal<Array<Register>>([]);

View File

@ -1,120 +1,10 @@
import { JSX, Resource, Show, createContext, createMemo, createResource, createSignal, useContext } from "solid-js"; import { CertsImpl } from "./CertsImpl";
import { NewRegister } from "./NewRegister"; import { CoursesProvider } from "./CourseContext";
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() { 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");
const coursesLoading = createMemo(() => courses.state === "pending");
return ( return (
<> <CoursesProvider>
<div class={`top-0 left-0 w-screen h-1 ${coursesLoading() ? "fixed" : "hidden"}`}> <CertsImpl />
<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}>
<Show when={courses.state !== "errored"}>
<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>
</Show>
</CoursesProvider> </CoursesProvider>
<Show when={courses.state === "errored"}>
<div>
<div class="p-2 m-8 text-c-on-error bg-c-error rounded-md">
Error al cargar recursos vitales del sistema (lista de cursos)
<br />
Recargue la página para intentar de nuevo
<br />
<br />
{courses.error?.message}
</div>
</div>
</Show>
</>
); );
} }
const CoursesContext = createContext<{
data: Resource<Course[]>,
nameMap: () => {[course_name: string]: Course},
idMap: () => {[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 = {[course_name: string]: Course};
const map: CourseMap = {};
for (const course of data) {
map[course.course_name] = course;
}
return map;
});
const courseIdMapMemo = createMemo(() => {
if (props.courses === undefined || props.courses?.state !== "ready") {
return {};
}
const data = props.courses();
type CourseMap = {[course_id: number]: Course};
const map: CourseMap = {};
for (const course of data) {
map[course.course_id] = course;
}
return map;
});
const coursesData = {
data: props.courses,
nameMap: courseMapMemo,
idMap: courseIdMapMemo,
};
return (
<CoursesContext.Provider value={coursesData}>
{props.children}
</CoursesContext.Provider>
);
}
let cachedCourses: Array<Course> | null = null;
async function fetchAllCourses(): Promise<Array<Course>> {
if (cachedCourses !== null) {
return cachedCourses;
}
const result = await axios.get<Array<Course>>(`${import.meta.env.VITE_BACKEND_URL}/api/course`);
cachedCourses = result.data;
return result.data;
}
export function useCourses() {
return useContext(CoursesContext);
}