From ba20459c7117443f286c2807801919ef2e023476 Mon Sep 17 00:00:00 2001 From: Bob Molitor Date: Fri, 31 Oct 2025 12:57:06 +0100 Subject: [PATCH 1/3] update .gitignore, composer update, MCP --- .cursor/rules/laravel-boost.mdc | 32 +++- .gitignore | 4 +- .mcp.json | 8 +- CLAUDE.md | 32 +++- composer.json | 3 +- composer.lock | 316 ++++++++++++++++---------------- 6 files changed, 230 insertions(+), 165 deletions(-) diff --git a/.cursor/rules/laravel-boost.mdc b/.cursor/rules/laravel-boost.mdc index f4132dd..1b3ac54 100644 --- a/.cursor/rules/laravel-boost.mdc +++ b/.cursor/rules/laravel-boost.mdc @@ -11,7 +11,7 @@ The Laravel Boost guidelines are specifically curated by Laravel maintainers for ## Foundational Context This application is a Laravel application and its main Laravel ecosystems package & versions are below. You are an expert with them all. Ensure you abide by these specific packages & versions. -- php - 8.4.13 +- php - 8.4.14 - laravel/fortify (FORTIFY) - v1 - laravel/framework (LARAVEL) - v12 - laravel/prompts (PROMPTS) - v0 @@ -25,7 +25,6 @@ This application is a Laravel application and its main Laravel ecosystems packag - phpunit/phpunit (PHPUNIT) - v12 - tailwindcss (TAILWINDCSS) - v4 - ## Conventions - You must follow all existing code conventions used in this application. When creating or editing a file, check sibling files for the correct structure, approach, naming. - Use descriptive names for variables and methods. For example, `isRegisteredForDiscounts`, not `discount()`. @@ -575,4 +574,33 @@ $pages->assertNoJavascriptErrors()->assertNoConsoleLogs(); - Every change must be programmatically tested. Write a new test or update an existing test, then run the affected tests to make sure they pass. - Run the minimum number of tests needed to ensure code quality and speed. Use `php artisan test` with a specific filename or filter. + + +=== laravel/fortify rules === + +## Laravel Fortify + +Fortify is a headless authentication backend that provides authentication routes and controllers for Laravel applications. + +**Before implementing any authentication features, use the `search-docs` tool to get the latest docs for that specific feature.** + +### Configuration & Setup +- Check `config/fortify.php` to see what's enabled. Use `search-docs` for detailed information on specific features. +- Enable features by adding them to the `'features' => []` array: `Features::registration()`, `Features::resetPasswords()`, etc. +- To see the all Fortify registered routes, use the `list-routes` tool with the `only_vendor: true` and `action: "Fortify"` parameters. +- Fortify includes view routes by default (login, register). Set `'views' => false` in the configuration file to disable them if you're handling views yourself. + +### Customization +- Views can be customized in `FortifyServiceProvider`'s `boot()` method using `Fortify::loginView()`, `Fortify::registerView()`, etc. +- Customize authentication logic with `Fortify::authenticateUsing()` for custom user retrieval / validation. +- Actions in `app/Actions/Fortify/` handle business logic (user creation, password reset, etc.). They're fully customizable, so you can modify them to change feature behavior. + +## Available Features +- `Features::registration()` for user registration. +- `Features::emailVerification()` to verify new user emails. +- `Features::twoFactorAuthentication()` for 2FA with QR codes and recovery codes. + - Add options: `['confirmPassword' => true, 'confirm' => true]` to require password confirmation and OTP confirmation before enabling 2FA. +- `Features::updateProfileInformation()` to let users update their profile. +- `Features::updatePasswords()` to let users change their passwords. +- `Features::resetPasswords()` for password reset via email. diff --git a/.gitignore b/.gitignore index 864f73c..e2b1505 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,6 @@ yarn-error.log .DS_Store env *.mcp* -*.mdc \ No newline at end of file +*.mdc +.mcp.json +AGENTS*.* \ No newline at end of file diff --git a/.mcp.json b/.mcp.json index 06d8f4f..d03c71c 100644 --- a/.mcp.json +++ b/.mcp.json @@ -13,7 +13,7 @@ "/Applications/Herd.app/Contents/Resources/herd-mcp.phar" ], "env": { - "SITE_PATH": "/Users/sebastianfrohlich/Herd/frontend" + "SITE_PATH": "/Users/bobmolitor/Herd/trusted_ai" } }, "filesystem": { @@ -30,8 +30,10 @@ }, "bear": { "command": "node", - "args": ["/Users/sebastianfrohlich/Projekte/bear-notes-mcp/dist/index.js"], - "env": {} + "args": [ + "/Users/sebastianfrohlich/Projekte/bear-notes-mcp/dist/index.js" + ], + "env": [] }, "postgresql": { "command": "npx", diff --git a/CLAUDE.md b/CLAUDE.md index ddb51e4..3158776 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -8,7 +8,7 @@ The Laravel Boost guidelines are specifically curated by Laravel maintainers for ## Foundational Context This application is a Laravel application and its main Laravel ecosystems package & versions are below. You are an expert with them all. Ensure you abide by these specific packages & versions. -- php - 8.4.13 +- php - 8.4.14 - laravel/fortify (FORTIFY) - v1 - laravel/framework (LARAVEL) - v12 - laravel/prompts (PROMPTS) - v0 @@ -22,7 +22,6 @@ This application is a Laravel application and its main Laravel ecosystems packag - phpunit/phpunit (PHPUNIT) - v12 - tailwindcss (TAILWINDCSS) - v4 - ## Conventions - You must follow all existing code conventions used in this application. When creating or editing a file, check sibling files for the correct structure, approach, naming. - Use descriptive names for variables and methods. For example, `isRegisteredForDiscounts`, not `discount()`. @@ -572,4 +571,33 @@ $pages->assertNoJavascriptErrors()->assertNoConsoleLogs(); - Every change must be programmatically tested. Write a new test or update an existing test, then run the affected tests to make sure they pass. - Run the minimum number of tests needed to ensure code quality and speed. Use `php artisan test` with a specific filename or filter. + + +=== laravel/fortify rules === + +## Laravel Fortify + +Fortify is a headless authentication backend that provides authentication routes and controllers for Laravel applications. + +**Before implementing any authentication features, use the `search-docs` tool to get the latest docs for that specific feature.** + +### Configuration & Setup +- Check `config/fortify.php` to see what's enabled. Use `search-docs` for detailed information on specific features. +- Enable features by adding them to the `'features' => []` array: `Features::registration()`, `Features::resetPasswords()`, etc. +- To see the all Fortify registered routes, use the `list-routes` tool with the `only_vendor: true` and `action: "Fortify"` parameters. +- Fortify includes view routes by default (login, register). Set `'views' => false` in the configuration file to disable them if you're handling views yourself. + +### Customization +- Views can be customized in `FortifyServiceProvider`'s `boot()` method using `Fortify::loginView()`, `Fortify::registerView()`, etc. +- Customize authentication logic with `Fortify::authenticateUsing()` for custom user retrieval / validation. +- Actions in `app/Actions/Fortify/` handle business logic (user creation, password reset, etc.). They're fully customizable, so you can modify them to change feature behavior. + +## Available Features +- `Features::registration()` for user registration. +- `Features::emailVerification()` to verify new user emails. +- `Features::twoFactorAuthentication()` for 2FA with QR codes and recovery codes. + - Add options: `['confirmPassword' => true, 'confirm' => true]` to require password confirmation and OTP confirmation before enabling 2FA. +- `Features::updateProfileInformation()` to let users update their profile. +- `Features::updatePasswords()` to let users change their passwords. +- `Features::resetPasswords()` for password reset via email. diff --git a/composer.json b/composer.json index 1ad9a9d..338d9bc 100644 --- a/composer.json +++ b/composer.json @@ -61,7 +61,8 @@ "@php artisan package:discover --ansi" ], "post-update-cmd": [ - "@php artisan vendor:publish --tag=laravel-assets --ansi --force" + "@php artisan vendor:publish --tag=laravel-assets --ansi --force", + "@php artisan boost:update --ansi" ], "post-root-package-install": [ "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" diff --git a/composer.lock b/composer.lock index 4cf9f8c..cf19ceb 100644 --- a/composer.lock +++ b/composer.lock @@ -1159,16 +1159,16 @@ }, { "name": "laravel/fortify", - "version": "v1.31.1", + "version": "v1.31.2", "source": { "type": "git", "url": "https://github.com/laravel/fortify.git", - "reference": "e39a49592e1440508337a765cdc913ff5bcba66f" + "reference": "a046d52ee087ee52c9852b840cf4bbad19f10934" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/fortify/zipball/e39a49592e1440508337a765cdc913ff5bcba66f", - "reference": "e39a49592e1440508337a765cdc913ff5bcba66f", + "url": "https://api.github.com/repos/laravel/fortify/zipball/a046d52ee087ee52c9852b840cf4bbad19f10934", + "reference": "a046d52ee087ee52c9852b840cf4bbad19f10934", "shasum": "" }, "require": { @@ -1220,20 +1220,20 @@ "issues": "https://github.com/laravel/fortify/issues", "source": "https://github.com/laravel/fortify" }, - "time": "2025-10-03T09:10:57+00:00" + "time": "2025-10-21T14:47:38+00:00" }, { "name": "laravel/framework", - "version": "v12.34.0", + "version": "v12.36.1", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "f9ec5a5d88bc8c468f17b59f88e05c8ac3c8d687" + "reference": "cad110d7685fbab990a6bb8184d0cfd847d7c4d8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/f9ec5a5d88bc8c468f17b59f88e05c8ac3c8d687", - "reference": "f9ec5a5d88bc8c468f17b59f88e05c8ac3c8d687", + "url": "https://api.github.com/repos/laravel/framework/zipball/cad110d7685fbab990a6bb8184d0cfd847d7c4d8", + "reference": "cad110d7685fbab990a6bb8184d0cfd847d7c4d8", "shasum": "" }, "require": { @@ -1439,7 +1439,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-10-14T13:58:31+00:00" + "time": "2025-10-29T14:20:57+00:00" }, { "name": "laravel/prompts", @@ -1818,16 +1818,16 @@ }, { "name": "league/flysystem", - "version": "3.30.0", + "version": "3.30.1", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "2203e3151755d874bb2943649dae1eb8533ac93e" + "reference": "c139fd65c1f796b926f4aec0df37f6caa959a8da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/2203e3151755d874bb2943649dae1eb8533ac93e", - "reference": "2203e3151755d874bb2943649dae1eb8533ac93e", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/c139fd65c1f796b926f4aec0df37f6caa959a8da", + "reference": "c139fd65c1f796b926f4aec0df37f6caa959a8da", "shasum": "" }, "require": { @@ -1895,9 +1895,9 @@ ], "support": { "issues": "https://github.com/thephpleague/flysystem/issues", - "source": "https://github.com/thephpleague/flysystem/tree/3.30.0" + "source": "https://github.com/thephpleague/flysystem/tree/3.30.1" }, - "time": "2025-06-25T13:29:59+00:00" + "time": "2025-10-20T15:35:26+00:00" }, { "name": "league/flysystem-local", @@ -2180,16 +2180,16 @@ }, { "name": "livewire/flux", - "version": "v2.6.0", + "version": "v2.6.1", "source": { "type": "git", "url": "https://github.com/livewire/flux.git", - "reference": "3cb2ea40978449da74b3814eeef75f0388124224" + "reference": "227b88db0a02db91666af2303ea6727a3af78c51" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/livewire/flux/zipball/3cb2ea40978449da74b3814eeef75f0388124224", - "reference": "3cb2ea40978449da74b3814eeef75f0388124224", + "url": "https://api.github.com/repos/livewire/flux/zipball/227b88db0a02db91666af2303ea6727a3af78c51", + "reference": "227b88db0a02db91666af2303ea6727a3af78c51", "shasum": "" }, "require": { @@ -2197,7 +2197,7 @@ "illuminate/support": "^10.0|^11.0|^12.0", "illuminate/view": "^10.0|^11.0|^12.0", "laravel/prompts": "^0.1|^0.2|^0.3", - "livewire/livewire": "^3.5.19", + "livewire/livewire": "^3.5.19|^4.0", "php": "^8.1", "symfony/console": "^6.0|^7.0" }, @@ -2240,9 +2240,9 @@ ], "support": { "issues": "https://github.com/livewire/flux/issues", - "source": "https://github.com/livewire/flux/tree/v2.6.0" + "source": "https://github.com/livewire/flux/tree/v2.6.1" }, - "time": "2025-10-13T23:17:18+00:00" + "time": "2025-10-28T21:12:05+00:00" }, { "name": "livewire/livewire", @@ -2322,21 +2322,21 @@ }, { "name": "livewire/volt", - "version": "v1.7.2", + "version": "v1.8.0", "source": { "type": "git", "url": "https://github.com/livewire/volt.git", - "reference": "91ba934e72bbd162442840862959ade24dbe728a" + "reference": "2d9783a340d612d32f4ffd38070780ca7d7e9205" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/livewire/volt/zipball/91ba934e72bbd162442840862959ade24dbe728a", - "reference": "91ba934e72bbd162442840862959ade24dbe728a", + "url": "https://api.github.com/repos/livewire/volt/zipball/2d9783a340d612d32f4ffd38070780ca7d7e9205", + "reference": "2d9783a340d612d32f4ffd38070780ca7d7e9205", "shasum": "" }, "require": { "laravel/framework": "^10.38.2|^11.0|^12.0", - "livewire/livewire": "^3.6.1", + "livewire/livewire": "^3.6.1|^4.0", "php": "^8.1" }, "require-dev": { @@ -2390,7 +2390,7 @@ "issues": "https://github.com/livewire/volt/issues", "source": "https://github.com/livewire/volt" }, - "time": "2025-08-06T15:40:50+00:00" + "time": "2025-10-29T15:52:35+00:00" }, { "name": "monolog/monolog", @@ -2602,25 +2602,25 @@ }, { "name": "nette/schema", - "version": "v1.3.2", + "version": "v1.3.3", "source": { "type": "git", "url": "https://github.com/nette/schema.git", - "reference": "da801d52f0354f70a638673c4a0f04e16529431d" + "reference": "2befc2f42d7c715fd9d95efc31b1081e5d765004" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/schema/zipball/da801d52f0354f70a638673c4a0f04e16529431d", - "reference": "da801d52f0354f70a638673c4a0f04e16529431d", + "url": "https://api.github.com/repos/nette/schema/zipball/2befc2f42d7c715fd9d95efc31b1081e5d765004", + "reference": "2befc2f42d7c715fd9d95efc31b1081e5d765004", "shasum": "" }, "require": { "nette/utils": "^4.0", - "php": "8.1 - 8.4" + "php": "8.1 - 8.5" }, "require-dev": { "nette/tester": "^2.5.2", - "phpstan/phpstan-nette": "^1.0", + "phpstan/phpstan-nette": "^2.0@stable", "tracy/tracy": "^2.8" }, "type": "library", @@ -2630,6 +2630,9 @@ } }, "autoload": { + "psr-4": { + "Nette\\": "src" + }, "classmap": [ "src/" ] @@ -2658,9 +2661,9 @@ ], "support": { "issues": "https://github.com/nette/schema/issues", - "source": "https://github.com/nette/schema/tree/v1.3.2" + "source": "https://github.com/nette/schema/tree/v1.3.3" }, - "time": "2024-10-06T23:10:23+00:00" + "time": "2025-10-30T22:57:59+00:00" }, { "name": "nette/utils", @@ -2753,16 +2756,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.6.1", + "version": "v5.6.2", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2" + "reference": "3a454ca033b9e06b63282ce19562e892747449bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", - "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3a454ca033b9e06b63282ce19562e892747449bb", + "reference": "3a454ca033b9e06b63282ce19562e892747449bb", "shasum": "" }, "require": { @@ -2805,9 +2808,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.1" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.2" }, - "time": "2025-08-13T20:13:15+00:00" + "time": "2025-10-21T19:32:17+00:00" }, { "name": "nunomaduro/termwind", @@ -3506,16 +3509,16 @@ }, { "name": "psy/psysh", - "version": "v0.12.12", + "version": "v0.12.14", "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "cd23863404a40ccfaf733e3af4db2b459837f7e7" + "reference": "95c29b3756a23855a30566b745d218bee690bef2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/cd23863404a40ccfaf733e3af4db2b459837f7e7", - "reference": "cd23863404a40ccfaf733e3af4db2b459837f7e7", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/95c29b3756a23855a30566b745d218bee690bef2", + "reference": "95c29b3756a23855a30566b745d218bee690bef2", "shasum": "" }, "require": { @@ -3530,11 +3533,12 @@ "symfony/console": "4.4.37 || 5.3.14 || 5.3.15 || 5.4.3 || 5.4.4 || 6.0.3 || 6.0.4" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.2" + "bamarni/composer-bin-plugin": "^1.2", + "composer/class-map-generator": "^1.6" }, "suggest": { + "composer/class-map-generator": "Improved tab completion performance with better class discovery.", "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)", - "ext-pdo-sqlite": "The doc command requires SQLite to work.", "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well." }, "bin": [ @@ -3578,9 +3582,9 @@ ], "support": { "issues": "https://github.com/bobthecow/psysh/issues", - "source": "https://github.com/bobthecow/psysh/tree/v0.12.12" + "source": "https://github.com/bobthecow/psysh/tree/v0.12.14" }, - "time": "2025-09-20T13:46:31+00:00" + "time": "2025-10-27T17:15:31+00:00" }, { "name": "ralouphie/getallheaders", @@ -3856,16 +3860,16 @@ }, { "name": "symfony/console", - "version": "v7.3.4", + "version": "v7.3.5", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "2b9c5fafbac0399a20a2e82429e2bd735dcfb7db" + "reference": "cdb80fa5869653c83cfe1a9084a673b6daf57ea7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/2b9c5fafbac0399a20a2e82429e2bd735dcfb7db", - "reference": "2b9c5fafbac0399a20a2e82429e2bd735dcfb7db", + "url": "https://api.github.com/repos/symfony/console/zipball/cdb80fa5869653c83cfe1a9084a673b6daf57ea7", + "reference": "cdb80fa5869653c83cfe1a9084a673b6daf57ea7", "shasum": "" }, "require": { @@ -3930,7 +3934,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.3.4" + "source": "https://github.com/symfony/console/tree/v7.3.5" }, "funding": [ { @@ -3950,7 +3954,7 @@ "type": "tidelift" } ], - "time": "2025-09-22T15:31:00+00:00" + "time": "2025-10-14T15:46:26+00:00" }, { "name": "symfony/css-selector", @@ -4327,16 +4331,16 @@ }, { "name": "symfony/finder", - "version": "v7.3.2", + "version": "v7.3.5", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "2a6614966ba1074fa93dae0bc804227422df4dfe" + "reference": "9f696d2f1e340484b4683f7853b273abff94421f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/2a6614966ba1074fa93dae0bc804227422df4dfe", - "reference": "2a6614966ba1074fa93dae0bc804227422df4dfe", + "url": "https://api.github.com/repos/symfony/finder/zipball/9f696d2f1e340484b4683f7853b273abff94421f", + "reference": "9f696d2f1e340484b4683f7853b273abff94421f", "shasum": "" }, "require": { @@ -4371,7 +4375,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.3.2" + "source": "https://github.com/symfony/finder/tree/v7.3.5" }, "funding": [ { @@ -4391,20 +4395,20 @@ "type": "tidelift" } ], - "time": "2025-07-15T13:41:35+00:00" + "time": "2025-10-15T18:45:57+00:00" }, { "name": "symfony/http-foundation", - "version": "v7.3.4", + "version": "v7.3.5", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "c061c7c18918b1b64268771aad04b40be41dd2e6" + "reference": "ce31218c7cac92eab280762c4375fb70a6f4f897" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/c061c7c18918b1b64268771aad04b40be41dd2e6", - "reference": "c061c7c18918b1b64268771aad04b40be41dd2e6", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/ce31218c7cac92eab280762c4375fb70a6f4f897", + "reference": "ce31218c7cac92eab280762c4375fb70a6f4f897", "shasum": "" }, "require": { @@ -4454,7 +4458,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v7.3.4" + "source": "https://github.com/symfony/http-foundation/tree/v7.3.5" }, "funding": [ { @@ -4474,20 +4478,20 @@ "type": "tidelift" } ], - "time": "2025-09-16T08:38:17+00:00" + "time": "2025-10-24T21:42:11+00:00" }, { "name": "symfony/http-kernel", - "version": "v7.3.4", + "version": "v7.3.5", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "b796dffea7821f035047235e076b60ca2446e3cf" + "reference": "24fd3f123532e26025f49f1abefcc01a69ef15ab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/b796dffea7821f035047235e076b60ca2446e3cf", - "reference": "b796dffea7821f035047235e076b60ca2446e3cf", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/24fd3f123532e26025f49f1abefcc01a69ef15ab", + "reference": "24fd3f123532e26025f49f1abefcc01a69ef15ab", "shasum": "" }, "require": { @@ -4572,7 +4576,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v7.3.4" + "source": "https://github.com/symfony/http-kernel/tree/v7.3.5" }, "funding": [ { @@ -4592,20 +4596,20 @@ "type": "tidelift" } ], - "time": "2025-09-27T12:32:17+00:00" + "time": "2025-10-28T10:19:01+00:00" }, { "name": "symfony/mailer", - "version": "v7.3.4", + "version": "v7.3.5", "source": { "type": "git", "url": "https://github.com/symfony/mailer.git", - "reference": "ab97ef2f7acf0216955f5845484235113047a31d" + "reference": "fd497c45ba9c10c37864e19466b090dcb60a50ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/ab97ef2f7acf0216955f5845484235113047a31d", - "reference": "ab97ef2f7acf0216955f5845484235113047a31d", + "url": "https://api.github.com/repos/symfony/mailer/zipball/fd497c45ba9c10c37864e19466b090dcb60a50ba", + "reference": "fd497c45ba9c10c37864e19466b090dcb60a50ba", "shasum": "" }, "require": { @@ -4656,7 +4660,7 @@ "description": "Helps sending emails", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailer/tree/v7.3.4" + "source": "https://github.com/symfony/mailer/tree/v7.3.5" }, "funding": [ { @@ -4676,7 +4680,7 @@ "type": "tidelift" } ], - "time": "2025-09-17T05:51:54+00:00" + "time": "2025-10-24T14:27:20+00:00" }, { "name": "symfony/mime", @@ -6172,16 +6176,16 @@ }, { "name": "symfony/var-dumper", - "version": "v7.3.4", + "version": "v7.3.5", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "b8abe7daf2730d07dfd4b2ee1cecbf0dd2fbdabb" + "reference": "476c4ae17f43a9a36650c69879dcf5b1e6ae724d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/b8abe7daf2730d07dfd4b2ee1cecbf0dd2fbdabb", - "reference": "b8abe7daf2730d07dfd4b2ee1cecbf0dd2fbdabb", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/476c4ae17f43a9a36650c69879dcf5b1e6ae724d", + "reference": "476c4ae17f43a9a36650c69879dcf5b1e6ae724d", "shasum": "" }, "require": { @@ -6235,7 +6239,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.3.4" + "source": "https://github.com/symfony/var-dumper/tree/v7.3.5" }, "funding": [ { @@ -6255,7 +6259,7 @@ "type": "tidelift" } ], - "time": "2025-09-11T10:12:26+00:00" + "time": "2025-09-27T09:00:46+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", @@ -6472,28 +6476,28 @@ }, { "name": "webmozart/assert", - "version": "1.11.0", + "version": "1.12.1", "source": { "type": "git", "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + "reference": "9be6926d8b485f55b9229203f962b51ed377ba68" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/9be6926d8b485f55b9229203f962b51ed377ba68", + "reference": "9be6926d8b485f55b9229203f962b51ed377ba68", "shasum": "" }, "require": { "ext-ctype": "*", + "ext-date": "*", + "ext-filter": "*", "php": "^7.2 || ^8.0" }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" + "suggest": { + "ext-intl": "", + "ext-simplexml": "", + "ext-spl": "" }, "type": "library", "extra": { @@ -6524,24 +6528,24 @@ ], "support": { "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" + "source": "https://github.com/webmozarts/assert/tree/1.12.1" }, - "time": "2022-06-03T18:03:27+00:00" + "time": "2025-10-29T15:56:20+00:00" } ], "packages-dev": [ { "name": "brianium/paratest", - "version": "v7.14.1", + "version": "v7.14.2", "source": { "type": "git", "url": "https://github.com/paratestphp/paratest.git", - "reference": "e1a93c38a94f4808faf75552e835666d3a6f8bb2" + "reference": "de06de1ae1203b11976c6ca01d6a9081c8b33d45" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paratestphp/paratest/zipball/e1a93c38a94f4808faf75552e835666d3a6f8bb2", - "reference": "e1a93c38a94f4808faf75552e835666d3a6f8bb2", + "url": "https://api.github.com/repos/paratestphp/paratest/zipball/de06de1ae1203b11976c6ca01d6a9081c8b33d45", + "reference": "de06de1ae1203b11976c6ca01d6a9081c8b33d45", "shasum": "" }, "require": { @@ -6555,7 +6559,7 @@ "phpunit/php-code-coverage": "^12.4.0", "phpunit/php-file-iterator": "^6", "phpunit/php-timer": "^8", - "phpunit/phpunit": "^12.4.0", + "phpunit/phpunit": "^12.4.1", "sebastian/environment": "^8.0.3", "symfony/console": "^6.4.20 || ^7.3.4", "symfony/process": "^6.4.20 || ^7.3.4" @@ -6565,7 +6569,7 @@ "ext-pcntl": "*", "ext-pcov": "*", "ext-posix": "*", - "phpstan/phpstan": "^2.1.30", + "phpstan/phpstan": "^2.1.31", "phpstan/phpstan-deprecation-rules": "^2.0.3", "phpstan/phpstan-phpunit": "^2.0.7", "phpstan/phpstan-strict-rules": "^2.0.7", @@ -6609,7 +6613,7 @@ ], "support": { "issues": "https://github.com/paratestphp/paratest/issues", - "source": "https://github.com/paratestphp/paratest/tree/v7.14.1" + "source": "https://github.com/paratestphp/paratest/tree/v7.14.2" }, "funding": [ { @@ -6621,7 +6625,7 @@ "type": "paypal" } ], - "time": "2025-10-06T08:26:52+00:00" + "time": "2025-10-24T07:20:53+00:00" }, { "name": "doctrine/deprecations", @@ -6979,16 +6983,16 @@ }, { "name": "laravel/boost", - "version": "v1.4.0", + "version": "v1.6.0", "source": { "type": "git", "url": "https://github.com/laravel/boost.git", - "reference": "8d2dedf7779c2e175a02a176dec38e6f9b35352b" + "reference": "29d1c7c5a816d2b55c39f50bb07bdbca6c595b09" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/boost/zipball/8d2dedf7779c2e175a02a176dec38e6f9b35352b", - "reference": "8d2dedf7779c2e175a02a176dec38e6f9b35352b", + "url": "https://api.github.com/repos/laravel/boost/zipball/29d1c7c5a816d2b55c39f50bb07bdbca6c595b09", + "reference": "29d1c7c5a816d2b55c39f50bb07bdbca6c595b09", "shasum": "" }, "require": { @@ -6999,7 +7003,7 @@ "illuminate/support": "^10.49.0|^11.45.3|^12.28.1", "laravel/mcp": "^0.2.0|^0.3.0", "laravel/prompts": "0.1.25|^0.3.6", - "laravel/roster": "^0.2.8", + "laravel/roster": "^0.2.9", "php": "^8.1" }, "require-dev": { @@ -7041,20 +7045,20 @@ "issues": "https://github.com/laravel/boost/issues", "source": "https://github.com/laravel/boost" }, - "time": "2025-10-14T01:13:19+00:00" + "time": "2025-10-28T17:43:53+00:00" }, { "name": "laravel/mcp", - "version": "v0.3.0", + "version": "v0.3.2", "source": { "type": "git", "url": "https://github.com/laravel/mcp.git", - "reference": "4e1389eedb4741a624e26cc3660b31bae04c4342" + "reference": "dc722a4c388f172365dec70461f0413ac366f360" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/mcp/zipball/4e1389eedb4741a624e26cc3660b31bae04c4342", - "reference": "4e1389eedb4741a624e26cc3660b31bae04c4342", + "url": "https://api.github.com/repos/laravel/mcp/zipball/dc722a4c388f172365dec70461f0413ac366f360", + "reference": "dc722a4c388f172365dec70461f0413ac366f360", "shasum": "" }, "require": { @@ -7075,7 +7079,7 @@ "orchestra/testbench": "^8.36.0|^9.15.0|^10.6.0", "pestphp/pest": "^2.36.0|^3.8.4|^4.1.0", "phpstan/phpstan": "^2.1.27", - "rector/rector": "^2.1.7" + "rector/rector": "^2.2.4" }, "type": "library", "extra": { @@ -7114,7 +7118,7 @@ "issues": "https://github.com/laravel/mcp/issues", "source": "https://github.com/laravel/mcp" }, - "time": "2025-10-07T14:28:56+00:00" + "time": "2025-10-29T14:26:01+00:00" }, { "name": "laravel/pail", @@ -7263,16 +7267,16 @@ }, { "name": "laravel/roster", - "version": "v0.2.8", + "version": "v0.2.9", "source": { "type": "git", "url": "https://github.com/laravel/roster.git", - "reference": "832a6db43743bf08a58691da207f977ec8dc43aa" + "reference": "82bbd0e2de614906811aebdf16b4305956816fa6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/roster/zipball/832a6db43743bf08a58691da207f977ec8dc43aa", - "reference": "832a6db43743bf08a58691da207f977ec8dc43aa", + "url": "https://api.github.com/repos/laravel/roster/zipball/82bbd0e2de614906811aebdf16b4305956816fa6", + "reference": "82bbd0e2de614906811aebdf16b4305956816fa6", "shasum": "" }, "require": { @@ -7320,20 +7324,20 @@ "issues": "https://github.com/laravel/roster/issues", "source": "https://github.com/laravel/roster" }, - "time": "2025-09-22T13:28:47+00:00" + "time": "2025-10-20T09:56:46+00:00" }, { "name": "laravel/sail", - "version": "v1.46.0", + "version": "v1.47.0", "source": { "type": "git", "url": "https://github.com/laravel/sail.git", - "reference": "eb90c4f113c4a9637b8fdd16e24cfc64f2b0ae6e" + "reference": "9a11e822238167ad8b791e4ea51155d25cf4d8f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sail/zipball/eb90c4f113c4a9637b8fdd16e24cfc64f2b0ae6e", - "reference": "eb90c4f113c4a9637b8fdd16e24cfc64f2b0ae6e", + "url": "https://api.github.com/repos/laravel/sail/zipball/9a11e822238167ad8b791e4ea51155d25cf4d8f2", + "reference": "9a11e822238167ad8b791e4ea51155d25cf4d8f2", "shasum": "" }, "require": { @@ -7346,7 +7350,7 @@ }, "require-dev": { "orchestra/testbench": "^7.0|^8.0|^9.0|^10.0", - "phpstan/phpstan": "^1.10" + "phpstan/phpstan": "^2.0" }, "bin": [ "bin/sail" @@ -7383,7 +7387,7 @@ "issues": "https://github.com/laravel/sail/issues", "source": "https://github.com/laravel/sail" }, - "time": "2025-09-23T13:44:39+00:00" + "time": "2025-10-28T13:55:29+00:00" }, { "name": "mockery/mockery", @@ -7629,16 +7633,16 @@ }, { "name": "pestphp/pest", - "version": "v4.1.2", + "version": "v4.1.3", "source": { "type": "git", "url": "https://github.com/pestphp/pest.git", - "reference": "08b09f2e98fc6830050c0237968b233768642d46" + "reference": "477d20a54fd9329ddfb0f8d4eb90dca7bc81b027" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pestphp/pest/zipball/08b09f2e98fc6830050c0237968b233768642d46", - "reference": "08b09f2e98fc6830050c0237968b233768642d46", + "url": "https://api.github.com/repos/pestphp/pest/zipball/477d20a54fd9329ddfb0f8d4eb90dca7bc81b027", + "reference": "477d20a54fd9329ddfb0f8d4eb90dca7bc81b027", "shasum": "" }, "require": { @@ -7650,12 +7654,12 @@ "pestphp/pest-plugin-mutate": "^4.0.1", "pestphp/pest-plugin-profanity": "^4.1.0", "php": "^8.3.0", - "phpunit/phpunit": "^12.4.0", + "phpunit/phpunit": "^12.4.1", "symfony/process": "^7.3.4" }, "conflict": { "filp/whoops": "<2.18.3", - "phpunit/phpunit": ">12.4.0", + "phpunit/phpunit": ">12.4.1", "sebastian/exporter": "<7.0.0", "webmozart/assert": "<1.11.0" }, @@ -7729,7 +7733,7 @@ ], "support": { "issues": "https://github.com/pestphp/pest/issues", - "source": "https://github.com/pestphp/pest/tree/v4.1.2" + "source": "https://github.com/pestphp/pest/tree/v4.1.3" }, "funding": [ { @@ -7741,7 +7745,7 @@ "type": "github" } ], - "time": "2025-10-05T19:09:49+00:00" + "time": "2025-10-29T22:45:27+00:00" }, { "name": "pestphp/pest-plugin", @@ -8035,16 +8039,16 @@ }, { "name": "pestphp/pest-plugin-profanity", - "version": "v4.1.0", + "version": "v4.2.0", "source": { "type": "git", "url": "https://github.com/pestphp/pest-plugin-profanity.git", - "reference": "e279c844b6868da92052be27b5202c2ad7216e80" + "reference": "c37e5e2c7136ee4eae12082e7952332bc1c6600a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pestphp/pest-plugin-profanity/zipball/e279c844b6868da92052be27b5202c2ad7216e80", - "reference": "e279c844b6868da92052be27b5202c2ad7216e80", + "url": "https://api.github.com/repos/pestphp/pest-plugin-profanity/zipball/c37e5e2c7136ee4eae12082e7952332bc1c6600a", + "reference": "c37e5e2c7136ee4eae12082e7952332bc1c6600a", "shasum": "" }, "require": { @@ -8085,9 +8089,9 @@ "unit" ], "support": { - "source": "https://github.com/pestphp/pest-plugin-profanity/tree/v4.1.0" + "source": "https://github.com/pestphp/pest-plugin-profanity/tree/v4.2.0" }, - "time": "2025-09-10T06:17:03+00:00" + "time": "2025-10-28T23:14:11+00:00" }, { "name": "phar-io/manifest", @@ -8765,16 +8769,16 @@ }, { "name": "phpunit/phpunit", - "version": "12.4.0", + "version": "12.4.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "f62aab5794e36ccd26860db2d1bbf89ac19028d9" + "reference": "fc5413a2e6d240d2f6d9317bdf7f0a24e73de194" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f62aab5794e36ccd26860db2d1bbf89ac19028d9", - "reference": "f62aab5794e36ccd26860db2d1bbf89ac19028d9", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/fc5413a2e6d240d2f6d9317bdf7f0a24e73de194", + "reference": "fc5413a2e6d240d2f6d9317bdf7f0a24e73de194", "shasum": "" }, "require": { @@ -8842,7 +8846,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/12.4.0" + "source": "https://github.com/sebastianbergmann/phpunit/tree/12.4.1" }, "funding": [ { @@ -8866,7 +8870,7 @@ "type": "tidelift" } ], - "time": "2025-10-03T04:28:03+00:00" + "time": "2025-10-09T14:08:29+00:00" }, { "name": "sebastian/cli-parser", @@ -9819,16 +9823,16 @@ }, { "name": "symfony/yaml", - "version": "v7.3.3", + "version": "v7.3.5", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "d4f4a66866fe2451f61296924767280ab5732d9d" + "reference": "90208e2fc6f68f613eae7ca25a2458a931b1bacc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/d4f4a66866fe2451f61296924767280ab5732d9d", - "reference": "d4f4a66866fe2451f61296924767280ab5732d9d", + "url": "https://api.github.com/repos/symfony/yaml/zipball/90208e2fc6f68f613eae7ca25a2458a931b1bacc", + "reference": "90208e2fc6f68f613eae7ca25a2458a931b1bacc", "shasum": "" }, "require": { @@ -9871,7 +9875,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v7.3.3" + "source": "https://github.com/symfony/yaml/tree/v7.3.5" }, "funding": [ { @@ -9891,7 +9895,7 @@ "type": "tidelift" } ], - "time": "2025-08-27T11:34:33+00:00" + "time": "2025-09-27T09:00:46+00:00" }, { "name": "ta-tikoma/phpunit-architecture-test", From 9b47429c88408ea2ff866447004bd17b609041d9 Mon Sep 17 00:00:00 2001 From: Bob Molitor Date: Fri, 31 Oct 2025 13:03:47 +0100 Subject: [PATCH 2/3] wip --- .gitignore | 2 +- .mcp.json | 37 ------------------------------------- 2 files changed, 1 insertion(+), 38 deletions(-) diff --git a/.gitignore b/.gitignore index e2b1505..3238862 100644 --- a/.gitignore +++ b/.gitignore @@ -24,5 +24,5 @@ yarn-error.log env *.mcp* *.mdc -.mcp.json +/.mcp.json AGENTS*.* \ No newline at end of file diff --git a/.mcp.json b/.mcp.json index d03c71c..01738d7 100644 --- a/.mcp.json +++ b/.mcp.json @@ -15,43 +15,6 @@ "env": { "SITE_PATH": "/Users/bobmolitor/Herd/trusted_ai" } - }, - "filesystem": { - "command": "npx", - "args": [ - "-y", - "@modelcontextprotocol/server-filesystem", - "/Users/sebastianfrohlich/Desktop", - "/Users/sebastianfrohlich/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks", - "/Users/sebastianfrohlich/Downloads", - "/Users/sebastianfrohlich/", - "/Users/sebastianfrohlich/Projekte" - ] - }, - "bear": { - "command": "node", - "args": [ - "/Users/sebastianfrohlich/Projekte/bear-notes-mcp/dist/index.js" - ], - "env": [] - }, - "postgresql": { - "command": "npx", - "args": [ - "-y", - "@executeautomation/database-server", - "--postgresql", - "--host", - "localhost", - "--database", - "risk_ingest_db", - "--user", - "risk_ingest_user", - "--password", - "S0prast3r1a", - "--port", - "5433" - ] } } } \ No newline at end of file From 338c39bac00852784534ac0cf6b4526becc69427 Mon Sep 17 00:00:00 2001 From: Bob Molitor Date: Fri, 31 Oct 2025 13:33:59 +0100 Subject: [PATCH 3/3] feat: new UI for Fallansicht --- .../livewire/companies/transactions.blade.php | 560 ++++++++++++++---- 1 file changed, 461 insertions(+), 99 deletions(-) diff --git a/resources/views/livewire/companies/transactions.blade.php b/resources/views/livewire/companies/transactions.blade.php index 8b6d18e..a035147 100644 --- a/resources/views/livewire/companies/transactions.blade.php +++ b/resources/views/livewire/companies/transactions.blade.php @@ -289,6 +289,7 @@ new #[Layout('components.layouts.app')] class extends Component { $counterpartyHistory = $this->counterpartyHistory; $recentAlerts = $this->recentAlerts; $actionChecklist = $this->actionChecklist; + $checklistIntro = __('Fokusbereiche für diesen Fall'); $formatCurrency = static fn (float $value): string => number_format($value, 2, ',', '.') . ' €'; $formatAmount = static fn (float $value, string $currency): string => number_format($value, 2, ',', '.') . ' ' . $currency; @@ -353,10 +354,42 @@ new #[Layout('components.layouts.app')] class extends Component { - + -
-
+@if (! empty($actionChecklist)) +
+
+
+

