feat/011-restore-run-wizard #37
@ -177,10 +177,15 @@ public static function getWizardSteps(): array
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
->reactive()
|
->reactive()
|
||||||
->afterStateUpdated(function (Set $set): void {
|
->afterStateUpdated(function (Set $set, Get $get): void {
|
||||||
$set('scope_mode', 'all');
|
$set('scope_mode', 'all');
|
||||||
$set('backup_item_ids', null);
|
$set('backup_item_ids', null);
|
||||||
$set('group_mapping', []);
|
$set('group_mapping', static::groupMappingPlaceholders(
|
||||||
|
backupSetId: $get('backup_set_id'),
|
||||||
|
scopeMode: 'all',
|
||||||
|
selectedItemIds: null,
|
||||||
|
tenant: Tenant::current(),
|
||||||
|
));
|
||||||
$set('is_dry_run', true);
|
$set('is_dry_run', true);
|
||||||
$set('acknowledged_impact', false);
|
$set('acknowledged_impact', false);
|
||||||
$set('tenant_confirm', null);
|
$set('tenant_confirm', null);
|
||||||
@ -204,8 +209,9 @@ public static function getWizardSteps(): array
|
|||||||
])
|
])
|
||||||
->default('all')
|
->default('all')
|
||||||
->reactive()
|
->reactive()
|
||||||
->afterStateUpdated(function (Set $set, $state): void {
|
->afterStateUpdated(function (Set $set, Get $get, $state): void {
|
||||||
$set('group_mapping', []);
|
$backupSetId = $get('backup_set_id');
|
||||||
|
$tenant = Tenant::current();
|
||||||
$set('is_dry_run', true);
|
$set('is_dry_run', true);
|
||||||
$set('acknowledged_impact', false);
|
$set('acknowledged_impact', false);
|
||||||
$set('tenant_confirm', null);
|
$set('tenant_confirm', null);
|
||||||
@ -218,10 +224,17 @@ public static function getWizardSteps(): array
|
|||||||
|
|
||||||
if ($state === 'all') {
|
if ($state === 'all') {
|
||||||
$set('backup_item_ids', null);
|
$set('backup_item_ids', null);
|
||||||
|
$set('group_mapping', static::groupMappingPlaceholders(
|
||||||
|
backupSetId: $backupSetId,
|
||||||
|
scopeMode: 'all',
|
||||||
|
selectedItemIds: null,
|
||||||
|
tenant: $tenant,
|
||||||
|
));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$set('group_mapping', []);
|
||||||
$set('backup_item_ids', []);
|
$set('backup_item_ids', []);
|
||||||
})
|
})
|
||||||
->required(),
|
->required(),
|
||||||
@ -234,8 +247,18 @@ public static function getWizardSteps(): array
|
|||||||
->optionsLimit(300)
|
->optionsLimit(300)
|
||||||
->options(fn (Get $get) => static::restoreItemGroupedOptions($get('backup_set_id')))
|
->options(fn (Get $get) => static::restoreItemGroupedOptions($get('backup_set_id')))
|
||||||
->reactive()
|
->reactive()
|
||||||
->afterStateUpdated(function (Set $set): void {
|
->afterStateUpdated(function (Set $set, Get $get): void {
|
||||||
$set('group_mapping', []);
|
$backupSetId = $get('backup_set_id');
|
||||||
|
$selectedItemIds = $get('backup_item_ids');
|
||||||
|
$selectedItemIds = is_array($selectedItemIds) ? $selectedItemIds : null;
|
||||||
|
$tenant = Tenant::current();
|
||||||
|
|
||||||
|
$set('group_mapping', static::groupMappingPlaceholders(
|
||||||
|
backupSetId: $backupSetId,
|
||||||
|
scopeMode: 'selected',
|
||||||
|
selectedItemIds: $selectedItemIds,
|
||||||
|
tenant: $tenant,
|
||||||
|
));
|
||||||
$set('is_dry_run', true);
|
$set('is_dry_run', true);
|
||||||
$set('acknowledged_impact', false);
|
$set('acknowledged_impact', false);
|
||||||
$set('tenant_confirm', null);
|
$set('tenant_confirm', null);
|
||||||
@ -249,29 +272,29 @@ public static function getWizardSteps(): array
|
|||||||
->visible(fn (Get $get): bool => $get('scope_mode') === 'selected')
|
->visible(fn (Get $get): bool => $get('scope_mode') === 'selected')
|
||||||
->required(fn (Get $get): bool => $get('scope_mode') === 'selected')
|
->required(fn (Get $get): bool => $get('scope_mode') === 'selected')
|
||||||
->hintActions([
|
->hintActions([
|
||||||
Actions\Action::make('select_all_backup_items')
|
Actions\Action::make('select_all_backup_items')
|
||||||
->label('Select all')
|
->label('Select all')
|
||||||
->icon('heroicon-o-check')
|
->icon('heroicon-o-check')
|
||||||
->color('gray')
|
->color('gray')
|
||||||
->visible(fn (Get $get): bool => filled($get('backup_set_id')) && $get('scope_mode') === 'selected')
|
->visible(fn (Get $get): bool => filled($get('backup_set_id')) && $get('scope_mode') === 'selected')
|
||||||
->action(function (Get $get, Set $set): void {
|
->action(function (Get $get, Set $set): void {
|
||||||
$groupedOptions = static::restoreItemGroupedOptions($get('backup_set_id'));
|
$groupedOptions = static::restoreItemGroupedOptions($get('backup_set_id'));
|
||||||
|
|
||||||
$allItemIds = [];
|
$allItemIds = [];
|
||||||
|
|
||||||
foreach ($groupedOptions as $options) {
|
foreach ($groupedOptions as $options) {
|
||||||
$allItemIds = array_merge($allItemIds, array_keys($options));
|
$allItemIds = array_merge($allItemIds, array_keys($options));
|
||||||
}
|
}
|
||||||
|
|
||||||
$set('backup_item_ids', array_values($allItemIds), shouldCallUpdatedHooks: true);
|
$set('backup_item_ids', array_values($allItemIds), shouldCallUpdatedHooks: true);
|
||||||
}),
|
}),
|
||||||
Actions\Action::make('clear_backup_items')
|
Actions\Action::make('clear_backup_items')
|
||||||
->label('Clear')
|
->label('Clear')
|
||||||
->icon('heroicon-o-x-mark')
|
->icon('heroicon-o-x-mark')
|
||||||
->color('gray')
|
->color('gray')
|
||||||
->visible(fn (Get $get): bool => $get('scope_mode') === 'selected')
|
->visible(fn (Get $get): bool => $get('scope_mode') === 'selected')
|
||||||
->action(fn (Set $set) => $set('backup_item_ids', [], shouldCallUpdatedHooks: true)),
|
->action(fn (Set $set) => $set('backup_item_ids', [], shouldCallUpdatedHooks: true)),
|
||||||
])
|
])
|
||||||
->helperText('Search by name or ID. Include foundations (scope tags, assignment filters) with policies to re-map IDs. Options are grouped by category, type, and platform.'),
|
->helperText('Search by name or ID. Include foundations (scope tags, assignment filters) with policies to re-map IDs. Options are grouped by category, type, and platform.'),
|
||||||
Section::make('Group mapping')
|
Section::make('Group mapping')
|
||||||
->description('Some source groups do not exist in the target tenant. Map them or choose Skip.')
|
->description('Some source groups do not exist in the target tenant. Map them or choose Skip.')
|
||||||
@ -1383,8 +1406,43 @@ private static function unresolvedGroups(?int $backupSetId, ?array $selectedItem
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param array<int>|null $selectedItemIds
|
||||||
* @return array<string, string|null>
|
* @return array<string, string|null>
|
||||||
*/
|
*/
|
||||||
|
private static function groupMappingPlaceholders(?int $backupSetId, string $scopeMode, ?array $selectedItemIds, ?Tenant $tenant): array
|
||||||
|
{
|
||||||
|
if (! $tenant || ! $backupSetId) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($scopeMode === 'selected' && ($selectedItemIds === null || $selectedItemIds === [])) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$unresolved = static::unresolvedGroups(
|
||||||
|
backupSetId: $backupSetId,
|
||||||
|
selectedItemIds: $scopeMode === 'selected' ? $selectedItemIds : null,
|
||||||
|
tenant: $tenant,
|
||||||
|
);
|
||||||
|
|
||||||
|
$placeholders = [];
|
||||||
|
|
||||||
|
foreach ($unresolved as $group) {
|
||||||
|
$groupId = $group['id'] ?? null;
|
||||||
|
|
||||||
|
if (! is_string($groupId) || $groupId === '') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$placeholders[$groupId] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $placeholders;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, string>
|
||||||
|
*/
|
||||||
private static function normalizeGroupMapping(mixed $mapping): array
|
private static function normalizeGroupMapping(mixed $mapping): array
|
||||||
{
|
{
|
||||||
if ($mapping instanceof \Illuminate\Contracts\Support\Arrayable) {
|
if ($mapping instanceof \Illuminate\Contracts\Support\Arrayable) {
|
||||||
@ -1449,7 +1507,7 @@ private static function normalizeGroupMapping(mixed $mapping): array
|
|||||||
$result[$sourceGroupId] = null;
|
$result[$sourceGroupId] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $result;
|
return array_filter($result, static fn (?string $value): bool => is_string($value) && $value !== '');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -77,7 +77,7 @@
|
|||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$this->actingAs($user);
|
$this->actingAs($user);
|
||||||
|
|
||||||
Livewire::test(CreateRestoreRun::class)
|
$component = Livewire::test(CreateRestoreRun::class)
|
||||||
->fillForm([
|
->fillForm([
|
||||||
'backup_set_id' => $backupSet->id,
|
'backup_set_id' => $backupSet->id,
|
||||||
])
|
])
|
||||||
@ -85,8 +85,15 @@
|
|||||||
->fillForm([
|
->fillForm([
|
||||||
'scope_mode' => 'selected',
|
'scope_mode' => 'selected',
|
||||||
'backup_item_ids' => [$backupItem->id],
|
'backup_item_ids' => [$backupItem->id],
|
||||||
])
|
]);
|
||||||
->assertFormFieldVisible('group_mapping.source-group-1');
|
|
||||||
|
$mapping = $component->get('data.group_mapping');
|
||||||
|
|
||||||
|
expect($mapping)->toBeArray();
|
||||||
|
expect(array_key_exists('source-group-1', $mapping))->toBeTrue();
|
||||||
|
expect($mapping['source-group-1'])->toBeNull();
|
||||||
|
|
||||||
|
$component->assertFormFieldVisible('group_mapping.source-group-1');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('restore wizard persists group mapping selections', function () {
|
test('restore wizard persists group mapping selections', function () {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user