* Do not trigger unsaved changes alert for action modals that cannot contain unsaved changes
The panel-level unsaved changes alert hooked `beforeunload` whenever any
action was mounted, so read-only modals such as `ViewAction`'s warned
about unsaved changes that cannot exist. Mounted actions now expose a
`hasUnsavedChangesAlert` flag that the client-side guard checks, which
defaults to `false` for actions with a disabled schema and can be
controlled per action with `unsavedChangesAlert()`.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Fix unsaved action changes alert edge cases
Co-authored-by: Amp <amp@ampcode.com>
* Prevent stale unsaved action alert state
Co-authored-by: Amp <amp@ampcode.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Dan Harrin <git@danharrin.com>
Co-authored-by: Amp <amp@ampcode.com>
* feat: add the published assets to `.gitignore` on install
`filament:install` now appends `/public/css/filament`, `/public/js/filament`
and `/public/fonts/filament` to the app's `.gitignore`, since those files are
copied out of the installed packages by `filament:assets` and do not need to be
committed.
Rules that are already ignored are left alone, the configured
`filament.assets_path` is respected, and nothing happens if the app has no
`.gitignore` file.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015vxpe34oAqmDtzZxC8HSV2
* fix: preserve `.gitignore` formatting during installation
Co-authored-by: Amp <amp@ampcode.com>
* docs: clarify custom theme deployment
Co-authored-by: Amp <amp@ampcode.com>
* fix: place published asset ignore rules with public paths
Co-authored-by: Amp <amp@ampcode.com>
* fix: preserve leading whitespace in ignore rules
Co-authored-by: Amp <amp@ampcode.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Dan Harrin <git@danharrin.com>
Co-authored-by: Amp <amp@ampcode.com>
* Support trait-named lifecycle hooks on pages and importers
Lifecycle hooks such as afterCreate() and afterSave() can currently only
be defined directly on the page class. A trait that defines one of these
hooks is silently overridden as soon as the page declares its own,
which makes it impossible for reusable traits or plugins to reliably
hook into the page lifecycle.
callHook() now also invokes {hook}{TraitName}() for every trait used by
the class (recursively), mirroring how Eloquent boots model traits via
boot{TraitName}(). The class's own hook runs first, followed by each
trait hook. Applies to panel pages (BasePage) and importers (Importer).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Test and document trait lifecycle hooks
Co-authored-by: Amp <amp@ampcode.com>
---------
Co-authored-by: jim <jim@townsend-music.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Dan Harrin <git@danharrin.com>
Co-authored-by: Amp <amp@ampcode.com>
* feat: add Closure support to darkMode() and add darkModeToggle()
* feat: add hasDarkModeToggle() to FilamentManager
* feat: add hasDarkModeToggle() to Facade PHPDoc
* feat: respect darkModeToggle() in user menu
* consistency
* theme switcher
---------
Co-authored-by: Dan Harrin <git@danharrin.com>
* feat(panels): support grouped user menu items with separate dropdown lists
* (fix) adjust for PHPStan
* cleanup
* chore: format and build
* cleanup
* screenshot
* Update 03-user-menu.md
* tenant menu grouping
* refactor for perf
---------
Co-authored-by: Dan Harrin <git@danharrin.com>
Co-authored-by: danharrin <41773797+danharrin@users.noreply.github.com>
* security: 2FA concurrent recovery code reuse
* Update AppAuthentication.php
* Merge commit from fork
* security: also serialise recovery code verification on SQLite
The `lockForUpdate()` row lock does not exist on SQLite — Laravel's SQLite
query grammar compiles `compileLockForUpdate()` to an empty string — so the
transaction provides no mutual exclusion there. Two concurrent requests still
both read the full recovery code set before either commits, and the losing
write resurrects the just-consumed code.
Verified against a true `pcntl_fork()` race on v5.6.4: the transaction-only
fix is bypassed 6/6 runs on SQLite while holding 7/7 on MySQL.
Wrap the transaction in a per-user `Cache::lock()` so verification is
serialised regardless of database driver. The DB transaction and row lock are
kept for drivers that support them. Re-verified: combined fix holds 6/6 on
SQLite and 5/5 on MySQL.
* cleanup
---------
Co-authored-by: StarPlatinu <thanhnolove21@gmail.com>
Co-authored-by: Dan Harrin <git@danharrin.com>
---------
Co-authored-by: ThanhVu <72287279+StarPlatinu@users.noreply.github.com>
Co-authored-by: StarPlatinu <thanhnolove21@gmail.com>
* feat: add deferred badge loading for relation manager tabs
Extends the deferred tab badge loading introduced in PR #19336 to
relation manager tabs, fixing two upstream issues:
1. `RelationManager::getTabComponent()` was evaluating `getBadge()`
eagerly before the deferred mechanism could intercept it.
2. `HasRelationManagers::getRelationManagersContentComponent()` built
`Tabs::make()` with no key, causing `callSchemaComponentMethod()` to
receive null when the async badge fetch fired.
Changes:
- Add `protected static bool $deferBadge = false` to `RelationManager`
with an overrideable `isBadgeDeferred()` method for per-record control
- Add fluent `->deferBadge()` and `isBadgeDeferred()` to `RelationGroup`
- Add `->key('relationManagerTabs')` to the `Tabs` built in
`HasRelationManagers`
- Add docs and tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* cleanup
---------
Co-authored-by: buzkall <buzkall@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Dan Harrin <git@danharrin.com>
* screenshots
* fix cut off sidebar
* Screenshots
* Screenshots
* retake
* fix loading data issue
* retake
* screenshots
* Update custom-toolbar.jpg
* retake
* ss
* retake
* ss
* ss
* ss
* ss
* ss
* ss
* ss
* ss
* Update dashboard.jpg
* ss
* Update openable.jpg
* ss
* ss
* ss
* Update database.sqlite
* Update database.sqlite
* ss
* ss
* ss
* ss
* ss
* ss
* clean up
* cs