feat(ui): 引入 sonner 消息反馈系统,并在首页部署全局行情同步按钮
This commit is contained in:
@@ -33,6 +33,7 @@ import {
|
||||
SelectValue,
|
||||
} from '@/components/ui/select';
|
||||
import { Plus } from 'lucide-react';
|
||||
import { toast } from 'sonner';
|
||||
import { createAsset } from '@/actions/asset';
|
||||
|
||||
const addAssetSchema = z.object({
|
||||
@@ -94,6 +95,7 @@ export function AddAssetDialog() {
|
||||
startTransition(async () => {
|
||||
const result = await createAsset(values);
|
||||
if (result.success) {
|
||||
toast.success('资产添加成功');
|
||||
setOpen(false);
|
||||
form.reset();
|
||||
router.refresh();
|
||||
|
||||
@@ -3,14 +3,20 @@
|
||||
import { useState, useTransition } from 'react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { RefreshCw } from 'lucide-react';
|
||||
import { toast } from 'sonner';
|
||||
import { syncAllMarketPrices } from '@/actions/market';
|
||||
|
||||
export function SyncButton() {
|
||||
const [isPending, startTransition] = useTransition();
|
||||
|
||||
function handleClick() {
|
||||
async function handleClick() {
|
||||
startTransition(async () => {
|
||||
await syncAllMarketPrices();
|
||||
try {
|
||||
await syncAllMarketPrices();
|
||||
toast.success('行情同步成功', { description: '全市场资产价格已更新为最新市价' });
|
||||
} catch (error) {
|
||||
toast.error('行情同步异常', { description: '部分接口可能触发了熔断或网络阻断' });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ import {
|
||||
SelectValue,
|
||||
} from '@/components/ui/select';
|
||||
import { Plus } from 'lucide-react';
|
||||
import { toast } from 'sonner';
|
||||
import { createTransaction } from '@/actions/transaction';
|
||||
|
||||
const addTransactionSchema = z.object({
|
||||
@@ -117,6 +118,7 @@ export function AddTransactionDialog({ assets }: AddTransactionDialogProps) {
|
||||
exchangeRate: '1',
|
||||
});
|
||||
if (result.success) {
|
||||
toast.success('流水记录成功');
|
||||
setOpen(false);
|
||||
form.reset();
|
||||
router.refresh();
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
"use client"
|
||||
|
||||
import {
|
||||
CircleCheck,
|
||||
Info,
|
||||
LoaderCircle,
|
||||
OctagonX,
|
||||
TriangleAlert,
|
||||
} from "lucide-react"
|
||||
import { useTheme } from "next-themes"
|
||||
import { Toaster as Sonner } from "sonner"
|
||||
|
||||
type ToasterProps = React.ComponentProps<typeof Sonner>
|
||||
|
||||
const Toaster = ({ ...props }: ToasterProps) => {
|
||||
const { theme = "system" } = useTheme()
|
||||
|
||||
return (
|
||||
<Sonner
|
||||
theme={theme as ToasterProps["theme"]}
|
||||
className="toaster group"
|
||||
icons={{
|
||||
success: <CircleCheck className="h-4 w-4" />,
|
||||
info: <Info className="h-4 w-4" />,
|
||||
warning: <TriangleAlert className="h-4 w-4" />,
|
||||
error: <OctagonX className="h-4 w-4" />,
|
||||
loading: <LoaderCircle className="h-4 w-4 animate-spin" />,
|
||||
}}
|
||||
toastOptions={{
|
||||
classNames: {
|
||||
toast:
|
||||
"group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg",
|
||||
description: "group-[.toast]:text-muted-foreground",
|
||||
actionButton:
|
||||
"group-[.toast]:bg-primary group-[.toast]:text-primary-foreground",
|
||||
cancelButton:
|
||||
"group-[.toast]:bg-muted group-[.toast]:text-muted-foreground",
|
||||
},
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Toaster }
|
||||
Reference in New Issue
Block a user