The (components) directory held 28 loose files alongside six feature
folders, so the convention existed but was only half applied. Every new
feature added to the pile rather than to a place.
Grouped into account, billing, devices, api-keys, alerts, search and
community. Only nav-items and overview stay at the root, since both
compose across features rather than belonging to one.
Also merged the duplicate webhooks component trees. Components lived in
both dashboard/(components)/webhooks and dashboard/webhooks/(components),
and the second tree was reached by a cross-tree import from
webhooks-history. All three of its files are delivery-history specific
and had no other consumer, so they moved into webhooks-history and that
tree is gone. webhook-table also became deliveries-table, matching what
it renders.
Moves only, no logic changes: git records all 25 as renames, and no test
needed editing, which is the check that behaviour did not move with them.
Two files turned out to be dead code and are deliberately left in place
rather than deleted: black-friday-modal (seasonal, clearly parked) and
community-alert. Nothing imports either.
Verified: typecheck clean, build clean, 0 lint errors (21 warnings,
unchanged), 155 unit tests, 78 e2e.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Both bugs were hand-written query keys. lib/api/query-keys.ts exists to
prevent exactly this and neither site used it.
1. /auth/who-am-i was cached under two keys. Four call sites used
['whoAmI'] and seven used ['currentUser'], including the typed
useCurrentUser hook. So the endpoint was fetched twice on any page
using both, and invalidating one never invalidated the other:
edit-profile-form refreshed ['currentUser'] and left the email
verification banner reading stale data under ['whoAmI'], while
use-onboarding had the mirror-image bug.
verify-email/page.tsx additionally cached the whole axios response
where the others cached the unwrapped user, so unifying the key meant
normalising that shape too. One key demands one shape.
2. generate-api-key invalidated ['apiKeys', 'stats'], which no query
uses. react-query matches by prefix, so it matched neither the key
list (['apiKeys', status]) nor the dashboard stats (['stats']).
Generating an API key refreshed nothing at all. It is now three
invalidations against queryKeys, using a new apiKeysAll prefix so
every status filter refreshes rather than just 'active'.
Also added accountDeletionRequestedAt to the User type. It is a real
field on the user schema and the deletion banner already reads it; only
the type did not know about it.
Both bugs are invisible on inspection, which is how they shipped, so the
regression tests were each confirmed to fail against the pre-change code
before being trusted.
Verified: build clean, 0 lint errors (21 warnings, unchanged), 155 unit
tests (from 153), 78 e2e.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>