{{ $checklistIntro }}

+

+ {{ __('Bearbeiten Sie die wichtigsten Schritte nacheinander, um den Fallabschluss vorzubereiten.') }} +

+
+ + {{ count($actionChecklist) }} {{ __('Schritte') }} + +
+ +
+
    + @foreach ($actionChecklist as $index => $item) +
  1. + + {{ sprintf('%02d', $index + 1) }} + +
    +

    {{ $item['title'] }}

    +

    {{ $item['description'] }}

    +
    +
  2. + @endforeach +
+
+
+@endif + +
+
@@ -481,114 +514,443 @@ new #[Layout('components.layouts.app')] class extends Component {
-
+
@if ($selected) -
-
-
- - {{ __('Case ID') }} {{ $selected->id }} - -

- {{ $selected->counterparty }} — {{ $formatAmount($selected->amount, $selected->currency) }} -

-

- {{ __('Ausgeführt am') }} {{ $selected->executed_at?->format('d.m.Y, H:i') }} • {{ $selected->channel }} + @php + $badgeStyle = $statusStyles[$selected->status] ?? 'bg-zinc-500/10 text-zinc-600 border border-zinc-500/20'; + $riskLevel = match (true) { + $selected->risk_score >= 80 => __('Hoch'), + $selected->risk_score >= 60 => __('Erhöht'), + $selected->risk_score >= 40 => __('Moderat'), + default => __('Niedrig'), + }; + $riskNarrative = match (true) { + $selected->risk_score >= 80 => __('Die Einstufung über 80 Punkten signalisiert einen erhöhten AML-Prüffokus. Eskalation und enges Monitoring sind angeraten.'), + $selected->risk_score >= 60 => __('Das Risiko liegt im oberen Mittelfeld. Prüfen Sie Plausibilitäten und dokumentieren Sie Gegenmaßnahmen.'), + $selected->risk_score >= 40 => __('Die Einstufung erscheint moderat. Stellen Sie sicher, dass grundlegende Prüfungen vollständig dokumentiert sind.'), + default => __('Der Score deutet auf ein geringes AML-Risiko hin. Ein strukturierter Nachweis der Prüfung ist dennoch erforderlich.'), + }; + $statusGuidance = $statusCopy[$selected->status] ?? __('Review abschließen und Audit-Trail ergänzen.'); + $executedDate = $selected->executed_at?->format('d.m.Y'); + $executedTime = $selected->executed_at?->format('H:i'); + $amountValue = $formatAmount($selected->amount, $selected->currency); + $companyName = $company->legal_name ?? $company->name; + $companyCountryLabel = $company->country ?? __('Jurisdiktion offen'); + $counterpartyCountryLabel = $selected->counterparty_country ?? __('Jurisdiktion offen'); + $referenceLabel = $selected->reference ?: __('Keine Referenz angegeben'); + $flaggedReasonLabel = $selected->flagged_reason ?: __('Nicht dokumentiert'); + $domesticFlow = $company->country && $selected->counterparty_country && strcasecmp($company->country, $selected->counterparty_country) === 0; + $purposeDescription = __('Die Zahlung nutzt den Referenztext ":reference" und wurde mit dem Flag ":reason" markiert. Prüfen Sie Rechnungs- und Liefernachweise, um den wirtschaftlichen Anlass zu bestätigen.', [ + 'reference' => $referenceLabel, + 'reason' => $flaggedReasonLabel, + ]); + $countryNotes = []; + if ($domesticFlow) { + $countryNotes[] = __('Transparency International – CPI 2024: 75/100 (Rang 15).'); + } else { + $countryNotes[] = __('Ermitteln Sie länderspezifische Restrisiken (Sanktionen, Exportkontrolle, Devisenauflagen).'); + } + if (! $company->country || ! $selected->counterparty_country) { + $countryNotes[] = __('Länderangaben unvollständig – KYC-Stammdaten aktualisieren.'); + } + $analysisSections = [ + [ + 'title' => __('Verwendungszweck'), + 'description' => $purposeDescription, + 'notes' => [ + __('Rechnungskopie inkl. HR-/VAT-/IBAN-Angaben anfordern.'), + __('3-Way-Match (PO–GR–Invoice) vollständig dokumentieren.'), + ], + ], + [ + 'title' => __('LEI'), + 'description' => __('Für :company und :counterparty liegt kein verifizierter LEI im Datensatz vor. Ein Direktabruf im GLEIF-Register wird empfohlen.', [ + 'company' => $companyName, + 'counterparty' => $selected->counterparty, + ]), + 'notes' => [ + __('Status: not verified.'), + __('Bankinterne LEI-Prüfung vor Freigabe abschließen.'), + ], + ], + [ + 'title' => __('Länderanalyse'), + 'description' => $domesticFlow + ? __('Vorliegende Daten deuten auf eine Inlandszahlung (:from → :to) hin. Das systemische Korruptionsrisiko gilt als niedrig.', [ + 'from' => $companyCountryLabel, + 'to' => $counterpartyCountryLabel, + ]) + : __('Transaktion verläuft von :from nach :to. Prüfen Sie Export-/Importauflagen sowie mögliche Hochrisikofaktoren.', [ + 'from' => $companyCountryLabel, + 'to' => $counterpartyCountryLabel, + ]), + 'notes' => $countryNotes, + ], + [ + 'title' => __('PEP-Exposure'), + 'description' => __('Es liegen keine Hinweise auf PEP-Einfluss in den Stammdaten vor. Screening der wirtschaftlich Berechtigten bleibt fortzuführen.'), + 'notes' => [ + __('Manuelles Screening der Management- und Eigentümerebene abschließen (Status: not verified).'), + ], + ], + [ + 'title' => __('AML Exposure (transaktionsbezogen)'), + 'description' => __('Klarer Leistungszweck und etablierte Gegenparteien wirken risikomindernd. Restrisiko besteht in der eindeutigen Identifikation der Rechnungseinheit und des Zahlungswegs.'), + 'notes' => [ + __('Kontoinhaber und IBAN mit der Rechnungseinheit matchen.'), + __('Preis- und Mengenabweichungen gegen Bestellung analysieren.'), + ], + ], + [ + 'title' => __('Sanktions-Exposure'), + 'description' => __('Bisherige Namenstests ergaben keine Treffer. Automatisierte Listenprüfungen sind auf den finalen Rechtsträger erneut auszuführen.'), + 'notes' => [ + __('EU-/OFAC-Abgleich sowie interne Sanktionslisten auf finaler Rechnungseinheit bestätigen.'), + ], + ], + [ + 'title' => __('Korruptions-/Bestechungs-Exposure'), + 'description' => __('Makrorisiko im deutschen Markt ist gering. Beschaffungsrisiken (Kick-backs, Scheinrechnungen) bleiben bei Großvolumina bestehen.'), + 'notes' => [ + __('Zahlungen ausschließlich an bestätigte Gesellschaftskonten leisten.'), + __('Vier-Augen-Prinzip und Liefernachweise dokumentieren.'), + ], + ], + [ + 'title' => __('Adverse Media Scanning (≤ 12 Monate)'), + 'description' => __('In den letzten 12 Monaten wurden keine AML-relevanten Negativmeldungen identifiziert. Restrisiko: laufende Restrukturierungs- und Compliance-News beobachten.'), + 'notes' => [ + __('Interne Newsfeeds und Watchlists weiterführen.'), + ], + ], + [ + 'title' => __('KYC/EDD Feststellungen'), + 'description' => __('KYC-Dossier aktualisieren: rechtliche Einheit, wirtschaftlich Berechtigte und Zahlungsweg eindeutig dokumentieren.'), + 'notes' => [ + __('IBAN/BIC sowie Steuerkennzeichen im Dossier hinterlegen.'), + __('Rechnungskopie, Liefernachweis und Vertragsgrundlage einholen.'), + ], + ], + [ + 'title' => __('Gesamtbeurteilung (AML-Sicht)'), + 'description' => __('Die Transaktion über :amount zwischen :company und :counterparty wirkt branchenüblich. Freigabe ist möglich, sobald Rechnungseinheit, Zahlungsweg und Screening final bestätigt sind.', [ + 'amount' => $amountValue, + 'company' => $companyName, + 'counterparty' => $selected->counterparty, + ]), + 'notes' => [ + __('Votum: Freigabefähig unter Auflagen (Rechnung, 3-Way-Match, Screening, Exportkontrolle).'), + ], + ], + ]; + $combineNotes = static function (array $sections): array { + $notes = []; + + foreach ($sections as $section) { + $notes = array_merge($notes, $section['notes'] ?? []); + } + + return $notes; + }; + + $clusterFactory = static function (string $title, array $sections) use ($combineNotes): array { + $leadSection = $sections[0] ?? null; + $contexts = array_slice($sections, 1); + + return [ + 'title' => $title, + 'lead_label' => $leadSection['title'] ?? null, + 'lead' => $leadSection['description'] ?? '', + 'contexts' => array_map( + static fn (array $section): array => [ + 'label' => $section['title'], + 'body' => $section['description'], + ], + $contexts + ), + 'actions' => $combineNotes($sections), + 'count' => count($sections), + ]; + }; + + $transactionSections = array_slice($analysisSections, 0, 2); + $riskSections = array_slice($analysisSections, 2, 6); + $dueSections = array_slice($analysisSections, 8); + + $analysisClusters = [ + $clusterFactory(__('Transaktion & Zweck'), $transactionSections), + $clusterFactory(__('Risikoperspektive'), $riskSections), + $clusterFactory(__('Due Diligence & Abschluss'), $dueSections), + ]; + + $analysisClusterCount = array_sum(array_map(static fn (array $cluster): int => $cluster['count'], $analysisClusters)); + + $sources = array_values(array_filter([ + $companyName ? __('Unternehmensprofil :company (:country) – interne KYC-Stammdaten.', [ + 'company' => $companyName, + 'country' => $companyCountryLabel, + ]) : null, + __('Transaktionsdatensatz #:id – Core Banking Export.', ['id' => $selected->id]), + $selected->reference ? __('Rechnungsreferenz ":reference" – Plausibilisierung steht aus.', ['reference' => $referenceLabel]) : null, + __('GLEIF – LEI Register (Direktabruf, Status: not verified).'), + __('Transparency International – CPI 2024 (Deutschland 75/100).'), + __('EU/OFAC Sanktionsportale – orientierender Namensabgleich (endgültige Prüfung intern).'), + ])); + @endphp + +

