From 8b790013a96eb1d5bc4fa0f294fb00d31e018757 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vi=CC=81ctor=20Falco=CC=81n?= Date: Mon, 22 Jun 2026 12:39:08 +0200 Subject: [PATCH] feat(transactions): release transaction analysis to all users Remove the TransactionAnalysis Pennant feature flag and its gating across the API, Inertia shared data, and the transactions UI so the analysis drawer and saved filters are available to everyone. --- app/Features/TransactionAnalysis.php | 19 --- .../Api/TransactionAnalysisController.php | 4 - app/Http/Middleware/HandleInertiaRequests.php | 4 - .../transaction-actions-menu.test.tsx | 16 +-- .../transactions/transaction-actions-menu.tsx | 129 ++++++++---------- .../transactions/transaction-filters.tsx | 5 +- .../js/pages/settings/connections.test.tsx | 1 - resources/js/types/index.d.ts | 1 - tests/Feature/InertiaSharedDataTest.php | 1 - tests/Feature/TransactionAnalysisTest.php | 9 -- 10 files changed, 62 insertions(+), 127 deletions(-) delete mode 100644 app/Features/TransactionAnalysis.php diff --git a/app/Features/TransactionAnalysis.php b/app/Features/TransactionAnalysis.php deleted file mode 100644 index 260c4ff7..00000000 --- a/app/Features/TransactionAnalysis.php +++ /dev/null @@ -1,19 +0,0 @@ -user(); - abort_unless(Feature::for($user)->active(TransactionAnalysis::class), 403); - $validated = $request->validated(); $currency = $user->currency_code; diff --git a/app/Http/Middleware/HandleInertiaRequests.php b/app/Http/Middleware/HandleInertiaRequests.php index 47a66ee0..6a4e6cbd 100644 --- a/app/Http/Middleware/HandleInertiaRequests.php +++ b/app/Http/Middleware/HandleInertiaRequests.php @@ -5,7 +5,6 @@ namespace App\Http\Middleware; use App\Enums\BankingConnectionStatus; use App\Enums\BankingProvider; use App\Features\CalculateBalancesOnImport; -use App\Features\TransactionAnalysis; use App\Models\BankingConnection; use App\Services\CurrencyOptions; use Illuminate\Foundation\Inspiring; @@ -178,19 +177,16 @@ class HandleInertiaRequests extends Middleware return [ 'cashflow' => true, 'calculateBalancesOnImport' => false, - 'transactionAnalysis' => false, ]; } $features = Feature::for($user)->values([ CalculateBalancesOnImport::class, - TransactionAnalysis::class, ]); return [ 'cashflow' => true, 'calculateBalancesOnImport' => $features[CalculateBalancesOnImport::class] !== false, - 'transactionAnalysis' => $features[TransactionAnalysis::class] !== false, ]; } diff --git a/resources/js/components/transactions/transaction-actions-menu.test.tsx b/resources/js/components/transactions/transaction-actions-menu.test.tsx index b4700d38..08660c7c 100644 --- a/resources/js/components/transactions/transaction-actions-menu.test.tsx +++ b/resources/js/components/transactions/transaction-actions-menu.test.tsx @@ -1,11 +1,9 @@ import { type TransactionFilters } from '@/types/transaction'; import { fireEvent, render, screen } from '@testing-library/react'; import type React from 'react'; -import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { describe, expect, it, vi } from 'vitest'; import { TransactionActionsMenu } from './transaction-actions-menu'; -const features = { transactionAnalysis: true }; - vi.mock('@/actions/App/Http/Controllers/TransactionController', () => ({ categorize: { url: () => '/transactions/categorize' }, })); @@ -22,7 +20,6 @@ vi.mock('@inertiajs/react', () => ({ Link: ({ children, href }: { children: React.ReactNode; href: string }) => ( {children} ), - usePage: () => ({ props: { features } }), })); vi.mock('./import-transactions-drawer', () => ({ @@ -61,17 +58,6 @@ function renderMenu(filters: TransactionFilters) { } describe('TransactionActionsMenu analysis button', () => { - beforeEach(() => { - features.transactionAnalysis = true; - }); - - it('is hidden when the TransactionAnalysis feature flag is off', () => { - features.transactionAnalysis = false; - renderMenu(emptyFilters); - - expect(screen.queryByText('Analysis')).not.toBeInTheDocument(); - }); - it('is disabled when no filter is applied', () => { renderMenu(emptyFilters); diff --git a/resources/js/components/transactions/transaction-actions-menu.tsx b/resources/js/components/transactions/transaction-actions-menu.tsx index ccdc2858..f704ed13 100644 --- a/resources/js/components/transactions/transaction-actions-menu.tsx +++ b/resources/js/components/transactions/transaction-actions-menu.tsx @@ -17,7 +17,6 @@ import { useIsMobile } from '@/hooks/use-mobile'; import { useReEvaluateAllTransactions } from '@/hooks/use-re-evaluate-all-transactions'; import { hasActiveFilters } from '@/lib/transaction-filter-serialization'; -import { type SharedData } from '@/types'; import { type Account, type Bank } from '@/types/account'; import { type AutomationRule } from '@/types/automation-rule'; import { type Category } from '@/types/category'; @@ -26,7 +25,7 @@ import { type TransactionFilters, } from '@/types/transaction'; import { __ } from '@/utils/i18n'; -import { Link, usePage } from '@inertiajs/react'; +import { Link } from '@inertiajs/react'; import { BarChart3, ChevronDown, @@ -61,7 +60,6 @@ export function TransactionActionsMenu({ onImportComplete, filters, }: TransactionActionsMenuProps) { - const { features } = usePage().props; const isMobile = useIsMobile(); const [importDrawerOpen, setImportDrawerOpen] = useState(false); const [analysisDrawerOpen, setAnalysisDrawerOpen] = useState(false); @@ -106,65 +104,60 @@ export function TransactionActionsMenu({ return ( <> - {features.transactionAnalysis && - (isMobile ? ( - - - - - - {!canAnalyze && ( - - {__( - 'Apply a filter to enable this button', - )} - - )} - - - ) : ( - - - - - - {!canAnalyze && ( - - {__( - 'Apply a filter to enable this button', - )} - - )} - - - ))} + {isMobile ? ( + + + + + + {!canAnalyze && ( + + {__('Apply a filter to enable this button')} + + )} + + + ) : ( + + + + + + {!canAnalyze && ( + + {__('Apply a filter to enable this button')} + + )} + + + )} @@ -273,13 +266,11 @@ export function TransactionActionsMenu({ onImportComplete={onImportComplete} /> - {features.transactionAnalysis && ( - - )} + ); } diff --git a/resources/js/components/transactions/transaction-filters.tsx b/resources/js/components/transactions/transaction-filters.tsx index 50cb3bba..236ae37f 100644 --- a/resources/js/components/transactions/transaction-filters.tsx +++ b/resources/js/components/transactions/transaction-filters.tsx @@ -1,5 +1,4 @@ import { __ } from '@/utils/i18n'; -import { usePage } from '@inertiajs/react'; import { format } from 'date-fns'; import * as Icons from 'lucide-react'; import { ChevronsUpDown, Tag, X } from 'lucide-react'; @@ -32,7 +31,6 @@ import { toggleCategorySelection, } from '@/lib/category-tree'; import { cn } from '@/lib/utils'; -import { type SharedData } from '@/types'; import { type Account } from '@/types/account'; import { type Category, getCategoryColorClasses } from '@/types/category'; import { getLabelColorClasses, type Label } from '@/types/label'; @@ -60,7 +58,6 @@ export function TransactionFilters({ hideAccountFilter = false, enableSavedFilters = false, }: TransactionFiltersProps) { - const { features } = usePage().props; const [isOpen, setIsOpen] = useState(false); const [categoryDropdownOpen, setCategoryDropdownOpen] = useState(false); const [categorySearch, setCategorySearch] = useState(''); @@ -733,7 +730,7 @@ export function TransactionFilters({ />
- {enableSavedFilters && features.transactionAnalysis && ( + {enableSavedFilters && ( ({ features: { cashflow: true, calculateBalancesOnImport: false, - transactionAnalysis: false, }, }, }), diff --git a/resources/js/types/index.d.ts b/resources/js/types/index.d.ts index 87c4fd41..aac63618 100644 --- a/resources/js/types/index.d.ts +++ b/resources/js/types/index.d.ts @@ -42,7 +42,6 @@ export interface NavDivider { export interface Features { cashflow: boolean; calculateBalancesOnImport: boolean; - transactionAnalysis: boolean; } export interface ExpiredBankingConnectionNotification { diff --git a/tests/Feature/InertiaSharedDataTest.php b/tests/Feature/InertiaSharedDataTest.php index 120aab4f..a2d2c691 100644 --- a/tests/Feature/InertiaSharedDataTest.php +++ b/tests/Feature/InertiaSharedDataTest.php @@ -43,7 +43,6 @@ test('shared feature flags do not include coinbase flag', function () { expect($props['features'])->toBe([ 'cashflow' => true, 'calculateBalancesOnImport' => false, - 'transactionAnalysis' => false, ]); }); diff --git a/tests/Feature/TransactionAnalysisTest.php b/tests/Feature/TransactionAnalysisTest.php index 2e33bfd9..dca09186 100644 --- a/tests/Feature/TransactionAnalysisTest.php +++ b/tests/Feature/TransactionAnalysisTest.php @@ -1,7 +1,6 @@ user = User::factory()->create(['currency_code' => 'USD']); $this->actingAs($this->user); - Feature::for($this->user)->activate(TransactionAnalysis::class); $this->account = Account::factory()->create([ 'user_id' => $this->user->id, @@ -34,12 +31,6 @@ function makeTransaction(array $attributes = []): Transaction ]); } -test('analysis endpoint is gated behind the TransactionAnalysis feature flag', function () { - Feature::for($this->user)->deactivate(TransactionAnalysis::class); - - $this->getJson('/api/transactions/analysis')->assertForbidden(); -}); - test('analysis response is not cached between users', function () { $this->getJson('/api/transactions/analysis') ->assertOk()