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);