Skip to content

Conversation

@talissoncosta
Copy link
Contributor

@talissoncosta talissoncosta commented Nov 21, 2025

Thanks for submitting a PR! Please check the boxes below:

  • I have added information to docs/ if required so people know about the feature!
  • I have filled in the "Changes" section below?
  • I have filled in the "How did you test this code" section below?
  • I have used a Conventional Commit title for this Pull Request

Changes

Refactored FeaturesPage.js by extracting three focused components to improve code organization and maintainability:

New Components Created

  • FeaturesTableFilters.tsx - Pure TypeScript component managing all filter UI:

    • Search filter
    • Tag filter with UNION/INTERSECTION strategy
    • Archived features toggle
    • Enabled/disabled state filter
    • Value search filter
    • Owner filter
    • Group owner filter
    • View mode selector (default/compact)
    • Sort options (name/created date)
    • Clear all filters functionality
  • FeaturesEmptyState.tsx - Empty state component with onboarding information for new users

  • FeatureMetricsSection.tsx - Conditional metrics display wrapper (feature-flagged)

  • features/index.ts - Barrel export file for the features module

Changes to Existing Files

  • FeaturesPage.js - Refactored to use extracted components, reducing file size by ~140 lines (21% reduction)
  • Improved separation of concerns (presentation vs state management)
  • Added TypeScript types for better type safety (FilterState, SortValue, TagStrategy)
  • Fixed type compatibility issues between JavaScript and TypeScript components

Technical Details

  • All extracted components are pure and stateless
  • Props-driven architecture for better testability
  • No breaking changes to existing functionality
  • Maintains backward compatibility with existing filter state management

Migration Roadmap

This PR completes Phase 1 of the FeaturesPage migration. Future phases will be handled in separate PRs.

Phase 1: Extract Components ✅ (This PR)

  • ✅ Extracted FeaturesTableFilters.tsx (163 lines) - All filter UI
  • ✅ Extracted FeaturesEmptyState.tsx (109 lines) - Empty state with onboarding
  • ✅ Extracted FeatureMetricsSection.tsx (32 lines) - Metrics display wrapper
  • ✅ Created features/index.ts - Barrel exports
  • ✅ Reduced FeaturesPage.js from ~650 to 510 lines (21% reduction)

Future Phases (Separate PRs)

Phase 2: Create RTK Query Services (2-3 days)

  • Replace FeatureListStore with RTK Query
  • Modern data fetching with automatic caching
  • Type-safe API layer

Phase 3: Convert to Functional Component (3-4 days)

  • Migrate FeaturesPage to functional component with hooks
  • Create custom hooks: useFeatureFilters, useFeatureActions
  • Target: Reduce to ~200 lines orchestrator

Phase 4: Deprecate FeatureListProvider (1-2 days)

  • Modernize or remove provider pattern
  • Update dependent components

Phase 5: Testing & Cleanup (2-3 days)

  • Add E2E tests for filters
  • Final cleanup and documentation

Progress Tracking

  • Phase 1: ✅ Complete (this PR)
  • Phase 2: 🔜 Next PR
  • Phases 3-5: 📋 Planned

How did you test this code?

Manual Testing

  1. Verified features page loads correctly with all filters functional
  2. Tested each filter type individually:
    • Search filter updates URL params and filters results
    • Tag filter with both UNION and INTERSECTION strategies
    • Archived features toggle shows/hides archived items
    • State filter (enabled/disabled) works correctly
    • Owner and group filters apply correctly
    • Sort by name and created date functions properly
    • View mode toggle switches between default and compact views
  3. Tested clear all filters button resets all filter state
  4. Verified empty state displays when no features exist
  5. Confirmed metrics section displays when feature flag is enabled
  6. Tested filter state persistence across page refreshes (URL params)

Automated Testing

  • Existing E2E tests for feature CRUD operations continue to pass
  • Note: Filter-specific E2E tests recommended as follow-up work

Browser Testing

  • Tested in Chrome/Firefox/Safari
  • Verified no console errors or warnings
  • Confirmed TypeScript compilation successful

@vercel
Copy link

vercel bot commented Nov 21, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
flagsmith-frontend-preview Ready Ready Preview Comment Nov 26, 2025 9:14pm
flagsmith-frontend-staging Ready Ready Preview Comment Nov 26, 2025 9:14pm
1 Skipped Deployment
Project Deployment Preview Comments Updated (UTC)
docs Ignored Ignored Preview Nov 26, 2025 9:14pm

talissoncosta and others added 18 commits November 24, 2025 12:14
Create reusable hook that wraps useUpdateProjectMutation with automatic toast notifications, supporting customizable messages and error callbacks.
Replace duplicated try/catch/toast patterns across 5 components with useUpdateProjectWithToast hook, eliminating ~70 lines of duplicated code.
Replace complex Omit/Partial approach with explicit Pick for better clarity and type safety. This makes it immediately clear which Project fields are updateable via the API.
Add RTK Query optimistic updates to updateProject mutation using
onQueryStarted lifecycle hook. This immediately updates the cache
when mutations are called and automatically rolls back on errors,
providing instant UI feedback without manual rollback logic.

Benefits:
- Instant UI updates before server responds
- Automatic error rollback via patchResult.undo()
- Reduces component complexity by eliminating manual error handling

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Remove useEffect and manual rollback logic from 3 components that now
benefit from mutation-level optimistic updates:

- ProjectInformation: Remove useEffect syncing and onError rollback
- AdditionalSettings: Remove useEffect syncing
- FeatureNameValidation: Remove useEffect syncing and manual optimistic update

Components now use local state only for form editing, with RTK Query
handling all cache updates and automatic rollbacks on errors.

