Enhance ManualServers functionality: add user-provided information display and credential handling; improve TagsInput validation; update navigation properties

This commit is contained in:
2026-03-13 05:58:51 -04:00
parent 0f36dba867
commit a8b6e5cfba
8 changed files with 951 additions and 19 deletions

View File

@@ -12,8 +12,8 @@ use Paymenter\Extensions\Servers\ManualServers\ManualServers;
class ManualServersManage extends Page implements HasForms
{
use InteractsWithForms;
protected static string|\BackedEnum|null $navigationIcon = 'ri-hard-drive-3-line';
protected string $view = 'manual-servers::admin.pages.manual-servers-manage';
protected static ?string $navigationIcon = 'ri-hard-drive-3-line';
protected static string $view = 'manual-servers::admin.pages.manual-servers-manage';
protected static bool $shouldRegisterNavigation = false;
protected static ?string $slug = 'manual-servers-manage/{service}';
public ?Service $service = null;

View File

@@ -13,9 +13,9 @@ class ManualServersQueue extends Page implements HasTable
{
use InteractsWithTable;
protected static string|\BackedEnum|null $navigationIcon = 'ri-hard-drive-3-line';
protected string $view = 'manual-servers::admin.pages.manual-servers-queue';
protected static string|\UnitEnum|null $navigationGroup = 'Extensions';
protected static ?string $navigationIcon = 'ri-hard-drive-3-line';
protected static string $view = 'manual-servers::admin.pages.manual-servers-queue';
protected static ?string $navigationGroup = 'Extensions';
protected static ?string $navigationLabel = 'Manual Servers';
protected static ?int $navigationSort = 100;

View File

@@ -162,6 +162,70 @@ class ManualServers extends Server
return $config;
}
/**
* Get configuration options for checkout (user-provided data during purchase)
*
* @param Product $product
* @return array
*/
public function getCheckoutConfig($product = null): array
{
if (!$product) {
return [];
}
// Create a temporary service object to use getCredentialFieldsForService
$tempService = new Service(['product_id' => $product->id]);
$tempService->setRelation('product', $product);
$fields = $this->getCredentialFieldsForService($tempService);
// Convert to checkout config format
$checkoutFields = [];
foreach ($fields as $field) {
$checkoutFields[] = [
'name' => $field['name'],
'type' => $field['type'],
'label' => $field['label'],
'required' => true,
'placeholder' => 'Enter ' . strtolower($field['label']),
];
}
return $checkoutFields;
}
/**
* Get configuration options for admin when managing a service
* This allows admins to enter credentials and other service-specific data
*
* @param Service $service
* @return array
*/
public function getUserConfig(Service $service): array
{
$fields = $this->getCredentialFieldsForService($service);
// Get existing credentials
$credentialsJson = $service->properties->where('key', 'credentials_data')->first()?->value ?? '[]';
$existingCredentials = json_decode($credentialsJson, true) ?? [];
// Format fields for the admin interface
$configFields = [];
foreach ($fields as $field) {
$configFields[] = [
'name' => $field['name'],
'type' => $field['type'],
'label' => $field['label'],
'required' => false,
'default' => $existingCredentials[$field['name']] ?? '',
];
}
return $configFields;
}
public function createServer(Service $service, $params = []): bool
{
$processes = $this->getProcessesForService($service);
@@ -181,9 +245,20 @@ class ManualServers extends Server
['key' => 'processes_data'],
['value' => json_encode($processData)]
);
// Extract credential values from params (checkout form data)
$credentialFields = $this->getCredentialFieldsForService($service);
$credentials = [];
foreach ($credentialFields as $field) {
$fieldName = $field['name'];
if (isset($params[$fieldName])) {
$credentials[$fieldName] = $params[$fieldName];
}
}
$service->properties()->updateOrCreate(
['key' => 'credentials_data'],
['value' => json_encode([])]
['value' => json_encode($credentials)]
);
$service->properties()->updateOrCreate(
['key' => 'deployed_at'],
@@ -370,13 +445,30 @@ class ManualServers extends Server
$credentialsJson = $service->properties->where('key', 'credentials_data')->first()?->value ?? '[]';
$deployedAt = $service->properties->where('key', 'deployed_at')->first()?->value;
$feedback = $service->properties->where('key', 'feedback')->first()?->value ?? '';
// Read credentials from individual properties
$credentialFields = $this->getCredentialFieldsForService($service);
$credentials = [];
foreach ($credentialFields as $field) {
$fieldName = $field['name'];
$value = $service->properties->where('key', $fieldName)->first()?->value;
if ($value !== null) {
$credentials[$fieldName] = $value;
}
}
// Fallback to credentials_data JSON if individual properties are empty
if (empty($credentials)) {
$credentials = json_decode($credentialsJson, true) ?? [];
}
return [
'status' => $status,
'processes' => json_decode($processesJson, true) ?? [],
'credentials' => json_decode($credentialsJson, true) ?? [],
'credentials' => $credentials,
'deployed_at' => $deployedAt,
'feedback' => $feedback,
'credential_fields' => $this->getCredentialFieldsForService($service),
'credential_fields' => $credentialFields,
'buttons' => $this->getButtonsForService($service),
'instructions' => $service->product->settings->where('key', 'instructions')->first()?->value ?? '',
];

View File

@@ -65,6 +65,52 @@
</div>
</x-filament::section>
@if(!empty($this->credentialData))
<x-filament::section>
<x-slot name="heading">User-Provided Information</x-slot>
<x-slot name="description">
Information entered by the client during checkout
</x-slot>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
@foreach($this->credentialData as $key => $value)
@php
$fieldConfig = collect($this->credentialFields)->where('name', $key)->first();
$label = $fieldConfig['label'] ?? ucfirst(str_replace('_', ' ', $key));
$isPassword = ($fieldConfig['type'] ?? 'text') === 'password';
@endphp
<div class="p-4 rounded-lg border border-gray-200 dark:border-white/10 bg-gray-50 dark:bg-white/5">
<p class="text-xs font-medium text-gray-500 dark:text-gray-400 mb-2">{{ $label }}</p>
<div class="flex items-start justify-between gap-3">
<div class="flex-1 min-w-0">
@if($isPassword)
<p class="font-mono text-sm" x-data="{ show: false }">
<span x-show="!show" class="text-gray-400">••••••••</span>
<span x-show="show" class="break-all">{{ $value }}</span>
<x-filament::link @click="show = !show" size="xs" class="ml-2">
<span x-show="!show">Show</span>
<span x-show="show">Hide</span>
</x-filament::link>
</p>
@else
<p class="font-mono text-sm break-all">{{ $value ?: 'N/A' }}</p>
@endif
</div>
<x-filament::button
size="xs"
color="gray"
x-data
x-on:click="navigator.clipboard.writeText('{{ addslashes($value) }}'); $tooltip('Copied!')">
Copy
</x-filament::button>
</div>
</div>
@endforeach
</div>
</x-filament::section>
@endif
@if(empty($this->processData))
<x-filament::section>
<x-slot name="heading">Initialize Setup Flow</x-slot>