diff --git a/.cursor/rules/laravel-boost.mdc b/.cursor/rules/laravel-boost.mdc new file mode 100644 index 00000000..8f67ba95 --- /dev/null +++ b/.cursor/rules/laravel-boost.mdc @@ -0,0 +1,74 @@ +--- +alwaysApply: true +--- + +=== foundation rules === + +# Laravel Boost Guidelines + +The Laravel Boost guidelines are specifically curated by Laravel maintainers for this application. These guidelines should be followed closely to enhance the user's satisfaction building Laravel applications. + +## 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.3.22 + + +## 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()`. +- Check for existing components to reuse before writing a new one. + +## Verification Scripts +- Do not create verification scripts or tinker when tests cover that functionality and prove it works. Unit and feature tests are more important. + +## Application Structure & Architecture +- Stick to existing directory structure - don't create new base folders without approval. +- Do not change the application's dependencies without approval. + +## Frontend Bundling +- If the user doesn't see a frontend change reflected in the UI, it could mean they need to run `npm run build`, `npm run dev`, or `composer run dev`. Ask them. + +## Replies +- Be concise in your explanations - focus on what's important rather than explaining obvious details. + +## Documentation Files +- You must only create documentation files if explicitly requested by the user. + + +=== boost rules === + +## Laravel Boost +- Laravel Boost is an MCP server that comes with powerful tools designed specifically for this application. Use them. + +## Artisan +- Use the `list-artisan-commands` tool when you need to call an Artisan command to double check the available parameters. + +## URLs +- Whenever you share a project URL with the user you should use the `get-absolute-url` tool to ensure you're using the correct scheme, domain / IP, and port. + +## Tinker / Debugging +- You should use the `tinker` tool when you need to execute PHP to debug code or query Eloquent models directly. +- Use the `database-query` tool when you only need to read from the database. + +## Reading Browser Logs With the `browser-logs` Tool +- You can read browser logs, errors, and exceptions using the `browser-logs` tool from Boost. +- Only recent browser logs will be useful - ignore old logs. + +## Searching Documentation (Critically Important) +- Boost comes with a powerful `search-docs` tool you should use before any other approaches. This tool automatically passes a list of installed packages and their versions to the remote Boost API, so it returns only version-specific documentation specific for the user's circumstance. You should pass an array of packages to filter on if you know you need docs for particular packages. +- The 'search-docs' tool is perfect for all Laravel related packages, including Laravel, Inertia, Livewire, Filament, Tailwind, Pest, Nova, Nightwatch, etc. +- You must use this tool to search for Laravel-ecosystem documentation before falling back to other approaches. +- Search the documentation before making code changes to ensure we are taking the correct approach. +- Use multiple, broad, simple, topic based queries to start. For example: `['rate limiting', 'routing rate limiting', 'routing']`. +- Do not add package names to queries, package information is already shared. Use `test resource table`, not `filament 4 test resource table`. + +### Available Search Syntax +- You can and should pass multiple queries at once. The most relevant results will be returned first. + +1. Simple Word Searches with auto-stemming - query=authentication - finds 'authenticate' and 'auth' +2. Multiple Words (AND Logic) - query=rate limit - finds knowledge containing both "rate" AND "limit" +3. Quoted Phrases (Exact Position) - query="infinite scroll" - Words must be adjacent and in that order +4. Mixed Queries - query=middleware "rate limit" - "middleware" AND exact phrase "rate limit" +5. Multiple Queries - queries=["authentication", "middleware"] - ANY of these terms + \ No newline at end of file diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index d17a1610..ec9e2f2f 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -16,7 +16,7 @@ jobs: fail-fast: false matrix: php: [8.4, 8.3, 8.2] - laravel: [11.x] + laravel: [12.x, 11.x] stability: [prefer-lowest, prefer-stable] name: Static Analysis - P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} @@ -59,7 +59,7 @@ jobs: fail-fast: false matrix: php: [8.4, 8.3, 8.2] - laravel: [11.x] + laravel: [12.x, 11.x] stability: [prefer-lowest, prefer-stable] name: Tests - P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} - MySQL 8.0 @@ -116,7 +116,7 @@ jobs: fail-fast: false matrix: php: [8.4, 8.3, 8.2] - laravel: [11.x] + laravel: [12.x, 11.x] stability: [prefer-lowest, prefer-stable] name: Tests - P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} - PostgreSQL 13 @@ -172,8 +172,8 @@ jobs: strategy: fail-fast: false matrix: - php: [ 8.3, 8.2 ] - laravel: [ 11.x ] + php: [8.4, 8.3, 8.2] + laravel: [12.x, 11.x] stability: [ prefer-lowest, prefer-stable ] name: Tests - P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} - PostgreSQL 14 @@ -229,8 +229,8 @@ jobs: strategy: fail-fast: false matrix: - php: [ 8.3, 8.2 ] - laravel: [ 11.x ] + php: [8.4, 8.3, 8.2] + laravel: [12.x, 11.x] stability: [ prefer-lowest, prefer-stable ] name: Tests - P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} - PostgreSQL 15 @@ -274,8 +274,8 @@ jobs: strategy: fail-fast: false matrix: - php: [ 8.3, 8.2 ] - laravel: [ 11.x ] + php: [8.4, 8.3, 8.2] + laravel: [12.x, 11.x] stability: [ prefer-lowest, prefer-stable ] name: Tests - P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} - SQLite diff --git a/.junie/guidelines.md b/.junie/guidelines.md new file mode 100644 index 00000000..1c2a40ca --- /dev/null +++ b/.junie/guidelines.md @@ -0,0 +1,71 @@ + +=== foundation rules === + +# Laravel Boost Guidelines + +The Laravel Boost guidelines are specifically curated by Laravel maintainers for this application. These guidelines should be followed closely to enhance the user's satisfaction building Laravel applications. + +## 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.3.22 + + +## 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()`. +- Check for existing components to reuse before writing a new one. + +## Verification Scripts +- Do not create verification scripts or tinker when tests cover that functionality and prove it works. Unit and feature tests are more important. + +## Application Structure & Architecture +- Stick to existing directory structure - don't create new base folders without approval. +- Do not change the application's dependencies without approval. + +## Frontend Bundling +- If the user doesn't see a frontend change reflected in the UI, it could mean they need to run `npm run build`, `npm run dev`, or `composer run dev`. Ask them. + +## Replies +- Be concise in your explanations - focus on what's important rather than explaining obvious details. + +## Documentation Files +- You must only create documentation files if explicitly requested by the user. + + +=== boost rules === + +## Laravel Boost +- Laravel Boost is an MCP server that comes with powerful tools designed specifically for this application. Use them. + +## Artisan +- Use the `list-artisan-commands` tool when you need to call an Artisan command to double check the available parameters. + +## URLs +- Whenever you share a project URL with the user you should use the `get-absolute-url` tool to ensure you're using the correct scheme, domain / IP, and port. + +## Tinker / Debugging +- You should use the `tinker` tool when you need to execute PHP to debug code or query Eloquent models directly. +- Use the `database-query` tool when you only need to read from the database. + +## Reading Browser Logs With the `browser-logs` Tool +- You can read browser logs, errors, and exceptions using the `browser-logs` tool from Boost. +- Only recent browser logs will be useful - ignore old logs. + +## Searching Documentation (Critically Important) +- Boost comes with a powerful `search-docs` tool you should use before any other approaches. This tool automatically passes a list of installed packages and their versions to the remote Boost API, so it returns only version-specific documentation specific for the user's circumstance. You should pass an array of packages to filter on if you know you need docs for particular packages. +- The 'search-docs' tool is perfect for all Laravel related packages, including Laravel, Inertia, Livewire, Filament, Tailwind, Pest, Nova, Nightwatch, etc. +- You must use this tool to search for Laravel-ecosystem documentation before falling back to other approaches. +- Search the documentation before making code changes to ensure we are taking the correct approach. +- Use multiple, broad, simple, topic based queries to start. For example: `['rate limiting', 'routing rate limiting', 'routing']`. +- Do not add package names to queries, package information is already shared. Use `test resource table`, not `filament 4 test resource table`. + +### Available Search Syntax +- You can and should pass multiple queries at once. The most relevant results will be returned first. + +1. Simple Word Searches with auto-stemming - query=authentication - finds 'authenticate' and 'auth' +2. Multiple Words (AND Logic) - query=rate limit - finds knowledge containing both "rate" AND "limit" +3. Quoted Phrases (Exact Position) - query="infinite scroll" - Words must be adjacent and in that order +4. Mixed Queries - query=middleware "rate limit" - "middleware" AND exact phrase "rate limit" +5. Multiple Queries - queries=["authentication", "middleware"] - ANY of these terms + \ No newline at end of file diff --git a/.junie/mcp/mcp.json b/.junie/mcp/mcp.json new file mode 100644 index 00000000..94c1f301 --- /dev/null +++ b/.junie/mcp/mcp.json @@ -0,0 +1,20 @@ +{ + "mcpServers": { + "laravel-boost": { + "command": "php", + "args": [ + "/Users/james/Code/cachet-core/vendor/orchestra/testbench-core/laravel/artisan", + "boost:mcp" + ] + }, + "herd": { + "command": "php", + "args": [ + "/Applications/Herd.app/Contents/Resources/herd-mcp.phar" + ], + "env": { + "SITE_PATH": "/Users/james/Code/cachet-core/vendor/orchestra/testbench-core/laravel" + } + } + } +} \ No newline at end of file diff --git a/.phpintel/78cf68db85cd542bf9de34ad0946f297 b/.phpintel/78cf68db85cd542bf9de34ad0946f297 new file mode 100644 index 00000000..fe999e58 Binary files /dev/null and b/.phpintel/78cf68db85cd542bf9de34ad0946f297 differ diff --git a/.phpintel/f3b825f1c120f0e9e153a8a953ce2797 b/.phpintel/f3b825f1c120f0e9e153a8a953ce2797 new file mode 100644 index 00000000..d0236058 Binary files /dev/null and b/.phpintel/f3b825f1c120f0e9e153a8a953ce2797 differ diff --git a/.phpintel/index b/.phpintel/index new file mode 100644 index 00000000..c24756b7 Binary files /dev/null and b/.phpintel/index differ diff --git a/composer.json b/composer.json index 315283c0..1a5ea0eb 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,5 @@ { - "name": "pingoo31/cachet-3-core", + "name": "cachethq/core", "description": "Cachet core package.", "license": "proprietary", "keywords": [ @@ -25,16 +25,16 @@ "filament/filament": "^4.0", "filament/spatie-laravel-settings-plugin": "^4.0", "guzzlehttp/guzzle": "^7.8", - "illuminate/cache": "^11.35.0", - "illuminate/console": "^11.35.0", - "illuminate/database": "^11.35.0", - "illuminate/events": "^11.35.0", - "illuminate/queue": "^11.35.0", - "illuminate/support": "^11.35.0", + "illuminate/cache": "^11.35.0|^12.0", + "illuminate/console": "^11.35.0|^12.0", + "illuminate/database": "^11.35.0|^12.0", + "illuminate/events": "^11.35.0|^12.0", + "illuminate/queue": "^11.35.0|^12.0", + "illuminate/support": "^11.35.0|^12.0", "laravel/sanctum": "^4.0", - "nesbot/carbon": "^2.70", + "nesbot/carbon": "^2.70|^3.0", "spatie/laravel-data": "^4.11", - "spatie/laravel-query-builder": "^5.5", + "spatie/laravel-query-builder": "^6.3", "spatie/laravel-settings": "^3.2", "spatie/laravel-webhook-server": "^3.8", "timacdonald/json-api": "^1.0.0-beta.4", @@ -45,11 +45,11 @@ "larastan/larastan": "^3.4", "laravel/pail": "^1.1", "laravel/pint": "^1.24", - "orchestra/testbench": "^9.5.1", - "pestphp/pest": "^3.2", - "pestphp/pest-plugin-laravel": "^3.0", - "pestphp/pest-plugin-livewire": "*", - "pestphp/pest-plugin-type-coverage": "^3.3" + "orchestra/testbench": "^9.5.1|^10.0", + "pestphp/pest": "^3.8", + "pestphp/pest-plugin-laravel": "^3.7", + "pestphp/pest-plugin-livewire": "^v3.0", + "pestphp/pest-plugin-type-coverage": "^3.5" }, "minimum-stability": "dev", "prefer-stable": true, diff --git a/src/Http/Controllers/Api/IncidentController.php b/src/Http/Controllers/Api/IncidentController.php index 5ed3cf27..890ce569 100644 --- a/src/Http/Controllers/Api/IncidentController.php +++ b/src/Http/Controllers/Api/IncidentController.php @@ -13,6 +13,8 @@ use Dedoc\Scramble\Attributes\Group; use Dedoc\Scramble\Attributes\QueryParameter; use Illuminate\Database\Eloquent\Builder; +use Illuminate\Http\Request; +use Illuminate\Http\Resources\Json\AnonymousResourceCollection; use Illuminate\Http\Response; use Illuminate\Routing\Controller; use Spatie\QueryBuilder\AllowedFilter; @@ -37,22 +39,24 @@ class IncidentController extends Controller */ #[QueryParameter('per_page', 'How many items to show per page.', type: 'int', default: 15, example: 20)] #[QueryParameter('page', 'Which page to show.', type: 'int', example: 2)] - public function index() + public function index(Request $request) { $query = Incident::query() - ->when(! request('sort'), function (Builder $builder) { + ->when(!$request->has('sort'), function (Builder $builder) { $builder->orderByDesc('created_at'); }); - $incidents = QueryBuilder::for($query) + $incidents = QueryBuilder::for(Incident::query()) ->allowedIncludes(self::ALLOWED_INCLUDES) ->allowedFilters([ 'name', AllowedFilter::exact('status'), - 'occurred_at', + AllowedFilter::scope('occurs_after'), + AllowedFilter::scope('occurs_before'), + AllowedFilter::scope('occurs_on'), ]) ->allowedSorts(['name', 'status', 'id']) - ->simplePaginate(request('per_page', 15)); + ->simplePaginate($request->input('per_page', 15)); return IncidentResource::collection($incidents); } diff --git a/src/Models/Incident.php b/src/Models/Incident.php index 4e624807..17c341b1 100644 --- a/src/Models/Incident.php +++ b/src/Models/Incident.php @@ -171,6 +171,21 @@ public function scopeStickied(Builder $query): void $query->where('stickied', true); } + public function scopeOccursAfter(Builder $query, $date): void + { + $query->where('occurred_at', '>=', $date); + } + + public function scopeOccursBefore(Builder $query, $date): void + { + $query->where('occurred_at', '<=', $date); + } + + public function scopeOccursOn(Builder $query, $date): void + { + $query->whereDate('occurred_at', $date); + } + /** * @return Attribute */ diff --git a/tests/Feature/Api/IncidentTest.php b/tests/Feature/Api/IncidentTest.php index 9ddf7926..76c8d1db 100644 --- a/tests/Feature/Api/IncidentTest.php +++ b/tests/Feature/Api/IncidentTest.php @@ -10,6 +10,7 @@ use function Pest\Laravel\getJson; use function Pest\Laravel\postJson; use function Pest\Laravel\putJson; +use function Pest\Laravel\withoutExceptionHandling; it('can list incidents', function () { Incident::factory(2)->create(); @@ -144,19 +145,26 @@ it('can filter incidents by occurred at date', function () { Incident::factory(20)->create([ - 'occurred_at' => '2019-01-01', + 'occurred_at' => '2019-01-01 00:00:00', ]); $incident = Incident::factory()->create([ - 'occurred_at' => '2023-01-01', + 'occurred_at' => '2025-01-01 00:00:00', ]); - $query = http_build_query([ + withoutExceptionHandling(); + + dd(route('cachet.api.incidents.index', [ 'filter' => [ - 'occurred_at' => '2023-01-01', + 'occurs_after' => '2024-12-31', ], - ]); + ])); - $response = getJson('/status/api/incidents?'.$query); + $response = getJson(route('cachet.api.incidents.index', [ + 'filter' => [ + 'occurs_after' => '2024-12-31', + ], + ])) + ->assertOk(); $response->assertJsonCount(1, 'data'); $response->assertJsonPath('data.0.attributes.id', $incident->id);