Benefits:
- Simpler components (~22 lines removed across 3 files)
- No cursor jumps during typing when other settings update
- No manual error handling needed

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Create ChangeRequestsApprovalsSetting component following the same
pattern as other settings (PreventFlagDefaultsSetting,
CaseSensitivitySetting, FeatureNameValidation).

Changes:
- New ChangeRequestsApprovalsSetting.tsx component
- AdditionalSettings simplified from 76 to 25 lines
- All settings now follow consistent pattern (receive project prop)

Benefits:
- Better consistency across all setting components
- Improved encapsulation (component owns its state)
- Easier to test in isolation
- Cleaner AdditionalSettings parent component

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Remove 9 unnecessary useCallback wrappers from project settings
components. These callbacks had dependencies that change frequently
(project properties, local state), causing recreation on every render
and providing no memoization benefit.

Changes across 7 files:
- CaseSensitivitySetting: Remove useCallback from handleToggle
- PreventFlagDefaultsSetting: Remove useCallback from handleToggle
- FeatureNameValidation: Remove useCallback from handleToggle + handleSave
- ProjectInformation: Remove useCallback from handleSubmit
- ChangeRequestsApprovalsSetting: Remove useCallback from both functions
- SDKSettingsTab: Remove useCallback from 2 toggle handlers
- DeleteProject: Remove useCallback from handleDelete

Benefits:
- Simpler, more honest code (~36 lines removed)
- Same performance (callbacks already recreated every render)
- Removed unused useCallback imports

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Call AppActions.refreshOrganisation() after project updates to ensure
the legacy OrganisationStore stays in sync with RTK Query cache.

This fixes the navbar not updating when project name changes, which was causing the E2E test (project-test.ts) to fail.
…e dependencies

Extract the features table filters into a separate component with explicit
prop dependencies, removing direct store access. This prepares the component
for future RTK Query migration by making it a pure presentational component.

Changes:
- Create FeaturesTableFilters component accepting filter state as props
- Remove FeatureListStore and AccountStore imports from component
- Remove redundant FeatureListStore.isLoading mutations (handled by AppActions)
- Pass isLoading and orgId as explicit props from parent
- Export FeaturesTableFilters from features index
- Fix nested ternary linting issue in getFiltersFromParams

The component is now framework-agnostic and fully testable with props-based state.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Copy link
Contributor

@Zaimwa9 Zaimwa9 left a comment

Choose a reason for hiding this comment

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

Just a couple of initial comments

@@ -0,0 +1,173 @@
import React, { FC } from 'react'
Copy link
Contributor

Choose a reason for hiding this comment

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

Opening the discussion. If we go with the features folder. Why not move this component there? And have the larger ones as their own folders?

Overall, how do you foresee the ideal structure ? What will we have in web/components, web/components/pages.

Of course we don't have to do anything now in this PR but it could be interesting to start projecting and go in the future direction

Copy link
Contributor Author

@talissoncosta talissoncosta Nov 28, 2025

Choose a reason for hiding this comment

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

Great question! Here’s the structure I’m envisioning moving forward:

/pages/ – Feature-Specific Pages and Local Components

These folders contain the main page component and any components that are only used within that specific feature or page.

Examples:

  • features/
  • organisation-settings/
  • project-settings/
  • account-settings/

/components/ – Shared, Reusable Components

These are components that are used across multiple pages or features.

Examples:

  • PageHeader
  • Button
  • Modal

A component moves here only when it has real reuse.


Migration Path

  • Start by keeping components inside their feature-specific folders under /pages/.
  • When a component begins to be used across multiple places, extract it into /components/.
  • This keeps development fast and co-located early on, and only promotes components to shared when there’s clear benefit.

In my view it brings:

  • Clear separation between page-level and shared components
  • Keeps feature-related code grouped for readability and maintainability
  • Avoids premature abstraction
  • Makes the shared layer intentional and clean
  • Aligns with our incremental refactoring approach

About /components/pages/

Ideally, I’d like to move everything currently under /components/pages/ into /pages/, but I don’t want to introduce too many changes at once. For now, I’m keeping the refactor focused on extracting shared components within the existing structure.


Note on the Migration Start

Also, sorry for not discussing this before starting the migration. In my mind, we were already heading in this direction — we just weren’t placing things into their specific folders yet.


Does this structure make sense for our future direction? Happy to adjust or discuss alternatives!

)}
<FeatureMetricsSection
environmentApiKey={environment?.api_key}
forceRefetch={this.state.forceMetricsRefetch}
Copy link
Contributor

Choose a reason for hiding this comment

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

So I remember having added that because some components were relying on the store and updating the feature state or identity wouldn't invalidate the correct tags.

Cf:

      updateIdentity: builder.mutation<Res['identity'], Req['updateIdentity']>({
        invalidatesTags: (res) => [
          { id: 'LIST', type: 'Identity' },
          { id: res?.id, type: 'Identity' },
          { id: 'METRICS', type: 'Environment' },
        ],

I really don't think the whole FeaturesPage refactoring will be enough but putting it on your radar if ever there is the possibility to get rid of it

<TableGroupsFilter
className='me-4'
projectId={projectId}
orgId={orgId?.toString()}
Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry to be sooo annoying with this orgIds ahah, reminder to check if we can use number there

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is all good, I think we should move forward with this one. Does it sounds good to you ?

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm up, let me know when the one you pinned is ready

Base automatically changed from refactor/migrate-organisationsettingspage to main November 28, 2025 11:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

front-end Issue related to the React Front End Dashboard refactor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Migrate critical components: FeaturesPage.js

3 participants