Skip to content

Tag Module#2526

Merged
pylipp merged 86 commits intomasterfrom
tag-module
Apr 7, 2026
Merged

Tag Module#2526
pylipp merged 86 commits intomasterfrom
tag-module

Conversation

@MomoRazor
Copy link
Copy Markdown
Contributor

@MomoRazor MomoRazor commented Jan 8, 2026

Three bugs in the Tags overview table: header not sticky, date columns not sorting, and a crash when sorting tags with null dates.

Sticky header (TagsTable.tsx)

<Flex overflowX="auto"> created a new scroll container, scoping <Thead position="sticky"> to a container that never scrolled vertically. Changed outer Flex to height="100%" (matches BoxesTable) and moved overflowX="auto" to a <Box> wrapping only <Table>.

Date column sorting (TagsContainer.tsx)

All three date columns used sort: "datetime" — not a react-table v7 column property. Changed to sortType: "datetime".

Null date sort crash (TagsContainer.tsx)

react-table's built-in "datetime" sort calls .getTime() unconditionally, throwing TypeError when lastUsedOn (or other date fields) is null on a newly created tag. Replaced with a custom sort function that pushes nulls to the end:

function nullableDateSortType(rowA, rowB, columnId) {
  const a = rowA.values[columnId];
  const b = rowB.values[columnId];
  if (!a && !b) return 0;
  if (!a) return 1;
  if (!b) return -1;
  return a.getTime() - b.getTime();
}

Pre-existing TS error (transformers.ts)

lastModifiedOn fallback was new Date(createdOn)createdOn can be null, causing a type error. Changed to undefined when both are null, consistent with how lastUsedOn is handled.

@sentry
Copy link
Copy Markdown

sentry bot commented Jan 9, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 78.49%. Comparing base (0edd692) to head (4a92038).

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2526      +/-   ##
==========================================
+ Coverage   78.29%   78.49%   +0.19%     
==========================================
  Files         281      290       +9     
  Lines       20623    20898     +275     
  Branches     2109     2115       +6     
==========================================
+ Hits        16147    16404     +257     
- Misses       4428     4447      +19     
+ Partials       48       47       -1     
Flag Coverage Δ
backend 99.50% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Contributor

@pylipp pylipp left a comment

Choose a reason for hiding this comment

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

Hey @MomoRazor , thanks a lot for this large PR! I could do some functional testing, and I'm already quite happy with the new module. I found some small bugs, and I have some UX questions that I'm getting Roanna's feedback for.

Bugs

  • table breaks when filtering Application for All and Box
  • in the selection for columns displayed in the table, the first (checkbox) column should not be listed; it's always displayed
  • when creating/updating a tag, and selecting "Boxes" or "Beneficiaries" as Application, I can't submit the form. Maybe a zod issue?

UX questions @aerinsol please let us know your preferences:

  1. Tags overview - action buttons: side by side on the same level as the table like in dropapp, or unfolding from a menu? Are the icons ok?
  2. Tags overview column name "Application". In dropapp it is "Apply to". Change it or keep it?
  3. UpdateTag title: "Update Tag # 57" . It contains the ID. In UpdateBox we show "Box 12345678" as title. I think "Update Tag" would be fine
  4. created/lastModified info: in dropapp, the tag details page shows a box with created and last-modified info. We don't have this in v2 but we could add the created/lastModified columns in the TagsOverview (but hide them by default)
  5. UpdateTag: when changing the Application to "Boxes" or "Beneficiaries", it would be nice to have an info text appear (like from the i info icon in dropapp): "changing type will unassign tags applied to boxes" or "...to beneficiaries"
  6. CreateTag: would be nice to have a random color picked

Comment thread front/src/views/Tags/CreateTagView.tsx Outdated
Comment thread front/src/views/Tags/UpdateTagView.tsx Outdated
Comment thread front/src/hooks/useDeleteTags.ts Outdated
Comment thread front/src/views/Tags/UpdateTagView.tsx Outdated
const { createToast } = useNotification();
const { triggerError } = useErrorHandling();

