Skip to content
BankBird
🐦 Demo →
NL EN
← Back to overview
⚙️ Advanced update

Technical release notes

An honest look under the hood: what changed in the codebase and what that means for your BankBird installation. All changes are automatically included in the update — you don't need to do anything manually.

v2.1.1 📅 17 May 2026 Released
1 fix — default Dutch merchants are now synchronised on every migrate, even without the --seed flag
View changes

🏪 Default merchants on every install Bug fix

Seeder: MerchantSeeder::sync() Migration: 2026_05_17_120100_seed_default_merchants

On fresh installs where the install agent ran php artisan migrate without --seed, the Dutch default merchant list (Albert Heijn, Jumbo, ING, Shell, Netflix, etc.) was missing until a manual db:seed step. From v2.1.1 onwards this list is kept up to date via an idempotent migration — without touching existing merchants (firstOrCreate on normalized_name).

✅ Known issue, already fixed. Running php artisan migrate after the update will add any missing default merchants. Your own manually created merchants are left untouched.
v2.1.0 📅 17 May 2026 Released
6 changes — manual merchants + logo upload, expanded category library, transaction detail page, more compact global search, plus-button fix in import flow, idempotent category sync
View changes

🏪 Add merchants manually + logo upload New feature

Page: CreateMerchant Storage: public/merchants

Until now merchants were only created automatically during an import. From v2.1 there is a dedicated /merchants/create page where you can enter a merchant yourself and attach a logo via URL or file upload (max 2 MB, PNG/JPG/SVG). An uploaded file is stored on the public disk under storage/app/public/merchants and takes precedence over a manually entered URL.

The logo path is stored by MerchantResource::resolveLogoSource() as a relative storage/ URL. The existing logoUrl accessor on the Merchant model automatically resolves that to an absolute URL for the ImageColumn in tables and global search results.

✅ Known issue, already fixed. The update does not run php artisan storage:link automatically — if you have never done that on your install, run it once after the update so uploaded logos are accessible via /storage/....

🏷️ Expanded category library New feature

Seeder: CategorySeeder::sync() Migration: 2026_05_17_120000_seed_extra_default_categories

The default set has grown from 11 to 35 categories: 20 top-level ones (including Sport & Fitness, Holiday, Gifts & Donations, Education, Children, Taxes, Bank Charges, Insurance, Business) and 15 sub-categories under Groceries, Restaurant/Food, Transport, Housing, Subscriptions and Income. The icon dropdown went from 14 to ~45 heroicons, grouped by theme and searchable.

For existing installs there is a one-time migration that calls CategorySeeder::sync(). sync() uses firstOrCreate on (name, parent_id) — so existing categories are left untouched; only missing defaults are added. This keeps a single source of truth for both fresh installs and migrations on live installs.

✅ Known issue, already fixed. Running php artisan migrate after the update adds the new categories. Your existing categories are not affected — if you already had a category with the same name, your version with your colour and icon is kept.

👁️ Transaction detail page New feature

Page: ViewTransaction URL: /transactions/{id}

The transaction resource previously only had an index and edit page. From v2.1 there is a dedicated view page with a Filament infolist that neatly displays the date, amount, description, original bank line, account, counter-account, merchant and category. Editing is still available via the prominent "Edit" button at the top.

The table on /transactions now has a recordUrl pointing to this view page — clicking a row opens the detail page rather than jumping straight to the edit screen. Filament's getGlobalSearchResultUrl() automatically picks the view page when it exists, so clicking a result in the global search bar also lands on the detail page.

✅ Known issue, already fixed. Included automatically in the update. A hard refresh (Ctrl+F5) helps if you see a stale browser cache; no other action required.

🔍 More compact global search UX improvement

Concern: WithCompactGlobalSearch Limit: 5 per resource

The global search bar used to dump all matches into one long list. This became unreadable whenever a search term matched hundreds of transactions. From v2.1 the dropdown shows at most 5 results per resource, with an extra "View all X results" entry at the bottom that links to the list page with the query pre-filled via ?search=....

The implementation lives in a reusable trait App\Filament\Concerns\WithCompactGlobalSearch that overrides the default getGlobalSearchResults(). Resources implement globalSearchSeeMoreUrl(string $search): string to supply their own index route. Applied to TransactionResource and MerchantResource.

✅ Known issue, already fixed. No action needed — the new search flow works immediately after the update.

➕ Plus button in import flow Bug fix

Page: CreateImport Action: createAccountAction()

The analysed-files table on /imports/create called mountAction('createAccount') without the Livewire page having a matching createAccountAction() method. Result: clicking the plus button did nothing — no modal, no feedback. Fixed by properly defining the action; the newly created account is immediately selected for the corresponding analysed file.

✅ Known issue, already fixed. Included automatically in the update.

🌱 Idempotent category sync via migration Bug fix

Pattern: firstOrCreate op (name, parent_id) Single source of truth

Previously, new default categories could only reach existing installs via php artisan db:seed — and seeders don't run automatically during an update. From v2.1 the seeder logic has been extracted into a static CategorySeeder::sync() method that is called from both the seeder and a migration.

Result: the CHANGELOG and the seeder no longer need to be kept in sync every time a new default category is added. Future additions work automatically for both fresh installs (via db:seed) and live installs (via a data migration that calls sync()).

