feat: 反馈前需登录
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
import { Toaster } from '@/components/ui/sonner';
|
||||||
import './globals.css';
|
import './globals.css';
|
||||||
import AnalyticsGate from './components/AnalyticsGate';
|
import AnalyticsGate from './components/AnalyticsGate';
|
||||||
import packageJson from '../package.json';
|
import packageJson from '../package.json';
|
||||||
@@ -27,8 +28,9 @@ export default function RootLayout({ children }) {
|
|||||||
/>
|
/>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<AnalyticsGate GA_ID={GA_ID} />
|
<AnalyticsGate GA_ID={GA_ID} />
|
||||||
{children}
|
{children}
|
||||||
|
<Toaster />
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ import WeChatModal from "./components/WeChatModal";
|
|||||||
import DcaModal from "./components/DcaModal";
|
import DcaModal from "./components/DcaModal";
|
||||||
import githubImg from "./assets/github.svg";
|
import githubImg from "./assets/github.svg";
|
||||||
import { supabase, isSupabaseConfigured } from './lib/supabase';
|
import { supabase, isSupabaseConfigured } from './lib/supabase';
|
||||||
|
import { toast as sonnerToast } from 'sonner';
|
||||||
import { recordValuation, getAllValuationSeries, clearFund } from './lib/valuationTimeseries';
|
import { recordValuation, getAllValuationSeries, clearFund } from './lib/valuationTimeseries';
|
||||||
import { loadHolidaysForYears, isTradingDay as isDateTradingDay } from './lib/tradingCalendar';
|
import { loadHolidaysForYears, isTradingDay as isDateTradingDay } from './lib/tradingCalendar';
|
||||||
import { parseFundTextWithLLM, fetchFundData, fetchLatestRelease, fetchShanghaiIndexDate, fetchSmartFundNetValue, searchFunds } from './api/fund';
|
import { parseFundTextWithLLM, fetchFundData, fetchLatestRelease, fetchShanghaiIndexDate, fetchSmartFundNetValue, searchFunds } from './api/fund';
|
||||||
@@ -4216,6 +4217,10 @@ export default function HomePage() {
|
|||||||
<button
|
<button
|
||||||
className="link-button"
|
className="link-button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
if (!user?.id) {
|
||||||
|
sonnerToast.error('请先登录后再提交反馈');
|
||||||
|
return;
|
||||||
|
}
|
||||||
setFeedbackNonce((n) => n + 1);
|
setFeedbackNonce((n) => n + 1);
|
||||||
setFeedbackOpen(true);
|
setFeedbackOpen(true);
|
||||||
}}
|
}}
|
||||||
|
|||||||
61
components/ui/sonner.jsx
Normal file
61
components/ui/sonner.jsx
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import {
|
||||||
|
CircleCheckIcon,
|
||||||
|
InfoIcon,
|
||||||
|
Loader2Icon,
|
||||||
|
OctagonXIcon,
|
||||||
|
TriangleAlertIcon,
|
||||||
|
} from "lucide-react"
|
||||||
|
import { useTheme } from "next-themes"
|
||||||
|
import { Toaster as Sonner } from "sonner";
|
||||||
|
|
||||||
|
const Toaster = ({ ...props }) => {
|
||||||
|
const { theme = "system" } = useTheme();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Sonner
|
||||||
|
theme={theme}
|
||||||
|
// 外层容器:固定在页面顶部中间
|
||||||
|
className="toaster pointer-events-none fixed inset-x-0 top-4 z-[70] flex items-start justify-center px-4 sm:top-6"
|
||||||
|
icons={{
|
||||||
|
success: <CircleCheckIcon className="h-4 w-4 text-emerald-500" />,
|
||||||
|
info: <InfoIcon className="h-4 w-4 text-sky-500" />,
|
||||||
|
warning: <TriangleAlertIcon className="h-4 w-4 text-amber-500" />,
|
||||||
|
error: <OctagonXIcon className="h-4 w-4 text-destructive" />,
|
||||||
|
loading: <Loader2Icon className="h-4 w-4 animate-spin text-primary" />,
|
||||||
|
}}
|
||||||
|
richColors
|
||||||
|
// 统一 toast 样式,使用 ui-ux-pro-max 建议的明暗主题对比度
|
||||||
|
toastOptions={{
|
||||||
|
classNames: {
|
||||||
|
toast:
|
||||||
|
// 基础:浅色模式下使用高对比白色卡片,暗色模式使用深色卡片
|
||||||
|
"pointer-events-auto relative flex w-full max-w-sm items-start gap-3 rounded-xl border border-slate-200 bg-white/90 text-slate-900 px-4 py-3 shadow-lg shadow-black/10 backdrop-blur-md transition-all duration-200 " +
|
||||||
|
"data-[state=open]:animate-in data-[state=open]:fade-in data-[state=open]:slide-in-from-top sm:data-[state=open]:slide-in-from-bottom " +
|
||||||
|
"data-[state=closed]:animate-out data-[state=closed]:fade-out data-[state=closed]:slide-out-to-right " +
|
||||||
|
"data-[swipe=move]:translate-x-[var(--sonner-swipe-move-x)] data-[swipe=move]:transition-none " +
|
||||||
|
"data-[swipe=cancel]:translate-x-0 data-[swipe=cancel]:transition-transform data-[swipe=end]:translate-x-[var(--sonner-swipe-end-x)] " +
|
||||||
|
"dark:border-slate-800 dark:bg-slate-900/90 dark:text-slate-100",
|
||||||
|
title: "text-sm font-medium",
|
||||||
|
description: "mt-1 text-xs text-slate-600 dark:text-slate-400",
|
||||||
|
closeButton:
|
||||||
|
"cursor-pointer text-muted-foreground/70 transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
||||||
|
actionButton:
|
||||||
|
"inline-flex h-8 items-center justify-center rounded-full bg-primary px-3 text-xs font-medium text-primary-foreground shadow-sm transition-colors hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2",
|
||||||
|
cancelButton:
|
||||||
|
"inline-flex h-8 items-center justify-center rounded-full border border-border bg-background px-3 text-xs font-medium text-foreground shadow-sm transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
||||||
|
// 状态色:成功/信息/警告只强化边框,错误使用红色背景,满足你“提示为红色”的需求
|
||||||
|
success: "border-emerald-500/70",
|
||||||
|
info: "border-sky-500/70",
|
||||||
|
warning: "border-amber-500/70",
|
||||||
|
error: "bg-destructive text-destructive-foreground border-destructive/80",
|
||||||
|
loading: "border-primary/60",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { Toaster }
|
||||||
22
package-lock.json
generated
22
package-lock.json
generated
@@ -26,10 +26,12 @@
|
|||||||
"lodash": "^4.17.23",
|
"lodash": "^4.17.23",
|
||||||
"lucide-react": "^0.577.0",
|
"lucide-react": "^0.577.0",
|
||||||
"next": "^16.1.5",
|
"next": "^16.1.5",
|
||||||
|
"next-themes": "^0.4.6",
|
||||||
"radix-ui": "^1.4.3",
|
"radix-ui": "^1.4.3",
|
||||||
"react": "18.3.1",
|
"react": "18.3.1",
|
||||||
"react-chartjs-2": "^5.3.1",
|
"react-chartjs-2": "^5.3.1",
|
||||||
"react-dom": "18.3.1",
|
"react-dom": "18.3.1",
|
||||||
|
"sonner": "^2.0.7",
|
||||||
"tailwind-merge": "^3.5.0",
|
"tailwind-merge": "^3.5.0",
|
||||||
"tesseract.js": "^5.1.1",
|
"tesseract.js": "^5.1.1",
|
||||||
"uuid": "^13.0.0",
|
"uuid": "^13.0.0",
|
||||||
@@ -9903,6 +9905,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/next-themes": {
|
||||||
|
"version": "0.4.6",
|
||||||
|
"resolved": "https://registry.npmmirror.com/next-themes/-/next-themes-0.4.6.tgz",
|
||||||
|
"integrity": "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/next/node_modules/postcss": {
|
"node_modules/next/node_modules/postcss": {
|
||||||
"version": "8.4.31",
|
"version": "8.4.31",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
|
||||||
@@ -11669,6 +11681,16 @@
|
|||||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/sonner": {
|
||||||
|
"version": "2.0.7",
|
||||||
|
"resolved": "https://registry.npmmirror.com/sonner/-/sonner-2.0.7.tgz",
|
||||||
|
"integrity": "sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/source-map": {
|
"node_modules/source-map": {
|
||||||
"version": "0.6.1",
|
"version": "0.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||||
|
|||||||
@@ -29,10 +29,12 @@
|
|||||||
"lodash": "^4.17.23",
|
"lodash": "^4.17.23",
|
||||||
"lucide-react": "^0.577.0",
|
"lucide-react": "^0.577.0",
|
||||||
"next": "^16.1.5",
|
"next": "^16.1.5",
|
||||||
|
"next-themes": "^0.4.6",
|
||||||
"radix-ui": "^1.4.3",
|
"radix-ui": "^1.4.3",
|
||||||
"react": "18.3.1",
|
"react": "18.3.1",
|
||||||
"react-chartjs-2": "^5.3.1",
|
"react-chartjs-2": "^5.3.1",
|
||||||
"react-dom": "18.3.1",
|
"react-dom": "18.3.1",
|
||||||
|
"sonner": "^2.0.7",
|
||||||
"tailwind-merge": "^3.5.0",
|
"tailwind-merge": "^3.5.0",
|
||||||
"tesseract.js": "^5.1.1",
|
"tesseract.js": "^5.1.1",
|
||||||
"uuid": "^13.0.0",
|
"uuid": "^13.0.0",
|
||||||
|
|||||||
Reference in New Issue
Block a user