Support registering & displaying preview certs

This commit is contained in:
Araozu 2023-09-20 12:26:40 -05:00
parent 6bd0acc03e
commit e8e5835236
9 changed files with 88 additions and 31 deletions

View File

@ -1,6 +1,5 @@
use crate::db; use crate::db;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sqlx::Row;
#[derive(sqlx::FromRow, Serialize, Deserialize)] #[derive(sqlx::FromRow, Serialize, Deserialize)]
pub struct CustomLabel { pub struct CustomLabel {

View File

@ -16,6 +16,7 @@ pub struct RegisterCreate {
date: String, date: String,
/// Foreign key to the custom_label table /// Foreign key to the custom_label table
custom_label: String, custom_label: String,
is_preview: bool,
} }
impl RegisterCreate { impl RegisterCreate {
@ -57,7 +58,7 @@ impl RegisterCreate {
current_date, current_date,
self.date, self.date,
custom_label_id, custom_label_id,
false, self.is_preview,
self.person_id, self.person_id,
self.course_id self.course_id
) )

View File

@ -18,6 +18,7 @@ export function ManualRegistration(props: {
const [selectedCourseId, seSelectedCourseId] = createSignal<number | null>(null); const [selectedCourseId, seSelectedCourseId] = createSignal<number | null>(null);
const [customLabel, setCustomLabel] = createSignal(""); const [customLabel, setCustomLabel] = createSignal("");
const [isPreview, setIsPreview] = createSignal(false);
let datePicker: HTMLInputElement | undefined; let datePicker: HTMLInputElement | undefined;
@ -68,6 +69,7 @@ export function ManualRegistration(props: {
courseId: subject, courseId: subject,
date, date,
customLabel: customLabel(), customLabel: customLabel(),
is_preview: isPreview(),
}; };
props.onAdd(data); props.onAdd(data);
@ -86,7 +88,7 @@ export function ManualRegistration(props: {
/> />
</div> </div>
<div class="my-4 grid grid-cols-[9.5rem_auto] gap-2"> <div class="mt-4 mb-2 grid grid-cols-[9.5rem_auto] gap-2">
<div class="relative"> <div class="relative">
<input <input
ref={datePicker} ref={datePicker}
@ -108,7 +110,16 @@ export function ManualRegistration(props: {
</div> </div>
</Show> </Show>
</div> </div>
<div>
<input
oninput={(ev) => setIsPreview(ev.target.checked)}
checked={isPreview()}
type="checkbox"
name="is_preview"
id="manual-is-preview"
/>
<label class="px-2 py-2 select-none" for="manual-is-preview">Cert sin firmas</label>
</div>
<div> <div>

View File

@ -46,6 +46,7 @@ export function RegisterPresets(props: {
}) { }) {
let datePicker: HTMLInputElement | undefined; let datePicker: HTMLInputElement | undefined;
const [selectedPreset, setSelectedPreset] = createSignal<PresetName>("None"); const [selectedPreset, setSelectedPreset] = createSignal<PresetName>("None");
const [isPreview, setIsPreview] = createSignal(false);
const [error, setError] = createSignal(""); const [error, setError] = createSignal("");
const presets = genPresets(); const presets = genPresets();
@ -86,6 +87,7 @@ export function RegisterPresets(props: {
courseId, courseId,
date: dateYYYYMMDD, date: dateYYYYMMDD,
customLabel: "", customLabel: "",
is_preview: isPreview(),
}); });
// Substract current date for the next course // Substract current date for the next course
@ -112,7 +114,7 @@ export function RegisterPresets(props: {
</For> </For>
</div> </div>
</div> </div>
<div class="my-4"> <div class="mt-4 mb-2">
<div class="relative"> <div class="relative">
<input <input
ref={datePicker} ref={datePicker}
@ -123,6 +125,16 @@ export function RegisterPresets(props: {
<label for="create-date" class="absolute -top-2 left-2 text-xs bg-c-surface px-1">Fecha</label> <label for="create-date" class="absolute -top-2 left-2 text-xs bg-c-surface px-1">Fecha</label>
</div> </div>
</div> </div>
<div>
<input
oninput={(ev) => setIsPreview(ev.target.checked)}
checked={isPreview()}
type="checkbox"
name="is_preview"
id="preset-is-preview"
/>
<label class="px-2 py-2 select-none" for="preset-is-preview">Cert sin firmas</label>
</div>
<div> <div>
<input <input
class="bg-c-primary text-c-on-primary px-4 py-2 rounded-full cursor-pointer class="bg-c-primary text-c-on-primary px-4 py-2 rounded-full cursor-pointer

View File

@ -1,10 +1,11 @@
import { FilledCard } from "../../components/FilledCard"; import { FilledCard } from "../../components/FilledCard";
import { For, createSignal } from "solid-js"; import { For, Show, createSignal } from "solid-js";
import { XIcon } from "../../icons/XIcon"; import { XIcon } from "../../icons/XIcon";
import { allCourses } from "../../utils/allCourses"; import { allCourses } from "../../utils/allCourses";
import { RegisterBatchCreate } from "../../types/Register"; import { RegisterBatchCreate } from "../../types/Register";
import { RegistrationPreview } from "."; import { RegistrationPreview } from ".";
import { loadCustomLabels } from "../../utils/allCustomLabels"; import { loadCustomLabels } from "../../utils/allCustomLabels";
import { FileDashedIcon } from "../../icons/FileDashedIcon";
function isoDateToLocalDate(date: string): string { function isoDateToLocalDate(date: string): string {
@ -24,11 +25,12 @@ export function RegisterPreview(props: {selections: Array<RegistrationPreview>,
await wait(2000); await wait(2000);
const registers: RegisterBatchCreate = props.selections.map(({courseId, date, customLabel}) => ({ const registers: RegisterBatchCreate = props.selections.map(({courseId, date, customLabel, is_preview}) => ({
person_id: props.personId!, person_id: props.personId!,
course_id: courseId, course_id: courseId,
date, date,
custom_label: customLabel, custom_label: customLabel,
is_preview,
})); }));
const result = await createRegisters(registers); const result = await createRegisters(registers);
@ -50,12 +52,13 @@ export function RegisterPreview(props: {selections: Array<RegistrationPreview>,
<h2 class="p-4 font-bold text-xl">Confirmar registro</h2> <h2 class="p-4 font-bold text-xl">Confirmar registro</h2>
<div class="bg-c-surface p-4"> <div class="bg-c-surface p-4">
<For each={props.selections}> <For each={props.selections}>
{({courseId, date, customLabel}) => ( {({courseId, date, customLabel, is_preview}) => (
<Register <Register
courseId={courseId} courseId={courseId}
date={date} date={date}
onDelete={props.onDelete} onDelete={props.onDelete}
customLabel={customLabel} customLabel={customLabel}
isPreview={is_preview}
/> />
)} )}
</For> </For>
@ -74,23 +77,38 @@ export function RegisterPreview(props: {selections: Array<RegistrationPreview>,
); );
} }
function Register(props: {courseId: number, date: string, customLabel: string, onDelete: (v: number) => void}) { function Register(props: {
courseId: number,
date: string,
customLabel: string,
onDelete: (v: number) => void,
isPreview: boolean,
}) {
const courseName = () => { const courseName = () => {
const courses = allCourses(); const courses = allCourses();
return courses.find((course) => course.course_id === props.courseId)?.course_name ?? `Curso invalido! (${props.courseId})`; return courses.find((course) => course.course_id === props.courseId)?.course_name ?? `Curso invalido! (${props.courseId})`;
}; };
return ( return (
<div class="grid grid-cols-[auto_4rem_1.5rem] py-1 px-2 rounded-md border border-c-outline my-1"> <div class="grid grid-cols-[auto_3rem_1.5rem] py-1 px-2 rounded-md border border-c-outline my-1">
{courseName()} <span title={props.isPreview ? "Sin firmas" : ""}>
<span class="font-mono">{isoDateToLocalDate(props.date)}</span> <Show when={props.isPreview}>
<FileDashedIcon size={16} class="pr-2" fill="var(--c-on-surface)" />
</Show>
{courseName()}
</span>
<span class="font-mono">
{isoDateToLocalDate(props.date)}
</span>
<button <button
class="hover:bg-c-surface-variant rounded-md" class="hover:bg-c-surface-variant rounded-md"
onclick={() => props.onDelete(props.courseId)} onclick={() => props.onDelete(props.courseId)}
> >
<XIcon fill="var(--c-on-surface)" /> <XIcon fill="var(--c-on-surface)" />
</button> </button>
{props.customLabel} <span>
{props.customLabel}
</span>
</div> </div>
); );
} }

View File

@ -11,6 +11,7 @@ export type RegistrationPreview = {
courseId: number, courseId: number,
date: string, date: string,
customLabel: string, customLabel: string,
is_preview: boolean,
} }
export function NewRegister(props: { export function NewRegister(props: {
@ -32,7 +33,7 @@ export function NewRegister(props: {
<RegisterTabs active={active()} setActive={setActive} /> <RegisterTabs active={active()} setActive={setActive} />
<div class="bg-c-surface p-4 h-[22rem]"> <div class="bg-c-surface p-4 h-[23rem]">
<Show when={active() === "Presets"}> <Show when={active() === "Presets"}>
<RegisterPresets <RegisterPresets
onAdd={(v) => setSelections((x) => [...x, v])} onAdd={(v) => setSelections((x) => [...x, v])}

View File

@ -1,4 +1,4 @@
import { For, Show, createEffect, createMemo, createSignal } from "solid-js"; import { For, Match, Show, createEffect, createMemo, createSignal } from "solid-js";
import { Person } from "../../types/Person"; import { Person } from "../../types/Person";
import { Register } from "../../types/Register"; import { Register } from "../../types/Register";
import { courseMap } from "../../utils/allCourses"; import { courseMap } from "../../utils/allCourses";
@ -180,18 +180,6 @@ function RegisterEl(props: {register: Register, person: Person, onClick: () => v
return ( return (
<div class="grid grid-cols-[11rem_1.5rem_1.25rem] border border-c-outline rounded-md"> <div class="grid grid-cols-[11rem_1.5rem_1.25rem] border border-c-outline rounded-md">
{/*
<div class=" w-12 inline-block">
<Show when={generateable()}>
<button
onclick={genCert}
class="rounded-full bg-c-primary-container hover:bg-c-primary transition-colors h-12 w-12"
>
<DownloadIcon fill="var(--c-on-primary-container)" />
</button>
</Show>
</div>
*/}
<div class="p-1 border-r border-c-outline-50 align-middle"> <div class="p-1 border-r border-c-outline-50 align-middle">
<p <p
class="font-bold overflow-hidden whitespace-nowrap" class="font-bold overflow-hidden whitespace-nowrap"
@ -205,10 +193,21 @@ function RegisterEl(props: {register: Register, person: Person, onClick: () => v
</span> </span>
&nbsp; &nbsp;
</p> </p>
<p class="font-mono text-sm"> <p class="text-sm">
{displayDate()} <span class="font-mono">
&nbsp;-&nbsp; {displayDate()}
{props.register.register_code} </span>
<Show when={props.register.register_is_preview}>
<span class="text-c-error font-bold select-none">
&nbsp;sin firmas
</span>
</Show>
<Show when={!props.register.register_is_preview}>
<span class="font-mono">
&nbsp;- {props.register.register_code}
</span>
</Show>
</p> </p>
</div> </div>
<button <button

View File

@ -0,0 +1,15 @@
export function FileDashedIcon(props: {fill: string, size?: number, class?: string}) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
class={`inline-block w-6 ${props.class}`}
width={props.size ?? 32}
height={props.size ?? 32}
fill={props.fill}
viewBox="0 0 256 256"
>
<path d="M80,224a8,8,0,0,1-8,8H56a16,16,0,0,1-16-16V184a8,8,0,0,1,16,0v32H72A8,8,0,0,1,80,224ZM216,88v48a8,8,0,0,1-16,0V96H152a8,8,0,0,1-8-8V40H120a8,8,0,0,1,0-16h32a8,8,0,0,1,5.66,2.34l56,56A8,8,0,0,1,216,88Zm-56-8h28.69L160,51.31ZM80,24H56A16,16,0,0,0,40,40V64a8,8,0,0,0,16,0V40H80a8,8,0,0,0,0-16ZM208,168a8,8,0,0,0-8,8v40h-8a8,8,0,0,0,0,16h8a16,16,0,0,0,16-16V176A8,8,0,0,0,208,168ZM48,152a8,8,0,0,0,8-8V104a8,8,0,0,0-16,0v40A8,8,0,0,0,48,152Zm104,64H112a8,8,0,0,0,0,16h40a8,8,0,0,0,0-16Z" />
</svg>
);
}

View File

@ -9,6 +9,7 @@ export type RegisterBatchCreate = Array<{
* Value of the custom label * Value of the custom label
*/ */
custom_label: string, custom_label: string,
is_preview: boolean,
}> }>
export type Register = { export type Register = {