refactor(ai): remove AiConsentSettings feature flag

Retire the Pennant flag that gated the AI consent management UI so the
billing settings toggle and transactions banner are available to
everyone. The underlying AI consent functionality is unchanged.

Swaps the FeatureEnableCommand test onto CalculateBalancesOnImport as
its example feature.
This commit is contained in:
Víctor Falcón 2026-07-01 09:31:10 +02:00
parent 10442c1e32
commit cb3a428587
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,
]);
});