✅ Known issue, already fixed. No action needed — new categories appear automatically after php artisan migrate.
v2.0.0 📅 10 May 2026 Released
7 changes — SNS & Knab PDF import, friendly 403/404 pages, top navigation, admin panel on root, Vite security upgrade, idempotent demo seeder, demo login fix
View changes

📄 SNS & Knab PDF import New feature

Service: PdfImportService Automatic bank detection

Alongside the existing ING parser, there are now full parsers for SNS and Knab bank statements. On upload the source bank is detected automatically based on keywords in the text — no configuration required.

Knab detail: the PDF text extractor loses the Debit/Credit column position. Debit vs. credit is inferred from the keyword Ontvangen (Received) in the description — works on all tested statements.

✅ Known issue, already fixed. Included by default on new installs. Existing installs receive it automatically via the update — nothing to do; SNS and Knab uploads work immediately after pulling the update.

🎨 Friendly 403 & 404 error pages UX improvement

Views: resources/views/errors/

Laravel's default "Forbidden" and "Not Found" pages have been replaced by BankBird-styled versions with clear explanations and a back button. A raw error page gives the impression the system is broken, even when the cause is ordinary access rules — that confusion is now gone.

The 403 page detects demo mode and then shows "Not available in demo" with a dashboard button instead of a generic error message.

✅ Known issue, already fixed. Nothing to do — all existing installs get the styled error pages automatically once the update is pulled.

🧭 Top navigation + page footer UX improvement

Filament v5 layout switch Full content width

The admin panel switches from a sidebar menu to top navigation with full content width. The previous sidebar footer showing version info has been replaced by a centred page footer at the bottom of every page. The version link goes to the public updates overview in a new tab.

✅ Known issue, already fixed. Included automatically in the update. A hard refresh (Ctrl+F5) may help clear a stale browser cache, but no other action is needed.

🏠 Admin panel on the root URL UX improvement

Helper: App\Support\Demo::panelPath() Helper: App\Support\Demo::panelUrl(...)

Self-hosted installs no longer serve the admin panel at /admin but at the root URL. A fresh install at bankbird.test therefore opens the dashboard directly, not bankbird.test/admin. The marketing host (bankbird.app) keeps /admin to avoid conflicts with the public landing pages, and the combined host continues to use /dev and /demo for local development.

The active prefix is determined centrally by Demo::panelPath() based on the host. All hardcoded url('/admin/...') calls in views have been replaced by a new Demo::panelUrl(string $path) helper that builds the correct path per host — the admin banner, sidebar footer and 404 page therefore work correctly across all deployment forms.

✅ Known issue, already fixed. Existing installs at bankbird.test/admin automatically move to bankbird.test/ after the update. Old bookmarks to /admin/... will no longer work — replace them with the new URL or simply navigate from the root.

🛡️ Vite 5 → 6 security upgrade Security

Package: vite ^5.0 → ^6.0 Package: laravel-vite-plugin ^1.0 → ^1.2

Two moderate npm audit advisories resolved: GHSA-4w7w-66w2-5vf9 (Vite path traversal in optimised deps .map handling) and GHSA-67mh-4wv8-2f99 (esbuild dev server accepts requests from arbitrary origins). Both advisories affect only the npm run dev development server and are not present in a production build.

We chose the minor-major bump from Vite 5 to 6 rather than running npm audit fix --force (which would have jumped to Vite 8 and also bumped laravel-vite-plugin to 3.x). Tailwind v4 via @tailwindcss/vite@4.2.4 was left untouched and is compatible with Vite 5/6/7/8. Installed versions: vite@6.4.2, laravel-vite-plugin@1.3.0. npm audit now reports 0 vulnerabilities.

Additionally, a new "Advanced updates" section has been added to /updates where dependency and security advisories are published permanently, with severity and status badges, plain-language explanations and collapsible commands for developers.

✅ Known issue, already fixed. No action needed on running installs — the production build was never vulnerable. For future custom builds, run npm install and npm run build once after the update to pull in the new versions.

🌱 Idempotent demo seeder Bug fix

Seeder: DemoSeeder Reproducibility: mt_srand

The demo seeder generated new random amounts on every php artisan db:seed run, leading to duplicate transactions and shifting balances on re-runs. Fixed by using mt_srand with a fixed seed for reproducible amounts, and firstOrCreate on import_hash to prevent duplicate inserts.

Result: the demo database is now deterministic — every seed produces exactly the same transactions with exactly the same amounts, and re-runs are no-ops rather than duplicate creators. This also makes it easier to spot regressions in demo data during code review.

✅ Known issue, already fixed. No action needed — the fix is automatically active on the next seed.

🔑 Demo login 419 fix on combined host Bug fix

Middleware: SwitchDatabaseForCombinedHost Symptom: 419 Page Expired on demo login

On the combined local host the Livewire update endpoint picked the wrong database for demo form submits, causing the CSRF token in the demo database to be validated against the session from the production database — resulting in a 419 mismatch. Fixed by adding a Referer fallback in SwitchDatabaseForCombinedHost: when the host header is ambiguous, the demo database is chosen based on the request's origin URL.

✅ Known issue, already fixed. Only relevant for local combined-host setups. Production installs on a dedicated host were not affected.
← Back to updates