[FE] UI for online classroom user

This commit is contained in:
Araozu 2023-09-26 16:13:30 -05:00
parent 62e7b1a29c
commit abd9f789d3
12 changed files with 233 additions and 49 deletions

View File

@ -10,7 +10,8 @@ CREATE TABLE person (
person_dni VARCHAR(8) NOT NULL,
person_names VARCHAR(50) NOT NULL,
person_paternal_surname VARCHAR(30) NOT NULL,
person_maternal_surname VARCHAR(30) NOT NULL
person_maternal_surname VARCHAR(30) NOT NULL,
person_classroom_id INTEGER DEFAULT -1
);
CREATE TABLE course (

View File

@ -0,0 +1,31 @@
import { createSignal } from "solid-js";
import { FilledButton } from "../components/FilledButton";
import { UserPlusIcon } from "../icons/UserPlusIcon";
import { MaterialInput } from "../components/MaterialInput";
export function ClassroomUserCreation() {
const [email, setEmail] = createSignal("yuli.palo.apaza@gmail.com");
const [loading, setLoading] = createSignal(false);
return (
<div class="px-4 pb-4">
<p class="py-2">
O cree un nuevo usuario:
</p>
<form>
<MaterialInput
resourceName="Correo"
value={email()}
setValue={setEmail}
disabled={loading()}
/>
</form>
<FilledButton class="ml-2">
<UserPlusIcon fill="var(--c-on-primary)" class="mr-2 relative scale-150" size={16} />
Crear usuario
</FilledButton>
</div>
);
}

View File

@ -34,7 +34,7 @@ export function ClassroomConection() {
<Switch fallback={"Desconectado"}>
<Match when={status() === "connected"}>
<span class="bg-c-success text-c-on-success px-2 rounded select-none font-mono">
Conectado
Ok
</span>
</Match>
<Match when={status() === "connecting"}>

View File

@ -1,7 +1,11 @@
import { Show, createSignal } from "solid-js";
import { Search } from "../certs/Search";
import { Person } from "../types/Person";
import { ClassroomConection } from "./ConnectionStatus";
import { FilledCard } from "../components/FilledCard";
import { LinkIcon } from "../icons/LinkIcon";
import { ClassroomUserCreation } from "./ClassroomUserCreation";
type TabType = "Vinculate" | "Create";
export function OnlineClassroom() {
const [person, setPerson] = createSignal<Person | null>(null);
@ -10,10 +14,9 @@ export function OnlineClassroom() {
<div class="grid grid-cols-[16rem_25rem_1fr]">
<Search setPerson={setPerson} />
<div>
<ClassroomConection />
<Show when={person()}>
<ClassroomUsers person={person()!} />
</Show>
<ClassroomUser person={person()!} />
</div>
</div>
);
@ -21,12 +24,96 @@ export function OnlineClassroom() {
function ClassroomUsers(props: {person: Person}) {
function ClassroomUser(props: {person: Person}) {
const [active, setActive] = createSignal<TabType>("Vinculate");
return (
<div>
<h2 class="text-xl">
Usuarios para {props.person.person_names}:
</h2>
<FilledCard class="border border-c-outline overflow-hidden">
<h2 class="p-3 font-bold text-xl">
Persona no vinculada:
</h2>
<ClassroomTabs active={active()} setActive={setActive} />
<div class="bg-c-surface">
<Show when={active() === "Vinculate"}>
<ClassroomVinculation />
</Show>
<Show when={active() === "Create"}>
<ClassroomUserCreation />
</Show>
</div>
</FilledCard>
</div>
);
}
function ClassroomVinculation() {
return (
<div>
<p class="py-2 px-4">
Vincule un usuario existente:
</p>
<ClassroomSingleUser />
<ClassroomSingleUser />
<ClassroomSingleUser />
</div>
);
}
function ClassroomSingleUser() {
return (
<div
class="hover:bg-c-surface-variant hover:text-c-on-surface-variant transition-colors
grid grid-cols-[auto_3rem] gap-4"
>
<div class="pl-4 py-2">
NOMBRE NOMBRE APELLIDO APELLIDO
<br />
<div class="grid grid-cols-[auto_10rem]">
<span class="font-mono">
NNAPELLA
</span>
<div>
registrado:&nbsp;
<span class="font-mono">
26/05/2023
</span>
</div>
</div>
</div>
<button
title="Vincular usuario"
class="border-2 border-c-transparent hover:border-c-primary transition-colors rounded"
>
<LinkIcon fill="var(--c-primary)" />
</button>
</div>
);
}
function ClassroomTabs(props: {active: TabType, setActive: (v: TabType) => void}) {
const presetsClasses = () => ((props.active === "Vinculate") ? "font-bold border-c-primary" : "border-c-transparent");
const manualClasses = () => ((props.active === "Create") ? "font-bold border-c-primary" : "border-c-transparent");
return (
<div class="grid grid-cols-2">
<button
class={`py-2 border-b-4 ${presetsClasses()}`}
onclick={() => props.setActive("Vinculate")}
>
Vincular
</button>
<button
class={`py-2 border-b-4 ${manualClasses()}`}
onclick={() => props.setActive("Create")}
>
Crear
</button>
</div>
);
}

View File

@ -1,4 +1,5 @@
import { createSignal } from "solid-js";
import { MaterialInput } from "../../components/MaterialInput";
export function PersonRegister(props: {dni: string, onRegister: () => void}) {
const [paternalSurname, setPaternalSurname] = createSignal("");
@ -48,22 +49,22 @@ export function PersonRegister(props: {dni: string, onRegister: () => void}) {
}}
>
<MaterialInput
name="Apellido paterno"
resourceName="Apellido paterno"
value={paternalSurname()}
setValue={(v) => setPaternalSurname(v.toUpperCase())}
loading={loading()}
disabled={loading()}
/>
<MaterialInput
name="Apellido materno"
resourceName="Apellido materno"
value={maternalSurname()}
setValue={(v) => setMaternalSurname(v.toUpperCase())}
loading={loading()}
disabled={loading()}
/>
<MaterialInput
name="Nombres"
resourceName="Nombres"
value={names()}
setValue={(v) => setNames(v.toUpperCase())}
loading={loading()}
disabled={loading()}
/>
<button
@ -77,35 +78,4 @@ export function PersonRegister(props: {dni: string, onRegister: () => void}) {
);
}
function MaterialInput(props: {
name: string,
loading: boolean,
value: string,
setValue: (v: string) => void,
}) {
let inputElement: HTMLInputElement | undefined;
return (
<div class="relative my-6">
<input
ref={inputElement}
id="search-dni"
class="bg-c-background text-c-on-background border-c-outline border-2 rounded px-2 py-1 w-full
invalid:border-c-error invalid:text-c-error
focus:border-c-primary outline-none font-mono
disabled:opacity-50 disabled:cursor-not-allowed"
type="text"
placeholder={props.name}
value={props.value}
required
onInput={(e) => props.setValue(e.target.value)}
disabled={props.loading}
/>
<label for="search-dni" class="absolute -top-2 left-2 text-xs bg-c-surface px-1 select-none">
{props.name}
</label>
</div>
);
}

View File

@ -24,7 +24,7 @@ export function Dialog(props: {onClose: () => void, children: Children, class?:
});
return (
<div class={`modal transition-colors ${positionClasses()}`}>
<div class={`modal transition-colors bg-c-background ${positionClasses()}`}>
{props.children}
</div>
);

View File

@ -0,0 +1,16 @@
import { JSX } from "solid-js";
export function FilledButton(props: {
children?: Array<JSX.Element> | JSX.Element,
class?: string,
type?: "button" | "input",
}) {
return (
<button
class={`bg-c-primary text-c-on-primary px-4 py-2 rounded-full cursor-pointer mt-4
disabled:opacity-50 disabled:cursor-not-allowed ${props.class}`}
>
{props.children}
</button>
);
}

View File

@ -0,0 +1,32 @@
export function MaterialInput(props: {
resourceName: string,
disabled: boolean,
value: string,
setValue: (v: string) => void,
}) {
let inputElement: HTMLInputElement | undefined;
return (
<div class="relative my-6">
<input
ref={inputElement}
id="search-dni"
class="bg-c-background text-c-on-background border-c-outline border-2 rounded px-2 py-1 w-full
invalid:border-c-error invalid:text-c-error
focus:border-c-primary outline-none font-mono
disabled:opacity-50 disabled:cursor-not-allowed"
type="text"
placeholder={props.resourceName}
value={props.value}
required
onInput={(e) => props.setValue(e.target.value)}
disabled={props.disabled}
/>
<label for="search-dni" class="absolute -top-2 left-2 text-xs bg-c-surface px-1 select-none">
{props.resourceName}
</label>
</div>
);
}

View File

@ -0,0 +1,16 @@
import { JSX } from "solid-js";
export function TonalButton(props: {
children?: Array<JSX.Element> | JSX.Element,
class?: string,
type?: "button" | "input",
}) {
return (
<button
class="bg-c-surface-variant text-c-on-surface-variant px-4 py-2 rounded-full cursor-pointer mt-4
disabled:opacity-50 disabled:cursor-not-allowed"
>
{props.children}
</button>
);
}

View File

@ -0,0 +1,15 @@
export function LinkIcon(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="M137.54,186.36a8,8,0,0,1,0,11.31l-9.94,10A56,56,0,0,1,48.38,128.4L72.5,104.28A56,56,0,0,1,149.31,102a8,8,0,1,1-10.64,12,40,40,0,0,0-54.85,1.63L59.7,139.72a40,40,0,0,0,56.58,56.58l9.94-9.94A8,8,0,0,1,137.54,186.36Zm70.08-138a56.08,56.08,0,0,0-79.22,0l-9.94,9.95a8,8,0,0,0,11.32,11.31l9.94-9.94a40,40,0,0,1,56.58,56.58L172.18,140.4A40,40,0,0,1,117.33,142,8,8,0,1,0,106.69,154a56,56,0,0,0,76.81-2.26l24.12-24.12A56.08,56.08,0,0,0,207.62,48.38Z" />
</svg>
);
}

View File

@ -0,0 +1,15 @@
export function UserPlusIcon(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="M256,136a8,8,0,0,1-8,8H232v16a8,8,0,0,1-16,0V144H200a8,8,0,0,1,0-16h16V112a8,8,0,0,1,16,0v16h16A8,8,0,0,1,256,136Zm-57.87,58.85a8,8,0,0,1-12.26,10.3C165.75,181.19,138.09,168,108,168s-57.75,13.19-77.87,37.15a8,8,0,0,1-12.25-10.3c14.94-17.78,33.52-30.41,54.17-37.17a68,68,0,1,1,71.9,0C164.6,164.44,183.18,177.07,198.13,194.85ZM108,152a52,52,0,1,0-52-52A52.06,52.06,0,0,0,108,152Z" />
</svg>
);
}

View File

@ -5,5 +5,6 @@ export type Person = {
person_names: string
person_paternal_surname: string
person_maternal_surname: string
person_classroom_id: string | undefined
}