Skip to content

Conversation

@annavik
Copy link
Member

@annavik annavik commented Feb 10, 2026

Summary

In this PR, we change the wording "collections" to "capture sets". We also make sure to use the term "capture" instead of the more generic "image" in other places in UI, when referring to camera trap source images. We also clarify both the term "Capture" and "Capture set" with tooltips (see screenshots).

List of Changes

  • Change UI strings to use wording "capture set" instead of "collection"
  • Change UI strings to use wording "capture" instead of "image"
  • Update routing from "/collections" to "/capture-sets"
  • Tweak capture related tooltips
  • Update FE code (component names, function names, variable names, etc.) to use wording "capture sets" instead of "collections"
  • A bit of cleanup to remove unused code

Detailed Description

How to Test the Changes

Examples of views affected:

  • Captures view
  • Capture sets view
  • Views related to uploading or syncing captures (project quick start, station configuration, etc.)
  • Views where data can be filtered by capture set (jobs, occurrences, etc.)
  • Forms where capture set is a form field (jobs, exports, etc.)

Screenshots

Screenshot 2026-02-10 at 13 52 57 Screenshot 2026-02-10 at 13 53 09

Deployment Notes

No BE deploy needed. These changes only concerns the FE code.

Summary by CodeRabbit

  • Refactor

    • Renamed "collections" to "capture sets" across the app and routes; /collections now redirects to /capture-sets and the old collection detail route was removed.
    • Replaced collection data/model usage with capture-set equivalents.
  • UI

    • Replaced "images" with "captures" in dialogs, labels and strings; updated picker and list headings.
    • Added per-project filter info tooltips (with optional Configure links) and a PageHeader tooltip on the captures page.
  • New Features

    • New contextual FilterInfo display for filters.

@netlify
Copy link

netlify bot commented Feb 10, 2026

Deploy Preview for antenna-preview ready!

Name Link
🔨 Latest commit fa34697
🔍 Latest deploy log https://app.netlify.com/projects/antenna-preview/deploys/698c5a155e8dd200081db4ab
😎 Deploy Preview https://deploy-preview-1127--antenna-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 73 (🟢 up 7 from production)
Accessibility: 89 (🟢 up 9 from production)
Best Practices: 92 (🔴 down 8 from production)
SEO: 100 (🟢 up 8 from production)
PWA: 80 (no change from production)
View the detailed breakdown and full score reports

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify
Copy link

netlify bot commented Feb 10, 2026

Deploy Preview for antenna-ssec ready!

Name Link
🔨 Latest commit fa34697
🔍 Latest deploy log https://app.netlify.com/projects/antenna-ssec/deploys/698c5a15fe8e3d00088cca50
😎 Deploy Preview https://deploy-preview-1127--antenna-ssec.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 10, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Replaces "Collections" with "CaptureSets" across routes, hooks, models, components, filters, translations, and forms; removes collection-detail redirect/component and collection-specific hooks; adds project-scoped filters with tooltip/linking to capture-sets and updates UI labels from images/collections → captures/capture-sets.

Changes