+
+
+
+ + {{ __('Case ID') }} {{ $selected->id }} + +

+ {{ $selected->counterparty }} — {{ $amountValue }} +

+

+ {{ __('Ausgeführt am') }} {{ $executedDate ?? __('Nicht verfügbar') }} + @if ($executedTime) + • {{ $executedTime }} + @endif + • {{ $selected->channel ?: __('Kanal offen') }} +

+
+ +
+ + {{ $selected->statusLabel() }} + + + {{ __('Risikowert') }} {{ $selected->risk_score }} ({{ $riskLevel }}) + +
+
+ +
+
+

{{ __('Stichtag') }}

+

{{ $executedDate ?? __('Nicht verfügbar') }}

+

{{ $executedTime ? __('Zeit') . ' ' . $executedTime : __('Zeitpunkt offen') }}

+
+
+

{{ __('Kanal & Referenz') }}

+

{{ $selected->channel ?: __('Unbekannt') }}

+

{{ $selected->reference ?: __('Keine Referenz hinterlegt') }}

+
+
+

{{ __('Flagging durch') }}

+

{{ $selected->flagged_by ?: __('Automatisiertes Monitoring') }}

+

{{ $selected->flagged_reason ?: __('Flagging-Grund nicht dokumentiert') }}

+
+
+

