OutOfDateNotice.tsx142 lines · main
1// @ts-nocheck
2import { GitBranchIcon } from 'lucide-react'
3import { useState } from 'react'
4import {
5 AlertDialog,
6 AlertDialogAction,
7 AlertDialogCancel,
8 AlertDialogContent,
9 AlertDialogDescription,
10 AlertDialogFooter,
11 AlertDialogHeader,
12 AlertDialogTitle,
13 AlertDialogTrigger,
14 Button,
15} from 'ui'
16import { Admonition } from 'ui-patterns'
17
18import { useSendEventMutation } from '@/data/telemetry/send-event-mutation'
19import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
20import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
21
22interface OutOfDateNoticeProps {
23 isBranchOutOfDateMigrations: boolean
24 missingMigrationsCount: number
25 hasMissingFunctions: boolean
26 missingFunctionsCount: number
27 hasOutOfDateFunctions: boolean
28 outOfDateFunctionsCount: number
29 hasEdgeFunctionModifications: boolean
30 modifiedFunctionsCount: number
31 isPushing: boolean
32 onPush: () => void
33}
34
35export const OutOfDateNotice = ({
36 isBranchOutOfDateMigrations,
37 missingMigrationsCount,
38 hasMissingFunctions,
39 hasOutOfDateFunctions,
40 hasEdgeFunctionModifications,
41 modifiedFunctionsCount,
42 isPushing,
43 onPush,
44}: OutOfDateNoticeProps) => {
45 const [isDialogOpen, setIsDialogOpen] = useState(false)
46 const hasOutdatedMigrations = isBranchOutOfDateMigrations && missingMigrationsCount > 0
47 const { data: selectedOrg } = useSelectedOrganizationQuery()
48 const { data: project } = useSelectedProjectQuery()
49 const { mutate: sendEvent } = useSendEventMutation()
50
51 const isBranch = project?.parent_project_ref !== undefined
52 const parentProjectRef = isBranch ? project?.parent_project_ref : project?.ref
53
54 const getTitle = () => {
55 if (hasOutdatedMigrations && (hasMissingFunctions || hasOutOfDateFunctions)) {
56 return 'Your database schema and edge functions are out of date'
57 } else if (hasOutdatedMigrations) {
58 return 'Your database schema is out of date'
59 } else if (hasMissingFunctions || hasOutOfDateFunctions) {
60 return 'Your functions are out of date'
61 }
62 return 'Branch is out of date'
63 }
64
65 const getDescription = () => {
66 return 'Update this branch to get the latest changes from the production branch.'
67 }
68
69 const handleUpdate = (shouldCloseDialog = false) => {
70 if (shouldCloseDialog) {
71 setIsDialogOpen(false)
72 }
73
74 // Track branch update
75 sendEvent({
76 action: 'branch_updated',
77 properties: {
78 modifiedEdgeFunctions: hasEdgeFunctionModifications,
79 source: 'out_of_date_notice',
80 },
81 groups: {
82 project: parentProjectRef ?? 'Unknown',
83 organization: selectedOrg?.slug ?? 'Unknown',
84 },
85 })
86
87 onPush()
88 }
89
90 return (
91 <Admonition type="warning" className="my-4">
92 <div className="w-full flex items-center justify-between">
93 <div>
94 <h3 className="text-sm font-medium">{getTitle()}</h3>
95 <p className="text-sm text-foreground-light">{getDescription()}</p>
96 </div>
97
98 {hasEdgeFunctionModifications ? (
99 <AlertDialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
100 <AlertDialogTrigger asChild>
101 <Button
102 type="default"
103 loading={isPushing}
104 icon={<GitBranchIcon size={16} strokeWidth={1.5} />}
105 className="shrink-0"
106 >
107 {isPushing ? 'Updating...' : 'Update branch'}
108 </Button>
109 </AlertDialogTrigger>
110 <AlertDialogContent>
111 <AlertDialogHeader>
112 <AlertDialogTitle>Update branch with modified functions</AlertDialogTitle>
113 <AlertDialogDescription>
114 This branch has {modifiedFunctionsCount} modified edge function
115 {modifiedFunctionsCount !== 1 ? 's' : ''} that will be overwritten when updating
116 with the latest functions from the production branch. This action cannot be
117 undone.
118 </AlertDialogDescription>
119 </AlertDialogHeader>
120 <AlertDialogFooter>
121 <AlertDialogCancel>Cancel</AlertDialogCancel>
122 <AlertDialogAction variant="warning" onClick={() => handleUpdate(true)}>
123 Update anyway
124 </AlertDialogAction>
125 </AlertDialogFooter>
126 </AlertDialogContent>
127 </AlertDialog>
128 ) : (
129 <Button
130 type="default"
131 loading={isPushing}
132 onClick={() => handleUpdate()}
133 icon={<GitBranchIcon size={16} strokeWidth={1.5} />}
134 className="shrink-0"
135 >
136 {isPushing ? 'Updating...' : 'Update branch'}
137 </Button>
138 )}
139 </div>
140 </Admonition>
141 )
142}