Big update to front end/database

This commit is contained in:
Jadowyne Ulve 2024-10-07 19:00:39 -05:00
parent c1b13a74c8
commit 9a57fa7ad6
14 changed files with 438 additions and 34 deletions

Binary file not shown.

Binary file not shown.

46
api.py
View File

@ -1,5 +1,5 @@
from flask import Blueprint, request, render_template, redirect, session, url_for, send_file, jsonify, Response from flask import Blueprint, request, render_template, redirect, session, url_for, send_file, jsonify, Response
import psycopg2 import psycopg2, math
from config import config from config import config
database_api= Blueprint('database_api', __name__) database_api= Blueprint('database_api', __name__)
@ -9,18 +9,58 @@ def pagninate_items():
print("hello") print("hello")
page = int(request.args.get('page', 1)) page = int(request.args.get('page', 1))
limit = int(request.args.get('limit', 10)) limit = int(request.args.get('limit', 10))
search_string = str(request.args.get('search_text', ""))
offset = (page - 1) * limit offset = (page - 1) * limit
pantry_inventory = [] pantry_inventory = []
count = 0
database_config = config() database_config = config()
with psycopg2.connect(**database_config) as conn: with psycopg2.connect(**database_config) as conn:
try: try:
with conn.cursor() as cur: with conn.cursor() as cur:
sql = f"SELECT * FROM main_items LIMIT %s OFFSET %s;" if search_string != "":
sql = f"SELECT * FROM main_items LEFT JOIN main_logistics_info ON main_items.logistics_info_id = main_logistics_info.id WHERE search_string LIKE '%{search_string}%' LIMIT {limit} OFFSET {offset};"
cur.execute(sql)
pantry_inventory = cur.fetchall()
cur.execute(f"SELECT COUNT(*) FROM main_items WHERE search_string LIKE '%{search_string}%';")
count = cur.fetchone()[0]
else:
sql = f"SELECT * FROM main_items LEFT JOIN main_logistics_info ON main_items.logistics_info_id = main_logistics_info.id LIMIT %s OFFSET %s;"
cur.execute(sql, (limit, offset)) cur.execute(sql, (limit, offset))
pantry_inventory = cur.fetchall() pantry_inventory = cur.fetchall()
cur.execute("SELECT COUNT(*) FROM main_items;")
count = cur.fetchone()[0]
print(sql)
print(count, math.ceil(count/limit))
except (Exception, psycopg2.DatabaseError) as error: except (Exception, psycopg2.DatabaseError) as error:
print(error) print(error)
return jsonify({'items': pantry_inventory}) pantry_inventory = sorted(pantry_inventory, key=lambda x: x[2])
return jsonify({'items': pantry_inventory, "end": math.ceil(count/limit)})
@database_api.route("/getItem")
def get_item():
id = int(request.args.get('id', 1))
database_config = config()
site_name = "main"
item = []
with psycopg2.connect(**database_config) as conn:
try:
with conn.cursor() as cur:
with open(f"sites/{site_name}/sql/unique/select_item_all.sql", "r+") as file:
sql = file.read()
cur.execute(sql, (id, ))
item = list(cur.fetchone())
item[5] = {'walmart': 'https://www.walmart.com/ip/Ptasie-Mleczko-Chocolate-Covered-Vanilla-Marshmallow-birds-milk-chocolate-13-4-Oz-Includes-Our-Exclusive-HolanDeli-Chocolate-Mints/965551629?classType=REGULAR&from=/search', 'target': 'https://www.target.com/p/hershey-39-s-cookies-39-n-39-cr-232-me-fangs-halloween-candy-snack-size-9-45oz/-/A-79687769#lnk=sametab'}
item[22] = ['test_list', 'main_list']
item[23] = ['test_recipe',]
item[24] = ['test_group', 'main_group', 'test2_group']
except (Exception, psycopg2.DatabaseError) as error:
print(error)
return render_template(f"item_page/index.html", item=item)