{{ __('Empfohlene Maßnahme') }}

+

{{ $statusGuidance }}

+
+
+ +
+

{{ __('Parteiprofile') }}

+
+
+
+

{{ __('Sending party') }}

+ + {{ __('Unternehmensprofil') }} + +
+

{{ $selected->company?->legal_name ?? $company->legal_name ?? $company->name }}

+
+
+
{{ __('Jurisdiktion') }}
+
{{ $company->country ?? __('Nicht verifiziert') }}
+
+
+
{{ __('Sitz') }}
+
{{ $company->headquarters ?? __('Nicht verifiziert') }}
+
+
+
{{ __('Sektor') }}
+
{{ $company->sector ?? __('Nicht verifiziert') }}
+
+
+
+
+
+

{{ __('Receiving party') }}

+ + {{ __('Verifizierung offen') }} + +
+

{{ $selected->counterparty }}

+
+
+
{{ __('Jurisdiktion') }}
+
{{ $selected->counterparty_country ?? __('Nicht verifiziert') }}
+
+
+
{{ __('Referenzhinweis') }}
+
{{ $selected->reference ?: __('Keine Angabe') }}
+
+
+
{{ __('Next step') }}
+
{{ __('IBAN/Beneficiary eindeutig zuordnen') }}
+
+
+
+
+
+ +
+
+

