[FE][Certs] Fixes #22: Improve styles for the register list
This commit is contained in:
parent
791f946538
commit
12093b88c3
@ -16,7 +16,7 @@ export function RegisterPresets(props: {
|
|||||||
const [error, setError] = createSignal("");
|
const [error, setError] = createSignal("");
|
||||||
const courses = useCourses();
|
const courses = useCourses();
|
||||||
|
|
||||||
const presets = createMemo<{[key: string]: Array<Course>}>(() => {
|
const presets = createMemo<{ [key: string]: Array<Course> }>(() => {
|
||||||
if (courses === undefined) {
|
if (courses === undefined) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@ -43,10 +43,10 @@ export function RegisterPresets(props: {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
const data: {[key: string]: Array<Course>} = {
|
const data: { [key: string]: Array<Course> } = {
|
||||||
"2 Matpel": [matpel2, matpel1],
|
"2 Matpel": [matpel2, matpel1],
|
||||||
"3 Matpel": [matpel3,matpel2, matpel1],
|
"3 Matpel": [matpel3, matpel2, matpel1],
|
||||||
"4 Escolta": [escolta, matpel3,matpel2, matpel1],
|
"4 Escolta": [escolta, matpel3, matpel2, matpel1],
|
||||||
"MD, 2 Matpel": [matpel2, matpel1, manejo],
|
"MD, 2 Matpel": [matpel2, matpel1, manejo],
|
||||||
"3 4x4": [cuatroXcuatro, mecanica, manejo],
|
"3 4x4": [cuatroXcuatro, mecanica, manejo],
|
||||||
"2 4x4": [cuatroXcuatro, manejo],
|
"2 4x4": [cuatroXcuatro, manejo],
|
||||||
|
@ -3,31 +3,31 @@ import { Person } from "../../types/Person";
|
|||||||
import { Register } from "../../types/Register";
|
import { Register } from "../../types/Register";
|
||||||
import { RegisterSidebar } from "./RegisterSidebar";
|
import { RegisterSidebar } from "./RegisterSidebar";
|
||||||
import { DownloadSimpleIcon } from "../../icons/DownloadSimpleIcon";
|
import { DownloadSimpleIcon } from "../../icons/DownloadSimpleIcon";
|
||||||
import { backend, wait } from "../../utils/functions";
|
import { backend } from "../../utils/functions";
|
||||||
import { JsonResult } from "../../types/JsonResult";
|
import { JsonResult } from "../../types/JsonResult";
|
||||||
import { LoadingIcon } from "../../icons/LoadingIcon";
|
import { LoadingIcon } from "../../icons/LoadingIcon";
|
||||||
import { RegisterElement, generateCert } from "./RegisterElement";
|
import { RegisterElement, generateCert } from "./RegisterElement";
|
||||||
import { useCourses } from "../CourseContext";
|
import { useCourses } from "../CourseContext";
|
||||||
import { useCustomLabel } from "../CustomLabelContext";
|
import { useCustomLabel } from "../CustomLabelContext";
|
||||||
|
import { TrayIcon } from "../../icons/TrayIcon";
|
||||||
|
import { CaretDownIcon } from "../../icons/CaretDownIcon";
|
||||||
|
import { CaretUpIcon } from "../../icons/CaretUpIcon";
|
||||||
|
|
||||||
export function Registers(props: {person: Person | null, count: number}) {
|
export function Registers(props: {person: Person | null, count: number}) {
|
||||||
const [registers, setRegisters] = createSignal<Array<Register>>([]);
|
const [registers, setRegisters] = createSignal<Array<Register>>([]);
|
||||||
const [showHidden, setShowHidden] = createSignal(false);
|
|
||||||
const [sidebarRegister, setSidebarRegister] = createSignal<Register | null>(null);
|
const [sidebarRegister, setSidebarRegister] = createSignal<Register | null>(null);
|
||||||
const [loading, setLoading] = createSignal(false);
|
const [loading, setLoading] = createSignal(false);
|
||||||
const [error, setError] = createSignal("");
|
const [error, setError] = createSignal("");
|
||||||
const courses = useCourses();
|
const courses = useCourses();
|
||||||
const customLabels = useCustomLabel();
|
const customLabels = useCustomLabel();
|
||||||
|
|
||||||
const loadRegisters = async() => {
|
const loadRegisters = () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError("");
|
setError("");
|
||||||
|
|
||||||
const person = props.person!;
|
const person = props.person!;
|
||||||
setRegisters([]);
|
setRegisters([]);
|
||||||
|
|
||||||
if (import.meta.env.DEV) await wait(1500);
|
|
||||||
|
|
||||||
backend.get<JsonResult<Array<Register>>>(`/api/register/${person.person_dni}`)
|
backend.get<JsonResult<Array<Register>>>(`/api/register/${person.person_dni}`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
@ -56,25 +56,20 @@ export function Registers(props: {person: Person | null, count: number}) {
|
|||||||
loadRegisters();
|
loadRegisters();
|
||||||
});
|
});
|
||||||
|
|
||||||
// All registers sorted by year, and an indicator of whether there's register older
|
const registersReady = createMemo(() => !loading() && props.person !== null);
|
||||||
// than last year
|
|
||||||
const registerSortedList = createMemo<[Array<Array<Register>>, boolean]>(() => {
|
// Returns a map with years & their registers
|
||||||
|
const registerSortedList = createMemo<{[year: number]: Array<Register>}>(() => {
|
||||||
|
if (loading() || props.person === null) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
const allRegisters = registers();
|
const allRegisters = registers();
|
||||||
|
|
||||||
const registerByYear: {[y: string]: Array<Register>} = {};
|
const registerByYear: {[y: string]: Array<Register>} = {};
|
||||||
|
|
||||||
let olderThanLastYear = false;
|
|
||||||
allRegisters.forEach((register) => {
|
allRegisters.forEach((register) => {
|
||||||
const year = new Date(register.register_display_date).getFullYear();
|
const year = new Date(register.register_display_date).getFullYear();
|
||||||
const currentYear = new Date().getFullYear();
|
|
||||||
|
|
||||||
// Don't show registers from before last year
|
|
||||||
if (year < (currentYear - 1)) {
|
|
||||||
olderThanLastYear = true;
|
|
||||||
if (!showHidden()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (registerByYear[year] === undefined) {
|
if (registerByYear[year] === undefined) {
|
||||||
registerByYear[year] = [];
|
registerByYear[year] = [];
|
||||||
@ -83,23 +78,28 @@ export function Registers(props: {person: Person | null, count: number}) {
|
|||||||
registerByYear[year].push(register);
|
registerByYear[year].push(register);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create a new array with each year's registers
|
// Make sure that there's a key for the current year
|
||||||
// Sort the object keys (years) in descending order
|
const currentYear = new Date().getFullYear();
|
||||||
const sortedYears = Object.keys(registerByYear).sort((y1, y2) => (y1 < y2 ? 1 : -1));
|
if (registerByYear[currentYear] === undefined) {
|
||||||
|
registerByYear[currentYear] = [];
|
||||||
|
}
|
||||||
|
|
||||||
// Create the final array
|
// Sort the registers by date
|
||||||
const result = sortedYears.map((year) => registerByYear[year].sort((r1, r2) => (r1.register_display_date < r2.register_display_date ? 1 : -1)));
|
Object.entries(registerByYear).forEach(([, registers]) => {
|
||||||
|
registers.sort((r1, r2) => (r1.register_display_date < r2.register_display_date ? 1 : -1));
|
||||||
return [result, olderThanLastYear];
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const inputCheck = () => setShowHidden((x) => !x);
|
return registerByYear;
|
||||||
|
});
|
||||||
|
|
||||||
const todayRegisters = createMemo(() => {
|
const todayRegisters = createMemo(() => {
|
||||||
const today = new Date().toISOString()
|
const today = new Date().toISOString()
|
||||||
.split("T")[0];
|
.split("T")[0];
|
||||||
return registerSortedList()[0][0]
|
|
||||||
?.filter((r) => r.register_creation_date === today) ?? [];
|
return Object.entries(registerSortedList())
|
||||||
|
.map(([,registers]) => registers)
|
||||||
|
.flat()
|
||||||
|
.filter((r) => r.register_creation_date === today);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -120,48 +120,36 @@ export function Registers(props: {person: Person | null, count: number}) {
|
|||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="m-4 p-4 rounded-md relative">
|
<div class="p-2 rounded-md relative">
|
||||||
<h3 class="text-xl font-medium">
|
<Show when={registersReady()}>
|
||||||
|
<h3 class="text-xl font-medium mb-5">
|
||||||
<button
|
<button
|
||||||
class={`${downButtonBg()} inline-block mr-2 rounded-full transition-colors
|
class={`${downButtonBg()} inline-block mr-2 rounded-full transition-colors
|
||||||
disabled:opacity-25 disabled:cursor-not-allowed px-2`}
|
disabled:opacity-25 disabled:cursor-not-allowed px-2 hover:underline`}
|
||||||
disabled={todayRegisters().length === 0}
|
disabled={todayRegisters().length === 0}
|
||||||
title="Descargar todos los cert. creados hoy"
|
title="Descargar todos los cert. creados hoy"
|
||||||
onclick={downloadTodayCerts}
|
onclick={downloadTodayCerts}
|
||||||
>
|
>
|
||||||
<DownloadSimpleIcon fill="var(--c-on-primary)" size={24} />
|
<DownloadSimpleIcon fill="var(--c-on-primary)" size={24} />
|
||||||
<span class="text-sm">
|
<span class="text-sm">
|
||||||
hoy
|
Descargar los cert. creados hoy
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
{props.person?.person_names}
|
|
||||||
{props.person?.person_paternal_surname}
|
|
||||||
{props.person?.person_maternal_surname}
|
|
||||||
|
|
||||||
<Show when={registerSortedList()[1]}>
|
· Certificados registrados
|
||||||
|
|
|
||||||
<input id={`person_${props.person?.person_id ?? "_"}`} type="checkbox" class="ml-4 mr-2" checked oninput={inputCheck} />
|
|
||||||
<label for={`person_${props.person?.person_id ?? "_"}`} class="text-sm">
|
|
||||||
Ocultar cert. anteriores a {new Date().getFullYear() - 1}
|
|
||||||
</label>
|
|
||||||
</Show>
|
|
||||||
</h3>
|
</h3>
|
||||||
|
</Show>
|
||||||
|
|
||||||
<For each={registerSortedList()[0]}>
|
<For each={Object.entries(registerSortedList()).sort(([year1], [year2]) => (year1 < year2 ? 1 : -1))}>
|
||||||
{(year) => (
|
{([year,yearRegisters]) => (
|
||||||
<div class="flex flex-wrap justify-start gap-2 my-6">
|
<YearRegisters
|
||||||
<For each={year}>
|
year={parseInt(year, 10)}
|
||||||
{(register) => (
|
registers={yearRegisters}
|
||||||
<RegisterElement
|
|
||||||
register={register}
|
|
||||||
person={props.person!}
|
person={props.person!}
|
||||||
onClick={() => setSidebarRegister(register)}
|
setSidebarRegister={setSidebarRegister}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</For>
|
|
||||||
|
|
||||||
<Show when={loading()}>
|
<Show when={loading()}>
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
@ -183,3 +171,62 @@ export function Registers(props: {person: Person | null, count: number}) {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function YearRegisters(props: {
|
||||||
|
year: number,
|
||||||
|
registers: Array<Register>,
|
||||||
|
person: Person,
|
||||||
|
setSidebarRegister: (register: Register) => void,
|
||||||
|
}) {
|
||||||
|
const [collapsed, setCollapsed] = createSignal(props.year < new Date().getFullYear() - 1);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
class={`rounded-lg mb-8 hover:shadow-md ${collapsed() ? "border border-c-outline" : ""}`}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="p-2 rounded-lg bg-c-surface-variant text-c-on-surface-variant font-bold
|
||||||
|
grid grid-cols-[auto_2rem] w-full text-left hover:underline"
|
||||||
|
onclick={() => setCollapsed((x) => !x)}
|
||||||
|
>
|
||||||
|
{props.year}
|
||||||
|
<Show when={collapsed()}>
|
||||||
|
<CaretDownIcon size={24} fill="var(--c-outline)" />
|
||||||
|
</Show>
|
||||||
|
<Show when={!collapsed()}>
|
||||||
|
<CaretUpIcon size={24} fill="var(--c-outline)" />
|
||||||
|
</Show>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class={`${collapsed() ? "hidden" : ""} ${props.year === 2023 ? "min-h-[11rem]" : ""}`}
|
||||||
|
>
|
||||||
|
|
||||||
|
<Show when={props.registers.length > 0}>
|
||||||
|
<div
|
||||||
|
class={"flex flex-wrap justify-start gap-2 p-2 "}
|
||||||
|
>
|
||||||
|
<For each={props.registers}>
|
||||||
|
{(register) => (
|
||||||
|
<RegisterElement
|
||||||
|
register={register}
|
||||||
|
person={props.person}
|
||||||
|
onClick={() => props.setSidebarRegister(register)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
<Show when={props.registers.length === 0}>
|
||||||
|
<div class="text-center">
|
||||||
|
<div class="text-center p-10">
|
||||||
|
<TrayIcon class="scale-[300%]" fill="var(--c-outline)" />
|
||||||
|
</div>
|
||||||
|
<p>No hay certificados registrados en {props.year}.</p>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
14
frontend/src/icons/CaretDownIcon.tsx
Normal file
14
frontend/src/icons/CaretDownIcon.tsx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
export function CaretDownIcon(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="M213.66,101.66l-80,80a8,8,0,0,1-11.32,0l-80-80A8,8,0,0,1,53.66,90.34L128,164.69l74.34-74.35a8,8,0,0,1,11.32,11.32Z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
14
frontend/src/icons/CaretUpIcon.tsx
Normal file
14
frontend/src/icons/CaretUpIcon.tsx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
export function CaretUpIcon(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="M213.66,165.66a8,8,0,0,1-11.32,0L128,91.31,53.66,165.66a8,8,0,0,1-11.32-11.32l80-80a8,8,0,0,1,11.32,0l80,80A8,8,0,0,1,213.66,165.66Z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
15
frontend/src/icons/TrayIcon.tsx
Normal file
15
frontend/src/icons/TrayIcon.tsx
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
export function TrayIcon(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="M208,32H48A16,16,0,0,0,32,48V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32Zm0,16V152h-28.7A15.86,15.86,0,0,0,168,156.69L148.69,176H107.31L88,156.69A15.86,15.86,0,0,0,76.69,152H48V48Zm0,160H48V168H76.69L96,187.31A15.86,15.86,0,0,0,107.31,192h41.38A15.86,15.86,0,0,0,160,187.31L179.31,168H208v40Z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user