
3D Extruded Buildings & Urban Footprints
Real-world 3D building extrusions with dynamic sunlight shadows, terrain DEM elevations, and pitch/bearing camera controls.
Prisma, Flyway, and Liquibase database migration runner with pending DDL visual diffs, rollback scripts, schema integrity telemetry, and execution history.
Also available for Vue ->$pnpm dlx shadcn@latest add https://uipkge.dev/r/react/database-migration-schema-diff.json$npx shadcn@latest add https://uipkge.dev/r/react/database-migration-schema-diff.json$yarn dlx shadcn@latest add https://uipkge.dev/r/react/database-migration-schema-diff.json$bunx shadcn@latest add https://uipkge.dev/r/react/database-migration-schema-diff.jsonnpx shadcn@latest add @uipkge-react/database-migration-schema-diffInstalls to:components/blocks/| Name | Type / Values | Default | Required |
|---|---|---|---|
className | string | — | optional |
Type aliases exported from this item's source. Use these to shape the data you pass in.
DiffLineinterface DiffLine {
num: number
type: 'addition' | 'modification' | 'deletion' | 'neutral'
prefix: string
text: string
}HistoricalMigrationinterface HistoricalMigration {
id: string
filename: string
version: string
description: string
appliedAt: string
author: string
executionTime: string
tablesAffected: string
status: 'applied' | 'pending' | 'rolled_back'
checksum: string
}'use client'
import * as React from 'react'
import {
Check,
CheckCircle2,
Clock,
Code2,
Copy,
Database,
FileCode2,
HardDrive,
Layers,
Loader2,
Play,
RotateCcw,
ShieldCheck,
Terminal,
Timer,
Undo2,
Zap,
} from 'lucide-react'
import { cn } from '@/lib/utils'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Separator } from '@/components/ui/separator'
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
export interface DiffLine {
num: number
type: 'addition' | 'modification' | 'deletion' | 'neutral'
prefix: string
text: string
}
export interface HistoricalMigration {
id: string
filename: string
version: string
description: string
appliedAt: string
author: string
executionTime: string
tablesAffected: string
status: 'applied' | 'pending' | 'rolled_back'
checksum: string
}
export interface DatabaseMigrationSchemaDiffProps {
className?: string
}
const upSqlRaw = `-- Up Migration
-- Migration: 20260821142850_add_org_billing_seats_and_quotas.sql
-- Target: public.organizations
ALTER TABLE public.organizations
ADD COLUMN billing_seats_allocated INTEGER NOT NULL DEFAULT 5,
ADD COLUMN max_seat_limit INTEGER NOT NULL DEFAULT 20,
ADD COLUMN last_seat_audit_at TIMESTAMPTZ DEFAULT NOW();
CREATE INDEX idx_orgs_seat_audit ON public.organizations(last_seat_audit_at);`
const downSqlRaw = `-- Down Migration (Rollback)
-- Reverts: 20260821142850_add_org_billing_seats_and_quotas.sql
-- Target: public.organizations
DROP INDEX IF EXISTS idx_orgs_seat_audit;
ALTER TABLE public.organizations
DROP COLUMN IF EXISTS billing_seats_allocated,
DROP COLUMN IF EXISTS max_seat_limit,
DROP COLUMN IF EXISTS last_seat_audit_at;`
const upDiffLines: DiffLine[] = [
{ num: 1, type: 'neutral', prefix: ' ', text: '-- Up Migration' },
{ num: 2, type: 'neutral', prefix: ' ', text: '-- Migration: 20260821142850_add_org_billing_seats_and_quotas.sql' },
{ num: 3, type: 'neutral', prefix: ' ', text: '-- Target: public.organizations' },
{ num: 4, type: 'neutral', prefix: ' ', text: '' },
{ num: 5, type: 'modification', prefix: '~', text: 'ALTER TABLE public.organizations ' },
{ num: 6, type: 'addition', prefix: '+', text: ' ADD COLUMN billing_seats_allocated INTEGER NOT NULL DEFAULT 5,' },
{ num: 7, type: 'addition', prefix: '+', text: ' ADD COLUMN max_seat_limit INTEGER NOT NULL DEFAULT 20,' },
{ num: 8, type: 'addition', prefix: '+', text: ' ADD COLUMN last_seat_audit_at TIMESTAMPTZ DEFAULT NOW();' },
{ num: 9, type: 'neutral', prefix: ' ', text: '' },
{
num: 10,
type: 'addition',
prefix: '+',
text: 'CREATE INDEX idx_orgs_seat_audit ON public.organizations(last_seat_audit_at);',
},
]
const downDiffLines: DiffLine[] = [
{ num: 1, type: 'neutral', prefix: ' ', text: '-- Down Migration (Rollback)' },
{ num: 2, type: 'neutral', prefix: ' ', text: '-- Reverts: 20260821142850_add_org_billing_seats_and_quotas.sql' },
{ num: 3, type: 'neutral', prefix: ' ', text: '-- Target: public.organizations' },
{ num: 4, type: 'neutral', prefix: ' ', text: '' },
{ num: 5, type: 'deletion', prefix: '-', text: 'DROP INDEX IF EXISTS idx_orgs_seat_audit;' },
{ num: 6, type: 'neutral', prefix: ' ', text: '' },
{ num: 7, type: 'modification', prefix: '~', text: 'ALTER TABLE public.organizations ' },
{ num: 8, type: 'deletion', prefix: '-', text: ' DROP COLUMN IF EXISTS billing_seats_allocated,' },
{ num: 9, type: 'deletion', prefix: '-', text: ' DROP COLUMN IF EXISTS max_seat_limit,' },
{ num: 10, type: 'deletion', prefix: '-', text: ' DROP COLUMN IF EXISTS last_seat_audit_at;' },
]
const historicalMigrations: HistoricalMigration[] = [
{
id: 'mig_48',
filename: '20260814091522_add_audit_logs_partitioning.sql',
version: 'v2026.08.14.02',
description: 'Partition audit_logs table by month range and attach default tablespace',
appliedAt: 'Aug 14, 2026 · 09:15 UTC',
author: 'alex.chen@corp',
executionTime: '1.8s',
tablesAffected: 'audit_logs, audit_logs_2026_08',
status: 'applied',
checksum: 'sha256:4e8b39c0',
},
{
id: 'mig_47',
filename: '20260810143011_create_api_rate_limits_table.sql',
version: 'v2026.08.10.01',
description: 'Create rate_limits table & redis fallback synchronization schema',
appliedAt: 'Aug 10, 2026 · 14:30 UTC',
author: 'sarah.m@corp',
executionTime: '0.9s',
tablesAffected: 'api_rate_limits',
status: 'applied',
checksum: 'sha256:d12c88f1',
},
{
id: 'mig_46',
filename: '20260802110540_add_saml_sso_workspaces.sql',
version: 'v2026.08.02.03',
description: 'Add SAML SSO domain verification and enterprise IdP metadata records',
appliedAt: 'Aug 02, 2026 · 11:05 UTC',
author: 'devops-bot',
executionTime: '2.1s',
tablesAffected: 'workspaces, idp_configs',
status: 'applied',
checksum: 'sha256:88a3b114',
},
{
id: 'mig_45',
filename: '20260725184219_optimize_query_cache_indexes.sql',
version: 'v2026.07.25.01',
description: 'Add CONCURRENT B-Tree composite index on team_id + created_at timestamp',
appliedAt: 'Jul 25, 2026 · 18:42 UTC',
author: 'alex.chen@corp',
executionTime: '4.2s',
tablesAffected: 'query_logs, cache_entries',
status: 'applied',
checksum: 'sha256:fa2300b9',
},
{
id: 'mig_44',
filename: '20260718082005_init_multi_tenant_memberships.sql',
version: 'v2026.07.18.01',
description: 'Initialize organization_memberships, team_roles, and permission cascades',
appliedAt: 'Jul 18, 2026 · 08:20 UTC',
author: 'elena.r@corp',
executionTime: '1.2s',
tablesAffected: 'org_members, permissions',
status: 'applied',
checksum: 'sha256:56df9201',
},
]
export function DatabaseMigrationSchemaDiff({ className }: DatabaseMigrationSchemaDiffProps) {
const [activeTab, setActiveTab] = React.useState('up')
const [isApplying, setIsApplying] = React.useState(false)
const [isApplied, setIsApplied] = React.useState(false)
const [copiedUpSql, setCopiedUpSql] = React.useState(false)
const [copiedDownSql, setCopiedDownSql] = React.useState(false)
const [copiedCli, setCopiedCli] = React.useState(false)
const handleApplyMigration = () => {
if (isApplying || isApplied) return
setIsApplying(true)
setTimeout(() => {
setIsApplying(false)
setIsApplied(true)
}, 900)
}
const handlePreviewRollback = () => {
setActiveTab('rollback')
}
const handleResetState = () => {
setIsApplied(false)
setIsApplying(false)
}
const copyToClipboard = (text: string, type: 'up' | 'down' | 'cli') => {
navigator.clipboard.writeText(text)
if (type === 'up') {
setCopiedUpSql(true)
setTimeout(() => setCopiedUpSql(false), 2000)
} else if (type === 'down') {
setCopiedDownSql(true)
setTimeout(() => setCopiedDownSql(false), 2000)
} else if (type === 'cli') {
setCopiedCli(true)
setTimeout(() => setCopiedCli(false), 2000)
}
}
return (
<div data-slot="database-migration-schema-diff" className={cn('w-full space-y-6', className)}>
{/* Header: Target Database & Version Runner Status */}
<Card className="border-border bg-card shadow-xs">
<CardHeader className="pb-4">
<div className="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
<div className="space-y-1.5">
<div className="flex flex-wrap items-center gap-2">
<div className="text-foreground flex items-center gap-2 text-base font-semibold sm:text-lg">
<Database className="text-primary size-5 shrink-0" />
<span>PostgreSQL 16.2 · production_primary_cluster</span>
</div>
<div className="bg-success/10 text-success flex items-center gap-1.5 rounded-full px-2 py-0.5 text-xs font-medium">
<span className="bg-success size-1.5 animate-pulse rounded-full" />
<span>Connected</span>
</div>
</div>
<div className="text-muted-foreground flex flex-wrap items-center gap-2 text-xs sm:text-sm">
<span className="text-foreground font-mono font-medium">v2026.08.21.01_add_org_billing_seats</span>
<Separator orientation="vertical" className="h-3.5" />
<span>db.us-east-1.aws.neon.tech:5432</span>
<Separator orientation="vertical" className="h-3.5" />
<span>public schema</span>
</div>
</div>
<div className="flex flex-wrap items-center gap-2.5">
{!isApplied ? (
<Badge wrap variant="warning" className="gap-1.5 px-2.5 py-1 text-xs">
<Clock className="size-3.5" />
<span>1 Pending Migration Ready to Apply</span>
</Badge>
) : (
<Badge wrap variant="success" className="gap-1.5 px-2.5 py-1 text-xs">
<CheckCircle2 className="size-3.5" />
<span>All Migrations Applied · In Sync</span>
</Badge>
)}
<Button
variant="outline"
size="sm"
className="h-9 gap-1.5 text-xs font-medium shadow-xs active:scale-[0.98]"
onClick={handlePreviewRollback}
>
<Undo2 className="text-muted-foreground size-3.5" />
<span>Preview Rollback Script</span>
</Button>
{!isApplied ? (
<Button
variant="default"
size="sm"
className="h-9 gap-1.5 text-xs font-medium shadow-xs active:scale-[0.98]"
disabled={isApplying}
onClick={handleApplyMigration}
>
{isApplying ? (
<Loader2 className="size-3.5 animate-spin" />
) : (
<Play className="size-3.5 fill-current" />
)}
<span>{isApplying ? 'Applying Migration...' : 'Apply Migration (prisma migrate deploy)'}</span>
</Button>
) : (
<Button
variant="secondary"
size="sm"
className="h-9 gap-1.5 text-xs font-medium active:scale-[0.98]"
onClick={handleResetState}
>
<RotateCcw className="size-3.5" />
<span>Reset State</span>
</Button>
)}
</div>
</div>
</CardHeader>
</Card>
{/* 4 Migration Telemetry Cards */}
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
{/* Total Applied Migrations */}
<Card className="border-border bg-card shadow-xs">
<CardContent className="p-4 sm:p-5">
<div className="flex items-center justify-between">
<span className="text-muted-foreground text-xs font-medium">Total Applied Migrations</span>
<div className="bg-muted text-muted-foreground flex size-8 items-center justify-center rounded-md">
<Layers className="size-4" />
</div>
</div>
<div className="mt-3">
<div className="text-foreground text-2xl font-bold tracking-tight">{isApplied ? '49' : '48'}</div>
<p className="text-muted-foreground mt-1 text-xs">48 Migrations Applied · Baseline to v2026.08.14</p>
</div>
<div className="text-success mt-3 flex items-center gap-1.5 text-xs font-medium">
<Check className="size-3.5" />
<span>100% successful execution history</span>
</div>
</CardContent>
</Card>
{/* Pending Migrations */}
<Card className={cn('border-border bg-card shadow-xs transition-colors', !isApplied && 'border-warning/40')}>
<CardContent className="p-4 sm:p-5">
<div className="flex items-center justify-between">
<span className="text-muted-foreground text-xs font-medium">Pending Migrations</span>
<div
className={cn(
'flex size-8 items-center justify-center rounded-md',
!isApplied ? 'bg-warning/10 text-warning' : 'bg-success/10 text-success',
)}
>
{!isApplied ? <Clock className="size-4" /> : <CheckCircle2 className="size-4" />}
</div>
</div>
<div className="mt-3">
<div className="text-foreground text-2xl font-bold tracking-tight">{isApplied ? '0' : '1'}</div>
<p className="text-muted-foreground mt-1 text-xs">
{isApplied ? '0 Pending · Schema up to date' : '1 Pending Deployment'}
</p>
</div>
<div
className={cn(
'mt-3 flex items-center gap-1.5 text-xs font-medium',
!isApplied ? 'text-warning' : 'text-success',
)}
>
{!isApplied ? <ShieldCheck className="size-3.5" /> : <Check className="size-3.5" />}
<span>{!isApplied ? 'Safe DDL · Non-blocking ALTER' : 'Schema verified against shadow DB'}</span>
</div>
</CardContent>
</Card>
{/* Last Migration Execution */}
<Card className="border-border bg-card shadow-xs">
<CardContent className="p-4 sm:p-5">
<div className="flex items-center justify-between">
<span className="text-muted-foreground text-xs font-medium">Last Migration Execution</span>
<div className="bg-muted text-muted-foreground flex size-8 items-center justify-center rounded-md">
<Zap className="size-4" />
</div>
</div>
<div className="mt-3">
<div className="text-foreground text-2xl font-bold tracking-tight">{isApplied ? '0.12s' : '3.4s'}</div>
<p className="text-muted-foreground mt-1 text-xs">3.4s execution time · 0 lock timeouts</p>
</div>
<div className="text-muted-foreground mt-3 flex items-center gap-1.5 text-xs">
<Timer className="size-3.5" />
<span>Zero query contention or lock waits</span>
</div>
</CardContent>
</Card>
{/* Target Schema Integrity */}
<Card className="border-border bg-card shadow-xs">
<CardContent className="p-4 sm:p-5">
<div className="flex items-center justify-between">
<span className="text-muted-foreground text-xs font-medium">Target Schema Integrity</span>
<div className="bg-success/10 text-success flex size-8 items-center justify-center rounded-md">
<ShieldCheck className="size-4" />
</div>
</div>
<div className="mt-3">
<div className="text-foreground text-2xl font-bold tracking-tight">100%</div>
<p className="text-muted-foreground mt-1 text-xs">100% Validated · Zero Drift</p>
</div>
<div className="text-success mt-3 flex items-center gap-1.5 text-xs font-medium">
<CheckCircle2 className="size-3.5" />
<span>Prisma schema & DB in sync</span>
</div>
</CardContent>
</Card>
</div>
{/* Active Migration Inspector & DDL Diff Card */}
<Card className="border-border bg-card shadow-xs">
<CardHeader className="border-border border-b pb-4">
<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
<div className="space-y-1">
<div className="flex flex-wrap items-center gap-2">
<FileCode2 className="text-primary size-4 shrink-0" />
<CardTitle className="text-foreground font-mono text-sm font-semibold sm:text-base">
20260821142850_add_org_billing_seats_and_quotas.sql
</CardTitle>
{!isApplied ? (
<Badge wrap variant="warning" className="text-xs">
Pending DDL Execution
</Badge>
) : (
<Badge wrap variant="success" className="text-xs">
Applied in Production
</Badge>
)}
</div>
<CardDescription className="text-muted-foreground text-xs">
Target Table: <span className="text-foreground font-mono font-medium">public.organizations</span> ·
Author: <span className="text-foreground">alex.chen@corp</span> · Estimated duration:{' '}
<span className="text-foreground font-mono font-medium">~120ms</span>
</CardDescription>
</div>
<div className="flex items-center gap-2">
<Button
variant="outline"
size="sm"
className="h-8 gap-1.5 text-xs font-medium"
onClick={() =>
copyToClipboard(
activeTab === 'rollback' ? downSqlRaw : upSqlRaw,
activeTab === 'rollback' ? 'down' : 'up',
)
}
>
{(activeTab === 'rollback' ? copiedDownSql : copiedUpSql) ? (
<Check className="text-success size-3.5" />
) : (
<Copy className="size-3.5" />
)}
<span>{(activeTab === 'rollback' ? copiedDownSql : copiedUpSql) ? 'Copied SQL' : 'Copy SQL'}</span>
</Button>
</div>
</div>
</CardHeader>
<CardContent className="p-4 sm:p-6">
<Tabs value={activeTab} onValueChange={setActiveTab} className="w-full space-y-4">
<TabsList className="grid w-full grid-cols-2 sm:inline-flex sm:w-auto sm:grid-cols-none">
<TabsTrigger value="up" className="gap-1.5 text-xs">
<Code2 className="size-3.5" />
<span>DDL Diff (Up)</span>
</TabsTrigger>
<TabsTrigger value="rollback" className="gap-1.5 text-xs">
<Undo2 className="size-3.5" />
<span>Rollback Script (Down)</span>
</TabsTrigger>
<TabsTrigger value="impact" className="gap-1.5 text-xs">
<HardDrive className="size-3.5" />
<span>Schema Impact</span>
</TabsTrigger>
<TabsTrigger value="cli" className="gap-1.5 text-xs">
<Terminal className="size-3.5" />
<span>Prisma / CLI Runner</span>
</TabsTrigger>
</TabsList>
{/* Tab 1: DDL Diff (Up) */}
<TabsContent value="up" className="space-y-3 focus-visible:outline-none">
<div className="border-border bg-muted/50 flex items-center justify-between rounded-t-md border-x border-t px-3 py-2 text-xs">
<div className="text-muted-foreground flex items-center gap-2">
<span className="text-foreground font-medium">Forward Migration (Up)</span>
<span>·</span>
<span className="text-success font-medium">+3 columns added</span>
<span>·</span>
<span className="text-info font-medium">+1 index created</span>
</div>
<span className="text-muted-foreground font-mono">SQL (PostgreSQL DDL)</span>
</div>
<div className="border-border bg-muted/20 overflow-x-auto rounded-b-md border font-mono text-xs leading-relaxed">
<div className="max-w-[540px] min-w-full py-2">
{upDiffLines.map((line) => (
<div
key={line.num}
className={cn(
'flex items-center px-3 py-0.5 transition-colors',
line.type === 'addition' && 'border-success bg-success/10 text-success border-l-2 font-medium',
line.type === 'modification' && 'border-info bg-info/10 text-info border-l-2 font-medium',
line.type === 'neutral' && 'text-muted-foreground border-l-2 border-transparent',
)}
>
<span className="text-muted-foreground/50 w-7 shrink-0 pr-3 text-right select-none">
{line.num}
</span>
<span
className={cn(
'w-4 shrink-0 text-center font-bold select-none',
line.type === 'addition' && 'text-success',
line.type === 'modification' && 'text-info',
line.type === 'neutral' && 'text-transparent',
)}
>
{line.prefix}
</span>
<span className="pl-1 whitespace-pre">{line.text}</span>
</div>
))}
</div>
</div>
</TabsContent>
{/* Tab 2: Rollback Script (Down) */}
<TabsContent value="rollback" className="space-y-3 focus-visible:outline-none">
<div className="border-border bg-muted/50 flex items-center justify-between rounded-t-md border-x border-t px-3 py-2 text-xs">
<div className="text-muted-foreground flex items-center gap-2">
<span className="text-foreground font-medium">Revert Script (Down)</span>
<span>·</span>
<span className="text-destructive font-medium">-3 columns dropped</span>
<span>·</span>
<span className="text-destructive font-medium">-1 index dropped</span>
</div>
<span className="text-muted-foreground font-mono">SQL (PostgreSQL DDL)</span>
</div>
<div className="border-border bg-muted/20 overflow-x-auto rounded-b-md border font-mono text-xs leading-relaxed">
<div className="max-w-[540px] min-w-full py-2">
{downDiffLines.map((line) => (
<div
key={line.num}
className={cn(
'flex items-center px-3 py-0.5 transition-colors',
line.type === 'deletion' &&
'border-destructive bg-destructive/10 text-destructive border-l-2 font-medium',
line.type === 'modification' && 'border-info bg-info/10 text-info border-l-2 font-medium',
line.type === 'neutral' && 'text-muted-foreground border-l-2 border-transparent',
)}
>
<span className="text-muted-foreground/50 w-7 shrink-0 pr-3 text-right select-none">
{line.num}
</span>
<span
className={cn(
'w-4 shrink-0 text-center font-bold select-none',
line.type === 'deletion' && 'text-destructive',
line.type === 'modification' && 'text-info',
line.type === 'neutral' && 'text-transparent',
)}
>
{line.prefix}
</span>
<span className="pl-1 whitespace-pre">{line.text}</span>
</div>
))}
</div>
</div>
</TabsContent>
{/* Tab 3: Schema Impact & Locks */}
<TabsContent value="impact" className="space-y-4 focus-visible:outline-none">
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
<div className="border-border bg-muted/30 space-y-3 rounded-lg border p-4">
<div className="text-foreground flex items-center gap-2 text-sm font-medium">
<Database className="text-primary size-4 shrink-0" />
<span>Target Table & Column Additions</span>
</div>
<div className="space-y-2 text-xs">
<div className="border-border/60 bg-card flex items-center justify-between rounded-md border p-2">
<span className="text-foreground font-mono font-medium">billing_seats_allocated</span>
<Badge wrap variant="secondary" className="font-mono text-xs">
INTEGER NOT NULL DEFAULT 5
</Badge>
</div>
<div className="border-border/60 bg-card flex items-center justify-between rounded-md border p-2">
<span className="text-foreground font-mono font-medium">max_seat_limit</span>
<Badge wrap variant="secondary" className="font-mono text-xs">
INTEGER NOT NULL DEFAULT 20
</Badge>
</div>
<div className="border-border/60 bg-card flex items-center justify-between rounded-md border p-2">
<span className="text-foreground font-mono font-medium">last_seat_audit_at</span>
<Badge wrap variant="secondary" className="font-mono text-xs">
TIMESTAMPTZ DEFAULT NOW()
</Badge>
</div>
</div>
</div>
<div className="border-border bg-muted/30 space-y-3 rounded-lg border p-4">
<div className="text-foreground flex items-center gap-2 text-sm font-medium">
<ShieldCheck className="text-success size-4 shrink-0" />
<span>Lock Safety & Concurrency Evaluation</span>
</div>
<div className="text-muted-foreground space-y-2 text-xs">
<div className="border-border/60 bg-card flex items-center justify-between rounded-md border p-2">
<span>Lock Level Required</span>
<span className="text-foreground font-mono font-medium">ACCESS EXCLUSIVE (Catalog only)</span>
</div>
<div className="border-border/60 bg-card flex items-center justify-between rounded-md border p-2">
<span>Table Rewrite Required</span>
<Badge wrap variant="success" className="text-xs">
No (PG11+ Constant Default)
</Badge>
</div>
<div className="border-border/60 bg-card flex items-center justify-between rounded-md border p-2">
<span>Estimated Lock Duration</span>
<span className="text-success font-mono font-medium">< 2ms</span>
</div>
</div>
</div>
</div>
</TabsContent>
{/* Tab 4: CLI Runner */}
<TabsContent value="cli" className="space-y-3 focus-visible:outline-none">
<div className="border-border bg-card space-y-3 rounded-lg border p-4">
<div className="flex items-center justify-between">
<div className="text-foreground flex items-center gap-2 text-xs font-medium">
<Terminal className="text-primary size-4 shrink-0" />
<span>Execute via Prisma CLI</span>
</div>
<Button
variant="outline"
size="xs"
className="h-7 gap-1 text-xs"
onClick={() => copyToClipboard('npx prisma migrate deploy --schema=./prisma/schema.prisma', 'cli')}
>
{copiedCli ? <Check className="text-success size-3" /> : <Copy className="size-3" />}
<span>{copiedCli ? 'Copied' : 'Copy'}</span>
</Button>
</div>
<div className="bg-muted/60 text-foreground rounded-md p-3 font-mono text-xs select-all">
npx prisma migrate deploy --schema=./prisma/schema.prisma
</div>
<p className="text-muted-foreground text-xs">
Applies all pending migrations to the configured datasource cluster without prompting for interactive
confirmation.
</p>
</div>
</TabsContent>
</Tabs>
</CardContent>
</Card>
{/* Historical Migrations Table Card */}
<Card className="border-border bg-card shadow-xs">
<CardHeader className="pb-3">
<div className="flex items-center justify-between">
<div>
<CardTitle className="text-foreground text-base font-semibold">Recent Applied Migrations</CardTitle>
<CardDescription className="text-muted-foreground text-xs">
Historical DDL execution logs, author signatures, and validation checksums
</CardDescription>
</div>
<Badge wrap variant="outline" className="font-mono text-xs">
5 Most Recent
</Badge>
</div>
</CardHeader>
<CardContent className="p-0">
<div className="overflow-x-auto">
<Table>
<TableHeader>
<TableRow className="hover:bg-transparent">
<TableHead className="text-xs font-medium">Migration & Version</TableHead>
<TableHead className="text-xs font-medium">Target Scope</TableHead>
<TableHead className="text-xs font-medium">Executed At</TableHead>
<TableHead className="text-xs font-medium">Author</TableHead>
<TableHead className="text-xs font-medium">Duration</TableHead>
<TableHead className="text-right text-xs font-medium">Status</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{historicalMigrations.map((migration) => (
<TableRow key={migration.id} className="hover:bg-muted/50 text-xs transition-colors">
<TableCell className="py-3">
<div className="space-y-0.5">
<div className="text-foreground font-mono font-medium">{migration.filename}</div>
<div className="text-muted-foreground flex items-center gap-1.5">
<span className="text-primary font-mono font-medium">{migration.version}</span>
<span>·</span>
<span>{migration.description}</span>
</div>
</div>
</TableCell>
<TableCell className="text-muted-foreground font-mono whitespace-nowrap">
{migration.tablesAffected}
</TableCell>
<TableCell className="text-muted-foreground whitespace-nowrap">{migration.appliedAt}</TableCell>
<TableCell className="text-muted-foreground whitespace-nowrap">{migration.author}</TableCell>
<TableCell className="text-foreground font-mono font-medium whitespace-nowrap">
{migration.executionTime}
</TableCell>
<TableCell className="text-right whitespace-nowrap">
<Badge wrap variant="success" className="gap-1 px-2 py-0.5 text-xs font-medium">
<CheckCircle2 className="size-3" />
<span>Applied</span>
</Badge>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
</CardContent>
</Card>
</div>
)
}
export default DatabaseMigrationSchemaDiff
Raw manifest:https://uipkge.dev/r/react/database-migration-schema-diff.json