View File

@ -4,7 +4,7 @@ CREATE TABLE IF NOT EXISTS %sitename%_items(
item_name VARCHAR(255) NOT NULL, item_name VARCHAR(255) NOT NULL,
brand INTEGER, brand INTEGER,
tags TEXT [], tags TEXT [],
links TEXT [], links JSONB,
item_info_id INTEGER NOT NULL, item_info_id INTEGER NOT NULL,
logistics_info_id INTEGER NOT NULL, logistics_info_id INTEGER NOT NULL,
food_info_id INTEGER, food_info_id INTEGER,

View File

@ -0,0 +1,9 @@
CREATE TABLE IF NOT EXISTS %sitename%_receipt_items (
id SERIAL PRIMARY KEY,
type VARCHAR(255) NOT NULL,
barcode VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL,
qty FLOAT8 NOT NULL,
data JSONB,
status VARCHAR (64)
)

View File

@ -0,0 +1,10 @@
CREATE TABLE IF NOT EXISTS %sitename%_receipts (
id SERIAL PRIMARY KEY,
receipt_id INTEGER NOT NULL,
receipt_status VARCHAR (64) NOT NULL,
date_submitted TIMESTAMP NOT NULL,
submitted_by INTEGER NOT NULL,
vendor_id INTEGER,
files JSONB,
UNIQUE(receipt_id)
);

View File

@ -0,0 +1,12 @@
CREATE TABLE IF NOT EXISTS %sitename%_recipes (
id SERIAL PRIMARY KEY,
name VARCHAR,
author INTEGER,
description TEXT,
creation_date TIMESTAMP,
custom_items JSONB,
pantry_items JSONB,
group_items JSONB,
instructions TEXT [],
picture_path TEXT
);

View File

@ -0,0 +1,13 @@
CREATE TABLE IF NOT EXISTS %sitename%_shopping_lists (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description TEXT,
pantry_items JSONB,
custom_items JSONB,
recipes JSONB,
groups JSONB,
author INTEGER,
creation_date TIMESTAMP,
type VARCHAR(64),
UNIQUE(name)
);

View File

@ -0,0 +1,8 @@
CREATE TABLE IF NOT EXISTS %sitename%_vendors (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
address VARCHAR(255),
creation_date TIMESTAMP NOT NULL,
created_by TIMESTAMP NOT NULL,
phone_number VARCHAR(32)
)

View File

@ -0,0 +1,44 @@
SELECT * FROM %sitename%_items
LEFT JOIN %sitename%_logistics_info ON %sitename%_items.logistics_info_id = %sitename%_logistics_info.id
LEFT JOIN %sitename%_item_info ON %sitename%_items.item_info_id = %sitename%_item_info.id
LEFT JOIN %sitename%_food_info ON %sitename%_items.food_info_id = %sitename%_food_info.id
WHERE %sitename%_items.id=%s;
/*
00 - item_id
01 - barcode
02 - item_name
03 - brand (id)
04 - tags
05 - links
06 - item_info_id
07 - logistics_info_id
08 - food_info_id
09 - row_type
10 - item_type
11 - search_string
12 - logistics_info_id
13 - barcode
14 - primary_location
15 - auto_issue_location
16 - dynamic_locations
17 - location_data
18 - quantity_on_hand
19 - item_info_id
20 - barcode
21 - linked_items
22 - shopping_lists
23 - recipes
24 - groups
25 - packaging
26 - uom
27 - cost
28 - safety_stock
29 - lead_time_days
30 - ai_pick
31 - food_info_id
32 - food_groups
33 - ingrediants
34 - nutrients
35 - expires
*/

View File

@ -0,0 +1,5 @@
SELECT * FROM main_items
LEFT JOIN main_logistics_info ON main_items.logistics_info_id = main_logistics_info.id
LEFT JOIN main_item_info ON main_items.item_info_id = main_item_info.id
LEFT JOIN main_food_info ON main_items.food_info_id = main_food_info.id
WHERE main_items.id=%s;

View File

@ -9,71 +9,196 @@
</head> </head>
<body> <body>
<div class="container"> <div class="container section">
<div class="row">
<div class="col s9 m6 offset-m3 input-field outlined align-center">
<i class="material-icons prefix">search</i>
<input style="border-radius: 20px; border: 1px solid #ccc;" id="search" name="search" type="search" placeholder="Search" value="">
</div>
<div class="col s3">
<a class="btn waves-effect waves-light center-align right tooltipped" data-position="bottom" data-tooltip="Open up filter options." style="margin-top: 5px;" onclick="hideFilters()"><i class="material-icons">tune</i></a>
</div>
<div class="col s12 hide" id="filter_options">
<div class="row"> <div class="row">
<div class="col s12"> <div class="col s12">
<ul class="collection" id="collection_list"> <p>Item Limit</p>
<li class="collection-item">Alvin</li> </div>
<li class="collection-item">Alvin</li> <div class="col s2">
<li class="collection-item">Alvin</li> <label>
<li class="collection-item">Alvin</li> <input name="group1" type="radio" checked onclick="changeLimit(25)"/>
<span>25 items</span>
</label>
</div>
<div class="col s2">
<label>
<input name="group1" type="radio" onclick="changeLimit(50)"/>
<span>50 items</span>
</label>
</div>
<div class="col s2">
<label>
<input name="group1" type="radio" onclick="changeLimit(75)"/>
<span>75 itesm</span>
</label>
</div>
<div class="col s2">
<label>
<input name="group1" type="radio" onclick="changeLimit(100)"/>
<span>100 items</span>
</label>
</div>
<div class="col s12 divider"></div>
</div>
</div>
<div class="col s12">
<div class="collection" id="collection_list">
</div>
</div>
<div class="col s12 center" id="pagination_list">
<ul class="pagination">
<li id="first" class="waves-effect"><a><i class="material-icons">first_page</i></a></li>
<li id="back" class="waves-effect"><a><i class="material-icons">chevron_left</i></a></li>
<li id="current_page" class="flow-text">page_number</li>
<li id="forward" class="waves-effect"><a><i class="material-icons">chevron_right</i></a></li>
<li id="last" class="waves-effect"><a><i class="material-icons">last_page</i></a></li>
</ul> </ul>
</div> </div>
<div class="divider col s12"></div>
<div class="col s12 center">
<a id="back" class="btn icon-left" href="#!"><i class="material-icons">chevron_left</i></a>
<a id="forward" class="btn icon-right"><i class="material-icons">chevron_right</i></a>
</div>
</div> </div>
</div> </div>
</body> </body>
<script> <script>
let current_page = 1 let current_page = 1
let limit = 25 let end_page = 10
const checked_items = new Array(); let limit = 50
let instructions = new Array() let filter_state = "hidden"
let searchText = ""
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
update_list() update_list()
}); });
document.addEventListener('DOMContentLoaded', function() {
var elems = document.querySelectorAll('.modal');
var instances = M.Modal.init(elems, {
// specify options here
});
M.AutoInit();
});
function update_list(){ function update_list(){
console.log(current_page)
if (current_page === 1){
document.getElementById('back').classList.add("disabled")
document.getElementById('back').classList.remove("waves-effect")
document.getElementById('first').classList.add("disabled")
document.getElementById('first').classList.remove("waves-effect")
} else {
document.getElementById('back').classList.remove("disabled")
document.getElementById('back').classList.add("waves-effect")
document.getElementById('first').classList.remove("disabled")
document.getElementById('first').classList.add("waves-effect")
};
const url = new URL('/getItems', window.location.origin); const url = new URL('/getItems', window.location.origin);
url.searchParams.append('page', current_page); url.searchParams.append('page', current_page);
url.searchParams.append('limit', limit); url.searchParams.append('limit', limit);
url.searchParams.append('search_text', searchText);
fetch(url) fetch(url)
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
if (data.items.length < limit){
end_page = parseInt(data.end)
if (current_page === end_page){
document.getElementById('forward').classList.add("disabled") document.getElementById('forward').classList.add("disabled")
document.getElementById('forward').classList.remove("waves-effect")
document.getElementById('last').classList.add("disabled")
document.getElementById('last').classList.remove("waves-effect")
} else { } else {
document.getElementById('forward').classList.remove("disabled") document.getElementById('forward').classList.remove("disabled")
} document.getElementById('forward').classList.add("waves-effect")
document.getElementById('last').classList.remove("disabled")
document.getElementById('last').classList.add("waves-effect")
};
const collection = document.getElementById('collection_list'); const collection = document.getElementById('collection_list');
collection.style = "border: 0px; gap: 12px;"
while(collection.firstChild){ while(collection.firstChild){
collection.removeChild(collection.firstChild); collection.removeChild(collection.firstChild);
} }
data.items.forEach(item => { data.items.forEach(item => {
const collection_item = document.createElement('li'); const collection_item = document.createElement('a');
collection_item.classList.add('collection-item'); collection_item.classList.add('collection-item');
collection_item.innerHTML = `<div>${item[2]} collection_item.classList.add('truncate');
<a href="#!" class="btn-small right" style="position: relative; bottom: 5px;"><i class="material-icons">send</i></a>
<a href="#!" class="btn-small right" style="position: relative; bottom: 5px; right: 5px;"><i class="material-icons">edit</i></a>
</div>`; collection_item.href = `/getItem?id=${item[0]}`
collection_item.style = "border-radius: 20px; border: 1px solid #ccc; margin-top: 5px;"
let qty = item[18]
let roundedQty = qty.toFixed(2)
collection_item.innerHTML = `${item[2]}<span class="badge hide-on-small-only indigo lighten-4" style="border-radius: 20px;" data-badge-caption="">${roundedQty}</span>`;
collection.appendChild(collection_item); collection.appendChild(collection_item);
}) })
document.getElementById("current_page").innerHTML = `${String(current_page)} / ${String(end_page)}`
}) })
}; };
function changeLimit(limit_value){
limit = limit_value
current_page = 1
update_list()
};
function hideFilters(){
if (filter_state == "hidden"){
document.getElementById("filter_options").classList.remove("hide");
filter_state = "shown";
} else {
document.getElementById("filter_options").classList.add("hide");
filter_state = "hidden";
}
}
document.getElementById('forward').addEventListener('click', () =>{ document.getElementById('forward').addEventListener('click', () =>{
if (!(document.getElementById("forward").classList.contains("disabled"))){
current_page++ current_page++
update_list(); update_list();
};
}); });
document.getElementById('back').addEventListener('click', () =>{ document.getElementById('back').addEventListener('click', () =>{
if (!(document.getElementById("back").classList.contains("disabled"))){
current_page-- current_page--
update_list(); update_list();
}
});
document.getElementById('last').addEventListener('click', () =>{
if(!(document.getElementById("last").classList.contains("disabled"))){
current_page = end_page
update_list();
};
});
document.getElementById('first').addEventListener('click', () =>{
if (!(document.getElementById("first").classList.contains("disabled"))){
current_page = 1
update_list();
};
});
document.querySelector("#search").addEventListener('change', () =>{
searchText = document.getElementById("search").value;
current_page = 1
update_list()
}); });
</script> </script>

