refactor(ai): remove AiConsentSettings feature flag (#619)

## What

Removes the `AiConsentSettings` Laravel Pennant feature flag. The AI
consent management UI — the toggle in **Billing settings** and the
banner in **Transactions** — is now available to all users
automatically, no longer gated behind the flag.

The underlying AI consent functionality (model, controller, routes,
`User` consent methods) is unchanged; only the gate that hid its UI was
removed.

## Changes

- Delete `app/Features/AiConsentSettings.php`.
- `HandleInertiaRequests`: drop the `aiConsentSettings` shared prop and
its resolution.
- Frontend: render `AiConsentSection` and the transactions consent
banner unconditionally; drop the now-unused `aiConsentSettings` type and
`features` destructuring.
- Tests: remove the two flag-specific assertions in
`AiConsentSettingsTest` (consent-state coverage kept), update
`InertiaSharedDataTest`, and switch `FeatureEnableCommandTest` to
`CalculateBalancesOnImport` as its example feature.

## Testing

- `php artisan test` on the affected suites — 13 passed.
- `vendor/bin/pint`, `bun run format`, `bun run lint` — clean.
This commit is contained in:
Víctor Falcón 2026-07-01 09:47:55 +02:00 committed by GitHub
parent 10442c1e32
commit 4120e12861
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 10 additions and 64 deletions

View File

@ -1,24 +0,0 @@
<?php
namespace App\Features;
use App\Models\User;
/**
* @api
*/
class AiConsentSettings
{
/**
* Resolve the feature's initial value.
*
* Off by default; enable per user with
* `php artisan feature:enable AiConsentSettings user@example.com`
* to surface AI consent management outside of onboarding (billing
* settings toggle + transactions banner).
*/
public function resolve(?User $user): bool
{
return false;
}
}

View File

@ -4,7 +4,6 @@ namespace App\Http\Middleware;
use App\Enums\BankingConnectionStatus;
use App\Enums\BankingProvider;
use App\Features\AiConsentSettings;
use App\Features\CalculateBalancesOnImport;
use App\Models\BankingConnection;
use App\Services\CurrencyOptions;
@ -179,19 +178,16 @@ class HandleInertiaRequests extends Middleware
return [
'cashflow' => true,
'calculateBalancesOnImport' => false,
'aiConsentSettings' => false,
];
}
$features = Feature::for($user)->values([
CalculateBalancesOnImport::class,
AiConsentSettings::class,
]);
return [
'cashflow' => true,
'calculateBalancesOnImport' => $features[CalculateBalancesOnImport::class] !== false,
'aiConsentSettings' => $features[AiConsentSettings::class] !== false,
];
}

View File

@ -476,7 +476,7 @@ function AiConsentSection({
}
export default function Billing() {
const { auth, pricing, locale, features, hasAiConsent, refund } = usePage<
const { auth, pricing, locale, hasAiConsent, refund } = usePage<
SharedData & { hasAiConsent: boolean; refund?: RefundInfo }
>().props;
const isDemoAccount = auth?.isDemoAccount ?? false;
@ -507,12 +507,10 @@ export default function Billing() {
/>
)}
{features.aiConsentSettings && (
<AiConsentSection
initialConsent={hasAiConsent}
hasProPlan={hasProPlan}
/>
)}
<AiConsentSection
initialConsent={hasAiConsent}
hasProPlan={hasProPlan}
/>
</div>
</SettingsLayout>
</AppLayout>

View File

@ -437,12 +437,11 @@ export default function Transactions({
lastVisitAt,
}: Props) {
const locale = useLocale();
const { auth, features } = usePage<SharedData>().props;
const { auth } = usePage<SharedData>().props;
const [aiConsentResolved, setAiConsentResolved] = useState(false);
const [aiConsentSaving, setAiConsentSaving] = useState(false);
const showAiConsentBanner =
auth.hasProPlan &&
features.aiConsentSettings &&
!hasAiConsent &&
!aiConsentPromptDismissed &&
!aiConsentResolved;

View File

@ -42,7 +42,6 @@ export interface NavDivider {
export interface Features {
cashflow: boolean;
calculateBalancesOnImport: boolean;
aiConsentSettings: boolean;
}
export interface ExpiredBankingConnectionNotification {

View File

@ -1,31 +1,10 @@
<?php
use App\Features\AiConsentSettings;
use App\Models\User;
use Inertia\Testing\AssertableInertia as Assert;
use Laravel\Pennant\Feature;
use function Pest\Laravel\actingAs;
test('ai consent settings flag is off by default in shared props', function () {
$user = User::factory()->onboarded()->create();
actingAs($user)->withoutVite()->get(route('dashboard'))
->assertInertia(fn (Assert $page) => $page
->where('features.aiConsentSettings', false)
);
});
test('ai consent settings flag is exposed when activated for the user', function () {
$user = User::factory()->onboarded()->create();
Feature::for($user)->activate(AiConsentSettings::class);
actingAs($user)->withoutVite()->get(route('dashboard'))
->assertInertia(fn (Assert $page) => $page
->where('features.aiConsentSettings', true)
);
});
test('billing page reports current ai consent state', function () {
config(['subscriptions.enabled' => true]);
$user = User::factory()->onboarded()->create();

View File

@ -1,17 +1,17 @@
<?php
use App\Features\AiConsentSettings;
use App\Features\CalculateBalancesOnImport;
use App\Models\User;
use Laravel\Pennant\Feature;
test('enables a feature for a percentage of users', function () {
$users = User::factory()->count(10)->create();
$this->artisan('feature:enable', ['feature' => 'AiConsentSettings', 'target' => '40%'])
$this->artisan('feature:enable', ['feature' => 'CalculateBalancesOnImport', 'target' => '40%'])
->expectsOutputToContain('enabled for 4 users')
->assertSuccessful();
$enabled = $users->filter(fn (User $user) => Feature::for($user)->active(AiConsentSettings::class));
$enabled = $users->filter(fn (User $user) => Feature::for($user)->active(CalculateBalancesOnImport::class));
expect($enabled)->toHaveCount(4);
});
@ -19,6 +19,6 @@ test('enables a feature for a percentage of users', function () {
test('rejects an out-of-range percentage', function () {
User::factory()->create();
$this->artisan('feature:enable', ['feature' => 'AiConsentSettings', 'target' => '0%'])
$this->artisan('feature:enable', ['feature' => 'CalculateBalancesOnImport', 'target' => '0%'])
->assertFailed();
});

View File

@ -43,7 +43,6 @@ test('shared feature flags do not include coinbase flag', function () {
expect($props['features'])->toBe([
'cashflow' => true,
'calculateBalancesOnImport' => false,
'aiConsentSettings' => false,
]);
});