moved some components around

This commit is contained in:
Jonathan Summers-Muir committed 2024-10-29 16:11:36 +08:00
1 parent a695060b8e
commit 994953a4d0
11 files changed
+163 -130

No files matched your search

+5
View File
@@ -0,0 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
@@ -47,13 +47,17 @@ import {
} from './ui/DiskManagement.constants'
import { DiskManagementPlanUpgradeRequired } from './ui/DiskManagementPlanUpgradeRequired'
import { NoticeBar } from './ui/NoticeBar'
import { DiskMangementRestartRequiredSection } from './DiskManagementRestartRequiredSection'
import { useQueryClient } from '@tanstack/react-query'
import { PROJECT_STATUS } from 'lib/constants'
import { setProjectStatus } from 'data/projects/projects-query'
export function DiskManagementForm() {
const showDiskAndComputeForm = useFlag('diskAndComputeForm')
const { project } = useProjectContext()
const org = useSelectedOrganization()
const { ref: projectRef } = useParams()
const queryClient = useQueryClient()
const [isDialogOpen, setIsDialogOpen] = useState<boolean>(false)
const [remainingTime, setRemainingTime] = useState(0)
@@ -229,7 +233,11 @@ export function DiskManagementForm() {
const { mutateAsync: updateSubscriptionAddon, isLoading: isUpdatingCompute } =
useProjectAddonUpdateMutation({
onSuccess: () => {
setShowRestartPendingState(true)
/**
* Manually set project status to RESIZING
* Project status should be RESIZING on next project status request.
*/
setProjectStatus(queryClient, projectRef!, PROJECT_STATUS.RESIZING)
},
})
@@ -311,28 +319,33 @@ export function DiskManagementForm() {
// return <></>
const isDirty = !!Object.keys(form.formState.dirtyFields).length
const isProjectResizing = project?.status === PROJECT_STATUS.RESIZING
const isProjectRequestingDiskChanges = isRequestingChanges && !isProjectResizing
return (
<>
{isProjectResizing || isProjectRequestingDiskChanges ? (
<ScaffoldContainer className="relative flex flex-col gap-10" bottomPadding>
<DiskMangementRestartRequiredSection
visible={isProjectResizing}
title="Your project will now automatically restart."
description="Your project will be unavailable for up to 2 mins."
/>
<NoticeBar
visible={isProjectRequestingDiskChanges}
title="Disk configuration changes have been requested"
description="The requested changes will be applied to your disk shortly"
/>
</ScaffoldContainer>
) : null}
<Form_Shadcn_ {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="flex flex-col gap-8">
<ScaffoldContainer className="relative flex flex-col gap-10" bottomPadding>
{showRestartPending ? (
<NoticeBar
title="Project about to restart"
description={`This project is about to restart to change compute to ${formatComputeName(watchedComputeSize)}.`}
/>
) : isRequestingChanges ? (
<NoticeBar
title="Disk configuration changes have been requested"
description="The requested changes will be applied to your disk shortly"
/>
) : null}
<Separator />
<ComputeSizeField form={form} />
<Separator />
<DiskSizeField form={form} disableInput={disableInput} />
<DiskCountdownRadial />
<DiskSizeField form={form} disableInput={disableInput} />
{isPlanUpgradeRequired && <DiskManagementPlanUpgradeRequired />}
<Separator />
<Collapsible_Shadcn_
@@ -1,42 +1,39 @@
import { AnimatePresence, motion } from 'framer-motion'
import { RotateCcw, X } from 'lucide-react'
import React, { useState } from 'react'
import { DialogSection } from 'ui'
import { RotateCcw } from 'lucide-react'
import React from 'react'
import { DialogSection, WarningIcon } from 'ui'
export const DiskMangementRestartRequiredSection: React.FC = () => {
const [progress, setProgress] = useState(100)
const [showCountdown, setShowCountdown] = useState(false)
const [isJumping, setIsJumping] = useState(false)
const [isVisible, setIsVisible] = useState(true)
interface Props {
visible: boolean
title: string
description: string
}
export const DiskMangementRestartRequiredSection: React.FC<Props> = ({
visible,
title,
description,
}) => {
return (
<AnimatePresence>
{isVisible && (
{visible && (
<motion.div
initial={{ opacity: 1, height: 'auto' }}
exit={{ opacity: 0, height: 0 }}
transition={{ duration: 0.15 }}
className="w-full"
>
<DialogSection className="bg-surface-100 text-sm text-foreground-light flex items-center gap-4 relative w-full border rounded-md">
<div className="w-12 h-12 opacity-75">
<div className="w-10 h-10 m-1 bg-studio border rounded-md flex items-center justify-center">
<DialogSection className="bg-surface-100 text-sm text-foreground-light flex items-center gap-4 relative w-full rounded-md border-spacing-0 border">
<div className="w-12 h-12">
<div className="relative w-10 h-10 m-1 bg-alternative text-foreground border rounded-full flex items-center justify-center">
<RotateCcw strokeWidth={1} />
<WarningIcon className="absolute -right-2 -top-2" />
</div>
</div>
<div className="flex flex-col gap-0 flex-grow">
<p className="text-sm text-foreground">A Project restart will be required.</p>
<p className="text-sm text-foreground-light">
You can restart after confirming changes or when convenient.
</p>
<p className="text-sm text-foreground">{title}</p>
<p className="text-sm text-foreground-light">{description}</p>
</div>
<button
type="button"
className="text-foreground-lighter hover:text-foreground-light transition-colors self-start p-1"
onClick={() => setIsVisible(false)}
>
<X size={14} />
</button>
</DialogSection>
</motion.div>
)}
@@ -1,12 +1,14 @@
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { ChevronRight } from 'lucide-react'
import { UseFormReturn } from 'react-hook-form'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query'
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
import { formatCurrency } from 'lib/helpers'
import { ChevronRight } from 'lucide-react'
import { useMemo } from 'react'
import { UseFormReturn } from 'react-hook-form'
import {
Alert_Shadcn_,
AlertTitle_Shadcn_,
@@ -28,8 +30,7 @@ import {
TableRow,
WarningIcon,
} from 'ui'
import { BillingChangeBadge } from './ui/BillingChangeBadge'
import { DiskType } from './ui/DiskManagement.constants'
import { DiskStorageSchemaType } from './DiskManagement.schema'
import {
calculateComputeSizePrice,
calculateDiskSizePrice,
@@ -37,12 +38,10 @@ import {
calculateThroughputPrice,
getAvailableComputeOptions,
} from './DiskManagement.utils'
import { DiskMangementCoolDownSection } from './ui/DiskManagementCoolDownSection'
import { DiskStorageSchemaType } from './DiskManagement.schema'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { useMemo } from 'react'
import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query'
import { DiskMangementRestartRequiredSection } from './DiskManagementRestartRequiredSection'
import { BillingChangeBadge } from './ui/BillingChangeBadge'
import { DiskType } from './ui/DiskManagement.constants'
import { DiskMangementCoolDownSection } from './ui/DiskManagementCoolDownSection'
const TableHeaderRow = () => (
<TableRow>
@@ -249,7 +248,15 @@ export const DiskManagementReviewAndSubmitDialog = ({
<DialogDescription>Changes will be applied shortly once confirmed.</DialogDescription>
</DialogHeader>
<DialogSectionSeparator />
<div className="flex flex-col gap-2 p-5">
<DiskMangementRestartRequiredSection
visible={form.formState.defaultValues?.computeSize !== form.getValues('computeSize')}
title="Your project will be unavailable for up to 2 mins."
description="Project will restart automatically on confirmation."
/>
{hasDiskConfigChanges && <DiskMangementCoolDownSection />}
</div>
<DialogSectionSeparator />
<Table>
<TableHeader className="font-mono uppercase text-xs [&_th]:h-auto [&_th]:pb-2 [&_th]:pt-4">
<TableHeaderRow />
@@ -314,23 +321,17 @@ export const DiskManagementReviewAndSubmitDialog = ({
</TableBody>
</Table>
<div className="flex flex-col gap-2 p-5">
{form.formState.defaultValues?.computeSize !== form.getValues('computeSize') && (
<DiskMangementRestartRequiredSection />
)}
{hasDiskConfigChanges && <DiskMangementCoolDownSection />}
</div>
{/* <DialogSectionSeparator /> */}
<DialogFooter>
<Button block size="small" type="default" onClick={() => setIsDialogOpen(false)}>
<Button block size="large" type="default" onClick={() => setIsDialogOpen(false)}>
Cancel
</Button>
<Button
block
size="small"
type={'warning'}
size="large"
htmlType="submit"
loading={loading}
onClick={async () => {
@@ -81,7 +81,7 @@ export function ComputeSizeField({ form }: ComputeSizeFieldProps) {
}
>
<RadioGroupCard
className="grid grid-cols-3 flex-wrap gap-3"
className="grid grid-cols-2 xl:grid-cols-3 flex-wrap gap-3"
onValueChange={(value: components['schemas']['AddonVariantId']) => {
setValue('computeSize', value, {
shouldDirty: true,
@@ -6,37 +6,42 @@ import CountdownTimerSpan from '../../../ui/CountdownTimer/CountdownTimerSpan'
import { useRemainingDurationForDiskAttributeUpdate } from 'data/config/disk-attributes-query'
import { useParams } from 'common'
import FormMessage from './FormMessage'
import { useEffect, useState } from 'react'
export function DiskCountdownRadial() {
const { projectRef } = useParams()
const { ref } = useParams()
const [remainingTime, setRemainingTime] = useState(0)
const {
remainingDuration: remainingTime,
remainingDuration: initialRemainingTime,
isWithinCooldownWindow,
error,
isSuccess,
} = useRemainingDurationForDiskAttributeUpdate({
projectRef,
projectRef: ref,
})
const progressPercentage = (remainingTime / COOLDOWN_DURATION) * 100
// useEffect(() => {
// if (initialRemainingTime > 0) setRemainingTime(initialRemainingTime)
// }, [initialRemainingTime])
useEffect(() => {
if (initialRemainingTime > 0) setRemainingTime(initialRemainingTime)
}, [initialRemainingTime])
// useEffect(() => {
// if (remainingTime <= 0) return
useEffect(() => {
if (remainingTime <= 0) return
// const timer = setInterval(() => {
// setRemainingTime(Math.max(0, remainingTime - 1))
// }, 1000)
const timer = setInterval(() => {
setRemainingTime(Math.max(0, remainingTime - 1))
}, 1000)
// return () => clearInterval(timer)
// }, [remainingTime])
return () => clearInterval(timer)
}, [remainingTime])
console.log('isWithinCooldownWindow IN COMPONENT', isWithinCooldownWindow)
return (
<AnimatePresence>
{remainingTime !== 0 && isSuccess && (
{remainingTime > 0 && isSuccess && (
<motion.div
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: 'auto' }}
@@ -44,16 +49,18 @@ export function DiskCountdownRadial() {
transition={{ duration: 0.2 }}
>
<Card className="px-2 rounded bg-surface-100">
<CardContent className="py-3 flex gap-3 px-3 items-center">
<CardContent className="py-3 flex gap-3 px-3 items-start">
<CountdownTimerRadial progress={progressPercentage} />
<div className="flex flex-col">
<p className="text-foreground text-sm p-0">
6-hour wait period between disk modifications
</p>
<p className="text-foreground-lighter text-sm p-0">
You can't modify your disk configuration until the 6-hour cool down period ends.
You can however change the compute size..
</p>
<div className="flex flex-col gap-2">
<div>
<p className="text-foreground text-sm p-0">
6-hour cooldown period is in progress
</p>
<p className="text-foreground-lighter text-sm p-0">
You can't modify your disk configuration again until the 6-hour cool down period
ends.
</p>
</div>
<CountdownTimerSpan seconds={remainingTime} />
</div>
</CardContent>
@@ -1,9 +1,7 @@
import CountdownTimerRadial from 'components/ui/CountdownTimer/CountdownTimerRadial'
import { AnimatePresence, motion } from 'framer-motion'
import { X } from 'lucide-react'
import React, { useEffect, useState } from 'react'
import { DialogSection, TableCell, TableRow } from 'ui'
import CountdownTimerRadial from '../../../ui/CountdownTimer/CountdownTimerRadial'
import { DialogSection } from 'ui'
export const DiskMangementCoolDownSection: React.FC = () => {
const [progress, setProgress] = useState(100)
@@ -39,8 +37,6 @@ export const DiskMangementCoolDownSection: React.FC = () => {
return () => clearTimeout(initialDelay)
}, [])
const MotionTableRow = motion(TableRow)
return (
<AnimatePresence>
{isVisible && (
@@ -51,7 +47,7 @@ export const DiskMangementCoolDownSection: React.FC = () => {
className="w-full"
>
<DialogSection className="bg-surface-100 text-sm text-foreground-light flex items-center gap-4 relative w-full border rounded-md">
<div className="w-12 h-12 opacity-75">
<div className="w-12 h-12">
<AnimatePresence>
{showCountdown && (
<motion.div
@@ -82,13 +78,13 @@ export const DiskMangementCoolDownSection: React.FC = () => {
This is due to a cooldown period between disk changes.
</p>
</div>
<button
{/* <button
type="button"
className="text-foreground-lighter hover:text-foreground-light transition-colors self-start p-1"
onClick={() => setIsVisible(false)}
>
<X size={14} />
</button>
</button> */}
</DialogSection>
</motion.div>
)}
@@ -54,7 +54,7 @@ export default function DiskSpaceBar({ form }: DiskSpaceBarProps) {
const databaseSizeBytes = data?.result[0].db_size ?? 0
return (
<div className="flex flex-col gap-3">
<div className="flex flex-col gap-2">
<div className="flex items-center h-6 gap-3">
<span className="text-foreground-light text-sm font-mono flex items-center gap-2">
{usedSize.toFixed(2)}
@@ -75,7 +75,7 @@ export default function DiskSpaceBar({ form }: DiskSpaceBarProps) {
<div className="relative">
<div
className={cn(
'h-[34px] relative border rounded-sm w-full transition',
'h-[35px] relative border rounded-sm w-full transition',
show ? 'bg-selection border border-brand-button' : 'bg-surface-300'
)}
>
@@ -1,30 +1,40 @@
import { Card, CardContent } from 'ui'
import { formatComputeName } from '../DiskManagement.utils'
import { AnimatePresence, motion } from 'framer-motion'
import { LucideIcon } from 'lucide-react'
import { Card, CardContent } from 'ui'
interface NoticeBarProps {
title?: string
description?: string
icon?: LucideIcon
visible: boolean
}
export function NoticeBar({
visible,
title = 'Project about to restart',
description = `This project is about to restart to change compute to.`,
icon: Icon,
}: NoticeBarProps) {
return (
<Card
// TO DO: wrap component into pattern
className="px-2 bg-surface-100"
>
<CardContent className="py-3 flex gap-3 px-3 items-center">
{Icon && <Icon className="text-foreground-light w-4 h-4" />}
<div className="flex flex-col">
<p className="text-foreground text-sm p-0">{title}</p>
<p className="text-foreground-lighter text-sm">{description}</p>
</div>
</CardContent>
</Card>
<AnimatePresence>
<motion.div
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: 'auto' }}
exit={{ opacity: 0, height: 0 }}
transition={{ duration: 0.2 }}
>
{visible && (
<Card className="px-2 bg-surface-100">
<CardContent className="py-3 flex gap-3 px-3 items-center">
{Icon && <Icon className="text-foreground-light w-4 h-4" />}
<div className="flex flex-col">
<p className="text-foreground text-sm p-0">{title}</p>
<p className="text-foreground-lighter text-sm">{description}</p>
</div>
</CardContent>
</Card>
)}
</motion.div>
</AnimatePresence>
)
}
@@ -24,13 +24,15 @@ export const ResizingState = () => {
<div className="space-y-6 py-6">
<div className="flex px-8 space-x-8">
<div className="mt-1">
<Loader2 className="animate-spin" size={18} />
<Loader2 className="animate-spin text-foreground-light" size={18} />
</div>
<div className="space-y-1">
<p>Resizing project...</p>
<div className="flex flex-col gap-1">
<p>Resizing Project Compute size</p>
<p className="text-sm text-foreground-light">
Your project is being restarted to apply compute size changes. It can take a few
minutes. Your project will be offline while it is being restarted.
Your project is being restarted to apply compute size changes.
</p>
<p className="text-sm text-foreground-light">
This can take a few minutes. Project will be offline while it is being restarted.
</p>
</div>
</div>
@@ -17,25 +17,27 @@ const CountdownTimerSpan = ({ seconds }: CountdownTimerSpanProps) => {
const formatValue = (value: number) => value.toString().padStart(2, '0')
return (
<span className="text-foreground text-sm font-mono">
<MotionNumber
format={formatConfig}
value={formatValue(hours)}
transition={transitionConfig}
/>
hr
<MotionNumber
format={formatConfig}
value={formatValue(minutes)}
transition={transitionConfig}
/>
m
<MotionNumber
format={formatConfig}
value={formatValue(remainingSeconds)}
transition={transitionConfig}
/>
s
<span className="inline-flex gap-2">
<span className="text-foreground-lighter text-sm p-0">Time remaining: </span>
<span className="text-foreground text-sm font-mono flex items-center gap-0">
<MotionNumber
format={formatConfig}
value={formatValue(hours)}
transition={transitionConfig}
/>
hr
<MotionNumber
format={formatConfig}
value={formatValue(minutes)}
transition={transitionConfig}
/>
m
<MotionNumber
format={formatConfig}
value={formatValue(remainingSeconds)}
transition={transitionConfig}
/>
</span>
</span>
)
}