{{ __('Transaktionsbewertung') }}

+

{{ $riskNarrative }}

+
    +
  • • {{ __('Betrag') }} {{ $amountValue }}
  • +
  • • {{ __('Flagging-Grund') }}: {{ $selected->flagged_reason ?: __('nicht dokumentiert') }}
  • +
  • • {{ __('Kanal') }}: {{ $selected->channel ?: __('unbekannt') }}
  • +
+
+ +
+

{{ __('Kontrollaufgaben (Zusammenfassung)') }}

+

+ {{ __('Sicherstellen, dass Rechnungskopf, Beneficiary-Daten und 3-Way-Match (PO–GR–Invoice) dokumentiert sind. Interne Listenabgleiche (Sanktionen/PEP) auf den konkreten Rechtsträger erneut ausführen.') }} +

+
+
+ + @if (! empty($selected->signals)) +
+

{{ __('Detektionssignale') }}

+
+ @foreach ($selected->signals as $signal) + + + {{ data_get($signal, 'type') }} — {{ data_get($signal, 'value') }} + + @endforeach +
+
+ @endif + +
+

{{ __('Narrative Zusammenfassung') }}

+

+ {{ __('Die Transaktion verbindet :company (:jurisdiction) mit :counterparty (:country). Der Betrag von :amount wurde über den Kanal :channel ausgeführt. Für die abschließende AML-Beurteilung ist der Nachweis der wirtschaftlich Berechtigten sowie ein Abgleich der Zahlungsempfänger-Daten erforderlich.', [ + 'company' => $company->legal_name ?? $company->name, + 'jurisdiction' => $company->country ?? __('Jurisdiktion offen'), + 'counterparty' => $selected->counterparty, + 'country' => $selected->counterparty_country ?? __('Jurisdiktion offen'), + 'amount' => $amountValue, + 'channel' => $selected->channel ?: __('unbekannt'), + ]) }} +

