SEO landing page

This commit is contained in:
Víctor Falcón 2025-11-25 11:33:11 +01:00
parent 3ac7102501
commit 669646aacb
15 changed files with 210 additions and 543 deletions

View File

@ -1,211 +0,0 @@
[
{
"title": "Padel / Sport",
"priority": 10,
"rules_json": "{\"or\":[{\"in\":[\"PLAYTOMIC\",{\"var\":\"description\"}]},{\"in\":[\"M3 SPORT\",{\"var\":\"description\"}]},{\"in\":[\"Padel\",{\"var\":\"description\"}]}]}",
"action_category_id": 99,
"action_note": null,
"action_note_iv": null,
"category": {
"name": "Sport and sports goods"
}
},
{
"title": "Gym / Sport",
"priority": 10,
"rules_json": "{\"in\":[\"Wellhub\",{\"var\":\"description\"}]}",
"action_category_id": 99,
"action_note": null,
"action_note_iv": null,
"category": {
"name": "Sport and sports goods"
}
},
{
"title": "Rent income and expenses",
"priority": 10,
"rules_json": "{\"or\":[{\"in\":[\"MARIAM NAFEE\",{\"var\":\"description\"}]},{\"in\":[\"san pantaleon 3\",{\"var\":\"description\"}]},{\"in\":[\"bbva allianz seguros\",{\"var\":\"description\"}]},{\"in\":[\"Adeudo satec s.a.\",{\"var\":\"description\"}]}]}",
"action_category_id": 129,
"action_note": null,
"action_note_iv": null,
"category": {
"name": "Income from rent"
}
},
{
"title": "Fuel",
"priority": 10,
"rules_json": "{\"in\":[\"REPSOL WAYLET\",{\"var\":\"description\"}]}",
"action_category_id": 91,
"action_note": null,
"action_note_iv": null,
"category": {
"name": "Fuel"
}
},
{
"title": "Groceries",
"priority": 10,
"rules_json": "{\"or\":[{\"in\":[\"WAYLET ALBOLOTE ES\",{\"var\":\"description\"}]},{\"in\":[\"SQRUPS\",{\"var\":\"description\"}]},{\"in\":[\"MERCADONA\",{\"var\":\"description\"}]}]}",
"action_category_id": 77,
"action_note": null,
"action_note_iv": null,
"category": {
"name": "Groceries"
}
},
{
"title": "Online expenses",
"priority": 10,
"rules_json": "{\"or\":[{\"or\":[{\"in\":[\"T3 CHAT\",{\"var\":\"description\"}]},{\"in\":[\"Google CLOUD\",{\"var\":\"description\"}]},{\"in\":[\"AWS\",{\"var\":\"description\"}]},{\"in\":[\"YouTubePremium\",{\"var\":\"description\"}]},{\"in\":[\"LARAVEL FORGE\",{\"var\":\"description\"}]},{\"in\":[\"CLAUDE.AI\",{\"var\":\"description\"}]},{\"in\":[\"DIGITALOCEAN.COM\",{\"var\":\"description\"}]},{\"in\":[\"REDDIT INC ADS\",{\"var\":\"description\"}]},{\"in\":[\"Amazon Prime\",{\"var\":\"description\"}]}]},{\"and\":[{\"in\":[\"Bizum\",{\"var\":\"description\"}]},{\"==\":[{\"var\":\"amount\"},13]}]}]}",
"action_category_id": 107,
"action_note": null,
"action_note_iv": null,
"category": {
"name": "Online services"
}
},
{
"title": "Traveling",
"priority": 10,
"rules_json": "{\"in\":[\"IBERIAEXPR\",{\"var\":\"description\"}]}",
"action_category_id": 95,
"action_note": null,
"action_note_iv": null,
"category": {
"name": "Leisure activities, traveling"
}
},
{
"title": "Food delivery",
"priority": 10,
"rules_json": "{\"in\":[\"UBER *EATS\",{\"var\":\"description\"}]}",
"action_category_id": 80,
"action_note": null,
"action_note_iv": null,
"category": {
"name": "Food delivery"
}
},
{
"title": "Own account movements",
"priority": 10,
"rules_json": "{\"and\":[{\"==\":[{\"var\":\"description\"},\"Transferencia recibida de VICTOR FALCON RUIZ\"]},{\"in\":[\"VICTOR FALCON RUIZ Own account\",{\"var\":\"description\"}]}]}",
"action_category_id": 133,
"action_note": null,
"action_note_iv": null,
"category": {
"name": "Own account"
}
},
{
"title": "Indexa / Investments",
"priority": 10,
"rules_json": "{\"and\":[{\"==\":[{\"var\":\"description\"},\"Transferencia realizada\"]},{\"or\":[{\"==\":[{\"var\":\"amount\"},-575]},{\"==\":[{\"var\":\"amount\"},-500]}]}]}",
"action_category_id": 109,
"action_note": null,
"action_note_iv": null,
"category": {
"name": "Investments"
}
},
{
"title": "Savings",
"priority": 10,
"rules_json": "{\"or\":[{\"in\":[\"VICTOR FALCON RUIZ Savings\",{\"var\":\"description\"}]},{\"and\":[{\"in\":[\"Transferencia realizada\",{\"var\":\"description\"}]},{\"==\":[{\"var\":\"amount\"},-750]}]}]}",
"action_category_id": 110,
"action_note": null,
"action_note_iv": null,
"category": {
"name": "Savings"
}
},
{
"title": "ISP / Utilities and internet",
"priority": 10,
"rules_json": "{\"in\":[\"digi spain telecom\",{\"var\":\"description\"}]}",
"action_category_id": 85,
"action_note": null,
"action_note_iv": null,
"category": {
"name": "Telephone, internet, TV, computer"
}
},
{
"title": "Farmacia",
"priority": 10,
"rules_json": "{\"in\":[\"Pago en FCIA.\",{\"var\":\"description\"}]}",
"action_category_id": 105,
"action_note": null,
"action_note_iv": null,
"category": {
"name": "Health and pharmaceuticals"
}
},
{
"title": "Taxes",
"priority": 10,
"rules_json": "{\"or\":[{\"in\":[\"internetmadrid es\",{\"var\":\"description\"}]},{\"==\":[{\"var\":\"description\"},\"Ay ciudad real ciudad real es\"]},{\"in\":[\"Pago de impuestos\",{\"var\":\"description\"}]},{\"in\":[\"PAGO TRIBUTOS BARCELONA\",{\"var\":\"description\"}]}]}",
"action_category_id": 119,
"action_note": null,
"action_note_iv": null,
"category": {
"name": "Taxes and government fees"
}
},
{
"title": "Payroll",
"priority": 10,
"rules_json": "{\"in\":[\"nómina\",{\"var\":\"description\"}]}",
"action_category_id": 126,
"action_note": null,
"action_note_iv": null,
"category": {
"name": "Salary"
}
},
{
"title": "Restaurants",
"priority": 10,
"rules_json": "{\"in\":[\"GRANIER PACIFICO\",{\"var\":\"description\"}]}",
"action_category_id": 76,
"action_note": null,
"action_note_iv": null,
"category": {
"name": "Cafes, restaurants, bars"
}
},
{
"title": "Mortage",
"priority": 10,
"rules_json": "{\"in\":[\"amortizacion de prestamo/credito\",{\"var\":\"description\"}]}",
"action_category_id": 114,
"action_note": null,
"action_note_iv": null,
"category": {
"name": "Mortgage"
}
},
{
"title": "Credit Card",
"priority": 10,
"rules_json": "{\"in\":[\"mensual de tarjeta\",{\"var\":\"description\"}]}",
"action_category_id": 115,
"action_note": null,
"action_note_iv": null,
"category": {
"name": "Credit card repayment"
}
},
{
"title": "Investments",
"priority": 10,
"rules_json": "{\"in\":[\"Adeudo indexa mas rentabilidad acciones f.p.\",{\"var\":\"description\"}]}",
"action_category_id": 109,
"action_note": null,
"action_note_iv": null,
"category": {
"name": "Investments"
}
}
]