View File

@ -0,0 +1,138 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
<title id="title"></title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@materializecss/materialize@2.0.3-alpha/dist/css/materialize.min.css" />
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/@materializecss/materialize@2.0.3-alpha/dist/js/materialize.min.js"></script>
</head>
<body>
<div class="container section">
<div class="row g-4">
<div class="col s12">
<h3>{{item[2]}}</h3>
<h5>Database ID: {{item[0]}}</h5>
<h5>Barcode: {{item[1]}}</h5>
</div>
<div class="s12 m6" style="margin-right: 5px;">
<label for="entry_type">Entry Type</label>
<select id="entry_type" class="browser-default" >
<option value="" disabled selected>Choose your option</option>
<option value="item">item</option>
<option value="linked list">linked list</option>
</select>
</div>
<div class="s12 m6">
<label for="item_type">Item Type</label>
<select id="item_type" class="browser-default">
<option value="" disabled selected>Choose your option</option>
<option value="FOOD">FOOD</option>
<option value="FOOD (PLU)">FOOD (PLU)</option>
<option value="OTHER">OTHER</option>
</select>
</div>
<!-- Weblinks Input perhaps a modal to add a link or a text input..?-->
<div class="divider col s12" style="margin-top: 5px;"></div>
<div class="col s12">
<p style="font-weight: bold; font-size: 16px; margin-top: 5px;">Links</p>
<div id="weblinks">
</div>
</div>
<div class="divider col s12" style="margin-top: 5px;"></div>
<div class="col s12">
<div class="row">
<div class="col s12">
<ul class="tabs tabs-fixed-width" id="info_tabs" style="background-color: white;">
<li class="tab col s3"><a class="active" href="#item_info">Item Info</a></li>
<li class="tab col s3"><a href="#food_info">Food Info</a></li>
<li class="tab col s3"><a href="#logistics_info">Logistics Info</a></li>
<li class="tab col s3 disabled"><a href="#linked_items">Linked Items</a></li>
</ul>
</div>
<div id="item_info" class="col s12">
<table class="" id="reference_table">
<thead>
<tr>
<th>Reference Type</th>
<th>Reference Name</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
<div id="food_info" class="col s12">Food Info</div>
<div id="logistics_info" class="col s12">Logistics Info</div>
<div id="linked_items" class="col s12 disabled">Linked Items</div>
</div>
</div>
</div>
</div>
</body>
<script>
const item = {{ item|tojson }}
var reference_state = 1
document.addEventListener('DOMContentLoaded', function() {
document.getElementById("title").innerHTML = String(item[2])
var elemsSelects = document.querySelectorAll('select');
var instancesSelects = M.FormSelect.init(elemsSelects, {});
var el = document.querySelector('.tabs');
var instance = M.Tabs.init(el, {});
setEntryType()
setItemType()
populateLinks(item[5])
populateReferences(item[22], 'shopping_list')
populateReferences(item[23], 'recipe')
populateReferences(item[24], 'group')
});
function setEntryType(){
const selectElement = document.getElementById('entry_type');
selectElement.value = item[9];
}
function setItemType(){
const selectElement = document.getElementById('item_type');
selectElement.value = item[10];
}
function populateLinks(links){
var element = document.getElementById("weblinks");
for (let key in links){
var link = document.createElement("a");
link.classList.add("btn-small");
link.classList.add("outlined");
link.target = "_blank";
link.style = "border-radius: 20px; margin-right: 5px;";
link.innerHTML = String(key);;
link.href = links[key];
element.appendChild(link);
};
};
function populateReferences(references, reference_type){
var table = document.getElementById("reference_table")
for (let i = 0; i < references.length; i++){
var row = table.insertRow();
var row_type = row.insertCell();
var row_name = row.insertCell();
row_type.innerHTML = reference_type
row_name.innerHTML = String(references[i])
if ((reference_state % 2) == 0){
row.style = "background-color: gainsboro;"
}
reference_state++
}
}
</script>
</html>

View File

@ -9,4 +9,4 @@ app.register_blueprint(api.database_api)
def home(): def home():
return render_template("home.html") return render_template("home.html")
app.run(host="127.0.0.1", debug=True) app.run(host="0.0.0.0", port=5002, debug=True)