+

+ {{ __('Hinweis: Interne Quellen (ERP, KYC-Stammdaten, Screening) ergänzen, um den Verifizierungsstatus auf „abgeschlossen“ anzuheben.') }}

-
- @php($badgeStyle = $statusStyles[$selected->status] ?? 'bg-zinc-500/10 text-zinc-600 border border-zinc-500/20') - - {{ $selected->statusLabel() }} - - - {{ __('Risikowert') }} {{ $selected->risk_score }} - -
-
+ @if (! empty($analysisClusters)) +
+
+
+

{{ __('Vertiefte Analysefelder') }}

+ + {{ $analysisClusterCount }} {{ __('Prüfpunkte') }} + +
+

+ {{ __('Verdichten Sie die Prüfung auf drei Themenblöcke. Jeder Block fasst die wesentlichen Anforderungen zusammen.') }} +

+
-
-
-

{{ __('Gegenpartei') }}

-

{{ $selected->counterparty }}

-

{{ $selected->counterparty_country }}

-
-
-

{{ __('Referenz') }}

-

{{ $selected->reference }}

-

{{ $selected->flagged_by }}

-
-
-

{{ __('Flagging-Grund') }}

-

{{ $selected->flagged_reason }}

-
-
-

{{ __('Empfohlene Maßnahme') }}

