Support registering & displaying preview certs
This commit is contained in:
parent
6bd0acc03e
commit
e8e5835236
@ -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 {
|
||||||
|
@ -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
|
||||||
)
|
)
|
||||||
|
@ -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>
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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])}
|
||||||
|
@ -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>
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
<p class="font-mono text-sm">
|
<p class="text-sm">
|
||||||
{displayDate()}
|
<span class="font-mono">
|
||||||
-
|
{displayDate()}
|
||||||
{props.register.register_code}
|
</span>
|
||||||
|
|
||||||
|
<Show when={props.register.register_is_preview}>
|
||||||
|
<span class="text-c-error font-bold select-none">
|
||||||
|
sin firmas
|
||||||
|
</span>
|
||||||
|
</Show>
|
||||||
|
<Show when={!props.register.register_is_preview}>
|
||||||
|
<span class="font-mono">
|
||||||
|
- {props.register.register_code}
|
||||||
|
</span>
|
||||||
|
</Show>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
|
15
frontend/src/icons/FileDashedIcon.tsx
Normal file
15
frontend/src/icons/FileDashedIcon.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
@ -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 = {
|
||||||
|
Loading…
Reference in New Issue
Block a user