mirror of
https://github.com/usetrmnl/byos_laravel.git
synced 2026-01-13 15:07:49 +00:00
Compare commits
2 commits
e12f9bb91c
...
6d7968a7b0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d7968a7b0 | ||
|
|
7434911275 |
13 changed files with 763 additions and 165 deletions
|
|
@ -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.3.24
|
||||
- php - 8.4.12
|
||||
- laravel/framework (LARAVEL) - v12
|
||||
- laravel/prompts (PROMPTS) - v0
|
||||
- livewire/flux (FLUXUI_FREE) - v2
|
||||
|
|
@ -19,7 +19,7 @@ This application is a Laravel application and its main Laravel ecosystems packag
|
|||
- livewire/volt (VOLT) - v1
|
||||
- larastan/larastan (LARASTAN) - v3
|
||||
- laravel/pint (PINT) - v1
|
||||
- pestphp/pest (PEST) - v3
|
||||
- pestphp/pest (PEST) - v4
|
||||
- tailwindcss (TAILWINDCSS) - v4
|
||||
|
||||
|
||||
|
|
@ -465,6 +465,53 @@ it('has emails', function (string $email) {
|
|||
</code-snippet>
|
||||
|
||||
|
||||
=== pest/v4 rules ===
|
||||
|
||||
## Pest 4
|
||||
|
||||
- Pest v4 is a huge upgrade to Pest and offers: browser testing, smoke testing, visual regression testing, test sharding, and faster type coverage.
|
||||
- Browser testing is incredibly powerful and useful for this project.
|
||||
- Browser tests should live in `tests/Browser/`.
|
||||
- Use the `search-docs` tool for detailed guidance on utilizing these features.
|
||||
|
||||
### Browser Testing
|
||||
- You can use Laravel features like `Event::fake()`, `assertAuthenticated()`, and model factories within Pest v4 browser tests, as well as `RefreshDatabase` (when needed) to ensure a clean state for each test.
|
||||
- Interact with the page (click, type, scroll, select, submit, drag-and-drop, touch gestures, etc.) when appropriate to complete the test.
|
||||
- If requested, test on multiple browsers (Chrome, Firefox, Safari).
|
||||
- If requested, test on different devices and viewports (like iPhone 14 Pro, tablets, or custom breakpoints).
|
||||
- Switch color schemes (light/dark mode) when appropriate.
|
||||
- Take screenshots or pause tests for debugging when appropriate.
|
||||
|
||||
### Example Tests
|
||||
|
||||
<code-snippet name="Pest Browser Test Example" lang="php">
|
||||
it('may reset the password', function () {
|
||||
Notification::fake();
|
||||
|
||||
$this->actingAs(User::factory()->create());
|
||||
|
||||
$page = visit('/sign-in'); // Visit on a real browser...
|
||||
|
||||
$page->assertSee('Sign In')
|
||||
->assertNoJavascriptErrors() // or ->assertNoConsoleLogs()
|
||||
->click('Forgot Password?')
|
||||
->fill('email', 'nuno@laravel.com')
|
||||
->click('Send Reset Link')
|
||||
->assertSee('We have emailed your password reset link!')
|
||||
|
||||
Notification::assertSent(ResetPassword::class);
|
||||
});
|
||||
</code-snippet>
|
||||
|
||||
|
||||
|
||||
<code-snippet name="Pest Smoke Testing Example" lang="php">
|
||||
$pages = visit(['/', '/about', '/contact']);
|
||||
|
||||
$pages->assertNoJavascriptErrors()->assertNoConsoleLogs();
|
||||
</code-snippet>
|
||||
|
||||
|
||||
=== tailwindcss/core rules ===
|
||||
|
||||
## Tailwind Core
|
||||
|
|
|
|||
51
.github/copilot-instructions.md
vendored
51
.github/copilot-instructions.md
vendored
|
|
@ -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.3.24
|
||||
- php - 8.4.12
|
||||
- laravel/framework (LARAVEL) - v12
|
||||
- laravel/prompts (PROMPTS) - v0
|
||||
- livewire/flux (FLUXUI_FREE) - v2
|
||||
|
|
@ -16,7 +16,7 @@ This application is a Laravel application and its main Laravel ecosystems packag
|
|||
- livewire/volt (VOLT) - v1
|
||||
- larastan/larastan (LARASTAN) - v3
|
||||
- laravel/pint (PINT) - v1
|
||||
- pestphp/pest (PEST) - v3
|
||||
- pestphp/pest (PEST) - v4
|
||||
- tailwindcss (TAILWINDCSS) - v4
|
||||
|
||||
|
||||
|
|
@ -462,6 +462,53 @@ it('has emails', function (string $email) {
|
|||
</code-snippet>
|
||||
|
||||
|
||||
=== pest/v4 rules ===
|
||||
|
||||
## Pest 4
|
||||
|
||||
- Pest v4 is a huge upgrade to Pest and offers: browser testing, smoke testing, visual regression testing, test sharding, and faster type coverage.
|
||||
- Browser testing is incredibly powerful and useful for this project.
|
||||
- Browser tests should live in `tests/Browser/`.
|
||||
- Use the `search-docs` tool for detailed guidance on utilizing these features.
|
||||
|
||||
### Browser Testing
|
||||
- You can use Laravel features like `Event::fake()`, `assertAuthenticated()`, and model factories within Pest v4 browser tests, as well as `RefreshDatabase` (when needed) to ensure a clean state for each test.
|
||||
- Interact with the page (click, type, scroll, select, submit, drag-and-drop, touch gestures, etc.) when appropriate to complete the test.
|
||||
- If requested, test on multiple browsers (Chrome, Firefox, Safari).
|
||||
- If requested, test on different devices and viewports (like iPhone 14 Pro, tablets, or custom breakpoints).
|
||||
- Switch color schemes (light/dark mode) when appropriate.
|
||||
- Take screenshots or pause tests for debugging when appropriate.
|
||||
|
||||
### Example Tests
|
||||
|
||||
<code-snippet name="Pest Browser Test Example" lang="php">
|
||||
it('may reset the password', function () {
|
||||
Notification::fake();
|
||||
|
||||
$this->actingAs(User::factory()->create());
|
||||
|
||||
$page = visit('/sign-in'); // Visit on a real browser...
|
||||
|
||||
$page->assertSee('Sign In')
|
||||
->assertNoJavascriptErrors() // or ->assertNoConsoleLogs()
|
||||
->click('Forgot Password?')
|
||||
->fill('email', 'nuno@laravel.com')
|
||||
->click('Send Reset Link')
|
||||
->assertSee('We have emailed your password reset link!')
|
||||
|
||||
Notification::assertSent(ResetPassword::class);
|
||||
});
|
||||
</code-snippet>
|
||||
|
||||
|
||||
|
||||
<code-snippet name="Pest Smoke Testing Example" lang="php">
|
||||
$pages = visit(['/', '/about', '/contact']);
|
||||
|
||||
$pages->assertNoJavascriptErrors()->assertNoConsoleLogs();
|
||||
</code-snippet>
|
||||
|
||||
|
||||
=== tailwindcss/core rules ===
|
||||
|
||||
## Tailwind Core
|
||||
|
|
|
|||
|
|
@ -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.3.24
|
||||
- php - 8.4.12
|
||||
- laravel/framework (LARAVEL) - v12
|
||||
- laravel/prompts (PROMPTS) - v0
|
||||
- livewire/flux (FLUXUI_FREE) - v2
|
||||
|
|
@ -16,7 +16,7 @@ This application is a Laravel application and its main Laravel ecosystems packag
|
|||
- livewire/volt (VOLT) - v1
|
||||
- larastan/larastan (LARASTAN) - v3
|
||||
- laravel/pint (PINT) - v1
|
||||
- pestphp/pest (PEST) - v3
|
||||
- pestphp/pest (PEST) - v4
|
||||
- tailwindcss (TAILWINDCSS) - v4
|
||||
|
||||
|
||||
|
|
@ -462,6 +462,53 @@ it('has emails', function (string $email) {
|
|||
</code-snippet>
|
||||
|
||||
|
||||
=== pest/v4 rules ===
|
||||
|
||||
## Pest 4
|
||||
|
||||
- Pest v4 is a huge upgrade to Pest and offers: browser testing, smoke testing, visual regression testing, test sharding, and faster type coverage.
|
||||
- Browser testing is incredibly powerful and useful for this project.
|
||||
- Browser tests should live in `tests/Browser/`.
|
||||
- Use the `search-docs` tool for detailed guidance on utilizing these features.
|
||||
|
||||
### Browser Testing
|
||||
- You can use Laravel features like `Event::fake()`, `assertAuthenticated()`, and model factories within Pest v4 browser tests, as well as `RefreshDatabase` (when needed) to ensure a clean state for each test.
|
||||
- Interact with the page (click, type, scroll, select, submit, drag-and-drop, touch gestures, etc.) when appropriate to complete the test.
|
||||
- If requested, test on multiple browsers (Chrome, Firefox, Safari).
|
||||
- If requested, test on different devices and viewports (like iPhone 14 Pro, tablets, or custom breakpoints).
|
||||
- Switch color schemes (light/dark mode) when appropriate.
|
||||
- Take screenshots or pause tests for debugging when appropriate.
|
||||
|
||||
### Example Tests
|
||||
|
||||
<code-snippet name="Pest Browser Test Example" lang="php">
|
||||
it('may reset the password', function () {
|
||||
Notification::fake();
|
||||
|
||||
$this->actingAs(User::factory()->create());
|
||||
|
||||
$page = visit('/sign-in'); // Visit on a real browser...
|
||||
|
||||
$page->assertSee('Sign In')
|
||||
->assertNoJavascriptErrors() // or ->assertNoConsoleLogs()
|
||||
->click('Forgot Password?')
|
||||
->fill('email', 'nuno@laravel.com')
|
||||
->click('Send Reset Link')
|
||||
->assertSee('We have emailed your password reset link!')
|
||||
|
||||
Notification::assertSent(ResetPassword::class);
|
||||
});
|
||||
</code-snippet>
|
||||
|
||||
|
||||
|
||||
<code-snippet name="Pest Smoke Testing Example" lang="php">
|
||||
$pages = visit(['/', '/about', '/contact']);
|
||||
|
||||
$pages->assertNoJavascriptErrors()->assertNoConsoleLogs();
|
||||
</code-snippet>
|
||||
|
||||
|
||||
=== tailwindcss/core rules ===
|
||||
|
||||
## Tailwind Core
|
||||
|
|
|
|||
51
CLAUDE.md
51
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.3.24
|
||||
- php - 8.4.12
|
||||
- laravel/framework (LARAVEL) - v12
|
||||
- laravel/prompts (PROMPTS) - v0
|
||||
- livewire/flux (FLUXUI_FREE) - v2
|
||||
|
|
@ -16,7 +16,7 @@ This application is a Laravel application and its main Laravel ecosystems packag
|
|||
- livewire/volt (VOLT) - v1
|
||||
- larastan/larastan (LARASTAN) - v3
|
||||
- laravel/pint (PINT) - v1
|
||||
- pestphp/pest (PEST) - v3
|
||||
- pestphp/pest (PEST) - v4
|
||||
- tailwindcss (TAILWINDCSS) - v4
|
||||
|
||||
|
||||
|
|
@ -462,6 +462,53 @@ it('has emails', function (string $email) {
|
|||
</code-snippet>
|
||||
|
||||
|
||||
=== pest/v4 rules ===
|
||||
|
||||
## Pest 4
|
||||
|
||||
- Pest v4 is a huge upgrade to Pest and offers: browser testing, smoke testing, visual regression testing, test sharding, and faster type coverage.
|
||||
- Browser testing is incredibly powerful and useful for this project.
|
||||
- Browser tests should live in `tests/Browser/`.
|
||||
- Use the `search-docs` tool for detailed guidance on utilizing these features.
|
||||
|
||||
### Browser Testing
|
||||
- You can use Laravel features like `Event::fake()`, `assertAuthenticated()`, and model factories within Pest v4 browser tests, as well as `RefreshDatabase` (when needed) to ensure a clean state for each test.
|
||||
- Interact with the page (click, type, scroll, select, submit, drag-and-drop, touch gestures, etc.) when appropriate to complete the test.
|
||||
- If requested, test on multiple browsers (Chrome, Firefox, Safari).
|
||||
- If requested, test on different devices and viewports (like iPhone 14 Pro, tablets, or custom breakpoints).
|
||||
- Switch color schemes (light/dark mode) when appropriate.
|
||||
- Take screenshots or pause tests for debugging when appropriate.
|
||||
|
||||
### Example Tests
|
||||
|
||||
<code-snippet name="Pest Browser Test Example" lang="php">
|
||||
it('may reset the password', function () {
|
||||
Notification::fake();
|
||||
|
||||
$this->actingAs(User::factory()->create());
|
||||
|
||||
$page = visit('/sign-in'); // Visit on a real browser...
|
||||
|
||||
$page->assertSee('Sign In')
|
||||
->assertNoJavascriptErrors() // or ->assertNoConsoleLogs()
|
||||
->click('Forgot Password?')
|
||||
->fill('email', 'nuno@laravel.com')
|
||||
->click('Send Reset Link')
|
||||
->assertSee('We have emailed your password reset link!')
|
||||
|
||||
Notification::assertSent(ResetPassword::class);
|
||||
});
|
||||
</code-snippet>
|
||||
|
||||
|
||||
|
||||
<code-snippet name="Pest Smoke Testing Example" lang="php">
|
||||
$pages = visit(['/', '/about', '/contact']);
|
||||
|
||||
$pages->assertNoJavascriptErrors()->assertNoConsoleLogs();
|
||||
</code-snippet>
|
||||
|
||||
|
||||
=== tailwindcss/core rules ===
|
||||
|
||||
## Tailwind Core
|
||||
|
|
|
|||
|
|
@ -236,11 +236,11 @@ class Plugin extends Model
|
|||
$template = preg_replace_callback(
|
||||
'/{%\s*for\s+(\w+)\s+in\s+([^|]+)\s*\|\s*([^}]+)%}/',
|
||||
function ($matches) {
|
||||
$variableName = trim($matches[1]);
|
||||
$collection = trim($matches[2]);
|
||||
$filter = trim($matches[3]);
|
||||
$tempVarName = '_temp_' . uniqid();
|
||||
|
||||
$variableName = mb_trim($matches[1]);
|
||||
$collection = mb_trim($matches[2]);
|
||||
$filter = mb_trim($matches[3]);
|
||||
$tempVarName = '_temp_'.uniqid();
|
||||
|
||||
return "{% assign {$tempVarName} = {$collection} | {$filter} %}{% for {$variableName} in {$tempVarName} %}";
|
||||
},
|
||||
$template
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use App\Models\User;
|
|||
use Exception;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use RecursiveDirectoryIterator;
|
||||
use RecursiveIteratorIterator;
|
||||
|
|
@ -48,6 +49,130 @@ class PluginImportService
|
|||
// Find the required files (settings.yml and full.liquid/full.blade.php)
|
||||
$filePaths = $this->findRequiredFiles($tempDir);
|
||||
|
||||
// Validate that we found the required files
|
||||
if (! $filePaths['settingsYamlPath'] || ! $filePaths['fullLiquidPath']) {
|
||||
throw new Exception('Invalid ZIP structure. Required files settings.yml and full.liquid are missing.'); // full.blade.php
|
||||
}
|
||||
|
||||
// Parse settings.yml
|
||||
$settingsYaml = File::get($filePaths['settingsYamlPath']);
|
||||
$settings = Yaml::parse($settingsYaml);
|
||||
|
||||
// Read full.liquid content
|
||||
$fullLiquid = File::get($filePaths['fullLiquidPath']);
|
||||
|
||||
// Prepend shared.liquid content if available
|
||||
if ($filePaths['sharedLiquidPath'] && File::exists($filePaths['sharedLiquidPath'])) {
|
||||
$sharedLiquid = File::get($filePaths['sharedLiquidPath']);
|
||||
$fullLiquid = $sharedLiquid."\n".$fullLiquid;
|
||||
}
|
||||
|
||||
$fullLiquid = '<div class="view view--{{ size }}">'."\n".$fullLiquid."\n".'</div>';
|
||||
|
||||
// Check if the file ends with .liquid to set markup language
|
||||
$markupLanguage = 'blade';
|
||||
if (pathinfo($filePaths['fullLiquidPath'], PATHINFO_EXTENSION) === 'liquid') {
|
||||
$markupLanguage = 'liquid';
|
||||
}
|
||||
|
||||
// Ensure custom_fields is properly formatted
|
||||
if (! isset($settings['custom_fields']) || ! is_array($settings['custom_fields'])) {
|
||||
$settings['custom_fields'] = [];
|
||||
}
|
||||
|
||||
// Create configuration template with the custom fields
|
||||
$configurationTemplate = [
|
||||
'custom_fields' => $settings['custom_fields'],
|
||||
];
|
||||
|
||||
$plugin_updated = isset($settings['id'])
|
||||
&& Plugin::where('user_id', $user->id)->where('trmnlp_id', $settings['id'])->exists();
|
||||
// Create a new plugin
|
||||
$plugin = Plugin::updateOrCreate(
|
||||
[
|
||||
'user_id' => $user->id, 'trmnlp_id' => $settings['id'] ?? Uuid::v7(),
|
||||
],
|
||||
[
|
||||
'user_id' => $user->id,
|
||||
'name' => $settings['name'] ?? 'Imported Plugin',
|
||||
'trmnlp_id' => $settings['id'] ?? Uuid::v7(),
|
||||
'data_stale_minutes' => $settings['refresh_interval'] ?? 15,
|
||||
'data_strategy' => $settings['strategy'] ?? 'static',
|
||||
'polling_url' => $settings['polling_url'] ?? null,
|
||||
'polling_verb' => $settings['polling_verb'] ?? 'get',
|
||||
'polling_header' => isset($settings['polling_headers'])
|
||||
? str_replace('=', ':', $settings['polling_headers'])
|
||||
: null,
|
||||
'polling_body' => $settings['polling_body'] ?? null,
|
||||
'markup_language' => $markupLanguage,
|
||||
'render_markup' => $fullLiquid,
|
||||
'configuration_template' => $configurationTemplate,
|
||||
'data_payload' => json_decode($settings['static_data'] ?? '{}', true),
|
||||
]);
|
||||
|
||||
if (! $plugin_updated) {
|
||||
// Extract default values from custom_fields and populate configuration
|
||||
$configuration = [];
|
||||
foreach ($settings['custom_fields'] as $field) {
|
||||
if (isset($field['keyname']) && isset($field['default'])) {
|
||||
$configuration[$field['keyname']] = $field['default'];
|
||||
}
|
||||
}
|
||||
// set only if plugin is new
|
||||
$plugin->update([
|
||||
'configuration' => $configuration,
|
||||
]);
|
||||
}
|
||||
$plugin['trmnlp_yaml'] = $settingsYaml;
|
||||
|
||||
return $plugin;
|
||||
|
||||
} finally {
|
||||
// Clean up temporary directory
|
||||
Storage::deleteDirectory($tempDirName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Import a plugin from a ZIP URL
|
||||
*
|
||||
* @param string $zipUrl The URL to the ZIP file
|
||||
* @param User $user The user importing the plugin
|
||||
* @return Plugin The created plugin instance
|
||||
*
|
||||
* @throws Exception If the ZIP file is invalid or required files are missing
|
||||
*/
|
||||
public function importFromUrl(string $zipUrl, User $user): Plugin
|
||||
{
|
||||
// Download the ZIP file
|
||||
$response = Http::timeout(60)->get($zipUrl);
|
||||
|
||||
if (! $response->successful()) {
|
||||
throw new Exception('Could not download the ZIP file from the provided URL.');
|
||||
}
|
||||
|
||||
// Create a temporary file
|
||||
$tempDirName = 'temp/'.uniqid('plugin_import_', true);
|
||||
Storage::makeDirectory($tempDirName);
|
||||
$tempDir = Storage::path($tempDirName);
|
||||
$zipPath = $tempDir.'/plugin.zip';
|
||||
|
||||
// Save the downloaded content to a temporary file
|
||||
File::put($zipPath, $response->body());
|
||||
|
||||
try {
|
||||
// Extract the ZIP file using ZipArchive
|
||||
$zip = new ZipArchive();
|
||||
if ($zip->open($zipPath) !== true) {
|
||||
throw new Exception('Could not open the downloaded ZIP file.');
|
||||
}
|
||||
|
||||
$zip->extractTo($tempDir);
|
||||
$zip->close();
|
||||
|
||||
// Find the required files (settings.yml and full.liquid/full.blade.php)
|
||||
$filePaths = $this->findRequiredFiles($tempDir);
|
||||
|
||||
// Validate that we found the required files
|
||||
if (! $filePaths['settingsYamlPath'] || ! $filePaths['fullLiquidPath']) {
|
||||
throw new Exception('Invalid ZIP structure. Required files settings.yml and full.liquid/full.blade.php are missing.');
|
||||
|
|
|
|||
306
composer.lock
generated
306
composer.lock
generated
|
|
@ -62,16 +62,16 @@
|
|||
},
|
||||
{
|
||||
"name": "aws/aws-sdk-php",
|
||||
"version": "3.356.5",
|
||||
"version": "3.356.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/aws/aws-sdk-php.git",
|
||||
"reference": "5872ccb5100c4afb0dae3db0bd46636f63ae8147"
|
||||
"reference": "3efa8c62c11fedb17b90f60b2d3a9f815b406e63"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/5872ccb5100c4afb0dae3db0bd46636f63ae8147",
|
||||
"reference": "5872ccb5100c4afb0dae3db0bd46636f63ae8147",
|
||||
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/3efa8c62c11fedb17b90f60b2d3a9f815b406e63",
|
||||
"reference": "3efa8c62c11fedb17b90f60b2d3a9f815b406e63",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -153,9 +153,9 @@
|
|||
"support": {
|
||||
"forum": "https://github.com/aws/aws-sdk-php/discussions",
|
||||
"issues": "https://github.com/aws/aws-sdk-php/issues",
|
||||
"source": "https://github.com/aws/aws-sdk-php/tree/3.356.5"
|
||||
"source": "https://github.com/aws/aws-sdk-php/tree/3.356.8"
|
||||
},
|
||||
"time": "2025-08-26T18:05:04+00:00"
|
||||
"time": "2025-08-29T18:06:18+00:00"
|
||||
},
|
||||
{
|
||||
"name": "bnussbau/laravel-trmnl-blade",
|
||||
|
|
@ -1691,16 +1691,16 @@
|
|||
},
|
||||
{
|
||||
"name": "laravel/framework",
|
||||
"version": "v12.26.2",
|
||||
"version": "v12.26.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/framework.git",
|
||||
"reference": "56c5fc46cfb1005d0aaa82c7592d63edb776a787"
|
||||
"reference": "085a367a32ba86fcfa647bfc796098ae6f795b09"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/56c5fc46cfb1005d0aaa82c7592d63edb776a787",
|
||||
"reference": "56c5fc46cfb1005d0aaa82c7592d63edb776a787",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/085a367a32ba86fcfa647bfc796098ae6f795b09",
|
||||
"reference": "085a367a32ba86fcfa647bfc796098ae6f795b09",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -1740,8 +1740,8 @@
|
|||
"symfony/http-kernel": "^7.2.0",
|
||||
"symfony/mailer": "^7.2.0",
|
||||
"symfony/mime": "^7.2.0",
|
||||
"symfony/polyfill-php83": "^1.31",
|
||||
"symfony/polyfill-php84": "^1.31",
|
||||
"symfony/polyfill-php83": "^1.33",
|
||||
"symfony/polyfill-php84": "^1.33",
|
||||
"symfony/polyfill-php85": "^1.33",
|
||||
"symfony/process": "^7.2.0",
|
||||
"symfony/routing": "^7.2.0",
|
||||
|
|
@ -1810,7 +1810,7 @@
|
|||
"league/flysystem-read-only": "^3.25.1",
|
||||
"league/flysystem-sftp-v3": "^3.25.1",
|
||||
"mockery/mockery": "^1.6.10",
|
||||
"orchestra/testbench-core": "^10.6.0",
|
||||
"orchestra/testbench-core": "^10.6.3",
|
||||
"pda/pheanstalk": "^5.0.6|^7.0.0",
|
||||
"php-http/discovery": "^1.15",
|
||||
"phpstan/phpstan": "^2.0",
|
||||
|
|
@ -1904,7 +1904,7 @@
|
|||
"issues": "https://github.com/laravel/framework/issues",
|
||||
"source": "https://github.com/laravel/framework"
|
||||
},
|
||||
"time": "2025-08-26T18:04:56+00:00"
|
||||
"time": "2025-08-29T14:15:53+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/prompts",
|
||||
|
|
@ -4973,16 +4973,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v7.3.2",
|
||||
"version": "v7.3.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "5f360ebc65c55265a74d23d7fe27f957870158a1"
|
||||
"reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/5f360ebc65c55265a74d23d7fe27f957870158a1",
|
||||
"reference": "5f360ebc65c55265a74d23d7fe27f957870158a1",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7",
|
||||
"reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -5047,7 +5047,7 @@
|
|||
"terminal"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/console/tree/v7.3.2"
|
||||
"source": "https://github.com/symfony/console/tree/v7.3.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -5067,7 +5067,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-07-30T17:13:41+00:00"
|
||||
"time": "2025-08-25T06:35:40+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/css-selector",
|
||||
|
|
@ -5284,16 +5284,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/event-dispatcher",
|
||||
"version": "v7.3.0",
|
||||
"version": "v7.3.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/event-dispatcher.git",
|
||||
"reference": "497f73ac996a598c92409b44ac43b6690c4f666d"
|
||||
"reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/497f73ac996a598c92409b44ac43b6690c4f666d",
|
||||
"reference": "497f73ac996a598c92409b44ac43b6690c4f666d",
|
||||
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b7dc69e71de420ac04bc9ab830cf3ffebba48191",
|
||||
"reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -5344,7 +5344,7 @@
|
|||
"description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/event-dispatcher/tree/v7.3.0"
|
||||
"source": "https://github.com/symfony/event-dispatcher/tree/v7.3.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -5355,12 +5355,16 @@
|
|||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nicolas-grekas",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-04-22T09:11:45+00:00"
|
||||
"time": "2025-08-13T11:49:31+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/event-dispatcher-contracts",
|
||||
|
|
@ -5508,16 +5512,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/http-foundation",
|
||||
"version": "v7.3.2",
|
||||
"version": "v7.3.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-foundation.git",
|
||||
"reference": "6877c122b3a6cc3695849622720054f6e6fa5fa6"
|
||||
"reference": "7475561ec27020196c49bb7c4f178d33d7d3dc00"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/6877c122b3a6cc3695849622720054f6e6fa5fa6",
|
||||
"reference": "6877c122b3a6cc3695849622720054f6e6fa5fa6",
|
||||
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/7475561ec27020196c49bb7c4f178d33d7d3dc00",
|
||||
"reference": "7475561ec27020196c49bb7c4f178d33d7d3dc00",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -5567,7 +5571,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.2"
|
||||
"source": "https://github.com/symfony/http-foundation/tree/v7.3.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -5587,20 +5591,20 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-07-10T08:47:49+00:00"
|
||||
"time": "2025-08-20T08:04:18+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-kernel",
|
||||
"version": "v7.3.2",
|
||||
"version": "v7.3.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-kernel.git",
|
||||
"reference": "6ecc895559ec0097e221ed2fd5eb44d5fede083c"
|
||||
"reference": "72c304de37e1a1cec6d5d12b81187ebd4850a17b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/6ecc895559ec0097e221ed2fd5eb44d5fede083c",
|
||||
"reference": "6ecc895559ec0097e221ed2fd5eb44d5fede083c",
|
||||
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/72c304de37e1a1cec6d5d12b81187ebd4850a17b",
|
||||
"reference": "72c304de37e1a1cec6d5d12b81187ebd4850a17b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -5685,7 +5689,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.2"
|
||||
"source": "https://github.com/symfony/http-kernel/tree/v7.3.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -5705,20 +5709,20 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-07-31T10:45:04+00:00"
|
||||
"time": "2025-08-29T08:23:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/mailer",
|
||||
"version": "v7.3.2",
|
||||
"version": "v7.3.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/mailer.git",
|
||||
"reference": "d43e84d9522345f96ad6283d5dfccc8c1cfc299b"
|
||||
"reference": "a32f3f45f1990db8c4341d5122a7d3a381c7e575"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/mailer/zipball/d43e84d9522345f96ad6283d5dfccc8c1cfc299b",
|
||||
"reference": "d43e84d9522345f96ad6283d5dfccc8c1cfc299b",
|
||||
"url": "https://api.github.com/repos/symfony/mailer/zipball/a32f3f45f1990db8c4341d5122a7d3a381c7e575",
|
||||
"reference": "a32f3f45f1990db8c4341d5122a7d3a381c7e575",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -5769,7 +5773,7 @@
|
|||
"description": "Helps sending emails",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/mailer/tree/v7.3.2"
|
||||
"source": "https://github.com/symfony/mailer/tree/v7.3.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -5789,7 +5793,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-07-15T11:36:08+00:00"
|
||||
"time": "2025-08-13T11:49:31+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/mime",
|
||||
|
|
@ -6710,16 +6714,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/process",
|
||||
"version": "v7.3.0",
|
||||
"version": "v7.3.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/process.git",
|
||||
"reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af"
|
||||
"reference": "32241012d521e2e8a9d713adb0812bb773b907f1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/40c295f2deb408d5e9d2d32b8ba1dd61e36f05af",
|
||||
"reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/32241012d521e2e8a9d713adb0812bb773b907f1",
|
||||
"reference": "32241012d521e2e8a9d713adb0812bb773b907f1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -6751,7 +6755,7 @@
|
|||
"description": "Executes commands in sub-processes",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/process/tree/v7.3.0"
|
||||
"source": "https://github.com/symfony/process/tree/v7.3.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -6762,12 +6766,16 @@
|
|||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nicolas-grekas",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-04-17T09:11:12+00:00"
|
||||
"time": "2025-08-18T09:42:54+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/routing",
|
||||
|
|
@ -6939,16 +6947,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/string",
|
||||
"version": "v7.3.2",
|
||||
"version": "v7.3.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/string.git",
|
||||
"reference": "42f505aff654e62ac7ac2ce21033818297ca89ca"
|
||||
"reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/string/zipball/42f505aff654e62ac7ac2ce21033818297ca89ca",
|
||||
"reference": "42f505aff654e62ac7ac2ce21033818297ca89ca",
|
||||
"url": "https://api.github.com/repos/symfony/string/zipball/17a426cce5fd1f0901fefa9b2a490d0038fd3c9c",
|
||||
"reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -7006,7 +7014,7 @@
|
|||
"utf8"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/string/tree/v7.3.2"
|
||||
"source": "https://github.com/symfony/string/tree/v7.3.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -7026,20 +7034,20 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-07-10T08:47:49+00:00"
|
||||
"time": "2025-08-25T06:35:40+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/translation",
|
||||
"version": "v7.3.2",
|
||||
"version": "v7.3.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/translation.git",
|
||||
"reference": "81b48f4daa96272efcce9c7a6c4b58e629df3c90"
|
||||
"reference": "e0837b4cbcef63c754d89a4806575cada743a38d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/translation/zipball/81b48f4daa96272efcce9c7a6c4b58e629df3c90",
|
||||
"reference": "81b48f4daa96272efcce9c7a6c4b58e629df3c90",
|
||||
"url": "https://api.github.com/repos/symfony/translation/zipball/e0837b4cbcef63c754d89a4806575cada743a38d",
|
||||
"reference": "e0837b4cbcef63c754d89a4806575cada743a38d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -7106,7 +7114,7 @@
|
|||
"description": "Provides tools to internationalize your application",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/translation/tree/v7.3.2"
|
||||
"source": "https://github.com/symfony/translation/tree/v7.3.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -7126,7 +7134,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-07-30T17:31:46+00:00"
|
||||
"time": "2025-08-01T21:02:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/translation-contracts",
|
||||
|
|
@ -7282,16 +7290,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/var-dumper",
|
||||
"version": "v7.3.2",
|
||||
"version": "v7.3.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/var-dumper.git",
|
||||
"reference": "53205bea27450dc5c65377518b3275e126d45e75"
|
||||
"reference": "34d8d4c4b9597347306d1ec8eb4e1319b1e6986f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/53205bea27450dc5c65377518b3275e126d45e75",
|
||||
"reference": "53205bea27450dc5c65377518b3275e126d45e75",
|
||||
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/34d8d4c4b9597347306d1ec8eb4e1319b1e6986f",
|
||||
"reference": "34d8d4c4b9597347306d1ec8eb4e1319b1e6986f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -7345,7 +7353,7 @@
|
|||
"dump"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/var-dumper/tree/v7.3.2"
|
||||
"source": "https://github.com/symfony/var-dumper/tree/v7.3.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -7365,20 +7373,20 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-07-29T20:02:46+00:00"
|
||||
"time": "2025-08-13T11:49:31+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/var-exporter",
|
||||
"version": "v7.3.2",
|
||||
"version": "v7.3.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/var-exporter.git",
|
||||
"reference": "05b3e90654c097817325d6abd284f7938b05f467"
|
||||
"reference": "d4dfcd2a822cbedd7612eb6fbd260e46f87b7137"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/var-exporter/zipball/05b3e90654c097817325d6abd284f7938b05f467",
|
||||
"reference": "05b3e90654c097817325d6abd284f7938b05f467",
|
||||
"url": "https://api.github.com/repos/symfony/var-exporter/zipball/d4dfcd2a822cbedd7612eb6fbd260e46f87b7137",
|
||||
"reference": "d4dfcd2a822cbedd7612eb6fbd260e46f87b7137",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -7426,7 +7434,7 @@
|
|||
"serialize"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/var-exporter/tree/v7.3.2"
|
||||
"source": "https://github.com/symfony/var-exporter/tree/v7.3.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -7446,20 +7454,20 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-07-10T08:47:49+00:00"
|
||||
"time": "2025-08-18T13:10:53+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/yaml",
|
||||
"version": "v7.3.2",
|
||||
"version": "v7.3.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/yaml.git",
|
||||
"reference": "b8d7d868da9eb0919e99c8830431ea087d6aae30"
|
||||
"reference": "d4f4a66866fe2451f61296924767280ab5732d9d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/b8d7d868da9eb0919e99c8830431ea087d6aae30",
|
||||
"reference": "b8d7d868da9eb0919e99c8830431ea087d6aae30",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/d4f4a66866fe2451f61296924767280ab5732d9d",
|
||||
"reference": "d4f4a66866fe2451f61296924767280ab5732d9d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -7502,7 +7510,7 @@
|
|||
"description": "Loads and dumps YAML files",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/yaml/tree/v7.3.2"
|
||||
"source": "https://github.com/symfony/yaml/tree/v7.3.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -7522,7 +7530,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-07-10T08:47:49+00:00"
|
||||
"time": "2025-08-27T11:34:33+00:00"
|
||||
},
|
||||
{
|
||||
"name": "tijsverkoyen/css-to-inline-styles",
|
||||
|
|
@ -7885,16 +7893,16 @@
|
|||
"packages-dev": [
|
||||
{
|
||||
"name": "brianium/paratest",
|
||||
"version": "v7.11.2",
|
||||
"version": "v7.12.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/paratestphp/paratest.git",
|
||||
"reference": "521aa381c212816d0dc2f04f1532a5831969cb5e"
|
||||
"reference": "6a34ddb12a3bd5bd07d831ce95f111087f3bcbd8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/paratestphp/paratest/zipball/521aa381c212816d0dc2f04f1532a5831969cb5e",
|
||||
"reference": "521aa381c212816d0dc2f04f1532a5831969cb5e",
|
||||
"url": "https://api.github.com/repos/paratestphp/paratest/zipball/6a34ddb12a3bd5bd07d831ce95f111087f3bcbd8",
|
||||
"reference": "6a34ddb12a3bd5bd07d831ce95f111087f3bcbd8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -7904,11 +7912,11 @@
|
|||
"ext-simplexml": "*",
|
||||
"fidry/cpu-core-counter": "^1.3.0",
|
||||
"jean85/pretty-package-versions": "^2.1.1",
|
||||
"php": "~8.3.0 || ~8.4.0",
|
||||
"php": "~8.3.0 || ~8.4.0 || ~8.5.0",
|
||||
"phpunit/php-code-coverage": "^12.3.2",
|
||||
"phpunit/php-file-iterator": "^6",
|
||||
"phpunit/php-timer": "^8",
|
||||
"phpunit/phpunit": "^12.3.5",
|
||||
"phpunit/phpunit": "^12.3.6",
|
||||
"sebastian/environment": "^8.0.3",
|
||||
"symfony/console": "^6.4.20 || ^7.3.2",
|
||||
"symfony/process": "^6.4.20 || ^7.3.0"
|
||||
|
|
@ -7963,7 +7971,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/paratestphp/paratest/issues",
|
||||
"source": "https://github.com/paratestphp/paratest/tree/v7.11.2"
|
||||
"source": "https://github.com/paratestphp/paratest/tree/v7.12.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -7975,7 +7983,7 @@
|
|||
"type": "paypal"
|
||||
}
|
||||
],
|
||||
"time": "2025-08-19T09:24:27+00:00"
|
||||
"time": "2025-08-29T05:28:31+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/deprecations",
|
||||
|
|
@ -8463,16 +8471,16 @@
|
|||
},
|
||||
{
|
||||
"name": "laravel/boost",
|
||||
"version": "v1.0.18",
|
||||
"version": "v1.0.20",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/boost.git",
|
||||
"reference": "df2a62b5864759ea8cce8a4b7575b657e9c7d4ab"
|
||||
"reference": "c2ac67ce42c39ffe6c3c073c9202d54a96eaa5b5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/boost/zipball/df2a62b5864759ea8cce8a4b7575b657e9c7d4ab",
|
||||
"reference": "df2a62b5864759ea8cce8a4b7575b657e9c7d4ab",
|
||||
"url": "https://api.github.com/repos/laravel/boost/zipball/c2ac67ce42c39ffe6c3c073c9202d54a96eaa5b5",
|
||||
"reference": "c2ac67ce42c39ffe6c3c073c9202d54a96eaa5b5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -8481,13 +8489,13 @@
|
|||
"illuminate/contracts": "^10.0|^11.0|^12.0",
|
||||
"illuminate/routing": "^10.0|^11.0|^12.0",
|
||||
"illuminate/support": "^10.0|^11.0|^12.0",
|
||||
"laravel/mcp": "^0.1.0",
|
||||
"laravel/mcp": "^0.1.1",
|
||||
"laravel/prompts": "^0.1.9|^0.3",
|
||||
"laravel/roster": "^0.2",
|
||||
"php": "^8.1|^8.2"
|
||||
"laravel/roster": "^0.2.4",
|
||||
"php": "^8.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"laravel/pint": "^1.14|^1.23",
|
||||
"laravel/pint": "^1.14",
|
||||
"mockery/mockery": "^1.6",
|
||||
"orchestra/testbench": "^8.22.0|^9.0|^10.0",
|
||||
"pestphp/pest": "^2.0|^3.0",
|
||||
|
|
@ -8524,7 +8532,7 @@
|
|||
"issues": "https://github.com/laravel/boost/issues",
|
||||
"source": "https://github.com/laravel/boost"
|
||||
},
|
||||
"time": "2025-08-16T09:10:03+00:00"
|
||||
"time": "2025-08-28T14:46:17+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/mcp",
|
||||
|
|
@ -8740,16 +8748,16 @@
|
|||
},
|
||||
{
|
||||
"name": "laravel/roster",
|
||||
"version": "v0.2.3",
|
||||
"version": "v0.2.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/roster.git",
|
||||
"reference": "caeed7609b02c00c3f1efec52812d8d87c5d4096"
|
||||
"reference": "0252fa419733c61b3ebeba8e4e2b9ad2a63f3a17"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/roster/zipball/caeed7609b02c00c3f1efec52812d8d87c5d4096",
|
||||
"reference": "caeed7609b02c00c3f1efec52812d8d87c5d4096",
|
||||
"url": "https://api.github.com/repos/laravel/roster/zipball/0252fa419733c61b3ebeba8e4e2b9ad2a63f3a17",
|
||||
"reference": "0252fa419733c61b3ebeba8e4e2b9ad2a63f3a17",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -8797,20 +8805,20 @@
|
|||
"issues": "https://github.com/laravel/roster/issues",
|
||||
"source": "https://github.com/laravel/roster"
|
||||
},
|
||||
"time": "2025-08-13T15:00:25+00:00"
|
||||
"time": "2025-08-29T07:47:42+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/sail",
|
||||
"version": "v1.44.0",
|
||||
"version": "v1.45.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/sail.git",
|
||||
"reference": "a09097bd2a8a38e23ac472fa6a6cf5b0d1c1d3fe"
|
||||
"reference": "019a2933ff4a9199f098d4259713f9bc266a874e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/sail/zipball/a09097bd2a8a38e23ac472fa6a6cf5b0d1c1d3fe",
|
||||
"reference": "a09097bd2a8a38e23ac472fa6a6cf5b0d1c1d3fe",
|
||||
"url": "https://api.github.com/repos/laravel/sail/zipball/019a2933ff4a9199f098d4259713f9bc266a874e",
|
||||
"reference": "019a2933ff4a9199f098d4259713f9bc266a874e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -8860,7 +8868,7 @@
|
|||
"issues": "https://github.com/laravel/sail/issues",
|
||||
"source": "https://github.com/laravel/sail"
|
||||
},
|
||||
"time": "2025-07-04T16:17:06+00:00"
|
||||
"time": "2025-08-25T19:28:31+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mockery/mockery",
|
||||
|
|
@ -9106,16 +9114,16 @@
|
|||
},
|
||||
{
|
||||
"name": "pestphp/pest",
|
||||
"version": "v4.0.3",
|
||||
"version": "v4.0.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pestphp/pest.git",
|
||||
"reference": "e54e4a0178889209a928f7bee63286149d4eb707"
|
||||
"reference": "47fb1d77631d608022cc7af96cac90ac741c8394"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pestphp/pest/zipball/e54e4a0178889209a928f7bee63286149d4eb707",
|
||||
"reference": "e54e4a0178889209a928f7bee63286149d4eb707",
|
||||
"url": "https://api.github.com/repos/pestphp/pest/zipball/47fb1d77631d608022cc7af96cac90ac741c8394",
|
||||
"reference": "47fb1d77631d608022cc7af96cac90ac741c8394",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -9127,12 +9135,12 @@
|
|||
"pestphp/pest-plugin-mutate": "^4.0.1",
|
||||
"pestphp/pest-plugin-profanity": "^4.0.1",
|
||||
"php": "^8.3.0",
|
||||
"phpunit/phpunit": "^12.3.6",
|
||||
"phpunit/phpunit": "^12.3.7",
|
||||
"symfony/process": "^7.3.0"
|
||||
},
|
||||
"conflict": {
|
||||
"filp/whoops": "<2.18.3",
|
||||
"phpunit/phpunit": ">12.3.6",
|
||||
"phpunit/phpunit": ">12.3.7",
|
||||
"sebastian/exporter": "<7.0.0",
|
||||
"webmozart/assert": "<1.11.0"
|
||||
},
|
||||
|
|
@ -9206,7 +9214,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/pestphp/pest/issues",
|
||||
"source": "https://github.com/pestphp/pest/tree/v4.0.3"
|
||||
"source": "https://github.com/pestphp/pest/tree/v4.0.4"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -9218,7 +9226,7 @@
|
|||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-08-24T14:17:23+00:00"
|
||||
"time": "2025-08-28T18:19:42+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pestphp/pest-plugin",
|
||||
|
|
@ -9930,16 +9938,16 @@
|
|||
},
|
||||
{
|
||||
"name": "phpstan/phpdoc-parser",
|
||||
"version": "2.2.0",
|
||||
"version": "2.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpdoc-parser.git",
|
||||
"reference": "b9e61a61e39e02dd90944e9115241c7f7e76bfd8"
|
||||
"reference": "1e0cd5370df5dd2e556a36b9c62f62e555870495"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/b9e61a61e39e02dd90944e9115241c7f7e76bfd8",
|
||||
"reference": "b9e61a61e39e02dd90944e9115241c7f7e76bfd8",
|
||||
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/1e0cd5370df5dd2e556a36b9c62f62e555870495",
|
||||
"reference": "1e0cd5370df5dd2e556a36b9c62f62e555870495",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -9971,9 +9979,9 @@
|
|||
"description": "PHPDoc parser with support for nullable, intersection and generic types",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpstan/phpdoc-parser/issues",
|
||||
"source": "https://github.com/phpstan/phpdoc-parser/tree/2.2.0"
|
||||
"source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.0"
|
||||
},
|
||||
"time": "2025-07-13T07:04:09+00:00"
|
||||
"time": "2025-08-30T15:50:23+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
|
|
@ -10035,34 +10043,34 @@
|
|||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
"version": "12.3.2",
|
||||
"version": "12.3.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
||||
"reference": "086553c5b2e0e1e20293d782d788ab768202b621"
|
||||
"reference": "96dc0466673e215bf5536301039017f03cd45c6b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/086553c5b2e0e1e20293d782d788ab768202b621",
|
||||
"reference": "086553c5b2e0e1e20293d782d788ab768202b621",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/96dc0466673e215bf5536301039017f03cd45c6b",
|
||||
"reference": "96dc0466673e215bf5536301039017f03cd45c6b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-dom": "*",
|
||||
"ext-libxml": "*",
|
||||
"ext-xmlwriter": "*",
|
||||
"nikic/php-parser": "^5.4.0",
|
||||
"nikic/php-parser": "^5.6.1",
|
||||
"php": ">=8.3",
|
||||
"phpunit/php-file-iterator": "^6.0",
|
||||
"phpunit/php-text-template": "^5.0",
|
||||
"sebastian/complexity": "^5.0",
|
||||
"sebastian/environment": "^8.0",
|
||||
"sebastian/environment": "^8.0.3",
|
||||
"sebastian/lines-of-code": "^4.0",
|
||||
"sebastian/version": "^6.0",
|
||||
"theseer/tokenizer": "^1.2.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^12.1"
|
||||
"phpunit/phpunit": "^12.3.7"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-pcov": "PHP extension that provides line coverage",
|
||||
|
|
@ -10100,7 +10108,7 @@
|
|||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
|
||||
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.3.2"
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.3.5"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -10120,7 +10128,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-07-29T06:19:24+00:00"
|
||||
"time": "2025-09-01T08:07:42+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-file-iterator",
|
||||
|
|
@ -10369,16 +10377,16 @@
|
|||
},
|
||||
{
|
||||
"name": "phpunit/phpunit",
|
||||
"version": "12.3.6",
|
||||
"version": "12.3.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||
"reference": "a2cab3224f687150ac2f3cc13d99b64ba1e1d088"
|
||||
"reference": "b8fa997c49682979ad6bfaa0d7fb25f54954965e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a2cab3224f687150ac2f3cc13d99b64ba1e1d088",
|
||||
"reference": "a2cab3224f687150ac2f3cc13d99b64ba1e1d088",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b8fa997c49682979ad6bfaa0d7fb25f54954965e",
|
||||
"reference": "b8fa997c49682979ad6bfaa0d7fb25f54954965e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -10392,7 +10400,7 @@
|
|||
"phar-io/manifest": "^2.0.4",
|
||||
"phar-io/version": "^3.2.1",
|
||||
"php": ">=8.3",
|
||||
"phpunit/php-code-coverage": "^12.3.2",
|
||||
"phpunit/php-code-coverage": "^12.3.3",
|
||||
"phpunit/php-file-iterator": "^6.0.0",
|
||||
"phpunit/php-invoker": "^6.0.0",
|
||||
"phpunit/php-text-template": "^5.0.0",
|
||||
|
|
@ -10446,7 +10454,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.3.6"
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/12.3.7"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -10470,7 +10478,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-08-20T14:43:23+00:00"
|
||||
"time": "2025-08-28T05:15:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/cli-parser",
|
||||
|
|
@ -10902,16 +10910,16 @@
|
|||
},
|
||||
{
|
||||
"name": "sebastian/global-state",
|
||||
"version": "8.0.0",
|
||||
"version": "8.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/global-state.git",
|
||||
"reference": "570a2aeb26d40f057af686d63c4e99b075fb6cbc"
|
||||
"reference": "ef1377171613d09edd25b7816f05be8313f9115d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/570a2aeb26d40f057af686d63c4e99b075fb6cbc",
|
||||
"reference": "570a2aeb26d40f057af686d63c4e99b075fb6cbc",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/ef1377171613d09edd25b7816f05be8313f9115d",
|
||||
"reference": "ef1377171613d09edd25b7816f05be8313f9115d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -10952,15 +10960,27 @@
|
|||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/global-state/issues",
|
||||
"security": "https://github.com/sebastianbergmann/global-state/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/global-state/tree/8.0.0"
|
||||
"source": "https://github.com/sebastianbergmann/global-state/tree/8.0.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/sebastianbergmann",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://liberapay.com/sebastianbergmann",
|
||||
"type": "liberapay"
|
||||
},
|
||||
{
|
||||
"url": "https://thanks.dev/u/gh/sebastianbergmann",
|
||||
"type": "thanks_dev"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/sebastian/global-state",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-02-07T04:56:59+00:00"
|
||||
"time": "2025-08-29T11:29:25+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/lines-of-code",
|
||||
|
|
|
|||
|
|
@ -152,4 +152,5 @@ return [
|
|||
|
||||
'version' => env('APP_VERSION', null),
|
||||
|
||||
'catalog_url' => env('CATALOG_URL', 'https://raw.githubusercontent.com/bnussbau/trmnl-recipe-catalog/refs/heads/main/catalog.yaml'),
|
||||
];
|
||||
|
|
|
|||
148
resources/views/livewire/catalog/index.blade.php
Normal file
148
resources/views/livewire/catalog/index.blade.php
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
<?php
|
||||
|
||||
use App\Services\PluginImportService;
|
||||
use Livewire\Volt\Component;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
new class extends Component {
|
||||
public array $catalogPlugins = [];
|
||||
public string $installingPlugin = '';
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$this->loadCatalogPlugins();
|
||||
}
|
||||
|
||||
private function loadCatalogPlugins(): void
|
||||
{
|
||||
$catalogUrl = config('app.catalog_url');
|
||||
|
||||
$this->catalogPlugins = Cache::remember('catalog_plugins', 43200, function () use ($catalogUrl) {
|
||||
try {
|
||||
$response = Http::get($catalogUrl);
|
||||
$catalogContent = $response->body();
|
||||
$catalog = Yaml::parse($catalogContent);
|
||||
|
||||
return collect($catalog)->map(function ($plugin, $key) {
|
||||
return [
|
||||
'id' => $key,
|
||||
'name' => $plugin['name'] ?? 'Unknown Plugin',
|
||||
'description' => $plugin['author_bio']['description'] ?? '',
|
||||
'author' => $plugin['author']['name'] ?? 'Unknown Author',
|
||||
'github' => $plugin['author']['github'] ?? null,
|
||||
'license' => $plugin['license'] ?? null,
|
||||
'zip_url' => $plugin['trmnlp']['zip_url'] ?? null,
|
||||
'repo_url' => $plugin['trmnlp']['repo'] ?? null,
|
||||
'logo_url' => $plugin['logo_url'] ?? null,
|
||||
'screenshot_url' => $plugin['screenshot_url'] ?? null,
|
||||
'learn_more_url' => $plugin['author_bio']['learn_more_url'] ?? null,
|
||||
];
|
||||
})->toArray();
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Failed to load catalog from URL: ' . $e->getMessage());
|
||||
return [];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function installPlugin(string $pluginId, PluginImportService $pluginImportService): void
|
||||
{
|
||||
abort_unless(auth()->user() !== null, 403);
|
||||
|
||||
$plugin = collect($this->catalogPlugins)->firstWhere('id', $pluginId);
|
||||
|
||||
if (!$plugin || !$plugin['zip_url']) {
|
||||
$this->addError('installation', 'Plugin not found or no download URL available.');
|
||||
return;
|
||||
}
|
||||
|
||||
$this->installingPlugin = $pluginId;
|
||||
|
||||
try {
|
||||
$importedPlugin = $pluginImportService->importFromUrl($plugin['zip_url'], auth()->user());
|
||||
|
||||
$this->dispatch('plugin-installed');
|
||||
Flux::modal('import-from-catalog')->close();
|
||||
|
||||
} catch (\Exception $e) {
|
||||
$this->addError('installation', 'Error installing plugin: ' . $e->getMessage());
|
||||
} finally {
|
||||
$this->installingPlugin = '';
|
||||
}
|
||||
}
|
||||
}; ?>
|
||||
|
||||
<div class="space-y-4">
|
||||
@if(empty($catalogPlugins))
|
||||
<div class="text-center py-8">
|
||||
<flux:icon name="exclamation-triangle" class="mx-auto h-12 w-12 text-gray-400" />
|
||||
<flux:heading class="mt-2">No plugins available</flux:heading>
|
||||
<flux:subheading>Catalog is empty</flux:subheading>
|
||||
</div>
|
||||
@else
|
||||
<div class="grid grid-cols-1 gap-4">
|
||||
@error('installation')
|
||||
<flux:callout variant="danger" icon="x-circle" heading="{{$message}}" />
|
||||
@enderror
|
||||
|
||||
@foreach($catalogPlugins as $plugin)
|
||||
<div class="bg-white dark:bg-white/10 border border-zinc-200 dark:border-white/10 [:where(&)]:p-6 [:where(&)]:rounded-xl space-y-6">
|
||||
<div class="flex items-start space-x-4">
|
||||
@if($plugin['logo_url'])
|
||||
<img src="{{ $plugin['logo_url'] }}" alt="{{ $plugin['name'] }}" class="w-12 h-12 rounded-lg object-cover">
|
||||
@else
|
||||
<div class="w-12 h-12 bg-gray-200 dark:bg-gray-700 rounded-lg flex items-center justify-center">
|
||||
<flux:icon name="puzzle-piece" class="w-6 h-6 text-gray-400" />
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100">{{ $plugin['name'] }}</h3>
|
||||
@if ($plugin['github'])
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">by {{ $plugin['github'] }}</p>
|
||||
@endif
|
||||
</div>
|
||||
<div class="flex items-center space-x-2">
|
||||
@if($plugin['license'])
|
||||
<flux:badge color="gray" size="sm">{{ $plugin['license'] }}</flux:badge>
|
||||
@endif
|
||||
@if($plugin['repo_url'])
|
||||
<a href="{{ $plugin['repo_url'] }}" target="_blank" class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300">
|
||||
<flux:icon name="github" class="w-5 h-5" />
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if($plugin['description'])
|
||||
<p class="mt-2 text-sm text-gray-600 dark:text-gray-300">{{ $plugin['description'] }}</p>
|
||||
@endif
|
||||
|
||||
<div class="mt-4 flex items-center space-x-3">
|
||||
<flux:button
|
||||
wire:click="installPlugin('{{ $plugin['id'] }}')"
|
||||
variant="primary">
|
||||
Install
|
||||
</flux:button>
|
||||
|
||||
@if($plugin['learn_more_url'])
|
||||
<flux:button
|
||||
href="{{ $plugin['learn_more_url'] }}"
|
||||
target="_blank"
|
||||
variant="subtle">
|
||||
Learn More
|
||||
</flux:button>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
|
@ -36,7 +36,7 @@ new class extends Component {
|
|||
'polling_body' => 'nullable|string',
|
||||
];
|
||||
|
||||
private function refreshPlugins(): void
|
||||
public function refreshPlugins(): void
|
||||
{
|
||||
$userPlugins = auth()->user()?->plugins?->map(function ($plugin) {
|
||||
return $plugin->toArray();
|
||||
|
|
@ -96,10 +96,8 @@ new class extends Component {
|
|||
$this->reset(['zipFile']);
|
||||
|
||||
Flux::modal('import-zip')->close();
|
||||
$this->dispatch('notify', ['type' => 'success', 'message' => 'Plugin imported successfully!']);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
$this->dispatch('notify', ['type' => 'error', 'message' => 'Error importing plugin: ' . $e->getMessage()]);
|
||||
$this->addError('zipFile', 'Error installing plugin: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -120,7 +118,10 @@ new class extends Component {
|
|||
<flux:button icon="chevron-down" variant="primary"></flux:button>
|
||||
<flux:menu>
|
||||
<flux:modal.trigger name="import-zip">
|
||||
<flux:menu.item icon="archive-box">Import Recipe</flux:menu.item>
|
||||
<flux:menu.item icon="archive-box">Import Recipe Archive</flux:menu.item>
|
||||
</flux:modal.trigger>
|
||||
<flux:modal.trigger name="import-from-catalog">
|
||||
<flux:menu.item icon="book-open">Import from Catalog</flux:menu.item>
|
||||
</flux:modal.trigger>
|
||||
<flux:menu.item icon="beaker" wire:click="seedExamplePlugins">Seed Example Recipes</flux:menu.item>
|
||||
</flux:menu>
|
||||
|
|
@ -167,7 +168,7 @@ new class extends Component {
|
|||
|
||||
<form wire:submit="importZip">
|
||||
<div class="mb-4">
|
||||
<label for="zipFile" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">.zip Archive</label>
|
||||
<flux:label for="zipFile">.zip Archive</flux:label>
|
||||
<input
|
||||
type="file"
|
||||
wire:model="zipFile"
|
||||
|
|
@ -175,7 +176,9 @@ new class extends Component {
|
|||
accept=".zip"
|
||||
class="block w-full text-sm text-gray-900 border border-gray-300 rounded-lg cursor-pointer bg-gray-50 dark:text-gray-400 focus:outline-none dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 p-2.5"
|
||||
/>
|
||||
@error('zipFile') <span class="text-red-500 text-xs mt-1">{{ $message }}</span> @enderror
|
||||
@error('zipFile')
|
||||
<flux:callout variant="danger" icon="x-circle" heading="{{$message}}" class="mt-2" />
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="flex">
|
||||
|
|
@ -186,6 +189,18 @@ new class extends Component {
|
|||
</div>
|
||||
</flux:modal>
|
||||
|
||||
<flux:modal name="import-from-catalog">
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<flux:heading size="lg">Import from Catalog
|
||||
<flux:badge color="yellow" class="ml-2">Alpha</flux:badge>
|
||||
</flux:heading>
|
||||
<flux:subheading>Browse and install Recipes from the community. Add yours <a href="https://github.com/bnussbau/trmnl-recipe-catalog" class="underline" target="_blank">here</a>.</flux:subheading>
|
||||
</div>
|
||||
<livewire:catalog.index @plugin-installed="refreshPlugins" />
|
||||
</div>
|
||||
</flux:modal>
|
||||
|
||||
<flux:modal name="add-plugin" class="md:w-96">
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
|
|
|
|||
102
tests/Feature/Livewire/Catalog/IndexTest.php
Normal file
102
tests/Feature/Livewire/Catalog/IndexTest.php
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
<?php
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Livewire\Volt\Volt;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
beforeEach(function () {
|
||||
Cache::flush();
|
||||
});
|
||||
|
||||
it('can render catalog component', function () {
|
||||
// Mock empty catalog response
|
||||
Http::fake([
|
||||
config('app.catalog_url') => Http::response('', 200),
|
||||
]);
|
||||
|
||||
$component = Volt::test('catalog.index');
|
||||
|
||||
$component->assertSee('No plugins available');
|
||||
});
|
||||
|
||||
it('loads plugins from catalog URL', function () {
|
||||
// Clear cache first to ensure fresh data
|
||||
Cache::forget('catalog_plugins');
|
||||
|
||||
// Mock the HTTP response for the catalog URL
|
||||
$catalogData = [
|
||||
'test-plugin' => [
|
||||
'name' => 'Test Plugin',
|
||||
'author' => ['name' => 'Test Author', 'github' => 'testuser'],
|
||||
'author_bio' => [
|
||||
'description' => 'A test plugin',
|
||||
'learn_more_url' => 'https://example.com',
|
||||
],
|
||||
'license' => 'MIT',
|
||||
'trmnlp' => [
|
||||
'zip_url' => 'https://example.com/plugin.zip',
|
||||
],
|
||||
'logo_url' => 'https://example.com/logo.png',
|
||||
],
|
||||
];
|
||||
|
||||
$yamlContent = Yaml::dump($catalogData);
|
||||
|
||||
// Override the default mock with specific data
|
||||
Http::fake([
|
||||
config('app.catalog_url') => Http::response($yamlContent, 200),
|
||||
]);
|
||||
|
||||
$component = Volt::test('catalog.index');
|
||||
|
||||
$component->assertSee('Test Plugin');
|
||||
$component->assertSee('testuser');
|
||||
$component->assertSee('A test plugin');
|
||||
$component->assertSee('MIT');
|
||||
});
|
||||
|
||||
it('shows error when plugin not found', function () {
|
||||
$user = User::factory()->create();
|
||||
|
||||
$this->actingAs($user);
|
||||
|
||||
$component = Volt::test('catalog.index');
|
||||
|
||||
$component->call('installPlugin', 'non-existent-plugin');
|
||||
|
||||
// The component should dispatch an error notification
|
||||
$component->assertHasErrors();
|
||||
});
|
||||
|
||||
it('shows error when zip_url is missing', function () {
|
||||
$user = User::factory()->create();
|
||||
|
||||
// Mock the HTTP response for the catalog URL without zip_url
|
||||
$catalogData = [
|
||||
'test-plugin' => [
|
||||
'name' => 'Test Plugin',
|
||||
'author' => ['name' => 'Test Author'],
|
||||
'author_bio' => ['description' => 'A test plugin'],
|
||||
'license' => 'MIT',
|
||||
'trmnlp' => [],
|
||||
],
|
||||
];
|
||||
|
||||
$yamlContent = Yaml::dump($catalogData);
|
||||
|
||||
Http::fake([
|
||||
config('app.catalog_url') => Http::response($yamlContent, 200),
|
||||
]);
|
||||
|
||||
$this->actingAs($user);
|
||||
|
||||
$component = Volt::test('catalog.index');
|
||||
|
||||
$component->call('installPlugin', 'test-plugin');
|
||||
|
||||
// The component should dispatch an error notification
|
||||
$component->assertHasErrors();
|
||||
|
||||
});
|
||||
|
|
@ -94,7 +94,7 @@ it('throws exception for missing required files', function () {
|
|||
|
||||
$pluginImportService = new PluginImportService();
|
||||
expect(fn () => $pluginImportService->importFromZip($zipFile, $user))
|
||||
->toThrow(Exception::class, 'Invalid ZIP structure. Required files settings.yml and full.liquid/full.blade.php are missing.');
|
||||
->toThrow(Exception::class, 'Invalid ZIP structure. Required files settings.yml and full.liquid are missing.');
|
||||
});
|
||||
|
||||
it('sets default values when settings are missing', function () {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ use App\Models\Plugin;
|
|||
* to:
|
||||
* {% assign _temp_xxx = collection | filter: "key", "value" %}{% for item in _temp_xxx %}
|
||||
*/
|
||||
|
||||
test('where filter works when assigned to variable first', function () {
|
||||
$plugin = Plugin::factory()->create([
|
||||
'markup_language' => 'liquid',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue