DatabaseDiffPanel.tsx122 lines · main
1import { CircleAlert, Database, Download, Loader2, Wind } from 'lucide-react'
2import Link from 'next/link'
3import { toast } from 'sonner'
4import { Button, Card, CardContent, CardHeader, CardTitle, Skeleton } from 'ui'
5
6import { DiffEditor } from '@/components/ui/DiffEditor'
7
8interface DatabaseDiffPanelProps {
9 diffContent?: string
10 isLoading: boolean
11 error?: any
12 showRefreshButton?: boolean
13 currentBranchRef?: string
14}
15
16export const DatabaseDiffPanel = ({
17 diffContent,
18 isLoading,
19 error,
20 currentBranchRef,
21}: DatabaseDiffPanelProps) => {
22 if (isLoading) {
23 return (
24 <div className="flex flex-1 min-h-0 flex-col">
25 <div className="flex flex-1 min-h-[400px] flex-col rounded-md border border-border bg-surface-100">
26 <div className="flex shrink-0 items-center gap-2 border-b border-border px-4 py-3">
27 <Loader2
28 size={16}
29 strokeWidth={1.5}
30 className="animate-spin text-foreground-muted"
31 aria-hidden
32 />
33 <span className="text-sm text-foreground-light">Loading database diff…</span>
34 </div>
35 <div className="min-h-0 flex-1 p-4">
36 <Skeleton className="h-full w-full rounded-sm" />
37 </div>
38 </div>
39 </div>
40 )
41 }
42
43 if (error)
44 return (
45 <div className="p-6 text-center">
46 <CircleAlert size={32} strokeWidth={1.5} className="text-foreground-muted mx-auto mb-8" />
47 <h3 className="mb-1">Error loading branch diff</h3>
48 <p className="text-sm text-foreground-light">
49 Please try again in a few minutes and contact support if the problem persists.
50 </p>
51 </div>
52 )
53
54 if (!diffContent || diffContent.trim() === '') {
55 return (
56 <div className="p-6 text-center">
57 <Wind size={32} strokeWidth={1.5} className="text-foreground-muted mx-auto mb-8" />
58 <h3 className="mb-1">No changes detected between branches</h3>
59 <p className="text-sm text-foreground-light">
60 Any changes to your database schema will be shown here for review
61 </p>
62 </div>
63 )
64 }
65
66 return (
67 <Card className="flex flex-1 min-h-0 flex-col">
68 <CardHeader className="flex shrink-0 flex-row items-center justify-between space-y-0 py-3">
69 <CardTitle>
70 <Link
71 href={`/project/${currentBranchRef}/database/schema`}
72 className="flex items-center gap-2"
73 >
74 <Database strokeWidth={1.5} size={16} className="text-foreground-muted" />
75 Schema Changes
76 </Link>
77 </CardTitle>
78 <Button
79 type="default"
80 size="tiny"
81 icon={<Download strokeWidth={1.5} size={14} className="text-foreground-light" />}
82 className="mt-0"
83 onClick={() => {
84 if (!diffContent) return
85 const now = new Date()
86 const pad = (n: number) => n.toString().padStart(2, '0')
87 const timestamp =
88 now.getFullYear().toString() +
89 pad(now.getMonth() + 1) +
90 pad(now.getDate()) +
91 pad(now.getHours()) +
92 pad(now.getMinutes()) +
93 pad(now.getSeconds())
94 const filename = `${timestamp}_migration.sql`
95 const blob = new Blob([diffContent], { type: 'text/plain;charset=utf-8;' })
96 const url = window.URL.createObjectURL(blob)
97 const a = document.createElement('a')
98 a.href = url
99 a.setAttribute('download', filename)
100 document.body.appendChild(a)
101 a.click()
102 document.body.removeChild(a)
103 window.URL.revokeObjectURL(url)
104 toast.success('Migration file downloaded!')
105 }}
106 >
107 Download as migration
108 </Button>
109 </CardHeader>
110 <CardContent className="flex min-h-0 flex-1 flex-col p-0">
111 <div className="min-h-0 flex-1">
112 <DiffEditor
113 language="sql"
114 original=""
115 modified={diffContent}
116 options={{ readOnly: true }}
117 />
118 </div>
119 </CardContent>
120 </Card>
121 )
122}