mirror of
https://github.com/coollabsio/coolify.git
synced 2026-09-25 17:01:49 -04:00
feat(previews): move pull request settings into a modal
Add pull request loading, refreshing, and deployment controls to the preview settings modal, simplify modal subtitles, and update sponsor listings.
This commit is contained in:
@@ -57,7 +57,9 @@ Thank you so much!
|
||||
|
||||
### Huge Sponsors
|
||||
|
||||
* [CubePath](https://cubepath.com/coolify) - Premium dedicated servers and cloud VPS hosting
|
||||
* [Context.dev](https://www.context.dev/) - Web scraping API for AI agents
|
||||
* [Ginernet](https://ginernet.com/) - Hosting powerful servers in Spain
|
||||
* [SerpAPI](https://serpapi.com) - Google Search API — Scrape Google and other search engines from our fast, easy, and complete API.
|
||||
* [MVPS](https://www.mvps.net) - Cheap VPS servers at the highest possible quality
|
||||
* [ScreenshotOne](https://screenshotone.com) - Screenshot API for devs
|
||||
@@ -67,6 +69,7 @@ Thank you so much!
|
||||
|
||||
### Big Sponsors
|
||||
|
||||
* [Vanaways](https://www.vanaways.co.uk) - New vans for sale and lease across the UK
|
||||
* [Cloudways](https://www.cloudways.com/en/?id=2125302) - Managed cloud hosting platform by DigitalOcean
|
||||
* [ByteBase](https://www.bytebase.com) - Database CI/CD and Security at Scale
|
||||
* [Ramnode](https://ramnode.com/) - High Performance Cloud VPS Hosting
|
||||
@@ -105,6 +108,9 @@ Thank you so much!
|
||||
|
||||
### Small Sponsors
|
||||
|
||||
<a href="https://darkvps.pro"><img width="60px" alt="DarkVPS" src="https://cdn.coollabs.io/sponsors/darkvps.png"/></a>
|
||||
<a href="https://www.opensourcealternatives.to"><img width="60px" alt="Open Source Alternatives" src="https://cdn.coollabs.io/sponsors/opensourcealternatives.png"/></a>
|
||||
<a href="https://onserva.com/"><img width="60px" alt="Onserva" src="https://onserva.com/icon.svg"/></a>
|
||||
<a href="https://www.movavi.com/mac.html?utm_source=coolify.io"><img width="60px" alt="Movavi" src="https://cdn.coollabs.io/sponsors/movavi.png"/></a>
|
||||
<a href="https://usefoil.com/"><img width="60px" alt="ABXY" src="https://usefoil.com/favicon.svg"/></a>
|
||||
<a href="https://www.launchfa.st/?utm_source=coolify.io"><img width="60px" alt="LaunchFast Boilerplates" src="https://github.com/LaunchFast-Boilerplates.png"/></a>
|
||||
@@ -133,6 +139,8 @@ Thank you so much!
|
||||
<a href="https://cirun.io/?utm_source=coolify.io"><img width="60px" alt="Cirun" src="https://cdn.coollabs.io/sponsors/cirun-logo.png"/></a>
|
||||
<a href="https://github.com/puls-digital-group"><img width="60px" alt="Puls Digital Group" src="https://github.com/puls-digital-group.png"/></a>
|
||||
<a href="https://github.com/jonathanprl"><img width="60px" alt="Jonathan Pereira" src="https://github.com/jonathanprl.png"/></a>
|
||||
<a href="https://outboundgateway.com/"><img width="60px" alt="OutboundGateway" src="https://github.com/OutboundGateway.png"/></a>
|
||||
<a href="https://github.com/t4dt"><img width="60px" alt="T4DT GmbH" src="https://github.com/t4dt.png"/></a>
|
||||
<a href="https://internetgarden.co/?utm_source=coolify.io"><img width="60px" alt="Internet Garden" src="https://cdn.coollabs.io/sponsors/internetgarden.ico"/></a>
|
||||
<a href="https://evercam.io/?utm_source=coolify.io"><img width="60px" alt="Evercam" src="https://github.com/evercam.png"/></a>
|
||||
<a href="https://web3.career/?utm_source=coolify.io"><img width="60px" alt="Web3 Jobs" src="https://cdn.coollabs.io/sponsors/web3jobs.png"/></a>
|
||||
|
||||
@@ -204,9 +204,6 @@
|
||||
],
|
||||
'project.application.preview-deployments' => array_values(array_filter([
|
||||
['id' => 'preview-template-section', 'label' => 'URL template'],
|
||||
$application->is_github_based()
|
||||
? ['id' => 'preview-pull-requests-section', 'label' => 'Pull requests']
|
||||
: null,
|
||||
$application->build_pack === 'dockerimage'
|
||||
? ['id' => 'manual-preview-section', 'label' => 'Manual preview']
|
||||
: null,
|
||||
|
||||
@@ -62,16 +62,9 @@
|
||||
style="box-shadow: 0 0 0 1px var(--coollabs-hairline), var(--shadow-modal)">
|
||||
<header class="flex-wrap! sm:flex-nowrap!">
|
||||
<div class="min-w-0 flex-1 py-0.5">
|
||||
<h3 class="truncate">{{ $title }}</h3>
|
||||
@if ($subtitle)
|
||||
<h3>
|
||||
<x-helper :helper="$subtitle" :label="'More information about '.$title">
|
||||
<x-slot:trigger>
|
||||
<span class="underline underline-offset-4">{{ $title }}</span>
|
||||
</x-slot:trigger>
|
||||
</x-helper>
|
||||
</h3>
|
||||
@else
|
||||
<h3 class="truncate">{{ $title }}</h3>
|
||||
<p class="mt-0.5 text-xs text-neutral-500 dark:text-fg-dim">{{ $subtitle }}</p>
|
||||
@endif
|
||||
</div>
|
||||
@isset($headerActions)
|
||||
|
||||
@@ -7,6 +7,70 @@
|
||||
helper="Automatic pull request deployments and who can trigger them.">
|
||||
<x-slot:actions>
|
||||
@can('update', $application)
|
||||
@if ($application->is_github_based())
|
||||
<x-modal-input title="Pull requests"
|
||||
subtitle="Load open pull requests from GitHub, then configure or deploy a preview."
|
||||
:wireIgnore="false" :isLarge="true">
|
||||
<x-slot:content>
|
||||
<x-forms.button wire:click="load_prs">
|
||||
Load pull requests
|
||||
</x-forms.button>
|
||||
</x-slot:content>
|
||||
<x-slot:headerActions>
|
||||
@isset($rate_limit_remaining)
|
||||
<span class="text-xs text-neutral-500 dark:text-fg-dim">
|
||||
{{ $rate_limit_remaining }} requests remaining
|
||||
</span>
|
||||
@endisset
|
||||
<x-forms.button wire:click="load_prs">
|
||||
Refresh
|
||||
</x-forms.button>
|
||||
</x-slot:headerActions>
|
||||
|
||||
<div class="flex min-h-48 items-center justify-center" wire:loading wire:target="load_prs">
|
||||
<x-loading text="Loading pull requests…" />
|
||||
</div>
|
||||
|
||||
<div class="-m-4" wire:loading.remove wire:target="load_prs">
|
||||
@forelse ($pull_requests as $pull_request)
|
||||
<div
|
||||
class="flex flex-col gap-3 border-b border-neutral-200 px-4 py-3.5 last:border-b-0 sm:flex-row sm:items-center dark:border-white/[0.07]">
|
||||
<div
|
||||
class="flex size-9 shrink-0 items-center justify-center rounded-lg bg-neutral-100 font-mono text-xs font-semibold text-neutral-600 ring-1 ring-neutral-200 dark:bg-white/[0.05] dark:text-fg-dim dark:ring-white/[0.07]">
|
||||
#{{ data_get($pull_request, 'number') }}
|
||||
</div>
|
||||
<div class="min-w-0 flex-1">
|
||||
<h4 class="truncate text-sm font-semibold text-black dark:text-fg">
|
||||
{{ data_get($pull_request, 'title') }}
|
||||
</h4>
|
||||
<a target="_blank"
|
||||
class="mt-1 inline-flex items-center gap-1 text-xs text-neutral-500 hover:text-coollabs dark:text-fg-dim dark:hover:text-warning"
|
||||
href="{{ data_get($pull_request, 'html_url') }}">
|
||||
Open on GitHub
|
||||
<x-external-link />
|
||||
</a>
|
||||
</div>
|
||||
<div class="flex shrink-0 items-center gap-2">
|
||||
<x-forms.button
|
||||
wire:click="add('{{ data_get($pull_request, 'number') }}', '{{ data_get($pull_request, 'html_url') }}')">
|
||||
Configure
|
||||
</x-forms.button>
|
||||
@can('deploy', $application)
|
||||
<x-forms.button
|
||||
wire:click="add_and_deploy('{{ data_get($pull_request, 'number') }}', '{{ data_get($pull_request, 'html_url') }}')">
|
||||
Deploy preview
|
||||
</x-forms.button>
|
||||
@endcan
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<x-empty size="sm" title="No open pull requests"
|
||||
description="No open pull requests were found for this repository."
|
||||
icon-name="sources" />
|
||||
@endforelse
|
||||
</div>
|
||||
</x-modal-input>
|
||||
@endif
|
||||
@if ($isPreviewDeploymentsEnabled)
|
||||
<x-forms.button wire:click="togglePreviewDeployments" wire:target="togglePreviewDeployments">
|
||||
Disable preview deployments
|
||||
@@ -39,65 +103,6 @@
|
||||
</x-callout>
|
||||
@endif
|
||||
|
||||
@if ($application->is_github_based())
|
||||
<x-application.settings-section id="preview-pull-requests-section" title="Pull requests"
|
||||
helper="Load open pull requests from GitHub, then configure or deploy a preview." flush>
|
||||
<x-slot:actions>
|
||||
@isset($rate_limit_remaining)
|
||||
<span class="text-xs text-neutral-500 dark:text-fg-dim">
|
||||
{{ $rate_limit_remaining }} requests remaining
|
||||
</span>
|
||||
@endisset
|
||||
@can('update', $application)
|
||||
<x-forms.button wire:click="load_prs">
|
||||
Load pull requests
|
||||
</x-forms.button>
|
||||
@endcan
|
||||
</x-slot:actions>
|
||||
|
||||
<div>
|
||||
@forelse ($pull_requests as $pull_request)
|
||||
<div
|
||||
class="flex flex-col gap-3 border-b border-neutral-200 px-4 py-3.5 last:border-b-0 sm:flex-row sm:items-center dark:border-white/[0.07]">
|
||||
<div
|
||||
class="flex size-9 shrink-0 items-center justify-center rounded-lg bg-neutral-100 font-mono text-xs font-semibold text-neutral-600 ring-1 ring-neutral-200 dark:bg-white/[0.05] dark:text-fg-dim dark:ring-white/[0.07]">
|
||||
#{{ data_get($pull_request, 'number') }}
|
||||
</div>
|
||||
<div class="min-w-0 flex-1">
|
||||
<h4 class="truncate text-sm font-semibold text-black dark:text-fg">
|
||||
{{ data_get($pull_request, 'title') }}
|
||||
</h4>
|
||||
<a target="_blank"
|
||||
class="mt-1 inline-flex items-center gap-1 text-xs text-neutral-500 hover:text-coollabs dark:text-fg-dim dark:hover:text-warning"
|
||||
href="{{ data_get($pull_request, 'html_url') }}">
|
||||
Open on GitHub
|
||||
<x-external-link />
|
||||
</a>
|
||||
</div>
|
||||
<div class="flex shrink-0 items-center gap-2">
|
||||
@can('update', $application)
|
||||
<x-forms.button
|
||||
wire:click="add('{{ data_get($pull_request, 'number') }}', '{{ data_get($pull_request, 'html_url') }}')">
|
||||
Configure
|
||||
</x-forms.button>
|
||||
@endcan
|
||||
@can('deploy', $application)
|
||||
<x-forms.button
|
||||
wire:click="add_and_deploy('{{ data_get($pull_request, 'number') }}', '{{ data_get($pull_request, 'html_url') }}')">
|
||||
Deploy preview
|
||||
</x-forms.button>
|
||||
@endcan
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<x-empty size="sm" title="No pull requests loaded"
|
||||
description="Load open pull requests from GitHub to configure a preview deployment."
|
||||
icon-name="sources" />
|
||||
@endforelse
|
||||
</div>
|
||||
</x-application.settings-section>
|
||||
@endif
|
||||
|
||||
@if ($application->build_pack === 'dockerimage')
|
||||
<x-application.settings-section id="manual-preview-section" title="Manual preview"
|
||||
helper="Deploy a preview directly from a Docker image tag.">
|
||||
|
||||
@@ -117,6 +117,21 @@ it('renders preview deployment enablement as a section action', function () {
|
||||
expect($this->application->fresh()->settings->is_preview_deployments_enabled)->toBeTrue();
|
||||
});
|
||||
|
||||
it('renders GitHub pull requests in a modal opened from the preview settings', function () {
|
||||
$view = file_get_contents(resource_path('views/livewire/project/application/previews.blade.php'));
|
||||
$sidebar = file_get_contents(resource_path('views/components/application/configuration-sidebar.blade.php'));
|
||||
|
||||
expect($view)
|
||||
->toContain('<x-modal-input title="Pull requests"')
|
||||
->toContain(':isLarge="true"')
|
||||
->toContain('wire:click="load_prs"')
|
||||
->not->toContain('id="preview-pull-requests-section"')
|
||||
->and(strpos($view, '<x-modal-input title="Pull requests"'))
|
||||
->toBeLessThan(strpos($view, '<livewire:project.application.preview.form'));
|
||||
|
||||
expect($sidebar)->not->toContain("['id' => 'preview-pull-requests-section', 'label' => 'Pull requests']");
|
||||
});
|
||||
|
||||
it('does not show git preview settings for non-git applications', function (string $buildPack, ?string $dockerfile) {
|
||||
$this->application->update(['build_pack' => $buildPack, 'dockerfile' => $dockerfile]);
|
||||
|
||||
|
||||
@@ -17,6 +17,16 @@ test('input modal overlay is fixed to the viewport without its own page scrollba
|
||||
->not->toContain('class="fixed inset-0 z-99 overflow-y-auto"');
|
||||
});
|
||||
|
||||
test('input modal renders its subtitle as static text instead of a tooltip', function () {
|
||||
$html = Blade::render('<x-modal-input title="Pull requests" subtitle="Load repository pull requests.">Modal content</x-modal-input>');
|
||||
|
||||
expect($html)
|
||||
->toContain('Load repository pull requests.')
|
||||
->toContain('text-xs text-neutral-500 dark:text-fg-dim')
|
||||
->not->toContain('More information about Pull requests')
|
||||
->not->toContain('role="tooltip"');
|
||||
});
|
||||
|
||||
test('confirmation modal closes before dispatching an event that can open another modal', function () {
|
||||
$modal = file_get_contents(resource_path('views/components/modal-confirmation.blade.php'));
|
||||
|
||||
|
||||
@@ -441,14 +441,14 @@ it('centers the rollback image loading state across the card', function () {
|
||||
->toContain('flex items-center justify-center');
|
||||
});
|
||||
|
||||
it('shows pull request loading feedback only on the action button', function () {
|
||||
it('shows pull request loading feedback in the modal body', function () {
|
||||
$previews = file_get_contents(resource_path('views/livewire/project/application/previews.blade.php'));
|
||||
|
||||
expect($previews)
|
||||
->toContain('wire:click="load_prs"')
|
||||
->not->toContain('wire:loading.remove wire:target="load_prs"')
|
||||
->not->toContain('wire:loading wire:target="load_prs"')
|
||||
->not->toContain('Loading pull requests…');
|
||||
->toContain('wire:loading.remove wire:target="load_prs"')
|
||||
->toContain('wire:loading wire:target="load_prs"')
|
||||
->toContain('Loading pull requests…');
|
||||
});
|
||||
|
||||
it('shows loading feedback while a service deployment starts', function () {
|
||||
|
||||
Reference in New Issue
Block a user