-

{{ $statusCopy[$selected->status] ?? __('Review abschließen und Audit-Trail ergänzen.') }}

-
-
+ @foreach ($analysisClusters as $clusterIndex => $cluster) +
+
+
+ @if ($cluster['lead_label']) + + {{ $cluster['lead_label'] }} + + @endif +
+ {{ sprintf('%02d', $clusterIndex + 1) }} — {{ $cluster['title'] }} +
+

+ {{ $cluster['lead'] }} +

+
+ + {{ $cluster['count'] }} {{ __('Teilfelder') }} + +
- @if (! empty($selected->signals)) -
-

{{ __('Detektionssignale') }}

-
- @foreach ($selected->signals as $signal) - - - {{ data_get($signal, 'type') }} — {{ data_get($signal, 'value') }} - + @if (! empty($cluster['contexts'])) +
+ @foreach ($cluster['contexts'] as $context) +
+

{{ $context['label'] }}

+

{{ $context['body'] }}

+
+ @endforeach +
+ @endif + + @if (! empty($cluster['actions'])) +
+

{{ __('Schlüsselmaßnahmen') }}

+
    + @foreach ($cluster['actions'] as $action) +
  • + + {{ $action }} +
  • + @endforeach +
+
+ @endif +
@endforeach
-
- @endif + @endif -
-

