diff --git a/app/Http/Controllers/EtsySettingsController.php b/app/Http/Controllers/EtsySettingsController.php index a51da80..b3b3f42 100644 --- a/app/Http/Controllers/EtsySettingsController.php +++ b/app/Http/Controllers/EtsySettingsController.php @@ -28,6 +28,7 @@ class EtsySettingsController extends Controller return view('settings.index', [ 'storedCredentials' => $credentials !== null, 'configured' => $etsy->configured(), + 'shopConnected' => EtsyConnection::query()->exists(), 'redirectUri' => $etsy->redirectUri(), 'shopName' => $credentials?->shop_name ?: config('services.etsy.shop_name', 'HickLab3D'), 'integrations' => [ diff --git a/resources/views/settings/index.blade.php b/resources/views/settings/index.blade.php index 05fd885..e704cc2 100644 --- a/resources/views/settings/index.blade.php +++ b/resources/views/settings/index.blade.php @@ -1,114 +1,130 @@ @extends('layouts.app') @section('title', 'Settings') @section('content') -
-

Integrations

-

Integration settings

-

Manage the connections used by your shop and workshop. Saved values are encrypted in the database.

+
+

Workspace connections

Integration settings

Manage the services behind your orders, shipping and workshop. Saved credentials are encrypted in the database.

+ Administrators only
-
+ + +

Etsy shop

-

{{ $storedCredentials ? 'Credentials saved in the database.' : ($configured ? 'Using server credentials. Save them here to manage them in the dashboard.' : 'No credentials saved yet.') }}

+

{{ $storedCredentials ? 'App credentials saved in dashboard.' : ($configured ? 'Using server app credentials.' : 'Add your Etsy app credentials.') }}

- {{ $configured ? 'Configured' : 'Setup needed' }} + $configured && $shopConnected, 'bg-amber-500/10 text-amber-200' => ! ($configured && $shopConnected)])>{{ $configured && $shopConnected ? 'Shop connected' : ($configured ? 'Connect shop' : 'Setup needed') }}
@csrf @method('PUT') - +
has('redirect_uri')) open @endif> + Advanced: OAuth callback + +

Register this exact HTTPS URL in your Etsy developer app.

+
- + + @if($configured){{ $shopConnected ? 'Open Etsy shop' : 'Connect Etsy shop' }}@endif
-

Blank credential fields keep the current values. Saving imports any server credentials you have not entered here. Changing the app key disconnects the current Etsy shop. The callback must match the URL registered with Etsy.

+

Leave saved credentials blank to keep them. Saving imports server credentials if needed. Changing the app key disconnects the shop until you reconnect it.

-
+
-

Parcel2Go

Quotes, labels, shipments and collections.

- $integrations['parcel2go']['configured'], 'bg-amber-500/10 text-amber-200' => ! $integrations['parcel2go']['configured']])>{{ $integrations['parcel2go']['configured'] ? 'Configured' : 'Setup needed' }} +

Parcel2Go

Client Credentials for shipping, account history and labels.

+ $integrations['parcel2go']['configured'], 'bg-amber-500/10 text-amber-200' => ! $integrations['parcel2go']['configured']])>{{ $integrations['parcel2go']['configured'] ? ($integrations['parcel2go']['stored'] ? 'Saved in dashboard' : 'Using server settings') : 'Setup needed' }}
-

{{ $integrations['parcel2go']['stored'] ? 'Saved in the database.' : 'Using server values where available. Save to import them.' }}

-
+

Use the Client ID and secret from the Parcel2Go client that worked in your previous dashboard. Leave saved credentials blank to keep them.

+ @csrf @method('PUT') - - - - - - - -

The dashboard tries shipments, account details and balance with your Client Credentials connection, as the previous dashboard did. If Parcel2Go denies history, check that these credentials belong to the same client that worked before.

- + + + +
has('auth_url') || $errors->has('base_url') || $errors->has('scope') || $errors->has('profile_client_id')) open @endif> + Advanced: API endpoints and access +
+ + + + +
+
+

Optional Parcel2Go account consent

$integrations['parcel2go']['profile_connected'], 'bg-amber-500/10 text-amber-200' => ! $integrations['parcel2go']['profile_connected']])>{{ $integrations['parcel2go']['profile_connected'] ? 'Connected' : 'Not connected' }}
-

Create an Authorization Code client in Parcel2Go. Set Application URL to {{ url('/') }} and Redirect URI to this exact address:

- {{ $integrations['parcel2go']['callback_url'] }} -

