Fixed several features
This commit is contained in:
parent
c06ddc9498
commit
45b34f5fc8
|
|
@ -1,20 +0,0 @@
|
|||
[default]
|
||||
template_dir = "templates"
|
||||
secret_key = "${ROCKET_SECRET_KEY}"
|
||||
address = "0.0.0.0"
|
||||
port = 8001
|
||||
ident = "Randerath Inventur"
|
||||
|
||||
[default.tls]
|
||||
certs="private/cert.pem"
|
||||
key="private/key.pem"
|
||||
|
||||
#[default.databases.inventur]
|
||||
#url = "${ROCKET_DATABASE_URL}"
|
||||
|
||||
[default.oauth.oauth]
|
||||
auth_uri = "https://ldap.randerath.eu/realms/master/protocol/openid-connect/auth"
|
||||
token_uri = "https://ldap.randerath.eu/realms/master/protocol/openid-connect/token"
|
||||
client_id = "randy_inventur"
|
||||
client_secret = "${ROCKET_CLIENT_SECRET}"
|
||||
redirect_uri = "https://inventur.rander.at/auth"
|
||||
|
|
@ -17,5 +17,5 @@ fn main() {
|
|||
.expect("Usage: add_column <tblid> <name> <uid>.");
|
||||
|
||||
let conn = &mut establish_connection();
|
||||
println!("{}", add_column(conn, table_id, name, uid).expect("Error"));
|
||||
println!("{}", add_column(conn, table_id, name, crate::FIELDTYPE::TEXT, uid).expect("Error"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,5 +34,5 @@ fn main() {
|
|||
}
|
||||
|
||||
|
||||
println!("{}", create_table(conn, name.to_string(), fields, uid).unwrap());
|
||||
println!("{}", create_table(conn, name.to_string(), fields.clone(), vec![crate::FIELDTYPE::TEXT; fields.len()], uid).unwrap());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ pub fn create_jrcell_relative(conn: &mut MysqlConnection, tblid: i32, rowpos: i3
|
|||
create_jrcell(conn, ntrid, clmid, &value)
|
||||
}
|
||||
|
||||
pub fn change_jrcell_value_relative(conn: &mut MysqlConnection, tblid: i32, rowpos: i32, idintbl: i32, new_value: String) -> Result<usize, diesel::result::Error> {
|
||||
pub fn change_jrcell_value_relative(conn: &mut MysqlConnection, tblid: i32, rowpos: i32, idintbl: i32, new_value: &String) -> Result<usize, diesel::result::Error> {
|
||||
let ntrid = jrentries::get_jrentry_id(conn, tblid, rowpos);
|
||||
if ntrid.is_err() {
|
||||
return Err(ntrid.err().unwrap());
|
||||
|
|
@ -78,7 +78,7 @@ pub fn create_jrcell(conn: &mut MysqlConnection, entryid: i32, columnid: i32, va
|
|||
|
||||
}
|
||||
|
||||
pub fn change_jrcell_value(conn: &mut MysqlConnection, entryid: i32, columnid: i32, new_value: String) -> Result<usize, diesel::result::Error> {
|
||||
pub fn change_jrcell_value(conn: &mut MysqlConnection, entryid: i32, columnid: i32, new_value: &String) -> Result<usize, diesel::result::Error> {
|
||||
diesel::update(jrcells.filter(jrentry_id.eq(entryid)).filter(jrcolumn_id.eq(columnid)))
|
||||
.set(cell_value.eq(new_value))
|
||||
.execute(conn)
|
||||
|
|
@ -96,3 +96,10 @@ pub fn get_entry_cells(conn: &mut MysqlConnection, entryid: i32) -> Result<Vec<J
|
|||
.select(Jrcell::as_select())
|
||||
.load(conn)
|
||||
}
|
||||
|
||||
pub fn get_entry_cell_ids(conn: &mut MysqlConnection, entryid: i32) -> Result<Vec<i32>, diesel::result::Error> {
|
||||
jrcells
|
||||
.filter(jrentry_id.eq(entryid))
|
||||
.select(id)
|
||||
.load(conn)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use models::{ Jrcolumn, NewJrcolumn };
|
|||
use schema::jrcolumns::dsl::{ jrcolumns, id, name, jrtable_id, id_in_table };
|
||||
|
||||
|
||||
pub fn create_jrcolumn(conn: &mut MysqlConnection, tblid: i32, clmnname: String) -> Result<usize, diesel::result::Error> {
|
||||
pub fn create_jrcolumn(conn: &mut MysqlConnection, tblid: i32, clmnname: String, clmtype: crate::FIELDTYPE) -> Result<usize, diesel::result::Error> {
|
||||
use schema::jrentries::dsl::{ jrentries, jrtable_id };
|
||||
use models::{ Jrentry, NewJrcell };
|
||||
|
||||
|
|
@ -16,7 +16,7 @@ pub fn create_jrcolumn(conn: &mut MysqlConnection, tblid: i32, clmnname: String)
|
|||
return ncols;
|
||||
}
|
||||
let ncols = ncols.unwrap() as i32;
|
||||
let jrcolumn = NewJrcolumn { name: clmnname, jrtable_id: tblid, id_in_table: ncols + 1 };
|
||||
let jrcolumn = NewJrcolumn { name: clmnname.clone(), jrtable_id: tblid, column_type: clmtype as i32, id_in_table: (ncols + 1) as i32 };
|
||||
let res = diesel::insert_into(crate::schema::jrcolumns::table)
|
||||
.values(&jrcolumn)
|
||||
.execute(conn);
|
||||
|
|
@ -48,16 +48,11 @@ pub fn create_jrcolumn(conn: &mut MysqlConnection, tblid: i32, clmnname: String)
|
|||
|
||||
}
|
||||
|
||||
pub fn create_jrcolumns_empty_tbl(conn: &mut MysqlConnection, tblid: i32, names: Vec<String>) -> Result<usize, diesel::result::Error> {
|
||||
pub fn create_jrcolumns_empty_tbl(conn: &mut MysqlConnection, tblid: i32, names: Vec<String>, types: Vec<crate::FIELDTYPE>) -> Result<usize, diesel::result::Error> {
|
||||
let mut cols : Vec<NewJrcolumn> = Vec::new();
|
||||
let ncols = jrtables::get_ncols(conn, tblid);
|
||||
if ncols.is_err() {
|
||||
return ncols;
|
||||
}
|
||||
let mut ncols = ncols.unwrap() as i32;
|
||||
for clmnname in names {
|
||||
ncols += 1;
|
||||
cols.push(NewJrcolumn { name: clmnname, jrtable_id: tblid, id_in_table: ncols});
|
||||
|
||||
for i in 0..names.len() {
|
||||
cols.push(NewJrcolumn { name: names[i].clone(), column_type: types[i] as i32, jrtable_id: tblid, id_in_table: (i+1) as i32});
|
||||
}
|
||||
let cols = cols;
|
||||
|
||||
|
|
@ -169,8 +164,8 @@ pub fn move_column(conn: &mut MysqlConnection, clmnid: i32, new_id_in_table: i32
|
|||
.execute(conn)
|
||||
}
|
||||
|
||||
pub fn delete_column_relative(conn: &mut MysqlConnection, tblid: i32, idinclm: i32) -> Result<usize, diesel::result::Error> {
|
||||
let clmid = get_clmid_relative(conn, tblid, idinclm);
|
||||
pub fn delete_column_relative(conn: &mut MysqlConnection, tblid: i32, idintbl: i32) -> Result<usize, diesel::result::Error> {
|
||||
let clmid = get_clmid_relative(conn, tblid, idintbl);
|
||||
if clmid.is_err() {
|
||||
return Err(clmid.err().unwrap());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,7 +76,6 @@ pub fn delete_jrentry_relative(conn: &mut MysqlConnection, tblid: i32, rowpos:
|
|||
return Err(entryid.err().unwrap());
|
||||
}
|
||||
delete_jrentry(conn, entryid.unwrap())
|
||||
|
||||
}
|
||||
|
||||
pub fn delete_jrentry(conn: &mut MysqlConnection, entryid: i32) -> Result<usize, diesel::result::Error> {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use crate::jrcolumns;
|
|||
use models::{ Jrtable, NewJrtable };
|
||||
use schema::jrtables::dsl::{ jrtables, id, name, owner_id };
|
||||
|
||||
pub fn create_jrtable(conn: &mut MysqlConnection, tblname: &String, field_names: Vec<String>, uid: i32) -> Result<i32, diesel::result::Error> {
|
||||
pub fn create_jrtable(conn: &mut MysqlConnection, tblname: &String, field_names: Vec<String>, field_types: Vec<crate::FIELDTYPE>, uid: i32) -> Result<i32, diesel::result::Error> {
|
||||
let jrtable = NewJrtable { name: tblname.to_string(), owner_id: uid };
|
||||
|
||||
let tbl = diesel::insert_into(crate::schema::jrtables::table)
|
||||
|
|
@ -24,7 +24,7 @@ pub fn create_jrtable(conn: &mut MysqlConnection, tblname: &String, field_names:
|
|||
return Err(tblid.err().unwrap());
|
||||
}
|
||||
let tblid = tblid.unwrap();
|
||||
let cols = jrcolumns::create_jrcolumns_empty_tbl(conn, tblid, field_names);
|
||||
let cols = jrcolumns::create_jrcolumns_empty_tbl(conn, tblid, field_names, field_types);
|
||||
if cols.is_err() {
|
||||
return Err(cols.err().unwrap());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![recursion_limit = "512"]
|
||||
//! Database API using the table style system provided by this crate
|
||||
mod models;
|
||||
mod schema;
|
||||
|
|
@ -17,6 +18,8 @@ use diesel::deserialize::FromSql;
|
|||
use diesel::deserialize;
|
||||
use diesel::backend::Backend;
|
||||
use diesel::sql_types::Integer;
|
||||
use diesel::expression::Expression;
|
||||
|
||||
|
||||
#[derive(PartialEq, Clone, Copy, diesel::FromSqlRow)]
|
||||
#[repr(i32)]
|
||||
|
|
@ -25,6 +28,10 @@ pub enum FIELDTYPE {
|
|||
NUMBER = 1,
|
||||
}
|
||||
|
||||
impl Expression for FIELDTYPE {
|
||||
type SqlType = Integer;
|
||||
}
|
||||
|
||||
impl<DB> FromSql<Integer, DB> for FIELDTYPE
|
||||
where DB: Backend, i32: FromSql<Integer,DB> {
|
||||
fn from_sql(bytes: DB::RawValue<'_>) -> deserialize::Result<Self> {
|
||||
|
|
@ -35,6 +42,15 @@ where DB: Backend, i32: FromSql<Integer,DB> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<isize> for FIELDTYPE {
|
||||
fn from(value: isize) -> Self {
|
||||
match value {
|
||||
1 => FIELDTYPE::NUMBER,
|
||||
x => FIELDTYPE::TEXT,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// represents and summarised all relevant data of a table.
|
||||
/// Standard return type if whole tables should be returned
|
||||
|
|
@ -169,7 +185,7 @@ pub fn search_table(tbl: Tbl, search_fields: Vec<i32>, search_value: String) ->
|
|||
let mut field_sets = HashSet::new();
|
||||
for field in search_fields {
|
||||
for row in &rows {
|
||||
if row.cells[field as usize].contains(&search_value) {
|
||||
if row.cells[field as usize].to_lowercase().contains(&search_value) {
|
||||
field_sets.insert(row.clone());
|
||||
}
|
||||
}
|
||||
|
|
@ -241,11 +257,11 @@ pub fn delete_table(conn: &mut MysqlConnection, tblid: i32, uid: i32) -> Option<
|
|||
/// Returns the id of the newly created table if the action seems to have been successful and the requesting user is the
|
||||
/// table's owner.
|
||||
/// None otherwise.
|
||||
pub fn create_table(conn: &mut MysqlConnection, name: String, field_names: Vec<String>, uid: i32) -> Option<i32> {
|
||||
pub fn create_table(conn: &mut MysqlConnection, name: String, field_names: Vec<String>, field_types: Vec<crate::FIELDTYPE>, uid: i32) -> Option<i32> {
|
||||
if jrtables::get_tbl_by_name_uid(conn, &name, uid).is_ok() {
|
||||
return None;
|
||||
}
|
||||
if jrtables::create_jrtable(conn, &name, field_names, uid).is_err() {
|
||||
if jrtables::create_jrtable(conn, &name, field_names, field_types, uid).is_err() {
|
||||
return None;
|
||||
}
|
||||
let tblid = jrtables::get_tbl_by_name_uid(conn, &name, uid);
|
||||
|
|
@ -327,8 +343,21 @@ pub fn move_row(conn: &mut MysqlConnection, tblid: i32, rowpos: i32, newpos: i32
|
|||
Some(true)
|
||||
}
|
||||
|
||||
pub fn edit_row(conn: &mut MysqlConnection, tblid: i32, cells: Vec<String>, row_pos: i32, uid: i32) -> Option<bool> {
|
||||
let owner = jrtables::get_owner_id(conn, tblid);
|
||||
if owner.is_err() || owner.unwrap() != uid {
|
||||
return None;
|
||||
}
|
||||
for (i, cell) in cells.iter().enumerate() {
|
||||
if jrcells::change_jrcell_value_relative(conn, tblid, row_pos, (i+1) as i32, cell).is_err() {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Some(true)
|
||||
}
|
||||
|
||||
pub fn edit_cell(conn: &mut MysqlConnection, tblid: i32, row_pos: i32, column_pos: i32, new_value: String, uid: i32) -> Option<bool> {
|
||||
|
||||
pub fn edit_cell(conn: &mut MysqlConnection, tblid: i32, row_pos: i32, column_pos: i32, new_value: &String, uid: i32) -> Option<bool> {
|
||||
let owner = jrtables::get_owner_id(conn, tblid);
|
||||
if owner.is_err() ||
|
||||
owner.unwrap() != uid ||
|
||||
|
|
@ -350,11 +379,11 @@ pub fn move_column(conn: &mut MysqlConnection, tblid:i32, column_pos: i32, new_c
|
|||
Some(true)
|
||||
}
|
||||
|
||||
pub fn add_column(conn: &mut MysqlConnection, tblid: i32, name: String, uid: i32) -> Option<i32> {
|
||||
pub fn add_column(conn: &mut MysqlConnection, tblid: i32, name: String, clmtype: FIELDTYPE, uid: i32) -> Option<i32> {
|
||||
let owner = jrtables::get_owner_id(conn, tblid);
|
||||
if owner.is_err() ||
|
||||
owner.unwrap() != uid ||
|
||||
jrcolumns::create_jrcolumn(conn, tblid, name).is_err()
|
||||
jrcolumns::create_jrcolumn(conn, tblid, name, clmtype).is_err()
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ pub struct Jrcolumn {
|
|||
pub struct NewJrcolumn {
|
||||
pub name: String,
|
||||
pub jrtable_id: i32,
|
||||
pub column_type: i32,
|
||||
pub id_in_table: i32,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -65,8 +65,9 @@ async fn rocket() -> _ {
|
|||
.attach(Db::fairing())
|
||||
.attach(OAuth2::<auth::RanderathIdentity>::fairing("oauth"))
|
||||
.mount("/", routes![auth::oauth_login, auth::oauth_callback, home, login_home])
|
||||
.mount("/table", routes![table::table, table::table_sec, table::edit_tname, table::create, table::import_table])
|
||||
.mount("/entry", routes![table::new_entry])
|
||||
.mount("/table", routes![table::table, table::table_sec, table::edit_tname, table::create, table::import_table, table::delete_table])
|
||||
.mount("/row", routes![table::new_entry, table::edit_entry, table::delete_entry])
|
||||
.mount("/column", routes![table::delete_column])
|
||||
.register("/", catchers![auth::redirect_to_login])
|
||||
.mount("/static", FileServer::from(relative!("static")))
|
||||
|
||||
|
|
|
|||
83
src/table.rs
83
src/table.rs
|
|
@ -9,8 +9,13 @@ 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 NewEntry {
|
||||
|
|
@ -18,6 +23,19 @@ struct NewEntry {
|
|||
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,
|
||||
|
|
@ -27,7 +45,8 @@ struct EditTname {
|
|||
#[derive(FromForm)]
|
||||
struct NewTable {
|
||||
name: String,
|
||||
fields: Vec<String>,
|
||||
field_names: Vec<String>,
|
||||
field_types: Vec<isize>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
|
|
@ -38,6 +57,11 @@ struct ImportTable {
|
|||
rows: Vec<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(FromForm)]
|
||||
struct DeleteTable {
|
||||
tblid: i32,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
struct JRows {
|
||||
|
|
@ -109,7 +133,8 @@ pub async fn table(conn: Db, tid: i32, sort_dir: Option<u8>, sort_field: Option<
|
|||
let table = inventur_db::sort_table(table, sortfield, sortdir);
|
||||
|
||||
let tname = table.name;
|
||||
let columns = table.column_names;
|
||||
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;
|
||||
|
||||
|
|
@ -124,13 +149,24 @@ pub async fn table(conn: Db, tid: i32, sort_dir: Option<u8>, sort_field: Option<
|
|||
tblname: tname,
|
||||
tids: tids,
|
||||
tnames: tnames,
|
||||
columns: columns,
|
||||
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;
|
||||
|
|
@ -142,6 +178,40 @@ pub async fn new_entry(conn: Db, data: Form<NewEntry>, user: AuthUser) -> Result
|
|||
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("/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;
|
||||
|
|
@ -155,7 +225,7 @@ pub async fn edit_tname(conn: Db, data: Form<EditTname>, user: AuthUser) -> Resu
|
|||
#[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.fields.clone(), uid)).await;
|
||||
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);
|
||||
}
|
||||
|
|
@ -167,7 +237,7 @@ pub async fn import_table(conn: Db, data: Json<ImportTable>, user: AuthUser) ->
|
|||
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, uid)).await;
|
||||
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);
|
||||
}
|
||||
|
|
@ -177,4 +247,3 @@ pub async fn import_table(conn: Db, data: Json<ImportTable>, user: AuthUser) ->
|
|||
}
|
||||
Ok(Redirect::to(uri!("/table", table_sec(tblid))))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,193 +1,239 @@
|
|||
{# vim: set filetype=html :#}
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
This file is part of Inventory.
|
||||
This file is part of Inventory.
|
||||
|
||||
Inventory 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.
|
||||
Inventory 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.
|
||||
|
||||
Foobar 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 General Public License for more details.
|
||||
Foobar 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with Inventory. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
You should have received a copy of the GNU General Public License along with Inventory. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<html>
|
||||
<head lang="en">
|
||||
<meta name="author" content="Johannes Randerath" />
|
||||
<meta charset="utf-8" />
|
||||
<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 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">
|
||||
<script src="https://unpkg.com/papaparse@latest/papaparse.min.js"></script>
|
||||
<title>Inventory - {% block pagename %}{% endblock pagename %}</title>
|
||||
<script>
|
||||
function create_table() {
|
||||
document.getElementById('create_table').submit().clear();
|
||||
document.getElementById('dynamic_columns').innerHTML = "";
|
||||
ncol = 1;
|
||||
<meta name="author" content="Johannes Randerath" />
|
||||
<meta charset="utf-8" />
|
||||
<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 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">
|
||||
<script src="https://unpkg.com/papaparse@latest/papaparse.min.js"></script>
|
||||
<title>Inventory - {% block pagename %}{% endblock pagename %}</title>
|
||||
<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);
|
||||
}
|
||||
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();
|
||||
}
|
||||
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 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;
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{% block script %}{% endblock script %}
|
||||
</head>
|
||||
};
|
||||
}
|
||||
});
|
||||
};
|
||||
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>
|
||||
<body>
|
||||
<nav class='navbar navbar-expand-lg bg-body bg-body-tertiary'>
|
||||
<div class='container-fluid'>
|
||||
<a class='navbar-brand' href='/'>Inventory</a>
|
||||
<button class='navbar-toggler' type='button' data-bs-toggle='collapse' data-bs-target='#navbarSupportedContent' aria-controls='navbarSupportedContent' aria-expanded='false' aria-label='Toggle navigation'>
|
||||
<span class='navbar-toggler-icon'></span>
|
||||
</button>
|
||||
<div class='collapse navbar-collapse' id='navbarSupportedContent'>
|
||||
<ul class='navbar-nav me-auto mb-2 mb-lg-0'>
|
||||
<li class='nav-item dropdown'>
|
||||
<a class='nav-link dropdown-toggle' role='button' data-bs-toggle='dropdown' aria-expanded='false'>
|
||||
Choose Table
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
{% for tid in tids %}
|
||||
<li class="dropdown-option"><a class="nav-link" href="/table/{{ tid }}">{{ tnames[loop.index0] }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" role="button" data-bs-toggle="modal" data-bs-target="#new_table">
|
||||
<i class="bi bi-plus-lg"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" role="button" data-bs-toggle="modal" data-bs-target="#import_table">
|
||||
<i class="bi bi-folder2-open"></i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<nav class='navbar navbar-expand-lg bg-body bg-body-tertiary'>
|
||||
<div class='container-fluid'>
|
||||
<a class='navbar-brand' href='/'>Inventory</a>
|
||||
<button class='navbar-toggler' type='button' data-bs-toggle='collapse' data-bs-target='#navbarSupportedContent' aria-controls='navbarSupportedContent' aria-expanded='false' aria-label='Toggle navigation'>
|
||||
<span class='navbar-toggler-icon'></span>
|
||||
</button>
|
||||
<div class='collapse navbar-collapse' id='navbarSupportedContent'>
|
||||
<ul class='navbar-nav me-auto mb-2 mb-lg-0'>
|
||||
<li class='nav-item dropdown'>
|
||||
<a class='nav-link dropdown-toggle' role='button' data-bs-toggle='dropdown' aria-expanded='false'>
|
||||
Choose Table
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
{% for tid in tids %}
|
||||
<li class="dropdown-option"><a class="nav-link" href="/table/{{ tid }}">{{ tnames[loop.index0] }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" role="button" data-bs-toggle="modal" data-bs-target="#new_table">
|
||||
<i class="bi bi-plus-lg"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" role="button" data-bs-toggle="modal" data-bs-target="#import_table">
|
||||
<i class="bi bi-folder2-open"></i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
{% block body %}{% endblock body %}
|
||||
|
||||
{% block body %}{% endblock body %}
|
||||
|
||||
<!-- Modals -->
|
||||
<!-- 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="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="fields">
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<label for="field_name" class="form-label">Data type</label>
|
||||
<select class="form-select" aria-label="data type">
|
||||
<option value="text" selected>Text</option>
|
||||
<option value="number">Number</option>
|
||||
<option value="date">Date</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>
|
||||
<!-- Modals -->
|
||||
<!-- 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="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 -->
|
||||
<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>
|
||||
<!-- 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>
|
||||
|
||||
<!-- 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>
|
||||
{% 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>
|
||||
<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>
|
||||
|
|
|
|||
|
|
@ -2,232 +2,303 @@
|
|||
|
||||
{% extends "base" %}
|
||||
{% block pagename %}Table{% endblock pagename %}
|
||||
{% block script %}
|
||||
<script type="text/javascript">
|
||||
let ncol = 1;
|
||||
let edit_mode = false;
|
||||
document.addEventListener("DOMContentLoaded", (event) => {
|
||||
for (let row of document.getElementById("content_table").getElementsByTagName('tbody')[0].rows) {
|
||||
let createClickHandler = function(r) {
|
||||
return function() {
|
||||
$('#edit_item').modal('show');
|
||||
};
|
||||
};
|
||||
row.onclick = createClickHandler(row);
|
||||
}
|
||||
});
|
||||
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='fields' id='field_name' aria-describedby='field_name_label'>
|
||||
</div>
|
||||
<div class='col-auto'>
|
||||
<select class='form-select' aria-label='data type'>
|
||||
<option value='text' selected>Text</option>
|
||||
<option value='number'>Number</option>
|
||||
<option value='date'>Date</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
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_tname' 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'>
|
||||
<button class='btn btn-success' type='button' onclick='toggle_edit_tname()'>
|
||||
<i class='bi bi-check'></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>`;
|
||||
edit_mode = true;
|
||||
} else {
|
||||
document.getElementById('form_edit_tname').submit().clear();
|
||||
span.innerHTML = '{{ tblname }}';
|
||||
edit_mode = false;
|
||||
}
|
||||
}
|
||||
function add_entry() {
|
||||
document.getElementById('form_add_entry').submit().clear();
|
||||
|
||||
{% 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;
|
||||
}
|
||||
</script>
|
||||
{% endblock script %}
|
||||
{% block body %}
|
||||
<div class="row justify-content-between">
|
||||
<div class="col-auto">
|
||||
<h1><div class='input-group'><span id="tname">{{ tblname }}</span><button class="btn" type="button" onclick="toggle_edit_tname();"><i class="bi bi-pencil"></i></button></div></h1>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="btn-toolbar mt-2" role="toolbar">
|
||||
<div class="btn-group me-2">
|
||||
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#new_item"><i class="bi bi-plus-lg"></i></button>
|
||||
</div>
|
||||
<form method="get" action="/table/{{ tblid }}">
|
||||
<div class="input-group">
|
||||
<div class="input-group-text dropdown-toggle" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="bi bi-table"></i>
|
||||
</div>
|
||||
<input id="input_search_value" name="search_value" type="text" placeholder="Search" value="{{ search_value }}" class="form-control">
|
||||
<button class="btn btn-outline-primary" type="submit"><i class="bi bi-search"></i></button>
|
||||
<ul class="dropdown-menu">
|
||||
<select name="search_fields" class="form-select" multiple>
|
||||
<li><option disabled class="dropdown-header">Search in</option></li>
|
||||
{% for column in columns %}
|
||||
<li><option class="dropdown-option" value="{{ loop.index - 1 }}" {% if (loop.index - 1) in search_fields %}selected{% endif %}>{{ column }}</option></li>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<table id="content_table" class="table table-striped table-hover table-responsive table-bordered mt-2">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:7%">
|
||||
<div class="row justify-content-between">
|
||||
<div class="col-auto">
|
||||
#
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="btn-toolbar">
|
||||
<button class="btn p-0 me-2 mdl" type="button" data-bs-toggle="modal" data-bs-target="#filter"><i class="bi bi-filter"></i></button>
|
||||
<form method="GET" action="/table/{{ tblid }}">
|
||||
<input value="0" name="sort_field" 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 %}
|
||||
{% set dir='up' %}
|
||||
{% else %}
|
||||
{% set dir='down' %}
|
||||
{% endif %}
|
||||
<button class="btn p-0" type="submit"><i class="bi bi-sort-{{ dir }}"></i></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</th>
|
||||
{% for column in columns %}
|
||||
<th>
|
||||
<div class="row justify-content-between">
|
||||
<div class="col-auto">
|
||||
{{ column }}
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="btn-toolbar">
|
||||
<button class="btn p-0 me-2" type="button" data-bs-toggle="modal" data-bs-target="#filter" onclick="set_modal_target('{{ column }}');"><i class="bi bi-filter"></i></button>
|
||||
<form method="GET" action="/table/{{ tblid }}">
|
||||
<input value="{{ loop.index }}" name="sort_field" hidden />
|
||||
<input value="{% if sort_field == loop.index %}{{ (sort_dir + 1) % 2}}{% else %}0{% endif %}" name="sort_dir" hidden />
|
||||
{% if sort_field == loop.index and sort_dir == 0 %}
|
||||
{% set dir='up' %}
|
||||
{% else %}
|
||||
{% set dir='down'%}
|
||||
{% endif %}
|
||||
<button class="btn p-0" type="submit"><i class="bi bi-sort-{{ dir }}"></i></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for row in rows %}
|
||||
<tr>
|
||||
{% for column in row %}
|
||||
<td>{{ column }}</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
};
|
||||
};
|
||||
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;
|
||||
}
|
||||
</script>
|
||||
{% endblock script %}
|
||||
{% block body %}
|
||||
<div class="row justify-content-between">
|
||||
<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>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="btn-toolbar mt-2" role="toolbar">
|
||||
<div class="btn-group me-2">
|
||||
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#new_item"><i class="bi bi-plus-lg"></i></button>
|
||||
</div>
|
||||
<form method="get" action="/table/{{ tblid }}">
|
||||
<div class="input-group">
|
||||
<div class="input-group-text dropdown-toggle" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="bi bi-table"></i>
|
||||
</div>
|
||||
<input id="input_search_value" name="search_value" type="text" placeholder="Search" value="{{ search_value }}" class="form-control">
|
||||
<button class="btn btn-outline-primary" type="submit"><i class="bi bi-search"></i></button>
|
||||
<ul class="dropdown-menu">
|
||||
<select name="search_fields" class="form-select" multiple>
|
||||
<li><option disabled class="dropdown-header">Search in</option></li>
|
||||
{% 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>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<table id="content_table" class="table table-striped table-hover table-responsive table-bordered mt-2">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:7%">
|
||||
<div class="row justify-content-between">
|
||||
<div class="col-auto">
|
||||
#
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="btn-toolbar">
|
||||
<form method="GET" action="/table/{{ tblid }}">
|
||||
<input value="0" name="sort_field" 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 %}
|
||||
{% set dir='up' %}
|
||||
{% else %}
|
||||
{% set dir='down' %}
|
||||
{% endif %}
|
||||
<button class="btn p-0" type="submit"><i class="bi bi-sort-{{ dir }}"></i></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</th>
|
||||
{% for column in column_names %}
|
||||
<th>
|
||||
<div class="row justify-content-between">
|
||||
<div class="col-auto">
|
||||
{{ column }}
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="btn-toolbar">
|
||||
<button class="btn p-0 me-2" type="button" data-bs-toggle="modal" data-bs-target="#edit_column_modal" onclick="edit_column({{ loop.index0 }});"><i class="bi bi-pencil"></i></button>
|
||||
<form method="GET" action="/table/{{ tblid }}">
|
||||
<input value="{{ loop.index }}" name="sort_field" hidden />
|
||||
<input value="{% if sort_field == loop.index %}{{ (sort_dir + 1) % 2}}{% else %}0{% endif %}" name="sort_dir" hidden />
|
||||
{% if sort_field == loop.index and sort_dir == 0 %}
|
||||
{% set dir='up' %}
|
||||
{% else %}
|
||||
{% set dir='down'%}
|
||||
{% endif %}
|
||||
<button class="btn p-0" type="submit"><i class="bi bi-sort-{{ dir }}"></i></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for row in rows %}
|
||||
<tr>
|
||||
{% for column in row %}
|
||||
<td>{{ column }}</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endblock body %}
|
||||
|
||||
{% block more_modals %}
|
||||
<!-- Add new item -->
|
||||
<div class="modal fade" id="new_item" 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"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<h5>Cell values:</h5>
|
||||
<br>
|
||||
<form id="form_add_entry" action="/entry/new" method="post">
|
||||
<input name="tblid" value="{{ tblid }}" hidden />
|
||||
{% for column in columns %}
|
||||
<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">
|
||||
<button class="btn btn-secondary" data-bs-dismiss="modal" type="button">Close</button>
|
||||
<button class="btn btn-primary" type="button" onclick="add_entry()">Add</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Add new item -->
|
||||
<div class="modal fade" id="new_item" 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"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<h5>Cell values:</h5>
|
||||
<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 item -->
|
||||
<div class="modal fade" id="edit_item" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5">Edit entry</h1>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
</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">Save</button>
|
||||
</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"></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>
|
||||
|
||||
<!-- Filter column -->
|
||||
<div class="modal fade" id="filter" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5">Edit filter on <span id="modal_caller"></span></h1>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
</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">Filter</button>
|
||||
</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>
|
||||
<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 %}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user