Conversation
added 15 commits
April 29, 2026 19:41
…l dumping ground Per Bulletproof React conventions documented in .github/PROJECT_LAYOUT.md, services/ should be feature-scoped, not contain a generic util/ bucket. Renamed services/util/downloadFilename.ts -> services/download/filename.ts and updated the two call sites.
- Create empty features/ and shared/ directories with .gitkeep placeholders - Add ESLint no-restricted-imports rules enforcing the Bulletproof-style boundaries: pages/ -> features/ -> shared/ -> app/. Sibling features may only cross via their public barrels; shared/ cannot depend on features/. No file moves yet. Subsequent phases populate features/ and shared/. [skip-changelog]
- services/{audio,ws,download} -> shared/services/
- hooks/shared/{useTheme,useWebSocket} -> shared/hooks/
- types/{api,ws,ui,config} -> shared/types/
- Update all imports to @/shared/* paths
- Keep @/types and @/hooks/shared barrels as transitional shims
[skip-changelog]
Extract `features/auth/` and `features/setup/` per the Bulletproof React
migration plan — smallest cohesive feature first to validate the pattern.
Moves:
- app/slices/shared/authSlice.{ts,test.ts} → features/auth/
- hooks/shared/useAuthInit.{ts,test.tsx} → features/auth/
- hooks/shared/useTokenRefresh.{ts,test.tsx} → features/auth/
- types/auth.ts → features/auth/types.ts
- pages/Login.{tsx,test.tsx} → features/auth/
- pages/Setup.{tsx,test.tsx} → features/setup/
Public barrels (`features/auth/index.ts`, `features/setup/index.ts`)
re-export the data layer (slice + types). Page modules and effect-hooks
are imported by main.tsx via direct paths to avoid a circular eager-load
through @/app/store. main.tsx gets a `no-restricted-imports: off`
override so it can wire routes without going through barrels.
Documented eslint debt: shared/services/ws/{client,adminClient}{,.test}.ts
and shared/hooks/useWebSocket.ts may import `@/features/auth` (barrel
only) until the auth-action dispatch is inverted via callbacks. Comment
in eslint.config.js tracks the follow-up.
Drops stale transitional barrels (hooks/index.ts, hooks/shared/index.ts)
and the `./auth` re-export from types/index.ts.
Validation: tsc clean, eslint clean, 22 test files / 201 tests pass,
backend go vet + build clean.
[skip-changelog]
Extract `features/scanner/` and `features/shared-call/` per the
Bulletproof React migration plan.
Moves:
- components/scanner/* → features/scanner/components/*
- hooks/scanner/* → features/scanner/hooks/*
- app/slices/scanner/{scannerSlice,callsSlice,shareSlice}{,.test}.ts
→ features/scanner/
- types/call.ts → features/scanner/types.ts
- pages/Scanner.tsx → features/scanner/Scanner.tsx
- pages/SharedCall.{tsx,test.tsx}
→ features/shared-call/
Public barrel `features/scanner/index.ts` re-exports the data layer
(scannerSlice + callsSlice + shareSlice + types). Components and
feature-internal hooks are not re-exported — nothing outside the
feature consumes them. Page module is imported directly by main.tsx.
`features/shared-call/index.ts` is empty (page imported directly).
Documented eslint debt expanded to cover scanner: shared/services/audio/
player.ts and shared/types/ws.ts may import the @/features/scanner
barrel for the Call/TranscriptionSegment types (they are logically
shared but live in features/scanner/types.ts per the plan). Same goes
for shared/services/ws/client.{ts,test.ts} which dispatches scanner
actions. Comment in eslint.config.js tracks the follow-up.
Drops the `./call` re-export from types/index.ts; consumers of Call /
TranscriptionSegment now import them from the scanner feature directly.
Validation: tsc clean, eslint clean, 22 test files / 201 tests pass,
backend go vet + build clean.
[skip-changelog]
Moves the admin page and its chrome into `features/admin/` per the
Bulletproof React migration plan.
Moves:
- pages/Admin.{tsx,test.tsx} → features/admin/
- app/slices/admin/adminSlice.ts → features/admin/_shell/
- hooks/admin/useAdminWebSocket.ts → features/admin/_shell/
- hooks/admin/useAdminWsOps.ts → features/admin/_shell/
- hooks/admin/useNavigationGuard.tsx → features/admin/_shell/
- hooks/admin/useWsQuery.ts → features/admin/_shell/
The leading-underscore `_shell/` folder marks chrome that is shared
across admin sub-features but is not itself a feature. A new
`_shell/index.ts` barrel re-exports the slice and chrome hooks; admin
sub-features (Phase 5) will import from `@/features/admin/_shell` —
the eslint pattern is whitelisted to allow this single 3-segment
import path.
`features/admin/index.ts` is empty for now (Admin page imported
directly by main.tsx, mirroring the auth/setup/scanner pattern).
Activity / logs hooks (`useAdminActivity`, `useAdminLogs`) and
`activitySlice.ts` stay in their current location — they move into
`features/admin/dashboards/` (with TR-MQTT) and `features/admin/logs/`
in Phase 5. The transitional `hooks/admin/index.ts` barrel is slimmed
to re-export only those two.
Decomposing Admin.tsx into a `_shell/AdminTabs.tsx` chrome component
is intentionally deferred — the route table will be rewritten in
Phase 5 anyway as panels move into sub-feature barrels.
Validation: tsc clean, eslint clean, 22 test files / 201 tests pass,
backend go vet + build clean.
[skip-changelog]
…b-features (phase 5.1)
Moves:
- components/admin/LegacyUsageBanner.{tsx,test.tsx} → features/admin/legacy-usage/
- components/admin/RadioReferenceCard.tsx → features/admin/radio-reference/
Each new sub-feature has an `index.ts` barrel with the standard
named-root-file convention (`export { default } from "./<Panel>";`).
Admin.tsx and ToolsPanel updated to import via the new barrels.
eslint `no-restricted-imports` extended: `@/features/admin/*`
(admin sub-feature barrels at depth 3) is now an explicit exception
to the generic depth-3 deep-import block, while the second pattern
still bans imports past the sub-feature barrel.
Validation: tsc clean, eslint clean, 22 test files / 201 tests pass.
[skip-changelog]
…in sub-features (phase 5.2)
Moves four single-panel admin sub-features (no slices, no tests):
- components/admin/ToolsPanel.tsx → features/admin/tools/
- components/admin/OptionsPanel.tsx → features/admin/options/
- components/admin/DirMonitorPanel.tsx → features/admin/dir-monitor/
- components/admin/DownstreamsPanel.tsx → features/admin/downstreams/
Each new sub-feature has the standard
`export { default } from "./<Panel>";` barrel. Admin.tsx imports
updated.
Validation: tsc clean, eslint clean, 22 test files / 201 tests pass.
[skip-changelog]
…iption admin sub-features (phase 5.3)
Moves four more single-panel admin sub-features (no slices, no tests):
- components/admin/WebhooksPanel.tsx → features/admin/webhooks/
- components/admin/SharedLinksPanel.tsx → features/admin/shared-links/
- components/admin/GroupsTagsPanel.tsx → features/admin/groups-tags/
- components/admin/TranscriptionPanel.tsx → features/admin/transcription/
Each new sub-feature uses the standard
`export { default } from "./<Panel>";` barrel. Admin.tsx imports
updated.
Validation: tsc clean, eslint clean, 22 test files / 201 tests pass.
[skip-changelog]
…ase 5.4)
Moves:
- components/admin/ApiKeysPanel.{tsx,test.tsx} → features/admin/api-keys/
- components/admin/UsersPanel.{tsx,test.tsx} → features/admin/users/
New `index.ts` barrels re-export the default panel. Admin.tsx imports
each panel from its sub-feature barrel.
Validation: tsc clean, eslint clean, 22 test files / 201 tests pass,
backend go vet + build clean.
[skip-changelog]
…e 5.5)
Moves:
- components/admin/SystemsPanel.{tsx,test.tsx} → features/admin/systems/
- components/admin/LogsPanel.tsx → features/admin/logs/
- hooks/admin/useAdminLogs.ts → features/admin/logs/
New `index.ts` barrels in each sub-feature; Admin.tsx imports panels
through them. The transitional `hooks/admin/index.ts` barrel is
slimmed to re-export only `useAdminActivity` (which moves with
activity into features/admin/dashboards/ as part of the TR-MQTT
plan).
Validation: tsc clean, eslint clean, 22 test files / 201 tests pass,
backend go vet + build clean.
[skip-changelog]
Move Activity into features/admin/dashboards/activity/ behind a thin DashboardsPanel wrapper, so the upcoming TR-MQTT integration (docs/plans/tr-mqtt-plan.md) can land as an additive sub-tab without renames or folder regroups. - components/admin/ActivityPanel.tsx -> features/admin/dashboards/activity/ActivityPanel.tsx - hooks/admin/useAdminActivity.ts -> features/admin/dashboards/activity/useAdminActivity.ts - app/slices/admin/activitySlice.ts -> features/admin/dashboards/activity/activitySlice.ts - New DashboardsPanel.tsx wrapper + barrel; Admin.tsx imports DashboardsPanel - Removed transitional hooks/admin/index.ts and now-empty components/, hooks/, app/slices/ trees Validated: tsc --noEmit clean, eslint clean, 22/201 vitest pass, go vet + build OK. [skip-changelog]
…e 6) Update PROJECT_LAYOUT.md \xc2\xa7 3 to match the post-migration tree: - Replace pages/+components/+hooks/+app/slices/ description with features/ tree - Document admin/_shell/ underscore-prefix convention and the eslint exception - Document the two intentional Bulletproof deviations (single api.ts, single shared/) - Rewrite \xc2\xa7 3.1 import rules around feature barrels and dependency direction - Drop \xc2\xa7 3.2 pages-vs-components dichotomy; pages now live inside their feature - Rewrite \xc2\xa7 3.3 state to put slices next to their feature - Replace \xc2\xa7 3.4 folder-as-component with feature-folder layout + seam rules - Move hooks (\xc2\xa7 3.5) and types (\xc2\xa7 3.6) to per-feature with shared/ fallbacks - Note transitional @/types barrel [skip-changelog]
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Internal frontend refactor — no user-visible changes.
Reorganizes
frontend/src/from a kind-first layout (components/,hooks/,pages/,app/slices/,services/,types/) into a feature-first layout underfeatures/+ a singleshared/lowest layer, following Bulletproof React with two intentional deviations documented in .github/PROJECT_LAYOUT.md § 3:app/api.ts(sub-features attach viaapi.injectEndpoints) instead of per-featurecreateApi.shared/instead of splitlib/+components/+hooks/+types/.Phases
features/andshared/directoriesshared/authandsetupfeaturesscannerandshared-callfeaturesfeatures/admin/_shell/PROJECT_LAYOUT.md § 3to document the new layoutEnforcement
Eslint
no-restricted-importsenforces dependency direction:pages → features → shared → app. Sibling features are opaque (cross-feature imports only via public barrels). Admin sub-features are opaque to each other, with one documented exception (features/admin/_shell).Validation
npx tsc --noEmit— cleannpx eslint src— cleanpnpm test— 22 files / 201 tests passgo vet ./... && go build ./...— cleanCHANGELOG
[Unreleased]intentionally empty; next user-visible feature triggers the next release tag.skip-changelog