-
Notifications
You must be signed in to change notification settings - Fork 0
Quick Add Capture(s) to Dataset #259
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,288 @@ | ||||||||||||||||
| /** | ||||||||||||||||
| * Quick Add to Dataset Manager | ||||||||||||||||
| * Handles opening the quick-add modal, loading datasets, and adding a capture to a dataset. | ||||||||||||||||
| */ | ||||||||||||||||
| class QuickAddToDatasetManager { | ||||||||||||||||
| constructor() { | ||||||||||||||||
| this.modalEl = document.getElementById("quickAddToDatasetModal"); | ||||||||||||||||
| this.currentCaptureUuid = null; | ||||||||||||||||
| this.currentCaptureName = null; | ||||||||||||||||
| /** @type {string[]|null} When set, call quick-add API once per UUID (e.g. from file list "Add" button) */ | ||||||||||||||||
| this.currentCaptureUuids = null; | ||||||||||||||||
| if (!this.modalEl) return; | ||||||||||||||||
| this.quickAddUrl = this.modalEl.getAttribute("data-quick-add-url"); | ||||||||||||||||
| this.datasetsUrl = this.modalEl.getAttribute("data-datasets-url"); | ||||||||||||||||
| this.selectEl = document.getElementById("quick-add-dataset-select"); | ||||||||||||||||
| this.confirmBtn = document.getElementById("quick-add-confirm-btn"); | ||||||||||||||||
| this.messageEl = document.getElementById("quick-add-message"); | ||||||||||||||||
| this.captureNameEl = document.getElementById("quick-add-capture-name"); | ||||||||||||||||
| this.initializeEventListeners(); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| initializeEventListeners() { | ||||||||||||||||
| // Delegate click on "Add to dataset" buttons (e.g. in table dropdown) | ||||||||||||||||
| document.addEventListener("click", (e) => { | ||||||||||||||||
| const btn = e.target.closest(".add-to-dataset-btn"); | ||||||||||||||||
| if (!btn) return; | ||||||||||||||||
| e.preventDefault(); | ||||||||||||||||
| e.stopPropagation(); | ||||||||||||||||
| this.currentCaptureUuid = btn.getAttribute("data-capture-uuid"); | ||||||||||||||||
| this.currentCaptureName = | ||||||||||||||||
| btn.getAttribute("data-capture-name") || "This capture"; | ||||||||||||||||
| this.openModal(); | ||||||||||||||||
| }); | ||||||||||||||||
|
|
||||||||||||||||
| if (!this.modalEl) return; | ||||||||||||||||
|
|
||||||||||||||||
| // When modal is shown, load datasets and apply state (single vs multi from file list) | ||||||||||||||||
| this.modalEl.addEventListener("show.bs.modal", () => { | ||||||||||||||||
| this.resetMessage(); | ||||||||||||||||
| const rawIds = this.modalEl.dataset.captureUuids; | ||||||||||||||||
| if (rawIds) { | ||||||||||||||||
| try { | ||||||||||||||||
| this.currentCaptureUuids = JSON.parse(rawIds); | ||||||||||||||||
| this.currentCaptureUuid = null; | ||||||||||||||||
| this.currentCaptureName = null; | ||||||||||||||||
| const n = this.currentCaptureUuids.length; | ||||||||||||||||
| if (this.captureNameEl) { | ||||||||||||||||
| this.captureNameEl.textContent = | ||||||||||||||||
| n === 1 ? "1 capture" : `${n} captures`; | ||||||||||||||||
| } | ||||||||||||||||
| delete this.modalEl.dataset.captureUuids; | ||||||||||||||||
| } catch (_) { | ||||||||||||||||
| this.currentCaptureUuids = null; | ||||||||||||||||
| } | ||||||||||||||||
| } else { | ||||||||||||||||
| this.currentCaptureUuids = null; | ||||||||||||||||
| if (this.captureNameEl) { | ||||||||||||||||
| this.captureNameEl.textContent = | ||||||||||||||||
| this.currentCaptureName || "This capture"; | ||||||||||||||||
| } | ||||||||||||||||
| } | ||||||||||||||||
| this.loadDatasets(); | ||||||||||||||||
| }); | ||||||||||||||||
|
|
||||||||||||||||
| // When dataset select changes, enable/disable Add button | ||||||||||||||||
| if (this.selectEl) { | ||||||||||||||||
| this.selectEl.addEventListener("change", () => { | ||||||||||||||||
| if (this.confirmBtn) { | ||||||||||||||||
| this.confirmBtn.disabled = !this.selectEl.value; | ||||||||||||||||
| } | ||||||||||||||||
| }); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| // Add button click | ||||||||||||||||
| if (this.confirmBtn) { | ||||||||||||||||
| this.confirmBtn.addEventListener("click", () => this.handleAdd()); | ||||||||||||||||
| } | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| openModal() { | ||||||||||||||||
| if (!this.modalEl) return; | ||||||||||||||||
| const Modal = window.bootstrap?.Modal; | ||||||||||||||||
| if (Modal) { | ||||||||||||||||
| const modal = Modal.getOrCreateInstance(this.modalEl); | ||||||||||||||||
| modal.show(); | ||||||||||||||||
| } | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| resetMessage() { | ||||||||||||||||
| if (this.messageEl) { | ||||||||||||||||
| this.messageEl.classList.add("d-none"); | ||||||||||||||||
| this.messageEl.classList.remove( | ||||||||||||||||
| "alert-success", | ||||||||||||||||
| "alert-danger", | ||||||||||||||||
| "alert-warning", | ||||||||||||||||
| ); | ||||||||||||||||
| this.messageEl.textContent = ""; | ||||||||||||||||
| } | ||||||||||||||||
| if (this.confirmBtn) { | ||||||||||||||||
| this.confirmBtn.disabled = true; | ||||||||||||||||
| } | ||||||||||||||||
| if (this.selectEl) { | ||||||||||||||||
| this.selectEl.innerHTML = '<option value="">Loading...</option>'; | ||||||||||||||||
| } | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| showMessage(text, type) { | ||||||||||||||||
| if (!this.messageEl) return; | ||||||||||||||||
| this.messageEl.textContent = text; | ||||||||||||||||
| this.messageEl.classList.remove( | ||||||||||||||||
| "d-none", | ||||||||||||||||
| "alert-success", | ||||||||||||||||
| "alert-danger", | ||||||||||||||||
| "alert-warning", | ||||||||||||||||
| ); | ||||||||||||||||
| this.messageEl.classList.add(`alert-${type}`); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| async loadDatasets() { | ||||||||||||||||
| if (!this.selectEl || !this.datasetsUrl) return; | ||||||||||||||||
| this.selectEl.innerHTML = '<option value="">Loading...</option>'; | ||||||||||||||||
| if (this.confirmBtn) this.confirmBtn.disabled = true; | ||||||||||||||||
| try { | ||||||||||||||||
| const response = await window.APIClient.get(this.datasetsUrl); | ||||||||||||||||
| const datasets = response.datasets || []; | ||||||||||||||||
| this.selectEl.innerHTML = '<option value="">Select dataset...</option>'; | ||||||||||||||||
| for (const d of datasets) { | ||||||||||||||||
| const opt = document.createElement("option"); | ||||||||||||||||
| opt.value = d.uuid; | ||||||||||||||||
| opt.textContent = d.name; | ||||||||||||||||
| this.selectEl.appendChild(opt); | ||||||||||||||||
| } | ||||||||||||||||
| if (datasets.length === 0) { | ||||||||||||||||
| this.showMessage( | ||||||||||||||||
| "You have no datasets you can add captures to.", | ||||||||||||||||
| "warning", | ||||||||||||||||
| ); | ||||||||||||||||
| } | ||||||||||||||||
| } catch (err) { | ||||||||||||||||
| this.selectEl.innerHTML = '<option value="">Failed to load</option>'; | ||||||||||||||||
| const reason = err?.data?.error || err?.message || "Try again."; | ||||||||||||||||
| this.showMessage(`Failed to load datasets. ${reason}`, "danger"); | ||||||||||||||||
| } | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| async handleAdd() { | ||||||||||||||||
| const datasetUuid = this.selectEl?.value; | ||||||||||||||||
| if (!datasetUuid) return; | ||||||||||||||||
| const isMulti = | ||||||||||||||||
| Array.isArray(this.currentCaptureUuids) && | ||||||||||||||||
| this.currentCaptureUuids.length > 0; | ||||||||||||||||
| const isSingle = this.currentCaptureUuid && this.quickAddUrl; | ||||||||||||||||
| if (!isMulti && !isSingle) return; | ||||||||||||||||
| if (this.confirmBtn) this.confirmBtn.disabled = true; | ||||||||||||||||
| this.resetMessage(); | ||||||||||||||||
| if (isMulti) { | ||||||||||||||||
| await this.handleMultiAdd(datasetUuid); | ||||||||||||||||
| } else { | ||||||||||||||||
| await this.handleSingleAdd(datasetUuid); | ||||||||||||||||
| } | ||||||||||||||||
|
Comment on lines
+146
to
+160
|
||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| /** | ||||||||||||||||
| * Build a concise summary from quick-add counts (added, skipped, failed count). | ||||||||||||||||
| * API returns detailed JSON; we show one short line. | ||||||||||||||||
| * Failed = request threw (non-2xx HTTP or network) or response.success false or per-capture errors in 200 body. | ||||||||||||||||
| */ | ||||||||||||||||
| formatQuickAddSummary(added, skipped, failedCount, firstErrorMessage) { | ||||||||||||||||
| const parts = []; | ||||||||||||||||
| if (added > 0) parts.push(`${added} added`); | ||||||||||||||||
| if (skipped > 0) parts.push(`${skipped} already in dataset`); | ||||||||||||||||
| if (failedCount > 0) { | ||||||||||||||||
| parts.push(`${failedCount} failed`); | ||||||||||||||||
| if (firstErrorMessage != null) { | ||||||||||||||||
| const text = | ||||||||||||||||
| typeof firstErrorMessage === "object" | ||||||||||||||||
| ? (firstErrorMessage.message ?? | ||||||||||||||||
| firstErrorMessage.detail ?? | ||||||||||||||||
| String(firstErrorMessage)) | ||||||||||||||||
| : String(firstErrorMessage); | ||||||||||||||||
|
Comment on lines
+175
to
+180
|
||||||||||||||||
| const text = | |
| typeof firstErrorMessage === "object" | |
| ? (firstErrorMessage.message ?? | |
| firstErrorMessage.detail ?? | |
| String(firstErrorMessage)) | |
| : String(firstErrorMessage); | |
| const text = String(firstErrorMessage); |
Copilot
AI
Feb 23, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After successfully adding captures to a dataset, the modal closes but selection mode remains active with checkboxes still checked. The modal should exit selection mode after a successful add operation by calling exitSelectionMode (or exposing it globally so the manager can access it) to provide a clean user experience.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The event listener for .add-to-dataset-btn (lines 24-33) appears to be unused code, as no elements with this class exist in the templates included in this PR. This suggests either incomplete implementation or planned future functionality. If this is intended for single-capture quick-add from dropdown menus, the corresponding HTML buttons should be added. Otherwise, remove this code to avoid confusion.