[FE][Certs] UI for certificate presets

master
Araozu 2023-08-25 17:54:30 -05:00
parent a4f46c75cf
commit 5dc64e217e
9 changed files with 104 additions and 40 deletions

View File

@ -0,0 +1,18 @@
import { Chip } from "../../components/Chip";
export function RegisterPresets() {
return (
<div class="h-52">
<p>Las fechas se colocan automáticamente según la duración del curso.</p>
<br />
<div class="flex flex-wrap gap-2">
<Chip text="2 Matpel" />
<Chip text="3 Matpel" />
<Chip text="4 Escolta" />
<Chip text="MD, 2 Matpel" />
<Chip text="3 4x4" />
<Chip text="2 4x4" />
</div>
</div>
);
}

View File

@ -1,10 +1,8 @@
import { FilledCard } from "../../components/FilledCard";
import { For } from "solid-js";
// import { subjects } from "src/views/subjects";
import { XIcon } from "../../icons/XIcon";
import { allCourses } from "../../utils/allCourses";
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const subjects: () => Array<any> = () => [];
function isoDateToLocalDate(date: string): string {
const [,month, day] = /\d{4}-(\d{2})-(\d{2})/.exec(date) ?? "";
@ -15,6 +13,7 @@ export function RegisterPreview(props: {selections: Array<[number, string]>, per
const submit = async() => {
console.log("Submit...");
// TODO: Send all register requests at once
for (const [courseId, date] of props.selections) {
const result = await defaultNewRegisterFn(
props.personId ?? -1,
@ -56,8 +55,8 @@ export function RegisterPreview(props: {selections: Array<[number, string]>, per
function Register(props: {courseId: number, date: string, onDelete: (v: number) => void}) {
const courseName = () => {
const courses = subjects();
return courses.find((c) => c.id === props.courseId)?.nombre ?? "!";
const courses = allCourses();
return courses.find((course) => course.course_id === props.courseId)?.course_name ?? "!";
};
return (

View File

@ -1,9 +1,9 @@
import { createEffect, createSignal, For } from "solid-js";
import type {CursoGIE} from "../../../model/CursoGIE/cursoGIE.entity";
// import type {CursoGIE} from "../../../model/CursoGIE/cursoGIE.entity";
import { isServer } from "solid-js/web";
import { allCourses } from "../../utils/allCourses";
export function SearchableSelect(props: {
subjects: Array<CursoGIE>,
onChange: (id: number | null) => void,
count: number
}) {
@ -68,15 +68,15 @@ export function SearchableSelect(props: {
const filteredOptions = () => {
const filterText = filter();
return props.subjects.filter((subject) => {
let subjectText = subject.nombre.toLowerCase();
subjectText = subjectText.replace("á", "a");
subjectText = subjectText.replace("é", "e");
subjectText = subjectText.replace("í", "i");
subjectText = subjectText.replace("ó", "o");
subjectText = subjectText.replace("ú", "u");
return allCourses().filter((course) => {
let courseText = course.course_name.toLowerCase();
courseText = courseText.replace("á", "a");
courseText = courseText.replace("é", "e");
courseText = courseText.replace("í", "i");
courseText = courseText.replace("ó", "o");
courseText = courseText.replace("ú", "u");
return selected() === null && subjectText.indexOf(filterText) !== -1;
return selected() === null && courseText.indexOf(filterText) !== -1;
});
};
@ -95,11 +95,11 @@ export function SearchableSelect(props: {
onclick={(ev) => {
ev.preventDefault();
setSelected(s.id);
setInputValue(s.nombre);
setSelected(s.course_id);
setInputValue(s.course_name);
}}
>
{s.nombre}
{s.course_name}
</button>
)}
</For>

View File

@ -1,12 +1,10 @@
import { createSignal, Show } from "solid-js";
import { SearchableSelect } from "./SearchableSelect";
import { JSX } from "solid-js/jsx-runtime";
// import { subjects } from "../subjects";
import { FilledCard } from "../../components/FilledCard";
import { RegisterPreview } from "./RegisterPreview";
import { RegisterPresets } from "./RegisterPresets";
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const subjects: () => Array<any> = () => [];
type HTMLEventFn = JSX.EventHandlerUnion<HTMLFormElement, Event & {
submitter: HTMLElement;
@ -30,13 +28,13 @@ export function NewRegister(props: {
return (
<div class="h-screen overflow-y-scroll">
<FilledCard class="border border-c-outline overflow-hidden">
<h2 class="p-4 font-bold text-xl">Registrar certs</h2>
<h2 class="p-3 font-bold text-xl">Agregar certs</h2>
<RegisterTabs active={active()} setActive={setActive} />
<div class="bg-c-surface p-4 h-[22rem]">
<Show when={active() === "Presets"}>
<p>Proximamente...</p>
<RegisterPresets />
</Show>
<Show when={active() === "Manual"}>
<ManualCerts personId={props.personId} onAdd={(v) => setSelections((x) => [...x, v])} />
@ -82,21 +80,20 @@ function ManualCerts(props: {personId: number | null, onAdd: (v: [number, string
const [count, setCount] = createSignal(0);
const [error, setError] = createSignal("");
const [selectedSubject, setSelectedSubject] = createSignal<number | null>(null);
const [selectedCourseId, seSelectedCourseId] = createSignal<number | null>(null);
const datePicker = (
<input
id="create-date"
class="bg-c-surface text-c-on-surface border border-c-outline rounded-lg p-2"
type="date"
/>
);
let datePicker: HTMLInputElement | undefined;
const register: HTMLEventFn = async(ev) => {
ev.preventDefault();
const subject = selectedSubject();
const date = (datePicker as HTMLInputElement).value;
const subject = selectedCourseId();
if (datePicker === undefined) {
return;
}
const date = datePicker.value;
if (subject === null) {
setError("Selecciona un curso");
@ -123,16 +120,35 @@ function ManualCerts(props: {personId: number | null, onAdd: (v: [number, string
<form onsubmit={register}>
<div>
<SearchableSelect
subjects={subjects()}
onChange={setSelectedSubject}
onChange={seSelectedCourseId}
count={count()}
/>
</div>
<div class="relative my-4">
{datePicker}
<div class="my-4 grid grid-cols-[9.5rem_auto] gap-2">
<div class="relative">
<input
ref={datePicker}
id="create-date"
class="bg-c-surface text-c-on-surface border border-c-outline rounded-lg p-2 font-mono"
type="date"
/>
<label for="create-date" class="absolute -top-2 left-2 text-xs bg-c-surface px-1">Fecha</label>
</div>
<div class="relative">
<input
ref={datePicker}
id="create-date"
class="bg-c-surface text-c-on-surface border border-c-outline rounded-lg p-2 font-mono w-full
disabled:opacity-50 disabled:cursor-not-allowed"
type="text"
disabled
/>
<label for="create-date" class="absolute -top-2 left-2 text-xs bg-c-surface px-1">
Denominación
</label>
</div>
</div>
<input
class="bg-c-primary text-c-on-primary px-4 py-2 rounded-full cursor-pointer

View File

@ -33,6 +33,7 @@ export function Search(props: {setPerson: (p: Person | null) => void}) {
});
} else {
setQrBase64(null);
setPerson(null);
}
});
@ -44,8 +45,9 @@ export function Search(props: {setPerson: (p: Person | null) => void}) {
setError("");
try {
const response = await fetch(`/api/person/${dni()}`);
const response = await fetch(`${import.meta.env.VITE_BACKEND_URL}/api/person/${dni()}`);
const body = await response.json();
if (response.ok) {
setPerson(body);
props.setPerson(body);

View File

@ -0,0 +1,9 @@
export function Chip(props: {text: string}) {
return (
<div class="inline-block px-3 py-1 border border-c-outline rounded-md text-sm cursor-pointer
hover:bg-c-surface-variant hover:text-c-on-surface-variant transition-colors"
>
{props.text}
</div>
);
}

View File

@ -0,0 +1,8 @@
export type Course = {
course_id: number,
course_code: number,
course_name: string,
course_display_name: string,
course_days_amount: number,
course_has_custom_label: boolean,
}

View File

@ -0,0 +1,12 @@
import { createSignal } from "solid-js";
import { Course } from "../types/Course";
export const [allCourses, setAllCourses] = createSignal<Array<Course>>([]);
(() => {
// Get all courses from the API
fetch(`${import.meta.env.VITE_BACKEND_URL}/api/course`)
.then((res) => res.json())
.then((data) => setAllCourses(data));
})();