Cohort / File(s) Summary
Routing & App
ui/src/app.tsx, ui/src/utils/constants.ts, ui/src/pages/project/sidebar/useSidebarSections.tsx
Replace collections routes/sidebar entries with capture-sets, add local redirect from /collections/capture-sets, update route exports and matchPath logic.
Models & API constants
ui/src/data-services/models/capture-set.ts, ui/src/data-services/constants.ts
Rename Collection → CaptureSet types/classes; rename API_ROUTES.COLLECTIONS → API_ROUTES.CAPTURE_SETS (path preserved).
Data hooks & services
ui/src/data-services/hooks/capture-sets/useCaptureSets.ts, ui/src/data-services/hooks/capture-sets/usePopulateCaptureSet.ts, ui/src/data-services/hooks/collections/useCollectionDetails.ts (deleted), ui/src/data-services/hooks/entities/useEntities.ts, ui/src/data-services/hooks/storage-sources/useStorageDetails.ts (deleted)
Introduce useCaptureSets/usePopulateCaptureSet, switch query keys/endpoints to CAPTURE_SETS, change model types to CaptureSet, and remove single-collection detail hooks.
Project pages & actions
ui/src/pages/project/capture-sets/*, ui/src/pages/project/collections/collection-details.tsx (deleted)
Add CaptureSets page, columns, populate-capture-set action, and remove old collection-details redirect; update dialogs/actions to use capture-set types and routes.
Filtering & pickers
ui/src/components/filtering/filter-control.tsx, ui/src/components/filtering/filters/capture-set-filter.tsx, ui/src/design-system/components/capture-set-picker.tsx, ui/src/utils/useFilters.ts
Replace CollectionFilter/CollectionsPicker with CaptureSetFilter/CaptureSetPicker; make AVAILABLE_FILTERS project-scoped, add filter info tooltips/links to capture-sets.
Entity forms & mappings
ui/src/pages/project/entities/details-form/..., ui/src/pages/project/entities/details-form/capture-set-details-form.tsx
Replace CollectionDetailsForm with CaptureSetDetailsForm, update customFormMap and form value types and picker usages.
UI label/text updates
ui/src/pages/captures/*, ui/src/pages/job-details/*, ui/src/pages/jobs/*, ui/src/pages/deployment-details/*, ui/src/utils/language.ts
Change translation keys and UI text from images/collections → captures/capture-sets; add capture-set related STRING keys and remove collection-specific keys.
Filtering control UI
ui/src/components/filtering/filter-control.tsx
Introduce new exported FilterInfo component that renders tooltip + optional Configure link; integrate into filter control header when filter.info exists.
Styling & small UI changes
ui/src/pages/captures/upload-images-dialog/select-images-section/styles.module.scss, ui/src/pages/captures/captures.tsx
Rename CSS selector .collection.captures; PageHeader for captures now accepts a tooltip prop.
Miscellaneous small updates
ui/src/pages/captures/upload-images-dialog/upload-images-dialog.tsx, ui/src/pages/captures/upload-images-dialog/select-images-section/select-images-section.tsx, ui/src/pages/job-details/job-details-form/job-details-form.tsx, ui/src/pages/project/entities/details-form/export-details-form.tsx, ui/src/pages/project/entities/details-form/constants.ts
Adjust strings, component imports and props to reference captures/capture-sets (labels, button text, picker components, details form mappings).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Suggested reviewers

  • mihow

Poem

🐰 I hopped through folders, careful and spry,
Collections became CaptureSets with a twinkle in my eye.
Routes rerouted, filters now show tips,
Labels updated, and pickers gained new quips.
A nibble of change — a joyful code-hop, hi!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely describes the main change: renaming terminology from 'collections' to 'capture sets' across the frontend.
Description check ✅ Passed The PR description covers required sections with sufficient detail: Summary, List of Changes, Detailed Description, How to Test the Changes, Screenshots, and Deployment Notes. All key aspects of the change are documented.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch copy/capture-sets

No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
ui/src/utils/useFilters.ts (1)

20-48: Three capture-set filters share identical label/info but differ by field — consider DRY helper.

Lines 26–31, 34–39, and 42–47 repeat the same label, info.text, and info.to for collection, source_image_collection, and collections. If one is updated and another is missed, they'll drift. A small helper could reduce that risk:

♻️ Suggested helper
+const captureSetFilterInfo = (projectId: string) => ({
+  label: translate(STRING.FIELD_LABEL_CAPTURE_SET),
+  info: {
+    text: translate(STRING.TOOLTIP_CAPTURE_SET),
+    to: APP_ROUTES.CAPTURE_SETS({ projectId }),
+  },
+})
+
 export const AVAILABLE_FILTERS = (projectId: string): FilterConfig[] => [
   ...
   {
-    label: translate(STRING.FIELD_LABEL_CAPTURE_SET),
-    field: 'collection',
-    info: {
-      text: translate(STRING.TOOLTIP_CAPTURE_SET),
-      to: APP_ROUTES.CAPTURE_SETS({ projectId }),
-    },
+    ...captureSetFilterInfo(projectId),
+    field: 'collection',
   },
   {
-    label: translate(STRING.FIELD_LABEL_CAPTURE_SET),
-    field: 'source_image_collection',
-    info: {
-      text: translate(STRING.TOOLTIP_CAPTURE_SET),
-      to: APP_ROUTES.CAPTURE_SETS({ projectId }),
-    },
+    ...captureSetFilterInfo(projectId),
+    field: 'source_image_collection',
   },
   {
-    label: translate(STRING.FIELD_LABEL_CAPTURE_SET),
-    field: 'collections',
-    info: {
-      text: translate(STRING.TOOLTIP_CAPTURE_SET),
-      to: APP_ROUTES.CAPTURE_SETS({ projectId }),
-    },
+    ...captureSetFilterInfo(projectId),
+    field: 'collections',
   },

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
ui/src/utils/language.ts (3)

504-505: ⚠️ Potential issue | 🟡 Minor

MESSAGE_PROCESS_NOW_TOOLTIP still references "image" instead of "capture".

This tooltip text reads "Process this single image with presets" — should likely be updated to "capture" for consistency with the rest of the rename.

Proposed fix
-  [STRING.MESSAGE_PROCESS_NOW_TOOLTIP]:
-    'Process this single image with presets',
+  [STRING.MESSAGE_PROCESS_NOW_TOOLTIP]:
+    'Process this single capture with presets',

416-416: ⚠️ Potential issue | 🟡 Minor

FIELD_LABEL_SAMPLE_CAPTURES still maps to 'Sample images'.

The key name references "captures" but the displayed string says "images". Should this be "Sample captures" for consistency?


486-487: ⚠️ Potential issue | 🟡 Minor

Pre-existing grammar issue: "must smaller" → "must be smaller".

Not part of this PR's scope, but worth noting: the string reads "The image must smaller than" — it should be "The image must be smaller than".

Proposed fix
   [STRING.MESSAGE_IMAGE_SIZE]:
-    'The image must smaller than {{value}} {{unit}}.',
+    'The image must be smaller than {{value}} {{unit}}.',
ui/src/utils/useFilters.ts (1)

72-75: ⚠️ Potential issue | 🟡 Minor

"Source image" labels not updated to "Capture".

The filters at lines 73 and 109 still use the hardcoded label 'Source image' rather than translate(STRING.FIELD_LABEL_CAPTURE). Given the PR's goal of replacing "source image" with "capture" in UI text, these should likely be updated for consistency.

Suggested fix
   {
-    label: 'Source image',
+    label: translate(STRING.FIELD_LABEL_CAPTURE),
     field: 'detections__source_image', // This is for viewing Occurrences by source image. `@TODO`: Can we update this key to "source_image" to streamline?
   },
   {
-    label: 'Source image',
+    label: translate(STRING.FIELD_LABEL_CAPTURE),
     field: 'source_image_single', // This is for viewing Jobs by source image. `@TODO`: Can we update this key to "source_image" to streamline?
   },

Also applies to: 108-111

ui/src/pages/job-details/job-details-form/job-details-form.tsx (1)

152-155: ⚠️ Potential issue | 🟡 Minor

Pre-existing bug: config.pipeline used for startNow controller.

Line 155 passes config.pipeline as the config for the startNow field. This should likely be config.startNow. Not introduced by this PR, but worth noting.

Proposed fix
           <FormController
              name="startNow"
              control={control}
-             config={config.pipeline}
+             config={config.startNow}
              render={({ field }) => (
🤖 Fix all issues with AI agents
In `@ui/src/pages/captures/upload-images-dialog/upload-images-dialog.tsx`:
- Line 293: In the upload summary of the UploadImagesDialog component locate the
InputValue usage that currently reads InputValue label="Images"
value={images.length} and change the label prop to "Captures" so it reads
label="Captures" (preserving the value={images.length} and any surrounding
structure); ensure no other occurrences of the old "Images" label remain in the
component.

In `@ui/src/pages/project/capture-sets/capture-sets.tsx`:
- Around line 51-58: Update the stale inline comment inside the useEffect that
checks captureSets — change the wording from "collection" to "capture set" so it
reads something like "If any capture set has a job in progress, we want to poll
the endpoint so we can show job updates"; locate the comment near the useEffect
that references captureSets and setPoll and replace the phrase accordingly.

In `@ui/src/utils/constants.ts`:
- Around line 20-21: The exported route constant CAPTURE_SETS was changed to
`/projects/${params.projectId}/capture-sets` but there is no redirect from the
old `/collections` path; update the app router to add a redirect from
`/projects/:projectId/collections` to the new CAPTURE_SETS route (use the same
projectId param and preserve query/search/hash) so bookmarked/shared URLs
continue to work—locate where routes are defined/registered in the router and
add a redirect entry that programmatically resolves to CAPTURE_SETS (or uses
history.replace/navigation redirect) for backward compatibility.

In `@ui/src/utils/useFilters.ts`:
- Around line 23-24: Update the inline TODO comment in the useFilters hook where
the filter with field 'source_image_collection' is defined: fix the typo by
adding the missing word "to" so the comment reads "...Can we update this key to
"capture_set_id" to streamline?"—look for the filter object referencing field
'source_image_collection' in the useFilters.ts file (the hook/function
useFilters) and correct the comment text.
🧹 Nitpick comments (5)
ui/src/pages/captures/upload-images-dialog/select-images-section/select-images-section.tsx (1)

46-46: Leftover styles.collection CSS class name.

The className still references styles.collection while the rest of the PR renames "collection" → "capture set". Consider renaming this CSS class for consistency, though it's not user-facing.

ui/src/pages/project/entities/details-form/collection-details-form.tsx (1)

128-135: Consider renaming CollectionDetailsForm and related types to CaptureSetDetailsForm.

The component (CollectionDetailsForm), its type (CollectionFormValues), and the filename (collection-details-form.tsx) still use the old "collection" terminology. This is internal-only so it's not urgent, but it would complete the rename for consistency.

ui/src/pages/project/sidebar/useSidebarSections.tsx (1)

138-141: Unused expression — result is computed and discarded.

This .map().flat().find() chain produces a value that is never assigned or returned. It appears to be dead code (likely a leftover from a refactor). Not introduced by this PR, but worth cleaning up.

🧹 Remove dead code
-  sidebarSections
-    .map(({ items }) => items)
-    .flat()
-    .find((item) => !!matchPath(item.path, location.pathname))
-
   return { sidebarSections, activeItem }
ui/src/data-services/models/capture-set.ts (1)

26-37: currentJob mutates _jobs array via in-place sort.

Pre-existing issue: this._jobs.sort(...) mutates the readonly array on every access to currentJob. Consider using [...this._jobs].sort(...) or this._jobs.toSorted(...) to avoid side effects.

Proposed fix
-    return this._jobs.sort((j1: Job, j2: Job) => {
+    return [...this._jobs].sort((j1: Job, j2: Job) => {
ui/src/pages/project/capture-sets/populate-capture-set.tsx (1)

20-20: Hardcoded error string — consider using translate() for consistency with i18n.

Other UI strings in this file (e.g., translate(STRING.POPULATE) on line 31) go through the translation system. This error tooltip is a raw English string.

@annavik annavik requested a review from mihow February 10, 2026 13:50
@mihow
Copy link
Collaborator

mihow commented Feb 10, 2026

Exciting!

@mihow
Copy link
Collaborator

mihow commented Feb 10, 2026

what could be a simple way to link between the Captures view and Capture Sets? something for now until the layout is reworked?

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@ui/src/app.tsx`:
- Around line 112-113: Add a redirect Route for the removed "collections/:id"
path so bookmarked links don't hit NotFound: in the router where <Route
path="capture-sets" element={<CaptureSets />} /> and <Route path="collections"
element={<Collections />} /> are defined, add a route matching "collections/:id"
that redirects (via react-router Navigate) to the new listing (e.g.,
"capture-sets" or "collections") instead of rendering CollectionDetails or
falling through to NotFound; reference the "collections/:id" path, the
CaptureSets/Collections components, and the NotFound behavior when implementing
the redirect.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@ui/src/utils/useFilters.ts`:
- Around line 158-160: The code casts projectId from useParams() to string when
calling AVAILABLE_FILTERS(projectId as string), which can propagate "undefined"
into routes (e.g., APP_ROUTES.CAPTURE_SETS) if the hook is used outside a route
with :projectId; change the logic in useFilters so it does not force-cast
projectId — check for projectId existence first and either return an
empty/fallback filters list or build AVAILABLE_FILTERS only when projectId is
defined, and update any places that use the resulting info properties to be
conditional on projectId (refer to useParams, projectId, AVAILABLE_FILTERS and
APP_ROUTES.CAPTURE_SETS to locate the change).
🧹 Nitpick comments (3)
ui/src/utils/useFilters.ts (1)

160-160: Typo: avaibleFiltersavailableFilters.

This variable name is misspelled on lines 160, 162, 185, and 198. Consider renaming for readability.

✏️ Proposed fix
-  const avaibleFilters = AVAILABLE_FILTERS(projectId as string)
+  const availableFilters = AVAILABLE_FILTERS(projectId as string)

-  const _filters = avaibleFilters.map(({ field, ...rest }) => {
+  const _filters = availableFilters.map(({ field, ...rest }) => {

-    if (avaibleFilters.some((filter) => filter.field === field)) {
+    if (availableFilters.some((filter) => filter.field === field)) {

-    if (avaibleFilters.some((filter) => filter.field === field)) {
+    if (availableFilters.some((filter) => filter.field === field)) {
ui/src/components/filtering/filter-control.tsx (2)

113-138: FilterInfo component: consider hoisting Tooltip.Provider.

Each FilterInfo instance wraps itself in its own <Tooltip.Provider>. If multiple capture-set filters render on the same page (e.g., on the occurrences or jobs view), you'll have redundant providers. This works correctly, but a single Tooltip.Provider higher in the tree (or at the app level) would be slightly cleaner and allow shared delayDuration configuration.

This is a minor suggestion and not blocking.


125-128: Avoid !important override on the link styling.

The !w-auto Tailwind class uses the !important modifier to override width from buttonVariants. If the variant's width is coming from a default you don't want, consider customizing the variant instead, or using a more specific class composition. This is fine for now but can become fragile if variant styles change.

@annavik
Copy link
Member Author

annavik commented Feb 11, 2026

what could be a simple way to link between the Captures view and Capture Sets? something for now until the layout is reworked?

Yes, let's find some way to also connect these views sooner than later! I got a bit focused on the individual views and forgot about this 🙃

I gave this some thinking... One idea could be we apply this UI pattern for filtering controls in general (tooltip with some info and a link to configure):
Screenshot 2026-02-11 at 11 02 36

In the capture view, it could look like this.
Screenshot 2026-02-11 at 11 14 21
Screenshot 2026-02-11 at 11 13 39

Since the code for filtering controls is quite generalized, we could control all this from the big config object and easily apply the same pattern for other filters as well. We could also reuse the tooltip strings we are using for the main views. Example:
Screenshot 2026-02-11 at 11 09 46

I pushed a commit to test this idea for capture sets only to begin with! Let me know what you think? Maybe on the capture page specifically we could also consider a more explicit link to the capture sets page.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants