Compare commits
No commits in common. "f1f656a066210fb3a40bc14a19a05927a7296b20" and "f042c7ca82da70629a8c8e5a1bdfc3abb5b19dce" have entirely different histories.
f1f656a066
...
f042c7ca82
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
453
api.py
453
api.py
@ -1,14 +1,9 @@
|
|||||||
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, math, json, datetime, main, copy
|
import psycopg2, math
|
||||||
from config import config, sites_config
|
from config import config
|
||||||
|
|
||||||
database_api= Blueprint('database_api', __name__)
|
database_api= Blueprint('database_api', __name__)
|
||||||
|
|
||||||
@database_api.route("/changeSite")
|
|
||||||
def changeSite():
|
|
||||||
site = request.args.get('site', 'main')
|
|
||||||
session['selected_site'] = site
|
|
||||||
return jsonify({'status': 'SUCCESS'})
|
|
||||||
|
|
||||||
def paginate_with_params(cur, limit, offset, params):
|
def paginate_with_params(cur, limit, offset, params):
|
||||||
sql = f"SELECT * FROM main_items LEFT JOIN main_logistics_info ON main_items.logistics_info_id = main_logistics_info.id"
|
sql = f"SELECT * FROM main_items LEFT JOIN main_logistics_info ON main_items.logistics_info_id = main_logistics_info.id"
|
||||||
@ -53,87 +48,15 @@ def paginate_default(cur, limit, offset):
|
|||||||
count = cur.fetchone()[0]
|
count = cur.fetchone()[0]
|
||||||
return pantry_inventory, count
|
return pantry_inventory, count
|
||||||
|
|
||||||
def paginate_with_params_groups(cur, limit, offset, params):
|
|
||||||
sql = f"SELECT * FROM main_groups"
|
|
||||||
count = f"SELECT COUNT(*) FROM main_groups"
|
|
||||||
# WHERE search_string LIKE '%{search_string}%'
|
|
||||||
strings = []
|
|
||||||
count_strings = []
|
|
||||||
if params['search_string'] != "":
|
|
||||||
s = params['search_string']
|
|
||||||
strings.append(f" search_string LIKE '%{s}%'")
|
|
||||||
count_strings.append(f" search_string LIKE '%{s}%'")
|
|
||||||
|
|
||||||
|
|
||||||
# LIMIT {limit} OFFSET {offset};"
|
|
||||||
|
|
||||||
if len(strings) > 0:
|
|
||||||
sql = f"{sql} WHERE{" AND".join(strings)}"
|
|
||||||
|
|
||||||
if len(count_strings) > 0:
|
|
||||||
count = f"{count} WHERE{" AND".join(count_strings)}"
|
|
||||||
|
|
||||||
sql = f"{sql} ORDER BY main_logistics_info.quantity_on_hand LIMIT {limit} OFFSET {offset};"
|
|
||||||
count = f"{count};"
|
|
||||||
print(count)
|
|
||||||
print(sql)
|
|
||||||
cur.execute(sql)
|
|
||||||
pantry_inventory = cur.fetchall()
|
|
||||||
cur.execute(count)
|
|
||||||
count = cur.fetchone()[0]
|
|
||||||
return pantry_inventory, count
|
|
||||||
|
|
||||||
@database_api.route("/getGroups")
|
|
||||||
def paginate_groups():
|
|
||||||
page = int(request.args.get('page', 1))
|
|
||||||
limit = int(request.args.get('limit', 10))
|
|
||||||
site_name = session['selected_site']
|
|
||||||
offset = (page - 1) * limit
|
|
||||||
|
|
||||||
groups = []
|
|
||||||
count = 0
|
|
||||||
|
|
||||||
database_config = config()
|
|
||||||
with psycopg2.connect(**database_config) as conn:
|
|
||||||
try:
|
|
||||||
with conn.cursor() as cur:
|
|
||||||
sql = f"SELECT * FROM {site_name}_groups LIMIT %s OFFSET %s;"
|
|
||||||
count = f"SELECT COUNT(*) FROM {site_name}_groups"
|
|
||||||
|
|
||||||
cur.execute(sql, (limit, offset))
|
|
||||||
groups = cur.fetchall()
|
|
||||||
cur.execute(count)
|
|
||||||
count = cur.fetchone()[0]
|
|
||||||
|
|
||||||
|
|
||||||
sql_item = f"SELECT {site_name}_items.barcode, {site_name}_items.item_name, {site_name}_logistics_info.quantity_on_hand FROM {site_name}_items LEFT JOIN {site_name}_logistics_info ON {site_name}_items.logistics_info_id = {site_name}_logistics_info.id WHERE {site_name}_items.id = %s; "
|
|
||||||
new_groups = []
|
|
||||||
for group in groups:
|
|
||||||
qty = 0
|
|
||||||
group = list(group)
|
|
||||||
items = []
|
|
||||||
print(group[3])
|
|
||||||
for item_id in group[3]:
|
|
||||||
cur.execute(sql_item, (item_id,))
|
|
||||||
item_row = cur.fetchone()
|
|
||||||
qty += float(item_row[2])
|
|
||||||
items.append(item_row)
|
|
||||||
group[3] = items
|
|
||||||
group.append(qty)
|
|
||||||
new_groups.append(group)
|
|
||||||
except (Exception, psycopg2.DatabaseError) as error:
|
|
||||||
print(error)
|
|
||||||
|
|
||||||
return jsonify({'groups': new_groups, "end": math.ceil(count/limit)})
|
|
||||||
|
|
||||||
@database_api.route("/getItems")
|
@database_api.route("/getItems")
|
||||||
def pagninate_items():
|
def pagninate_items():
|
||||||
|
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', ""))
|
search_string = str(request.args.get('search_text', ""))
|
||||||
sort_order = int(request.args.get('sort_order', 1))
|
sort_order = int(request.args.get('sort_order', 1))
|
||||||
view = int(request.args.get('view', 0))
|
view = int(request.args.get('view', 0))
|
||||||
site_name = session['selected_site']
|
|
||||||
|
|
||||||
offset = (page - 1) * limit
|
offset = (page - 1) * limit
|
||||||
|
|
||||||
@ -144,25 +67,30 @@ def pagninate_items():
|
|||||||
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 {site_name}_items LEFT JOIN {site_name}_logistics_info ON {site_name}_items.logistics_info_id = {site_name}_logistics_info.id LIMIT {limit} OFFSET {offset};"
|
pantry_inventory, count = paginate_with_params(
|
||||||
count = f"SELECT COUNT(*) FROM {site_name}_items LEFT JOIN {site_name}_logistics_info ON {site_name}_items.logistics_info_id = {site_name}_logistics_info.id;"
|
cur, limit, offset,
|
||||||
cur.execute(sql)
|
{'search_string': search_string,
|
||||||
pantry_inventory = cur.fetchall()
|
'view': view}
|
||||||
cur.execute(count)
|
)
|
||||||
count = cur.fetchone()[0]
|
|
||||||
except (Exception, psycopg2.DatabaseError) as error:
|
except (Exception, psycopg2.DatabaseError) as error:
|
||||||
print(error)
|
print(error)
|
||||||
|
|
||||||
|
if sort_order == 0:
|
||||||
|
pantry_inventory = sorted(pantry_inventory, key=lambda x: x[1])
|
||||||
|
|
||||||
|
if sort_order == 1:
|
||||||
|
pantry_inventory = sorted(pantry_inventory, key=lambda x: x[2])
|
||||||
|
|
||||||
|
if sort_order == 2:
|
||||||
|
pantry_inventory = sorted(pantry_inventory, key=lambda x: x[18])
|
||||||
|
|
||||||
return jsonify({'items': pantry_inventory, "end": math.ceil(count/limit)})
|
return jsonify({'items': pantry_inventory, "end": math.ceil(count/limit)})
|
||||||
|
|
||||||
@database_api.route("/getItem")
|
@database_api.route("/getItem")
|
||||||
def get_item():
|
def get_item():
|
||||||
id = int(request.args.get('id', 1))
|
id = int(request.args.get('id', 1))
|
||||||
database_config = config()
|
database_config = config()
|
||||||
site_name = session['selected_site']
|
site_name = "main"
|
||||||
sites = sites_config()
|
|
||||||
|
|
||||||
|
|
||||||
item = []
|
item = []
|
||||||
with psycopg2.connect(**database_config) as conn:
|
with psycopg2.connect(**database_config) as conn:
|
||||||
try:
|
try:
|
||||||
@ -171,351 +99,24 @@ def get_item():
|
|||||||
sql = file.read()
|
sql = file.read()
|
||||||
cur.execute(sql, (id, ))
|
cur.execute(sql, (id, ))
|
||||||
item = list(cur.fetchone())
|
item = list(cur.fetchone())
|
||||||
SQL_groups = f"SELECT * FROM {site_name}_groups WHERE included_items @> ARRAY[%s];"
|
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'}
|
||||||
cur.execute(SQL_groups, (item[0], ))
|
item[22] = ['test_list', 'main_list']
|
||||||
item[25] = list(cur.fetchall())
|
item[23] = ['test_recipe',]
|
||||||
SQL_shopping_lists = f"SELECT * FROM {site_name}_shopping_lists WHERE pantry_items @> ARRAY[%s];"
|
item[24] = ['test_group', 'main_group', 'test2_group']
|
||||||
cur.execute(SQL_shopping_lists, (item[0], ))
|
|
||||||
item[23] = list(cur.fetchall())
|
|
||||||
print(item)
|
|
||||||
except (Exception, psycopg2.DatabaseError) as error:
|
except (Exception, psycopg2.DatabaseError) as error:
|
||||||
print(error)
|
print(error)
|
||||||
|
|
||||||
return render_template(f"items/item.html", item=item, current_site=site_name, sites=sites['sites'])
|
return render_template(f"item_page/index.html", item=item)
|
||||||
|
|
||||||
@database_api.route("/addItem")
|
|
||||||
def addItem():
|
|
||||||
barcode = str(request.args.get('barcode', ""))
|
|
||||||
name = str(request.args.get('item_name', ""))
|
|
||||||
description = str(request.args.get('item_description', ""))
|
|
||||||
item_type = str(request.args.get('item_type', ""))
|
|
||||||
subtype = str(request.args.get('sub_type', ""))
|
|
||||||
site_name = session['selected_site']
|
|
||||||
state = "FAILED"
|
|
||||||
|
|
||||||
payload = copy.deepcopy(main.payload_food_item)
|
|
||||||
|
|
||||||
defaults = config(filename=f"sites/{site_name}/site.ini", section="defaults")
|
|
||||||
uuid = f"{defaults["default_zone"]}@{defaults["default_primary_location"]}"
|
|
||||||
name = name.replace("'", "@&apostraphe&")
|
|
||||||
payload["logistics_info"]["primary_location"] = uuid
|
|
||||||
payload["logistics_info"]["auto_issue_location"] = uuid
|
|
||||||
|
|
||||||
|
|
||||||
database_config = config()
|
|
||||||
with psycopg2.connect(**database_config) as conn:
|
|
||||||
logistics_info_id = main.create_logistics_info(conn, site_name, barcode, payload["logistics_info"])
|
|
||||||
if not logistics_info_id:
|
|
||||||
return jsonify({'state': str(logistics_info_id)})
|
|
||||||
item_info_id = main.create_item_info(conn, site_name, barcode, payload["item_info"])
|
|
||||||
if not item_info_id:
|
|
||||||
return jsonify({'state': str(item_info_id)})
|
|
||||||
food_info_id = main.create_food_info(conn, site_name, payload["food_info"])
|
|
||||||
if not food_info_id:
|
|
||||||
return jsonify({'state': str(food_info_id)})
|
|
||||||
|
|
||||||
sqltwo = f"INSERT INTO {site_name}_items(barcode, item_name, description, item_info_id, logistics_info_id, food_info_id, row_type, item_type, search_string) VALUES('{barcode}', '{name}', '{description}', {item_info_id}, {logistics_info_id}, {food_info_id}, '{item_type}', '{subtype}', '{barcode}%{name}') RETURNING *;"
|
|
||||||
row = None
|
|
||||||
try:
|
|
||||||
with conn.cursor() as cur:
|
|
||||||
cur.execute(sqltwo)
|
|
||||||
rows = cur.fetchone()
|
|
||||||
if rows:
|
|
||||||
row = rows[:]
|
|
||||||
except (Exception, psycopg2.DatabaseError) as error:
|
|
||||||
print(error)
|
|
||||||
conn.rollback()
|
|
||||||
return jsonify({'state': str(error)})
|
|
||||||
|
|
||||||
|
|
||||||
conn.commit()
|
|
||||||
|
|
||||||
|
|
||||||
main.add_transaction(site_name, barcode, qty=0, user_id=1, description="Added Item to System!")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return jsonify({'state': "SUCCESS"})
|
|
||||||
|
|
||||||
@database_api.route("/addGroup")
|
@database_api.route("/addGroup")
|
||||||
def addGroup():
|
def addGroup():
|
||||||
name = str(request.args.get('name', ""))
|
name = str(request.args.get('name', ""))
|
||||||
description = str(request.args.get('description', ""))
|
description = str(request.args.get('description', ""))
|
||||||
group_type = str(request.args.get('type', ""))
|
group_type = str(request.args.get('type', ""))
|
||||||
site_name = session['selected_site']
|
|
||||||
state = "FAILED"
|
state = "FAILED"
|
||||||
|
|
||||||
database_config = config()
|
if name or description or group_type == "":
|
||||||
with psycopg2.connect(**database_config) as conn:
|
print("this is empty")
|
||||||
try:
|
|
||||||
with conn.cursor() as cur:
|
|
||||||
sql = f"INSERT INTO {site_name}_groups (name, description, included_items, group_type) VALUES (%s, %s, %s, %s);"
|
|
||||||
cur.execute(sql, (name, description, json.dumps({}), group_type))
|
|
||||||
state = "SUCCESS"
|
|
||||||
conn.commit()
|
|
||||||
except (Exception, psycopg2.DatabaseError) as error:
|
|
||||||
print(error)
|
|
||||||
conn.rollback()
|
|
||||||
|
|
||||||
|
return jsonify({'state': state})
|
||||||
return jsonify({'state': state})
|
|
||||||
|
|
||||||
@database_api.route("/getGroup")
|
|
||||||
def get_group():
|
|
||||||
id = int(request.args.get('id', 1))
|
|
||||||
database_config = config()
|
|
||||||
site_name = session['selected_site']
|
|
||||||
|
|
||||||
group = []
|
|
||||||
with psycopg2.connect(**database_config) as conn:
|
|
||||||
try:
|
|
||||||
with conn.cursor() as cur:
|
|
||||||
sql = f"SELECT * FROM {site_name}_groups WHERE id=%s;"
|
|
||||||
cur.execute(sql, (id, ))
|
|
||||||
group = list(cur.fetchone())
|
|
||||||
|
|
||||||
sql_item = f"SELECT {site_name}_items.id, {site_name}_items.barcode, {site_name}_items.item_name, {site_name}_logistics_info.quantity_on_hand FROM {site_name}_items LEFT JOIN {site_name}_logistics_info ON {site_name}_items.logistics_info_id = {site_name}_logistics_info.id WHERE {site_name}_items.id = %s;"
|
|
||||||
qty = 0
|
|
||||||
group = list(group)
|
|
||||||
items = []
|
|
||||||
print(group[3])
|
|
||||||
for item_id in group[3]:
|
|
||||||
cur.execute(sql_item, (item_id,))
|
|
||||||
item_row = cur.fetchone()
|
|
||||||
qty += float(item_row[3])
|
|
||||||
items.append(item_row)
|
|
||||||
group[3] = items
|
|
||||||
group.append(qty)
|
|
||||||
except (Exception, psycopg2.DatabaseError) as error:
|
|
||||||
print(error)
|
|
||||||
|
|
||||||
return jsonify(group=group)
|
|
||||||
|
|
||||||
@database_api.route("/updateGroup", methods=["POST"])
|
|
||||||
def update_group():
|
|
||||||
if request.method == "POST":
|
|
||||||
site_name = session['selected_site']
|
|
||||||
group_id = request.get_json()['id']
|
|
||||||
items = request.get_json()['items']
|
|
||||||
name = request.get_json()['name']
|
|
||||||
description = request.get_json()['description']
|
|
||||||
group_type = request.get_json()['group_type']
|
|
||||||
data = (name, description, items, group_type, group_id)
|
|
||||||
database_config = config()
|
|
||||||
with psycopg2.connect(**database_config) as conn:
|
|
||||||
try:
|
|
||||||
with conn.cursor() as cur:
|
|
||||||
# Start by updating the group -> included items with the up to date list
|
|
||||||
sql = f"UPDATE {site_name}_groups SET name = %s, description = %s, included_items = %s, group_type = %s WHERE id=%s;"
|
|
||||||
cur.execute(sql, data)
|
|
||||||
|
|
||||||
update_item_sql = f"UPDATE {site_name}_item_info SET groups = %s WHERE id = %s;"
|
|
||||||
select_item_sql = f"SELECT {site_name}_item_info.id, {site_name}_item_info.groups FROM {site_name}_items LEFT JOIN {site_name}_item_info ON {site_name}_items.item_info_id = {site_name}_item_info.id WHERE {site_name}_items.id = %s;"
|
|
||||||
# Now we will fetch each item row one by one and check if the group id is already inside of its groups array
|
|
||||||
for item_id in items:
|
|
||||||
cur.execute(select_item_sql, (item_id, ))
|
|
||||||
item = cur.fetchone()
|
|
||||||
print(item)
|
|
||||||
item_groups: set = set(item[1])
|
|
||||||
# Condition check, adds it if it doesnt exist.
|
|
||||||
if group_id not in item_groups:
|
|
||||||
item_groups.add(group_id)
|
|
||||||
cur.execute(update_item_sql, (list(item_groups), item[0]))
|
|
||||||
|
|
||||||
# Now we fetch all items that have the group id in its groups array
|
|
||||||
fetch_items_with_group = f"SELECT {site_name}_items.id, groups, {site_name}_item_info.id FROM {site_name}_item_info LEFT JOIN {site_name}_items ON {site_name}_items.item_info_id = {site_name}_item_info.id WHERE groups @> ARRAY[%s];"
|
|
||||||
cur.execute(fetch_items_with_group, (group_id, ))
|
|
||||||
group_items = cur.fetchall()
|
|
||||||
print(items)
|
|
||||||
# We will then check each item id against the groups new included_items list to see if the item should be in there
|
|
||||||
for item_id, group, info_id in group_items:
|
|
||||||
# If it is not we remove the group form the items list and update the item
|
|
||||||
if item_id not in items:
|
|
||||||
groups: list = list(group)
|
|
||||||
groups.remove(group_id)
|
|
||||||
cur.execute(update_item_sql, (list(groups), info_id))
|
|
||||||
|
|
||||||
conn.commit()
|
|
||||||
except (Exception, psycopg2.DatabaseError) as error:
|
|
||||||
print(error)
|
|
||||||
conn.rollback()
|
|
||||||
|
|
||||||
return jsonify({"state": "SUCCESS"})
|
|
||||||
return jsonify({"state": "FAILED"})
|
|
||||||
|
|
||||||
@database_api.route("/addList")
|
|
||||||
def addList():
|
|
||||||
name = str(request.args.get('name', ""))
|
|
||||||
description = str(request.args.get('description', ""))
|
|
||||||
list_type = str(request.args.get('type', ""))
|
|
||||||
site_name = session['selected_site']
|
|
||||||
|
|
||||||
print(name, description, list_type)
|
|
||||||
state = "FAILED"
|
|
||||||
|
|
||||||
#if name or description or group_type == "":
|
|
||||||
# print("this is empty")
|
|
||||||
# return jsonify({'state': state})
|
|
||||||
timestamp = datetime.datetime.now()
|
|
||||||
data = (name, description, [], json.dumps({}), [], [], 0, timestamp, list_type)
|
|
||||||
database_config = config()
|
|
||||||
with psycopg2.connect(**database_config) as conn:
|
|
||||||
try:
|
|
||||||
with conn.cursor() as cur:
|
|
||||||
sql = f"INSERT INTO {site_name}_shopping_lists (name, description, pantry_items, custom_items, recipes, groups, author, creation_date, type) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s);"
|
|
||||||
cur.execute(sql, data)
|
|
||||||
state = "SUCCESS"
|
|
||||||
conn.commit()
|
|
||||||
except (Exception, psycopg2.DatabaseError) as error:
|
|
||||||
print(error)
|
|
||||||
conn.rollback()
|
|
||||||
|
|
||||||
|
|
||||||
return jsonify({'state': state})
|
|
||||||
|
|
||||||
@database_api.route("/getLists")
|
|
||||||
def paginate_lists():
|
|
||||||
page = int(request.args.get('page', 1))
|
|
||||||
limit = int(request.args.get('limit', 10))
|
|
||||||
site_name = session['selected_site']
|
|
||||||
|
|
||||||
offset = (page - 1) * limit
|
|
||||||
|
|
||||||
lists = []
|
|
||||||
count = 0
|
|
||||||
|
|
||||||
database_config = config()
|
|
||||||
with psycopg2.connect(**database_config) as conn:
|
|
||||||
try:
|
|
||||||
with conn.cursor() as cur:
|
|
||||||
sql = f"SELECT * FROM {site_name}_shopping_lists LIMIT %s OFFSET %s;"
|
|
||||||
count = f"SELECT COUNT(*) FROM {site_name}_shopping_lists;"
|
|
||||||
|
|
||||||
cur.execute(sql, (limit, offset))
|
|
||||||
temp_lists = list(cur.fetchall())
|
|
||||||
cur.execute(count)
|
|
||||||
count = cur.fetchone()[0]
|
|
||||||
|
|
||||||
for shopping_list in temp_lists:
|
|
||||||
shopping_list: list = list(shopping_list)
|
|
||||||
pantry_items = shopping_list[3]
|
|
||||||
custom_items = shopping_list[4]
|
|
||||||
list_length = len(custom_items)
|
|
||||||
|
|
||||||
if shopping_list[10] == 'calculated':
|
|
||||||
item_sql = f"SELECT COUNT(*) FROM {site_name}_items LEFT JOIN {site_name}_logistics_info ON {site_name}_items.logistics_info_id = {site_name}_logistics_info.id LEFT JOIN {site_name}n_item_info ON {site_name}_items.item_info_id = {site_name}_item_info.id LEFT JOIN {site_name}_food_info ON {site_name}_items.food_info_id = {site_name}_food_info.id WHERE {site_name}_logistics_info.quantity_on_hand < {site_name}_item_info.safety_stock AND shopping_lists @> ARRAY[%s];"
|
|
||||||
cur.execute(item_sql, (shopping_list[0], ))
|
|
||||||
list_length += cur.fetchone()[0]
|
|
||||||
else:
|
|
||||||
list_length += len(pantry_items)
|
|
||||||
|
|
||||||
shopping_list.append(list_length)
|
|
||||||
lists.append(shopping_list)
|
|
||||||
|
|
||||||
except (Exception, psycopg2.DatabaseError) as error:
|
|
||||||
print(error)
|
|
||||||
|
|
||||||
return jsonify({'lists': lists, 'end': math.ceil(count/limit)})
|
|
||||||
|
|
||||||
@database_api.route("/getListView")
|
|
||||||
def get_list_view():
|
|
||||||
id = int(request.args.get('id', 1))
|
|
||||||
site_name = session['selected_site']
|
|
||||||
shopping_list = []
|
|
||||||
database_config = config()
|
|
||||||
with psycopg2.connect(**database_config) as conn:
|
|
||||||
try:
|
|
||||||
with conn.cursor() as cur:
|
|
||||||
sql = f"SELECT * FROM {site_name}_shopping_lists WHERE id=%s;"
|
|
||||||
cur.execute(sql, (id, ))
|
|
||||||
shopping_list = list(cur.fetchone())
|
|
||||||
|
|
||||||
if shopping_list[10] == "calculated":
|
|
||||||
itemSQL = f"SELECT {site_name}_items.id, {site_name}_items.barcode, {site_name}_items.item_name, {site_name}_items.links, {site_name}_logistics_info.quantity_on_hand, {site_name}_item_info.safety_stock, {site_name}_item_info.uom FROM {site_name}_items LEFT JOIN {site_name}_logistics_info ON {site_name}_items.logistics_info_id = {site_name}_logistics_info.id LEFT JOIN {site_name}_item_info ON {site_name}_items.item_info_id = {site_name}_item_info.id LEFT JOIN {site_name}_food_info ON {site_name}_items.food_info_id = {site_name}_food_info.id WHERE {site_name}_logistics_info.quantity_on_hand < {site_name}_item_info.safety_stock AND shopping_lists @> ARRAY[%s];"
|
|
||||||
else:
|
|
||||||
itemSQL = f"SELECT {site_name}_items.id, {site_name}_items.barcode, {site_name}_items.item_name, {site_name}_items.links, {site_name}_logistics_info.quantity_on_hand, {site_name}_item_info.safety_stock, {site_name}_item_info.uom FROM {site_name}_items LEFT JOIN {site_name}_logistics_info ON {site_name}_items.logistics_info_id = {site_name}_logistics_info.id LEFT JOIN {site_name}_item_info ON {site_name}_items.item_info_id = {site_name}_item_info.id LEFT JOIN {site_name}_food_info ON {site_name}_items.food_info_id = {site_name}_food_info.id WHERE shopping_lists @> ARRAY[%s];"
|
|
||||||
|
|
||||||
cur.execute(itemSQL, (id, ))
|
|
||||||
shopping_list[3] = list(cur.fetchall())
|
|
||||||
print(shopping_list)
|
|
||||||
|
|
||||||
except (Exception, psycopg2.DatabaseError) as error:
|
|
||||||
print(error)
|
|
||||||
|
|
||||||
return jsonify(shopping_list=shopping_list)
|
|
||||||
|
|
||||||
@database_api.route("/getList")
|
|
||||||
def get_list():
|
|
||||||
id = int(request.args.get('id', 1))
|
|
||||||
database_config = config()
|
|
||||||
site_name = session['selected_site']
|
|
||||||
shopping_list = []
|
|
||||||
with psycopg2.connect(**database_config) as conn:
|
|
||||||
try:
|
|
||||||
with conn.cursor() as cur:
|
|
||||||
sql = f"SELECT * FROM {site_name}_shopping_lists WHERE id=%s;"
|
|
||||||
cur.execute(sql, (id, ))
|
|
||||||
shopping_list = list(cur.fetchone())
|
|
||||||
itemSQL = f"SELECT {site_name}_items.id, {site_name}_items.barcode, {site_name}_items.item_name, {site_name}_items.links, {site_name}_item_info.uom FROM {site_name}_items LEFT JOIN {site_name}_item_info ON {site_name}_items.item_info_id = {site_name}_item_info.id WHERE {site_name}_item_info.shopping_lists @> ARRAY[%s];"
|
|
||||||
cur.execute(itemSQL, (id, ))
|
|
||||||
shopping_list[3] = list(cur.fetchall())
|
|
||||||
print(shopping_list)
|
|
||||||
except (Exception, psycopg2.DatabaseError) as error:
|
|
||||||
print(error)
|
|
||||||
|
|
||||||
return jsonify(shopping_list=shopping_list)
|
|
||||||
|
|
||||||
@database_api.route("/updateList", methods=["POST"])
|
|
||||||
def update_list():
|
|
||||||
if request.method == "POST":
|
|
||||||
site_name = session['selected_site']
|
|
||||||
list_id = request.get_json()['id']
|
|
||||||
items = request.get_json()['items']
|
|
||||||
print(items)
|
|
||||||
custom_items = request.get_json()['custom']
|
|
||||||
name = request.get_json()['name']
|
|
||||||
description = request.get_json()['description']
|
|
||||||
list_type = request.get_json()['list_type']
|
|
||||||
quantities = request.get_json()['quantities']
|
|
||||||
data = (name, description, items, json.dumps(custom_items), list_type, json.dumps(quantities), list_id)
|
|
||||||
database_config = config()
|
|
||||||
with psycopg2.connect(**database_config) as conn:
|
|
||||||
try:
|
|
||||||
with conn.cursor() as cur:
|
|
||||||
# Start by updating the group -> included items with the up to date list
|
|
||||||
sql = f"UPDATE {site_name}_shopping_lists SET name = %s, description = %s, pantry_items = %s, custom_items = %s, type = %s, quantities = %s WHERE id=%s;"
|
|
||||||
cur.execute(sql, data)
|
|
||||||
|
|
||||||
update_item_sql = f"UPDATE {site_name}_item_info SET shopping_lists = %s WHERE id = %s;"
|
|
||||||
select_item_sql = f"SELECT {site_name}_item_info.id, {site_name}_item_info.shopping_lists FROM {site_name}_items LEFT JOIN {site_name}_item_info ON {site_name}_items.item_info_id = {site_name}_item_info.id WHERE {site_name}_items.id = %s;"
|
|
||||||
# Now we will fetch each item row one by one and check if the group id is already inside of its groups array
|
|
||||||
for item_id in items:
|
|
||||||
cur.execute(select_item_sql, (item_id, ))
|
|
||||||
item = cur.fetchone()
|
|
||||||
print(item)
|
|
||||||
shopping_lists: set = set(item[1])
|
|
||||||
# Condition check, adds it if it doesnt exist.
|
|
||||||
if list_id not in shopping_lists:
|
|
||||||
shopping_lists.add(list_id)
|
|
||||||
cur.execute(update_item_sql, (list(shopping_lists), item[0]))
|
|
||||||
|
|
||||||
# Now we fetch all items that have the group id in its groups array
|
|
||||||
fetch_items_with_list = f"SELECT {site_name}_items.id, {site_name}_item_info.shopping_lists, {site_name}_item_info.id FROM {site_name}_item_info LEFT JOIN {site_name}_items ON {site_name}_items.item_info_id = {site_name}_item_info.id WHERE {site_name}_item_info.shopping_lists @> ARRAY[%s];"
|
|
||||||
cur.execute(fetch_items_with_list, (list_id, ))
|
|
||||||
list_items = cur.fetchall()
|
|
||||||
print(items)
|
|
||||||
# We will then check each item id against the groups new included_items list to see if the item should be in there
|
|
||||||
for item_id, shopping_list, info_id in list_items:
|
|
||||||
# If it is not we remove the group form the items list and update the item
|
|
||||||
if item_id not in items:
|
|
||||||
shopping_lists: list = list(shopping_list)
|
|
||||||
shopping_lists.remove(list_id)
|
|
||||||
cur.execute(update_item_sql, (list(shopping_lists), info_id))
|
|
||||||
|
|
||||||
conn.commit()
|
|
||||||
except (Exception, psycopg2.DatabaseError) as error:
|
|
||||||
print(error)
|
|
||||||
conn.rollback()
|
|
||||||
|
|
||||||
return jsonify({"state": "SUCCESS"})
|
|
||||||
return jsonify({"state": "FAILED"})
|
|
||||||
47
config.py
47
config.py
@ -1,6 +1,5 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
from configparser import ConfigParser
|
from configparser import ConfigParser
|
||||||
import json
|
|
||||||
|
|
||||||
|
|
||||||
def config(filename='database.ini', section='postgresql'):
|
def config(filename='database.ini', section='postgresql'):
|
||||||
@ -20,47 +19,3 @@ def config(filename='database.ini', section='postgresql'):
|
|||||||
|
|
||||||
return db
|
return db
|
||||||
|
|
||||||
def sites_config(filename='database.ini', section='manage'):
|
|
||||||
# create a parser
|
|
||||||
parser = ConfigParser()
|
|
||||||
# read config file
|
|
||||||
parser.read(filename)
|
|
||||||
|
|
||||||
# get section, default to postgresql
|
|
||||||
sites = {}
|
|
||||||
if parser.has_section(section):
|
|
||||||
params = parser.items(section)
|
|
||||||
for param in params:
|
|
||||||
sites[param[0]] = param[1].split(',')
|
|
||||||
else:
|
|
||||||
raise Exception('Section {0} not found in the {1} file'.format(section, filename))
|
|
||||||
|
|
||||||
return sites
|
|
||||||
|
|
||||||
|
|
||||||
def write_new_site(site_name):
|
|
||||||
|
|
||||||
old_value = sites_config()['sites']
|
|
||||||
print(old_value)
|
|
||||||
|
|
||||||
old_value.append(site_name)
|
|
||||||
old_value = set(old_value)
|
|
||||||
|
|
||||||
config = ConfigParser()
|
|
||||||
config.read('database.ini')
|
|
||||||
config.set('manage', 'sites', ','.join(old_value))
|
|
||||||
|
|
||||||
with open('database.ini', 'w') as configFile:
|
|
||||||
config.write(configFile)
|
|
||||||
|
|
||||||
def delete_site(site_name):
|
|
||||||
old_value = sites_config()['sites']
|
|
||||||
old_value.remove(site_name)
|
|
||||||
|
|
||||||
config = ConfigParser()
|
|
||||||
config.read('database.ini')
|
|
||||||
config.set('manage', 'sites', ','.join(old_value))
|
|
||||||
|
|
||||||
with open('database.ini', 'w') as configFile:
|
|
||||||
config.write(configFile)
|
|
||||||
|
|
||||||
|
|||||||
14
database.ini
14
database.ini
@ -1,10 +1,6 @@
|
|||||||
[postgresql]
|
[postgresql]
|
||||||
host = 192.168.1.67
|
host=192.168.1.67
|
||||||
database = test
|
database=test
|
||||||
user = test
|
user=test
|
||||||
password = test
|
password=test
|
||||||
port = 5432
|
port=5432
|
||||||
|
|
||||||
[manage]
|
|
||||||
sites = test,test2,main
|
|
||||||
|
|
||||||
21
main.py
21
main.py
@ -35,7 +35,7 @@ def update_item_primary(site_name, barcode, new_primary: str):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def insert_row(table_name, name):
|
def insert_row(table_name, name):
|
||||||
sql = f"INSERT INTO {table_name}(id, name) VALUES(%s, %s) RETURNING id;"
|
sql = f"INSERT INTO {table_name}(id, name) VALUES(%s, %s) RETURNING id"
|
||||||
id = None
|
id = None
|
||||||
try:
|
try:
|
||||||
database_config = config()
|
database_config = config()
|
||||||
@ -85,7 +85,7 @@ def create_logistics_info(conn, site_name, barcode, payload):
|
|||||||
except (Exception, psycopg2.DatabaseError) as error:
|
except (Exception, psycopg2.DatabaseError) as error:
|
||||||
print(error)
|
print(error)
|
||||||
conn.rollback()
|
conn.rollback()
|
||||||
return error
|
return False
|
||||||
|
|
||||||
return logistics_info_id
|
return logistics_info_id
|
||||||
|
|
||||||
@ -103,7 +103,7 @@ def create_item_info(conn, site_name, barcode, payload):
|
|||||||
except (Exception, psycopg2.DatabaseError) as error:
|
except (Exception, psycopg2.DatabaseError) as error:
|
||||||
print(error)
|
print(error)
|
||||||
conn.rollback()
|
conn.rollback()
|
||||||
return error
|
return False
|
||||||
|
|
||||||
return item_info_id
|
return item_info_id
|
||||||
|
|
||||||
@ -136,7 +136,7 @@ def add_location(site_name, name, zone_id):
|
|||||||
except (Exception, psycopg2.DatabaseError) as error:
|
except (Exception, psycopg2.DatabaseError) as error:
|
||||||
print(error)
|
print(error)
|
||||||
conn.rollback()
|
conn.rollback()
|
||||||
return error
|
return False
|
||||||
|
|
||||||
uuid = f"{zone_name}@{name}"
|
uuid = f"{zone_name}@{name}"
|
||||||
try:
|
try:
|
||||||
@ -145,7 +145,7 @@ def add_location(site_name, name, zone_id):
|
|||||||
except (Exception, psycopg2.DatabaseError) as error:
|
except (Exception, psycopg2.DatabaseError) as error:
|
||||||
print(error)
|
print(error)
|
||||||
conn.rollback()
|
conn.rollback()
|
||||||
return error
|
return False
|
||||||
|
|
||||||
def add_zone(site_name, name):
|
def add_zone(site_name, name):
|
||||||
database_config = config()
|
database_config = config()
|
||||||
@ -157,7 +157,7 @@ def add_zone(site_name, name):
|
|||||||
except (Exception, psycopg2.DatabaseError) as error:
|
except (Exception, psycopg2.DatabaseError) as error:
|
||||||
print(error)
|
print(error)
|
||||||
conn.rollback()
|
conn.rollback()
|
||||||
return error
|
return False
|
||||||
|
|
||||||
def add_transaction(site_name, barcode, qty, user_id, transaction_type = "info", description = "", data = {}, location=None):
|
def add_transaction(site_name, barcode, qty, user_id, transaction_type = "info", description = "", data = {}, location=None):
|
||||||
database_config = config()
|
database_config = config()
|
||||||
@ -192,7 +192,7 @@ def add_transaction(site_name, barcode, qty, user_id, transaction_type = "info",
|
|||||||
except (Exception, psycopg2.DatabaseError) as error:
|
except (Exception, psycopg2.DatabaseError) as error:
|
||||||
print(error)
|
print(error)
|
||||||
conn.rollback()
|
conn.rollback()
|
||||||
return error
|
return False
|
||||||
|
|
||||||
if not location:
|
if not location:
|
||||||
mover = logistics_info[2]
|
mover = logistics_info[2]
|
||||||
@ -211,7 +211,7 @@ def add_transaction(site_name, barcode, qty, user_id, transaction_type = "info",
|
|||||||
except (Exception, psycopg2.DatabaseError) as error:
|
except (Exception, psycopg2.DatabaseError) as error:
|
||||||
print(error)
|
print(error)
|
||||||
conn.rollback()
|
conn.rollback()
|
||||||
return error
|
return False
|
||||||
|
|
||||||
if logistics_info[3] in location_items.keys():
|
if logistics_info[3] in location_items.keys():
|
||||||
location_items[logistics_info[3]] = location_items[logistics_info[3]] + qty
|
location_items[logistics_info[3]] = location_items[logistics_info[3]] + qty
|
||||||
@ -234,11 +234,11 @@ def add_transaction(site_name, barcode, qty, user_id, transaction_type = "info",
|
|||||||
except (Exception, psycopg2.DatabaseError) as error:
|
except (Exception, psycopg2.DatabaseError) as error:
|
||||||
print(error)
|
print(error)
|
||||||
conn.rollback()
|
conn.rollback()
|
||||||
return error
|
return False
|
||||||
|
|
||||||
conn.commit()
|
conn.commit()
|
||||||
|
|
||||||
def add_food_item(site_name: str, barcode: str, name: str, payload: dict):
|
def add_food_item(site_name: str, barcode: str, name: str, qty: float, payload: dict):
|
||||||
|
|
||||||
# TODO: I need to validate the name so that it doesnt have characters against the SQL database schema such as '
|
# TODO: I need to validate the name so that it doesnt have characters against the SQL database schema such as '
|
||||||
|
|
||||||
@ -279,6 +279,7 @@ def add_food_item(site_name: str, barcode: str, name: str, payload: dict):
|
|||||||
|
|
||||||
|
|
||||||
add_transaction(site_name, barcode, qty=0, user_id=1, description="Added Item to System!")
|
add_transaction(site_name, barcode, qty=0, user_id=1, description="Added Item to System!")
|
||||||
|
add_transaction(site_name, barcode, qty=qty, user_id=1, description="scan in")
|
||||||
|
|
||||||
def drop_table(sql_file: str):
|
def drop_table(sql_file: str):
|
||||||
database_config = config()
|
database_config = config()
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import sys, os, shutil
|
import sys, os, shutil
|
||||||
import main
|
import main
|
||||||
import config as cfg
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Manage.py is where the databases and configuration is set up. Its a CLI for quick serving the databases necessary for
|
Manage.py is where the databases and configuration is set up. Its a CLI for quick serving the databases necessary for
|
||||||
@ -91,7 +90,6 @@ def create():
|
|||||||
config.write(f"default_primary_location={default_location_name}\n")
|
config.write(f"default_primary_location={default_location_name}\n")
|
||||||
config.write(f"default_auto_issue_location={default_location_name}\n")
|
config.write(f"default_auto_issue_location={default_location_name}\n")
|
||||||
|
|
||||||
cfg.write_new_site(site_name)
|
|
||||||
|
|
||||||
print(f"Site {site_name} config created!")
|
print(f"Site {site_name} config created!")
|
||||||
print(f"Site {site_name} created!")
|
print(f"Site {site_name} created!")
|
||||||
@ -110,8 +108,6 @@ if __name__ == "__main__":
|
|||||||
if func_name == "delete" and argument == "site":
|
if func_name == "delete" and argument == "site":
|
||||||
main.delete_site(sys.argv[3])
|
main.delete_site(sys.argv[3])
|
||||||
shutil.rmtree(f"sites/{sys.argv[3]}")
|
shutil.rmtree(f"sites/{sys.argv[3]}")
|
||||||
cfg.delete_site(sys.argv[3])
|
|
||||||
|
|
||||||
|
|
||||||
if func_name == "item":
|
if func_name == "item":
|
||||||
if argument == "add":
|
if argument == "add":
|
||||||
|
|||||||
@ -3,7 +3,7 @@ CREATE TABLE IF NOT EXISTS %sitename%_items(
|
|||||||
barcode VARCHAR(255) NOT NULL,
|
barcode VARCHAR(255) NOT NULL,
|
||||||
item_name VARCHAR(255) NOT NULL,
|
item_name VARCHAR(255) NOT NULL,
|
||||||
brand INTEGER,
|
brand INTEGER,
|
||||||
description TEXT,
|
description TEXT;
|
||||||
tags TEXT [],
|
tags TEXT [],
|
||||||
links JSONB,
|
links JSONB,
|
||||||
item_info_id INTEGER NOT NULL,
|
item_info_id INTEGER NOT NULL,
|
||||||
|
|||||||
@ -2,11 +2,10 @@ CREATE TABLE IF NOT EXISTS %sitename%_shopping_lists (
|
|||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
name VARCHAR(255) NOT NULL,
|
name VARCHAR(255) NOT NULL,
|
||||||
description TEXT,
|
description TEXT,
|
||||||
pantry_items INTEGER [],
|
pantry_items JSONB,
|
||||||
custom_items JSONB,
|
custom_items JSONB,
|
||||||
recipes INTEGER [],
|
recipes JSONB,
|
||||||
groups INTEGER [],
|
groups JSONB,
|
||||||
quantities JSONB,
|
|
||||||
author INTEGER,
|
author INTEGER,
|
||||||
creation_date TIMESTAMP,
|
creation_date TIMESTAMP,
|
||||||
type VARCHAR(64),
|
type VARCHAR(64),
|
||||||
|
|||||||
@ -3,7 +3,6 @@ CREATE TABLE IF NOT EXISTS main_items(
|
|||||||
barcode VARCHAR(255) NOT NULL,
|
barcode VARCHAR(255) NOT NULL,
|
||||||
item_name VARCHAR(255) NOT NULL,
|
item_name VARCHAR(255) NOT NULL,
|
||||||
brand INTEGER,
|
brand INTEGER,
|
||||||
description TEXT,
|
|
||||||
tags TEXT [],
|
tags TEXT [],
|
||||||
links JSONB,
|
links JSONB,
|
||||||
item_info_id INTEGER NOT NULL,
|
item_info_id INTEGER NOT NULL,
|
||||||
|
|||||||
@ -2,11 +2,10 @@ CREATE TABLE IF NOT EXISTS main_shopping_lists (
|
|||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
name VARCHAR(255) NOT NULL,
|
name VARCHAR(255) NOT NULL,
|
||||||
description TEXT,
|
description TEXT,
|
||||||
pantry_items INTEGER [],
|
pantry_items JSONB,
|
||||||
custom_items JSONB,
|
custom_items JSONB,
|
||||||
recipes INTEGER [],
|
recipes JSONB,
|
||||||
groups INTEGER [],
|
groups JSONB,
|
||||||
quantities JSONB,
|
|
||||||
author INTEGER,
|
author INTEGER,
|
||||||
creation_date TIMESTAMP,
|
creation_date TIMESTAMP,
|
||||||
type VARCHAR(64),
|
type VARCHAR(64),
|
||||||
|
|||||||
@ -9,37 +9,36 @@ WHERE main_items.id=%s;
|
|||||||
01 - barcode
|
01 - barcode
|
||||||
02 - item_name
|
02 - item_name
|
||||||
03 - brand (id)
|
03 - brand (id)
|
||||||
04 - description
|
04 - tags
|
||||||
05 - tags
|
05 - links
|
||||||
06 - links
|
06 - item_info_id
|
||||||
07 - item_info_id
|
07 - logistics_info_id
|
||||||
08 - logistics_info_id
|
08 - food_info_id
|
||||||
09 - food_info_id
|
09 - row_type
|
||||||
10 - row_type
|
10 - item_type
|
||||||
11 - item_type
|
11 - search_string
|
||||||
12 - search_string
|
12 - logistics_info_id
|
||||||
13 - logistics_info_id
|
13 - barcode
|
||||||
14 - barcode
|
14 - primary_location
|
||||||
15 - primary_location
|
15 - auto_issue_location
|
||||||
16 - auto_issue_location
|
16 - dynamic_locations
|
||||||
17 - dynamic_locations
|
17 - location_data
|
||||||
18 - location_data
|
18 - quantity_on_hand
|
||||||
19 - quantity_on_hand
|
19 - item_info_id
|
||||||
20 - item_info_id
|
20 - barcode
|
||||||
21 - barcode
|
21 - linked_items
|
||||||
22 - linked_items
|
22 - shopping_lists
|
||||||
23 - shopping_lists
|
23 - recipes
|
||||||
24 - recipes
|
24 - groups
|
||||||
25 - groups
|
25 - packaging
|
||||||
26 - packaging
|
26 - uom
|
||||||
27 - uom
|
27 - cost
|
||||||
28 - cost
|
28 - safety_stock
|
||||||
29 - safety_stock
|
29 - lead_time_days
|
||||||
30 - lead_time_days
|
30 - ai_pick
|
||||||
31 - ai_pick
|
31 - food_info_id
|
||||||
32 - food_info_id
|
32 - food_groups
|
||||||
33 - food_groups
|
33 - ingrediants
|
||||||
34 - ingrediants
|
34 - nutrients
|
||||||
35 - nutrients
|
35 - expires
|
||||||
36 - expires
|
|
||||||
*/
|
*/
|
||||||
@ -1,9 +0,0 @@
|
|||||||
[site]
|
|
||||||
site_name=test
|
|
||||||
site_owner=
|
|
||||||
email=
|
|
||||||
|
|
||||||
[defaults]
|
|
||||||
default_zone=default
|
|
||||||
default_primary_location=all
|
|
||||||
default_auto_issue_location=all
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS test_brands (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
name VARCHAR(255)
|
|
||||||
);
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS test_food_info (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
food_groups TEXT [],
|
|
||||||
ingrediants TEXT [],
|
|
||||||
nutrients JSONB,
|
|
||||||
expires BOOLEAN
|
|
||||||
);
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS test_groups(
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
name VARCHAR(255) NOT NULL,
|
|
||||||
description TEXT,
|
|
||||||
included_items INTEGER [],
|
|
||||||
group_type VARCHAR(255),
|
|
||||||
UNIQUE (name)
|
|
||||||
);
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS test_items(
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
barcode VARCHAR(255) NOT NULL,
|
|
||||||
item_name VARCHAR(255) NOT NULL,
|
|
||||||
brand INTEGER,
|
|
||||||
description TEXT,
|
|
||||||
tags TEXT [],
|
|
||||||
links JSONB,
|
|
||||||
item_info_id INTEGER NOT NULL,
|
|
||||||
logistics_info_id INTEGER NOT NULL,
|
|
||||||
food_info_id INTEGER,
|
|
||||||
row_type VARCHAR(255) NOT NULL,
|
|
||||||
item_type VARCHAR(255) NOT NULL,
|
|
||||||
search_string TEXT NOT NULL,
|
|
||||||
UNIQUE(barcode, item_info_id),
|
|
||||||
CONSTRAINT fk_item_info
|
|
||||||
FOREIGN KEY(item_info_id)
|
|
||||||
REFERENCES test_item_info(id),
|
|
||||||
CONSTRAINT fk_food_info
|
|
||||||
FOREIGN KEY(food_info_id)
|
|
||||||
REFERENCES test_food_info(id),
|
|
||||||
CONSTRAINT fk_brand
|
|
||||||
FOREIGN KEY(brand)
|
|
||||||
REFERENCES test_brands(id),
|
|
||||||
CONSTRAINT fk_logistics_info
|
|
||||||
FOREIGN KEY(logistics_info_id)
|
|
||||||
REFERENCES test_logistics_info(id)
|
|
||||||
);
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
CREATE TABLE IF NOt EXISTS test_item_info (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
barcode VARCHAR(255) NOT NULL,
|
|
||||||
linked_items INTEGER [],
|
|
||||||
shopping_lists INTEGER [],
|
|
||||||
recipes INTEGER [],
|
|
||||||
groups INTEGER [],
|
|
||||||
packaging VARCHAR(255),
|
|
||||||
uom VARCHAR(255),
|
|
||||||
cost FLOAT8,
|
|
||||||
safety_stock FLOAT8,
|
|
||||||
lead_time_days FLOAT8,
|
|
||||||
ai_pick BOOLEAN,
|
|
||||||
UNIQUE(barcode)
|
|
||||||
);
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS test_itemlinks (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
barcode VARCHAR(255) NOt NULL,
|
|
||||||
link INTEGER NOT NULL,
|
|
||||||
data JSONB NOT NULL,
|
|
||||||
conv_factor FLOAT8 NOt NULL,
|
|
||||||
UNIQUE(barcode)
|
|
||||||
);
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS test_locations(
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
uuid VARCHAR(255) NOT NULL,
|
|
||||||
name VARCHAR(32) NOT NULL,
|
|
||||||
zone_id INTEGER NOT NULL,
|
|
||||||
items JSONB,
|
|
||||||
UNIQUE(uuid),
|
|
||||||
CONSTRAINT fk_zone
|
|
||||||
FOREIGN KEY(zone_id)
|
|
||||||
REFERENCES test_zones(id)
|
|
||||||
);
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS logins(
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
username VARCHAR(255),
|
|
||||||
password VARCHAR(255),
|
|
||||||
favorites JSONB,
|
|
||||||
unseen_pantry_items INTEGER [],
|
|
||||||
unseen_groups INTEGER [],
|
|
||||||
unseen_shopping_lists INTEGER [],
|
|
||||||
unseen_recipes INTEGER [],
|
|
||||||
seen_pantry_items INTEGER [],
|
|
||||||
seen_groups INTEGER[],
|
|
||||||
seen_shopping_lists INTEGER [],
|
|
||||||
seen_recipes INTEGER [],
|
|
||||||
flags JSONB
|
|
||||||
);
|
|
||||||
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS test_logistics_info(
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
barcode VARCHAR(255) NOT NULL,
|
|
||||||
primary_location VARCHAR(64),
|
|
||||||
auto_issue_location VARCHAR(64),
|
|
||||||
dynamic_locations JSONB,
|
|
||||||
location_data JSONB,
|
|
||||||
quantity_on_hand FLOAT8 NOT NULL,
|
|
||||||
UNIQUE(barcode)
|
|
||||||
);
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS test_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)
|
|
||||||
);
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS test_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)
|
|
||||||
);
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS test_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
|
|
||||||
);
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS test_shopping_lists (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
name VARCHAR(255) NOT NULL,
|
|
||||||
description TEXT,
|
|
||||||
pantry_items INTEGER [],
|
|
||||||
custom_items JSONB,
|
|
||||||
recipes INTEGER [],
|
|
||||||
groups INTEGER [],
|
|
||||||
quantities JSONB,
|
|
||||||
author INTEGER,
|
|
||||||
creation_date TIMESTAMP,
|
|
||||||
type VARCHAR(64),
|
|
||||||
UNIQUE(name)
|
|
||||||
);
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS test_Transactions (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
timestamp TIMESTAMP,
|
|
||||||
logistics_info_id INTEGER NOT NULL,
|
|
||||||
barcode VARCHAR(255) NOT NULL,
|
|
||||||
name VARCHAR(255),
|
|
||||||
transaction_type VARCHAR(255) NOT NULL,
|
|
||||||
quantity FLOAT8 NOT NULL,
|
|
||||||
description TEXT,
|
|
||||||
user_id INTEGER NOT NULL,
|
|
||||||
data JSONB,
|
|
||||||
CONSTRAINT fk_logistics_info
|
|
||||||
FOREIGN KEY(logistics_info_id)
|
|
||||||
REFERENCES test_logistics_info(id)
|
|
||||||
);
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS test_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)
|
|
||||||
);
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS test_zones(
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
name VARCHAR(32) NOT NULL,
|
|
||||||
UNIQUE(name)
|
|
||||||
);
|
|
||||||
@ -1 +0,0 @@
|
|||||||
DROP TABLE test_brands CASCADE;
|
|
||||||
@ -1 +0,0 @@
|
|||||||
DROP TABLE test_food_info CASCADE;
|
|
||||||
@ -1 +0,0 @@
|
|||||||
DROP TABLE test_groups CASCADE;
|
|
||||||
@ -1 +0,0 @@
|
|||||||
DROP TABLE test_item_info CASCADE;
|
|
||||||
@ -1 +0,0 @@
|
|||||||
DROP TABLE test_items CASCADE;
|
|
||||||
@ -1 +0,0 @@
|
|||||||
DROP TABLE test_itemlinks CASCADE;
|
|
||||||
@ -1 +0,0 @@
|
|||||||
DROP TABLE test_locations CASCADE;
|
|
||||||
@ -1 +0,0 @@
|
|||||||
DROP TABLE test_logistics_info CASCADE;
|
|
||||||
@ -1 +0,0 @@
|
|||||||
DROP TABLE test_receipt_items CASCADE;
|
|
||||||
@ -1 +0,0 @@
|
|||||||
DROP TABLE test_receipts CASCADE;
|
|
||||||
@ -1 +0,0 @@
|
|||||||
DROP TABLE test_recipes CASCADE;
|
|
||||||
@ -1 +0,0 @@
|
|||||||
DROP TABLE test_shopping_lists CASCADE;
|
|
||||||
@ -1 +0,0 @@
|
|||||||
DROP TABLE test_transactions CASCADE;
|
|
||||||
@ -1 +0,0 @@
|
|||||||
DROP TABLE test_vendors CASCADE;
|
|
||||||
@ -1 +0,0 @@
|
|||||||
DROP TABLE test_zones CASCADE;
|
|
||||||
@ -1,45 +0,0 @@
|
|||||||
SELECT * FROM test_items
|
|
||||||
LEFT JOIN test_logistics_info ON test_items.logistics_info_id = test_logistics_info.id
|
|
||||||
LEFT JOIN test_item_info ON test_items.item_info_id = test_item_info.id
|
|
||||||
LEFT JOIN test_food_info ON test_items.food_info_id = test_food_info.id
|
|
||||||
WHERE test_items.id=%s;
|
|
||||||
|
|
||||||
/*
|
|
||||||
00 - item_id
|
|
||||||
01 - barcode
|
|
||||||
02 - item_name
|
|
||||||
03 - brand (id)
|
|
||||||
04 - description
|
|
||||||
05 - tags
|
|
||||||
06 - links
|
|
||||||
07 - item_info_id
|
|
||||||
08 - logistics_info_id
|
|
||||||
09 - food_info_id
|
|
||||||
10 - row_type
|
|
||||||
11 - item_type
|
|
||||||
12 - search_string
|
|
||||||
13 - logistics_info_id
|
|
||||||
14 - barcode
|
|
||||||
15 - primary_location
|
|
||||||
16 - auto_issue_location
|
|
||||||
17 - dynamic_locations
|
|
||||||
18 - location_data
|
|
||||||
19 - quantity_on_hand
|
|
||||||
20 - item_info_id
|
|
||||||
21 - barcode
|
|
||||||
22 - linked_items
|
|
||||||
23 - shopping_lists
|
|
||||||
24 - recipes
|
|
||||||
25 - groups <--
|
|
||||||
26 - packaging
|
|
||||||
27 - uom
|
|
||||||
28 - cost
|
|
||||||
29 - safety_stock
|
|
||||||
30 - lead_time_days
|
|
||||||
31 - ai_pick
|
|
||||||
32 - food_info_id
|
|
||||||
33 - food_groups
|
|
||||||
34 - ingrediants
|
|
||||||
35 - nutrients
|
|
||||||
36 - expires
|
|
||||||
*/
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
[site]
|
|
||||||
site_name=test2
|
|
||||||
site_owner=joe
|
|
||||||
email=jdoe@gmail.com
|
|
||||||
|
|
||||||
[defaults]
|
|
||||||
default_zone=default
|
|
||||||
default_primary_location=all
|
|
||||||
default_auto_issue_location=all
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS test2_brands (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
name VARCHAR(255)
|
|
||||||
);
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS test2_food_info (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
food_groups TEXT [],
|
|
||||||
ingrediants TEXT [],
|
|
||||||
nutrients JSONB,
|
|
||||||
expires BOOLEAN
|
|
||||||
);
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS test2_groups(
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
name VARCHAR(255) NOT NULL,
|
|
||||||
description TEXT,
|
|
||||||
included_items INTEGER [],
|
|
||||||
group_type VARCHAR(255),
|
|
||||||
UNIQUE (name)
|
|
||||||
);
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS test2_items(
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
barcode VARCHAR(255) NOT NULL,
|
|
||||||
item_name VARCHAR(255) NOT NULL,
|
|
||||||
brand INTEGER,
|
|
||||||
description TEXT,
|
|
||||||
tags TEXT [],
|
|
||||||
links JSONB,
|
|
||||||
item_info_id INTEGER NOT NULL,
|
|
||||||
logistics_info_id INTEGER NOT NULL,
|
|
||||||
food_info_id INTEGER,
|
|
||||||
row_type VARCHAR(255) NOT NULL,
|
|
||||||
item_type VARCHAR(255) NOT NULL,
|
|
||||||
search_string TEXT NOT NULL,
|
|
||||||
UNIQUE(barcode, item_info_id),
|
|
||||||
CONSTRAINT fk_item_info
|
|
||||||
FOREIGN KEY(item_info_id)
|
|
||||||
REFERENCES test2_item_info(id),
|
|
||||||
CONSTRAINT fk_food_info
|
|
||||||
FOREIGN KEY(food_info_id)
|
|
||||||
REFERENCES test2_food_info(id),
|
|
||||||
CONSTRAINT fk_brand
|
|
||||||
FOREIGN KEY(brand)
|
|
||||||
REFERENCES test2_brands(id),
|
|
||||||
CONSTRAINT fk_logistics_info
|
|
||||||
FOREIGN KEY(logistics_info_id)
|
|
||||||
REFERENCES test2_logistics_info(id)
|
|
||||||
);
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
CREATE TABLE IF NOt EXISTS test2_item_info (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
barcode VARCHAR(255) NOT NULL,
|
|
||||||
linked_items INTEGER [],
|
|
||||||
shopping_lists INTEGER [],
|
|
||||||
recipes INTEGER [],
|
|
||||||
groups INTEGER [],
|
|
||||||
packaging VARCHAR(255),
|
|
||||||
uom VARCHAR(255),
|
|
||||||
cost FLOAT8,
|
|
||||||
safety_stock FLOAT8,
|
|
||||||
lead_time_days FLOAT8,
|
|
||||||
ai_pick BOOLEAN,
|
|
||||||
UNIQUE(barcode)
|
|
||||||
);
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS test2_itemlinks (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
barcode VARCHAR(255) NOt NULL,
|
|
||||||
link INTEGER NOT NULL,
|
|
||||||
data JSONB NOT NULL,
|
|
||||||
conv_factor FLOAT8 NOt NULL,
|
|
||||||
UNIQUE(barcode)
|
|
||||||
);
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS test2_locations(
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
uuid VARCHAR(255) NOT NULL,
|
|
||||||
name VARCHAR(32) NOT NULL,
|
|
||||||
zone_id INTEGER NOT NULL,
|
|
||||||
items JSONB,
|
|
||||||
UNIQUE(uuid),
|
|
||||||
CONSTRAINT fk_zone
|
|
||||||
FOREIGN KEY(zone_id)
|
|
||||||
REFERENCES test2_zones(id)
|
|
||||||
);
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS logins(
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
username VARCHAR(255),
|
|
||||||
password VARCHAR(255),
|
|
||||||
favorites JSONB,
|
|
||||||
unseen_pantry_items INTEGER [],
|
|
||||||
unseen_groups INTEGER [],
|
|
||||||
unseen_shopping_lists INTEGER [],
|
|
||||||
unseen_recipes INTEGER [],
|
|
||||||
seen_pantry_items INTEGER [],
|
|
||||||
seen_groups INTEGER[],
|
|
||||||
seen_shopping_lists INTEGER [],
|
|
||||||
seen_recipes INTEGER [],
|
|
||||||
flags JSONB
|
|
||||||
);
|
|
||||||
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS test2_logistics_info(
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
barcode VARCHAR(255) NOT NULL,
|
|
||||||
primary_location VARCHAR(64),
|
|
||||||
auto_issue_location VARCHAR(64),
|
|
||||||
dynamic_locations JSONB,
|
|
||||||
location_data JSONB,
|
|
||||||
quantity_on_hand FLOAT8 NOT NULL,
|
|
||||||
UNIQUE(barcode)
|
|
||||||
);
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS test2_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)
|
|
||||||
);
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS test2_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)
|
|
||||||
);
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS test2_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
|
|
||||||
);
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS test2_shopping_lists (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
name VARCHAR(255) NOT NULL,
|
|
||||||
description TEXT,
|
|
||||||
pantry_items INTEGER [],
|
|
||||||
custom_items JSONB,
|
|
||||||
recipes INTEGER [],
|
|
||||||
groups INTEGER [],
|
|
||||||
quantities JSONB,
|
|
||||||
author INTEGER,
|
|
||||||
creation_date TIMESTAMP,
|
|
||||||
type VARCHAR(64),
|
|
||||||
UNIQUE(name)
|
|
||||||
);
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS test2_Transactions (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
timestamp TIMESTAMP,
|
|
||||||
logistics_info_id INTEGER NOT NULL,
|
|
||||||
barcode VARCHAR(255) NOT NULL,
|
|
||||||
name VARCHAR(255),
|
|
||||||
transaction_type VARCHAR(255) NOT NULL,
|
|
||||||
quantity FLOAT8 NOT NULL,
|
|
||||||
description TEXT,
|
|
||||||
user_id INTEGER NOT NULL,
|
|
||||||
data JSONB,
|
|
||||||
CONSTRAINT fk_logistics_info
|
|
||||||
FOREIGN KEY(logistics_info_id)
|
|
||||||
REFERENCES test2_logistics_info(id)
|
|
||||||
);
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS test2_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)
|
|
||||||
);
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS test2_zones(
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
name VARCHAR(32) NOT NULL,
|
|
||||||
UNIQUE(name)
|
|
||||||
);
|
|
||||||
@ -1 +0,0 @@
|
|||||||
DROP TABLE test2_brands CASCADE;
|
|
||||||
@ -1 +0,0 @@
|
|||||||
DROP TABLE test2_food_info CASCADE;
|
|
||||||
@ -1 +0,0 @@
|
|||||||
DROP TABLE test2_groups CASCADE;
|
|
||||||
@ -1 +0,0 @@
|
|||||||
DROP TABLE test2_item_info CASCADE;
|
|
||||||
@ -1 +0,0 @@
|
|||||||
DROP TABLE test2_items CASCADE;
|
|
||||||
@ -1 +0,0 @@
|
|||||||
DROP TABLE test2_itemlinks CASCADE;
|
|
||||||
@ -1 +0,0 @@
|
|||||||
DROP TABLE test2_locations CASCADE;
|
|
||||||
@ -1 +0,0 @@
|
|||||||
DROP TABLE test2_logistics_info CASCADE;
|
|
||||||
@ -1 +0,0 @@
|
|||||||
DROP TABLE test2_receipt_items CASCADE;
|
|
||||||
@ -1 +0,0 @@
|
|||||||
DROP TABLE test2_receipts CASCADE;
|
|
||||||
@ -1 +0,0 @@
|
|||||||
DROP TABLE test2_recipes CASCADE;
|
|
||||||
@ -1 +0,0 @@
|
|||||||
DROP TABLE test2_shopping_lists CASCADE;
|
|
||||||
@ -1 +0,0 @@
|
|||||||
DROP TABLE test2_transactions CASCADE;
|
|
||||||
@ -1 +0,0 @@
|
|||||||
DROP TABLE test2_vendors CASCADE;
|
|
||||||
@ -1 +0,0 @@
|
|||||||
DROP TABLE test2_zones CASCADE;
|
|
||||||
@ -1,45 +0,0 @@
|
|||||||
SELECT * FROM test2_items
|
|
||||||
LEFT JOIN test2_logistics_info ON test2_items.logistics_info_id = test2_logistics_info.id
|
|
||||||
LEFT JOIN test2_item_info ON test2_items.item_info_id = test2_item_info.id
|
|
||||||
LEFT JOIN test2_food_info ON test2_items.food_info_id = test2_food_info.id
|
|
||||||
WHERE test2_items.id=%s;
|
|
||||||
|
|
||||||
/*
|
|
||||||
00 - item_id
|
|
||||||
01 - barcode
|
|
||||||
02 - item_name
|
|
||||||
03 - brand (id)
|
|
||||||
04 - description
|
|
||||||
05 - tags
|
|
||||||
06 - links
|
|
||||||
07 - item_info_id
|
|
||||||
08 - logistics_info_id
|
|
||||||
09 - food_info_id
|
|
||||||
10 - row_type
|
|
||||||
11 - item_type
|
|
||||||
12 - search_string
|
|
||||||
13 - logistics_info_id
|
|
||||||
14 - barcode
|
|
||||||
15 - primary_location
|
|
||||||
16 - auto_issue_location
|
|
||||||
17 - dynamic_locations
|
|
||||||
18 - location_data
|
|
||||||
19 - quantity_on_hand
|
|
||||||
20 - item_info_id
|
|
||||||
21 - barcode
|
|
||||||
22 - linked_items
|
|
||||||
23 - shopping_lists
|
|
||||||
24 - recipes
|
|
||||||
25 - groups
|
|
||||||
26 - packaging
|
|
||||||
27 - uom
|
|
||||||
28 - cost
|
|
||||||
29 - safety_stock
|
|
||||||
30 - lead_time_days
|
|
||||||
31 - ai_pick
|
|
||||||
32 - food_info_id
|
|
||||||
33 - food_groups
|
|
||||||
34 - ingrediants
|
|
||||||
35 - nutrients
|
|
||||||
36 - expires
|
|
||||||
*/
|
|
||||||
@ -1,45 +0,0 @@
|
|||||||
async function addLink(){
|
|
||||||
event.preventDefault()
|
|
||||||
let key = document.getElementById('link_name').value;
|
|
||||||
let link = document.getElementById('link').value;
|
|
||||||
links[key] = link;
|
|
||||||
console.log(links)
|
|
||||||
await propagateLinks()
|
|
||||||
};
|
|
||||||
|
|
||||||
function updatePackaging(){
|
|
||||||
let packaging = document.getElementById('packaging').value;
|
|
||||||
item_info['packaging'] = packaging;
|
|
||||||
console.log(item_info)
|
|
||||||
};
|
|
||||||
|
|
||||||
function updateUOM(){
|
|
||||||
let uom = document.getElementById('uom').value;
|
|
||||||
item_info['uom'] = uom;
|
|
||||||
console.log(item_info)
|
|
||||||
};
|
|
||||||
|
|
||||||
function updateCost(){
|
|
||||||
let cost = document.getElementById('cost').value;
|
|
||||||
item_info['cost'] = parseFloat(cost);
|
|
||||||
console.log(item_info)
|
|
||||||
};
|
|
||||||
|
|
||||||
function updateSafetyStock(){
|
|
||||||
let safety_stock = document.getElementById('safety_stock').value;
|
|
||||||
item_info['safety_stock'] = parseFloat(safety_stock);
|
|
||||||
console.log(item_info)
|
|
||||||
};
|
|
||||||
|
|
||||||
function updateLeadTimeDays(){
|
|
||||||
let lead_time_days = document.getElementById('lead_time_days').value;
|
|
||||||
item_info['lead_time_days'] = parseFloat(lead_time_days);
|
|
||||||
console.log(item_info)
|
|
||||||
};
|
|
||||||
|
|
||||||
function updateAiPickable(){
|
|
||||||
let ai_pick = document.getElementById('ai_pickable');
|
|
||||||
item_info['ai_pick'] = ai_pick.checked;
|
|
||||||
console.log(item_info)
|
|
||||||
};
|
|
||||||
|
|
||||||
@ -1,352 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en" dir="ltr">
|
|
||||||
<head>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
|
||||||
<title>My Pantry - Groups</title>
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@materializecss/materialize@2.0.3-alpha/dist/css/materialize.min.css" />
|
|
||||||
<!-- Material Icons -->
|
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
|
||||||
<!-- Material Symbols - Outlined Set -->
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined" rel="stylesheet" />
|
|
||||||
<!-- Material Symbols - Rounded Set -->
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded" rel="stylesheet" />
|
|
||||||
<!-- Material Symbols - Sharp Set -->
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Sharp" rel="stylesheet" />
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@materializecss/materialize@2.0.3-alpha/dist/js/materialize.min.js"></script>
|
|
||||||
</head>
|
|
||||||
<style>
|
|
||||||
header, main, footer, body {
|
|
||||||
padding-left: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width : 992px) {
|
|
||||||
header, main, footer, body {
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.dropdown-disabled {
|
|
||||||
pointer-events: none;
|
|
||||||
opacity: 0.5; /* or your desired degree of transparency */
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
<ul id='dropdown1' class='dropdown-content'>
|
|
||||||
{% for site in sites %}
|
|
||||||
<li><button class="btn transparent black-text z-depth-0" onclick="changeSite('{{ site }}')">{{site}}</button></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
<ul id="slide-out" class="sidenav sidenav-fixed purple lighten-4" style="width: 250px;">
|
|
||||||
<li>
|
|
||||||
<div class="user-view">
|
|
||||||
<!-- <div class="background">
|
|
||||||
<img src="images/office.jpg">
|
|
||||||
</div> -->
|
|
||||||
<!-- <a href="#user"><img class="circle" src="images/yuna.jpg"></a> -->
|
|
||||||
<a href="#name"><span class="black-text name">John Doe</span></a>
|
|
||||||
<a href="#email"><span class="black-text email">jdoe@example.com</span></a>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<li><a class="dropdown-trigger dropdown-disabled" data-target="dropdown1">Current Site > {{current_site}}<i class="material-icons right">arrow_drop_down</i></a></li>
|
|
||||||
<li><div class="divider grey darken-1" style="margin-left: 5px; margin-right: 10px;"></div></li>
|
|
||||||
<li><a href="/items">Site Items</a></li>
|
|
||||||
<li><a href="/groups">Site Groups</a></li>
|
|
||||||
<li><a href="/shopping-lists">Site Shopping Lists</a></li>
|
|
||||||
</ul>
|
|
||||||
<body>
|
|
||||||
<div class="container">
|
|
||||||
<div class="section">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col s12">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col s2">
|
|
||||||
<h3><a href="#" data-target="slide-out" class="sidenav-trigger hide-on-large-only left"><i class="material-icons">menu</i></a></h3>
|
|
||||||
</div>
|
|
||||||
<div class="col s8 purple lighten-4" style="border-radius: 10px;">
|
|
||||||
<h3 id="group_name" class="center"></h3>
|
|
||||||
</div>
|
|
||||||
<div class="col s2">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col s12 p-2">
|
|
||||||
<h5 id="database_id"></h5>
|
|
||||||
</div>
|
|
||||||
<div class="col s12 m6 p-2">
|
|
||||||
<label for="group_type">Group Type</label>
|
|
||||||
<select id="group_type" class="browser-default">
|
|
||||||
<option value="plain">Plain</option>
|
|
||||||
<option value="calculated">Calculated</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="input-field col s12 p-2">
|
|
||||||
<textarea id="description" class="materialize-textarea" placeholder=" "></textarea>
|
|
||||||
<label for="description">Group Description</label>
|
|
||||||
</div>
|
|
||||||
<div class="divider col s12"></div>
|
|
||||||
<div class="col s3 p-2">
|
|
||||||
<button class="btn filled icon-left modal-trigger purple lighten-4 black-text z-depth-0" data-target="item_modal">Add Item</button>
|
|
||||||
</div>
|
|
||||||
<div id="table_div" class="col s12 p-2">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="fixed-action-btn">
|
|
||||||
<button class="btn-floating btn-large" onclick="saveGroup()">
|
|
||||||
<i class="large material-icons">save</i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div id="item_modal" class="modal">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="row" style="gap: 5px;">
|
|
||||||
<div class="col s12">
|
|
||||||
<h4>Add Items...</h4>
|
|
||||||
</div>
|
|
||||||
<div class="col s12">
|
|
||||||
<div class="card-panel purple lighten-4 z-depth-0">
|
|
||||||
<span class="black-text"> Here is where you can search, add, and remove items from this group by checking or unchecking the items below. You can select
|
|
||||||
multiple at a time or simply one. Utilize the search bar to filter down quickly to items you need or simply scroll to your hearts content.
|
|
||||||
<br><br>
|
|
||||||
<b>WARNING:</b> clicking the checkbox will not save the changes right off the bat! You <b>MUST</b> click the Update Items button!
|
|
||||||
<br><br>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<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 Items" value="">
|
|
||||||
</div>
|
|
||||||
<div class="col s12 center">
|
|
||||||
<a id="back" class="btn icon-left purple lighten-4 black-text z-depth-0"><i class="material-icons">chevron_left</i></a>
|
|
||||||
<a id="update_items" class="btn purple lighten-4 black-text z-depth-0">Update Items</a>
|
|
||||||
<a id="forward" class="btn icon-right purple lighten-4 black-text z-depth-0"><i class="material-icons">chevron_right</i></a>
|
|
||||||
</div>
|
|
||||||
<div class="divider col s12"></div>
|
|
||||||
<div id="checkbox-container" class="col s12">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
<script>
|
|
||||||
const group_id = {{id|tojson}}
|
|
||||||
let site = {{ current_site|tojson }}
|
|
||||||
|
|
||||||
let group;
|
|
||||||
let current_page = 1
|
|
||||||
let limit = 25
|
|
||||||
let checked_items = new Array();
|
|
||||||
let search_text = "";
|
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", async function(){
|
|
||||||
await fetchGroup()
|
|
||||||
|
|
||||||
var elems = document.querySelectorAll('select');
|
|
||||||
var instances = M.FormSelect.init(elems, {});
|
|
||||||
var elems = document.querySelectorAll('.modal');
|
|
||||||
var instances = M.Modal.init(elems, {});
|
|
||||||
var elems = document.querySelectorAll('.fixed-action-btn');
|
|
||||||
var instances = M.FloatingActionButton.init(elems, {});
|
|
||||||
var elems = document.querySelectorAll('.sidenav');
|
|
||||||
var instances = M.Sidenav.init(elems, {});
|
|
||||||
var elems = document.querySelectorAll('.dropdown-trigger');
|
|
||||||
var instances = M.Dropdown.init(elems, {});
|
|
||||||
var elems = document.querySelectorAll('.collapsible');
|
|
||||||
var instances = M.Collapsible.init(elems, {});
|
|
||||||
M.AutoInit();
|
|
||||||
|
|
||||||
await populateInfo()
|
|
||||||
await populateReferences(group[3])
|
|
||||||
update_list()
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
document.getElementById("search").addEventListener('keydown', function(event){
|
|
||||||
if(event.key === 'Enter'){
|
|
||||||
console.log(document.getElementById("search").value);
|
|
||||||
search_text = document.getElementById("search").value;
|
|
||||||
update_list()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
async function fetchGroup(){
|
|
||||||
const url = new URL('/getGroup', window.location.origin);
|
|
||||||
url.searchParams.append('id', group_id);
|
|
||||||
const response = await fetch(url);
|
|
||||||
data = await response.json();
|
|
||||||
group = data.group;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function populateInfo(){
|
|
||||||
var itemName = document.getElementById('group_name');
|
|
||||||
var databaseID = document.getElementById('database_id');
|
|
||||||
var selectElement = document.getElementById('group_type');
|
|
||||||
var description = document.getElementById('description');
|
|
||||||
|
|
||||||
itemName.innerHTML = group[1];
|
|
||||||
databaseID.innerHTML = `Database ID: ${group[0]}`;
|
|
||||||
selectElement.value = group[4];
|
|
||||||
selectElement.dispatchEvent(new Event('change'));
|
|
||||||
description.value = group[2];
|
|
||||||
M.Forms.textareaAutoResize(description);
|
|
||||||
|
|
||||||
|
|
||||||
checked_items = [];
|
|
||||||
|
|
||||||
for(let i=0; i < group[3].length; i++){
|
|
||||||
checked_items.push(group[3][i][0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function populateReferences(references){
|
|
||||||
var table_div = document.getElementById('table_div')
|
|
||||||
table_div.innerHTML = ""
|
|
||||||
|
|
||||||
var references_table = document.createElement('table')
|
|
||||||
references_table.classList.add("striped")
|
|
||||||
|
|
||||||
var tbl_header = document.createElement('thead')
|
|
||||||
tbl_header.innerHTML = `<tr><th>Database ID</th><th>Barcode</th><th>Item Name</th><th>QOH</th></tr>`
|
|
||||||
|
|
||||||
var tbl_body = document.createElement('tbody')
|
|
||||||
|
|
||||||
for (let i=0; i < references.length; i++){
|
|
||||||
var row = document.createElement('tr')
|
|
||||||
row.innerHTML = `
|
|
||||||
<td>${references[i][0]}</td>
|
|
||||||
<td>${references[i][1]}</td>
|
|
||||||
<td>${references[i][2]}</td>
|
|
||||||
<td>${references[i][3]}</td>
|
|
||||||
`
|
|
||||||
tbl_body.appendChild(row)
|
|
||||||
};
|
|
||||||
|
|
||||||
references_table.appendChild(tbl_header)
|
|
||||||
references_table.appendChild(tbl_body)
|
|
||||||
|
|
||||||
table_div.appendChild(references_table)
|
|
||||||
}
|
|
||||||
|
|
||||||
function save_checked(){
|
|
||||||
let checkboxes = document.querySelectorAll('.checkbox-class');
|
|
||||||
checkboxes.forEach((checkbox) => {
|
|
||||||
if (checked_items.includes(Number(checkbox.id)) && !checkbox.checked){
|
|
||||||
checked_items.splice(checked_items.indexOf(Number(Number(checkbox.id))), 1);
|
|
||||||
}
|
|
||||||
if (!checked_items.includes(Number(checkbox.id)) && checkbox.checked){
|
|
||||||
checked_items.push(Number(checkbox.id))
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function update_list(){
|
|
||||||
if (current_page == 1){
|
|
||||||
document.getElementById('back').classList.add("disabled")
|
|
||||||
}else{
|
|
||||||
document.getElementById('back').classList.remove("disabled")
|
|
||||||
};
|
|
||||||
|
|
||||||
const url = new URL('/getItems', window.location.origin);
|
|
||||||
url.searchParams.append('page', current_page);
|
|
||||||
url.searchParams.append('limit', limit);
|
|
||||||
url.searchParams.append('search_text', search_text);
|
|
||||||
|
|
||||||
let container = document.getElementById('checkbox-container')
|
|
||||||
fetch(url)
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
if (data.items.length < limit){
|
|
||||||
document.getElementById('forward').classList.add("disabled")
|
|
||||||
} else {
|
|
||||||
document.getElementById('forward').classList.remove("disabled")
|
|
||||||
}
|
|
||||||
|
|
||||||
const divi = document.createElement('div')
|
|
||||||
data.items.forEach(item => {
|
|
||||||
const checkboxHTML = document.createElement('p')
|
|
||||||
if (checked_items.includes(item[0])){
|
|
||||||
checkboxHTML.innerHTML = `<label>
|
|
||||||
<input class="checkbox-class" id=${item[0]} name=${item[0]} checked="checked" type="checkbox" />
|
|
||||||
<span>${item[1]} - ${item[2]}</span>
|
|
||||||
</label>`;
|
|
||||||
} else {
|
|
||||||
checkboxHTML.innerHTML = `<div class="col s12"><label>
|
|
||||||
<input class="checkbox-class" id=${item[0]} name=${item[0]} type="checkbox" />
|
|
||||||
<span>${item[1]} - ${item[2]}</span>
|
|
||||||
</label></div>`;
|
|
||||||
}
|
|
||||||
divi.appendChild(checkboxHTML)
|
|
||||||
})
|
|
||||||
|
|
||||||
container.innerHTML = divi.innerHTML
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async function saveGroup(){
|
|
||||||
save_checked()
|
|
||||||
var itemName = document.getElementById('group_name');
|
|
||||||
var selectElement = document.getElementById('group_type');
|
|
||||||
var description = document.getElementById('description');
|
|
||||||
|
|
||||||
await fetch(`/updateGroup`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
id: group[0],
|
|
||||||
name: itemName.innerHTML,
|
|
||||||
description:description.value,
|
|
||||||
items: checked_items,
|
|
||||||
group_type:selectElement.value,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
M.toast({text: "Group Saved!"})
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById('forward').addEventListener('click', () =>{
|
|
||||||
current_page++
|
|
||||||
save_checked()
|
|
||||||
update_list()
|
|
||||||
})
|
|
||||||
|
|
||||||
document.getElementById('back').addEventListener('click', () =>{
|
|
||||||
current_page--
|
|
||||||
save_checked()
|
|
||||||
update_list()
|
|
||||||
})
|
|
||||||
|
|
||||||
document.getElementById('update_items').addEventListener('click', async function(){
|
|
||||||
save_checked()
|
|
||||||
|
|
||||||
var itemName = document.getElementById('group_name');
|
|
||||||
var selectElement = document.getElementById('group_type');
|
|
||||||
var description = document.getElementById('description');
|
|
||||||
|
|
||||||
await fetch(`/updateGroup`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
id: group[0],
|
|
||||||
name: itemName.innerHTML,
|
|
||||||
description:description.value,
|
|
||||||
items: checked_items,
|
|
||||||
group_type:selectElement.value,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
|
|
||||||
var modalElem = document.querySelector('#item_modal');
|
|
||||||
var instance = M.Modal.getInstance(modalElem);
|
|
||||||
instance.close();
|
|
||||||
|
|
||||||
M.toast({text: "Group Saved!"})
|
|
||||||
await fetchGroup()
|
|
||||||
await populateInfo()
|
|
||||||
await populateReferences(group[3])
|
|
||||||
})
|
|
||||||
|
|
||||||
</script>
|
|
||||||
</html>
|
|
||||||
@ -1,448 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en" dir="ltr">
|
|
||||||
<head>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
|
||||||
<title>My Pantry - Groups</title>
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@materializecss/materialize@2.0.3-alpha/dist/css/materialize.min.css" />
|
|
||||||
<!-- Material Icons -->
|
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
|
||||||
<!-- Material Symbols - Outlined Set -->
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined" rel="stylesheet" />
|
|
||||||
<!-- Material Symbols - Rounded Set -->
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded" rel="stylesheet" />
|
|
||||||
<!-- Material Symbols - Sharp Set -->
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Sharp" rel="stylesheet" />
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@materializecss/materialize@2.0.3-alpha/dist/js/materialize.min.js"></script>
|
|
||||||
</head>
|
|
||||||
<style>
|
|
||||||
.hand-pointer {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
[type="radio"]:checked + span:after {
|
|
||||||
border: 2px solid rgb(177 156 217 / 30%); /* Outline color */
|
|
||||||
background-color: rgb(177 156 217 / 30%); /* Fill color */
|
|
||||||
}
|
|
||||||
header, main, footer, body {
|
|
||||||
padding-left: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width : 992px) {
|
|
||||||
header, main, footer, body {
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.dropdown-disabled {
|
|
||||||
pointer-events: none;
|
|
||||||
opacity: 0.5; /* or your desired degree of transparency */
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<!-- NAVBAR -->
|
|
||||||
<ul id='dropdown1' class='dropdown-content'>
|
|
||||||
{% for site in sites %}
|
|
||||||
<li><button class="btn transparent black-text z-depth-0" onclick="changeSite('{{ site }}')">{{site}}</button></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
<ul id="slide-out" class="sidenav sidenav-fixed purple lighten-4" style="width: 250px;">
|
|
||||||
<li>
|
|
||||||
<div class="user-view">
|
|
||||||
<!-- <div class="background">
|
|
||||||
<img src="images/office.jpg">
|
|
||||||
</div> -->
|
|
||||||
<!-- <a href="#user"><img class="circle" src="images/yuna.jpg"></a> -->
|
|
||||||
<a href="#name"><span class="black-text name">John Doe</span></a>
|
|
||||||
<a href="#email"><span class="black-text email">jdoe@example.com</span></a>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<li><a class="dropdown-trigger" data-target="dropdown1">Current Site > {{current_site}}<i class="material-icons right">arrow_drop_down</i></a></li>
|
|
||||||
<li><div class="divider grey darken-1" style="margin-left: 5px; margin-right: 10px;"></div></li>
|
|
||||||
<li><a href="/items">Site Items</a></li>
|
|
||||||
<li><a href="/groups">Site Groups</a></li>
|
|
||||||
<li><a href="/shopping-lists">Site Shopping Lists</a></li>
|
|
||||||
</ul>
|
|
||||||
<body>
|
|
||||||
<div class="container section" style="padding-bottom: 72px;">
|
|
||||||
<div class="row">
|
|
||||||
<!-- menu button, search bar, filters button -->
|
|
||||||
<div class="row col s12">
|
|
||||||
<div class="col-s3">
|
|
||||||
<a href="#" data-target="slide-out" class="sidenav-trigger hide-on-large-only left"><i class="material-icons">menu</i></a>
|
|
||||||
</div>
|
|
||||||
<div class="col s6 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 Items" value="">
|
|
||||||
</div>
|
|
||||||
<div class="col s3">
|
|
||||||
<a class="btn waves-effect waves-light center-align right tooltipped purple lighten-4 black-text text-darken-2 z-depth-0" data-position="bottom" data-tooltip="Open up filter options." style="margin-right: 5px; margin-top:0px; border-radius: 20px 10px 20px 10px;" onclick="hideFilters()"><i class="material-icons">tune</i></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col s12 hide" id="filter_options" style="padding: 20px">
|
|
||||||
<!-- Set the number of items -->
|
|
||||||
<div class="row center">
|
|
||||||
<div class="col s12">
|
|
||||||
<p>Set Items Per Page</p>
|
|
||||||
</div>
|
|
||||||
<div class="col s6 m4 l2">
|
|
||||||
<label>
|
|
||||||
<input name="group1" type="radio" checked onclick="changeLimit(25)"/>
|
|
||||||
<span>25 items</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="col s6 m4 l2">
|
|
||||||
<label>
|
|
||||||
<input name="group1" type="radio" onclick="changeLimit(50)"/>
|
|
||||||
<span>50 items</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="col s6 m4 l2">
|
|
||||||
<label>
|
|
||||||
<input name="group1" type="radio" onclick="changeLimit(75)"/>
|
|
||||||
<span>75 itesm</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="col s6 m4 l2">
|
|
||||||
<label>
|
|
||||||
<input name="group1" type="radio" onclick="changeLimit(100)"/>
|
|
||||||
<span>100 items</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="col s6 m4 l2">
|
|
||||||
<label>
|
|
||||||
<input name="group1" type="radio" onclick="changeLimit(150)"/>
|
|
||||||
<span>150 items</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="col s6 m4 l2">
|
|
||||||
<label>
|
|
||||||
<input name="group1" type="radio" onclick="changeLimit(200)"/>
|
|
||||||
<span>200 items</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="col s12 divider"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col s12 z-depth-0">
|
|
||||||
<div class="z-depth-0" id="cards">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col s12 center" id="pagination_list">
|
|
||||||
<ul class="pagination">
|
|
||||||
<li id="first" class="waves-effect hand-pointer"><a class="purple lighten-4" style="display: flex; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: 10px;"><i class="material-icons">first_page</i></a></li>
|
|
||||||
<li id="back" class="waves-effect hand-pointer" ><a class="purple lighten-4" style="display: flex; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: 10px;"><i class="material-icons">chevron_left</i></a></li>
|
|
||||||
<li id="current_page" style="padding-top: 7px; padding-left: 5px; padding-right: 5px; font-size: 18px;">page_number</li>
|
|
||||||
<li id="forward" class="waves-effect hand-pointer"><a class="purple lighten-4" style="display: flex; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: 10px;"><i class="material-icons">chevron_right</i></a></li>
|
|
||||||
<li id="last" class="waves-effect hand-pointer"><a class="purple lighten-4" style="display: flex; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: 10px;"><i class="material-icons">last_page</i></a></li>
|
|
||||||
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="fixed-action-btn">
|
|
||||||
<a class="btn-floating btn-large">
|
|
||||||
<i class="large material-icons">more_vert</i>
|
|
||||||
</a>
|
|
||||||
<ul>
|
|
||||||
<li><a class="btn-floating blue darken-1 modal-trigger" href="#modal1"><i class="material-icons">playlist_add</i></a></li>
|
|
||||||
<li><a class="btn-floating green darken-1"><i class="material-icons">download</i></a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="modal1" class="modal">
|
|
||||||
<div class="modal-content">
|
|
||||||
<h4>Adding a Group...</h4>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col s12">
|
|
||||||
<div class="card-panel purple lighten-4">
|
|
||||||
<span>Groups are internal "grouping" of items that can be of the same type, tag, process, etc. The point of them is that you
|
|
||||||
can keep track of all of one thing in a list for quick reference. There is three pieces of key information needed in order to
|
|
||||||
create a group; a name, a type, and a basic description.
|
|
||||||
<br><br>There are two types of groups you can create:
|
|
||||||
<br><br>
|
|
||||||
<b>Plain</b> - This group has no other extra features beyond being a place to keep a bunch of items categorized.
|
|
||||||
<br><br>
|
|
||||||
<b>Calculated</b> - This group will act a a substitutive group. The QOH of each item will be calculated together for you and when added to a
|
|
||||||
recipe the group will act as if the items can be used in place of each other.
|
|
||||||
<br><br>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col s12 m6 input-field p-2">
|
|
||||||
<input id="group_name" type="text" placeholder=" " maxlength="64">
|
|
||||||
<label for="group_name">Group Name</label>
|
|
||||||
</div>
|
|
||||||
<div class="input-field col s12 m6 p-2">
|
|
||||||
<select id="group_type">
|
|
||||||
<option value="plain" selected>Plain Group</option>
|
|
||||||
<option value="calculated">Calculated</option>
|
|
||||||
</select>
|
|
||||||
<label for="group_type">Group Type</label>
|
|
||||||
</div>
|
|
||||||
<div class="input-field col s12 p-2">
|
|
||||||
<textarea id="group_description" class="materialize-textarea" placeholder="A short description for what this group represents..."></textarea>
|
|
||||||
<label for="group_description">Group Description</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<a onclick="addGroup()" class="waves-effect btn">Add</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
<script>
|
|
||||||
let current_page = 1
|
|
||||||
let end_page = 10
|
|
||||||
let limit = 50
|
|
||||||
let filter_state = "hidden"
|
|
||||||
let site = {{ current_site|tojson }}
|
|
||||||
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
var elems = document.querySelectorAll('.collapsible');
|
|
||||||
var instances = M.Collapsible.init(elems, {
|
|
||||||
// specify options here
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
var elems = document.querySelectorAll('.fixed-action-btn');
|
|
||||||
var instances = M.FloatingActionButton.init(elems, {
|
|
||||||
// specify options here
|
|
||||||
});
|
|
||||||
});
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
var elems = document.querySelectorAll('.dropdown-trigger');
|
|
||||||
var instances = M.Dropdown.init(elems, {
|
|
||||||
alignment: 'right',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
update_list()
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
var elems = document.querySelectorAll('.modal');
|
|
||||||
var instances = M.Modal.init(elems, {
|
|
||||||
// specify options here
|
|
||||||
});
|
|
||||||
M.AutoInit();
|
|
||||||
});
|
|
||||||
|
|
||||||
async function changeSite(new_site){
|
|
||||||
console.log(`current_site: ${site}`)
|
|
||||||
console.log(`new site: ${new_site}`)
|
|
||||||
site = new_site
|
|
||||||
console.log(`current_site: ${site}`)
|
|
||||||
const url = new URL('/changeSite', window.location.origin);
|
|
||||||
url.searchParams.append('site', site)
|
|
||||||
await fetch(url)
|
|
||||||
location.reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
function update_list(){
|
|
||||||
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('/getGroups', window.location.origin);
|
|
||||||
url.searchParams.append('page', current_page);
|
|
||||||
url.searchParams.append('limit', limit);
|
|
||||||
fetch(url)
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
|
|
||||||
end_page = parseInt(data.end)
|
|
||||||
if (current_page === end_page){
|
|
||||||
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 {
|
|
||||||
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 cards = document.getElementById("cards")
|
|
||||||
const dummy_div = document.createElement('div')
|
|
||||||
const list_collapsible = document.createElement('ul')
|
|
||||||
list_collapsible.classList.add("collapsible")
|
|
||||||
list_collapsible.classList.add("popout")
|
|
||||||
list_collapsible.style = "background: white;"
|
|
||||||
|
|
||||||
data.groups.forEach(item => {
|
|
||||||
let qty = item[5]
|
|
||||||
let roundedQty = qty.toFixed(2)
|
|
||||||
var list_item = document.createElement('li')
|
|
||||||
list_item.classList.add("z-depth-0")
|
|
||||||
list_item.style = "background: white;"
|
|
||||||
|
|
||||||
var header = document.createElement("div")
|
|
||||||
header.classList.add("collapsible-header")
|
|
||||||
header.style = "box-shadow: none !important; background: white; border: 1px solid rgb(150 150 150 / 30%); border-radius: 10px 10px 0px 0px; align-items: center;"
|
|
||||||
if(item[4] == 'calculated'){
|
|
||||||
header.innerHTML = `<i class='material-symbols-outlined'>fastfood</i>${item[1]}
|
|
||||||
<span class="badge purple lighten-4 black-text text-darken-2 z-depth-0" style="border-radius: 5px;">QOH: ${roundedQty}</span>`
|
|
||||||
} else {
|
|
||||||
header.innerHTML = `<i class='material-symbols-outlined'>fastfood</i>${item[1]}`
|
|
||||||
}
|
|
||||||
|
|
||||||
var body = document.createElement('div')
|
|
||||||
body.classList.add("collapsible-body")
|
|
||||||
body.style = "box-shadow: none !important; background: rgb(177 156 217 / 10%); border: 1px solid rgb(177 156 217 / 30%); border-radius: 0px 0px 10px 10px;"
|
|
||||||
|
|
||||||
var description = document.createElement('div')
|
|
||||||
description.classList.add("col")
|
|
||||||
description.classList.add("s12")
|
|
||||||
|
|
||||||
var span_desc = document.createElement('span')
|
|
||||||
span_desc.innerHTML = `<span>${item[2]}</span><div class="divider s12" style="margin-bottom: 5px; margin-top: 5px;"></div>`
|
|
||||||
description.appendChild(span_desc)
|
|
||||||
|
|
||||||
var items_table = document.createElement('table')
|
|
||||||
items_table.classList.add("striped")
|
|
||||||
items_table.classList.add("hide-on-small-only")
|
|
||||||
|
|
||||||
|
|
||||||
var table_header = document.createElement('thead')
|
|
||||||
table_header.innerHTML = `<th>Barcode</th><th>Item Name</th><th>QOH</th>`
|
|
||||||
|
|
||||||
var table_body = document.createElement('tbody')
|
|
||||||
console.log(item[3])
|
|
||||||
let items = item[3]
|
|
||||||
for (let i=0; i < items.length; i++){
|
|
||||||
console.log(items[i])
|
|
||||||
var table_row = document.createElement('tr')
|
|
||||||
table_row.innerHTML = `
|
|
||||||
<td>${items[i][0]}</td>
|
|
||||||
<td>${items[i][1]}</td>
|
|
||||||
<td>${items[i][2]}</td>
|
|
||||||
`
|
|
||||||
table_body.appendChild(table_row)
|
|
||||||
}
|
|
||||||
|
|
||||||
items_table.append(table_header)
|
|
||||||
items_table.append(table_body)
|
|
||||||
|
|
||||||
description.appendChild(items_table)
|
|
||||||
|
|
||||||
var button_group = document.createElement('div')
|
|
||||||
button_group.classList.add("row")
|
|
||||||
button_group.style = "margin-bottom: 0px; padding-bottom: 0px;"
|
|
||||||
button_group.innerHTML = `
|
|
||||||
<div class="col s12" style="align-items: center; padding-top:10px;">
|
|
||||||
<a class="btn right purple lighten-4 black-text text-darken-2 z-depth-0 tooltipped" data-position="bottom" data-tooltip="Edit Group" style="display: inline-flex; border-radius: 20px 10px 20px 10px;" href="/group/${item[0]}">
|
|
||||||
<i class='material-icons'>edit</i>
|
|
||||||
</a>
|
|
||||||
<a class="btn right purple lighten-4 black-text text-darken-2 z-depth-0 tooltipped" data-position="left" data-tooltip="Transactions" style="display: inline-flex; margin-right: 5px; margin-top:0px; border-radius: 20px 10px 20px 10px;" disabled>
|
|
||||||
<i class='material-icons'>list_alt</i>
|
|
||||||
</a>
|
|
||||||
</div>`
|
|
||||||
|
|
||||||
body.appendChild(description)
|
|
||||||
body.appendChild(button_group)
|
|
||||||
|
|
||||||
list_item.appendChild(header)
|
|
||||||
list_item.appendChild(body)
|
|
||||||
|
|
||||||
list_collapsible.appendChild(list_item)
|
|
||||||
});
|
|
||||||
|
|
||||||
dummy_div.appendChild(list_collapsible)
|
|
||||||
|
|
||||||
cards.innerHTML = dummy_div.innerHTML
|
|
||||||
|
|
||||||
var elems = document.querySelectorAll('.collapsible');
|
|
||||||
var instances = M.Collapsible.init(elems, {
|
|
||||||
// specify options here
|
|
||||||
});
|
|
||||||
var elems = document.querySelectorAll('.tooltipped');
|
|
||||||
var instances = M.Tooltip.init(elems, {
|
|
||||||
// specify options here
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById("current_page").innerHTML = `${String(current_page)} / ${String(end_page)}`
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
function addGroup(){
|
|
||||||
var name = document.getElementById("group_name").value
|
|
||||||
var description = document.getElementById("group_description").value
|
|
||||||
var type = document.getElementById("group_type").value
|
|
||||||
const url = new URL('/addGroup', window.location.origin);
|
|
||||||
url.searchParams.append('name', name);
|
|
||||||
url.searchParams.append('description', description);
|
|
||||||
url.searchParams.append('type', type);
|
|
||||||
|
|
||||||
fetch(url)
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
M.toast({text: `Adding ${name}: ${data.state}`});
|
|
||||||
})
|
|
||||||
update_list()
|
|
||||||
var elem = document.getElementById("modal1");
|
|
||||||
var instance = M.Modal.getInstance(elem);
|
|
||||||
instance.close()
|
|
||||||
};
|
|
||||||
|
|
||||||
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', () =>{
|
|
||||||
if (!(document.getElementById("forward").classList.contains("disabled"))){
|
|
||||||
current_page++
|
|
||||||
update_list();
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById('back').addEventListener('click', () =>{
|
|
||||||
if (!(document.getElementById("back").classList.contains("disabled"))){
|
|
||||||
current_page--
|
|
||||||
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();
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
@ -2,7 +2,7 @@
|
|||||||
<html lang="en" dir="ltr">
|
<html lang="en" dir="ltr">
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
||||||
<title>My Pantry - Items</title>
|
<title>My Pantry</title>
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@materializecss/materialize@2.0.3-alpha/dist/css/materialize.min.css" />
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@materializecss/materialize@2.0.3-alpha/dist/css/materialize.min.css" />
|
||||||
<!-- Material Icons -->
|
<!-- Material Icons -->
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
||||||
@ -22,57 +22,17 @@
|
|||||||
border: 2px solid rgb(0 128 0 / 30%); /* Outline color */
|
border: 2px solid rgb(0 128 0 / 30%); /* Outline color */
|
||||||
background-color: rgb(0 128 0 / 30%); /* Fill color */
|
background-color: rgb(0 128 0 / 30%); /* Fill color */
|
||||||
}
|
}
|
||||||
header, main, footer, body {
|
|
||||||
padding-left: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width : 992px) {
|
|
||||||
header, main, footer, body {
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.dropdown-disabled {
|
|
||||||
pointer-events: none;
|
|
||||||
opacity: 0.5; /* or your desired degree of transparency */
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<ul id='dropdown1' class='dropdown-content'>
|
|
||||||
{% for site in sites %}
|
|
||||||
<li><button class="btn transparent black-text z-depth-0" onclick="changeSite('{{ site }}')">{{site}}</button></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
<ul id="slide-out" class="sidenav sidenav-fixed green lighten-4" style="width: 250px;">
|
|
||||||
<li>
|
|
||||||
<div class="user-view">
|
|
||||||
<!-- <div class="background">
|
|
||||||
<img src="images/office.jpg">
|
|
||||||
</div> -->
|
|
||||||
<!-- <a href="#user"><img class="circle" src="images/yuna.jpg"></a> -->
|
|
||||||
<a href="#name"><span class="black-text name">John Doe</span></a>
|
|
||||||
<a href="#email"><span class="black-text email">jdoe@example.com</span></a>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<li><a class="dropdown-trigger" data-target="dropdown1">Current Site > {{current_site}}<i class="material-icons right">arrow_drop_down</i></a></li>
|
|
||||||
<li><div class="divider grey darken-1" style="margin-left: 5px; margin-right: 10px;"></div></li>
|
|
||||||
<li class="active"><a href="/items">Site Items</a></li>
|
|
||||||
<li><a href="/groups">Site Groups</a></li>
|
|
||||||
<li><a href="/shopping-lists">Site Shopping Lists</a></li>
|
|
||||||
</ul>
|
|
||||||
<body>
|
<body>
|
||||||
<div class="container section" style="padding-bottom: 72px;">
|
<div class="container section" style="padding-bottom: 72px;">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="row col s12">
|
<div class="col s9 m6 offset-m3 input-field outlined align-center">
|
||||||
<div class="col-s3">
|
<i class="material-icons prefix">search</i>
|
||||||
<a href="#" data-target="slide-out" class="sidenav-trigger hide-on-large-only left"><i class="material-icons">menu</i></a>
|
<input style="border-radius: 20px; border: 1px solid #ccc;" id="search" name="search" type="search" placeholder="Search" value="">
|
||||||
</div>
|
</div>
|
||||||
<div class="col s6 m6 offset-m3 input-field outlined align-center">
|
<div class="col s3">
|
||||||
<i class="material-icons prefix">search</i>
|
<a class="btn waves-effect waves-light center-align right tooltipped green lighten-3 black-text text-darken-2 z-depth-0" data-position="bottom" data-tooltip="Open up filter options." style="margin-right: 5px; margin-top:0px; border-radius: 20px 10px 20px 10px;" onclick="hideFilters()"><i class="material-icons">tune</i></a>
|
||||||
<input style="border-radius: 20px; border: 1px solid #ccc;" id="search" name="search" type="search" placeholder="Search Items" value="">
|
|
||||||
</div>
|
|
||||||
<div class="col s3">
|
|
||||||
<a class="btn waves-effect waves-light center-align right tooltipped green lighten-3 black-text text-darken-2 z-depth-0" data-position="bottom" data-tooltip="Open up filter options." style="margin-right: 5px; margin-top:0px; border-radius: 20px 10px 20px 10px;" onclick="hideFilters()"><i class="material-icons">tune</i></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col s12 hide" id="filter_options" style="padding: 20px">
|
<div class="col s12 hide" id="filter_options" style="padding: 20px">
|
||||||
<!-- This is for basic views -->
|
<!-- This is for basic views -->
|
||||||
@ -186,64 +146,10 @@
|
|||||||
<i class="large material-icons">more_vert</i>
|
<i class="large material-icons">more_vert</i>
|
||||||
</a>
|
</a>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a class="btn-floating blue darken-1 modal-trigger" href="#modal1"><i class="material-icons">playlist_add</i></a></li>
|
<li><a class="btn-floating blue darken-1"><i class="material-icons">playlist_add</i></a></li>
|
||||||
<li><a class="btn-floating green darken-1"><i class="material-icons">download</i></a></li>
|
<li><a class="btn-floating green darken-1"><i class="material-icons">download</i></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div id="modal1" class="modal">
|
|
||||||
<div class="modal-content">
|
|
||||||
<h4>Adding a Item...</h4>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col s12">
|
|
||||||
<div class="card-panel green lighten-4">
|
|
||||||
<span>Adding an item is the main feature of your inventory. Items are used to store information and the most pertinant variables
|
|
||||||
around the items in your system and act as the foundation of all other aspects and features of this system. To create an Item
|
|
||||||
the 4 most important fields you need is the barcode (this is the unique number the item is tracked under), a name, an item type,
|
|
||||||
and finally a subtype.
|
|
||||||
<br><br>There are two item types of items you can create:
|
|
||||||
<br><br>
|
|
||||||
<b>Single Item</b> - These are the bare bone item as you might think it to be. Its a barcode that gets moved about your system and tracked by that
|
|
||||||
barcode.
|
|
||||||
<br><br>
|
|
||||||
<b>Linked Item</b> - Linked Items are a more advanced feature that allows you to chain together barcodes into one. Whenever one of those barcodes
|
|
||||||
are transacted on it is instead replaced with its linked barcode. This allows you to chain together the same types of items together if you want.
|
|
||||||
I.E. you have multiple barcodes for Chicken Noodle Soup and want them all to be the same item instead of seperate items in your system.
|
|
||||||
<br><br>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col s12 m6 input-field p-2">
|
|
||||||
<input id="barcode" type="text" placeholder=" " maxlength="64">
|
|
||||||
<label for="barcode">Barcode</label>
|
|
||||||
</div>
|
|
||||||
<div class="col s12 m6 input-field p-2">
|
|
||||||
<input id="item_name" type="text" placeholder=" " maxlength="64">
|
|
||||||
<label for="item_name">Item Name</label>
|
|
||||||
</div>
|
|
||||||
<div class="input-field col s12 m6 p-2">
|
|
||||||
<select id="item_type">
|
|
||||||
<option value="single" selected>Single Item</option>
|
|
||||||
<option value="linked">Linked Item</option>
|
|
||||||
</select>
|
|
||||||
<label for="item_type">Item Type</label>
|
|
||||||
</div>
|
|
||||||
<div class="input-field col s12 m6 p-2">
|
|
||||||
<select id="sub_type">
|
|
||||||
<option value="FOOD" selected>Food</option>
|
|
||||||
<option value="OTHER">Other</option>
|
|
||||||
</select>
|
|
||||||
<label for="sub_type">Sub Type</label>
|
|
||||||
</div>
|
|
||||||
<div class="input-field col s12 p-2">
|
|
||||||
<textarea id="item_description" class="materialize-textarea" placeholder="A short description for what this group represents..."></textarea>
|
|
||||||
<label for="item_description">Item Description</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<a onclick="addItem()" class="waves-effect btn green lighten-4">Add</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
<script>
|
<script>
|
||||||
@ -254,8 +160,6 @@
|
|||||||
let limit = 50
|
let limit = 50
|
||||||
let filter_state = "hidden"
|
let filter_state = "hidden"
|
||||||
let searchText = ""
|
let searchText = ""
|
||||||
let site = {{ current_site|tojson }}
|
|
||||||
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
var elems = document.querySelectorAll('.collapsible');
|
var elems = document.querySelectorAll('.collapsible');
|
||||||
@ -270,15 +174,9 @@
|
|||||||
// specify options here
|
// specify options here
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
var elems = document.querySelectorAll('.dropdown-trigger');
|
|
||||||
var instances = M.Dropdown.init(elems, {
|
|
||||||
alignment: 'right',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', async function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
await update_list()
|
update_list()
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
@ -289,18 +187,8 @@
|
|||||||
M.AutoInit();
|
M.AutoInit();
|
||||||
});
|
});
|
||||||
|
|
||||||
async function changeSite(new_site){
|
|
||||||
console.log(`current_site: ${site}`)
|
|
||||||
console.log(`new site: ${new_site}`)
|
|
||||||
site = new_site
|
|
||||||
console.log(`current_site: ${site}`)
|
|
||||||
const url = new URL('/changeSite', window.location.origin);
|
|
||||||
url.searchParams.append('site', site)
|
|
||||||
await fetch(url)
|
|
||||||
location.reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function update_list(){
|
function update_list(){
|
||||||
console.log(current_page)
|
console.log(current_page)
|
||||||
if (current_page === 1){
|
if (current_page === 1){
|
||||||
document.getElementById('back').classList.add("disabled")
|
document.getElementById('back').classList.add("disabled")
|
||||||
@ -324,7 +212,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
await fetch(url)
|
fetch(url)
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
|
|
||||||
@ -358,7 +246,7 @@
|
|||||||
list_collapsible.style = "background: white;"
|
list_collapsible.style = "background: white;"
|
||||||
|
|
||||||
data.items.forEach(item => {
|
data.items.forEach(item => {
|
||||||
let qty = item[19]
|
let qty = item[18]
|
||||||
let roundedQty = qty.toFixed(2)
|
let roundedQty = qty.toFixed(2)
|
||||||
var list_item = document.createElement('li')
|
var list_item = document.createElement('li')
|
||||||
list_item.classList.add("z-depth-0")
|
list_item.classList.add("z-depth-0")
|
||||||
@ -385,7 +273,7 @@
|
|||||||
button_group.style = "margin-bottom: 0px; padding-bottom: 0px;"
|
button_group.style = "margin-bottom: 0px; padding-bottom: 0px;"
|
||||||
button_group.innerHTML = `
|
button_group.innerHTML = `
|
||||||
<div class="col s12" style="align-items: center;">
|
<div class="col s12" style="align-items: center;">
|
||||||
<a href="/getItem?id=${item[0]}"class="btn right green lighten-3 black-text text-darken-2 z-depth-0 tooltipped" data-position="bottom" data-tooltip="Edit Item" style="display: inline-flex; border-radius: 20px 10px 20px 10px;">
|
<a class="btn right green lighten-3 black-text text-darken-2 z-depth-0 tooltipped" data-position="bottom" data-tooltip="Edit Item" style="display: inline-flex; border-radius: 20px 10px 20px 10px;">
|
||||||
<i class='material-icons'>edit</i>
|
<i class='material-icons'>edit</i>
|
||||||
</a>
|
</a>
|
||||||
<a class="btn right green lighten-3 black-text text-darken-2 z-depth-0 tooltipped" data-position="left" data-tooltip="Transactions" style="display: inline-flex; margin-right: 5px; margin-top:0px; border-radius: 20px 10px 20px 10px;">
|
<a class="btn right green lighten-3 black-text text-darken-2 z-depth-0 tooltipped" data-position="left" data-tooltip="Transactions" style="display: inline-flex; margin-right: 5px; margin-top:0px; border-radius: 20px 10px 20px 10px;">
|
||||||
@ -421,20 +309,20 @@
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
async function changeLimit(limit_value){
|
function changeLimit(limit_value){
|
||||||
limit = limit_value
|
limit = limit_value
|
||||||
current_page = 1
|
current_page = 1
|
||||||
await update_list()
|
update_list()
|
||||||
};
|
};
|
||||||
|
|
||||||
async function changeSort(order){
|
function changeSort(order){
|
||||||
sort_order = order
|
sort_order = order
|
||||||
await update_list()
|
update_list()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function changeView(view_num){
|
function changeView(view_num){
|
||||||
view = view_num
|
view = view_num
|
||||||
await update_list()
|
update_list()
|
||||||
}
|
}
|
||||||
|
|
||||||
function hideFilters(){
|
function hideFilters(){
|
||||||
@ -447,68 +335,40 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById('forward').addEventListener('click', async () =>{
|
document.getElementById('forward').addEventListener('click', () =>{
|
||||||
if (!(document.getElementById("forward").classList.contains("disabled"))){
|
if (!(document.getElementById("forward").classList.contains("disabled"))){
|
||||||
current_page++
|
current_page++
|
||||||
await update_list();
|
update_list();
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById('back').addEventListener('click', async () =>{
|
document.getElementById('back').addEventListener('click', () =>{
|
||||||
if (!(document.getElementById("back").classList.contains("disabled"))){
|
if (!(document.getElementById("back").classList.contains("disabled"))){
|
||||||
current_page--
|
current_page--
|
||||||
await update_list();
|
update_list();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById('last').addEventListener('click', async () =>{
|
document.getElementById('last').addEventListener('click', () =>{
|
||||||
if(!(document.getElementById("last").classList.contains("disabled"))){
|
if(!(document.getElementById("last").classList.contains("disabled"))){
|
||||||
current_page = end_page
|
current_page = end_page
|
||||||
await update_list();
|
update_list();
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById('first').addEventListener('click', async () =>{
|
document.getElementById('first').addEventListener('click', () =>{
|
||||||
if (!(document.getElementById("first").classList.contains("disabled"))){
|
if (!(document.getElementById("first").classList.contains("disabled"))){
|
||||||
current_page = 1
|
current_page = 1
|
||||||
await update_list();
|
update_list();
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
document.querySelector("#search").addEventListener('change', async () => {
|
document.querySelector("#search").addEventListener('change', () =>{
|
||||||
searchText = document.getElementById("search").value;
|
searchText = document.getElementById("search").value;
|
||||||
current_page = 1;
|
current_page = 1
|
||||||
await update_list();
|
update_list()
|
||||||
});
|
});
|
||||||
|
|
||||||
async function addItem(){
|
|
||||||
let barcode = document.getElementById("barcode")
|
|
||||||
let item_name = document.getElementById("item_name")
|
|
||||||
let item_type = document.getElementById("item_type")
|
|
||||||
let sub_type = document.getElementById("sub_type")
|
|
||||||
let description = document.getElementById("item_description")
|
|
||||||
|
|
||||||
const url = new URL('/addItem', window.location.origin);
|
|
||||||
url.searchParams.append('barcode', barcode.value);
|
|
||||||
url.searchParams.append('item_name', item_name.value);
|
|
||||||
url.searchParams.append('item_type', item_type.value);
|
|
||||||
url.searchParams.append('sub_type', sub_type.value);
|
|
||||||
url.searchParams.append('item_description', description.value);
|
|
||||||
|
|
||||||
|
|
||||||
await fetch(url)
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
M.toast({text: `Adding ${name}: ${data.state}`});
|
|
||||||
})
|
|
||||||
await update_list()
|
|
||||||
var elem = document.getElementById("modal1");
|
|
||||||
var instance = M.Modal.getInstance(elem);
|
|
||||||
instance.close()
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
138
templates/item_page/index.html
Normal file
138
templates/item_page/index.html
Normal 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>
|
||||||
@ -1,270 +0,0 @@
|
|||||||
<!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" />
|
|
||||||
<!-- Material Icons -->
|
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
|
||||||
<!-- Material Symbols - Outlined Set -->
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined" rel="stylesheet" />
|
|
||||||
<!-- Material Symbols - Rounded Set -->
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded" rel="stylesheet" />
|
|
||||||
<!-- Material Symbols - Sharp Set -->
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Sharp" rel="stylesheet" />
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@materializecss/materialize@2.0.3-alpha/dist/js/materialize.min.js"></script>
|
|
||||||
</head>
|
|
||||||
<style>
|
|
||||||
header, main, footer, body {
|
|
||||||
padding-left: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width : 992px) {
|
|
||||||
header, main, footer, body {
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.dropdown-disabled {
|
|
||||||
pointer-events: none;
|
|
||||||
opacity: 0.5; /* or your desired degree of transparency */
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<ul id='dropdown1' class='dropdown-content'>
|
|
||||||
{% for site in sites %}
|
|
||||||
<li><button class="btn transparent black-text z-depth-0" onclick="changeSite('{{ site }}')">{{site}}</button></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
<ul id="slide-out" class="sidenav sidenav-fixed green lighten-4" style="width: 250px;">
|
|
||||||
<li>
|
|
||||||
<div class="user-view">
|
|
||||||
<!-- <div class="background">
|
|
||||||
<img src="images/office.jpg">
|
|
||||||
</div> -->
|
|
||||||
<!-- <a href="#user"><img class="circle" src="images/yuna.jpg"></a> -->
|
|
||||||
<a href="#name"><span class="black-text name">John Doe</span></a>
|
|
||||||
<a href="#email"><span class="black-text email">jdoe@example.com</span></a>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<li><a class="dropdown-trigger dropdown-disabled" data-target="dropdown1">Current Site > {{current_site}}<i class="material-icons right">arrow_drop_down</i></a></li>
|
|
||||||
<li><div class="divider grey darken-1" style="margin-left: 5px; margin-right: 10px;"></div></li>
|
|
||||||
<li><a href="/items">Site Items</a></li>
|
|
||||||
<li><a href="/groups">Site Groups</a></li>
|
|
||||||
<li><a href="/shopping-lists">Site Shopping Lists</a></li>
|
|
||||||
</ul>
|
|
||||||
<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="single">item</option>
|
|
||||||
<option value="linked">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">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col s6" style="padding-top: 10px;">
|
|
||||||
<span style="font-size: 16px;">Links</span>
|
|
||||||
</div>
|
|
||||||
<div class="col s6">
|
|
||||||
<button class="btn btn-small btn-flat right modal-trigger green lighten-4 black-text" data-target="web-modal" style="margin-top: 5px; padding-bottom: 10px;">Add Link</button>
|
|
||||||
</div>
|
|
||||||
<div class="col s12 p-3">
|
|
||||||
<div id="weblinks">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</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">
|
|
||||||
<div class="row" style="gap: 10px; padding-top: 10px;">
|
|
||||||
<div class="col s6 m4 input-field outlined item_info_target">
|
|
||||||
<input onchange="updatePackaging()" id="packaging" type="text" placeholder=" " maxlength="32">
|
|
||||||
<label for="packaging">Packaging</label>
|
|
||||||
</div>
|
|
||||||
<div class="col s6 m4 input-field outlined item_info_target">
|
|
||||||
<input onchange="updateUOM()" id="uom" type="text" placeholder=" " maxlength="32">
|
|
||||||
<label for="uom">Unit of Measure</label>
|
|
||||||
</div>
|
|
||||||
<div class="col s6 m4 input-field outlined item_info_target">
|
|
||||||
<input onchange="updateCost()" id="cost" type="number" placeholder=" " maxlength="32">
|
|
||||||
<label for="cost">Cost</label>
|
|
||||||
</div>
|
|
||||||
<div class="col s6 m4 input-field outlined item_info_target">
|
|
||||||
<input onchange="updateSafetyStock()" id="safety_stock" type="number" placeholder=" " maxlength="32">
|
|
||||||
<label for="safety_stock">Safety Stock</label>
|
|
||||||
</div>
|
|
||||||
<div class="col s6 m4 input-field outlined item_info_target">
|
|
||||||
<input onchange="updateLeadTimeDays()" id="lead_time_days" type="number" placeholder=" " maxlength="32">
|
|
||||||
<label for="lead_time_days">Leadtime (Days)</label>
|
|
||||||
</div>
|
|
||||||
<div class="col s6 m4 center">
|
|
||||||
<p>
|
|
||||||
<label>
|
|
||||||
<input onclick="updateAiPickable()" id="ai_pickable" type="checkbox" />
|
|
||||||
<span>AI Pickable</span>
|
|
||||||
</label>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<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>
|
|
||||||
<div id="web-modal" class="modal">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col s12">
|
|
||||||
<h4>Add Weblink to Item...</h4>
|
|
||||||
</div>
|
|
||||||
<div class="col s12">
|
|
||||||
<div class="card-panel green lighten-4">
|
|
||||||
<span>Add a link to your favorite sites! Provide a name for the link and the link itself
|
|
||||||
and it will show up in this list. You should always try to provide a link with the name "<b>main</b>", as this
|
|
||||||
will be used in recipes and shopping lists for where the item will lead to.
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="s12 m6 input-field">
|
|
||||||
<input id="link_name" type="text" placeholder="main" maxlength="20">
|
|
||||||
<label for="link_name">Link Name</label>
|
|
||||||
<!--<span class="supporting-text">Supporting Text</span>-->
|
|
||||||
</div>
|
|
||||||
<div class="input-field col s12">
|
|
||||||
<textarea id="link" class="materialize-textarea" placeholder="a weblink to a website..."></textarea>
|
|
||||||
<label for="link">Textarea with placeholder</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button onclick="addLink()" class="waves-effect green lighten-4 btn-flat">Add</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
<script src="{{ url_for('static', filename='itemHandler.js') }}"></script>
|
|
||||||
<script>
|
|
||||||
const item = {{ item|tojson }}
|
|
||||||
var reference_state = 1
|
|
||||||
let links = {};
|
|
||||||
let updated = {};
|
|
||||||
let item_info = {};
|
|
||||||
let food_info = {};
|
|
||||||
let logistics_info = {};
|
|
||||||
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', async 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, {});
|
|
||||||
|
|
||||||
var elems = document.querySelectorAll('.modal');
|
|
||||||
var instances = M.Modal.init(elems, {});
|
|
||||||
|
|
||||||
await propagateInfo()
|
|
||||||
populateReferences(item[23], 'shopping_list')
|
|
||||||
populateReferences(item[24], 'recipe')
|
|
||||||
populateReferences(item[25], 'group')
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
async function propagateInfo(){
|
|
||||||
const entryType = document.getElementById('entry_type');
|
|
||||||
entryType.value = item[10];
|
|
||||||
const itemType = document.getElementById('item_type');
|
|
||||||
itemType.value = item[11];
|
|
||||||
await propagateLinks()
|
|
||||||
}
|
|
||||||
|
|
||||||
async function propagateLinks(){
|
|
||||||
var element = document.getElementById("weblinks");
|
|
||||||
element.innerHTML = ""
|
|
||||||
for (let key in links){
|
|
||||||
var link = document.createElement("a");
|
|
||||||
link.classList.add("btn-small");
|
|
||||||
link.classList.add("btn-flat");
|
|
||||||
link.classList.add("green");
|
|
||||||
link.classList.add("lighten-4");
|
|
||||||
link.target = "_blank";
|
|
||||||
link.style = "border-radius: 20px; margin-right: 5px; vertical-align: middle;";
|
|
||||||
link.innerHTML = `<i class="material-icons">open_in_new</i>${String(key)}`;
|
|
||||||
link.href = links[key];
|
|
||||||
element.appendChild(link);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function addLink(){
|
|
||||||
event.preventDefault()
|
|
||||||
let key = document.getElementById('link_name').value;
|
|
||||||
let link = document.getElementById('link').value;
|
|
||||||
links[key] = link;
|
|
||||||
console.log(links)
|
|
||||||
await propagateLinks()
|
|
||||||
}
|
|
||||||
|
|
||||||
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][1])
|
|
||||||
|
|
||||||
if ((reference_state % 2) == 0){
|
|
||||||
row.style = "background-color: gainsboro;"
|
|
||||||
}
|
|
||||||
reference_state++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
</html>
|
|
||||||
@ -1,525 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en" dir="ltr">
|
|
||||||
<head>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
|
||||||
<title>My Pantry</title>
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@materializecss/materialize@2.0.3-alpha/dist/css/materialize.min.css" />
|
|
||||||
<!-- Material Icons -->
|
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
|
||||||
<!-- Material Symbols - Outlined Set -->
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined" rel="stylesheet" />
|
|
||||||
<!-- Material Symbols - Rounded Set -->
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded" rel="stylesheet" />
|
|
||||||
<!-- Material Symbols - Sharp Set -->
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Sharp" rel="stylesheet" />
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@materializecss/materialize@2.0.3-alpha/dist/js/materialize.min.js"></script>
|
|
||||||
</head>
|
|
||||||
<style>
|
|
||||||
header, main, footer, body {
|
|
||||||
padding-left: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width : 992px) {
|
|
||||||
header, main, footer, body {
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.dropdown-disabled {
|
|
||||||
pointer-events: none;
|
|
||||||
opacity: 0.5; /* or your desired degree of transparency */
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
<ul id='dropdown1' class='dropdown-content'>
|
|
||||||
{% for site in sites %}
|
|
||||||
<li><button class="btn transparent black-text z-depth-0" onclick="changeSite('{{ site }}')">{{site}}</button></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
<ul id="slide-out" class="sidenav sidenav-fixed orange lighten-4" style="width: 250px;">
|
|
||||||
<li>
|
|
||||||
<div class="user-view">
|
|
||||||
<!-- <div class="background">
|
|
||||||
<img src="images/office.jpg">
|
|
||||||
</div> -->
|
|
||||||
<!-- <a href="#user"><img class="circle" src="images/yuna.jpg"></a> -->
|
|
||||||
<a href="#name"><span class="black-text name">John Doe</span></a>
|
|
||||||
<a href="#email"><span class="black-text email">jdoe@example.com</span></a>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<li><a class="dropdown-trigger dropdown-disabled" data-target="dropdown1">Current Site > {{current_site}}<i class="material-icons right">arrow_drop_down</i></a></li>
|
|
||||||
<li><div class="divider grey darken-1" style="margin-left: 5px; margin-right: 10px;"></div></li>
|
|
||||||
<li><a href="/items">Site Items</a></li>
|
|
||||||
<li><a href="/groups">Site Groups</a></li>
|
|
||||||
<li><a href="/shopping-lists">Site Shopping Lists</a></li>
|
|
||||||
</ul>
|
|
||||||
<body>
|
|
||||||
<div class="container">
|
|
||||||
<div class="section">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col s12">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col s2">
|
|
||||||
<h3><a href="#" data-target="slide-out" class="sidenav-trigger hide-on-large-only left"><i class="material-icons">menu</i></a></h3>
|
|
||||||
</div>
|
|
||||||
<div class="col s8 orange lighten-4" style="border-radius: 10px;">
|
|
||||||
<h3 id="list_name" class="center"></h3>
|
|
||||||
</div>
|
|
||||||
<div class="col s2">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col s12 p-2">
|
|
||||||
<h5 id="database_id"></h5>
|
|
||||||
</div>
|
|
||||||
<div class="col s12 m6 p-2">
|
|
||||||
<label for="list_type">List Type</label>
|
|
||||||
<select id="list_type" class="browser-default">
|
|
||||||
<option value="plain">Plain</option>
|
|
||||||
<option value="calculated">Calculated</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="input-field col s12 p-2">
|
|
||||||
<textarea id="description" class="materialize-textarea" placeholder=" "></textarea>
|
|
||||||
<label for="description">Group Description</label>
|
|
||||||
</div>
|
|
||||||
<div class="divider col s12"></div>
|
|
||||||
<div class="col s12 p-2">
|
|
||||||
<button class="btn filled icon-left modal-trigger orange lighten-4 black-text z-depth-0" data-target="item_modal">Add Item</button>
|
|
||||||
<button class="btn filled icon-left modal-trigger orange lighten-4 black-text z-depth-0" data-target="custom_modal">Add Custom</button>
|
|
||||||
<button class="btn filled icon-left modal-trigger orange lighten-4 black-text z-depth-0 disabled" data-target="item_modal">Add Recipe</button>
|
|
||||||
<button class="btn filled icon-left modal-trigger orange lighten-4 black-text z-depth-0 disabled" data-target="item_modal">Add Group</button>
|
|
||||||
</div>
|
|
||||||
<div id="table_div" class="col s12 p-2">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="fixed-action-btn">
|
|
||||||
<button class="btn-floating btn-large" onclick="saveList()">
|
|
||||||
<i class="large material-icons">save</i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="item_modal" class="modal">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="row" style="gap: 5px;">
|
|
||||||
<div class="col s12">
|
|
||||||
<h4>Add Items...</h4>
|
|
||||||
</div>
|
|
||||||
<div class="col s12">
|
|
||||||
<div class="card-panel orange lighten-4 z-depth-0">
|
|
||||||
<span class="black-text"> Here is where you can search, add, and remove items from this list by checking or unchecking the items below. You can select
|
|
||||||
multiple at a time or simply one. Utilize the search bar to filter down quickly to items you need or simply scroll to your hearts content.
|
|
||||||
<br><br>
|
|
||||||
<b>WARNING:</b> clicking the checkbox will not save the changes right off the bat! You <b>MUST</b> click the Update Items button!
|
|
||||||
<br><br>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<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 Items" value="">
|
|
||||||
</div>
|
|
||||||
<div class="col s12 center">
|
|
||||||
<a id="back" class="btn icon-left orange lighten-4 black-text z-depth-0"><i class="material-icons">chevron_left</i></a>
|
|
||||||
<a id="update_items" class="btn orange lighten-4 black-text z-depth-0">Update Items</a>
|
|
||||||
<a id="forward" class="btn icon-right orange lighten-4 black-text z-depth-0"><i class="material-icons">chevron_right</i></a>
|
|
||||||
</div>
|
|
||||||
<div class="divider col s12"></div>
|
|
||||||
<div id="checkbox-container" class="col s12">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="custom_modal" class="modal">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="row" style="gap: 5px;">
|
|
||||||
<div class="col s12">
|
|
||||||
<h4>Add Custom...</h4>
|
|
||||||
</div>
|
|
||||||
<div class="col s12">
|
|
||||||
<div class="card-panel orange lighten-4 z-depth-0">
|
|
||||||
<span class="black-text"> Here is where you can add custom items to the list.</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="s12 m6 input-field outlined">
|
|
||||||
<input id="item_name" type="text" placeholder=" ">
|
|
||||||
<label for="item_name">Item Name</label>
|
|
||||||
</div>
|
|
||||||
<div class="s12 input-field outlined">
|
|
||||||
<input id="weblink" type="text" placeholder=" ">
|
|
||||||
<label for="weblink">Weblink</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button onclick="addCustom()" class="modal-close waves-effect btn-flat">Add</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
<script>
|
|
||||||
const list_id = {{id|tojson}}
|
|
||||||
let shoppingList;
|
|
||||||
let current_page = 1
|
|
||||||
let limit = 25
|
|
||||||
let pantry_checked_items = new Array();
|
|
||||||
let groups_checked_items = new Array();
|
|
||||||
let search_text = "";
|
|
||||||
let custom_items = {};
|
|
||||||
let quantities = {};
|
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", async function(){
|
|
||||||
await fetchList()
|
|
||||||
|
|
||||||
var elems = document.querySelectorAll('select');
|
|
||||||
var instances = M.FormSelect.init(elems, {});
|
|
||||||
var elems = document.querySelectorAll('.modal');
|
|
||||||
var instances = M.Modal.init(elems, {});
|
|
||||||
var elems = document.querySelectorAll('.fixed-action-btn');
|
|
||||||
var instances = M.FloatingActionButton.init(elems, {});
|
|
||||||
var elems = document.querySelectorAll('.sidenav');
|
|
||||||
var instances = M.Sidenav.init(elems, {});
|
|
||||||
var elems = document.querySelectorAll('.dropdown-trigger');
|
|
||||||
var instances = M.Dropdown.init(elems, {});
|
|
||||||
var elems = document.querySelectorAll('.collapsible');
|
|
||||||
var instances = M.Collapsible.init(elems, {});
|
|
||||||
M.AutoInit();
|
|
||||||
|
|
||||||
await populateInfo()
|
|
||||||
await populateReferences()
|
|
||||||
update_list()
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
async function fetchList(){
|
|
||||||
const url = new URL('/getList', window.location.origin);
|
|
||||||
url.searchParams.append('id', list_id);
|
|
||||||
const response = await fetch(url);
|
|
||||||
data = await response.json();
|
|
||||||
shoppingList = data.shopping_list;
|
|
||||||
};
|
|
||||||
|
|
||||||
async function populateInfo(){
|
|
||||||
var itemName = document.getElementById('list_name');
|
|
||||||
var databaseID = document.getElementById('database_id');
|
|
||||||
var selectElement = document.getElementById('list_type');
|
|
||||||
var description = document.getElementById('description');
|
|
||||||
|
|
||||||
itemName.innerHTML = shoppingList[1];
|
|
||||||
databaseID.innerHTML = `Database ID: ${shoppingList[0]}`;
|
|
||||||
selectElement.value = shoppingList[10];
|
|
||||||
selectElement.dispatchEvent(new Event('change'));
|
|
||||||
description.value = shoppingList[2];
|
|
||||||
M.Forms.textareaAutoResize(description);
|
|
||||||
|
|
||||||
pantry_checked_items = [];
|
|
||||||
|
|
||||||
for(let i=0; i < shoppingList[3].length; i++){
|
|
||||||
pantry_checked_items.push(shoppingList[3][i][0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
custom_items = shoppingList[4];
|
|
||||||
if(shoppingList[7]){
|
|
||||||
quantities = shoppingList[7];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function populateReferences(){
|
|
||||||
console.log(shoppingList[4])
|
|
||||||
var table_div = document.getElementById('table_div')
|
|
||||||
table_div.innerHTML = ""
|
|
||||||
|
|
||||||
var references_table = document.createElement('table')
|
|
||||||
references_table.classList.add("striped")
|
|
||||||
|
|
||||||
var tbl_header = document.createElement('thead')
|
|
||||||
tbl_header.innerHTML = `
|
|
||||||
<tr>
|
|
||||||
<th>Database ID</th>
|
|
||||||
<th>Barcode</th>
|
|
||||||
<th>Item Name</th>
|
|
||||||
<th>Qty/UOM</th>
|
|
||||||
<th>type</th>
|
|
||||||
</tr>`
|
|
||||||
|
|
||||||
var tbl_body = document.createElement('tbody')
|
|
||||||
|
|
||||||
let inventory_items = shoppingList[3];
|
|
||||||
for (let i=0; i < inventory_items.length; i++){
|
|
||||||
var row = document.createElement('tr')
|
|
||||||
|
|
||||||
let database_id_cell = document.createElement('td')
|
|
||||||
database_id_cell.innerHTML = `${inventory_items[i][0]}`
|
|
||||||
|
|
||||||
let barcode_cell = document.createElement('td')
|
|
||||||
barcode_cell.innerHTML = `${inventory_items[i][1]}`
|
|
||||||
|
|
||||||
let name_cell = document.createElement('td')
|
|
||||||
if (inventory_items[i][3]){
|
|
||||||
name_cell.innerHTML = `<a href=${inventory_items[i][3].main} target="_blank">${inventory_items[i][2]}</a>`
|
|
||||||
} else {
|
|
||||||
name_cell.innerHTML = `${inventory_items[i][2]}`
|
|
||||||
}
|
|
||||||
let qty_uom_cell = document.createElement('td')
|
|
||||||
console.log(shoppingList[7])
|
|
||||||
if(shoppingList[10] == 'calculated'){
|
|
||||||
qty_uom_cell.innerHTML = `
|
|
||||||
<input class="item_qty" id="${inventory_items[i][0]}@item" value='0.00' type="text" placeholder=" " style="width: 60px; height: 30px;" disabled>
|
|
||||||
<input class="item_uom" id="${inventory_items[i][0]}@item" value='' type="text" placeholder=" " style="width: 60px; height: 30px;" disabled>`
|
|
||||||
} else {
|
|
||||||
console.log(inventory_items[i][0])
|
|
||||||
qty = quantities[`${inventory_items[i][0]}@item`]['qty']
|
|
||||||
uom = quantities[`${inventory_items[i][0]}@item`]['uom']
|
|
||||||
qty_uom_cell.innerHTML = `
|
|
||||||
<input class="item_qty" id="${inventory_items[i][0]}@item" value="${qty}" type="text" placeholder=" " style="width: 60px; height: 30px;">
|
|
||||||
<input class="item_uom" id="${inventory_items[i][0]}@item" value="${uom}" type="text" placeholder=" " style="width: 60px; height: 30px;">`
|
|
||||||
}
|
|
||||||
|
|
||||||
let type_cell = document.createElement('td')
|
|
||||||
type_cell.innerHTML = "Item"
|
|
||||||
|
|
||||||
row.appendChild(database_id_cell)
|
|
||||||
row.appendChild(barcode_cell)
|
|
||||||
row.appendChild(name_cell)
|
|
||||||
row.appendChild(qty_uom_cell)
|
|
||||||
row.appendChild(type_cell)
|
|
||||||
|
|
||||||
tbl_body.appendChild(row)
|
|
||||||
};
|
|
||||||
|
|
||||||
<!-- Reference creation for Custom Items -->
|
|
||||||
Object.entries(custom_items).forEach(([key, value]) =>{
|
|
||||||
var row = document.createElement('tr')
|
|
||||||
|
|
||||||
let database_id_cell = document.createElement('td')
|
|
||||||
database_id_cell.innerHTML = `${value[0]}`
|
|
||||||
|
|
||||||
let barcode_cell = document.createElement('td')
|
|
||||||
barcode_cell.innerHTML = `${value[1]}`
|
|
||||||
|
|
||||||
let name_cell = document.createElement('td')
|
|
||||||
if (value[3]){
|
|
||||||
name_cell.innerHTML = `<a href=${value[3].main} target="_blank">${value[2]}</a>`
|
|
||||||
} else {
|
|
||||||
name_cell.innerHTML = `${value[2]}`
|
|
||||||
}
|
|
||||||
let qty_uom_cell = document.createElement('td')
|
|
||||||
console.log(shoppingList[10])
|
|
||||||
if(shoppingList[10] == 'calculated'){
|
|
||||||
qty_uom_cell.innerHTML = `
|
|
||||||
<input class="item_qty" id="${value[0]}@custom" value=${quantities[`${value[0]}@custom`]['qty']} type="text" placeholder=" " style="width: 60px; height: 30px;">
|
|
||||||
<input class="item_uom" id="${value[0]}@custom" value=${quantities[`${value[0]}@custom`]['uom']} style="width: 60px; height: 30px;">`
|
|
||||||
} else {
|
|
||||||
qty_uom_cell.innerHTML = `
|
|
||||||
<input class="item_qty" id="${value[0]}@custom" value=${quantities[`${value[0]}@custom`]['qty']} type="text" placeholder=" " style="width: 60px; height: 30px;">
|
|
||||||
<input class="item_uom" id="${value[0]}@custom" value=${quantities[`${value[0]}@custom`]['uom']} type="text" placeholder=" " style="width: 60px; height: 30px;">`
|
|
||||||
}
|
|
||||||
|
|
||||||
let type_cell = document.createElement('td')
|
|
||||||
type_cell.innerHTML = "Custom"
|
|
||||||
|
|
||||||
row.appendChild(database_id_cell)
|
|
||||||
row.appendChild(barcode_cell)
|
|
||||||
row.appendChild(name_cell)
|
|
||||||
row.appendChild(qty_uom_cell)
|
|
||||||
row.appendChild(type_cell)
|
|
||||||
|
|
||||||
tbl_body.appendChild(row)
|
|
||||||
});
|
|
||||||
|
|
||||||
references_table.appendChild(tbl_header)
|
|
||||||
references_table.appendChild(tbl_body)
|
|
||||||
|
|
||||||
table_div.appendChild(references_table)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function save_checked(){
|
|
||||||
let checkboxes = document.querySelectorAll('.checkbox-class');
|
|
||||||
checkboxes.forEach((checkbox) => {
|
|
||||||
if (pantry_checked_items.includes(Number(checkbox.id)) && !checkbox.checked){
|
|
||||||
pantry_checked_items.splice(pantry_checked_items.indexOf(Number(Number(checkbox.id))), 1);
|
|
||||||
delete quantities[`${Number(checkbox.id)}@item`]
|
|
||||||
}
|
|
||||||
if (!pantry_checked_items.includes(Number(checkbox.id)) && checkbox.checked){
|
|
||||||
pantry_checked_items.push(Number(checkbox.id))
|
|
||||||
quantities[`${Number(checkbox.id)}@item`] = {
|
|
||||||
qty: 0.00,
|
|
||||||
uom: "each",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function save_quantities(){
|
|
||||||
let x = document.querySelectorAll(".item_qty")
|
|
||||||
console.log(x)
|
|
||||||
for(let i=0; i<x.length; i++){
|
|
||||||
if(shoppingList[10]== 'calculated'){
|
|
||||||
quantities[`${x[i].id}`] = {qty: Number(x[i].value)}
|
|
||||||
} else {
|
|
||||||
quantities[`${x[i].id}`] = {qty: Number(x[i].value)}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let y = document.querySelectorAll(".item_uom")
|
|
||||||
for(let i=0; i<y.length; i++){
|
|
||||||
if(shoppingList[10]== 'calculated'){
|
|
||||||
quantities[`${y[i].id}`]['uom'] = y[i].value
|
|
||||||
} else {
|
|
||||||
quantities[`${y[i].id}`]['uom'] = y[i].value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function update_list(){
|
|
||||||
if (current_page == 1){
|
|
||||||
document.getElementById('back').classList.add("disabled")
|
|
||||||
}else{
|
|
||||||
document.getElementById('back').classList.remove("disabled")
|
|
||||||
};
|
|
||||||
|
|
||||||
const url = new URL('/getItems', window.location.origin);
|
|
||||||
url.searchParams.append('page', current_page);
|
|
||||||
url.searchParams.append('limit', limit);
|
|
||||||
url.searchParams.append('search_text', search_text);
|
|
||||||
|
|
||||||
let container = document.getElementById('checkbox-container')
|
|
||||||
fetch(url)
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
if (data.items.length < limit){
|
|
||||||
document.getElementById('forward').classList.add("disabled")
|
|
||||||
} else {
|
|
||||||
document.getElementById('forward').classList.remove("disabled")
|
|
||||||
}
|
|
||||||
|
|
||||||
const divi = document.createElement('div')
|
|
||||||
data.items.forEach(item => {
|
|
||||||
const checkboxHTML = document.createElement('p')
|
|
||||||
if (pantry_checked_items.includes(item[0])){
|
|
||||||
checkboxHTML.innerHTML = `<label>
|
|
||||||
<input class="checkbox-class" id=${item[0]} name=${item[0]} checked="checked" type="checkbox" />
|
|
||||||
<span>${item[1]} - ${item[2]}</span>
|
|
||||||
</label>`;
|
|
||||||
} else {
|
|
||||||
checkboxHTML.innerHTML = `<div class="col s12"><label>
|
|
||||||
<input class="checkbox-class" id=${item[0]} name=${item[0]} type="checkbox" />
|
|
||||||
<span>${item[1]} - ${item[2]}</span>
|
|
||||||
</label></div>`;
|
|
||||||
}
|
|
||||||
divi.appendChild(checkboxHTML)
|
|
||||||
})
|
|
||||||
|
|
||||||
container.innerHTML = divi.innerHTML
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById("search").addEventListener('keydown', function(event){
|
|
||||||
if(event.key === 'Enter'){
|
|
||||||
console.log(document.getElementById("search").value);
|
|
||||||
search_text = document.getElementById("search").value;
|
|
||||||
update_list()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
document.getElementById('forward').addEventListener('click', () =>{
|
|
||||||
current_page++
|
|
||||||
save_checked()
|
|
||||||
update_list()
|
|
||||||
})
|
|
||||||
|
|
||||||
document.getElementById('back').addEventListener('click', () =>{
|
|
||||||
current_page--
|
|
||||||
save_checked()
|
|
||||||
update_list()
|
|
||||||
})
|
|
||||||
|
|
||||||
async function updateShoppingList(){
|
|
||||||
|
|
||||||
var itemName = document.getElementById('list_name');
|
|
||||||
var selectElement = document.getElementById('list_type');
|
|
||||||
var description = document.getElementById('description');
|
|
||||||
|
|
||||||
|
|
||||||
await fetch(`/updateList`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
id: shoppingList[0],
|
|
||||||
name: itemName.innerHTML,
|
|
||||||
description:description.value,
|
|
||||||
items: pantry_checked_items,
|
|
||||||
custom: custom_items,
|
|
||||||
list_type:selectElement.value,
|
|
||||||
quantities:quantities,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
M.toast({text: "List Saved!"})
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById('update_items').addEventListener('click', async function(){
|
|
||||||
await save_checked()
|
|
||||||
//await save_quantities()
|
|
||||||
|
|
||||||
await updateShoppingList()
|
|
||||||
|
|
||||||
var modalElem = document.querySelector('#item_modal');
|
|
||||||
var instance = M.Modal.getInstance(modalElem);
|
|
||||||
instance.close();
|
|
||||||
|
|
||||||
await fetchList()
|
|
||||||
await populateInfo()
|
|
||||||
await populateReferences()
|
|
||||||
})
|
|
||||||
|
|
||||||
function generateRandomString() {
|
|
||||||
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
||||||
let result = '';
|
|
||||||
for (let i = 0; i < 6; i++) {
|
|
||||||
result += characters.charAt(Math.floor(Math.random() * characters.length));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async function saveList(){
|
|
||||||
await save_checked()
|
|
||||||
await save_quantities()
|
|
||||||
|
|
||||||
await updateShoppingList()
|
|
||||||
await fetchList()
|
|
||||||
await populateInfo()
|
|
||||||
await populateReferences()
|
|
||||||
}
|
|
||||||
|
|
||||||
async function addCustom(){
|
|
||||||
let item_id = generateRandomString()
|
|
||||||
let name = document.getElementById('item_name').value
|
|
||||||
let weblink = document.getElementById('weblink').value
|
|
||||||
//await save_quantities()
|
|
||||||
|
|
||||||
|
|
||||||
item = new Array();
|
|
||||||
item[0] = item_id
|
|
||||||
item[1] = item_id
|
|
||||||
item[2] = name
|
|
||||||
item[3] = {
|
|
||||||
main: weblink
|
|
||||||
}
|
|
||||||
custom_items[item_id] = item
|
|
||||||
quantities[`${item_id}@custom`] = {
|
|
||||||
qty: 0.00,
|
|
||||||
uom: 'each',
|
|
||||||
}
|
|
||||||
|
|
||||||
await updateShoppingList()
|
|
||||||
await populateInfo()
|
|
||||||
await populateReferences()
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
</html>
|
|
||||||
@ -1,332 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en" dir="ltr">
|
|
||||||
<head>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
|
||||||
<title>My Pantry</title>
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@materializecss/materialize@2.0.3-alpha/dist/css/materialize.min.css" />
|
|
||||||
<!-- Material Icons -->
|
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
|
||||||
<!-- Material Symbols - Outlined Set -->
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined" rel="stylesheet" />
|
|
||||||
<!-- Material Symbols - Rounded Set -->
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded" rel="stylesheet" />
|
|
||||||
<!-- Material Symbols - Sharp Set -->
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Sharp" rel="stylesheet" />
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@materializecss/materialize@2.0.3-alpha/dist/js/materialize.min.js"></script>
|
|
||||||
</head>
|
|
||||||
<style>
|
|
||||||
.hand-pointer {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
[type="radio"]:checked + span:after {
|
|
||||||
border: 2px solid rgb(0 128 0 / 30%); /* Outline color */
|
|
||||||
background-color: rgb(0 128 0 / 30%); /* Fill color */
|
|
||||||
}
|
|
||||||
header, main, footer, body {
|
|
||||||
padding-left: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width : 992px) {
|
|
||||||
header, main, footer, body {
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.dropdown-disabled {
|
|
||||||
pointer-events: none;
|
|
||||||
opacity: 0.5; /* or your desired degree of transparency */
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
<ul id='dropdown1' class='dropdown-content'>
|
|
||||||
{% for site in sites %}
|
|
||||||
<li><button class="btn transparent black-text z-depth-0" onclick="changeSite('{{ site }}')">{{site}}</button></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
<ul id="slide-out" class="sidenav sidenav-fixed orange lighten-4" style="width: 250px;">
|
|
||||||
<li>
|
|
||||||
<div class="user-view">
|
|
||||||
<!-- <div class="background">
|
|
||||||
<img src="images/office.jpg">
|
|
||||||
</div> -->
|
|
||||||
<!-- <a href="#user"><img class="circle" src="images/yuna.jpg"></a> -->
|
|
||||||
<a href="#name"><span class="black-text name">John Doe</span></a>
|
|
||||||
<a href="#email"><span class="black-text email">jdoe@example.com</span></a>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<li><a class="dropdown-trigger" data-target="dropdown1">Current Site > {{current_site}}<i class="material-icons right">arrow_drop_down</i></a></li>
|
|
||||||
<li><div class="divider grey darken-1" style="margin-left: 5px; margin-right: 10px;"></div></li>
|
|
||||||
<li><a href="/items">Site Items</a></li>
|
|
||||||
<li><a href="/groups">Site Groups</a></li>
|
|
||||||
<li><a href="/shopping-lists">Site Shopping Lists</a></li>
|
|
||||||
</ul>
|
|
||||||
<body>
|
|
||||||
<div class="container section" style="padding-bottom: 72px;">
|
|
||||||
<div class="row">
|
|
||||||
<div class="row col s12">
|
|
||||||
<div class="col-s3">
|
|
||||||
<a href="#" data-target="slide-out" class="sidenav-trigger hide-on-large-only left"><i class="material-icons">menu</i></a>
|
|
||||||
</div>
|
|
||||||
<div class="col s6 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 Items" value="">
|
|
||||||
</div>
|
|
||||||
<div class="col s3">
|
|
||||||
<a class="btn waves-effect waves-light center-align right tooltipped orange lighten-4 black-text text-darken-2 z-depth-0" data-position="bottom" data-tooltip="Open up filter options." style="margin-right: 5px; margin-top:0px; border-radius: 20px 10px 20px 10px;" onclick="hideFilters()"><i class="material-icons">tune</i></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col s12 hide" id="filter_options" style="padding: 20px">
|
|
||||||
<!-- Set the number of items -->
|
|
||||||
<div class="row center">
|
|
||||||
<div class="col s12">
|
|
||||||
<p>Set Items Per Page</p>
|
|
||||||
</div>
|
|
||||||
<div class="col s6 m4 l2">
|
|
||||||
<label>
|
|
||||||
<input name="group1" type="radio" checked onclick="changeLimit(25)"/>
|
|
||||||
<span>25 items</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="col s6 m4 l2">
|
|
||||||
<label>
|
|
||||||
<input name="group1" type="radio" onclick="changeLimit(50)"/>
|
|
||||||
<span>50 items</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="col s6 m4 l2">
|
|
||||||
<label>
|
|
||||||
<input name="group1" type="radio" onclick="changeLimit(75)"/>
|
|
||||||
<span>75 itesm</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="col s6 m4 l2">
|
|
||||||
<label>
|
|
||||||
<input name="group1" type="radio" onclick="changeLimit(100)"/>
|
|
||||||
<span>100 items</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="col s6 m4 l2">
|
|
||||||
<label>
|
|
||||||
<input name="group1" type="radio" onclick="changeLimit(150)"/>
|
|
||||||
<span>150 items</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="col s6 m4 l2">
|
|
||||||
<label>
|
|
||||||
<input name="group1" type="radio" onclick="changeLimit(200)"/>
|
|
||||||
<span>200 items</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="col s12 divider"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="col s12 z-depth-0">
|
|
||||||
<div class="z-depth-0" id="cards">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col s12 center" id="pagination_list">
|
|
||||||
<ul class="pagination">
|
|
||||||
<li id="first" class="waves-effect hand-pointer"><a class="orange lighten-4" style="display: flex; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: 10px;"><i class="material-icons">first_page</i></a></li>
|
|
||||||
<li id="back" class="waves-effect hand-pointer" ><a class="orange lighten-4" style="display: flex; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: 10px;"><i class="material-icons">chevron_left</i></a></li>
|
|
||||||
<li id="current_page" style="padding-top: 7px; padding-left: 5px; padding-right: 5px; font-size: 18px;">page_number</li>
|
|
||||||
<li id="forward" class="waves-effect hand-pointer"><a class="orange lighten-4" style="display: flex; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: 10px;"><i class="material-icons">chevron_right</i></a></li>
|
|
||||||
<li id="last" class="waves-effect hand-pointer"><a class="orange lighten-4" style="display: flex; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: 10px;"><i class="material-icons">last_page</i></a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="fixed-action-btn">
|
|
||||||
<a class="btn-floating btn-large">
|
|
||||||
<i class="large material-icons">more_vert</i>
|
|
||||||
</a>
|
|
||||||
<ul>
|
|
||||||
<li><button class="btn-floating blue darken-1 modal-trigger" data-target="list_modal"><i class="material-icons">playlist_add</i></button></li>
|
|
||||||
<li><a class="btn-floating green darken-1"><i class="material-icons">download</i></a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="list_modal" class="modal">
|
|
||||||
<div class="modal-content">
|
|
||||||
<h4>Adding a Shopping List...</h4>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col s12">
|
|
||||||
<div class="card-panel orange lighten-4">
|
|
||||||
<span>Shopping Lists are a main feature of this system as a provides a simple interface and way to track when something
|
|
||||||
needs to be purchased either based on someone adding a custom item or through adding items from your inventory
|
|
||||||
that will use the items safety stock or a set quantity to create the list dynamically.
|
|
||||||
<br><br>There are two types of shopping lists you can create:
|
|
||||||
<br><br>
|
|
||||||
<b>Plain</b> - This shopping list has no other extra features beyond being a place to keep a list of items that need to be
|
|
||||||
purchsed and usually is a one time list or a list that isnt reoccuring.
|
|
||||||
<br><br>
|
|
||||||
<b>Safety Stock</b> - This list uses items added from the pantry to calculate the needed quantites based on what you have set
|
|
||||||
the safety stock to for said items. Usually used to create a reoccuring list that is calculated every time you open the list.
|
|
||||||
<br><br>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col s12 m6 input-field p-2">
|
|
||||||
<input id="list_name" type="text" placeholder=" " maxlength="64">
|
|
||||||
<label for="list_name">List Name</label>
|
|
||||||
</div>
|
|
||||||
<div class="col s12 m6 p=2">
|
|
||||||
<label for="list_type">List Type</label>
|
|
||||||
<select id="list_type" class="browser-default">
|
|
||||||
<option value="plain" selected>Plain Group</option>
|
|
||||||
<option value="calculated">Safety Stock</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="input-field col s12 p-2">
|
|
||||||
<textarea id="list_description" class="materialize-textarea" placeholder="A short description for what this shopping list represents..."></textarea>
|
|
||||||
<label for="list_description">List Description</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<a onclick="addList()" class="waves-effect btn orange lighten-4 black-text">Add</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
<script>
|
|
||||||
let current_page = 1
|
|
||||||
let end_page = 10
|
|
||||||
let sort_order = 1
|
|
||||||
let view = 0
|
|
||||||
let limit = 10
|
|
||||||
let filter_state = "hidden"
|
|
||||||
let searchText = ""
|
|
||||||
let site = {{ current_site|tojson }}
|
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", async function(){
|
|
||||||
var elems = document.querySelectorAll('.dropdown-trigger');
|
|
||||||
var instances = M.Dropdown.init(elems, {});
|
|
||||||
M.AutoInit();
|
|
||||||
update_list()
|
|
||||||
});
|
|
||||||
|
|
||||||
async function changeSite(new_site){
|
|
||||||
console.log(`current_site: ${site}`)
|
|
||||||
console.log(`new site: ${new_site}`)
|
|
||||||
site = new_site
|
|
||||||
console.log(`current_site: ${site}`)
|
|
||||||
const url = new URL('/changeSite', window.location.origin);
|
|
||||||
url.searchParams.append('site', site)
|
|
||||||
await fetch(url)
|
|
||||||
location.reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function update_list(){
|
|
||||||
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('/getLists', window.location.origin);
|
|
||||||
url.searchParams.append('page', current_page);
|
|
||||||
url.searchParams.append('limit', limit);
|
|
||||||
url.searchParams.append('site', site)
|
|
||||||
fetch(url)
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
|
|
||||||
end_page = parseInt(data.end)
|
|
||||||
if (current_page === end_page){
|
|
||||||
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 {
|
|
||||||
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 cards = document.getElementById("cards")
|
|
||||||
cards.classList.add("row")
|
|
||||||
cards.style = "gap: 1em; padding-top: 10px;"
|
|
||||||
const dummy_div = document.createElement('div')
|
|
||||||
|
|
||||||
|
|
||||||
data.lists.forEach(item => {
|
|
||||||
let item_card = document.createElement('div')
|
|
||||||
item_card.classList.add('card')
|
|
||||||
item_card.classList.add('col')
|
|
||||||
item_card.classList.add('s12')
|
|
||||||
|
|
||||||
let card_content = document.createElement('div')
|
|
||||||
card_content.classList.add('card-content')
|
|
||||||
card_content.classList.add('orange')
|
|
||||||
card_content.classList.add('lighten-4')
|
|
||||||
card_content.innerHTML = `
|
|
||||||
<span class="card-title">${item[1]}<span class="badge green lighten-1 white-text" data-badge-caption="Items" style="border-radius: 10px;">${item[11]}</span></span>
|
|
||||||
<p>${item[2]}
|
|
||||||
</p>`
|
|
||||||
|
|
||||||
let card_action = document.createElement('div')
|
|
||||||
card_action.classList.add('card-action')
|
|
||||||
card_action.innerHTML = `
|
|
||||||
<a class="left" style="color: black;">${item[9]}</a>
|
|
||||||
<a class="transparent z-depth-0 left" href="#!">
|
|
||||||
<i class="material-icons" style="color: white;">star</i>
|
|
||||||
</a>
|
|
||||||
<a class="right" href="/shopping-list/view/${item[0]}">View</a>
|
|
||||||
<a class="right" href="/shopping-list/edit/${item[0]}">Edit</a>
|
|
||||||
`
|
|
||||||
|
|
||||||
item_card.appendChild(card_content)
|
|
||||||
item_card.appendChild(card_action)
|
|
||||||
|
|
||||||
dummy_div.appendChild(item_card)
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
cards.innerHTML = dummy_div.innerHTML
|
|
||||||
|
|
||||||
var elems = document.querySelectorAll('.collapsible');
|
|
||||||
var instances = M.Collapsible.init(elems, {
|
|
||||||
// specify options here
|
|
||||||
});
|
|
||||||
var elems = document.querySelectorAll('.tooltipped');
|
|
||||||
var instances = M.Tooltip.init(elems, {
|
|
||||||
// specify options here
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById("current_page").innerHTML = `${String(current_page)} / ${String(end_page)}`
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
async function addList(){
|
|
||||||
var name = document.getElementById("list_name").value
|
|
||||||
var description = document.getElementById("list_description").value
|
|
||||||
var type = document.getElementById("list_type").value
|
|
||||||
const url = new URL('/addList', window.location.origin);
|
|
||||||
url.searchParams.append('name', name);
|
|
||||||
url.searchParams.append('description', description);
|
|
||||||
url.searchParams.append('type', type);
|
|
||||||
|
|
||||||
await fetch(url)
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
M.toast({text: `Adding ${name}: ${data.state}`});
|
|
||||||
})
|
|
||||||
update_list()
|
|
||||||
var elem = document.getElementById("list_modal");
|
|
||||||
var instance = M.Modal.getInstance(elem);
|
|
||||||
instance.close()
|
|
||||||
};
|
|
||||||
|
|
||||||
</script>
|
|
||||||
</html>
|
|
||||||
@ -1,173 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en" dir="ltr">
|
|
||||||
<head>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
|
||||||
<title>My Pantry</title>
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@materializecss/materialize@2.0.3-alpha/dist/css/materialize.min.css" />
|
|
||||||
<!-- Material Icons -->
|
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
|
||||||
<!-- Material Symbols - Outlined Set -->
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined" rel="stylesheet" />
|
|
||||||
<!-- Material Symbols - Rounded Set -->
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded" rel="stylesheet" />
|
|
||||||
<!-- Material Symbols - Sharp Set -->
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Sharp" rel="stylesheet" />
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@materializecss/materialize@2.0.3-alpha/dist/js/materialize.min.js"></script>
|
|
||||||
</head>
|
|
||||||
<style>
|
|
||||||
header, main, footer, body {
|
|
||||||
padding-left: 250px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width : 992px) {
|
|
||||||
header, main, footer, body {
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.dropdown-disabled {
|
|
||||||
pointer-events: none;
|
|
||||||
opacity: 0.5; /* or your desired degree of transparency */
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<ul id='dropdown1' class='dropdown-content'>
|
|
||||||
{% for site in sites %}
|
|
||||||
<li><button class="btn transparent black-text z-depth-0" onclick="changeSite('{{ site }}')">{{site}}</button></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
<ul id="slide-out" class="sidenav sidenav-fixed orange lighten-4" style="width: 250px;">
|
|
||||||
<li>
|
|
||||||
<div class="user-view">
|
|
||||||
<!-- <div class="background">
|
|
||||||
<img src="images/office.jpg">
|
|
||||||
</div> -->
|
|
||||||
<!-- <a href="#user"><img class="circle" src="images/yuna.jpg"></a> -->
|
|
||||||
<a href="#name"><span class="black-text name">John Doe</span></a>
|
|
||||||
<a href="#email"><span class="black-text email">jdoe@example.com</span></a>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<li><a class="dropdown-trigger dropdown-disabled" data-target="dropdown1">Current Site > {{session['selected_site']}}<i class="material-icons right">arrow_drop_down</i></a></li>
|
|
||||||
<li><div class="divider grey darken-1" style="margin-left: 5px; margin-right: 10px;"></div></li>
|
|
||||||
<li><a href="/items">Site Items</a></li>
|
|
||||||
<li><a href="/groups">Site Groups</a></li>
|
|
||||||
<li><a href="/shopping-lists">Site Shopping Lists</a></li>
|
|
||||||
</ul>
|
|
||||||
<body>
|
|
||||||
<div class="container">
|
|
||||||
<div class="section">
|
|
||||||
<div class="row">
|
|
||||||
<p id="temp"></p>
|
|
||||||
<div class="col s12">
|
|
||||||
<h3 id="list_name"></h3>
|
|
||||||
</div>
|
|
||||||
<div class="col s12">
|
|
||||||
<p id="description"></p>
|
|
||||||
</div>
|
|
||||||
<div id="table_div" class="col s12 p-2">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
<script>
|
|
||||||
const list_id = {{id|tojson}}
|
|
||||||
let shoppingList;
|
|
||||||
let quantities = {};
|
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", async function(){
|
|
||||||
await fetchList()
|
|
||||||
|
|
||||||
var elems = document.querySelectorAll('select');
|
|
||||||
var instances = M.FormSelect.init(elems, {});
|
|
||||||
var elems = document.querySelectorAll('.modal');
|
|
||||||
var instances = M.Modal.init(elems, {});
|
|
||||||
var elems = document.querySelectorAll('.fixed-action-btn');
|
|
||||||
var instances = M.FloatingActionButton.init(elems, {});
|
|
||||||
var elems = document.querySelectorAll('.sidenav');
|
|
||||||
var instances = M.Sidenav.init(elems, {});
|
|
||||||
var elems = document.querySelectorAll('.dropdown-trigger');
|
|
||||||
var instances = M.Dropdown.init(elems, {});
|
|
||||||
var elems = document.querySelectorAll('.collapsible');
|
|
||||||
var instances = M.Collapsible.init(elems, {});
|
|
||||||
M.AutoInit();
|
|
||||||
|
|
||||||
await populateInfo()
|
|
||||||
await populateReferences()
|
|
||||||
console.log(quantities)
|
|
||||||
})
|
|
||||||
|
|
||||||
async function fetchList(){
|
|
||||||
const url = new URL('/getListView', window.location.origin);
|
|
||||||
url.searchParams.append('id', list_id);
|
|
||||||
const response = await fetch(url);
|
|
||||||
data = await response.json();
|
|
||||||
shoppingList = data.shopping_list;
|
|
||||||
quantities = shoppingList[7]
|
|
||||||
}
|
|
||||||
|
|
||||||
function changeSite(site){
|
|
||||||
console.log(site)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function populateInfo(){
|
|
||||||
let listName = document.getElementById('list_name')
|
|
||||||
let description = document.getElementById('description')
|
|
||||||
|
|
||||||
listName.innerHTML = shoppingList[1];
|
|
||||||
description.innerHTML = shoppingList[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
async function populateReferences(){
|
|
||||||
var table_div = document.getElementById('table_div')
|
|
||||||
table_div.innerHTML = ""
|
|
||||||
|
|
||||||
var references_table = document.createElement('table')
|
|
||||||
references_table.classList.add("striped")
|
|
||||||
|
|
||||||
var tbl_header = document.createElement('thead')
|
|
||||||
tbl_header.innerHTML = `<tr><th>Item Name</th><th>Product Qty</th></tr>`
|
|
||||||
|
|
||||||
var tbl_body = document.createElement('tbody')
|
|
||||||
|
|
||||||
let inventory_items = shoppingList[3];
|
|
||||||
console.log(inventory_items)
|
|
||||||
|
|
||||||
// if it is plain then we want to grab the qty from the quantities dictionary
|
|
||||||
|
|
||||||
// if it is calculated we need to calculate qty for all inventory items
|
|
||||||
for (let i=0; i < inventory_items.length; i++){
|
|
||||||
var row = document.createElement('tr')
|
|
||||||
|
|
||||||
let name_cell = document.createElement('td')
|
|
||||||
if (inventory_items[i][3]){
|
|
||||||
name_cell.innerHTML = `<a href=${inventory_items[i][3].main} target="_blank">${inventory_items[i][2]}</a>`
|
|
||||||
} else {
|
|
||||||
name_cell.innerHTML = `${inventory_items[i][2]}`
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(inventory_items[i])
|
|
||||||
let qty_uom_cell = document.createElement('td')
|
|
||||||
if(shoppingList[10] == 'calculated'){
|
|
||||||
qty = Number(inventory_items[i][5]) - Number(inventory_items[i][4])
|
|
||||||
uom = inventory_items[i][6]
|
|
||||||
} else {
|
|
||||||
qty = quantities[`${inventory_items[i][0]}@item`]['qty']
|
|
||||||
uom = quantities[`${inventory_items[i][0]}@item`]['uom']
|
|
||||||
}
|
|
||||||
qty_uom_cell.innerHTML = `${qty} ${uom}`
|
|
||||||
|
|
||||||
row.appendChild(name_cell)
|
|
||||||
row.appendChild(qty_uom_cell)
|
|
||||||
|
|
||||||
tbl_body.appendChild(row)
|
|
||||||
};
|
|
||||||
|
|
||||||
// all custom items will pull quantities form the quantities dictionary.
|
|
||||||
|
|
||||||
references_table.appendChild(tbl_header)
|
|
||||||
references_table.appendChild(tbl_body)
|
|
||||||
|
|
||||||
table_div.appendChild(references_table)
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
</html>
|
|
||||||
43
webserver.py
43
webserver.py
@ -1,48 +1,15 @@
|
|||||||
from flask import Flask, render_template, session
|
from flask import Flask, render_template
|
||||||
import api, config
|
import api
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
app.secret_key = '11gs22h2h1a4h6ah8e413a45'
|
|
||||||
app.register_blueprint(api.database_api)
|
|
||||||
|
|
||||||
@app.route("/group/<id>")
|
app.register_blueprint(api.database_api)
|
||||||
def group(id):
|
|
||||||
sites = config.sites_config()
|
|
||||||
return render_template("groups/group.html", id=id, current_site=session['selected_site'], sites=sites['sites'])
|
|
||||||
|
|
||||||
@app.route("/workshop")
|
@app.route("/workshop")
|
||||||
def workshop():
|
def workshop():
|
||||||
sites = config.sites_config()
|
return render_template("workshop.html")
|
||||||
return render_template("workshop.html", current_site=session['selected_site'], sites=sites['sites'])
|
|
||||||
|
|
||||||
@app.route("/shopping-list/view/<id>")
|
|
||||||
def shopping_lists_view(id):
|
|
||||||
sites = config.sites_config()
|
|
||||||
return render_template("shopping-lists/view.html", id=id, current_site=session['selected_site'], sites=sites['sites'])
|
|
||||||
|
|
||||||
@app.route("/shopping-list/edit/<id>")
|
|
||||||
def shopping_lists_edit(id):
|
|
||||||
sites = config.sites_config()
|
|
||||||
return render_template("shopping-lists/edit.html", id=id, current_site=session['selected_site'], sites=sites['sites'])
|
|
||||||
|
|
||||||
@app.route("/shopping-lists")
|
|
||||||
def shopping_lists():
|
|
||||||
sites = config.sites_config()
|
|
||||||
return render_template("shopping-lists/index.html", current_site=session['selected_site'], sites=sites['sites'])
|
|
||||||
|
|
||||||
@app.route("/groups")
|
|
||||||
def groups():
|
|
||||||
sites = config.sites_config()
|
|
||||||
return render_template("groups/index.html", current_site=session['selected_site'], sites=sites['sites'])
|
|
||||||
|
|
||||||
@app.route("/items")
|
|
||||||
def items():
|
|
||||||
sites = config.sites_config()
|
|
||||||
return render_template("items/index.html", current_site=session['selected_site'], sites=sites['sites'])
|
|
||||||
|
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
def home():
|
def home():
|
||||||
session['selected_site'] = 'main'
|
return render_template("home.html")
|
||||||
sites = config.sites_config()
|
|
||||||
return render_template("items/index.html", current_site=session['selected_site'], sites=sites['sites'])
|
|
||||||
|
|
||||||
app.run(host="0.0.0.0", port=5002, debug=True)
|
app.run(host="0.0.0.0", port=5002, debug=True)
|
||||||
Loading…
x
Reference in New Issue
Block a user