From 2d4b2823bd62df54e0badc18a6fc488049a271c0 Mon Sep 17 00:00:00 2001 From: Tylan Davis Date: Thu, 6 Feb 2025 14:51:29 -0500 Subject: [PATCH] adds modal for selecting redeploy options --- .../(overview)/InfraOnline/index.module.scss | 114 +++++++++ .../(tabs)/(overview)/InfraOnline/index.tsx | 228 +++++++++++++++--- 2 files changed, 315 insertions(+), 27 deletions(-) diff --git a/src/app/(frontend)/(cloud)/cloud/[team-slug]/[project-slug]/(tabs)/(overview)/InfraOnline/index.module.scss b/src/app/(frontend)/(cloud)/cloud/[team-slug]/[project-slug]/(tabs)/(overview)/InfraOnline/index.module.scss index 081541df4..f08ccff15 100644 --- a/src/app/(frontend)/(cloud)/cloud/[team-slug]/[project-slug]/(tabs)/(overview)/InfraOnline/index.module.scss +++ b/src/app/(frontend)/(cloud)/cloud/[team-slug]/[project-slug]/(tabs)/(overview)/InfraOnline/index.module.scss @@ -149,3 +149,117 @@ opacity: 0.5; } } + +.redeployModal > div { + display: flex; + flex-direction: column; + padding: 0; + margin: 0; + overflow: hidden; + + .modalHeader { + display: flex; + justify-content: space-between; + align-items: center; + padding: 2rem 2rem 1.5rem; + + h4 { + margin: 0; + } + + .modalCloseButton { + appearance: none; + border: none; + background-color: transparent; + cursor: pointer; + padding: 0; + margin: 0; + height: 100%; + width: auto; + aspect-ratio: 1; + display: flex; + align-items: center; + justify-content: center; + } + } + + .modalTabs { + padding-inline: 2rem; + + button { + @include body; + & { + font-family: var(--font-body); + letter-spacing: normal; + } + } + } + + .modalContent { + padding: 0 2rem 2rem; + overflow: hidden; + + p { + margin: 0 0 1rem; + + ul { + padding: 1rem 1.5rem; + } + } + + hr { + margin: 1rem 0; + } + + .modalActions { + display: flex; + justify-content: flex-end; + gap: 1rem; + align-items: center; + } + + label { + display: flex; + gap: 0.75rem; + padding: 1rem; + background-color: var(--theme-elevation-100); + border: 1px solid var(--theme-border-color); + cursor: pointer; + transition-property: background-color, border; + transition-duration: 0.1s; + transition-timing-function: ease-in-out; + + input { + width: 16px; + height: 16px; + @include body; + display: inline; + vertical-align: middle; + line-height: 1; + cursor: pointer; + flex-shrink: 0; + } + + p { + margin: 0; + } + + &:hover { + border: 1px solid var(--theme-elevation-350); + } + + &:has(input:checked) { + background-color: var(--theme-elevation-150); + border: 1px solid var(--theme-elevation-500); + } + + @include data-theme-selector('light') { + background-color: var(--theme-elevation-50); + + &:has(input:checked) { + background-color: var(--theme-elevation-100); + } + } + } + } +} diff --git a/src/app/(frontend)/(cloud)/cloud/[team-slug]/[project-slug]/(tabs)/(overview)/InfraOnline/index.tsx b/src/app/(frontend)/(cloud)/cloud/[team-slug]/[project-slug]/(tabs)/(overview)/InfraOnline/index.tsx index 242de8152..9b233e72f 100644 --- a/src/app/(frontend)/(cloud)/cloud/[team-slug]/[project-slug]/(tabs)/(overview)/InfraOnline/index.tsx +++ b/src/app/(frontend)/(cloud)/cloud/[team-slug]/[project-slug]/(tabs)/(overview)/InfraOnline/index.tsx @@ -2,9 +2,15 @@ import type { Deployment, Project } from '@root/payload-cloud-types.js' +import { Tabs } from '@cloud/_components/Tabs/index.js' import { BackgroundScanline } from '@components/BackgroundScanline/index.js' +import { Button } from '@components/Button/index.js' import { Gutter } from '@components/Gutter/index.js' import { Indicator } from '@components/Indicator/index.js' +import { ModalWindow } from '@components/ModalWindow/index.js' +import { useModal } from '@faceless-ui/modal' +import Submit from '@forms/Submit/index.js' +import { CloseIcon } from '@icons/CloseIcon/index.js' import { CommitIcon } from '@root/graphics/CommitIcon/index.js' import { GitHubIcon } from '@root/graphics/GitHub/index.js' import { ArrowIcon } from '@root/icons/ArrowIcon/index.js' @@ -40,38 +46,76 @@ export const InfraOnline: React.FC<{ const [liveDeployment, setLiveDeployment] = React.useState() const [redeployTriggered, setRedeployTriggered] = React.useState(false) + const [redeployModalTabIndex, setRedeployModalTabIndex] = React.useState(0) - const triggerDeployment = React.useCallback(() => { - setRedeployTriggered(true) - const query = qs.stringify({ - env: environmentSlug, - }) - - fetch( - `${process.env.NEXT_PUBLIC_CLOUD_CMS_URL}/api/projects/${project?.id}/deploy${ - query ? `?${query}` : '' - }`, - { - credentials: 'include', - method: 'POST', + const { closeModal, openModal } = useModal() + + const handleModalClose = React.useCallback( + (slug: string) => { + closeModal(slug) + setRedeployModalTabIndex(0) + }, + [closeModal], + ) + + const triggerDeployment = React.useCallback( + ( + type: 'deploy' | 'force-rebuild' | 'restart', + options: { + clearBuildCache?: boolean }, - ).then((res) => { - setRedeployTriggered(false) + ) => { + setRedeployTriggered(true) - if (res.status === 200) { - reloadDeployments() - return toast.success('New deployment triggered successfully.') + const body = { + type, + options, } - if (res.status === 429) { - return toast.error( - 'You can only manually deploy once per minute. Please wait and try again.', - ) - } + const query = qs.stringify({ + env: environmentSlug, + }) - return toast.error('Failed to deploy') - }) - }, [project?.id, reloadDeployments]) + fetch( + ` + ${process.env.NEXT_PUBLIC_CLOUD_CMS_URL}/api/projects/${project?.id}/deploy${ + query ? `?${query}` : '' + }`, + { + body: JSON.stringify(body), + credentials: 'include', + headers: { + 'Content-Type': 'application/json', + }, + method: 'POST', + }, + ) + .then((res) => { + setRedeployTriggered(false) + + if (res.status === 200) { + handleModalClose('redeploy-modal') + reloadDeployments() + return toast.success('New deployment triggered successfully.') + } + + if (res.status === 429) { + return toast.error( + 'You can only manually deploy once per minute. Please wait and try again.', + ) + } + + handleModalClose('redeploy-modal') + return toast.error('Failed to deploy') + }) + .catch(() => { + setRedeployTriggered(false) + handleModalClose('redeploy-modal') + toast.error('Failed to deploy') + }) + }, + [environmentSlug, handleModalClose, project?.id, reloadDeployments], + ) // Poll deployments every 10 seconds React.useEffect(() => { @@ -260,13 +304,143 @@ export const InfraOnline: React.FC<{ + +
+

Redeploy

+ +
+ setRedeployModalTabIndex(0), + }, + { + isActive: redeployModalTabIndex === 1, + label: 'Force rebuild and deploy', + onClick: () => setRedeployModalTabIndex(1), + }, + { + isActive: redeployModalTabIndex === 2, + label: 'Restart', + onClick: () => setRedeployModalTabIndex(2), + }, + ]} + /> + {redeployModalTabIndex === 0 ? ( + // Deploy +
{ + e.preventDefault() + triggerDeployment('deploy', {}) + }} + > +

Deploying your app will:

+
    +
  • Fetch any updates from your source repository and rebuild if needed
  • +
  • Not result in any down time
  • +
+
+
+
+
+ ) : redeployModalTabIndex === 1 ? ( + // Force rebuild and deploy +
{ + e.preventDefault() + triggerDeployment('force-rebuild', { + clearBuildCache: e.target['clear-build-cache'].checked, + }) + }} + > +

Force rebuild and deploy will:

+
    +
  • + Rebuild your entire app from the source repository with the latest updates, even + if the source code has not changed +
  • +
  • Not result in any down time
  • +
+ +
+
+
+
+ ) : ( + // Restart +
{ + e.preventDefault() + triggerDeployment('restart', {}) + }} + > +

Restarting your app will:

+
    +
  • Help fix an app that is stuck in a connection loop or a deadlock
  • +
  • Not fetch any updates from your source repository
  • +
  • Not result in any down time
  • +
+
+
+
+
+ )} +
{deployments?.length > 0 && (