[Certs] FE & BE Mock for registers
This commit is contained in:
parent
fadbfbc7be
commit
81157d53de
@ -7,12 +7,19 @@ pub async fn get_all() -> Json<Vec<Course>> {
|
|||||||
// TODO: get from database
|
// TODO: get from database
|
||||||
let c = Course {
|
let c = Course {
|
||||||
course_id: 1,
|
course_id: 1,
|
||||||
course_code: 322,
|
|
||||||
course_name: "4x4".to_string(),
|
course_name: "4x4".to_string(),
|
||||||
course_display_name: "Manejo en 4x4 road danger".to_string(),
|
course_display_name: "Manejo en 4x4 road danger".to_string(),
|
||||||
course_days_amount: 2,
|
course_days_amount: 2,
|
||||||
course_has_custom_label: false,
|
course_has_custom_label: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
Json(vec![c.clone(), c])
|
let c2 = Course {
|
||||||
|
course_id: 2,
|
||||||
|
course_name: "Mec. Basica".to_string(),
|
||||||
|
course_display_name: "Mecánica Básica".to_string(),
|
||||||
|
course_days_amount: 2,
|
||||||
|
course_has_custom_label: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
Json(vec![c, c2])
|
||||||
}
|
}
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
pub mod course;
|
pub mod course;
|
||||||
pub mod person;
|
pub mod person;
|
||||||
|
pub mod register;
|
||||||
|
38
backend/src/controller/register/mod.rs
Normal file
38
backend/src/controller/register/mod.rs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
use rocket::{serde::json::Json, http::Status};
|
||||||
|
|
||||||
|
use crate::model::register::{RegisterCreate, Register};
|
||||||
|
|
||||||
|
#[options("/register/batch")]
|
||||||
|
pub fn options() -> Status {
|
||||||
|
Status::Ok
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/register/batch", format = "json", data = "<data>")]
|
||||||
|
pub async fn insert_all(data: Json<Vec<RegisterCreate>>) -> Status {
|
||||||
|
Status::Ok
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/register/<dni>")]
|
||||||
|
pub async fn get_by_dni(dni: i32) -> Json<Vec<Register>> {
|
||||||
|
let r1 = Register {
|
||||||
|
register_id: 1,
|
||||||
|
register_code: 322,
|
||||||
|
register_creation_date: "2021-01-01".to_string(),
|
||||||
|
register_display_date: "2021-01-01".to_string(),
|
||||||
|
register_custom_label: "".to_string(),
|
||||||
|
register_person_id: 1,
|
||||||
|
register_course_id: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
let r2 = Register {
|
||||||
|
register_id: 2,
|
||||||
|
register_code: 323,
|
||||||
|
register_creation_date: "2021-01-01".to_string(),
|
||||||
|
register_display_date: "2021-01-03".to_string(),
|
||||||
|
register_custom_label: "".to_string(),
|
||||||
|
register_person_id: 1,
|
||||||
|
register_course_id: 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
Json(vec![r1, r2])
|
||||||
|
}
|
@ -40,5 +40,8 @@ fn rocket() -> _ {
|
|||||||
.mount("/api", routes![
|
.mount("/api", routes![
|
||||||
controller::person::get_by_dni,
|
controller::person::get_by_dni,
|
||||||
controller::course::get_all,
|
controller::course::get_all,
|
||||||
|
controller::register::insert_all,
|
||||||
|
controller::register::options,
|
||||||
|
controller::register::get_by_dni,
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
@ -4,11 +4,6 @@ use rocket::serde::Serialize;
|
|||||||
#[serde(crate = "rocket::serde")]
|
#[serde(crate = "rocket::serde")]
|
||||||
pub struct Course {
|
pub struct Course {
|
||||||
pub course_id: i32,
|
pub course_id: i32,
|
||||||
/// Display code, shown on the certificate.
|
|
||||||
/// Currently a 4-digit number. After 9999, it will be a 4-digit hex number.
|
|
||||||
///
|
|
||||||
/// Example: `0322`
|
|
||||||
pub course_code: i32,
|
|
||||||
/// Internal name, shown in the admin panel
|
/// Internal name, shown in the admin panel
|
||||||
///
|
///
|
||||||
/// Example: `4x4`
|
/// Example: `4x4`
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
pub mod course;
|
pub mod course;
|
||||||
pub mod person;
|
pub mod person;
|
||||||
|
pub mod register;
|
||||||
|
36
backend/src/model/register.rs
Normal file
36
backend/src/model/register.rs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
use rocket::serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
/// Represents a single register send by the frontend
|
||||||
|
/// to create a new register in the database
|
||||||
|
pub struct RegisterCreate {
|
||||||
|
person_id: i32,
|
||||||
|
course_id: i32,
|
||||||
|
/// YYYY-MM-DD
|
||||||
|
date: String
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct Register {
|
||||||
|
pub register_id: i32,
|
||||||
|
/// Display code, shown on the certificate.
|
||||||
|
/// Currently a 4-digit number. After 9999, it will be a 4-digit hex number.
|
||||||
|
///
|
||||||
|
/// Example: `0322``
|
||||||
|
pub register_code: i32,
|
||||||
|
/// Date of creation of the register, in YYYY-MM-DD format
|
||||||
|
pub register_creation_date: String,
|
||||||
|
/// Date shown on the certificate, in YYYY-MM-DD format
|
||||||
|
pub register_display_date: String,
|
||||||
|
/// Some course's names can be extended with a label.
|
||||||
|
/// Used in machinery courses, where the course name is the type of machinery,
|
||||||
|
/// and the custom label the manufacturer and series (i.e. `320D CAT`).
|
||||||
|
pub register_custom_label: String,
|
||||||
|
/// Foreign key to the person table
|
||||||
|
pub register_person_id: i32,
|
||||||
|
/// Foreign key to the course table
|
||||||
|
pub register_course_id: i32,
|
||||||
|
}
|
2
frontend/.env.local
Normal file
2
frontend/.env.local
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
VITE_BACKEND_URL=http://localhost:8000
|
||||||
|
|
@ -7,7 +7,10 @@ const App: Component = () => (
|
|||||||
<div class="grid grid-cols-[5rem_auto]">
|
<div class="grid grid-cols-[5rem_auto]">
|
||||||
<NavRail />
|
<NavRail />
|
||||||
<Routes>
|
<Routes>
|
||||||
|
<Route path="/" component={() => <p>En construccion</p>} />
|
||||||
<Route path="/certs" component={Certs} />
|
<Route path="/certs" component={Certs} />
|
||||||
|
<Route path="/accesos" component={() => <p>En construccion</p>} />
|
||||||
|
<Route path="/escaneo" component={() => <p>En construccion</p>} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -67,7 +67,6 @@ export function ManualRegistration(props: {personId: number | null, onAdd: (v: [
|
|||||||
</div>
|
</div>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<input
|
<input
|
||||||
ref={datePicker}
|
|
||||||
id="create-date"
|
id="create-date"
|
||||||
class="bg-c-surface text-c-on-surface border border-c-outline rounded-lg p-2 font-mono w-full
|
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"
|
disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
@ -80,6 +79,9 @@ export function ManualRegistration(props: {personId: number | null, onAdd: (v: [
|
|||||||
</div>
|
</div>
|
||||||
</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
|
||||||
disabled:opacity-50 disabled:cursor-not-allowed"
|
disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
@ -87,14 +89,15 @@ export function ManualRegistration(props: {personId: number | null, onAdd: (v: [
|
|||||||
value="Agregar"
|
value="Agregar"
|
||||||
disabled={props.personId === null}
|
disabled={props.personId === null}
|
||||||
/>
|
/>
|
||||||
</form>
|
|
||||||
|
|
||||||
<p
|
<p
|
||||||
class="my-2 p-1 rounded w-fit mx-4 bg-c-error text-c-on-error"
|
class="my-2 p-1 w-fit mx-4 text-c-error inline-block"
|
||||||
style={{opacity: error() === "" ? "0" : "1", "user-select": "none"}}
|
// style={{opacity: error() === "" ? "0" : "1", "user-select": "none"}}
|
||||||
>
|
>
|
||||||
{error()}
|
{error()}
|
||||||
</p>
|
</p>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import { FilledCard } from "../../components/FilledCard";
|
|||||||
import { For } from "solid-js";
|
import { For } 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";
|
||||||
|
|
||||||
|
|
||||||
function isoDateToLocalDate(date: string): string {
|
function isoDateToLocalDate(date: string): string {
|
||||||
@ -13,20 +14,19 @@ export function RegisterPreview(props: {selections: Array<[number, string]>, per
|
|||||||
const submit = async() => {
|
const submit = async() => {
|
||||||
console.log("Submit...");
|
console.log("Submit...");
|
||||||
|
|
||||||
// TODO: Send all register requests at once
|
const registers: RegisterBatchCreate = props.selections.map(([courseId, date]) => ({
|
||||||
for (const [courseId, date] of props.selections) {
|
person_id: props.personId!,
|
||||||
const result = await defaultNewRegisterFn(
|
course_id: courseId,
|
||||||
props.personId ?? -1,
|
|
||||||
courseId,
|
|
||||||
date,
|
date,
|
||||||
);
|
}));
|
||||||
|
|
||||||
|
const result = await createRegisters(registers);
|
||||||
|
|
||||||
if (result === null) {
|
if (result === null) {
|
||||||
console.log("Success");
|
console.log("Success");
|
||||||
} else {
|
} else {
|
||||||
console.log(`error. ${result}`);
|
console.log(`error. ${result}`);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
props.onRegister();
|
props.onRegister();
|
||||||
};
|
};
|
||||||
@ -74,17 +74,13 @@ function Register(props: {courseId: number, date: string, onDelete: (v: number)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async function defaultNewRegisterFn(personId: number, subjectId: number, date: string): Promise<null | string> {
|
async function createRegisters(data: RegisterBatchCreate): Promise<null | string> {
|
||||||
const response = await fetch("/certificate", {
|
const response = await fetch(`${import.meta.env.VITE_BACKEND_URL}/api/register/batch`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify(data),
|
||||||
personId,
|
|
||||||
subjectId,
|
|
||||||
date,
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
import { DownloadIcon } from "../icons/DownloadIcon";
|
|
||||||
|
|
||||||
export function Registers() {
|
|
||||||
return (
|
|
||||||
<div class="m-4 p-4 rounded-md">
|
|
||||||
<h3 class="text-xl font-medium py-2">
|
|
||||||
SUMA BERNAL, MAIKOL
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
<div class="flex flex-wrap justify-start gap-2">
|
|
||||||
<div class="inline-block w-[12rem] p-1 border border-c-outline rounded-md">
|
|
||||||
<button 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>
|
|
||||||
<div class="inline-block h-12 w-32 pl-2 align-middle">
|
|
||||||
<p class="font-bold">Matpel 2</p>
|
|
||||||
<p class="font-mono text-sm">12/08/2023 - 6486</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
31
frontend/src/certs/Registers/index.tsx
Normal file
31
frontend/src/certs/Registers/index.tsx
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { DownloadIcon } from "../../icons/DownloadIcon";
|
||||||
|
import { Register } from "../../types/Register";
|
||||||
|
|
||||||
|
export function Registers() {
|
||||||
|
return (
|
||||||
|
<div class="m-4 p-4 rounded-md">
|
||||||
|
<h3 class="text-xl font-medium py-2">
|
||||||
|
SUMA BERNAL, MAIKOL
|
||||||
|
</h3>
|
||||||
|
<div class="flex flex-wrap justify-start gap-2">
|
||||||
|
<RegisterEl />
|
||||||
|
<RegisterEl />
|
||||||
|
<RegisterEl />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function RegisterEl(props: {register: Register}) {
|
||||||
|
return (
|
||||||
|
<div class="inline-block w-[12rem] p-1 border border-c-outline rounded-md">
|
||||||
|
<button 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>
|
||||||
|
<div class="inline-block h-12 w-32 pl-2 align-middle">
|
||||||
|
<p class="font-bold">Matpel 2</p>
|
||||||
|
<p class="font-mono text-sm">12/08/2023 - 6486</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
29
frontend/src/types/Register.ts
Normal file
29
frontend/src/types/Register.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
export type RegisterBatchCreate = Array<{
|
||||||
|
person_id: number,
|
||||||
|
course_id: number,
|
||||||
|
/**
|
||||||
|
* YYYY-MM-DD
|
||||||
|
*/
|
||||||
|
date: string,
|
||||||
|
}>
|
||||||
|
|
||||||
|
export type Register = {
|
||||||
|
register_id: number,
|
||||||
|
/// Display code, shown on the certificate.
|
||||||
|
/// Currently a 4-digit number. After 9999, it will be a 4-digit hex number.
|
||||||
|
///
|
||||||
|
/// Example: `0322``
|
||||||
|
register_code: number,
|
||||||
|
/// Date of creation of the register, in YYYY-MM-DD format
|
||||||
|
register_creation_date: string,
|
||||||
|
/// Date shown on the certificate, in YYYY-MM-DD format
|
||||||
|
register_display_date: string,
|
||||||
|
/// Some course's names can be extended with a label.
|
||||||
|
/// Used in machinery courses, where the course name is the type of machinery,
|
||||||
|
/// and the custom label the manufacturer and series (i.e. `320D CAT`).
|
||||||
|
register_custom_label: string,
|
||||||
|
/// Foreign key to the person table
|
||||||
|
register_person_id: number,
|
||||||
|
/// Foreign key to the course table
|
||||||
|
register_course_id: number,
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user