{{ __('Fallleitlinie') }}

-

- {{ __('Führen Sie eine ganzheitliche Bewertung durch: prüfen Sie Geschäftsbeziehung, Transaktionshistorie und Monitoring-Regeln. Stellen Sie sicher, dass alle Entscheidungswege und Maßnahmen dokumentiert und für Audits nachvollziehbar sind.') }} -

+ @if (! empty($sources)) +
+
{{ __('Quellen & Verifizierungsstand (≤ 365 Tage)') }}
+
    + @foreach ($sources as $source) +
  • {{ $source }}
  • + @endforeach +
+
+ @endif
-
-
-

{{ __('Aktions-Checkliste') }}

-
    - @foreach ($actionChecklist as $index => $item) -
  1. - - {{ $index + 1 }} - -
    -

    {{ $item['title'] }}

    -

    {{ $item['description'] }}

    -
    -
  2. - @endforeach -
-
- -
-

{{ __('Historie mit dieser Gegenpartei') }}

-
- @forelse ($counterpartyHistory as $history) -
-
-

{{ $history->executed_at?->format('d.m.Y H:i') }}

-

{{ $history->channel }} • {{ $history->reference }}

-
-
- {{ $formatAmount($history->amount, $history->currency) }} - - {{ __('Risiko') }} {{ $history->risk_score }} - -
+
+
+

{{ __('Historie mit dieser Gegenpartei') }}

+
+ @forelse ($counterpartyHistory as $history) +
+
+

{{ $history->executed_at?->format('d.m.Y H:i') }}

+

{{ $history->channel }} • {{ $history->reference }}

+
+
+ {{ $formatAmount($history->amount, $history->currency) }} + + {{ __('Risiko') }} {{ $history->risk_score }} + +
+
+ @empty +
+ {{ __('Keine weiteren Transaktionen mit dieser Gegenpartei gefunden.') }} +
+ @endforelse
- @empty -
- {{ __('Keine weiteren Transaktionen mit dieser Gegenpartei gefunden.') }} -
- @endforelse +
@else