2026-08-03 21:11:54 +00:00
< div class = " flex flex-col gap-6 " >
2026-09-07 16:29:58 +00:00
@ if ( $application -> git_based ())
@ php
$canUpdate = auth () -> user () -> can ( 'update' , $application );
@ endphp
< x - application . settings - section id = " preview-settings-section " title = " Preview settings "
helper = " Automatic pull request deployments and who can trigger them. " >
2026-09-08 08:21:34 +00:00
< x - slot : actions >
@ can ( 'update' , $application )
@ if ( $isPreviewDeploymentsEnabled )
< x - forms . button wire : click = " togglePreviewDeployments " wire : target = " togglePreviewDeployments " >
Disable preview deployments
</ x - forms . button >
@ else
< x - forms . button wire : click = " togglePreviewDeployments " wire : target = " togglePreviewDeployments "
isHighlighted >
Enable preview deployments
</ x - forms . button >
@ endif
@ endcan
</ x - slot : actions >
< div class = " w-full " >
2026-09-07 16:29:58 +00:00
< x - forms . listbox id = " isPrDeploymentsPublicEnabled " label = " PR deployment access " onChange = " savePreviewSettings "
helper = " When public, anyone can trigger PR deployments. Otherwise fork PRs are blocked and only repository owners, members, and collaborators can trigger them. "
: options = " [
[ 'value' => false , 'label' => 'Repository members only' ],
[ 'value' => true , 'label' => 'Public (fork PRs allowed)' ],
] " :disabled= " ! $canUpdate || ! $isPreviewDeploymentsEnabled " />
</ div >
</ x - application . settings - section >
@ endif
2023-05-30 13:52:17 +00:00
< livewire : project . application . preview . form : application = " $application " />
2026-08-03 21:11:54 +00:00
2024-02-11 16:29:14 +00:00
@ if ( count ( $application -> additional_servers ) > 0 )
2026-08-03 21:11:54 +00:00
< x - callout type = " info " title = " Preview deployment server " >
Preview deployments run on {{ $application -> destination -> server -> name }} .
</ x - callout >
2024-02-11 16:29:14 +00:00
@ endif
2026-08-03 21:11:54 +00:00
@ 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
2025-08-26 08:27:31 +00:00
@ can ( 'update' , $application )
2026-08-03 21:11:54 +00:00
< x - forms . button wire : click = " load_prs " >
Load pull requests
2025-08-26 08:27:31 +00:00
</ x - forms . button >
@ endcan
2026-08-03 21:11:54 +00:00
</ x - slot : actions >
2026-08-06 16:15:58 +00:00
< div >
2026-08-03 21:11:54 +00:00
@ 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
2024-01-29 09:43:18 +00:00
</ div >
2026-08-03 21:11:54 +00:00
</ x - application . settings - section >
@ endif
2026-03-30 11:35:35 +00:00
@ if ( $application -> build_pack === 'dockerimage' )
2026-08-03 21:11:54 +00:00
< x - application . settings - section id = " manual-preview-section " title = " Manual preview "
helper = " Deploy a preview directly from a Docker image tag. " >
< form wire : submit . prevent = " addDockerImagePreview "
class = " grid gap-4 md:grid-cols-[minmax(0,1fr)_minmax(0,1fr)_auto] md:items-end " >
< x - forms . input id = " manualPullRequestId " label = " Preview ID "
helper = " Used for domains, logs, container names, and cleanup. " />
< x - forms . input id = " manualDockerTag " label = " Docker tag "
helper = " For example, pr_1234. " />
2026-03-30 11:35:35 +00:00
@ can ( 'deploy' , $application )
2026-08-03 21:11:54 +00:00
< x - forms . button type = " submit " > Deploy preview </ x - forms . button >
2026-03-30 11:35:35 +00:00
@ endcan
</ form >
2026-08-03 21:11:54 +00:00
</ x - application . settings - section >
2026-03-30 11:35:35 +00:00
@ endif
2024-06-05 13:14:44 +00:00
2026-08-03 21:11:54 +00:00
< x - application . settings - section id = " preview-deployments-section " title = " Preview deployments "
helper = " Manage domains, deployments, logs, and lifecycle actions for configured previews. " flush >
@ forelse ( data_get ( $application , 'previews' ) as $previewName => $preview )
@ php
$previewStatus = str ( data_get ( $preview , 'status' ));
$previewIsStopped = $previewStatus -> startsWith ( 'exited' );
@ endphp
< section class = " border-b border-neutral-200 p-4 last:border-b-0 dark:border-white/[0.07] "
wire : key = " preview-container- { { $preview->pull_request_id }} " >
< div class = " flex flex-col gap-3 lg:flex-row lg:items-start lg:justify-between " >
< div class = " flex min-w-0 items-center gap-3 " >
< 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($preview, 'pull_request_id') }}
2024-06-05 13:14:44 +00:00
</ div >
2026-08-03 21:11:54 +00:00
< div class = " min-w-0 " >
< div class = " flex flex-wrap items-center gap-2 " >
< h4 class = " text-sm font-semibold text-black dark:text-fg " >
Preview #{{ data_get($preview, 'pull_request_id') }}
</ h4 >
2026-08-06 16:15:58 +00:00
< x - status - summary : status = " data_get( $preview , 'status') " title = " Preview status " />
2026-08-31 11:13:26 +00:00
< x - application . restart - limit - warning : application = " $preview " />
2026-08-03 21:11:54 +00:00
</ div >
2026-08-06 16:15:58 +00:00
</ div >
</ div >
< div id = " preview-header-controls- { { data_get( $preview , 'pull_request_id') }} "
class = " flex shrink-0 flex-wrap items-center gap-2 lg:justify-end " >
< div class = " relative " x - data = " { open: false } " @ click . outside = " open = false "
@ keydown . escape . window = " open = false " >
< button type = " button " class = " button gap-1.5 " title = " Preview links " @ click = " open = !open "
: aria - expanded = " open " aria - haspopup = " menu " >
< x - reicon name = " external-link " class = " size-3.5 opacity-70 " />
Links
< x - reicon name = " chevron-down " class = " size-3 opacity-55 " />
</ button >
< div x - cloak x - show = " open " x - transition . origin . top . right
class = " listbox-panel top-full! right-0! left-auto! z-[90]! mt-1! w-56! min-w-56! "
role = " menu " >
2026-08-03 21:11:54 +00:00
@ if ( ! $previewIsStopped && filled ( data_get ( $preview , 'fqdn' )))
2026-08-06 16:15:58 +00:00
< a target = " _blank " title = " Open preview in a new tab "
class = " listbox-option justify-start! gap-2.5! "
href = " { { data_get( $preview , 'fqdn') }} " @ click = " open = false " role = " menuitem " >
< x - reicon name = " external-link " class = " size-3.5 opacity-70 " />
< span class = " min-w-0 truncate " > Open preview </ span >
</ a >
2026-08-03 21:11:54 +00:00
@ endif
@ if ( filled ( data_get ( $preview , 'pull_request_html_url' )))
2026-08-06 16:15:58 +00:00
< a target = " _blank " title = " Open pull request in a new tab "
class = " listbox-option justify-start! gap-2.5! "
href = " { { data_get( $preview , 'pull_request_html_url') }} " @ click = " open = false "
role = " menuitem " >
< x - reicon name = " external-link " class = " size-3.5 opacity-70 " />
< span class = " min-w-0 truncate " > Open pull request </ span >
</ a >
2026-08-03 21:11:54 +00:00
@ endif
2026-08-06 16:15:58 +00:00
</ div >
</ div >
@ if ( count ( $parameters ) > 0 )
< div class = " relative " x - data = " { open: false } " @ click . outside = " open = false "
@ keydown . escape . window = " open = false " >
< button type = " button " class = " button gap-1.5 " title = " Preview logs " @ click = " open = !open "
: aria - expanded = " open " aria - haspopup = " menu " >
< x - reicon name = " browser-terminal " class = " size-3.5 opacity-70 " />
Logs
< x - reicon name = " chevron-down " class = " size-3 opacity-55 " />
</ button >
< div x - cloak x - show = " open " x - transition . origin . top . right
class = " listbox-panel top-full! right-0! left-auto! z-[90]! mt-1! w-44! min-w-44! "
role = " menu " >
< a {{ wireNavigate () }} class = " listbox-option justify-start! gap-2.5! "
href = " { { route('project.application.deployment.index', [... $parameters , 'pull_request_id' => data_get( $preview , 'pull_request_id')]) }} "
@ click = " open = false " role = " menuitem " >
< x - reicon name = " graph " class = " size-3.5 opacity-70 " />
2026-08-03 21:11:54 +00:00
Deployment logs
</ a >
2026-08-06 16:15:58 +00:00
< a {{ wireNavigate () }} class = " listbox-option justify-start! gap-2.5! "
href = " { { route('project.application.logs', [... $parameters , 'pull_request_id' => data_get( $preview , 'pull_request_id')]) }} "
@ click = " open = false " role = " menuitem " >
< x - reicon name = " browser-terminal " class = " size-3.5 opacity-70 " />
Runtime logs
2026-08-03 21:11:54 +00:00
</ a >
2026-08-06 16:15:58 +00:00
</ div >
2026-08-03 21:11:54 +00:00
</ div >
2026-08-06 16:15:58 +00:00
@ endif
< div class = " relative " x - data = " { open: false } " @ click . outside = " open = false "
@ keydown . escape . window = " open = false " >
< button type = " button " class = " button gap-1.5 " title = " Preview actions " @ click = " open = !open "
: aria - expanded = " open " aria - haspopup = " menu " >
Actions
< span class = " inline-flex transition-transform " : class = " open && 'rotate-180' " >
< x - reicon name = " chevron-down " class = " size-3 opacity-55 " />
</ span >
</ button >
< div x - cloak x - show = " open " x - transition . origin . top . right
class = " listbox-panel top-full! right-0! left-auto! z-[90]! mt-1! w-52! min-w-52! "
role = " menu " >
@ can ( 'deploy' , $application )
< button type = " button " class = " listbox-option justify-start! gap-2.5! "
wire : click = " force_deploy_without_cache( { { data_get( $preview , 'pull_request_id') }}) "
@ click = " open = false " role = " menuitem " >
< x - reicon name = " refresh " class = " size-3.5 opacity-70 " />
Rebuild
</ button >
< button type = " button " class = " listbox-option justify-start! gap-2.5! "
wire : click = " deploy( { { data_get( $preview , 'pull_request_id') }}, null, false, ' { { data_get( $preview , 'docker_registry_image_tag') }}') "
@ click = " open = false " role = " menuitem " >
< x - reicon name = " play-circle " class = " size-3.5 opacity-70 " />
{{ $previewIsStopped ? 'Deploy' : 'Redeploy' }}
</ button >
@ if ( ! $previewIsStopped )
< button type = " button "
class = " listbox-option justify-start! gap-2.5! text-error! "
@ click = " open = false; document.getElementById('preview-stop-trigger- { { data_get( $preview , 'pull_request_id') }}')?.click() "
role = " menuitem " >
< x - reicon name = " stop " class = " size-3.5 " />
Stop
</ button >
@ endif
@ endcan
@ can ( 'delete' , $application )
< button type = " button "
class = " listbox-option justify-start! gap-2.5! text-error! "
@ click = " open = false; document.getElementById('preview-delete-trigger- { { data_get( $preview , 'pull_request_id') }}')?.click() "
role = " menuitem " >
< x - reicon name = " trash " class = " size-3.5 " />
Delete
</ button >
@ endcan
2026-08-03 21:11:54 +00:00
</ div >
</ div >
2026-08-06 16:15:58 +00:00
</ div >
2026-08-03 21:11:54 +00:00
2026-08-06 16:15:58 +00:00
< div class = " hidden " aria - hidden = " true " >
2026-08-03 21:11:54 +00:00
@ if ( ! $previewIsStopped )
2025-08-26 08:27:31 +00:00
@ can ( 'deploy' , $application )
2026-08-03 21:11:54 +00:00
< x - modal - confirmation title = " Stop preview deployment? " buttonTitle = " Stop "
submitAction = " stop( { { data_get( $preview , 'pull_request_id') }}) "
: actions = " [
2025-08-26 08:27:31 +00:00
'This preview deployment will be stopped.' ,
2026-08-03 21:11:54 +00:00
'All non-persistent preview data will be removed.' ,
2025-08-26 08:27:31 +00:00
] "
2026-08-03 21:11:54 +00:00
: confirmWithText = " false " : confirmWithPassword = " false "
step2ButtonText = " Stop preview deployment " >
2026-08-06 16:15:58 +00:00
< x - slot : trigger >
< button id = " preview-stop-trigger- { { data_get( $preview , 'pull_request_id') }} "
type = " button " ></ button >
</ x - slot : trigger >
2025-08-26 08:27:31 +00:00
</ x - modal - confirmation >
@ endcan
2024-05-30 10:28:29 +00:00
@ endif
2025-08-26 08:27:31 +00:00
@ can ( 'delete' , $application )
2026-08-03 21:11:54 +00:00
< x - modal - confirmation title = " Delete preview deployment? " buttonTitle = " Delete "
2025-08-26 08:27:31 +00:00
isErrorButton submitAction = " delete( { { data_get( $preview , 'pull_request_id') }}) "
2026-08-03 21:11:54 +00:00
: actions = " ['All containers for this preview deployment will be stopped and permanently deleted.'] "
confirmationText = " { { data_get( $preview , 'fqdn') . '/' }} "
confirmationLabel = " Enter the preview deployment name to confirm deletion "
2026-08-06 16:15:58 +00:00
shortConfirmationLabel = " Preview deployment name " : confirmWithPassword = " false " >
< x - slot : trigger >
< button id = " preview-delete-trigger- { { data_get( $preview , 'pull_request_id') }} "
type = " button " ></ button >
</ x - slot : trigger >
</ x - modal - confirmation >
2025-08-26 08:27:31 +00:00
@ endcan
2023-05-30 13:52:17 +00:00
</ div >
</ div >
2026-08-03 21:11:54 +00:00
< div class = " mt-4 border-t border-neutral-200 pt-4 dark:border-white/[0.07] " >
2026-08-30 14:20:16 +00:00
< livewire : project . application . preview - domains
wire : key = " preview-domains- { { $preview->id }} "
: preview = " $preview " />
@ if ( $application -> build_pack === 'dockerimage' )
2026-08-03 21:11:54 +00:00
< form wire : submit = " save_preview(' { { $preview->id }}') "
2026-08-30 14:20:16 +00:00
class = " application-settings-section-body is-flush mt-3 overflow-visible " >
< div class = " data-table-header grid-cols-1 " >< span > Docker tag </ span ></ div >
< div class = " p-3 " >
< x - forms . input id = " previewDockerTags. { { $previewName }} " canGate = " update "
2026-08-03 21:11:54 +00:00
: canResource = " $application "
wire : change = " save_preview(' { { $preview->id }}') " />
2026-08-30 14:20:16 +00:00
</ div >
2026-08-03 21:11:54 +00:00
</ form >
@ endif
</ div >
</ section >
@ empty
< x - empty title = " No preview deployments "
description = " Configure a pull request or manual preview to create an isolated deployment. "
icon - name = " eye " />
@ endforelse
</ x - application . settings - section >
2023-05-16 11:11:05 +00:00
</ div >