const { data: tagData, error: tagError } = useSuspenseQuery(TAG_QUERY, {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm wondering if this query can be avoided since the data for the form could be taken from the row?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Seeing as the Update Tag Form is a page right now, have you go any idea on how I can pass all the row information to a different page, rather then just pass the 'id' and pull the tag a new? Or should I consider the option of not having the update form in a completely separate link as it is now?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could you use the Apollo Cache maybe? When TagOverview is shown first, it should have all tags loaded by keys Tag:ID (I use the Apollo extension to view this in the browser dev tools).
If it's a cache miss for some reason (e.g. someone navigating directly to /tags/ID without having viewed TagOverview before), then the BE query should be run.
I haven't actually done this, it's an idea rather that would need a bit more investigation.
Re your question I'd keep the update form on a separate link.

Comment thread front/src/views/Tags/TagsOverview/components/transformers.ts Outdated
Comment thread front/src/views/Tags/TagsOverview/components/TagsContainer.tsx Outdated
@pylipp
Copy link
Copy Markdown
Contributor

pylipp commented Jan 10, 2026

@aerinsol some dropapp UX elements for reference:
2026-01-10-154216_250x81_scrot 2026-01-10-170526_272x93_scrot

@pylipp pylipp requested a review from aerinsol January 10, 2026 16:27
@MomoRazor
Copy link
Copy Markdown
Contributor Author

Thanks for the feedback @pylipp ! I'll probably get stuck in tomorrow :)

@aerinsol
Copy link
Copy Markdown
Member

Yeesh, I really don't like the interaction look for updating a tag. I know we use it for agreements and boxes but ugh. Doesn't seem to fit the tags - likely a good quick and dirty redesign task for Mariam. Nevertheless.... To answer your questions

UX questions @aerinsol please let us know your preferences:

  1. Tags overview - action buttons: side by side on the same level as the table like in dropapp, or unfolding from a menu? Are the icons ok?

Drop down is unnecessary here. I would keep it side by side so there's not an extra click.

  1. Tags overview column name "Application". In dropapp it is "Apply to". Change it or keep it?

Use the drop app name.

  1. UpdateTag title: "Update Tag # 57" . It contains the ID. In UpdateBox we show "Box 12345678" as title. I think "Update Tag" would be fine

Agree.

  1. created/lastModified info: in dropapp, the tag details page shows a box with created and last-modified info. We don't have this in v2 but we could add the created/lastModified columns in the TagsOverview (but hide them by default)

Yes, would have this in the table. For the detailed view I would give it to Mariam.

  1. UpdateTag: when changing the Application to "Boxes" or "Beneficiaries", it would be nice to have an info text appear (like from the i info icon in dropapp): "changing type will unassign tags applied to boxes" or "...to beneficiaries"

Agree

  1. CreateTag: would be nice to have a random color picked

Agree

@MomoRazor
Copy link
Copy Markdown
Contributor Author

Hi guys! Quick update on this:

  • Firstly, I tackled all the feedback I marked as resolved here
  • I fixed the column filtering working as a union rather then an intersect. A side fix for this was using Portal to make the Filter UI be able to show even if the Table is too small for the UI to fit.
  • Fixed the create and update tag when selecting a different application
  • Removed the dropdown and rendering the buttons next to each other
  • Switched the column name of 'Application' to the dropapp name 'Apply To'
  • Fixed the Update form title
  • Added a helper text when Updating a Tag on the Application field
  • Have a random colour selected when creating a new Tag

With those done, what remains are the following:

  • Adding created and modified dates on the table
  • Stopping the checkout column from being hideable
  • The rest of the unresolved stuff

Let me know if I may have missed anything :)

@pylipp
Copy link
Copy Markdown
Contributor

pylipp commented Jan 15, 2026

thanks for the update @MomoRazor
this looks very promising! I'll give it another round of functional testing when you've implemented the remainders you listed.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds the new Tags module to the Boxtribute frontend (and supporting shared components / GraphQL cache updates) so coordinators can create, update, list, and delete tags in the new app UI.

Changes:

  • Introduces Tag management pages (overview table, create, update) with routing + header menu entry.
  • Adds tag deletion flow (bulk selection + confirmation) and a shared hex color picker field used by the tag form.
  • Extends Tag GraphQL fragments / mocks / tests to include additional tag metadata fields (e.g., deletedOn / lastUsedOn) and updates Apollo cache policy for Query.tag.

Reviewed changes

Copilot reviewed 31 out of 31 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
shared-components/form/ColorField.tsx New shared hex color picker input used by TagForm.
package.json Adds a convenience rebuild-restart script.
graphql/fragments.ts Formatting change to a fragment selection set.
front/src/views/Tags/UpdateTag/UpdateTagView.tsx New “Update Tag” view + mutation handling.
front/src/views/Tags/UpdateTag/UpdateTagView.test.tsx Tests for updating tags (success + error cases).
front/src/views/Tags/TagsOverview/TagsView.tsx New “Manage Tags” overview page shell.
front/src/views/Tags/TagsOverview/TagsView.test.tsx Tests for tags overview page behaviors (table, filtering, delete dialog, etc.).
front/src/views/Tags/TagsOverview/components/transformers.ts Transforms tag query results into table rows.
front/src/views/Tags/TagsOverview/components/TagsTable.tsx Tags table rendering with selection + actions.
front/src/views/Tags/TagsOverview/components/TagsContainer.tsx Suspense query + column definitions + navigation wiring.
front/src/views/Tags/TagsOverview/components/TagsActions.tsx Create/Delete actions above the table.
front/src/views/Tags/TagsOverview/components/RemoveTagsOverlay.tsx Confirmation dialog UI for bulk tag deletion.
front/src/views/Tags/TagsOverview/components/RemoveTagsButton.tsx Button + dialog state management for tag deletion.
front/src/views/Tags/hooks/useTagsActions.ts Hook tying selected rows to tag actions (delete).
front/src/views/Tags/CreateTag/CreateTagView.tsx New “Create Tag” view + mutation handling.
front/src/views/Tags/CreateTag/CreateTagView.test.tsx Tests for creating tags (success + error cases).
front/src/views/Tags/components/TagForm.tsx Shared Tag form with Zod validation + ColorField integration.
front/src/views/Tags/components/TagForm.test.tsx Unit tests for TagForm validation and submission.
front/src/views/QrReader/QrReaderMultiBoxAssignTags.test.tsx Updates mocks/timeouts due to Tag shape changes.
front/src/views/Boxes/BoxesViewActions.test.tsx Updates tag mocks used by box bulk actions tests.
front/src/views/Boxes/BoxesView.test.tsx Updates tag mocks + test timeouts to align with new Tag fields.
front/src/views/BoxEdit/BoxEditView.test.tsx Increases timeout (likely to reduce flakiness).
front/src/tests/helpers.ts Fixes async iteration in select-field helper.
front/src/queries/fragments.ts Extends TagBasicFields fragment with new fields.
front/src/queries/cache.ts Adds Query.tag typePolicy read to normalize tag queries.
front/src/mocks/tags.ts Extends tag mocks with new fields used across tests.
front/src/mocks/boxes.ts Extends embedded tag mocks inside box fixtures.
front/src/hooks/useDeleteTags.ts New hook + mutation for deleting tags.
front/src/components/Table/Filter.tsx Wraps popover content in a Portal for filter UI.
front/src/components/HeaderMenu/HeaderMenuContainer.tsx Adds internal “Manage Tags” link in coordinator menu.
front/src/App.tsx Adds routes for tags overview / create / update under base routes.

Comment thread front/src/views/Tags/TagsOverview/components/transformers.ts Outdated
Comment thread front/src/views/Tags/components/TagForm.tsx
Comment thread front/src/views/Tags/components/TagForm.tsx Outdated
Comment thread shared-components/form/ColorField.tsx Outdated
Comment thread shared-components/form/ColorField.tsx Outdated
Comment thread front/src/views/Tags/UpdateTag/UpdateTagView.tsx Outdated
name: updateTagOutput.name,
type: updateTagOutput.application,
color: updateTagOutput.color,
description: updateTagOutput.description ?? "",
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

UpdateTagView forces description to "" when it’s undefined (updateTagOutput.description ?? ""). This will overwrite a previously-null/undefined description with an empty string even if the user didn’t touch the field. Prefer passing undefined when the field is unset (or explicitly decide on a clear 'clear description' behavior) to avoid unintended data changes.

Suggested change
description: updateTagOutput.description ?? "",
description: updateTagOutput.description,

Copilot uses AI. Check for mistakes.
Comment thread front/src/views/Tags/TagsOverview/components/TagsTable.tsx
Comment thread front/src/views/Tags/TagsOverview/components/RemoveTagsButton.tsx Outdated
Comment thread front/src/views/Tags/hooks/useTagsActions.ts Outdated
pylipp and others added 4 commits April 7, 2026 22:46
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Philipp Metzner <10617122+pylipp@users.noreply.github.com>
Copilot AI changed the title Tag module Tag module: test assertion enhancements + ColorField trigger input fix Apr 7, 2026
@pylipp
Copy link
Copy Markdown
Contributor

pylipp commented Apr 7, 2026

@copilot try to fix two more issues:

  • the table header for TagsView is not sticky but it should be
  • the sorting of the last-modified and last-used columns does not work. The rows are not brought into correct order

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 7, 2026

@copilot try to fix two more issues:

  • the table header for TagsView is not sticky but it should be
  • the sorting of the last-modified and last-used columns does not work. The rows...

Fixed in 5bc7cf0:

Sticky header (TagsTable.tsx): The outer <Flex overflowX="auto"> was creating a new scroll container, so position: sticky on the <Thead> was scoped to that container (which didn't scroll vertically). Changed to height="100%" on the outer Flex (matching BoxesTable) and wrapped only the <Table> in a <Box overflowX="auto"> to preserve horizontal scroll.

Date sorting (TagsContainer.tsx): The three date columns were using sort: "datetime"sort is not a react-table v7 column option. Changed all three to sortType: "datetime" which is the correct property name.

Copilot AI changed the title Tag module: test assertion enhancements + ColorField trigger input fix TagsView: fix sticky header and date column sorting Apr 7, 2026
@pylipp
Copy link
Copy Markdown
Contributor

pylipp commented Apr 7, 2026

@copilot when a new tag is created it has null lastUsedOn, and the sorting breaks. This must not happen

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 7, 2026

@copilot when a new tag is created it has null lastUsedOn, and the sorting breaks. This must not happen

Fixed in d74f2ea. The built-in react-table "datetime" sort calls .getTime() directly on the column value, crashing with a TypeError when the value is null.

Replaced all three date column sort types with a custom nullableDateSortType function that gracefully handles null/undefined by placing those rows at the end:

function nullableDateSortType(rowA, rowB, columnId) {
  const a = rowA.values[columnId];
  const b = rowB.values[columnId];
  if (!a && !b) return 0;
  if (!a) return 1;   // null → end
  if (!b) return -1;
  return a.getTime() - b.getTime();
}

Also fixed a related pre-existing TS error in transformers.ts where the lastModifiedOn fallback was new Date(createdOn)createdOn can itself be null, so the fallback is now undefined when both are null.

Copilot AI changed the title TagsView: fix sticky header and date column sorting TagsView: fix sticky header, date column sorting, and null date crash Apr 7, 2026
@pylipp pylipp changed the title TagsView: fix sticky header, date column sorting, and null date crash Tag Module Apr 7, 2026
@pylipp pylipp merged commit 383dd05 into master Apr 7, 2026
12 of 14 checks passed
@pylipp pylipp deleted the tag-module branch April 7, 2026 23:12
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.

5 participants