From a81f7043fc44382775f9afac48e4c7a651e7ac6c Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Sun, 04 Jan 2026 18:29:10 +0000
Subject: [PATCH] =PopulateForm.js and ContentRoutes.php minor changes

---
 assets/js/concise/DataStore.js |   96 +++++++++++++++++++++++++++++++++++++++---------
 1 files changed, 78 insertions(+), 18 deletions(-)

diff --git a/assets/js/concise/DataStore.js b/assets/js/concise/DataStore.js
index d4cb8bc..95027e3 100644
--- a/assets/js/concise/DataStore.js
+++ b/assets/js/concise/DataStore.js
@@ -6,6 +6,7 @@
  *   this.store = window.jvbStore.register('feed', { config });
  */
 class DataStore {
+
 	constructor() {
 		// Singleton pattern
 		if (DataStore.instance) {
@@ -140,6 +141,8 @@
 			delete: (id) => this.delete(name, id),
 			get: (id) => this.get(name, id),
 			getAll: () => this.getAll(name),
+			getAllByIndex: (indexName, value) => this.getAllByIndex(name, indexName, value),
+			filterByIndex: (criteria) => this.filterByIndex(name, criteria),
 			getFiltered: () => this.getFiltered(name),
 			clear: () => this.clear(name),
 
@@ -747,30 +750,33 @@
 
 		// Reject functions
 		if (type === 'function') {
-			return validate ? { valid: false, error: `Function at ${path}` } : { valid: true, data: null };
+			if (validate) return { valid: false, error: `Function at ${path}` };
+			console.debug(`[DataStore] Stripped function at ${path}`);
+			return { valid: true, data: undefined };
 		}
 
 		// DOM elements
 		if (obj instanceof HTMLElement || obj.nodeType !== undefined) {
-			return validate ? { valid: false, error: `DOM element at ${path}` } : { valid: true, data: null };
+			if (validate) return { valid: false, error: `DOM element at ${path}` };
+			console.debug(`[DataStore] Stripped DOM element at ${path}`);
+			return { valid: true, data: undefined };
 		}
 
 		// FormData - convert and continue
 		if (obj instanceof FormData) {
-			return validate
-				? { valid: false, error: `FormData at ${path}` }
-				: { valid: true, data: this.formDataToObject(obj) };
+			if (validate) return { valid: false, error: `FormData at ${path}` };
+			console.debug(`[DataStore] Converted FormData at ${path}`);
+			return { valid: true, data: this.formDataToObject(obj) };
 		}
 
 		// Preserve safe types
-		if (obj instanceof Date || obj instanceof ArrayBuffer || ArrayBuffer.isView(obj)) {
+		if (obj instanceof Date || obj instanceof ArrayBuffer || ArrayBuffer.isView(obj) || obj instanceof Blob) {
 			return { valid: true, data: obj };
 		}
 
 		// Convert Sets to Arrays
 		if (obj instanceof Set) {
-			const arr = Array.from(obj);
-			return this.processForStorage(arr, validate, path);
+			return this.processForStorage(Array.from(obj), validate, path);
 		}
 
 		// Convert Maps to Objects
@@ -784,7 +790,7 @@
 			for (let i = 0; i < obj.length; i++) {
 				const result = this.processForStorage(obj[i], validate, `${path}[${i}]`);
 				if (!result.valid) return result;
-				if (result.data !== null) processed.push(result.data);
+				if (result.data !== undefined) processed.push(result.data);
 			}
 			return { valid: true, data: processed };
 		}
@@ -795,14 +801,14 @@
 			for (const [key, value] of Object.entries(obj)) {
 				const result = this.processForStorage(value, validate, `${path}.${key}`);
 				if (!result.valid) return result;
-				if (result.data !== null) processed[key] = result.data;
+				if (result.data !== undefined) processed[key] = result.data;
 			}
 			return { valid: true, data: processed };
 		}
 
-		return validate
-			? { valid: false, error: `Unknown type at ${path}` }
-			: { valid: true, data: null };
+		if (validate) return { valid: false, error: `Unknown type at ${path}` };
+		console.debug(`[DataStore] Stripped unknown type at ${path}`);
+		return { valid: true, data: undefined };
 	}
 
 	/***********************************************************************
@@ -829,6 +835,64 @@
 		const store = this.stores.get(name);
 		return Array.from(store.data.values());
 	}
+	/**
+	 * Filter in-memory data by multiple index/value pairs
+	 * @param {string} name - Store name
+	 * @param {Object} criteria - Object of { indexName: acceptedValue(s) }
+	 * @returns {Array} - Items matching ALL criteria
+	 *
+	 * @example
+	 * filterByIndex(name, { field: 'upload_123', status: ['queued', 'uploading'] })
+	 */
+	filterByIndex(name, criteria) {
+		const store = this.stores.get(name);
+		if (!store) return [];
+
+		return Array.from(store.data.values()).filter(item => {
+			return Object.entries(criteria).every(([key, value]) => {
+				const accepted = Array.isArray(value) ? value : [value];
+				return accepted.includes(item[key]);
+			});
+		});
+	}
+	/**
+	 * Get all items matching an index value
+	 * @param {string} name - Store name
+	 * @param {string} indexName - Name of the index to query
+	 * @param {*} value - Value to match
+	 * @returns {Promise<Array>} - Matching items
+	 */
+	async getAllByIndex(name, indexName, value) {
+		const store = this.stores.get(name);
+		const values = Array.isArray(value) ? value : [value];
+
+		// Try IndexedDB index query first (more efficient for large datasets)
+		if (store.db && store.db.objectStoreNames.contains(store.config.storeName)) {
+			try {
+				const tx = store.db.transaction([store.config.storeName], 'readonly');
+				const objectStore = tx.objectStore(store.config.storeName);
+
+				if (objectStore.indexNames.contains(indexName)) {
+					const index = objectStore.index(indexName);
+
+					const results = await Promise.all(
+						values.map(v => new Promise((resolve, reject) => {
+							const request = index.getAll(v);
+							request.onsuccess = () => resolve(request.result || []);
+							request.onerror = () => reject(request.error);
+						}))
+					);
+
+					return results.flat();
+				}
+			} catch (error) {
+				console.warn(`Index query failed for "${indexName}", falling back to filter:`, error);
+			}
+		}
+
+		// Fallback: filter in-memory data
+		return Array.from(store.data.values()).filter(item => values.includes(item[indexName]));
+	}
 
 	getFiltered(name) {
 		const store = this.stores.get(name);
@@ -859,12 +923,8 @@
 	}
 
 	/***********************************************************************
-	 * FILTER OPERATIONS (UNIFIED)
+	 * FILTER OPERATIONS
 	 ***********************************************************************/
-
-	/**
-	 * Unified filter update - handles all filter operations
-	 */
 	async updateFilters(name, updates, clearAll = false) {
 		const store = this.stores.get(name);
 		const oldFilters = { ...store.filters };

--
Gitblit v1.10.0