Lots of refactoring. More, smaller files.
This commit is contained in:
parent
36e0ec3ad1
commit
9dafd7504b
13
src/main.rs
13
src/main.rs
|
|
@ -68,6 +68,11 @@ async fn login_home() -> Redirect {
|
||||||
Redirect::to(uri!(auth::oauth_login()))
|
Redirect::to(uri!(auth::oauth_login()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[get("/favicon.ico")]
|
||||||
|
async fn favicon() -> Redirect {
|
||||||
|
Redirect::to(uri!("/img/favicon.ico"))
|
||||||
|
}
|
||||||
|
|
||||||
#[launch]
|
#[launch]
|
||||||
async fn rocket() -> _ {
|
async fn rocket() -> _ {
|
||||||
dotenv().ok();
|
dotenv().ok();
|
||||||
|
|
@ -80,12 +85,14 @@ async fn rocket() -> _ {
|
||||||
.attach(Template::fairing())
|
.attach(Template::fairing())
|
||||||
.attach(Db::fairing())
|
.attach(Db::fairing())
|
||||||
.attach(OAuth2::<auth::RanderathIdentity>::fairing("oauth"))
|
.attach(OAuth2::<auth::RanderathIdentity>::fairing("oauth"))
|
||||||
.mount("/", routes![auth::oauth_login, auth::oauth_callback, home, login_home])
|
.mount("/", routes![auth::oauth_login, auth::oauth_callback, home, login_home, favicon])
|
||||||
.mount("/table", routes![table::table, table::table_sec, table::edit_tname, table::create, table::import_table, table::delete_table])
|
.mount("/table", routes![table::table, table::table_sec, table::edit_tname, table::create_table, table::import_table, table::delete_table])
|
||||||
.mount("/row", routes![table::new_entry, table::edit_entry, table::delete_entry])
|
.mount("/row", routes![table::new_entry, table::edit_entry, table::delete_entry])
|
||||||
.mount("/column", routes![table::delete_column, table::edit_column])
|
.mount("/column", routes![table::delete_column, table::edit_column])
|
||||||
.register("/", catchers![auth::redirect_to_login])
|
.register("/", catchers![auth::redirect_to_login])
|
||||||
.mount("/static", FileServer::from(relative!("static")))
|
.mount("/img", FileServer::from(relative!("static/img")))
|
||||||
|
.mount("/css", FileServer::from(relative!("static/css")))
|
||||||
|
.mount("/js", FileServer::from(relative!("static/js")))
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
298
src/table.rs
298
src/table.rs
|
|
@ -1,4 +1,5 @@
|
||||||
/* Simple web app using rocket to help maintain inventory data.
|
/* This file is part of inventur.
|
||||||
|
* inventur is a simple web app using rocket to help maintain inventory data.
|
||||||
* Copyright (C) 2024 Johannes Randerath
|
* Copyright (C) 2024 Johannes Randerath
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -15,290 +16,15 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::auth;
|
pub mod table_view;
|
||||||
|
pub mod table_manipulate_table;
|
||||||
|
pub mod table_manipulate_column;
|
||||||
|
pub mod table_manipulate_entry;
|
||||||
|
pub mod forms;
|
||||||
|
|
||||||
use auth::AuthUser;
|
pub use self::table_view::*;
|
||||||
use crate::Db;
|
pub use self::table_manipulate_table::*;
|
||||||
|
pub use self::table_manipulate_entry::*;
|
||||||
|
pub use self::table_manipulate_column::*;
|
||||||
|
pub use self::forms::*;
|
||||||
|
|
||||||
use rocket_dyn_templates::{Template, context};
|
|
||||||
use rocket::form::Form;
|
|
||||||
use rocket::response::Redirect;
|
|
||||||
use inventur_db;
|
|
||||||
use rocket::serde::{Serialize, Deserialize, json::Json};
|
|
||||||
use rocket::http::{Status, Cookie, CookieJar, SameSite};
|
|
||||||
use inventur_db::FIELDTYPE;
|
|
||||||
|
|
||||||
#[derive(FromForm)]
|
|
||||||
struct DeleteColumn {
|
|
||||||
tblid: i32,
|
|
||||||
id_in_table: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(FromForm)]
|
|
||||||
struct EditColumn {
|
|
||||||
tblid: i32,
|
|
||||||
id_in_table: i32,
|
|
||||||
name: String,
|
|
||||||
column_type: isize,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(FromForm)]
|
|
||||||
struct NewColumn {
|
|
||||||
tblid: i32,
|
|
||||||
name: String,
|
|
||||||
column_type: isize,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(FromForm)]
|
|
||||||
struct NewEntry {
|
|
||||||
tblid: i32,
|
|
||||||
cells: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(FromForm)]
|
|
||||||
struct EditEntry {
|
|
||||||
tblid: i32,
|
|
||||||
cells: Vec<String>,
|
|
||||||
row_pos: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(FromForm)]
|
|
||||||
struct DeleteEntry {
|
|
||||||
tblid: i32,
|
|
||||||
row_pos: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(FromForm)]
|
|
||||||
struct EditTname {
|
|
||||||
tblid: i32,
|
|
||||||
new_name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(FromForm)]
|
|
||||||
struct NewTable {
|
|
||||||
name: String,
|
|
||||||
field_names: Vec<String>,
|
|
||||||
field_types: Vec<isize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
#[serde(crate = "rocket::serde")]
|
|
||||||
struct ImportTable {
|
|
||||||
name: String,
|
|
||||||
columns: Vec<String>,
|
|
||||||
rows: Vec<Vec<String>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(FromForm)]
|
|
||||||
struct DeleteTable {
|
|
||||||
tblid: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
|
||||||
#[serde(crate = "rocket::serde")]
|
|
||||||
struct JRows {
|
|
||||||
rows: Vec<Vec<String>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_tids(conn: &Db, uid: i32) -> (Vec<i32>, Vec<String>) {
|
|
||||||
let mut tids = conn.run(move |c| inventur_db::get_user_tblids(c, uid)).await;
|
|
||||||
let tnames;
|
|
||||||
if tids.is_none() {
|
|
||||||
tids = Some(Vec::new());
|
|
||||||
tnames = Some(Vec::new());
|
|
||||||
}else {
|
|
||||||
let tids = tids.clone().unwrap();
|
|
||||||
tnames = conn.run(move |c| inventur_db::get_tblnames(c, tids)).await;
|
|
||||||
}
|
|
||||||
(tids.unwrap(), tnames.unwrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/<tid>", rank=2)]
|
|
||||||
pub async fn table_sec(conn: Db, tid: i32, user: AuthUser) -> Redirect {
|
|
||||||
let nus : Option<usize> = None;
|
|
||||||
let nu8 : Option<u8> = None;
|
|
||||||
let nvi32 : Option<Vec<i32>> = None;
|
|
||||||
let ns : Option<String> = None;
|
|
||||||
Redirect::to(uri!("/table", table(tid, nu8, nus, nvi32, ns)))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/<tid>?<sort_dir>&<sort_field>&<search_fields>&<search_value>")]
|
|
||||||
pub async fn table(conn: Db, tid: i32, sort_dir: Option<u8>, sort_field: Option<usize>, search_fields: Option<Vec<i32>>, search_value: Option<String>, user: AuthUser) -> Option<Template> {
|
|
||||||
let uid = user.uid;
|
|
||||||
let table = conn.run(move |c| inventur_db::get_table(c, tid, uid)).await;
|
|
||||||
if table.is_none() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let mut table = table.unwrap();
|
|
||||||
let searchvalue;
|
|
||||||
let searchfields;
|
|
||||||
if search_value.is_some() && !search_value.clone().unwrap().eq_ignore_ascii_case("") && search_fields.is_some() {
|
|
||||||
searchvalue = search_value.unwrap();
|
|
||||||
searchfields = search_fields.unwrap();
|
|
||||||
table = inventur_db::search_table(table, searchfields.clone(), searchvalue.clone());
|
|
||||||
}else {
|
|
||||||
if search_value.is_none() {
|
|
||||||
searchvalue = String::new();
|
|
||||||
} else {
|
|
||||||
searchvalue = search_value.unwrap();
|
|
||||||
}
|
|
||||||
if search_fields.is_none() {
|
|
||||||
searchfields = (0i32..(table.column_names.len() as i32)).collect();
|
|
||||||
|
|
||||||
} else {
|
|
||||||
searchfields = search_fields.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let sortdir;
|
|
||||||
if sort_dir.is_none() || sort_dir.unwrap() > 1 {
|
|
||||||
sortdir = 0;
|
|
||||||
}else {
|
|
||||||
sortdir = sort_dir.unwrap();
|
|
||||||
}
|
|
||||||
let sortfield;
|
|
||||||
if sort_field.is_none() {
|
|
||||||
sortfield = 0;
|
|
||||||
}else {
|
|
||||||
sortfield = sort_field.unwrap();
|
|
||||||
}
|
|
||||||
let table = inventur_db::sort_table(table, sortfield, sortdir);
|
|
||||||
|
|
||||||
let tname = table.name;
|
|
||||||
let column_names = table.column_names;
|
|
||||||
let column_types = table.column_types.iter().map(|x: &inventur_db::FIELDTYPE| *x as i32).collect::<Vec<i32>>();
|
|
||||||
let rows : Vec<Vec<String>>= table.rows.iter().map(|v| {let mut r = v.cells.clone(); r.insert(0, v.row_pos.to_string()); r}).collect();
|
|
||||||
let (tids, tnames) = get_tids(&conn, uid).await;
|
|
||||||
|
|
||||||
Some(
|
|
||||||
Template::render("table",
|
|
||||||
context!{
|
|
||||||
search_value: searchvalue,
|
|
||||||
search_fields: searchfields,
|
|
||||||
sort_field: sortfield,
|
|
||||||
sort_dir: sortdir,
|
|
||||||
tblid: tid,
|
|
||||||
tblname: tname,
|
|
||||||
tids: tids,
|
|
||||||
tnames: tnames,
|
|
||||||
column_names: column_names,
|
|
||||||
column_types: column_types,
|
|
||||||
rows: rows,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[post("/delete", data="<data>")]
|
|
||||||
pub async fn delete_table(conn: Db, data: Form<DeleteTable>, user: AuthUser) -> Result<Redirect, Status> {
|
|
||||||
let uid = user.uid;
|
|
||||||
let tblid = data.tblid;
|
|
||||||
if conn.run(move |c| inventur_db::delete_table(c, tblid, uid)).await.is_none() {
|
|
||||||
return Err(Status::Forbidden);
|
|
||||||
}
|
|
||||||
Ok(Redirect::to(uri!("/")))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[post("/new", data="<data>")]
|
|
||||||
pub async fn new_entry(conn: Db, data: Form<NewEntry>, user: AuthUser) -> Result<Redirect, Status> {
|
|
||||||
let uid = user.uid;
|
|
||||||
let cells = data.cells.clone();
|
|
||||||
let tblid = data.tblid;
|
|
||||||
if conn.run(move |c| inventur_db::add_row(c, data.tblid, cells, uid)).await.is_none() {
|
|
||||||
return Err(Status::Forbidden);
|
|
||||||
}
|
|
||||||
Ok(Redirect::to(uri!("/table", table_sec(tblid))))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[post("/edit", data="<data>")]
|
|
||||||
pub async fn edit_entry(conn: Db, data: Form<EditEntry>, user: AuthUser) -> Result<Redirect, Status> {
|
|
||||||
let uid = user.uid;
|
|
||||||
let cells = data.cells.clone();
|
|
||||||
let tblid = data.tblid;
|
|
||||||
let row_pos = data.row_pos;
|
|
||||||
if conn.run(move |c| inventur_db::edit_row(c, tblid, cells, row_pos, uid)).await.is_none() {
|
|
||||||
return Err(Status::Forbidden);
|
|
||||||
}
|
|
||||||
Ok(Redirect::to(uri!("/table", table_sec(tblid))))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[post("/delete", data="<data>")]
|
|
||||||
pub async fn delete_entry(conn: Db, data: Form<DeleteEntry>, user: AuthUser) -> Result<Redirect, Status> {
|
|
||||||
let uid = user.uid;
|
|
||||||
let tblid = data.tblid;
|
|
||||||
let row_pos = data.row_pos;
|
|
||||||
if conn.run(move |c| inventur_db::delete_row(c, tblid, row_pos, uid)).await.is_none() {
|
|
||||||
return Err(Status::Forbidden);
|
|
||||||
}
|
|
||||||
Ok(Redirect::to(uri!("/table", table_sec(tblid))))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[post("/create", data="<data>")]
|
|
||||||
pub async fn create_column(conn: Db, data: Form<NewColumn>, user: AuthUser) -> Result<Redirect, Status> {
|
|
||||||
let uid = user.uid;
|
|
||||||
let tblid = data.tblid;
|
|
||||||
let coltype = FIELDTYPE::from(data.column_type);
|
|
||||||
if conn.run(move |c| inventur_db::add_column(c, tblid, data.name.clone(), coltype, uid)).await.is_none() {
|
|
||||||
return Err(Status::Forbidden);
|
|
||||||
}
|
|
||||||
Ok(Redirect::to(uri!("/table", table_sec(tblid))))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[post("/edit", data="<data>")]
|
|
||||||
pub async fn edit_column(conn: Db, data: Form<EditColumn>, user: AuthUser) -> Result<Redirect, Status> {
|
|
||||||
let uid = user.uid;
|
|
||||||
let tblid = data.tblid;
|
|
||||||
let idintbl = data.id_in_table;
|
|
||||||
let clmtype = FIELDTYPE::from(data.column_type);
|
|
||||||
if conn.run(move |c| inventur_db::edit_column(c, tblid, idintbl, &data.name, clmtype, uid)).await.is_none() {
|
|
||||||
return Err(Status::Forbidden);
|
|
||||||
}
|
|
||||||
Ok(Redirect::to(uri!("/table", table_sec(tblid))))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[post("/delete", data="<data>")]
|
|
||||||
pub async fn delete_column(conn: Db, data: Form<DeleteColumn>, user: AuthUser) -> Result<Redirect, Status> {
|
|
||||||
let uid = user.uid;
|
|
||||||
let tblid = data.tblid;
|
|
||||||
let idintbl = data.id_in_table;
|
|
||||||
if conn.run(move |c| inventur_db::delete_column(c, tblid, idintbl, uid)).await.is_none() {
|
|
||||||
return Err(Status::Forbidden);
|
|
||||||
}
|
|
||||||
Ok(Redirect::to(uri!("/table", table_sec(tblid))))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[post("/name/edit", data="<data>")]
|
|
||||||
pub async fn edit_tname(conn: Db, data: Form<EditTname>, user: AuthUser) -> Result<Redirect, Status> {
|
|
||||||
let uid = user.uid;
|
|
||||||
let tblid = data.tblid;
|
|
||||||
if conn.run(move |c| inventur_db::rename_table(c, data.tblid, data.new_name.clone(), uid)).await.is_none() {
|
|
||||||
return Err(Status::Forbidden);
|
|
||||||
}
|
|
||||||
Ok(Redirect::to(uri!("/table", table_sec(tblid))))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[post("/create", data="<data>")]
|
|
||||||
pub async fn create(conn: Db, data: Form<NewTable>, user: AuthUser) -> Result<Redirect, Status> {
|
|
||||||
let uid = user.uid;
|
|
||||||
let tblid = conn.run(move |c| inventur_db::create_table(c, data.name.clone(), data.field_names.clone(), data.field_types.iter().map(|x: &isize| FIELDTYPE::from(*x)).collect::<Vec<FIELDTYPE>>(), uid)).await;
|
|
||||||
if tblid.is_none() {
|
|
||||||
return Err(Status::Forbidden);
|
|
||||||
}
|
|
||||||
Ok(Redirect::to(uri!("/table", table_sec(tblid.unwrap()))))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[post("/import", data="<data>")]
|
|
||||||
pub async fn import_table(conn: Db, data: Json<ImportTable>, user: AuthUser) -> Result<Redirect, Status> {
|
|
||||||
let uid = user.uid;
|
|
||||||
let columns = data.columns.clone();
|
|
||||||
let rows = data.rows.clone();
|
|
||||||
let tblid = conn.run(move |c| inventur_db::create_table(c, data.name.clone(), columns.clone(), vec![FIELDTYPE::TEXT; columns.len()], uid)).await;
|
|
||||||
if tblid.is_none() {
|
|
||||||
return Err(Status::UnprocessableEntity);
|
|
||||||
}
|
|
||||||
let tblid = tblid.unwrap();
|
|
||||||
for row in rows {
|
|
||||||
conn.run(move |c| inventur_db::add_row(c, tblid, row, uid)).await;
|
|
||||||
}
|
|
||||||
Ok(Redirect::to(uri!("/table", table_sec(tblid))))
|
|
||||||
}
|
|
||||||
|
|
|
||||||
92
src/table/forms.rs
Normal file
92
src/table/forms.rs
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
/* This file is part of inventur.
|
||||||
|
* inventur is a simple web app using rocket to help maintain inventory data.
|
||||||
|
* Copyright (C) 2024 Johannes Randerath
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use rocket::form::Form;
|
||||||
|
use rocket::serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
|
#[derive(FromForm)]
|
||||||
|
pub struct DeleteColumn {
|
||||||
|
pub tblid: i32,
|
||||||
|
pub id_in_table: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(FromForm)]
|
||||||
|
pub struct EditColumn {
|
||||||
|
pub tblid: i32,
|
||||||
|
pub id_in_table: i32,
|
||||||
|
pub name: String,
|
||||||
|
pub column_type: isize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(FromForm)]
|
||||||
|
pub struct NewColumn {
|
||||||
|
pub tblid: i32,
|
||||||
|
pub name: String,
|
||||||
|
pub column_type: isize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(FromForm)]
|
||||||
|
pub struct NewEntry {
|
||||||
|
pub tblid: i32,
|
||||||
|
pub cells: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(FromForm)]
|
||||||
|
pub struct EditEntry {
|
||||||
|
pub tblid: i32,
|
||||||
|
pub cells: Vec<String>,
|
||||||
|
pub row_pos: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(FromForm)]
|
||||||
|
pub struct DeleteEntry {
|
||||||
|
pub tblid: i32,
|
||||||
|
pub row_pos: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(FromForm)]
|
||||||
|
pub struct EditTname {
|
||||||
|
pub tblid: i32,
|
||||||
|
pub new_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(FromForm)]
|
||||||
|
pub struct NewTable {
|
||||||
|
pub name: String,
|
||||||
|
pub field_names: Vec<String>,
|
||||||
|
pub field_types: Vec<isize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct ImportTable {
|
||||||
|
pub name: String,
|
||||||
|
pub columns: Vec<String>,
|
||||||
|
pub rows: Vec<Vec<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(FromForm)]
|
||||||
|
pub struct DeleteTable {
|
||||||
|
pub tblid: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct JRows {
|
||||||
|
pub rows: Vec<Vec<String>>,
|
||||||
|
}
|
||||||
69
src/table/table_manipulate_column.rs
Normal file
69
src/table/table_manipulate_column.rs
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
/* This file is part of inventur.
|
||||||
|
* inventur is a simple web app using rocket to help maintain inventory data.
|
||||||
|
* Copyright (C) 2024 Johannes Randerath
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use crate::auth;
|
||||||
|
use crate::Db;
|
||||||
|
use crate::table::forms;
|
||||||
|
use crate::table::table_view;
|
||||||
|
|
||||||
|
use inventur_db;
|
||||||
|
|
||||||
|
use rocket::form::Form;
|
||||||
|
use rocket::response::Redirect;
|
||||||
|
use rocket::http::Status;
|
||||||
|
|
||||||
|
use inventur_db::FIELDTYPE;
|
||||||
|
use auth::AuthUser;
|
||||||
|
|
||||||
|
use self::forms::{NewColumn, EditColumn, DeleteColumn};
|
||||||
|
use self::table_view::rocket_uri_macro_table_sec;
|
||||||
|
|
||||||
|
#[post("/create", data="<data>")]
|
||||||
|
pub async fn create_column(conn: Db, data: Form<NewColumn>, user: AuthUser) -> Result<Redirect, Status> {
|
||||||
|
let uid = user.uid;
|
||||||
|
let tblid = data.tblid;
|
||||||
|
let coltype = FIELDTYPE::from(data.column_type);
|
||||||
|
if conn.run(move |c| inventur_db::add_column(c, tblid, data.name.clone(), coltype, uid)).await.is_none() {
|
||||||
|
return Err(Status::Forbidden);
|
||||||
|
}
|
||||||
|
Ok(Redirect::to(uri!("/table", table_sec(tblid))))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/edit", data="<data>")]
|
||||||
|
pub async fn edit_column(conn: Db, data: Form<EditColumn>, user: AuthUser) -> Result<Redirect, Status> {
|
||||||
|
let uid = user.uid;
|
||||||
|
let tblid = data.tblid;
|
||||||
|
let idintbl = data.id_in_table;
|
||||||
|
let clmtype = FIELDTYPE::from(data.column_type);
|
||||||
|
if conn.run(move |c| inventur_db::edit_column(c, tblid, idintbl, &data.name, clmtype, uid)).await.is_none() {
|
||||||
|
return Err(Status::Forbidden);
|
||||||
|
}
|
||||||
|
Ok(Redirect::to(uri!("/table", table_sec(tblid))))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/delete", data="<data>")]
|
||||||
|
pub async fn delete_column(conn: Db, data: Form<DeleteColumn>, user: AuthUser) -> Result<Redirect, Status> {
|
||||||
|
let uid = user.uid;
|
||||||
|
let tblid = data.tblid;
|
||||||
|
let idintbl = data.id_in_table;
|
||||||
|
if conn.run(move |c| inventur_db::delete_column(c, tblid, idintbl, uid)).await.is_none() {
|
||||||
|
return Err(Status::Forbidden);
|
||||||
|
}
|
||||||
|
Ok(Redirect::to(uri!("/table", table_sec(tblid))))
|
||||||
|
}
|
||||||
|
|
||||||
67
src/table/table_manipulate_entry.rs
Normal file
67
src/table/table_manipulate_entry.rs
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
/* This file is part of inventur.
|
||||||
|
* inventur is a simple web app using rocket to help maintain inventory data.
|
||||||
|
* Copyright (C) 2024 Johannes Randerath
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use crate::auth;
|
||||||
|
use crate::Db;
|
||||||
|
use crate::table::forms;
|
||||||
|
use crate::table::table_view;
|
||||||
|
|
||||||
|
use inventur_db;
|
||||||
|
|
||||||
|
use rocket::form::Form;
|
||||||
|
use rocket::response::Redirect;
|
||||||
|
use rocket::http::Status;
|
||||||
|
|
||||||
|
use auth::AuthUser;
|
||||||
|
|
||||||
|
use self::forms::{NewEntry, EditEntry, DeleteEntry};
|
||||||
|
use self::table_view::rocket_uri_macro_table_sec;
|
||||||
|
|
||||||
|
#[post("/new", data="<data>")]
|
||||||
|
pub async fn new_entry(conn: Db, data: Form<NewEntry>, user: AuthUser) -> Result<Redirect, Status> {
|
||||||
|
let uid = user.uid;
|
||||||
|
let cells = data.cells.clone();
|
||||||
|
let tblid = data.tblid;
|
||||||
|
if conn.run(move |c| inventur_db::add_row(c, data.tblid, cells, uid)).await.is_none() {
|
||||||
|
return Err(Status::Forbidden);
|
||||||
|
}
|
||||||
|
Ok(Redirect::to(uri!("/table", table_sec(tblid))))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/edit", data="<data>")]
|
||||||
|
pub async fn edit_entry(conn: Db, data: Form<EditEntry>, user: AuthUser) -> Result<Redirect, Status> {
|
||||||
|
let uid = user.uid;
|
||||||
|
let cells = data.cells.clone();
|
||||||
|
let tblid = data.tblid;
|
||||||
|
let row_pos = data.row_pos;
|
||||||
|
if conn.run(move |c| inventur_db::edit_row(c, tblid, cells, row_pos, uid)).await.is_none() {
|
||||||
|
return Err(Status::Forbidden);
|
||||||
|
}
|
||||||
|
Ok(Redirect::to(uri!("/table", table_sec(tblid))))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/delete", data="<data>")]
|
||||||
|
pub async fn delete_entry(conn: Db, data: Form<DeleteEntry>, user: AuthUser) -> Result<Redirect, Status> {
|
||||||
|
let uid = user.uid;
|
||||||
|
let tblid = data.tblid;
|
||||||
|
let row_pos = data.row_pos;
|
||||||
|
if conn.run(move |c| inventur_db::delete_row(c, tblid, row_pos, uid)).await.is_none() {
|
||||||
|
return Err(Status::Forbidden);
|
||||||
|
}
|
||||||
|
Ok(Redirect::to(uri!("/table", table_sec(tblid))))
|
||||||
|
}
|
||||||
83
src/table/table_manipulate_table.rs
Normal file
83
src/table/table_manipulate_table.rs
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
/* This file is part of inventur.
|
||||||
|
* inventur is a simple web app using rocket to help maintain inventory data.
|
||||||
|
* Copyright (C) 2024 Johannes Randerath
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use crate::auth;
|
||||||
|
use crate::Db;
|
||||||
|
use crate::table::forms;
|
||||||
|
use crate::table::table_view;
|
||||||
|
|
||||||
|
use inventur_db;
|
||||||
|
|
||||||
|
use auth::AuthUser;
|
||||||
|
use rocket::form::Form;
|
||||||
|
use rocket::response::Redirect;
|
||||||
|
use rocket::http::Status;
|
||||||
|
use rocket::serde::json::Json;
|
||||||
|
|
||||||
|
use inventur_db::FIELDTYPE;
|
||||||
|
|
||||||
|
use self::forms::{NewTable, DeleteTable, ImportTable, EditTname};
|
||||||
|
use self::table_view::rocket_uri_macro_table_sec;
|
||||||
|
|
||||||
|
#[post("/delete", data="<data>")]
|
||||||
|
pub async fn delete_table(conn: Db, data: Form<DeleteTable>, user: AuthUser) -> Result<Redirect, Status> {
|
||||||
|
let uid = user.uid;
|
||||||
|
let tblid = data.tblid;
|
||||||
|
if conn.run(move |c| inventur_db::delete_table(c, tblid, uid)).await.is_none() {
|
||||||
|
return Err(Status::Forbidden);
|
||||||
|
}
|
||||||
|
Ok(Redirect::to(uri!("/")))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/create", data="<data>")]
|
||||||
|
pub async fn create_table(conn: Db, data: Form<NewTable>, user: AuthUser) -> Result<Redirect, Status> {
|
||||||
|
let uid = user.uid;
|
||||||
|
let tblid = conn.run(move |c| inventur_db::create_table(c, data.name.clone(), data.field_names.clone(), data.field_types.iter().map(|x: &isize| FIELDTYPE::from(*x)).collect::<Vec<FIELDTYPE>>(), uid)).await;
|
||||||
|
if tblid.is_none() {
|
||||||
|
return Err(Status::Forbidden);
|
||||||
|
}
|
||||||
|
Ok(Redirect::to(uri!("/table", table_sec(tblid.unwrap()))))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/import", data="<data>")]
|
||||||
|
pub async fn import_table(conn: Db, data: Json<ImportTable>, user: AuthUser) -> Result<Redirect, Status> {
|
||||||
|
let uid = user.uid;
|
||||||
|
let columns = data.columns.clone();
|
||||||
|
let rows = data.rows.clone();
|
||||||
|
let tblid = conn.run(move |c| inventur_db::create_table(c, data.name.clone(), columns.clone(), vec![FIELDTYPE::TEXT; columns.len()], uid)).await;
|
||||||
|
if tblid.is_none() {
|
||||||
|
return Err(Status::UnprocessableEntity);
|
||||||
|
}
|
||||||
|
let tblid = tblid.unwrap();
|
||||||
|
for row in rows {
|
||||||
|
conn.run(move |c| inventur_db::add_row(c, tblid, row, uid)).await;
|
||||||
|
}
|
||||||
|
Ok(Redirect::to(uri!("/table", table_sec(tblid))))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/name/edit", data="<data>")]
|
||||||
|
pub async fn edit_tname(conn: Db, data: Form<EditTname>, user: AuthUser) -> Result<Redirect, Status> {
|
||||||
|
let uid = user.uid;
|
||||||
|
let tblid = data.tblid;
|
||||||
|
if conn.run(move |c| inventur_db::rename_table(c, data.tblid, data.new_name.clone(), uid)).await.is_none() {
|
||||||
|
return Err(Status::Forbidden);
|
||||||
|
}
|
||||||
|
Ok(Redirect::to(uri!("/table", table_sec(tblid))))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
115
src/table/table_view.rs
Normal file
115
src/table/table_view.rs
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
/* This file is part of inventur.
|
||||||
|
* inventur is a simple web app using rocket to help maintain inventory data.
|
||||||
|
* Copyright (C) 2024 Johannes Randerath
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use crate::auth;
|
||||||
|
use crate::Db;
|
||||||
|
use inventur_db;
|
||||||
|
|
||||||
|
use auth::AuthUser;
|
||||||
|
use rocket_dyn_templates::{Template, context};
|
||||||
|
use rocket::response::Redirect;
|
||||||
|
|
||||||
|
#[get("/<tid>?<sort_dir>&<sort_field>&<search_fields>&<search_value>")]
|
||||||
|
pub async fn table(conn: Db, tid: i32, sort_dir: Option<u8>, sort_field: Option<usize>, search_fields: Option<Vec<i32>>, search_value: Option<String>, user: AuthUser) -> Option<Template> {
|
||||||
|
let uid = user.uid;
|
||||||
|
let table = conn.run(move |c| inventur_db::get_table(c, tid, uid)).await;
|
||||||
|
if table.is_none() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let mut table = table.unwrap();
|
||||||
|
let searchvalue;
|
||||||
|
let searchfields;
|
||||||
|
if search_value.is_some() && !search_value.clone().unwrap().eq_ignore_ascii_case("") && search_fields.is_some() {
|
||||||
|
searchvalue = search_value.unwrap();
|
||||||
|
searchfields = search_fields.unwrap();
|
||||||
|
table = inventur_db::search_table(table, searchfields.clone(), searchvalue.clone());
|
||||||
|
}else {
|
||||||
|
if search_value.is_none() {
|
||||||
|
searchvalue = String::new();
|
||||||
|
} else {
|
||||||
|
searchvalue = search_value.unwrap();
|
||||||
|
}
|
||||||
|
if search_fields.is_none() {
|
||||||
|
searchfields = (0i32..(table.column_names.len() as i32)).collect();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
searchfields = search_fields.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let sortdir;
|
||||||
|
if sort_dir.is_none() || sort_dir.unwrap() > 1 {
|
||||||
|
sortdir = 0;
|
||||||
|
}else {
|
||||||
|
sortdir = sort_dir.unwrap();
|
||||||
|
}
|
||||||
|
let sortfield;
|
||||||
|
if sort_field.is_none() {
|
||||||
|
sortfield = 0;
|
||||||
|
}else {
|
||||||
|
sortfield = sort_field.unwrap();
|
||||||
|
}
|
||||||
|
let table = inventur_db::sort_table(table, sortfield, sortdir);
|
||||||
|
|
||||||
|
let tname = table.name;
|
||||||
|
let column_names = table.column_names;
|
||||||
|
let column_types = table.column_types.iter().map(|x: &inventur_db::FIELDTYPE| *x as i32).collect::<Vec<i32>>();
|
||||||
|
let rows : Vec<Vec<String>>= table.rows.iter().map(|v| {let mut r = v.cells.clone(); r.insert(0, v.row_pos.to_string()); r}).collect();
|
||||||
|
let (tids, tnames) = get_tids(&conn, uid).await;
|
||||||
|
|
||||||
|
Some(
|
||||||
|
Template::render("table",
|
||||||
|
context!{
|
||||||
|
search_value: searchvalue,
|
||||||
|
search_fields: searchfields,
|
||||||
|
sort_field: sortfield,
|
||||||
|
sort_dir: sortdir,
|
||||||
|
tblid: tid,
|
||||||
|
tblname: tname,
|
||||||
|
tids: tids,
|
||||||
|
tnames: tnames,
|
||||||
|
column_names: column_names,
|
||||||
|
column_types: column_types,
|
||||||
|
rows: rows,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/<tid>", rank=2)]
|
||||||
|
pub async fn table_sec(conn: Db, tid: i32, user: AuthUser) -> Redirect {
|
||||||
|
let nus : Option<usize> = None;
|
||||||
|
let nu8 : Option<u8> = None;
|
||||||
|
let nvi32 : Option<Vec<i32>> = None;
|
||||||
|
let ns : Option<String> = None;
|
||||||
|
Redirect::to(uri!("/table", table(tid, nu8, nus, nvi32, ns)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_tids(conn: &Db, uid: i32) -> (Vec<i32>, Vec<String>) {
|
||||||
|
let mut tids = conn.run(move |c| inventur_db::get_user_tblids(c, uid)).await;
|
||||||
|
let tnames;
|
||||||
|
if tids.is_none() {
|
||||||
|
tids = Some(Vec::new());
|
||||||
|
tnames = Some(Vec::new());
|
||||||
|
}else {
|
||||||
|
let tids = tids.clone().unwrap();
|
||||||
|
tnames = conn.run(move |c| inventur_db::get_tblnames(c, tids)).await;
|
||||||
|
}
|
||||||
|
(tids.unwrap(), tnames.unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
102
static/js/base.js
Normal file
102
static/js/base.js
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
/* This file is part of inventur.
|
||||||
|
* inventur is a simple web app using rocket to help maintain inventory data.
|
||||||
|
* Copyright (C) 2024 Johannes Randerath
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
let ncol = 1;
|
||||||
|
let todelete = "";
|
||||||
|
function create_table() {
|
||||||
|
let form = document.getElementById('form_create_table');
|
||||||
|
form.submit();
|
||||||
|
form.reset();
|
||||||
|
document.getElementById('dynamic_columns').innerHTML = "";
|
||||||
|
ncol = 1;
|
||||||
|
}
|
||||||
|
function set_modal_target(modal_target) {
|
||||||
|
document.getElementById('modal_caller').innerHTML = modal_target;
|
||||||
|
}
|
||||||
|
function start_ajax(path, listener) {
|
||||||
|
let req = new XMLHttpRequest();
|
||||||
|
req.open('GET', path, true);
|
||||||
|
req.send();
|
||||||
|
req.onReadyStateChange = function() {
|
||||||
|
if (req.readyState == XMLHttpRequest.DONE) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
function import_table() {
|
||||||
|
console.log("1");
|
||||||
|
Papa.parse(document.getElementById('import_file').files[0], {
|
||||||
|
complete: function process(result) {
|
||||||
|
console.log("2");
|
||||||
|
let csv = result.data;
|
||||||
|
let req = new XMLHttpRequest();
|
||||||
|
req.open("POST", "/table/import", true);
|
||||||
|
req.responseType = 'blob';
|
||||||
|
req.setRequestHeader('Content-Type', 'application/json');
|
||||||
|
let cols = csv[0];
|
||||||
|
let rows = csv.slice(1);
|
||||||
|
if (rows[rows.length - 1].length != cols.length) {
|
||||||
|
rows = rows.slice(0, rows.length-1);
|
||||||
|
}
|
||||||
|
req.send(JSON.stringify({
|
||||||
|
name: document.getElementById('import_name').value,
|
||||||
|
columns: cols,
|
||||||
|
rows: rows,
|
||||||
|
}));
|
||||||
|
req.onload = function(resp) {
|
||||||
|
if (this.readyState == 4) {
|
||||||
|
window.location.href = req.responseURL;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
function add_column() {
|
||||||
|
ncol += 1;
|
||||||
|
document.getElementById('dynamic_columns').innerHTML += `
|
||||||
|
<div class='row mt-3' id='columns'>
|
||||||
|
<div class='col-auto align-bottom'>
|
||||||
|
<div class='form-text me-1' id='field_index'>
|
||||||
|
<strong>${ncol}</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class='col-auto'>
|
||||||
|
<input type='text' class='form-control' name='field_names' id='field_name' aria-describedby='field_name_label'>
|
||||||
|
</div>
|
||||||
|
<div class='col-auto'>
|
||||||
|
<select class='form-select' aria-label='data type' name='field_types'>
|
||||||
|
<option value='0' selected>Text</option>
|
||||||
|
<option value='1'>Number</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
};
|
||||||
|
function confirm_delete(form) {
|
||||||
|
document.getElementById(`form_edit_${form}`).reset();
|
||||||
|
$('#confirm_delete_modal').modal('show');
|
||||||
|
document.getElementById('confirm_delete_modal_entity').innerHTML = form;
|
||||||
|
document.getElementById('confirm_delete_modal_button').onclick = function () {
|
||||||
|
document.getElementById(`form_delete_${form}`).submit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function submit_and_reset(form) {
|
||||||
|
let f = document.getElementById(form);
|
||||||
|
f.submit();
|
||||||
|
f.reset();
|
||||||
|
}
|
||||||
85
static/js/table.js
Normal file
85
static/js/table.js
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
/* This file is part of inventur.
|
||||||
|
* inventur is a simple web app using rocket to help maintain inventory data.
|
||||||
|
* Copyright (C) 2024 Johannes Randerath
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
let edit_mode = false;
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", (event) => {
|
||||||
|
let rows = document.getElementById("content_table").getElementsByTagName('tbody')[0].rows;
|
||||||
|
for (let i = 0; i < rows.length; i++)
|
||||||
|
{
|
||||||
|
let row = rows[i];
|
||||||
|
let createClickHandler = function(r)
|
||||||
|
{
|
||||||
|
return function()
|
||||||
|
{
|
||||||
|
$('#edit_entry_modal').modal('show');
|
||||||
|
document.getElementById('modal_caller').innerHTML = i+1;
|
||||||
|
document.getElementById('form_edit_entry_rowpos').value = i+1;
|
||||||
|
document.getElementById('form_delete_entry_rowpos').value = i+1;
|
||||||
|
let cells = row.cells;
|
||||||
|
for (let j = 1; j < cells.length; j++) {
|
||||||
|
document.getElementById(`form_edit_entry_${j}`).value = cells[j].innerHTML;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
row.onclick = createClickHandler(row);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function toggle_edit_tname() {
|
||||||
|
let span = document.getElementById('tname');
|
||||||
|
if (!edit_mode) {
|
||||||
|
span.innerHTML = `
|
||||||
|
<div class='row'>
|
||||||
|
<div class='col-auto ml-1 p-2'>
|
||||||
|
<form id='form_edit_table' action='/table/name/edit' method='post'>
|
||||||
|
<input name='tblid' value='${tblid}' hidden />
|
||||||
|
<input type='text' class='form-control' id='tname_edit' name='new_name' value='${tblname}' />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class='col-auto mt-0 pt-0 pr-1'>
|
||||||
|
<div class="btn-group" role="group">
|
||||||
|
<button class="btn btn-secondary" onclick="toggle_edit_tname();" type="button"><i class="bi bi-x"></i></button>
|
||||||
|
<button class='btn btn-success' type='button' onclick='edit_tname()'>
|
||||||
|
<i class='bi bi-check'></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<button class='btn btn-danger' type="button" onclick='confirm_delete("table");'><i class="bi bi-trash3-fill"></i></button>
|
||||||
|
</div>
|
||||||
|
<form id="form_delete_table" action="/table/delete" method="post">
|
||||||
|
<input value="${tblid}" name="tblid" hidden />
|
||||||
|
</form>`;
|
||||||
|
edit_mode = true;
|
||||||
|
document.getElementById('pencil_button_edit_tname').hidden = true;
|
||||||
|
} else {
|
||||||
|
document.getElementById('pencil_button_edit_tname').hidden = false;
|
||||||
|
span.innerHTML = `${tblname}`;
|
||||||
|
edit_mode = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function edit_tname() {
|
||||||
|
document.getElementById('form_edit_table').submit();
|
||||||
|
toggle_edit_tname();
|
||||||
|
}
|
||||||
|
function edit_column(clmn_index) {
|
||||||
|
document.getElementById('form_edit_column_name').value = column_names[clmn_index];
|
||||||
|
document.getElementById('form_edit_column_type').value = column_types[clmn_index];
|
||||||
|
document.getElementById('modal_caller').innerHTML = column_names[clmn_index];
|
||||||
|
document.getElementById('form_delete_column_idintbl').value = clmn_index + 1;
|
||||||
|
document.getElementById('form_edit_column_idintbl').value = clmn_index + 1;
|
||||||
|
}
|
||||||
|
|
@ -17,96 +17,22 @@
|
||||||
-- You should have received a copy of the GNU Affero General Public License
|
-- You should have received a copy of the GNU Affero General Public License
|
||||||
-- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
-- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<html>
|
<html>
|
||||||
<head lang="en">
|
<head lang="en">
|
||||||
<meta name="author" content="Johannes Randerath" />
|
<meta name="author" content="Johannes Randerath" />
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
|
<!-- Bootstrap css -->
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
||||||
|
<!-- Bootstrap icons -->
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<!-- Papaparse for csv import -->
|
||||||
<script src="https://unpkg.com/papaparse@latest/papaparse.min.js"></script>
|
<script src="https://unpkg.com/papaparse@latest/papaparse.min.js"></script>
|
||||||
<title>Inventory - {% block pagename %}{% endblock pagename %}</title>
|
<title>Inventory - {% block pagename %}{% endblock pagename %}</title>
|
||||||
<script>
|
<script type="text/javascript" src="/js/base.js"></script>
|
||||||
let todelete = "";
|
|
||||||
function create_table() {
|
|
||||||
document.getElementById('create_table').submit().reset();
|
|
||||||
document.getElementById('dynamic_columns').innerHTML = "";
|
|
||||||
ncol = 1;
|
|
||||||
}
|
|
||||||
function set_modal_target(modal_target) {
|
|
||||||
document.getElementById('modal_caller').innerHTML = modal_target;
|
|
||||||
}
|
|
||||||
function start_ajax(path, listener) {
|
|
||||||
let req = new XMLHttpRequest();
|
|
||||||
req.open('GET', path, true);
|
|
||||||
req.send();
|
|
||||||
req.onReadyStateChange = function() {
|
|
||||||
if (req.readyState == XMLHttpRequest.DONE) {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
function import_table() {
|
|
||||||
console.log("1");
|
|
||||||
Papa.parse(document.getElementById('import_file').files[0], {
|
|
||||||
complete: function process(result) {
|
|
||||||
console.log("2");
|
|
||||||
let csv = result.data;
|
|
||||||
let req = new XMLHttpRequest();
|
|
||||||
req.open("POST", "/table/import", true);
|
|
||||||
req.responseType = 'blob';
|
|
||||||
req.setRequestHeader('Content-Type', 'application/json');
|
|
||||||
let cols = csv[0];
|
|
||||||
let rows = csv.slice(1);
|
|
||||||
if (rows[rows.length - 1].length != cols.length) {
|
|
||||||
rows = rows.slice(0, rows.length-1);
|
|
||||||
}
|
|
||||||
req.send(JSON.stringify({
|
|
||||||
name: document.getElementById('import_name').value,
|
|
||||||
columns: cols,
|
|
||||||
rows: rows,
|
|
||||||
}));
|
|
||||||
req.onload = function(resp) {
|
|
||||||
if (this.readyState == 4) {
|
|
||||||
window.location.href = req.responseURL;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
function add_column() {
|
|
||||||
ncol += 1;
|
|
||||||
document.getElementById('dynamic_columns').innerHTML += `
|
|
||||||
<div class='row mt-3' id='columns'>
|
|
||||||
<div class='col-auto align-bottom'>
|
|
||||||
<div class='form-text me-1' id='field_index'>
|
|
||||||
<strong>${ncol}</strong>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class='col-auto'>
|
|
||||||
<input type='text' class='form-control' name='field_names' id='field_name' aria-describedby='field_name_label'>
|
|
||||||
</div>
|
|
||||||
<div class='col-auto'>
|
|
||||||
<select class='form-select' aria-label='data type' name='field_types'>
|
|
||||||
<option value='0' selected>Text</option>
|
|
||||||
<option value='1'>Number</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>`;
|
|
||||||
};
|
|
||||||
function confirm_delete(form) {
|
|
||||||
document.getElementById(`form_edit_${form}`).reset();
|
|
||||||
$('#confirm_delete_modal').modal('show');
|
|
||||||
document.getElementById('confirm_delete_modal_entity').innerHTML = form;
|
|
||||||
document.getElementById('confirm_delete_modal_button').onclick = function () {
|
|
||||||
document.getElementById(`form_delete_${form}`).submit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
{% block script %}{% endblock script %}
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<!-- Navbar -->
|
||||||
<nav class='navbar navbar-expand-lg bg-body bg-body-tertiary'>
|
<nav class='navbar navbar-expand-lg bg-body bg-body-tertiary'>
|
||||||
<div class='container-fluid'>
|
<div class='container-fluid'>
|
||||||
<a class='navbar-brand' href='/'>Inventory</a>
|
<a class='navbar-brand' href='/'>Inventory</a>
|
||||||
|
|
@ -141,108 +67,17 @@
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
{% block body %}{% endblock body %}
|
{% block body %}{% endblock body %}
|
||||||
|
|
||||||
<!-- Modals -->
|
<!-- Modals -->
|
||||||
<!-- Create new table -->
|
<!-- Base modals -->
|
||||||
<div class="modal fade" id="new_table" tabindex="-1" aria-hidden="true">
|
{% include "base_modals" %}
|
||||||
<div class="modal-dialog">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h1 class="modal-title fs-5">New table</h1>
|
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<form id="create_table" method="post" action="/table/create">
|
|
||||||
<div class="mb-3">
|
|
||||||
<label for="new_table_name" class="form-label">Name</label>
|
|
||||||
<input type="text" class="form-control" name="name" aria-describedby="new_table_name_label">
|
|
||||||
<div id="new_table_name_label" class="form-text">Name of the new table</div>
|
|
||||||
</div>
|
|
||||||
<div class="mb-3">
|
|
||||||
<div class="row" id="columns">
|
|
||||||
<div class="col-auto align-bottom me-1">
|
|
||||||
<label for="field_index" class="form-label">#</label>
|
|
||||||
<div class="form-text" id="field_index"><strong>1</strong></div>
|
|
||||||
</div>
|
|
||||||
<div class="col-auto">
|
|
||||||
<label for="fields" class="form-label">Column name</label>
|
|
||||||
<input type="text" class="form-control" id="field_name" aria-describedby="field_name_label" name="field_names">
|
|
||||||
</div>
|
|
||||||
<div class="col-auto">
|
|
||||||
<label for="field_type" class="form-label">Data type</label>
|
|
||||||
<select class="form-select" aria-label="data type" name="field_types">
|
|
||||||
<option value="0" selected>Text</option>
|
|
||||||
<option value="1">Number</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="dynamic_columns">
|
|
||||||
<!-- Columns added using plus button -->
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<div class="row justify-content-center mb-3 mt-3">
|
|
||||||
<div class="col-auto">
|
|
||||||
<button class="btn btn-primary" type="button" onclick="add_column()"><i class="bi bi-plus-lg"></i></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button class="btn btn-secondary" data-bs-dismiss="modal" type="button">Close</button>
|
|
||||||
<button class="btn btn-primary" type="button" onclick="create_table()">Create</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Import new table -->
|
{% block modals %}{% endblock modals %}
|
||||||
<div class="modal fade" id="import_table" tabindex="-1" aria-hidden="true">
|
|
||||||
<div class="modal-dialog">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h1 class="modal-title fs-5">Import new table</h1>
|
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<form action="/table/import" method="post" id="form_import_table">
|
|
||||||
<label for="import_name" class="form-label">Name</label>
|
|
||||||
<input type="text" class="form-control" id="import_name" name="name">
|
|
||||||
<label for="import_file" class="form-label">File</label>
|
|
||||||
<input id="import_columns" name="columns" hidden>
|
|
||||||
<input id="import_rows" name="rows" hidden>
|
|
||||||
</form>
|
|
||||||
<input type="file" class="form-control" id="import_file" name="name" accept="text/csv">
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button class="btn btn-secondary" data-bs-dismiss="modal" type="button">Close</button>
|
|
||||||
<button class="btn btn-primary" type="button" onclick="import_table();">Import</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Confirm delete -->
|
{% block script %}{% endblock script %}
|
||||||
<div class="modal fade" id="confirm_delete_modal" tabindex="-1" aria-hidden="true">
|
<!-- Bootstrap -->
|
||||||
<div class="modal-dialog">
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
|
||||||
<div class="modal-content">
|
<!-- JQuery -->
|
||||||
<div class="modal-header">
|
<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
|
||||||
<h1 class="modal-title fs-5">Confirm deletion</h1>
|
</body>
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<h5>Are you sure you want to delete this <span id="confirm_delete_modal_entity"></span>?</h5>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button class="btn btn-secondary" data-bs-dismiss="modal" type="button"><i class="bi bi-x"></i></button>
|
|
||||||
<button class="btn btn-danger" type="button" data-bs-dismiss="modal" id="confirm_delete_modal_button"><i class="bi bi-trash3-fill"></i></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% block more_modals %}{% endblock more_modals %}
|
|
||||||
|
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
|
|
||||||
<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
|
|
||||||
120
templates/base_modals.html.tera
Normal file
120
templates/base_modals.html.tera
Normal file
|
|
@ -0,0 +1,120 @@
|
||||||
|
{# This file is part of inventur.
|
||||||
|
# inventur is a simple web app using rocket to help maintain inventory data.
|
||||||
|
# Copyright (C) 2024 Johannes Randerath
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
#}
|
||||||
|
|
||||||
|
{% block modal_new_table %}
|
||||||
|
<!-- Create new table -->
|
||||||
|
<div class="modal fade" id="new_table" tabindex="-1" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h1 class="modal-title fs-5">New table</h1>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form id="form_create_table" method="post" action="/table/create">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="new_table_name" class="form-label">Name</label>
|
||||||
|
<input type="text" class="form-control" name="name" aria-describedby="new_table_name_label">
|
||||||
|
<div id="new_table_name_label" class="form-text">Name of the new table</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<div class="row" id="columns">
|
||||||
|
<div class="col-auto align-bottom me-1">
|
||||||
|
<label for="field_index" class="form-label">#</label>
|
||||||
|
<div class="form-text" id="field_index"><strong>1</strong></div>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<label for="fields" class="form-label">Column name</label>
|
||||||
|
<input type="text" class="form-control" id="field_name" aria-describedby="field_name_label" name="field_names">
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<label for="field_type" class="form-label">Data type</label>
|
||||||
|
<select class="form-select" aria-label="data type" name="field_types">
|
||||||
|
<option value="0" selected>Text</option>
|
||||||
|
<option value="1">Number</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="dynamic_columns">
|
||||||
|
<!-- Columns added using plus button -->
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div class="row justify-content-center mb-3 mt-3">
|
||||||
|
<div class="col-auto">
|
||||||
|
<button class="btn btn-primary" type="button" onclick="add_column()"><i class="bi bi-plus-lg"></i></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button class="btn btn-secondary" data-bs-dismiss="modal" type="button">Close</button>
|
||||||
|
<button class="btn btn-primary" type="button" onclick="create_table()">Create</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock modal_new_table %}
|
||||||
|
|
||||||
|
{% block modal_import_table %}
|
||||||
|
<!-- Import new table -->
|
||||||
|
<div class="modal fade" id="import_table" tabindex="-1" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h1 class="modal-title fs-5">Import new table</h1>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form action="/table/import" method="post" id="form_import_table">
|
||||||
|
<label for="import_name" class="form-label">Name</label>
|
||||||
|
<input type="text" class="form-control" id="import_name" name="name">
|
||||||
|
<label for="import_file" class="form-label">File</label>
|
||||||
|
<input id="import_columns" name="columns" hidden>
|
||||||
|
<input id="import_rows" name="rows" hidden>
|
||||||
|
</form>
|
||||||
|
<input type="file" class="form-control" id="import_file" name="name" accept="text/csv">
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button class="btn btn-secondary" data-bs-dismiss="modal" type="button">Close</button>
|
||||||
|
<button class="btn btn-primary" type="button" onclick="import_table();">Import</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock modal_import_table %}
|
||||||
|
|
||||||
|
{% block modal_confirm_delete %}
|
||||||
|
<!-- Confirm delete -->
|
||||||
|
<div class="modal fade" id="confirm_delete_modal" tabindex="-1" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h1 class="modal-title fs-5">Confirm deletion</h1>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<h5>Are you sure you want to delete this <span id="confirm_delete_modal_entity"></span>?</h5>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button class="btn btn-secondary" data-bs-dismiss="modal" type="button"><i class="bi bi-x"></i></button>
|
||||||
|
<button class="btn btn-danger" type="button" data-bs-dismiss="modal" id="confirm_delete_modal_button"><i class="bi bi-trash3-fill"></i></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock modal_confirm_delete %}
|
||||||
|
|
||||||
|
|
@ -1,24 +1,26 @@
|
||||||
{# vim: set filetype=html: #}
|
{# vim: set filetype=html: #}
|
||||||
<!-- This file is part of inventur.
|
|
||||||
-- inventur is a simple web app using rocket to help maintain inventory data.
|
|
||||||
-- Copyright (C) 2024 Johannes Randerath
|
|
||||||
--
|
|
||||||
-- This program is free software: you can redistribute it and/or modify
|
|
||||||
-- it under the terms of the GNU Affero General Public License as published by
|
|
||||||
-- the Free Software Foundation, either version 3 of the License, or
|
|
||||||
-- (at your option) any later version.
|
|
||||||
--
|
|
||||||
-- This program is distributed in the hope that it will be useful,
|
|
||||||
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
-- GNU Affero General Public License for more details.
|
|
||||||
--
|
|
||||||
-- You should have received a copy of the GNU Affero General Public License
|
|
||||||
-- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
-->
|
|
||||||
|
|
||||||
{% extends "base" %}
|
{% extends "base" %}
|
||||||
|
|
||||||
|
{# This file is part of inventur.
|
||||||
|
# inventur is a simple web app using rocket to help maintain inventory data.
|
||||||
|
# Copyright (C) 2024 Johannes Randerath
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
#}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
|
<!-- Accordions of owned tables to get a preview and to choos -->
|
||||||
<div class="accordion" id="my_tables">
|
<div class="accordion" id="my_tables">
|
||||||
{% for tname in tnames %}
|
{% for tname in tnames %}
|
||||||
{% set clms = columns[loop.index0] %}
|
{% set clms = columns[loop.index0] %}
|
||||||
|
|
@ -35,6 +37,7 @@
|
||||||
<div class="row justify-content-between">
|
<div class="row justify-content-between">
|
||||||
<div class="col-auto"><h3>{{ tname }}</h3></div><div class="col-auto"><a class="btn btn-primary" href="/table/{{ tid }}">Open</a></div>
|
<div class="col-auto"><h3>{{ tname }}</h3></div><div class="col-auto"><a class="btn btn-primary" href="/table/{{ tid }}">Open</a></div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Preview -->
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
|
|
||||||
|
|
@ -1,108 +1,48 @@
|
||||||
{# vim: set filetype=html: #}
|
{# vim: set filetype=html: #}
|
||||||
<!-- This file is part of inventur.
|
|
||||||
-- inventur is a simple web app using rocket to help maintain inventory data.
|
|
||||||
-- Copyright (C) 2024 Johannes Randerath
|
|
||||||
--
|
|
||||||
-- This program is free software: you can redistribute it and/or modify
|
|
||||||
-- it under the terms of the GNU Affero General Public License as published by
|
|
||||||
-- the Free Software Foundation, either version 3 of the License, or
|
|
||||||
-- (at your option) any later version.
|
|
||||||
--
|
|
||||||
-- This program is distributed in the hope that it will be useful,
|
|
||||||
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
-- GNU Affero General Public License for more details.
|
|
||||||
--
|
|
||||||
-- You should have received a copy of the GNU Affero General Public License
|
|
||||||
-- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
-->
|
|
||||||
|
|
||||||
{% extends "base" %}
|
{% extends "base" %}
|
||||||
|
|
||||||
|
{# This file is part of inventur.
|
||||||
|
# inventur is a simple web app using rocket to help maintain inventory data.
|
||||||
|
# Copyright (C) 2024 Johannes Randerath
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
#}
|
||||||
|
|
||||||
{% block pagename %}Table{% endblock pagename %}
|
{% block pagename %}Table{% endblock pagename %}
|
||||||
|
|
||||||
{% block script %}
|
|
||||||
<script type="text/javascript">
|
|
||||||
let ncol = 1;
|
|
||||||
let edit_mode = false;
|
|
||||||
let column_names = [{% for col in column_names %} '{{ col }}', {% endfor %}];
|
|
||||||
let column_types = [{% for col in column_types %} {{ col }}, {% endfor %}];
|
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", (event) => {
|
|
||||||
let rows = document.getElementById("content_table").getElementsByTagName('tbody')[0].rows;
|
|
||||||
for (let i = 0; i < rows.length; i++)
|
|
||||||
{
|
|
||||||
let row = rows[i];
|
|
||||||
let createClickHandler = function(r)
|
|
||||||
{
|
|
||||||
return function()
|
|
||||||
{
|
|
||||||
$('#edit_entry_modal').modal('show');
|
|
||||||
document.getElementById('modal_caller').innerHTML = i+1;
|
|
||||||
document.getElementById('form_edit_entry_rowpos').value = i+1;
|
|
||||||
document.getElementById('form_delete_entry_rowpos').value = i+1;
|
|
||||||
let cells = row.cells;
|
|
||||||
for (let j = 1; j < cells.length; j++) {
|
|
||||||
document.getElementById(`form_edit_entry_${j}`).value = cells[j].innerHTML;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
row.onclick = createClickHandler(row);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function toggle_edit_tname() {
|
|
||||||
let span = document.getElementById('tname');
|
|
||||||
if (!edit_mode) {
|
|
||||||
span.innerHTML = `
|
|
||||||
<div class='row'>
|
|
||||||
<div class='col-auto ml-1 p-2'>
|
|
||||||
<form id='form_edit_table' action='/table/name/edit' method='post'>
|
|
||||||
<input name='tblid' value='{{ tblid }}' hidden />
|
|
||||||
<input type='text' class='form-control' id='tname_edit' name='new_name' value='{{ tblname }}' />
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div class='col-auto mt-0 pt-0 pr-1'>
|
|
||||||
<div class="btn-group" role="group">
|
|
||||||
<button class="btn btn-secondary" onclick="toggle_edit_tname();" type="button"><i class="bi bi-x"></i></button>
|
|
||||||
<button class='btn btn-success' type='button' onclick='edit_tname()'>
|
|
||||||
<i class='bi bi-check'></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<button class='btn btn-danger' type="button" onclick='confirm_delete("table");'><i class="bi bi-trash3-fill"></i></button>
|
|
||||||
</div>
|
|
||||||
<form id="form_delete_table" action="/table/delete" method="post">
|
|
||||||
<input value="{{ tblid}}" name="tblid" hidden />
|
|
||||||
</form>`;
|
|
||||||
edit_mode = true;
|
|
||||||
document.getElementById('pencil_button_edit_tname').hidden = true;
|
|
||||||
} else {
|
|
||||||
document.getElementById('pencil_button_edit_tname').hidden = false;
|
|
||||||
span.innerHTML = '{{ tblname }}';
|
|
||||||
edit_mode = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function edit_tname() {
|
|
||||||
document.getElementById('form_edit_tname').submit();
|
|
||||||
toggle_edit_tname();
|
|
||||||
}
|
|
||||||
function edit_column(clmn_index) {
|
|
||||||
document.getElementById('form_edit_column_name').value = column_names[clmn_index];
|
|
||||||
document.getElementById('form_edit_column_type').value = column_types[clmn_index];
|
|
||||||
document.getElementById('modal_caller').innerHTML = column_names[clmn_index];
|
|
||||||
document.getElementById('form_delete_column_idintbl').value = clmn_index + 1;
|
|
||||||
document.getElementById('form_edit_column_idintbl').value = clmn_index + 1;
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
{% endblock script %}
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
|
|
||||||
|
<!-- Table header and editing -->
|
||||||
<div class="row justify-content-between">
|
<div class="row justify-content-between">
|
||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
<h1><div class='input-group'><span id="tname">{{ tblname }}</span><button class="btn" type="button" onclick="toggle_edit_tname();" id="pencil_button_edit_tname"><i class="bi bi-pencil"></i></button></div></h1>
|
<h1>
|
||||||
|
<div class='input-group'>
|
||||||
|
<span id="tname">{{ tblname }}</span>
|
||||||
|
<button class="btn" type="button" onclick="toggle_edit_tname();" id="pencil_button_edit_tname">
|
||||||
|
<i class="bi bi-pencil"></i>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Search bar -->
|
||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
<div class="btn-toolbar mt-2" role="toolbar">
|
<div class="btn-toolbar mt-2" role="toolbar">
|
||||||
<div class="btn-group me-2">
|
<div class="btn-group me-2">
|
||||||
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#create_entry_modal"><i class="bi bi-plus-lg"></i></button>
|
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#create_entry_modal">
|
||||||
|
<i class="bi bi-plus-lg"></i>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<form method="get" action="/table/{{ tblid }}">
|
<form method="get" action="/table/{{ tblid }}">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
|
|
@ -113,20 +53,27 @@
|
||||||
<button class="btn btn-outline-primary" type="submit"><i class="bi bi-search"></i></button>
|
<button class="btn btn-outline-primary" type="submit"><i class="bi bi-search"></i></button>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<select name="search_fields" class="form-select" multiple>
|
<select name="search_fields" class="form-select" multiple>
|
||||||
<li><option disabled class="dropdown-header">Search in</option></li>
|
<option disabled class="dropdown-header">Search in</option>
|
||||||
{% for column in column_names %}
|
{% for column in column_names %}
|
||||||
<li><option class="dropdown-option" value="{{ loop.index - 1 }}" {% if (loop.index - 1) in search_fields %}selected{% endif %}>{{ column }}</option></li>
|
<option class="dropdown-option" value="{{ loop.index - 1 }}" {% if (loop.index - 1) in search_fields %}selected{% endif %}>{{ column }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
|
<!-- Table contents -->
|
||||||
<table id="content_table" class="table table-striped table-hover table-responsive table-bordered mt-2">
|
<table id="content_table" class="table table-striped table-hover table-responsive table-bordered mt-2">
|
||||||
|
<!-- column headings -->
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
<!-- Index (#) column -->
|
||||||
<th style="width:7%">
|
<th style="width:7%">
|
||||||
<div class="row justify-content-between">
|
<div class="row justify-content-between">
|
||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
|
|
@ -134,8 +81,8 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
<div class="btn-toolbar">
|
<div class="btn-toolbar">
|
||||||
<button class="btn" data-bs-toggle="modal" data-bs-target="#create_column_modal" type="button" onclick=""><i class="bi bi-bookmark-plus"></i></button>
|
<button class="btn" data-bs-toggle="modal" data-bs-target="#create_column_modal" type="button"><i class="bi bi-bookmark-plus"></i></button>
|
||||||
<form method="GET" action="/table/{{ tblid }}">
|
<form method="get" action="/table/{{ tblid }}">
|
||||||
<input value="0" name="sort_field" hidden />
|
<input value="0" name="sort_field" hidden />
|
||||||
<input value="{% if sort_field == 0 %}{{ (sort_dir + 1) % 2}}{% else %}0{% endif %}" name="sort_dir" hidden />
|
<input value="{% if sort_field == 0 %}{{ (sort_dir + 1) % 2}}{% else %}0{% endif %}" name="sort_dir" hidden />
|
||||||
{% if sort_field == 0 and sort_dir == 0 %}
|
{% if sort_field == 0 and sort_dir == 0 %}
|
||||||
|
|
@ -149,6 +96,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
|
<!-- individual columns -->
|
||||||
{% for column in column_names %}
|
{% for column in column_names %}
|
||||||
<th>
|
<th>
|
||||||
<div class="row justify-content-between">
|
<div class="row justify-content-between">
|
||||||
|
|
@ -175,6 +123,8 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
|
<!-- Table rows -->
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for row in rows %}
|
{% for row in rows %}
|
||||||
<tr>
|
<tr>
|
||||||
|
|
@ -186,179 +136,20 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{% endblock body %}
|
{% endblock body %}
|
||||||
|
{% block modals %}
|
||||||
|
<!-- Table specific modals -->
|
||||||
|
{% include "table_modals" %}
|
||||||
|
{% endblock modals %}
|
||||||
|
|
||||||
{% block more_modals %}
|
{% block script %}
|
||||||
<!-- Add new item -->
|
<!-- table specific values -->
|
||||||
<div class="modal fade" id="create_entry_modal" tabindex="-1" aria-hidden="true">
|
<script type="text/javascript">
|
||||||
<div class="modal-dialog">
|
const column_names = [{% for col in column_names %} '{{ col }}', {% endfor %}];
|
||||||
<div class="modal-content">
|
const column_types = [{% for col in column_types %} {{ col }}, {% endfor %}];
|
||||||
<div class="modal-header">
|
const tblid = {{ tblid }};
|
||||||
<h1 class="modal-title fs-5">Add entry</h1>
|
const tblname = '{{ tblname }}';
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" onclick="document.getElementById('form_edit_entry').reset();"></button>
|
</script>
|
||||||
</div>
|
<!-- Table specific functionality -->
|
||||||
<div class="modal-body">
|
<script type="text/javascript" src="/js/table.js"></script>
|
||||||
<h5>Cell values:</h5>
|
{% endblock script %}
|
||||||
<br>
|
|
||||||
<form id="form_add_entry" action="/row/new" method="post">
|
|
||||||
<input name="tblid" value="{{ tblid }}" hidden />
|
|
||||||
{% for column in column_names %}
|
|
||||||
<div class="mb-3">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-auto">
|
|
||||||
<label for="entry_value_{{ column }}" class="form-label" >{{ column }}:</label>
|
|
||||||
</div>
|
|
||||||
<div class="col-auto">
|
|
||||||
<input type="text" class="form-control" name="cells" id="entry_value_{{column}}" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<div class="container row justify-content-between">
|
|
||||||
<div class="col-auto">
|
|
||||||
</div>
|
|
||||||
<div class="col-auto">
|
|
||||||
<button class="btn btn-secondary" data-bs-dismiss="modal" type="button" onclick="document.getElementById('form_add_entry').reset();"><i class="bi bi-x"></i></button>
|
|
||||||
<button class="btn btn-primary" type="button" onclick="document.getElementById('form_add_entry').submit().reset();"><i class="bi bi-check-lg"></i></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Edit entry -->
|
|
||||||
<div class="modal fade" id="edit_entry_modal" tabindex="-1" aria-hidden="true">
|
|
||||||
<div class="modal-dialog">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h1 class="modal-title fs-5"><div class="row"><div class="col-auto">Row <span id="modal_caller"></span></div><div class="col-auto"><i class="bi bi-pencil-fill"></i></div></div></h1>
|
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" onclick="document.getElementById('form_edit_entry').reset();"></button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<form action="/row/edit" method="post" id="form_edit_entry">
|
|
||||||
<input name="tblid" value="{{ tblid }}" hidden>
|
|
||||||
<input name="row_pos" id="form_edit_entry_rowpos" hidden>
|
|
||||||
{% for column in column_names %}
|
|
||||||
<div class="mb-3">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-auto">
|
|
||||||
<label for="form_edit_entry_{{ loop.index }}" class="form-label">{{ column }}</label>
|
|
||||||
</div>
|
|
||||||
<div class="col-auto">
|
|
||||||
<input type="text" class="form-control" name="cells" id="form_edit_entry_{{ loop.index }}" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<div class="container row justify-content-between">
|
|
||||||
<div class="col-auto">
|
|
||||||
<form action="/row/delete" method="post" id="form_delete_entry">
|
|
||||||
<input name="tblid" value="{{ tblid }}" hidden>
|
|
||||||
<input id="form_delete_entry_rowpos" name="row_pos" hidden>
|
|
||||||
<button class="btn btn-danger" data-bs-dismiss="modal" type="button" onclick="confirm_delete('entry');"><i class="bi bi-trash3-fill"></i></button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div class="col-auto">
|
|
||||||
<button class="btn btn-secondary" data-bs-dismiss="modal" type="button" onclick="document.getElementById('form_edit_entry').reset();"><i class="bi bi-x"></i></button>
|
|
||||||
<button class="btn btn-primary" type="button" onclick="document.getElementById('form_edit_entry').submit().reset();"><i class="bi bi-check-lg"></i></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Add column -->
|
|
||||||
<div class="modal fade" id="create_column_modal" tabindex="-1" aria-hidden="true" onclick="document.getElementById('form_edit_entry').reset();">
|
|
||||||
<div class="modal-dialog">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h1 class="modal-title fs-5">New column</h1>
|
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<form action="/column/create" method="post" id="form_create_column">
|
|
||||||
<input name="tblid" value="{{ tblid }}" hidden>
|
|
||||||
<div class="mb-3">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-auto">
|
|
||||||
<label for="form_create_column_name" class="form-label">Name:</label>
|
|
||||||
<input type="text" class="form-control" name="name" id="form_create_column_name" />
|
|
||||||
</div>
|
|
||||||
<div class="col-auto">
|
|
||||||
<label for="form_create_column_type" class="form-label">Type</label>
|
|
||||||
<select class="form-select" name="column_type" id="form_create_column_type">
|
|
||||||
<option value="0">Text</option>
|
|
||||||
<option value="1">Number</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<div class="col-auto">
|
|
||||||
<button class="btn btn-secondary" data-bs-dismiss="modal" type="button" onclick="document.getElementById('form_create_column').reset();"><i class="bi bi-x"></i></button>
|
|
||||||
<button class="btn btn-primary" type="button" onclick="document.getElementById('form_create_column').submit().reset();"><i class="bi bi-check-lg"></i></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Edit column -->
|
|
||||||
<div class="modal fade" id="edit_column_modal" tabindex="-1" aria-hidden="true">
|
|
||||||
<div class="modal-dialog">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h1 class="modal-title fs-5"><div class="row"><div class="col-auto"><span id="modal_caller"></span></div><div class="col-auto"><i class="bi bi-pencil-fill"></i></div></div></h1>
|
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<form action="/column/edit" method="post" id="form_edit_column">
|
|
||||||
<input name="tblid" value="{{ tblid }}" hidden>
|
|
||||||
<input name="id_in_table" id="form_edit_column_idintbl" hidden>
|
|
||||||
<div class="mb-3">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-auto">
|
|
||||||
<label for="form_edit_column_name" class="form-label">Name:</label>
|
|
||||||
<input type="text" class="form-control" name="name" id="form_edit_column_name" />
|
|
||||||
</div>
|
|
||||||
<div class="col-auto">
|
|
||||||
<label for="form_edit_column_type" class="form-label">Type</label>
|
|
||||||
<select class="form-select" name="column_type" id="form_edit_column_type">
|
|
||||||
<option value="0">Text</option>
|
|
||||||
<option value="1">Number</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<div class="container row justify-content-between">
|
|
||||||
<div class="col-auto">
|
|
||||||
<form action="/column/delete" method="post" id="form_delete_column">
|
|
||||||
<input name="id_in_table" id="form_delete_column_idintbl" hidden>
|
|
||||||
<input name="tblid" value="{{ tblid }}" hidden>
|
|
||||||
<button class="btn btn-danger" data-bs-dismiss="modal" type="button" onclick="confirm_delete('column');"><i class="bi bi-trash3-fill"></i></button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div class="col-auto">
|
|
||||||
<button class="btn btn-secondary" data-bs-dismiss="modal" type="button" onclick="document.getElementById('form_edit_column').reset();"><i class="bi bi-x"></i></button>
|
|
||||||
<button class="btn btn-primary" type="button" onclick="document.getElementById('form_edit_column').submit().reset();"><i class="bi bi-check-lg"></i></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
{% endblock more_modals %}
|
|
||||||
|
|
|
||||||
197
templates/table_modals.html.tera
Normal file
197
templates/table_modals.html.tera
Normal file
|
|
@ -0,0 +1,197 @@
|
||||||
|
{# This file is part of inventur.
|
||||||
|
# inventur is a simple web app using rocket to help maintain inventory data.
|
||||||
|
# Copyright (C) 2024 Johannes Randerath
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
#}
|
||||||
|
|
||||||
|
{% block modal_new_entry %}
|
||||||
|
<!-- Add new item -->
|
||||||
|
<div class="modal fade" id="create_entry_modal" tabindex="-1" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h1 class="modal-title fs-5">Add entry</h1>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" onclick="document.getElementById('form_create_entry').reset();"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<h5>Cell values:</h5>
|
||||||
|
<br>
|
||||||
|
<form id="form_create_entry" action="/row/new" method="post">
|
||||||
|
<input name="tblid" value="{{ tblid }}" hidden />
|
||||||
|
{% for column in column_names %}
|
||||||
|
<div class="mb-3">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-auto">
|
||||||
|
<label for="entry_value_{{ column }}" class="form-label" >{{ column }}:</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<input type="text" class="form-control" name="cells" id="entry_value_{{column}}" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<div class="container row justify-content-between">
|
||||||
|
<div class="col-auto">
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<button class="btn btn-secondary" data-bs-dismiss="modal" type="button" onclick="document.getElementById('form_create_entry').reset();"><i class="bi bi-x"></i></button>
|
||||||
|
<button class="btn btn-primary" type="button" onclick="document.getElementById('form_create_entry').submit().reset();"><i class="bi bi-check-lg"></i></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock modal_new_entry %}
|
||||||
|
|
||||||
|
{% block modal_edit_entry %}
|
||||||
|
<!-- Edit entry -->
|
||||||
|
<div class="modal fade" id="edit_entry_modal" tabindex="-1" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h1 class="modal-title fs-5"><div class="row"><div class="col-auto">Row <span id="modal_caller"></span></div><div class="col-auto"><i class="bi bi-pencil-fill"></i></div></div></h1>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" onclick="document.getElementById('form_edit_entry').reset();"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form action="/row/edit" method="post" id="form_edit_entry">
|
||||||
|
<input name="tblid" value="{{ tblid }}" hidden>
|
||||||
|
<input name="row_pos" id="form_edit_entry_rowpos" hidden>
|
||||||
|
{% for column in column_names %}
|
||||||
|
<div class="mb-3">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-auto">
|
||||||
|
<label for="form_edit_entry_{{ loop.index }}" class="form-label">{{ column }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<input type="text" class="form-control" name="cells" id="form_edit_entry_{{ loop.index }}" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<div class="container row justify-content-between">
|
||||||
|
<div class="col-auto">
|
||||||
|
<form action="/row/delete" method="post" id="form_delete_entry">
|
||||||
|
<input name="tblid" value="{{ tblid }}" hidden>
|
||||||
|
<input id="form_delete_entry_rowpos" name="row_pos" hidden>
|
||||||
|
<button class="btn btn-danger" data-bs-dismiss="modal" type="button" onclick="confirm_delete('entry');"><i class="bi bi-trash3-fill"></i></button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<button class="btn btn-secondary" data-bs-dismiss="modal" type="button" onclick="document.getElementById('form_edit_entry').reset();"><i class="bi bi-x"></i></button>
|
||||||
|
<button class="btn btn-primary" type="button" onclick="submit_and_reset('form_edit_entry');" class="bi bi-check-lg"></i></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock modal_edit_entry %}
|
||||||
|
|
||||||
|
{% block modal_new_column %}
|
||||||
|
<!-- Add column -->
|
||||||
|
<div class="modal fade" id="create_column_modal" tabindex="-1" aria-hidden="true" onclick="document.getElementById('form_create_column').reset();">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h1 class="modal-title fs-5">New column</h1>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form action="/column/create" method="post" id="form_create_column">
|
||||||
|
<input name="tblid" value="{{ tblid }}" hidden>
|
||||||
|
<div class="mb-3">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-auto">
|
||||||
|
<label for="form_create_column_name" class="form-label">Name:</label>
|
||||||
|
<input type="text" class="form-control" name="name" id="form_create_column_name" />
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<label for="form_create_column_type" class="form-label">Type</label>
|
||||||
|
<select class="form-select" name="column_type" id="form_create_column_type">
|
||||||
|
<option value="0">Text</option>
|
||||||
|
<option value="1">Number</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<div class="col-auto">
|
||||||
|
<button class="btn btn-secondary" data-bs-dismiss="modal" type="button" onclick="document.getElementById('form_create_column').reset();"><i class="bi bi-x"></i></button>
|
||||||
|
<button class="btn btn-primary" type="button" onclick="submit_and_reset('form_create_column');"><i class="bi bi-check-lg"></i></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock modal_new_column %}
|
||||||
|
|
||||||
|
{% block modal_edit_column %}
|
||||||
|
<!-- Edit column -->
|
||||||
|
<div class="modal fade" id="edit_column_modal" tabindex="-1" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h1 class="modal-title fs-5"><div class="row"><div class="col-auto"><span id="modal_caller"></span></div><div class="col-auto"><i class="bi bi-pencil-fill"></i></div></div></h1>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form action="/column/edit" method="post" id="form_edit_column">
|
||||||
|
<input name="tblid" value="{{ tblid }}" hidden>
|
||||||
|
<input name="id_in_table" id="form_edit_column_idintbl" hidden>
|
||||||
|
<div class="mb-3">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-auto">
|
||||||
|
<label for="form_edit_column_name" class="form-label">Name:</label>
|
||||||
|
<input type="text" class="form-control" name="name" id="form_edit_column_name" />
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<label for="form_edit_column_type" class="form-label">Type</label>
|
||||||
|
<select class="form-select" name="column_type" id="form_edit_column_type">
|
||||||
|
<option value="0">Text</option>
|
||||||
|
<option value="1">Number</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<div class="container row justify-content-between">
|
||||||
|
<div class="col-auto">
|
||||||
|
<form action="/column/delete" method="post" id="form_delete_column">
|
||||||
|
<input name="id_in_table" id="form_delete_column_idintbl" hidden>
|
||||||
|
<input name="tblid" value="{{ tblid }}" hidden>
|
||||||
|
<button class="btn btn-danger" data-bs-dismiss="modal" type="button" onclick="confirm_delete('column');"><i class="bi bi-trash3-fill"></i></button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<button class="btn btn-secondary" data-bs-dismiss="modal" type="button" onclick="document.getElementById('form_edit_column').reset();"><i class="bi bi-x"></i></button>
|
||||||
|
<button class="btn btn-primary" type="button" onclick="submit_and_reset('form_edit_column');"><i class="bi bi-check-lg"></i></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock modal_edit_column %}
|
||||||
Loading…
Reference in New Issue
Block a user