From b162b14eb38b4011a8de064f3344199d1ebbffdb Mon Sep 17 00:00:00 2001 From: Araozu Date: Wed, 4 Oct 2023 16:53:54 -0500 Subject: [PATCH] [Classroom][FE] UI for classroom enrolling --- .../OnlineClassroom/ClassroomRegistration.tsx | 42 ++++++++ .../ClassroomRegistrationPreview.tsx | 62 ++++++++++++ .../ClassroomSearchableSelect.tsx | 96 +++++++++++++++++++ frontend/src/OnlineClassroom/index.tsx | 23 ++--- .../certs/NewRegister/ManualRegistration.tsx | 10 +- .../src/certs/NewRegister/RegisterPresets.tsx | 1 + .../src/certs/NewRegister/RegisterPreview.tsx | 3 +- frontend/src/certs/NewRegister/index.tsx | 1 + frontend/src/types/ClassroomCourse.ts | 45 +++++++++ frontend/src/utils/allCustomLabels.ts | 1 + 10 files changed, 270 insertions(+), 14 deletions(-) create mode 100644 frontend/src/OnlineClassroom/ClassroomRegistration.tsx create mode 100644 frontend/src/OnlineClassroom/ClassroomRegistrationPreview.tsx create mode 100644 frontend/src/OnlineClassroom/ClassroomSearchableSelect.tsx diff --git a/frontend/src/OnlineClassroom/ClassroomRegistration.tsx b/frontend/src/OnlineClassroom/ClassroomRegistration.tsx new file mode 100644 index 0000000..e9e15fd --- /dev/null +++ b/frontend/src/OnlineClassroom/ClassroomRegistration.tsx @@ -0,0 +1,42 @@ +import { createSignal } from "solid-js"; +import { FilledCard } from "../components/FilledCard"; +import { ClassroomRegistrationPreview } from "./ClassroomRegistrationPreview"; +import { ClassroomSearchableSelect } from "./ClassroomSearchableSelect"; +import { ClassroomCourseValue } from "../types/ClassroomCourse"; + +export function ClassroomRegistration() { + const [selections, setSelections] = createSignal>([]); + + return ( +
+ +

Registrar cursos

+ +
+ setSelections((s) => ([...s, x]))} + /> +
+
+ + setSelections((course) => course.filter((v) => v !== course_key))} + /> +
+ ); +} + + +function ManualClassroomRegistration(props: {onAdd: (k: ClassroomCourseValue) => void}) { + return ( +
+

Haz click en un curso para agregarlo

+
+ + + + ); +} diff --git a/frontend/src/OnlineClassroom/ClassroomRegistrationPreview.tsx b/frontend/src/OnlineClassroom/ClassroomRegistrationPreview.tsx new file mode 100644 index 0000000..9710f7f --- /dev/null +++ b/frontend/src/OnlineClassroom/ClassroomRegistrationPreview.tsx @@ -0,0 +1,62 @@ +import { For, createMemo } from "solid-js"; +import { FilledCard } from "../components/FilledCard"; +import { LoadingIcon } from "../icons/LoadingIcon"; +import { LoadingStatus, useLoading } from "../utils/functions"; +import { ClassroomCourseValue, allClassrooomCourses } from "../types/ClassroomCourse"; +import { XIcon } from "../icons/XIcon"; + +export function ClassroomRegistrationPreview(props: { + selections: Array, + deleteRegister: (k: string) => void, +}) { + const {status} = useLoading(); + + const loading = createMemo(() => status() === LoadingStatus.Loading); + + const submit = async() => { + }; + + return ( + +

Confirmar registro

+
+ + {(course_key) => ( +
+ + {allClassrooomCourses[course_key]} + + +
+ )} +
+ + +
+
+ ); +} diff --git a/frontend/src/OnlineClassroom/ClassroomSearchableSelect.tsx b/frontend/src/OnlineClassroom/ClassroomSearchableSelect.tsx new file mode 100644 index 0000000..8575874 --- /dev/null +++ b/frontend/src/OnlineClassroom/ClassroomSearchableSelect.tsx @@ -0,0 +1,96 @@ +import { createSignal, For } from "solid-js"; +import { isServer } from "solid-js/web"; +import { allClassrooomCourses, ClassroomCourseValue } from "../types/ClassroomCourse"; + +export function ClassroomSearchableSelect(props: { + onAdd: (key: ClassroomCourseValue) => void, +}) { + const [filter, setFilter] = createSignal(""); + const [inputValue, setInputValue] = createSignal(""); + + const iHandler = (ev: KeyboardEvent & {currentTarget: HTMLInputElement, target: Element}) => { + const inputEl = ev.target as HTMLInputElement; + + let filter: string = inputEl.value.toLowerCase(); + filter = filter.replace("á", "a"); + filter = filter.replace("é", "e"); + filter = filter.replace("í", "i"); + filter = filter.replace("ó", "o"); + filter = filter.replace("ú", "u"); + setFilter(filter); + }; + + const selectCourse = (key: ClassroomCourseValue) => { + props.onAdd(key); + setFilter(""); + setInputValue(""); + }; + + const inputElement = ( + setInputValue(ev.target.value)} + autocomplete="off" + /> + ); + + if (!isServer) { + (inputElement as HTMLInputElement).addEventListener("keydown", (ev) => { + if (ev.code === "Enter") { + ev.preventDefault(); + } + }); + } + + const filteredOptions = () => { + const filterText = filter(); + + return Object.entries(allClassrooomCourses).filter(([, course]) => { + let courseText = course.toLowerCase(); + courseText = courseText.replace("á", "a"); + courseText = courseText.replace("é", "e"); + courseText = courseText.replace("í", "i"); + courseText = courseText.replace("ó", "o"); + courseText = courseText.replace("ú", "u"); + + return courseText.indexOf(filterText) !== -1; + }); + }; + + return ( + <> + {inputElement} +
+
+ + {([courseKey, courseName]) => ( + + )} + +
+ + ); +} diff --git a/frontend/src/OnlineClassroom/index.tsx b/frontend/src/OnlineClassroom/index.tsx index 7d23709..70e2910 100644 --- a/frontend/src/OnlineClassroom/index.tsx +++ b/frontend/src/OnlineClassroom/index.tsx @@ -5,6 +5,7 @@ import { FilledCard } from "../components/FilledCard"; import { ClassroomUserCreation } from "./ClassroomUserCreation"; import { ClassroomVinculation } from "./ClassroomVinculation"; import { ClassroomUserCourses } from "./ClassroomUserCourses"; +import { ClassroomRegistration } from "./ClassroomRegistration"; type TabType = "Vinculate" | "Create"; @@ -14,19 +15,19 @@ export function OnlineClassroom() { return (
-
- - setPerson((p) => ({...p!, person_classroom_id: classroom_id}))} - /> - - - - + + + + setPerson((p) => ({...p!, person_classroom_id: classroom_id}))} + /> + + + + -
); } diff --git a/frontend/src/certs/NewRegister/ManualRegistration.tsx b/frontend/src/certs/NewRegister/ManualRegistration.tsx index 4804e9e..2769fd6 100644 --- a/frontend/src/certs/NewRegister/ManualRegistration.tsx +++ b/frontend/src/certs/NewRegister/ManualRegistration.tsx @@ -3,6 +3,7 @@ import { SearchableSelect } from "./SearchableSelect"; import { CustomLabelSelect } from "./CustomLabelSelect"; import { courseMap } from "../../utils/allCourses"; import { RegistrationPreview } from "."; +import { customLabelsMap } from "../../utils/allCustomLabels"; type HTMLEventFn = JSX.EventHandlerUnion(null); + const [selectedCourseId, setSelectedCourseId] = createSignal(null); const [customLabel, setCustomLabel] = createSignal(""); const [isPreview, setIsPreview] = createSignal(false); @@ -65,11 +66,16 @@ export function ManualRegistration(props: { return; } + const label = customLabel(); + // TODO: Refactor along with allCustomLabel.tsx + const custom_label_id = Object.entries(customLabelsMap()).find(([, v]) => (v.custom_label_value === label))?.[1].custom_label_id ?? -1; + const data: RegistrationPreview = { courseId: subject, date, customLabel: customLabel(), is_preview: isPreview(), + custom_label_id, }; props.onAdd(data); @@ -83,7 +89,7 @@ export function ManualRegistration(props: {
diff --git a/frontend/src/certs/NewRegister/RegisterPresets.tsx b/frontend/src/certs/NewRegister/RegisterPresets.tsx index 206f3ff..ebfe5f9 100644 --- a/frontend/src/certs/NewRegister/RegisterPresets.tsx +++ b/frontend/src/certs/NewRegister/RegisterPresets.tsx @@ -88,6 +88,7 @@ export function RegisterPresets(props: { date: dateYYYYMMDD, customLabel: "", is_preview: isPreview(), + custom_label_id: 1, }); // Substract current date for the next course diff --git a/frontend/src/certs/NewRegister/RegisterPreview.tsx b/frontend/src/certs/NewRegister/RegisterPreview.tsx index ba3f3ef..cb92775 100644 --- a/frontend/src/certs/NewRegister/RegisterPreview.tsx +++ b/frontend/src/certs/NewRegister/RegisterPreview.tsx @@ -26,12 +26,13 @@ export function RegisterPreview(props: {selections: Array, await wait(2000); - const registers: RegisterBatchCreate = props.selections.map(({courseId, date, customLabel, is_preview}) => ({ + const registers: RegisterBatchCreate = props.selections.map(({courseId, date, customLabel, is_preview, custom_label_id}) => ({ person_id: props.personId!, course_id: courseId, date, custom_label: customLabel, is_preview, + custom_label_id, })); const result = await createRegisters(registers); diff --git a/frontend/src/certs/NewRegister/index.tsx b/frontend/src/certs/NewRegister/index.tsx index 20f58a1..2e7ca36 100644 --- a/frontend/src/certs/NewRegister/index.tsx +++ b/frontend/src/certs/NewRegister/index.tsx @@ -12,6 +12,7 @@ export type RegistrationPreview = { date: string, customLabel: string, is_preview: boolean, + custom_label_id: number, } export function NewRegister(props: { diff --git a/frontend/src/types/ClassroomCourse.ts b/frontend/src/types/ClassroomCourse.ts index 61ff968..1151c24 100644 --- a/frontend/src/types/ClassroomCourse.ts +++ b/frontend/src/types/ClassroomCourse.ts @@ -1,3 +1,48 @@ export type ClassroomCourse = { name: string } + +export type ClassroomCourseValue = +| "MATPELNIVEL1" +| "MATPELNIVEL2" +| "MATPELNIVEL3" +| "CAJAEATONFULLER" +| "CAMIONMINERO797CAT" +| "ESPACIOSCONFINADOS" +| "HERRAMIENTASDEGESTION" +| "IPERC" +| "MANEJODEFENSIVO" +| "PREVENCIONDERIESGOSLABORALES" +| "PREVENCIONYPROTECCIONCONTRAINCENDIOS" +| "PRIMEROSAUXILIOS" +| "SBC" +| "SEGURIDADENLAOPERACION" +| "SEGURIDADMATPEL123" +| "SUPERVISORDEALTORIESGO" +| "SUPERVISORDESEGURIDADMINEROINDUSTRIA" +| "SUPERVISORESCOLTAMATPEL" +| "TRABAJOSENALTURA" +; + + +export const allClassrooomCourses = { + "MATPELNIVEL1": "MATPEL 1", + "MATPELNIVEL2": "MATPEL 2", + "MATPELNIVEL3": "MATPEL 3", + "SUPERVISORESCOLTAMATPEL": "SUPERVISOR ESCOLTA - MATPEL", + "MANEJODEFENSIVO": "MANEJO DEFENSIVO", + "SEGURIDADENLAOPERACION": "SEGURIDAD EN LA OPERACIÓN", + "HERRAMIENTASDEGESTION": "HERRAMIENTAS DE GESTIÓN", + "CAJAEATONFULLER": "CAJA EATON FULLER", + "CAMIONMINERO797CAT": "CAMION MINERO 797 CAT", + "ESPACIOSCONFINADOS": "ESPACIOS CONFINADOS", + "IPERC": "IPERC", + "PREVENCIONDERIESGOSLABORALES": "PREVENCION DE RIESGOS LABORALES", + "PREVENCIONYPROTECCIONCONTRAINCENDIOS": "PREVENCIÓN Y PROTECCIÓN CONTRA INCENDIOS", + "PRIMEROSAUXILIOS": "PRIMEROS AUXILIOS", + "SBC": "SEGURIDAD BASADA EN EL COMPORTAMIENTO \"SBC\"", + "SEGURIDADMATPEL123": "SEGURIDAD CON MATERIALES Y RESIDUOS PELIGROSOS - NIVEL 1,2 y 3", + "SUPERVISORDEALTORIESGO": "SUPERVISOR DE ALTO RIESGO", + "SUPERVISORDESEGURIDADMINEROINDUSTRIA": "SUPERVISOR DE SEGURIDAD MINERO INDUSTRIAL", + "TRABAJOSENALTURA": "TRABAJOS EN ALTURA", +}; diff --git a/frontend/src/utils/allCustomLabels.ts b/frontend/src/utils/allCustomLabels.ts index f5d4c50..3c02807 100644 --- a/frontend/src/utils/allCustomLabels.ts +++ b/frontend/src/utils/allCustomLabels.ts @@ -3,6 +3,7 @@ import { CustomLabel } from "../types/CustomLabel"; type CustomLabelsMap = {[k: number]: CustomLabel}; +// TODO: Refactor, this is inefficient export const [customLabelsMap, setCustomLabelsMap] = createSignal<{[k: number]: CustomLabel}>({}); export function loadCustomLabels() {