Three of the four candidates were real. The fourth was my mistake in the issue.
The category tree adapter, duplicated verbatim between CategoryTreeSelect.tsx and FilterDrawer.tsx. This one was mine: #139 moved the storefront filter to a TreeSelect and copied the admin's adapter rather than sharing it, with a comment saying the shape "matches the admin's CategoryTreeSelect so the two stay comparable" — an argument for one implementation that instead produced two. It now lives in filters.ts beside buildCategoryTree, which was already shared for exactly the same reason: one meaning, one implementation.
Categories.tsx keeps its own. It builds a different shape for a real antd Tree, keyed rather than valued, with a title that is a React node carrying that screen's buttons. Genuinely different, and folding it in would mean a parameterised adapter that serves neither case clearly.
The item_images insert loop, written separately by create and update and differing only in where the id came from and where the sort order started. Both are parameters now, which also means the /uploads/ prefix is written once — #103 made that the value uploadUrl joins an origin onto, so it is a contract rather than a string.
Extracting it turned up two things the inline versions hid. Create indexed files[i]?.filename ?? '', so a missing element would have stored a path pointing at the uploads directory itself; iterating by entry removes the possibility rather than defending against it. And the helper's typed itemId surfaced that req.params.id is string | undefined under noUncheckedIndexedAccess, which the old inline unknown[] swallowed — now Number(), as the setItemTags call two lines above already did.
The optional-field guards, eight identical lines opening both routes. The distinction worth preserving is that undefined means "not submitted", which update reads as "leave as-is", so an unparseable value has to be told apart from an absent one. That is what makes it more than a null check and worth stating once.
TAG_COLORS was not a duplication. The issue listed four files on the strength of a grep that also matched STATUS_TAG_COLORS in Admin.tsx — a status-to-colour map for the inventory table, unrelated to the tag palette. What remains is one definition in backend/src/utils.ts and one mirror in frontend/src/admin/Tags.tsx, already carrying a comment pointing at the other, which is the same treatment ALLOWED_IMAGE_TYPES gets and is correct: there is no shared package, and creating one for a colour list would cost more than it saves.
Verified beyond the type checker, since three of these are pure moves that compile either way: 278 unit and 254 integration tests, and the end-to-end specs covering both consumers of the shared adapter — the storefront drawer and the admin item form's category picker, including inline category creation.
Three of the four candidates were real. The fourth was my mistake in the issue.
**The category tree adapter**, duplicated verbatim between `CategoryTreeSelect.tsx` and `FilterDrawer.tsx`. This one was mine: #139 moved the storefront filter to a `TreeSelect` and copied the admin's adapter rather than sharing it, with a comment saying the shape "matches the admin's CategoryTreeSelect so the two stay comparable" — an argument for one implementation that instead produced two. It now lives in `filters.ts` beside `buildCategoryTree`, which was already shared for exactly the same reason: one meaning, one implementation.
`Categories.tsx` keeps its own. It builds a different shape for a real antd `Tree`, keyed rather than valued, with a title that is a React node carrying that screen's buttons. Genuinely different, and folding it in would mean a parameterised adapter that serves neither case clearly.
**The `item_images` insert loop**, written separately by create and update and differing only in where the id came from and where the sort order started. Both are parameters now, which also means the `/uploads/` prefix is written once — #103 made that the value `uploadUrl` joins an origin onto, so it is a contract rather than a string.
Extracting it turned up two things the inline versions hid. Create indexed `files[i]?.filename ?? ''`, so a missing element would have stored a path pointing at the uploads directory itself; iterating by entry removes the possibility rather than defending against it. And the helper's typed `itemId` surfaced that `req.params.id` is `string | undefined` under `noUncheckedIndexedAccess`, which the old inline `unknown[]` swallowed — now `Number()`, as the `setItemTags` call two lines above already did.
**The optional-field guards**, eight identical lines opening both routes. The distinction worth preserving is that `undefined` means "not submitted", which update reads as "leave as-is", so an unparseable value has to be told apart from an absent one. That is what makes it more than a null check and worth stating once.
**`TAG_COLORS` was not a duplication.** The issue listed four files on the strength of a grep that also matched `STATUS_TAG_COLORS` in `Admin.tsx` — a status-to-colour map for the inventory table, unrelated to the tag palette. What remains is one definition in `backend/src/utils.ts` and one mirror in `frontend/src/admin/Tags.tsx`, already carrying a comment pointing at the other, which is the same treatment `ALLOWED_IMAGE_TYPES` gets and is correct: there is no shared package, and creating one for a colour list would cost more than it saves.
Verified beyond the type checker, since three of these are pure moves that compile either way: 278 unit and 254 integration tests, and the end-to-end specs covering both consumers of the shared adapter — the storefront drawer and the admin item form's category picker, including inline category creation.
Closes #182
bermudalamb
self-assigned this 2026-08-25 13:12:20 -05:00
bermudalamb
added this to the Code Quality and Hardening 2 project 2026-08-25 13:12:20 -05:00
Three of the four candidates were real. The fourth was my mistake in the issue.
**The category tree adapter**, duplicated verbatim between `CategoryTreeSelect.tsx` and `FilterDrawer.tsx`. This one was mine: #139 moved the storefront filter to a `TreeSelect` and copied the admin's adapter rather than sharing it, with a comment saying the shape "matches the admin's CategoryTreeSelect so the two stay comparable" — an argument for one implementation that instead produced two. It now lives in `filters.ts` beside `buildCategoryTree`, which was already shared for exactly the same reason: one meaning, one implementation.
`Categories.tsx` keeps its own. It builds a different shape for a real antd `Tree`, keyed rather than valued, with a title that is a React node carrying that screen's buttons. Genuinely different, and folding it in would mean a parameterised adapter that serves neither case clearly.
**The `item_images` insert loop**, written separately by create and update and differing only in where the id came from and where the sort order started. Both are parameters now, which also means the `/uploads/` prefix is written once — #103 made that the value `uploadUrl` joins an origin onto, so it is a contract rather than a string.
Extracting it turned up two things the inline versions hid. Create indexed `files[i]?.filename ?? ''`, so a missing element would have stored a path pointing at the uploads directory itself; iterating by entry removes the possibility rather than defending against it. And the helper's typed `itemId` surfaced that `req.params.id` is `string | undefined` under `noUncheckedIndexedAccess`, which the old inline `unknown[]` swallowed — now `Number()`, as the `setItemTags` call two lines above already did.
**The optional-field guards**, eight identical lines opening both routes. The distinction worth preserving is that `undefined` means "not submitted", which update reads as "leave as-is", so an unparseable value has to be told apart from an absent one. That is what makes it more than a null check and worth stating once.
**`TAG_COLORS` was not a duplication.** The issue listed four files on the strength of a grep that also matched `STATUS_TAG_COLORS` in `Admin.tsx` — a status-to-colour map for the inventory table, unrelated to the tag palette. What remains is one definition in `backend/src/utils.ts` and one mirror in `frontend/src/admin/Tags.tsx`, already carrying a comment pointing at the other, which is the same treatment `ALLOWED_IMAGE_TYPES` gets and is correct: there is no shared package, and creating one for a colour list would cost more than it saves.
Verified beyond the type checker, since three of these are pure moves that compile either way: 278 unit and 254 integration tests, and the end-to-end specs covering both consumers of the shared adapter — the storefront drawer and the admin item form's category picker, including inline category creation.
Closes#182
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Three of the four candidates were real. The fourth was my mistake in the issue.
The category tree adapter, duplicated verbatim between
CategoryTreeSelect.tsxandFilterDrawer.tsx. This one was mine: #139 moved the storefront filter to aTreeSelectand copied the admin's adapter rather than sharing it, with a comment saying the shape "matches the admin's CategoryTreeSelect so the two stay comparable" — an argument for one implementation that instead produced two. It now lives infilters.tsbesidebuildCategoryTree, which was already shared for exactly the same reason: one meaning, one implementation.Categories.tsxkeeps its own. It builds a different shape for a real antdTree, keyed rather than valued, with a title that is a React node carrying that screen's buttons. Genuinely different, and folding it in would mean a parameterised adapter that serves neither case clearly.The
item_imagesinsert loop, written separately by create and update and differing only in where the id came from and where the sort order started. Both are parameters now, which also means the/uploads/prefix is written once — #103 made that the valueuploadUrljoins an origin onto, so it is a contract rather than a string.Extracting it turned up two things the inline versions hid. Create indexed
files[i]?.filename ?? '', so a missing element would have stored a path pointing at the uploads directory itself; iterating by entry removes the possibility rather than defending against it. And the helper's typeditemIdsurfaced thatreq.params.idisstring | undefinedundernoUncheckedIndexedAccess, which the old inlineunknown[]swallowed — nowNumber(), as thesetItemTagscall two lines above already did.The optional-field guards, eight identical lines opening both routes. The distinction worth preserving is that
undefinedmeans "not submitted", which update reads as "leave as-is", so an unparseable value has to be told apart from an absent one. That is what makes it more than a null check and worth stating once.TAG_COLORSwas not a duplication. The issue listed four files on the strength of a grep that also matchedSTATUS_TAG_COLORSinAdmin.tsx— a status-to-colour map for the inventory table, unrelated to the tag palette. What remains is one definition inbackend/src/utils.tsand one mirror infrontend/src/admin/Tags.tsx, already carrying a comment pointing at the other, which is the same treatmentALLOWED_IMAGE_TYPESgets and is correct: there is no shared package, and creating one for a colour list would cost more than it saves.Verified beyond the type checker, since three of these are pure moves that compile either way: 278 unit and 254 integration tests, and the end-to-end specs covering both consumers of the shared adapter — the storefront drawer and the admin item form's category picker, including inline category creation.
Closes #182