150 lines
5.5 KiB
TypeScript
150 lines
5.5 KiB
TypeScript
"use client"
|
||
|
||
import { useState } from "react"
|
||
import {
|
||
Dialog,
|
||
DialogContent,
|
||
DialogDescription,
|
||
DialogFooter,
|
||
DialogHeader,
|
||
DialogTitle,
|
||
} from "@/components/ui/dialog"
|
||
import { Button } from "@/components/ui/button"
|
||
import { Alert, AlertDescription } from "@/components/ui/alert"
|
||
import { Upload, Download, AlertCircle, Loader2 } from "lucide-react"
|
||
import { LocalFinanceProduct } from "@/hooks/use-local-storage"
|
||
|
||
interface DataSyncDialogProps {
|
||
open: boolean
|
||
onOpenChange: (open: boolean) => void
|
||
localProducts: LocalFinanceProduct[]
|
||
onSyncConfirm: () => Promise<void>
|
||
onSyncCancel: () => void
|
||
isSyncing: boolean
|
||
}
|
||
|
||
export function DataSyncDialog({
|
||
open,
|
||
onOpenChange,
|
||
localProducts,
|
||
onSyncConfirm,
|
||
onSyncCancel,
|
||
isSyncing
|
||
}: DataSyncDialogProps) {
|
||
const [selectedAction, setSelectedAction] = useState<'sync' | 'skip' | null>(null)
|
||
|
||
const handleConfirm = async () => {
|
||
if (selectedAction === 'sync') {
|
||
await onSyncConfirm()
|
||
} else {
|
||
onSyncCancel()
|
||
}
|
||
onOpenChange(false)
|
||
}
|
||
|
||
return (
|
||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||
<DialogContent className="sm:max-w-md">
|
||
<DialogHeader>
|
||
<DialogTitle className="flex items-center gap-2 text-foreground">
|
||
<AlertCircle className="h-5 w-5 text-amber-500 dark:text-amber-400" />
|
||
发现本地数据
|
||
</DialogTitle>
|
||
<DialogDescription>
|
||
检测到您在未登录状态下保存了 {localProducts.length} 条理财产品记录。
|
||
请选择如何处理这些本地数据:
|
||
</DialogDescription>
|
||
</DialogHeader>
|
||
|
||
<div className="space-y-4">
|
||
<Alert className="border-blue-200 bg-blue-50 dark:border-blue-800 dark:bg-blue-950/50">
|
||
<Upload className="h-4 w-4" />
|
||
<AlertDescription>
|
||
<strong>本地数据概览:</strong>
|
||
<ul className="mt-2 text-sm space-y-1">
|
||
<li>• 共 {localProducts.length} 条产品记录</li>
|
||
<li>• 总本金: ¥{localProducts.reduce((sum, p) => sum + p.principal, 0).toLocaleString()}</li>
|
||
<li>• 涉及币种: {Array.from(new Set(localProducts.map(p => p.currency))).join(', ')}</li>
|
||
</ul>
|
||
</AlertDescription>
|
||
</Alert>
|
||
|
||
<div className="space-y-3">
|
||
<div
|
||
className={`p-4 border rounded-lg cursor-pointer transition-all ${
|
||
selectedAction === 'sync'
|
||
? 'border-blue-500 bg-blue-50 dark:border-blue-600 dark:bg-blue-950/50'
|
||
: 'border-border hover:border-blue-300 dark:hover:border-blue-600 bg-background'
|
||
}`}
|
||
onClick={() => setSelectedAction('sync')}
|
||
>
|
||
<div className="flex items-start space-x-3">
|
||
<input
|
||
type="radio"
|
||
name="syncAction"
|
||
checked={selectedAction === 'sync'}
|
||
onChange={() => setSelectedAction('sync')}
|
||
className="mt-1"
|
||
/>
|
||
<div className="flex-1 min-w-0">
|
||
<div className="flex items-center gap-2 mb-1">
|
||
<Upload className="h-4 w-4 text-blue-600 dark:text-blue-400" />
|
||
<h4 className="font-medium text-blue-800 dark:text-blue-200">同步到云端</h4>
|
||
</div>
|
||
<p className="text-sm text-muted-foreground">
|
||
将本地数据上传到服务器,与云端数据合并。本地数据仍会保留。
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div
|
||
className={`p-4 border rounded-lg cursor-pointer transition-all ${
|
||
selectedAction === 'skip'
|
||
? 'border-blue-500 bg-blue-50 dark:border-blue-600 dark:bg-blue-950/50'
|
||
: 'border-border hover:border-blue-300 dark:hover:border-blue-600 bg-background'
|
||
}`}
|
||
onClick={() => setSelectedAction('skip')}
|
||
>
|
||
<div className="flex items-start space-x-3">
|
||
<input
|
||
type="radio"
|
||
name="syncAction"
|
||
checked={selectedAction === 'skip'}
|
||
onChange={() => setSelectedAction('skip')}
|
||
className="mt-1"
|
||
/>
|
||
<div className="flex-1 min-w-0">
|
||
<div className="flex items-center gap-2 mb-1">
|
||
<Download className="h-4 w-4 text-muted-foreground" />
|
||
<h4 className="font-medium text-foreground">使用云端数据</h4>
|
||
</div>
|
||
<p className="text-sm text-muted-foreground">
|
||
直接从服务器加载数据,本地数据不会被修改,但不会显示在界面中。
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<DialogFooter className="flex justify-between">
|
||
<Button
|
||
variant="outline"
|
||
onClick={() => onOpenChange(false)}
|
||
disabled={isSyncing}
|
||
>
|
||
稍后处理
|
||
</Button>
|
||
<Button
|
||
onClick={handleConfirm}
|
||
disabled={!selectedAction || isSyncing}
|
||
>
|
||
{isSyncing && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
|
||
{isSyncing ? '处理中...' : '确认'}
|
||
</Button>
|
||
</DialogFooter>
|
||
</DialogContent>
|
||
</Dialog>
|
||
)
|
||
}
|