diff --git a/app.py b/app.py index d409b94..687739c 100644 --- a/app.py +++ b/app.py @@ -603,6 +603,25 @@ def delete_item(item_id): return jsonify({"deleted": True}) +@app.post("/api/items/bulk-delete") +@permission_required("delete") +def bulk_delete_items(): + data = request.get_json(silent=True) or {} + item_ids = data.get("item_ids", []) + if not isinstance(item_ids, list) or not item_ids: + return jsonify({"error": "Choose at least one inventory item to remove."}), 400 + if len(item_ids) > 1000 or any(not isinstance(item_id, int) or isinstance(item_id, bool) for item_id in item_ids): + return jsonify({"error": "Choose up to 1,000 valid inventory items to remove."}), 400 + item_ids = list(set(item_ids)) + placeholders = ", ".join("?" for _ in item_ids) + with get_db() as connection: + images = connection.execute(f"SELECT filename FROM inventory_images WHERE item_id IN ({placeholders})", item_ids).fetchall() + result = connection.execute(f"DELETE FROM inventory_items WHERE id IN ({placeholders})", item_ids) + for image in images: + delete_image(image["filename"]) + return jsonify({"deleted": result.rowcount}) + + @app.post("/api/items//images") @permission_required("write") def add_images(item_id): diff --git a/data/inventory.db b/data/inventory.db index 540b96d..8ba638f 100644 Binary files a/data/inventory.db and b/data/inventory.db differ diff --git a/data/uploads/0074935896dd4b4da2b9cb8480ae06ec.jpg b/data/uploads/0074935896dd4b4da2b9cb8480ae06ec.jpg deleted file mode 100644 index d1a4ef1..0000000 Binary files a/data/uploads/0074935896dd4b4da2b9cb8480ae06ec.jpg and /dev/null differ diff --git a/data/uploads/049d936f40824989a830434aad1329e9.png b/data/uploads/049d936f40824989a830434aad1329e9.png deleted file mode 100644 index b5d66f5..0000000 Binary files a/data/uploads/049d936f40824989a830434aad1329e9.png and /dev/null differ diff --git a/data/uploads/07e3dfa8aba84dc6940a6ec550b60b68.png b/data/uploads/07e3dfa8aba84dc6940a6ec550b60b68.png deleted file mode 100644 index 89c39f5..0000000 Binary files a/data/uploads/07e3dfa8aba84dc6940a6ec550b60b68.png and /dev/null differ diff --git a/data/uploads/089d979d15dd452ea239bb992f7d047f.png b/data/uploads/089d979d15dd452ea239bb992f7d047f.png deleted file mode 100644 index b5d66f5..0000000 Binary files a/data/uploads/089d979d15dd452ea239bb992f7d047f.png and /dev/null differ diff --git a/data/uploads/0c182e61946f4c278d0bd19211fcf0e9.jpg b/data/uploads/0c182e61946f4c278d0bd19211fcf0e9.jpg deleted file mode 100644 index 5878c33..0000000 Binary files a/data/uploads/0c182e61946f4c278d0bd19211fcf0e9.jpg and /dev/null differ diff --git a/data/uploads/0d79b0edb94d44b3a306c59c4f03bc19.png b/data/uploads/0d79b0edb94d44b3a306c59c4f03bc19.png deleted file mode 100644 index 07e95ff..0000000 Binary files a/data/uploads/0d79b0edb94d44b3a306c59c4f03bc19.png and /dev/null differ diff --git a/data/uploads/129ad035d50b44bc818df6ec90ea3033.jpg b/data/uploads/129ad035d50b44bc818df6ec90ea3033.jpg deleted file mode 100644 index fb932dc..0000000 Binary files a/data/uploads/129ad035d50b44bc818df6ec90ea3033.jpg and /dev/null differ diff --git a/data/uploads/132216c1645349fdb04fd954b8735327.png b/data/uploads/132216c1645349fdb04fd954b8735327.png deleted file mode 100644 index ba1dc58..0000000 Binary files a/data/uploads/132216c1645349fdb04fd954b8735327.png and /dev/null differ diff --git a/data/uploads/168676f820424825b554866fe12b8b93.png b/data/uploads/168676f820424825b554866fe12b8b93.png deleted file mode 100644 index 3214d34..0000000 Binary files a/data/uploads/168676f820424825b554866fe12b8b93.png and /dev/null differ diff --git a/data/uploads/1774e1c13e2c48c09ed86a3a4921761a.png b/data/uploads/1774e1c13e2c48c09ed86a3a4921761a.png deleted file mode 100644 index d0aa203..0000000 Binary files a/data/uploads/1774e1c13e2c48c09ed86a3a4921761a.png and /dev/null differ diff --git a/data/uploads/2291eb34705d4f21a75edc5484a5e9da.jpg b/data/uploads/2291eb34705d4f21a75edc5484a5e9da.jpg deleted file mode 100644 index 0c845d3..0000000 Binary files a/data/uploads/2291eb34705d4f21a75edc5484a5e9da.jpg and /dev/null differ diff --git a/data/uploads/23508e71673c47a69e6729e265c03485.png b/data/uploads/23508e71673c47a69e6729e265c03485.png deleted file mode 100644 index 21c421a..0000000 Binary files a/data/uploads/23508e71673c47a69e6729e265c03485.png and /dev/null differ diff --git a/data/uploads/27a1c0a7c4ae47af9289f6479a9b9d8a.jpg b/data/uploads/27a1c0a7c4ae47af9289f6479a9b9d8a.jpg deleted file mode 100644 index 2915739..0000000 Binary files a/data/uploads/27a1c0a7c4ae47af9289f6479a9b9d8a.jpg and /dev/null differ diff --git a/data/uploads/40718d4189f1491a9d70164647cbb23f.png b/data/uploads/40718d4189f1491a9d70164647cbb23f.png deleted file mode 100644 index b5d66f5..0000000 Binary files a/data/uploads/40718d4189f1491a9d70164647cbb23f.png and /dev/null differ diff --git a/data/uploads/474904f243ca4ab0baec84b277c91cf2.png b/data/uploads/474904f243ca4ab0baec84b277c91cf2.png deleted file mode 100644 index b5d66f5..0000000 Binary files a/data/uploads/474904f243ca4ab0baec84b277c91cf2.png and /dev/null differ diff --git a/data/uploads/475e4910b72b4be195dc81b049dec2ad.png b/data/uploads/475e4910b72b4be195dc81b049dec2ad.png deleted file mode 100644 index 301648e..0000000 Binary files a/data/uploads/475e4910b72b4be195dc81b049dec2ad.png and /dev/null differ diff --git a/data/uploads/4c2c635a7ed646e2a2f311c32f191059.png b/data/uploads/4c2c635a7ed646e2a2f311c32f191059.png deleted file mode 100644 index 4c6312b..0000000 Binary files a/data/uploads/4c2c635a7ed646e2a2f311c32f191059.png and /dev/null differ diff --git a/data/uploads/51b9928ee6ef4d5190bcc0a8f3ea9481.jpg b/data/uploads/51b9928ee6ef4d5190bcc0a8f3ea9481.jpg deleted file mode 100644 index 0bb5f97..0000000 Binary files a/data/uploads/51b9928ee6ef4d5190bcc0a8f3ea9481.jpg and /dev/null differ diff --git a/data/uploads/54e344de860d4615a651a7c92348df57.png b/data/uploads/54e344de860d4615a651a7c92348df57.png deleted file mode 100644 index ba1dc58..0000000 Binary files a/data/uploads/54e344de860d4615a651a7c92348df57.png and /dev/null differ diff --git a/data/uploads/5a2e8cf4854945418670457c4631dbfc.png b/data/uploads/5a2e8cf4854945418670457c4631dbfc.png deleted file mode 100644 index f397678..0000000 Binary files a/data/uploads/5a2e8cf4854945418670457c4631dbfc.png and /dev/null differ diff --git a/data/uploads/5adf2ec9e7764330acd891360293173c.png b/data/uploads/5adf2ec9e7764330acd891360293173c.png deleted file mode 100644 index 301648e..0000000 Binary files a/data/uploads/5adf2ec9e7764330acd891360293173c.png and /dev/null differ diff --git a/data/uploads/64fae8da14f5465993fc0dd3625daf30.png b/data/uploads/64fae8da14f5465993fc0dd3625daf30.png deleted file mode 100644 index db2ad71..0000000 Binary files a/data/uploads/64fae8da14f5465993fc0dd3625daf30.png and /dev/null differ diff --git a/data/uploads/66d400c5f9d94c9c8257dc590ca1d106.png b/data/uploads/66d400c5f9d94c9c8257dc590ca1d106.png deleted file mode 100644 index 301648e..0000000 Binary files a/data/uploads/66d400c5f9d94c9c8257dc590ca1d106.png and /dev/null differ diff --git a/data/uploads/6a5b46bccd104f10ab53931089b37452.png b/data/uploads/6a5b46bccd104f10ab53931089b37452.png deleted file mode 100644 index 21c421a..0000000 Binary files a/data/uploads/6a5b46bccd104f10ab53931089b37452.png and /dev/null differ diff --git a/data/uploads/6d0382ffe5d24ba1b26437263b354f21.png b/data/uploads/6d0382ffe5d24ba1b26437263b354f21.png deleted file mode 100644 index c29e7f5..0000000 Binary files a/data/uploads/6d0382ffe5d24ba1b26437263b354f21.png and /dev/null differ diff --git a/data/uploads/6f6902ae6a344dbcb2ad6bf40e4fb03d.jpg b/data/uploads/6f6902ae6a344dbcb2ad6bf40e4fb03d.jpg deleted file mode 100644 index 6cd1c97..0000000 Binary files a/data/uploads/6f6902ae6a344dbcb2ad6bf40e4fb03d.jpg and /dev/null differ diff --git a/data/uploads/74c1083aea0d441fa18ed37f14f396ef.jpg b/data/uploads/74c1083aea0d441fa18ed37f14f396ef.jpg deleted file mode 100644 index e6bec09..0000000 Binary files a/data/uploads/74c1083aea0d441fa18ed37f14f396ef.jpg and /dev/null differ diff --git a/data/uploads/7dd8fb30090340458c19e8c36b7fdba5.png b/data/uploads/7dd8fb30090340458c19e8c36b7fdba5.png deleted file mode 100644 index 21c421a..0000000 Binary files a/data/uploads/7dd8fb30090340458c19e8c36b7fdba5.png and /dev/null differ diff --git a/data/uploads/7ea1f337e1ac42299acb11213c0b316d.jpg b/data/uploads/7ea1f337e1ac42299acb11213c0b316d.jpg deleted file mode 100644 index ef3420b..0000000 Binary files a/data/uploads/7ea1f337e1ac42299acb11213c0b316d.jpg and /dev/null differ diff --git a/data/uploads/81416bb126ff4f65840199c0ef33778d.png b/data/uploads/81416bb126ff4f65840199c0ef33778d.png deleted file mode 100644 index 59a5c64..0000000 Binary files a/data/uploads/81416bb126ff4f65840199c0ef33778d.png and /dev/null differ diff --git a/data/uploads/81e3c9ca0c23414ea5a9876c31c98528.png b/data/uploads/81e3c9ca0c23414ea5a9876c31c98528.png deleted file mode 100644 index 90aed12..0000000 Binary files a/data/uploads/81e3c9ca0c23414ea5a9876c31c98528.png and /dev/null differ diff --git a/data/uploads/844342c4ff364eb5860553b22c4984d6.png b/data/uploads/844342c4ff364eb5860553b22c4984d6.png deleted file mode 100644 index b5d66f5..0000000 Binary files a/data/uploads/844342c4ff364eb5860553b22c4984d6.png and /dev/null differ diff --git a/data/uploads/860be9b4772e4b94bfe67ee6befca08b.jpg b/data/uploads/860be9b4772e4b94bfe67ee6befca08b.jpg deleted file mode 100644 index 768a58b..0000000 Binary files a/data/uploads/860be9b4772e4b94bfe67ee6befca08b.jpg and /dev/null differ diff --git a/data/uploads/88e8b424e2214a54964966da187214ac.png b/data/uploads/88e8b424e2214a54964966da187214ac.png deleted file mode 100644 index 071415d..0000000 Binary files a/data/uploads/88e8b424e2214a54964966da187214ac.png and /dev/null differ diff --git a/data/uploads/8c098590432a459888a22eec92c14782.png b/data/uploads/8c098590432a459888a22eec92c14782.png deleted file mode 100644 index 21c421a..0000000 Binary files a/data/uploads/8c098590432a459888a22eec92c14782.png and /dev/null differ diff --git a/data/uploads/954835372c5b442da3bfab5c9aa4c14a.jpg b/data/uploads/954835372c5b442da3bfab5c9aa4c14a.jpg deleted file mode 100644 index fd9d8fa..0000000 Binary files a/data/uploads/954835372c5b442da3bfab5c9aa4c14a.jpg and /dev/null differ diff --git a/data/uploads/985588ea01f842b2a2a7562a04537cb2.png b/data/uploads/985588ea01f842b2a2a7562a04537cb2.png deleted file mode 100644 index 21c421a..0000000 Binary files a/data/uploads/985588ea01f842b2a2a7562a04537cb2.png and /dev/null differ diff --git a/data/uploads/9ac374d95479490bad8820e8958026cc.jpg b/data/uploads/9ac374d95479490bad8820e8958026cc.jpg deleted file mode 100644 index 1079927..0000000 Binary files a/data/uploads/9ac374d95479490bad8820e8958026cc.jpg and /dev/null differ diff --git a/data/uploads/9af2306cb2e94519b83b92a7f9498e1e.png b/data/uploads/9af2306cb2e94519b83b92a7f9498e1e.png deleted file mode 100644 index 3214d34..0000000 Binary files a/data/uploads/9af2306cb2e94519b83b92a7f9498e1e.png and /dev/null differ diff --git a/data/uploads/9baad8c9f6a44ca7be1934220f5ab93a.jpg b/data/uploads/9baad8c9f6a44ca7be1934220f5ab93a.jpg deleted file mode 100644 index 7362928..0000000 Binary files a/data/uploads/9baad8c9f6a44ca7be1934220f5ab93a.jpg and /dev/null differ diff --git a/data/uploads/9e3db64b119340808f21e41417eff989.jpg b/data/uploads/9e3db64b119340808f21e41417eff989.jpg deleted file mode 100644 index c7d0084..0000000 Binary files a/data/uploads/9e3db64b119340808f21e41417eff989.jpg and /dev/null differ diff --git a/data/uploads/9f037f9134d24a66a07ece268e247ebe.png b/data/uploads/9f037f9134d24a66a07ece268e247ebe.png deleted file mode 100644 index 21c421a..0000000 Binary files a/data/uploads/9f037f9134d24a66a07ece268e247ebe.png and /dev/null differ diff --git a/data/uploads/a2b3fcf8b46d4f70aba0fac3aab138a0.png b/data/uploads/a2b3fcf8b46d4f70aba0fac3aab138a0.png deleted file mode 100644 index ba1dc58..0000000 Binary files a/data/uploads/a2b3fcf8b46d4f70aba0fac3aab138a0.png and /dev/null differ diff --git a/data/uploads/a2e2a0f7272f40cc8956025504579c7d.jpg b/data/uploads/a2e2a0f7272f40cc8956025504579c7d.jpg deleted file mode 100644 index 874c9ab..0000000 Binary files a/data/uploads/a2e2a0f7272f40cc8956025504579c7d.jpg and /dev/null differ diff --git a/data/uploads/a30d874de65b412592ffc931da21d15f.png b/data/uploads/a30d874de65b412592ffc931da21d15f.png deleted file mode 100644 index 21c421a..0000000 Binary files a/data/uploads/a30d874de65b412592ffc931da21d15f.png and /dev/null differ diff --git a/data/uploads/a4444b5666124fcb938920a20c9cbf5d.jpg b/data/uploads/a4444b5666124fcb938920a20c9cbf5d.jpg deleted file mode 100644 index d6b8700..0000000 Binary files a/data/uploads/a4444b5666124fcb938920a20c9cbf5d.jpg and /dev/null differ diff --git a/data/uploads/a6365d49f9d4408a9a4c0b9c8af642b9.png b/data/uploads/a6365d49f9d4408a9a4c0b9c8af642b9.png deleted file mode 100644 index ba1dc58..0000000 Binary files a/data/uploads/a6365d49f9d4408a9a4c0b9c8af642b9.png and /dev/null differ diff --git a/data/uploads/a6ce9100cf154ac8937a672e3dfee816.png b/data/uploads/a6ce9100cf154ac8937a672e3dfee816.png deleted file mode 100644 index ba1dc58..0000000 Binary files a/data/uploads/a6ce9100cf154ac8937a672e3dfee816.png and /dev/null differ diff --git a/data/uploads/a7b004080ea9483993f5a2a3171e92b6.jpg b/data/uploads/a7b004080ea9483993f5a2a3171e92b6.jpg deleted file mode 100644 index a35c005..0000000 Binary files a/data/uploads/a7b004080ea9483993f5a2a3171e92b6.jpg and /dev/null differ diff --git a/data/uploads/aa5803ecc0ea4429b8b8397bd17f7e15.jpg b/data/uploads/aa5803ecc0ea4429b8b8397bd17f7e15.jpg deleted file mode 100644 index 911e9ba..0000000 Binary files a/data/uploads/aa5803ecc0ea4429b8b8397bd17f7e15.jpg and /dev/null differ diff --git a/data/uploads/ace4b2a6a65a4f5ab93af368dff49ed4.jpg b/data/uploads/ace4b2a6a65a4f5ab93af368dff49ed4.jpg deleted file mode 100644 index 55f0a92..0000000 Binary files a/data/uploads/ace4b2a6a65a4f5ab93af368dff49ed4.jpg and /dev/null differ diff --git a/data/uploads/ad3c1a155ae24eacb9d929f5f541d3d2.png b/data/uploads/ad3c1a155ae24eacb9d929f5f541d3d2.png deleted file mode 100644 index db2ad71..0000000 Binary files a/data/uploads/ad3c1a155ae24eacb9d929f5f541d3d2.png and /dev/null differ diff --git a/data/uploads/b00fd2359f1942c0941cc7d4af8fd150.png b/data/uploads/b00fd2359f1942c0941cc7d4af8fd150.png deleted file mode 100644 index 301648e..0000000 Binary files a/data/uploads/b00fd2359f1942c0941cc7d4af8fd150.png and /dev/null differ diff --git a/data/uploads/b2816ef5bc324f978f786910133185e7.jpg b/data/uploads/b2816ef5bc324f978f786910133185e7.jpg deleted file mode 100644 index 4d315a4..0000000 Binary files a/data/uploads/b2816ef5bc324f978f786910133185e7.jpg and /dev/null differ diff --git a/data/uploads/b3e41a827586487397acb15d5dc6b885.png b/data/uploads/b3e41a827586487397acb15d5dc6b885.png deleted file mode 100644 index ba1dc58..0000000 Binary files a/data/uploads/b3e41a827586487397acb15d5dc6b885.png and /dev/null differ diff --git a/data/uploads/b60052e4ca3140fa93f05e55ca7f3b41.png b/data/uploads/b60052e4ca3140fa93f05e55ca7f3b41.png deleted file mode 100644 index 301648e..0000000 Binary files a/data/uploads/b60052e4ca3140fa93f05e55ca7f3b41.png and /dev/null differ diff --git a/data/uploads/b74b926da72b4795bd8f5187edb8b823.png b/data/uploads/b74b926da72b4795bd8f5187edb8b823.png deleted file mode 100644 index 21c421a..0000000 Binary files a/data/uploads/b74b926da72b4795bd8f5187edb8b823.png and /dev/null differ diff --git a/data/uploads/b9e45e452b7843ed824061d6853634ee.png b/data/uploads/b9e45e452b7843ed824061d6853634ee.png deleted file mode 100644 index 301648e..0000000 Binary files a/data/uploads/b9e45e452b7843ed824061d6853634ee.png and /dev/null differ diff --git a/data/uploads/ba8e903f69dc4ea580810f8185e94b29.jpg b/data/uploads/ba8e903f69dc4ea580810f8185e94b29.jpg deleted file mode 100644 index 7c33253..0000000 Binary files a/data/uploads/ba8e903f69dc4ea580810f8185e94b29.jpg and /dev/null differ diff --git a/data/uploads/bb7871a8bb45412586edbd398d951b46.jpg b/data/uploads/bb7871a8bb45412586edbd398d951b46.jpg deleted file mode 100644 index 8fcec1f..0000000 Binary files a/data/uploads/bb7871a8bb45412586edbd398d951b46.jpg and /dev/null differ diff --git a/data/uploads/bbdb7173ce724dacaab583eaaba193ba.png b/data/uploads/bbdb7173ce724dacaab583eaaba193ba.png deleted file mode 100644 index 071415d..0000000 Binary files a/data/uploads/bbdb7173ce724dacaab583eaaba193ba.png and /dev/null differ diff --git a/data/uploads/c1b250c44e6946f48b76231570de23ef.png b/data/uploads/c1b250c44e6946f48b76231570de23ef.png deleted file mode 100644 index 301648e..0000000 Binary files a/data/uploads/c1b250c44e6946f48b76231570de23ef.png and /dev/null differ diff --git a/data/uploads/c6a67fa4a0394cf7ac63c7fd63ec2496.jpg b/data/uploads/c6a67fa4a0394cf7ac63c7fd63ec2496.jpg deleted file mode 100644 index aecaafc..0000000 Binary files a/data/uploads/c6a67fa4a0394cf7ac63c7fd63ec2496.jpg and /dev/null differ diff --git a/data/uploads/cc58b574d2d7419e9cd74049a98e8093.jpg b/data/uploads/cc58b574d2d7419e9cd74049a98e8093.jpg deleted file mode 100644 index 679d1bc..0000000 Binary files a/data/uploads/cc58b574d2d7419e9cd74049a98e8093.jpg and /dev/null differ diff --git a/data/uploads/ce7a227e9b5c45a69c64021156034ff4.jpg b/data/uploads/ce7a227e9b5c45a69c64021156034ff4.jpg deleted file mode 100644 index 66a5b9b..0000000 Binary files a/data/uploads/ce7a227e9b5c45a69c64021156034ff4.jpg and /dev/null differ diff --git a/data/uploads/cff4a1fd3cf54d318a0ba703cbc69e81.png b/data/uploads/cff4a1fd3cf54d318a0ba703cbc69e81.png deleted file mode 100644 index ba1dc58..0000000 Binary files a/data/uploads/cff4a1fd3cf54d318a0ba703cbc69e81.png and /dev/null differ diff --git a/data/uploads/d130f30ab9374b6f8ed2a8daa7368823.png b/data/uploads/d130f30ab9374b6f8ed2a8daa7368823.png deleted file mode 100644 index 9b1e3b8..0000000 Binary files a/data/uploads/d130f30ab9374b6f8ed2a8daa7368823.png and /dev/null differ diff --git a/data/uploads/d6ec576322d44ab6b3b7ec53893a09d4.png b/data/uploads/d6ec576322d44ab6b3b7ec53893a09d4.png deleted file mode 100644 index 59a5c64..0000000 Binary files a/data/uploads/d6ec576322d44ab6b3b7ec53893a09d4.png and /dev/null differ diff --git a/data/uploads/d79ccebdca4c464589686364823ed100.png b/data/uploads/d79ccebdca4c464589686364823ed100.png deleted file mode 100644 index b5d66f5..0000000 Binary files a/data/uploads/d79ccebdca4c464589686364823ed100.png and /dev/null differ diff --git a/data/uploads/d8e6b0f1ba924c17879278eec6eef6f7.png b/data/uploads/d8e6b0f1ba924c17879278eec6eef6f7.png deleted file mode 100644 index 301648e..0000000 Binary files a/data/uploads/d8e6b0f1ba924c17879278eec6eef6f7.png and /dev/null differ diff --git a/data/uploads/d9115ef36c954c50b6e6b897090dccd0.png b/data/uploads/d9115ef36c954c50b6e6b897090dccd0.png deleted file mode 100644 index 21c421a..0000000 Binary files a/data/uploads/d9115ef36c954c50b6e6b897090dccd0.png and /dev/null differ diff --git a/data/uploads/ddc7d22faa884d22890bba41167284b5.jpg b/data/uploads/ddc7d22faa884d22890bba41167284b5.jpg deleted file mode 100644 index 37c308c..0000000 Binary files a/data/uploads/ddc7d22faa884d22890bba41167284b5.jpg and /dev/null differ diff --git a/data/uploads/e081d4150af84be0b4345d3b94a3090f.png b/data/uploads/e081d4150af84be0b4345d3b94a3090f.png deleted file mode 100644 index b5d66f5..0000000 Binary files a/data/uploads/e081d4150af84be0b4345d3b94a3090f.png and /dev/null differ diff --git a/data/uploads/e1763b2e440049b5bb90f2bba05ee6b3.png b/data/uploads/e1763b2e440049b5bb90f2bba05ee6b3.png deleted file mode 100644 index 8bbb392..0000000 Binary files a/data/uploads/e1763b2e440049b5bb90f2bba05ee6b3.png and /dev/null differ diff --git a/data/uploads/e7b906e3767a4a8197936c70ea8ad4cd.jpg b/data/uploads/e7b906e3767a4a8197936c70ea8ad4cd.jpg deleted file mode 100644 index 8892070..0000000 Binary files a/data/uploads/e7b906e3767a4a8197936c70ea8ad4cd.jpg and /dev/null differ diff --git a/data/uploads/ec0f7bdb0b6d44b09931e2599e4f0bb3.jpg b/data/uploads/ec0f7bdb0b6d44b09931e2599e4f0bb3.jpg deleted file mode 100644 index 4cdb040..0000000 Binary files a/data/uploads/ec0f7bdb0b6d44b09931e2599e4f0bb3.jpg and /dev/null differ diff --git a/data/uploads/f08b4a0039784ec4933ea2af7249f73b.png b/data/uploads/f08b4a0039784ec4933ea2af7249f73b.png deleted file mode 100644 index 6c12033..0000000 Binary files a/data/uploads/f08b4a0039784ec4933ea2af7249f73b.png and /dev/null differ diff --git a/data/uploads/f6ec805446214cfc9a2f5744de487660.png b/data/uploads/f6ec805446214cfc9a2f5744de487660.png deleted file mode 100644 index c99afb2..0000000 Binary files a/data/uploads/f6ec805446214cfc9a2f5744de487660.png and /dev/null differ diff --git a/data/uploads/fc0890fb019349cabb737e899afc2876.jpg b/data/uploads/fc0890fb019349cabb737e899afc2876.jpg deleted file mode 100644 index c600bda..0000000 Binary files a/data/uploads/fc0890fb019349cabb737e899afc2876.jpg and /dev/null differ diff --git a/data/uploads/ff61e5a09c8d40ebb8b3a0c857c9dca7.jpg b/data/uploads/ff61e5a09c8d40ebb8b3a0c857c9dca7.jpg deleted file mode 100644 index 27bac5b..0000000 Binary files a/data/uploads/ff61e5a09c8d40ebb8b3a0c857c9dca7.jpg and /dev/null differ diff --git a/static/app.js b/static/app.js index a29fab0..584f02a 100644 --- a/static/app.js +++ b/static/app.js @@ -1,4 +1,4 @@ -const state = { items: [], types: [], editingId: null }; +const state = { items: [], types: [], editingId: null, selectedIds: new Set() }; const role = document.body.dataset.role; const canWrite = role === 'admin' || role === 'read_write'; const canDelete = role === 'admin'; @@ -21,6 +21,7 @@ async function load() { const query = new URLSearchParams({ q: $('#searchInput').value, type: $('#typeFilter').value, status: $('#statusFilter').value }); const [items, summary] = await Promise.all([request(`/api/items?${query}`), request('/api/summary')]); state.items = items; + state.selectedIds = new Set([...state.selectedIds].filter((itemId) => items.some((item) => item.id === itemId))); $('#totalCount').textContent = summary.total; $('#availableCount').textContent = summary.available; $('#checkedOutCount').textContent = summary.checked_out; @@ -33,7 +34,18 @@ function renderItems(items) { const body = $('#inventoryBody'); const actions = (itemId) => `${canWrite ? `` : ''}${canDelete ? `` : ''}`; $('#emptyState').style.display = items.length ? 'none' : 'block'; - body.innerHTML = items.map((item) => `
${item.images?.length ? `${item.images.length > 1 ? `+${item.images.length - 1}` : ''}` : '
◌
'}
${escapeHtml(item.name)}${escapeHtml([item.brand, item.model].filter(Boolean).join(' · ') || 'No manufacturer details')}
${escapeHtml(item.item_type)}${escapeHtml(item.serial_number || 'No serial')}${escapeHtml(item.asset_tag || 'No asset tag')}${escapeHtml(item.location || 'Unassigned')}${escapeHtml(item.status)}
${actions(item.id)}
`).join(''); + body.innerHTML = items.map((item) => `${canDelete ? `` : ''}
${item.images?.length ? `${item.images.length > 1 ? `+${item.images.length - 1}` : ''}` : '
◌
'}
${escapeHtml(item.name)}${escapeHtml([item.brand, item.model].filter(Boolean).join(' · ') || 'No manufacturer details')}
${escapeHtml(item.item_type)}${escapeHtml(item.serial_number || 'No serial')}${escapeHtml(item.asset_tag || 'No asset tag')}${escapeHtml(item.location || 'Unassigned')}${escapeHtml(item.status)}
${actions(item.id)}
`).join(''); + updateBulkActions(); +} + +function updateBulkActions() { + if (!canDelete) return; + const count = state.selectedIds.size; + $('#selectedCount').textContent = `${count} ${count === 1 ? 'item' : 'items'} selected`; + $('#bulkActions').classList.toggle('hidden', count === 0); + $('#bulkDeleteButton').disabled = count === 0; + $('#selectAllItems').checked = Boolean(state.items.length) && state.items.every((item) => state.selectedIds.has(item.id)); + $('#selectAllItems').indeterminate = count > 0 && !$('#selectAllItems').checked; } function escapeHtml(value) { return String(value).replace(/[&<>'"]/g, (char) => ({ '&': '&', '<': '<', '>': '>', "'": ''', '"': '"' }[char])); } @@ -79,10 +91,14 @@ async function saveType(event) { event.preventDefault(); const previousName = $( window.editItem = (id) => openModal(state.items.find((item) => item.id === id)); window.deleteImage = async (imageId) => { if (!state.editingId || !confirm('Delete this picture?')) return; try { const item = await request(`/api/items/${state.editingId}/images/${imageId}`, { method: 'DELETE' }); renderImageGallery(item.images); $('#imageName').textContent = `${item.images.length} saved picture${item.images.length === 1 ? '' : 's'}. Add more below.`; state.items = state.items.map((entry) => entry.id === item.id ? item : entry); renderItems(state.items); toast('Picture deleted.'); } catch (error) { toast(error.message); } }; window.removeItem = async (id) => { const item = state.items.find((entry) => entry.id === id); if (!confirm(`Delete "${item.name}"?`)) return; try { await request(`/api/items/${id}`, { method: 'DELETE' }); toast('Item removed from inventory.'); await load(); } catch (error) { toast(error.message); } }; +async function removeSelectedItems() { const count = state.selectedIds.size; if (!count || !confirm(`Remove ${count} selected ${count === 1 ? 'item' : 'items'} from inventory?`)) return; try { const result = await request('/api/items/bulk-delete', { method: 'POST', body: JSON.stringify({ item_ids: [...state.selectedIds] }) }); state.selectedIds.clear(); toast(`${result.deleted} ${result.deleted === 1 ? 'item' : 'items'} removed from inventory.`); await load(); } catch (error) { toast(error.message); } } if ($('#addButton')) $('#addButton').addEventListener('click', () => openModal()); if ($('#emptyAddButton')) $('#emptyAddButton').addEventListener('click', () => openModal()); if ($('#typeManagerButton')) $('#typeManagerButton').addEventListener('click', openTypeManager); +if ($('#selectAllItems')) $('#selectAllItems').addEventListener('change', (event) => { state.items.forEach((item) => { if (event.target.checked) state.selectedIds.add(item.id); else state.selectedIds.delete(item.id); }); renderItems(state.items); }); +if ($('#inventoryBody')) $('#inventoryBody').addEventListener('change', (event) => { if (!event.target.matches('.item-selector')) return; const itemId = Number(event.target.value); if (event.target.checked) state.selectedIds.add(itemId); else state.selectedIds.delete(itemId); updateBulkActions(); }); +if ($('#bulkDeleteButton')) $('#bulkDeleteButton').addEventListener('click', removeSelectedItems); $('#closeModal').addEventListener('click', closeModal); $('#cancelButton').addEventListener('click', closeModal); $('#modal').addEventListener('click', (event) => { if (event.target === $('#modal')) closeModal(); }); diff --git a/static/style.css b/static/style.css index 7254154..7b7ea75 100644 --- a/static/style.css +++ b/static/style.css @@ -1,4 +1,5 @@ -.type-manager{max-width:720px}.type-fields>span{display:block;color:var(--muted);font-size:12px;font-weight:700;margin-bottom:9px}.field-checklist{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px 14px}.field-checklist label{display:flex;align-items:center;gap:7px;font-size:12px;color:var(--ink)}.field-checklist input{width:auto}.type-list{border-top:1px solid var(--line);margin-top:22px;padding-top:4px}.type-row{display:flex;align-items:center;justify-content:space-between;gap:16px;border-bottom:1px solid var(--line);padding:13px 0}.type-row strong,.type-row span{display:block}.type-row span{color:var(--muted);font-size:11px;margin-top:4px;max-width:460px}.type-row .row-actions{display:flex;gap:7px;flex:none}@media(max-width:640px){.field-checklist{grid-template-columns:repeat(2,minmax(0,1fr))}.type-row{align-items:flex-start}.type-row .row-actions{flex-direction:column}.type-row span{max-width:230px}} +.bulk-actions{display:flex;align-items:center;gap:9px;margin-left:auto;color:var(--muted);font-size:12px;font-weight:700}.selection-column{width:34px;text-align:center}.selection-column input{accent-color:var(--teal)} +.type-manager{max-width:720px}.type-fields>span{display:block;color:var(--muted);font-size:12px;font-weight:700;margin-bottom:9px}.field-checklist{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px 14px}.field-checklist label{display:flex;align-items:center;gap:7px;font-size:12px;color:var(--ink)}.field-checklist input{width:auto}.type-list{border-top:1px solid var(--line);margin-top:22px;padding-top:4px}.type-row{display:flex;align-items:center;justify-content:space-between;gap:16px;border-bottom:1px solid var(--line);padding:13px 0}.type-row strong,.type-row span{display:block}.type-row span{color:var(--muted);font-size:11px;margin-top:4px;max-width:460px}.type-row .row-actions{display:flex;gap:7px;flex:none}@media(max-width:640px){.field-checklist{grid-template-columns:repeat(2,minmax(0,1fr))}.type-row{align-items:flex-start}.type-row .row-actions{flex-direction:column}.type-row span{max-width:230px}.bulk-actions{width:100%;margin-left:0;justify-content:space-between}} .item-cell{display:flex;align-items:center;gap:11px}.item-thumb{width:38px;height:38px;object-fit:cover;border-radius:6px;background:#e8efeb;flex:none}.item-thumb.placeholder{display:grid;place-items:center;color:#8aa39b;font-size:20px}.image-count{background:#e7f3f0;color:var(--teal);font-size:10px;font-weight:700;padding:4px 5px;border-radius:4px;margin-left:-7px}.image-field input{padding:7px 0!important;border:0!important}.image-field span{display:block;color:#8c9994;font-size:10px;font-weight:400;margin-top:4px}.image-gallery{display:flex;flex-wrap:wrap;gap:9px;margin-top:11px}.gallery-item{position:relative;width:82px;height:64px}.gallery-item img{width:100%;height:100%;object-fit:cover;border-radius:6px;border:1px solid var(--line)}.gallery-delete{position:absolute;top:-7px;right:-7px;width:20px;height:20px;padding:0;border:0;border-radius:50%;background:#193f3b;color:#fff;cursor:pointer;line-height:18px}.hidden{display:none!important} .row-actions{opacity:1!important} .login-page{min-height:100vh;display:grid;place-items:center;background:linear-gradient(135deg,#f6f8f5,#dcece5)}.login-card{width:min(390px,calc(100% - 32px));padding:34px;background:#fff;border:1px solid var(--line);border-radius:12px;box-shadow:var(--shadow)}.login-card .brand-mark{margin-bottom:22px}.login-card h1{font-family:'Space Grotesk';margin:0 0 24px}.login-card form{display:grid;gap:16px}.login-card label,.user-form label{display:grid;gap:6px;color:var(--muted);font-size:12px;font-weight:700}.login-card input,.user-form input,.user-form select{width:100%;padding:11px;border:1px solid var(--line);border-radius:6px;background:#fff}.login-card .primary-button{margin-top:8px}.login-error{padding:10px;background:#fff0ed;color:#a33e2b;border-radius:6px;font-size:12px}.logout-button{margin-top:12px;border:0;background:transparent;color:#90afa9;padding:0;cursor:pointer;font-size:11px}.users-panel{margin-top:24px;padding:24px;background:#fff;border:1px solid var(--line);border-radius:8px}.user-form{display:flex;align-items:end;gap:12px;flex-wrap:wrap}.user-form label{min-width:150px}.user-form .checkbox-label{display:flex;align-items:center;gap:7px;min-width:auto}.user-form .checkbox-label input{width:auto}.user-row{display:flex;justify-content:space-between;border-top:1px solid var(--line);padding:10px 0;margin:18px 0 0;font-size:12px}.user-row span{color:var(--muted)} diff --git a/templates/index.html b/templates/index.html index bdcd321..7c1c48e 100644 --- a/templates/index.html +++ b/templates/index.html @@ -25,8 +25,8 @@

All inventory

Search, filter, and manage every item in your stockroom.

0 items
-
-
ItemTypeIdentifiersLocationStatusActions
⌁

No items found

Adjust your search or add the first item to your inventory.

{% if user.role in ['admin', 'read_write'] %}{% endif %}
+
{% if user.role == 'admin' %}{% endif %}
+
{% if user.role == 'admin' %}{% endif %}
ItemTypeIdentifiersLocationStatusActions
⌁

No items found

Adjust your search or add the first item to your inventory.

{% if user.role in ['admin', 'read_write'] %}{% endif %}