View File

@ -1,317 +0,0 @@
[
{
"name": "Food",
"icon": "Utensils",
"color": "red"
},
{
"name": "Cafes, restaurants, bars",
"icon": "Wine",
"color": "red"
},
{
"name": "Groceries",
"icon": "ShoppingBasket",
"color": "red"
},
{
"name": "Tobacco and alcohol",
"icon": "Cigarette",
"color": "red"
},
{
"name": "Other groceries",
"icon": "ShoppingBasket",
"color": "red"
},
{
"name": "Food delivery",
"icon": "Pizza",
"color": "red"
},
{
"name": "Utility services",
"icon": "Home",
"color": "orange"
},
{
"name": "Electricity",
"icon": "Bolt",
"color": "orange"
},
{
"name": "Natural gas",
"icon": "Flame",
"color": "orange"
},
{
"name": "Rent and maintanence",
"icon": "Wrench",
"color": "orange"
},
{
"name": "Telephone, internet, TV, computer",
"icon": "Wifi",
"color": "orange"
},
{
"name": "Water",
"icon": "Droplets",
"color": "orange"
},
{
"name": "Other utility expenses",
"icon": "Receipt",
"color": "orange"
},
{
"name": "Household goods",
"icon": "Home",
"color": "orange"
},
{
"name": "Transportation",
"icon": "Bus",
"color": "amber"
},
{
"name": "Parking",
"icon": "ParkingMeter",
"color": "amber"
},
{
"name": "Fuel",
"icon": "Fuel",
"color": "amber"
},
{
"name": "Transportation expenses",
"icon": "Ticket",
"color": "amber"
},
{
"name": "Vehicle purchase, maintenance",
"icon": "Car",
"color": "amber"
},
{
"name": "Clothing and shoes",
"icon": "Shirt",
"color": "pink"
},
{
"name": "Leisure activities, traveling",
"icon": "Plane",
"color": "violet"
},
{
"name": "Gifts",
"icon": "Gift",
"color": "violet"
},
{
"name": "Books, newspapers, magazines",
"icon": "BookOpen",
"color": "violet"
},
{
"name": "Accommodation, travel expenses",
"icon": "Hotel",
"color": "violet"
},
{
"name": "Sport and sports goods",
"icon": "Dumbbell",
"color": "violet"
},
{
"name": "Theatre, music, cinema",
"icon": "Clapperboard",
"color": "violet"
},
{
"name": "Hobbies and other leisure time activites",
"icon": "Puzzle",
"color": "violet"
},
{
"name": "Education, health and beauty",
"icon": "GraduationCap",
"color": "rose"
},
{
"name": "Education and courses",
"icon": "GraduationCap",
"color": "rose"
},
{
"name": "Beauty, cosmetics",
"icon": "Sparkles",
"color": "rose"
},
{
"name": "Health and pharmaceuticals",
"icon": "HeartPulse",
"color": "rose"
},
{
"name": "Online transactions",
"icon": "Globe",
"color": "fuchsia"
},
{
"name": "Online services",
"icon": "Server",
"color": "fuchsia"
},
{
"name": "Insurance",
"icon": "ShieldCheck",
"color": "yellow"
},
{
"name": "Investments",
"icon": "LineChart",
"color": "lime"
},
{
"name": "Savings",
"icon": "PiggyBank",
"color": "lime"
},
{
"name": "Other investments",
"icon": "TrendingUp",
"color": "lime"
},
{
"name": "Financial services and commission",
"icon": "Landmark",
"color": "slate"
},
{
"name": "Fines",
"icon": "Gavel",
"color": "slate"
},
{
"name": "Mortgage",
"icon": "Building2",
"color": "slate"
},
{
"name": "Credit card repayment",
"icon": "CreditCard",
"color": "slate"
},
{
"name": "Cash withdrawal",
"icon": "Banknote",
"color": "neutral"
},
{
"name": "Gambling",
"icon": "Dice5",
"color": "purple"
},
{
"name": "Lottery",
"icon": "TicketPercent",
"color": "purple"
},
{
"name": "Taxes and government fees",
"icon": "FileText",
"color": "stone"
},
{
"name": "Invoices",
"icon": "FileText",
"color": "stone"
},
{
"name": "Personal transfers",
"icon": "ArrowLeftRight",
"color": "cyan"
},
{
"name": "Other personal transfers",
"icon": "ArrowLeftRight",
"color": "cyan"
},
{
"name": "Administrative violations",
"icon": "BadgeAlert",
"color": "stone"
},
{
"name": "Other transfers",
"icon": "Split",
"color": "stone"
},
{
"name": "Other payments",
"icon": "Wallet",
"color": "stone"
},
{
"name": "Salary",
"icon": "Coins",
"color": "green"
},
{
"name": "Regular income",
"icon": "Coins",
"color": "green"
},
{
"name": "Work on demand",
"icon": "Briefcase",
"color": "green"
},
{
"name": "Income from rent",
"icon": "Building",
"color": "green"
},
{
"name": "Unemployment benefit",
"icon": "HandCoins",
"color": "green"
},
{
"name": "Tax return",
"icon": "RotateCcw",
"color": "green"
},
{
"name": "Return debit",
"icon": "Undo2",
"color": "green"
},
{
"name": "Own account",
"icon": "ArrowRightLeft",
"color": "blue"
},
{
"name": "From account of relatives",
"icon": "Users",
"color": "blue"
},
{
"name": "Returned payments",
"icon": "RotateCw",
"color": "green"
},
{
"name": "Credit cards",
"icon": "CreditCard",
"color": "green"
},
{
"name": "Other incoming payments",
"icon": "DollarSign",
"color": "green"
}
]

