[FE] Batch mode: Add selector for courses

master
Araozu 2023-07-02 21:06:53 -05:00
parent 89bdc1499a
commit ab378de775
5 changed files with 120 additions and 42 deletions

View File

@ -1,5 +1,7 @@
import { For, createSignal, onMount } from "solid-js";
import { DniEntry } from "./DniEntry";
import { NewRegister } from "../components/NewRegister";
import { subjects } from "../subjects";
export function DniGroup(props: {group: string, index: number}) {
const [dnis, setDnis] = createSignal<Array<string>>([]);
@ -33,7 +35,63 @@ export function DniGroup(props: {group: string, index: number}) {
<h2 class="font-medium text-xl text-c-success pb-2">
Grupo #{props.index + 1} - cursos y fechas
</h2>
<hr />
<CourseManager />
</div>
</div>
);
}
function CourseManager() {
const [courses, setCourses] = createSignal<Array<[number, string]>>([]);
async function registerCourse(_: number, subjectId: number, date: string): Promise<null | string> {
setCourses((x) => [...x, [subjectId, date]]);
return null;
}
return (
<div class="grid grid-cols-2 gap-2">
<NewRegister
personId={0}
onSuccess={() => {}}
registerFn={registerCourse}
labelText={"Seleccionar cursos"}
/>
<div class="p-4">
<h2 class="mb-4 font-bold text-xl">Verificar y confirmar cursos</h2>
<div class="">
<For each={courses()}>
{(course) => <Course
id={course[0]}
date={course[1]}
onDelete={(id) => setCourses((x) => x.filter((y) => y[0] !== id))}
/>}
</For>
</div>
<button
class="my-2 bg-c-primary text-c-on-primary px-4 py-2 rounded-md cursor-pointer
disabled:opacity-50 disabled:cursor-not-allowed"
type="button"
>
Registrar <b>todas</b> las personas en <b>todos</b> los cursos
</button>
</div>
</div>
);
}
function Course(props: {id: number, date: string, onDelete: (id: number) => void}) {
const courseName = () => subjects().find((x) => x.id === props.id)?.nombre ?? "NOT FOUND";
return (
<div class="grid grid-cols-[auto_10rem_5rem] gap-2 hover:bg-c-surface-variant hover:text-c-on-surface-variant font-mono">
<p>{courseName()}</p>
<p>{props.date}</p>
<div>
<button class="underline" onclick={() => props.onDelete(props.id)}>Eliminar</button>
</div>
</div>
);

View File

@ -17,7 +17,7 @@ export function Certs() {
<div class="grid grid-cols-2 gap-2">
<Registers person={person()} lastUpdate={lastUpdate()} />
<NewRegister
person={person()}
personId={person()?.id ?? -1}
onSuccess={() => setLastUpdate((x) => x + 1)}
/>
</div>

View File

@ -1,16 +1,45 @@
import { createSignal, onMount, Show } from "solid-js";
import type { CursoGIE } from "../../model/CursoGIE/cursoGIE.entity";
import { createSignal, Show } from "solid-js";
import { SearchableSelect } from "./NewRegister/SearchableSelect";
import { JSX } from "solid-js/jsx-runtime";
import { Person } from "src/types/Person";
import { subjects } from "../subjects";
type HTMLEventFn = JSX.EventHandlerUnion<HTMLFormElement, Event & {
submitter: HTMLElement;
}>;
export function NewRegister(props: {person: Person | null, onSuccess: () => void}) {
const [subjects, setSubjects] = createSignal<Array<CursoGIE>>([]);
async function defaultNewRegisterFn(personId: number, subjectId: number, date: string): Promise<null | string> {
const response = await fetch("/certificate", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
personId,
subjectId,
date,
}),
});
if (response.ok) {
return null;
} else {
const data = await response.json();
return JSON.stringify(data);
}
}
type RegisterFn = (personId: number, subjectId: number, date: string) => Promise<null | string>;
const defaultLabelText = "3. Crear nuevos registros";
export function NewRegister(props: {
personId: number | null,
onSuccess: () => void,
registerFn?: RegisterFn,
labelText?: string,
}) {
const [error, setError] = createSignal("");
const [loading, setLoading] = createSignal(false);
// Used to update SearchableSelect.tsx manually
@ -27,23 +56,6 @@ export function NewRegister(props: {person: Person | null, onSuccess: () => void
/>
);
// Loads subjects from DB
onMount(async() => {
console.log("Retrieve all subjects");
try {
const response = await fetch("/subject/");
if (response.ok) {
setSubjects(await response.json());
} else {
setError("No se pudo cargar cursos");
}
} catch (e) {
setError(`No se pudo cargar cursos. ${JSON.stringify(e)}`);
}
});
const register: HTMLEventFn = async(ev) => {
ev.preventDefault();
@ -65,24 +77,17 @@ export function NewRegister(props: {person: Person | null, onSuccess: () => void
setLoading(true);
const response = await fetch("/certificate", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
personId: props.person?.id ?? -1,
subjectId: subject,
const result = await (props.registerFn ?? defaultNewRegisterFn)(
props.personId ?? -1,
subject,
date,
}),
});
);
if (response.ok) {
if (result === null) {
props.onSuccess();
setCount((x) => x + 1);
} else {
const data = await response.json();
setError(JSON.stringify(data));
setError(result);
}
setLoading(false);
@ -90,9 +95,9 @@ export function NewRegister(props: {person: Person | null, onSuccess: () => void
return (
<div class="p-4">
<h2 class="mb-4 font-bold text-xl">3. Crear nuevos registros</h2>
<h2 class="mb-4 font-bold text-xl">{props.labelText ?? defaultLabelText}</h2>
<Show when={props.person?.id !== -1}>
<Show when={props.personId !== -1}>
<form
class="px-4 grid"
style={{"grid-template-columns": "30rem 12rem 10rem auto", "grid-column-gap": "1rem"}}

View File

@ -63,8 +63,7 @@ export function SearchableSelect(props: {
{inputElement}
<br/>
<div
class="border-c-outline border-2 rounded overflow-y-scroll"
style={{"max-height": "18rem", "min-height": "12rem"}}
class="border-c-outline border-2 rounded overflow-y-scroll h-[18rem]"
>
<For each={props.subjects}>
{(s) => (

16
src/views/subjects.ts Normal file
View File

@ -0,0 +1,16 @@
import { createSignal } from "solid-js";
import { CursoGIE } from "src/model/CursoGIE/cursoGIE.entity";
export const [subjects, setSubjects] = createSignal<Array<CursoGIE>>([]);
(async() => {
try {
const response = await fetch("/subject/");
if (response.ok) {
setSubjects(await response.json());
}
} catch (e) {
/* empty */
}
})();