feat(ui): unify resource nav sidebars and polish project layouts

Extract application/database/service configuration sidebars and
unified headings; improve clone-to-environment, embedded deployments,
storage actions, OAuth icons, terminal, and shared form components.
This commit is contained in:
Andras Bacsai committed 2026-08-05 17:56:59 +02:00
1 parent 2291c3fa1c
commit 5de6f97256
85 files changed
+2087 -981

No files matched your search

@@ -42,6 +42,10 @@ class Index extends Component
public array $sourceFilterOptions = [];
public bool $embedded = false;
public ?string $selectedDeploymentUuid = null;
protected $queryString = ['pull_request_id'];
public function getListeners()
@@ -79,6 +83,9 @@ class Index extends Component
}
$this->application = $application;
if ($this->embedded) {
$this->defaultTake = 3;
}
$this->loadPullRequestOptions();
$this->loadDeploymentFilterOptions();
['deployments' => $deployments, 'count' => $count] = $application->deployments(
@@ -90,7 +97,11 @@ class Index extends Component
);
$this->deployments = $deployments;
$this->deployments_count = $count;
$this->current_url = url()->current();
$this->current_url = route('project.application.deployment.index', [
'project_uuid' => $project->uuid,
'environment_uuid' => $environment->uuid,
'application_uuid' => $application->uuid,
]);
$this->updateCurrentPage();
$this->showMore();
}
@@ -29,7 +29,9 @@ class Index extends Component
'database_uuid' => $database->uuid,
]);
}
$this->database = $database;
$this->database = $database->load([
'scheduledBackups' => fn ($query) => $query->withCount('executions'),
]);
}
public function render()
@@ -76,20 +76,10 @@ class ConfigurationChecker extends Component
public function configurationChanged(): void
{
// Banner only needs a lightweight summary in the Livewire snapshot.
$this->loadConfigurationState(includeChanges: false);
$this->loadConfigurationState();
}
public function refreshConfigurationChanges(): void
{
// Full change list is only needed when the user opens "View changes".
$this->loadConfigurationState(includeChanges: true);
}
/**
* @param bool $includeChanges When false, only summary keys are stored (smaller HTML/snapshots).
*/
private function loadConfigurationState(bool $includeChanges = false): void
private function loadConfigurationState(): void
{
$this->resource->refresh();
@@ -99,15 +89,6 @@ class ConfigurationChecker extends Component
$array = $diff->toArray();
if (! $includeChanges) {
$this->configurationDiff = [
'count' => data_get($array, 'count', 0),
'requires_build' => (bool) data_get($array, 'requires_build', false),
];
return;
}
// Fail closed: only owners/admins may see unlocked env values.
$redactEnvironment = ! (bool) auth()->user()?->isAdmin();
$array['changes'] = $this->redactEnvironmentChanges($array['changes'] ?? [], $redactEnvironment);
@@ -55,11 +55,19 @@ class ResourceOperations extends Component
$this->cloneVolumeData = $value;
}
public function cloneTo($destination_uuid)
public function cloneTo($destination_uuid, $environment_id = null)
{
try {
$this->authorize('update', $this->resource);
$new_environment = $this->resource->environment;
if ($environment_id !== null) {
$new_environment = Environment::ownedByCurrentTeam()->find($environment_id);
if (! $new_environment) {
return $this->addError('environment_id', 'Environment not found.');
}
}
$new_destination = find_resource_destination_for_current_team($destination_uuid);
if (! $new_destination) {
return $this->addError('destination_id', 'Destination not found.');
@@ -71,11 +79,14 @@ class ResourceOperations extends Component
}
if ($this->resource->getMorphClass() === Application::class) {
$new_resource = clone_application($this->resource, $new_destination, ['uuid' => $uuid], $this->cloneVolumeData);
$new_resource = clone_application($this->resource, $new_destination, [
'uuid' => $uuid,
'environment_id' => $new_environment->id,
], $this->cloneVolumeData);
$route = route('project.application.configuration', [
'project_uuid' => $this->projectUuid,
'environment_uuid' => $this->environmentUuid,
'project_uuid' => $new_environment->project->uuid,
'environment_uuid' => $new_environment->uuid,
'application_uuid' => $new_resource->uuid,
]).'#resource-operations';
@@ -100,6 +111,7 @@ class ResourceOperations extends Component
'name' => $this->resource->name.'-clone-'.$uuid,
'status' => 'exited',
'started_at' => null,
'environment_id' => $new_environment->id,
'destination_id' => $new_destination->id,
'destination_type' => $new_destination->getMorphClass(),
]);
@@ -211,8 +223,8 @@ class ResourceOperations extends Component
}
$route = route('project.database.configuration', [
'project_uuid' => $this->projectUuid,
'environment_uuid' => $this->environmentUuid,
'project_uuid' => $new_environment->project->uuid,
'environment_uuid' => $new_environment->uuid,
'database_uuid' => $new_resource->uuid,
]).'#resource-operations';
@@ -226,6 +238,7 @@ class ResourceOperations extends Component
])->fill([
'uuid' => $uuid,
'name' => $this->resource->name.'-clone-'.$uuid,
'environment_id' => $new_environment->id,
'destination_id' => $new_destination->id,
'destination_type' => $new_destination->getMorphClass(),
'server_id' => $new_destination->server_id,
@@ -354,8 +367,8 @@ class ResourceOperations extends Component
$new_resource->parse();
$route = route('project.service.configuration', [
'project_uuid' => $this->projectUuid,
'environment_uuid' => $this->environmentUuid,
'project_uuid' => $new_environment->project->uuid,
'environment_uuid' => $new_environment->uuid,
'service_uuid' => $new_resource->uuid,
]).'#resource-operations';
+5 -2
View File
@@ -36,6 +36,8 @@ class All extends Component
public bool $showActionsColumn = false;
public bool $showBackupAction = false;
public bool $isComposeOrService = false;
public bool $canUpdate = false;
@@ -47,7 +49,8 @@ class All extends Component
$this->canUpdate = (bool) auth()->user()?->can('update', $this->resource);
$this->supportsPreviewSuffix = $this->resource instanceof Application
&& $this->resource->git_based();
$this->showActionsColumn = $this->resource instanceof Application
$this->showActionsColumn = $this->canUpdate;
$this->showBackupAction = $this->resource instanceof Application
|| $this->resource instanceof ServiceApplication
|| $this->resource instanceof ServiceDatabase;
$this->isComposeOrService = $this->resource->type() === 'service'
@@ -175,7 +178,7 @@ class All extends Component
{
$this->volumeBackupMeta = [];
if (! $this->showActionsColumn) {
if (! $this->showBackupAction) {
return;
}
+1
View File
@@ -22,6 +22,7 @@ class Button extends Component
public ?string $canGate = null,
public mixed $canResource = null,
public bool $autoDisable = true,
public bool $isHighlighted = false,
public ?string $tooltip = null,
) {
// Handle authorization-based disabling