View File

@ -0,0 +1,25 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Response;
class RobotsController extends Controller
{
public function index(): Response
{
$baseUrl = config('app.url');
$content = "User-agent: *\n";
$content .= "Disallow: /api/\n";
$content .= "Disallow: /dashboard\n";
$content .= "Disallow: /setup-encryption\n";
$content .= "Disallow: /transactions\n";
$content .= "Disallow: /settings\n";
$content .= "\n";
$content .= "Sitemap: {$baseUrl}/sitemap.xml\n";
return response($content, 200)
->header('Content-Type', 'text/plain');
}
}

View File

@ -0,0 +1,51 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Response;
class SitemapController extends Controller
{
public function index(): Response
{
$baseUrl = config('app.url');
$urls = [
[
'loc' => $baseUrl,
'changefreq' => 'weekly',
'priority' => '1.0',
'lastmod' => now()->toW3cString(),
],
[
'loc' => "{$baseUrl}/privacy",
'changefreq' => 'monthly',
'priority' => '0.5',
'lastmod' => now()->toW3cString(),
],
[
'loc' => "{$baseUrl}/terms",
'changefreq' => 'monthly',
'priority' => '0.5',
'lastmod' => now()->toW3cString(),
],
];
$xml = '<?xml version="1.0" encoding="UTF-8"?>'."\n";
$xml .= '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">'."\n";
foreach ($urls as $url) {
$xml .= ' <url>'."\n";
$xml .= " <loc>{$url['loc']}</loc>\n";
$xml .= " <lastmod>{$url['lastmod']}</lastmod>\n";
$xml .= " <changefreq>{$url['changefreq']}</changefreq>\n";
$xml .= " <priority>{$url['priority']}</priority>\n";
$xml .= ' </url>'."\n";
}
$xml .= '</urlset>';
return response($xml, 200)
->header('Content-Type', 'application/xml');
}
}