Save its Client ID above, then sign in and approve my-profile access. The account tokens are encrypted in the database. If Parcel2Go does not issue a refresh token, reconnect when access expires. If consent rejects this scope, contact apihelp@parcel2go.com.

- @if($integrations['parcel2go']['profile_client_configured'])
@csrf
@endif +

The dashboard tries shipment history and account details with Client Credentials first. Authorization Code consent is optional.

+
has('parcel2go')) open @endif> + Set up optional account consent +
+

Create an Authorization Code client in Parcel2Go. Set Application URL to {{ url('/') }} and register this exact Redirect URI:

+ {{ $integrations['parcel2go']['callback_url'] }} +

Save its Client ID under Advanced API settings, then connect below. If consent is rejected, contact Parcel2Go API support.

+ @if($integrations['parcel2go']['profile_client_configured'])
@csrf
@endif +
+
-
+

BamBuddy

Live print farm data.

- $integrations['bambuddy']['configured'], 'bg-amber-500/10 text-amber-200' => ! $integrations['bambuddy']['configured']])>{{ $integrations['bambuddy']['configured'] ? 'Configured' : 'Setup needed' }} + $integrations['bambuddy']['configured'], 'bg-amber-500/10 text-amber-200' => ! $integrations['bambuddy']['configured']])>{{ $integrations['bambuddy']['configured'] ? ($integrations['bambuddy']['stored'] ? 'Saved in dashboard' : 'Using server settings') : 'Setup needed' }}
-

{{ $integrations['bambuddy']['stored'] ? 'Saved in the database.' : 'Using server values where available. Save to import them.' }}

@csrf @method('PUT') - - - + + + +
+

Leave the API key blank to keep its current value.

-
+

Filament dryer

Status, cycles and fault controls.

- $integrations['dryer']['configured'], 'bg-amber-500/10 text-amber-200' => ! $integrations['dryer']['configured']])>{{ $integrations['dryer']['configured'] ? 'Configured' : 'Setup needed' }} + $integrations['dryer']['configured'], 'bg-amber-500/10 text-amber-200' => ! $integrations['dryer']['configured']])>{{ $integrations['dryer']['configured'] ? ($integrations['dryer']['stored'] ? 'Saved in dashboard' : 'Using server settings') : 'Setup needed' }}
-

{{ $integrations['dryer']['stored'] ? 'Saved in the database.' : 'Using server values where available. Save to import them.' }}

@csrf @method('PUT') - - - + + + +
+

Leave the API key blank to keep its current value.

-
-

OAuth callback

-

Register this exact HTTPS URL in your Etsy developer app.

- {{ $redirectUri }} - @if($configured)Go to Etsy shop@endif -
@endsection diff --git a/tests/Feature/EtsySettingsTest.php b/tests/Feature/EtsySettingsTest.php index 941b054..1210980 100644 --- a/tests/Feature/EtsySettingsTest.php +++ b/tests/Feature/EtsySettingsTest.php @@ -43,7 +43,7 @@ class EtsySettingsTest extends TestCase $this->assertArrayNotHasKey('shared_secret', $credentials->toArray()); $this->actingAs($admin)->get(route('settings.index')) - ->assertOk()->assertSee('Credentials saved in the database.') + ->assertOk()->assertSee('App credentials saved in dashboard.') ->assertDontSee('saved-key')->assertDontSee('saved-secret'); $this->actingAs($admin)->post(route('etsy.connect')) ->assertRedirectContains('client_id=saved-key'); diff --git a/tests/Feature/IntegrationSettingsTest.php b/tests/Feature/IntegrationSettingsTest.php index 2aee66a..2a16854 100644 --- a/tests/Feature/IntegrationSettingsTest.php +++ b/tests/Feature/IntegrationSettingsTest.php @@ -91,11 +91,16 @@ class IntegrationSettingsTest extends TestCase public function test_endpoint_urls_must_use_https(): void { + config()->set('services.bambuddy.url', 'https://bambuddy.example.test'); + config()->set('services.dryer.url', 'https://dryer.example.test/api/v1'); $this->actingAs(User::factory()->create(['role' => 'admin'])) ->put(route('settings.integrations.update', 'dryer'), [ - 'url' => 'http://dryer.example.test', 'api_key' => 'key', + 'url' => 'http://dryer.example.test', 'api_key' => 'key', 'settings_section' => 'dryer', ])->assertSessionHasErrors('url'); $this->assertDatabaseCount('integration_settings', 0); + $this->get(route('settings.index'))->assertOk() + ->assertSee('value="https://bambuddy.example.test"', false) + ->assertSee('value="http://dryer.example.test"', false); } public function test_parcel2go_scope_is_validated_and_saved(): void