View File

@ -41,6 +41,7 @@ class HandleInertiaRequests extends Middleware
return [
...parent::share($request),
'name' => config('app.name'),
'appUrl' => config('app.url'),
'quote' => ['message' => trim($message), 'author' => trim($author)],
'auth' => [
'user' => $request->user(),

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

View File

@ -1,2 +0,0 @@
User-agent: *
Disallow:

View File

@ -1,13 +1,26 @@
import { Head, Link } from '@inertiajs/react';
import { Head, Link, usePage } from '@inertiajs/react';
import { type SharedData } from '@/types';
export default function Privacy() {
const { appUrl } = usePage<SharedData>().props;
return (
<>
<Head title="Privacy Policy">
<Head title="Privacy Policy - Whisper Money">
<meta
name="description"
content="Our privacy policy outlines how we collect, use, and protect your personal information."
content="Privacy policy for Whisper Money. Learn how we collect, use, and protect your personal information with end-to-end encryption."
/>
<link rel="canonical" href={`${appUrl}/privacy`} />
<meta name="robots" content="index, follow" />
<meta property="og:title" content="Privacy Policy - Whisper Money" />
<meta
property="og:description"
content="Privacy policy for Whisper Money. Learn how we collect, use, and protect your personal information."
/>
<meta property="og:type" content="website" />
<meta property="og:url" content={`${appUrl}/privacy`} />
</Head>
<div className="min-h-screen bg-[#FDFDFC] text-[#1b1b18] dark:bg-[#0a0a0a] dark:text-[#EDEDEC]">
<div className="mx-auto max-w-4xl px-6 py-12 lg:px-8 lg:py-16">

View File

@ -1,13 +1,26 @@
import { Head, Link } from '@inertiajs/react';
import { Head, Link, usePage } from '@inertiajs/react';
import { type SharedData } from '@/types';
export default function Terms() {
const { appUrl } = usePage<SharedData>().props;
return (
<>
<Head title="Terms of Service">
<Head title="Terms of Service - Whisper Money">
<meta
name="description"
content="Our terms of service outline the rules and regulations for using our platform."
content="Terms of service for Whisper Money. Review the rules and regulations for using our secure personal finance platform."
/>
<link rel="canonical" href={`${appUrl}/terms`} />
<meta name="robots" content="index, follow" />
<meta property="og:title" content="Terms of Service - Whisper Money" />
<meta
property="og:description"
content="Terms of service for Whisper Money. Review the rules and regulations for using our platform."
/>
<meta property="og:type" content="website" />
<meta property="og:url" content={`${appUrl}/terms`} />
</Head>
<div className="min-h-screen bg-[#FDFDFC] text-[#1b1b18] dark:bg-[#0a0a0a] dark:text-[#EDEDEC]">
<div className="mx-auto max-w-4xl px-6 py-12 lg:px-8 lg:py-16">

View File

@ -28,7 +28,7 @@ export default function Welcome({
canRegister?: boolean;
hideAuthButtons?: boolean;
}) {
const { auth } = usePage<SharedData>().props;
const { auth, appUrl } = usePage<SharedData>().props;
const emailInputRef = useRef<HTMLInputElement>(null);
const visitTrackedRef = useRef(false);
@ -44,25 +44,34 @@ export default function Welcome({
return (
<>
<Head title="Welcome">
<Head title="Whisper Money - The Most Secure Personal Finance App">
<meta
name="description"
content="The most secure personal finance app with end-to-end encryption. Track expenses, create budgets, and manage your money privately."
/>
<link rel="canonical" href={window.location.origin} />
<meta
name="keywords"
content="finance app, budgeting, expense tracking, end-to-end encryption, secure finance, personal finance, money management, privacy, encrypted finance app"
/>
<link rel="canonical" href={appUrl} />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />
<link href="https://fonts.googleapis.com/css2?family=Google+Sans+Code:ital,wght@0,300..800;1,300..800&family=Stack+Sans+Text:wght@200..700&display=swap" rel="stylesheet" />
<meta property="og:site_name" content="Whisper Money" />
<meta property="og:title" content="Whisper Money - The Most Secure Personal Finance App" />
<meta
property="og:description"
content="Your financial data stays private with end-to-end encryption. The most secure way to manage your personal finances."
/>
<meta property="og:type" content="website" />
<meta property="og:url" content={window.location.origin} />
<meta property="og:image" content={`${window.location.origin}/og-image.png`} />
<meta property="og:url" content={appUrl} />
<meta property="og:image" content={`${appUrl}/images/og_whisper_money.png`} />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:image:alt" content="Whisper Money - Secure Personal Finance App" />
<meta property="og:locale" content="en_US" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="Whisper Money - The Most Secure Personal Finance App" />
@ -70,7 +79,32 @@ export default function Welcome({
name="twitter:description"
content="Your financial data stays private with end-to-end encryption. The most secure way to manage your personal finances."
/>
<meta name="twitter:image" content={`${window.location.origin}/og-image.png`} />
<meta name="twitter:image" content={`${appUrl}/images/og_whisper_money.png`} />
<meta name="twitter:image:alt" content="Whisper Money - Secure Personal Finance App" />
<script type="application/ld+json">
{JSON.stringify({
'@context': 'https://schema.org',
'@type': 'WebApplication',
name: 'Whisper Money',
description: 'The most secure personal finance app with end-to-end encryption. Track expenses, create budgets, and manage your money privately.',
url: appUrl,
applicationCategory: 'FinanceApplication',
offers: {
'@type': 'Offer',
price: '0',
priceCurrency: 'USD',
},
featureList: [
'End-to-end encryption',
'Smart budgeting',
'Expense tracking',
'Visual insights',
'Zero tracking',
'Open source',
],
})}
</script>
</Head>
<div className="flex min-h-screen flex-col bg-[#FDFDFC] text-[#1b1b18] dark:bg-[#0a0a0a] dark:text-[#EDEDEC]">
<header className="w-full fade-bottom bg-background/5 fixed top-0 z-50 backdrop-blur-lg">

View File

@ -25,6 +25,7 @@ export interface NavItem {
export interface SharedData {
name: string;
appUrl: string;
quote: { message: string; author: string };
auth: Auth;
sidebarOpen: boolean;

View File

@ -3,6 +3,8 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#1b1b18" media="(prefers-color-scheme: light)">
<meta name="theme-color" content="#0a0a0a" media="(prefers-color-scheme: dark)">
{{-- Inline script to detect system dark mode preference and apply it immediately --}}
<script>

View File

@ -2,6 +2,8 @@
use App\Http\Controllers\AccountBalanceController;
use App\Http\Controllers\EncryptionController;
use App\Http\Controllers\RobotsController;
use App\Http\Controllers\SitemapController;
use App\Http\Controllers\Sync\AccountBalanceSyncController;
use App\Http\Controllers\Sync\AccountSyncController;
use App\Http\Controllers\Sync\BankSyncController;
@ -19,6 +21,9 @@ Route::get('/', function () {
]);
})->name('home');
Route::get('sitemap.xml', [SitemapController::class, 'index'])->name('sitemap');
Route::get('robots.txt', [RobotsController::class, 'index'])->name('robots');
Route::post('user-leads', [UserLeadController::class, 'store'])->name('user-leads.store');
Route::get('privacy', function () {

View File

@ -23,3 +23,11 @@ test('authenticated users receive auth user in shared props', function () {
->where('auth.user.email', $user->email)
);
});
test('all pages receive app url in shared props', function () {
$response = $this->get(route('home'));
$response->assertInertia(fn (Assert $page) => $page
->where('appUrl', config('app.url'))
);
});

View File

@ -0,0 +1,44 @@
<?php
test('sitemap returns valid xml', function () {
$response = $this->get('/sitemap.xml');
$response->assertSuccessful();
$response->assertHeader('Content-Type', 'application/xml');
expect($response->content())->toContain('<?xml version="1.0" encoding="UTF-8"?>');
expect($response->content())->toContain('<urlset');
expect($response->content())->toContain(config('app.url'));
});
test('sitemap includes homepage', function () {
$response = $this->get('/sitemap.xml');
$response->assertSuccessful();
expect($response->content())->toContain('<loc>'.config('app.url').'</loc>');
});
test('sitemap includes privacy and terms pages', function () {
$response = $this->get('/sitemap.xml');
$response->assertSuccessful();
expect($response->content())->toContain('<loc>'.config('app.url').'/privacy</loc>');
expect($response->content())->toContain('<loc>'.config('app.url').'/terms</loc>');
});
test('robots txt returns valid content', function () {
$response = $this->get('/robots.txt');
$response->assertSuccessful();
expect($response->headers->get('Content-Type'))->toContain('text/plain');
expect($response->content())->toContain('User-agent: *');
expect($response->content())->toContain('Sitemap: '.config('app.url').'/sitemap.xml');
});
test('robots txt disallows protected routes', function () {
$response = $this->get('/robots.txt');
$response->assertSuccessful();
expect($response->content())->toContain('Disallow: /api/');
expect($response->content())->toContain('Disallow: /dashboard');
expect($response->content())->toContain('Disallow: /setup-encryption');
});