From 2268db56c7ea9147956356a819b56bf65aa317b4 Mon Sep 17 00:00:00 2001 From: Ashi Jain Date: Tue, 17 Jun 2025 14:24:49 +0530 Subject: [PATCH] Added react-toastify notification for offline connectivity of user --- package-lock.json | 14 + package.json | 1 + src/Pages/ChatbotPage.jsx | 297 +++++----- src/Pages/Dashboard.jsx | 255 +++++---- src/Pages/ExpensePage.jsx | 511 ++++++++++-------- src/Pages/IncomePage.jsx | 496 +++++++++-------- src/Pages/InsightsPage.jsx | 153 +++--- src/Pages/Layout.jsx | 10 +- src/Pages/SettingsPage.jsx | 240 ++++---- src/Pages/login.jsx | 282 +++++----- src/Pages/signup.jsx | 222 ++++---- .../components/NetworkStatusToast.jsx | 41 ++ 12 files changed, 1414 insertions(+), 1108 deletions(-) create mode 100644 src/components/components/NetworkStatusToast.jsx diff --git a/package-lock.json b/package-lock.json index d51a4b4..4e3c946 100644 --- a/package-lock.json +++ b/package-lock.json @@ -48,6 +48,7 @@ "react-dom": "^18.3.1", "react-hook-form": "^7.54.2", "react-router-dom": "^7.1.1", + "react-toastify": "^11.0.5", "recharts": "^2.15.0", "sonner": "^2.0.1", "tailwind-merge": "^2.6.0", @@ -8798,6 +8799,19 @@ } } }, + "node_modules/react-toastify": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-11.0.5.tgz", + "integrity": "sha512-EpqHBGvnSTtHYhCPLxML05NLY2ZX0JURbAdNYa6BUkk+amz4wbKBQvoKQAB0ardvSarUBuY4Q4s1sluAzZwkmA==", + "license": "MIT", + "dependencies": { + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": "^18 || ^19", + "react-dom": "^18 || ^19" + } + }, "node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", diff --git a/package.json b/package.json index 6fd4664..afaf46b 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "react-dom": "^18.3.1", "react-hook-form": "^7.54.2", "react-router-dom": "^7.1.1", + "react-toastify": "^11.0.5", "recharts": "^2.15.0", "sonner": "^2.0.1", "tailwind-merge": "^2.6.0", diff --git a/src/Pages/ChatbotPage.jsx b/src/Pages/ChatbotPage.jsx index a6fc516..f9293a3 100644 --- a/src/Pages/ChatbotPage.jsx +++ b/src/Pages/ChatbotPage.jsx @@ -22,6 +22,9 @@ const model = genAI.getGenerativeModel({ model: "gemini-1.5-flash" }) const auth = getAuth() const db = getFirestore() +import { ToastContainer, toast } from "react-toastify"; +import "react-toastify/dist/ReactToastify.css"; + // Utility function to generate context-aware prompts const generateContextualPrompt = (userData, messages, userInput) => { return `You are an intelligent financial assistant. Here is the user's current financial data, remember that data is based on the INR/₹ not dollar: @@ -70,6 +73,31 @@ export default function ChatbotPage() { const [userData, setUserData] = useState(null) const messagesEndRef = useRef(null) + //✨ Toast network status + useEffect(() => { + const handleOffline = () => { + toast.error("You're offline. Please check your Internet Connection.", { + toastId: "offline-toast", + autoClose: false, + closeOnClick: false, + draggable: false, + }); + }; + + const handleOnline = () => { + toast.dismiss("offline-toast"); + }; + + window.addEventListener("offline", handleOffline); + window.addEventListener("online", handleOnline); + + return () => { + window.removeEventListener("offline", handleOffline); + window.removeEventListener("online", handleOnline); + }; + }, []); + + // Fetch user data and chat history useEffect(() => { const unsubscribe = auth.onAuthStateChanged(async (user) => { @@ -309,149 +337,152 @@ Security Guidelines: } return ( -
- {/* Sidebar overlay */} - {isSidebarOpen && ( -
setIsSidebarOpen(false)} /> - )} - - {/* Sidebar component */} - setIsSidebarOpen(false)} - user={user} - /> - - {/* Main content area - full width with no constraints */} -
- {/* Header - full width */} -
-
-
-
- - - AI - -
-

AI Assistant

- - {isLoading ? 'Thinking...' : 'Online'} - -
-
-
- - - setIsSidebarOpen(!isSidebarOpen)} - onLogout={() => auth.signOut()} - hideNameOnMobile={true} - /> -
-
-
-
- - {/* Main chat area - full width with padding */} -
-
- {messages.map((message, index) => ( -
- {/* Avatar for Bot */} - {message.sender === 'bot' && ( - - +
+
+ {/* Sidebar overlay */} + {isSidebarOpen && ( +
setIsSidebarOpen(false)} /> + )} + + {/* Sidebar component */} + setIsSidebarOpen(false)} + user={user} + /> + + {/* Main content area - full width with no constraints */} +
+ {/* Header - full width */} +
+
+
+
+ + AI - )} - - {/* Chat Bubble */} -
-
+

AI Assistant

+ + {isLoading ? 'Thinking...' : 'Online'} + +
+
+
+ + + setIsSidebarOpen(!isSidebarOpen)} + onLogout={() => auth.signOut()} + hideNameOnMobile={true} + /> +
+
+
+
+ + {/* Main chat area - full width with padding */} +
+
+ {messages.map((message, index) => ( +
+ {/* Avatar for Bot */} + {message.sender === 'bot' && ( + + + AI + + )} + + {/* Chat Bubble */} +
+
+
+ {message.text} +
+ + {/* Timestamp */} + + {formatTime(message.timestamp)} +
- {/* Timestamp */} - - {formatTime(message.timestamp)} - + {/* User Avatar */} + {message.sender === 'user' && ( + + {user?.displayName?.[0] || 'U'} + + )}
- - {/* User Avatar */} - {message.sender === 'user' && ( - - {user?.displayName?.[0] || 'U'} - + ))} +
+
+
+ + {/* Input area - full width */} +
+
+
+ + {showEmojiPicker && ( +
+ +
)}
- ))} -
-
-
- - {/* Input area - full width */} -
-
-
- - {showEmojiPicker && ( -
- -
- )} -
-
- setInput(e.target.value)} - placeholder="Type your message..." - className="rounded-full border-gray-200 pr-12 focus:border-blue-500 focus:ring-1 focus:ring-blue-500 text-sm sm:text-base py-2" - onKeyPress={(e) => e.key === 'Enter' && handleSend()} - disabled={isLoading} - /> - +
+ setInput(e.target.value)} + placeholder="Type your message..." + className="rounded-full border-gray-200 pr-12 focus:border-blue-500 focus:ring-1 focus:ring-blue-500 text-sm sm:text-base py-2" + onKeyPress={(e) => e.key === 'Enter' && handleSend()} + disabled={isLoading} + /> + +
-
-
-
+ +
+
+ ) } \ No newline at end of file diff --git a/src/Pages/Dashboard.jsx b/src/Pages/Dashboard.jsx index 3db1191..1d552fc 100644 --- a/src/Pages/Dashboard.jsx +++ b/src/Pages/Dashboard.jsx @@ -42,6 +42,9 @@ ChartJS.register( Legend ); +import { ToastContainer, toast } from "react-toastify"; +import "react-toastify/dist/ReactToastify.css"; + function Dashboard() { const navigate = useNavigate(); const [user, setUser] = useState(null); @@ -50,6 +53,30 @@ function Dashboard() { const [selectedTransactionType, setSelectedTransactionType] = useState("all"); const [isSidebarOpen, setIsSidebarOpen] = useState(false); + //✨ Toast network status + useEffect(() => { + const handleOffline = () => { + toast.error("You're offline. Please check your Internet Connection.", { + toastId: "offline-toast", + autoClose: false, + closeOnClick: false, + draggable: false, + }); + }; + + const handleOnline = () => { + toast.dismiss("offline-toast"); + }; + + window.addEventListener("offline", handleOffline); + window.addEventListener("online", handleOnline); + + return () => { + window.removeEventListener("offline", handleOffline); + window.removeEventListener("online", handleOnline); + }; + }, []); + useEffect(() => { const unsubscribe = auth.onAuthStateChanged(async (user) => { if (user) { @@ -337,123 +364,127 @@ function Dashboard() { const filteredTransactions = getFilteredTransactions(); return ( -
- {isSidebarOpen && ( -
setIsSidebarOpen(false)} /> - )} - setIsSidebarOpen(false)} - user={user} - /> - - {/* Header */} -
-
-
-

Dashboard

-
- setIsSidebarOpen(!isSidebarOpen)} - onLogout={() => auth.signOut()} - /> +
+
+ {isSidebarOpen && ( +
setIsSidebarOpen(false)} /> + )} + setIsSidebarOpen(false)} + user={user} + /> + + {/* Header */} +
+
+
+

Dashboard

+
+ setIsSidebarOpen(!isSidebarOpen)} + onLogout={() => auth.signOut()} + /> +
-
-
- - {/* Main Content */} -
-
- {/* Summary Cards */} -
- - - Total Balance - - - -
- {formatCurrency(userData?.totalBalance || 0)} -
-
-
- - - - Monthly Income - - - -
- {formatCurrency(income)} -
- {recurringIncome > 0 && ( -
- {/* - Passive Income: {formatCurrency(recurringIncome)} */} + + + {/* Main Content */} +
+
+ {/* Summary Cards */} +
+ + + Total Balance + + + +
+ {formatCurrency(userData?.totalBalance || 0)} +
+
+
+ + + + Monthly Income + + + +
+ {formatCurrency(income)} +
+ {recurringIncome > 0 && ( +
+ {/* + Passive Income: {formatCurrency(recurringIncome)} */} +
+ )} +
+
+ + + + Monthly Expenses + + + +
+ {formatCurrency(expenses)}
- )} -
-
- - - - Monthly Expenses - - - -
- {formatCurrency(expenses)} -
-
-
- - - - Savings Goal - - - -
- {formatCurrency(userData?.savingsGoal || 0)} -
-
-
+ + + + + + Savings Goal + + + +
+ {formatCurrency(userData?.savingsGoal || 0)} +
+
+
+
+ + {/* Financial Accounts Components */} + {userData?.accounts && ( + <> + + + + + )} + + {/* Charts Component */} + + + {/* Recent Transactions Component */} +
+
+
- {/* Financial Accounts Components */} - {userData?.accounts && ( - <> - - - - - )} - - {/* Charts Component */} - - - {/* Recent Transactions Component */} - -
-
+
); } diff --git a/src/Pages/ExpensePage.jsx b/src/Pages/ExpensePage.jsx index 61be3e3..10d47b2 100644 --- a/src/Pages/ExpensePage.jsx +++ b/src/Pages/ExpensePage.jsx @@ -41,6 +41,9 @@ import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import * as z from "zod"; +import { ToastContainer, toast } from "react-toastify"; +import "react-toastify/dist/ReactToastify.css"; + const formSchema = z.object({ amount: z.string().min(1, "Amount is required"), date: z.date({ @@ -73,6 +76,30 @@ function AddExpense() { }, }); + //✨ Toast network status + useEffect(() => { + const handleOffline = () => { + toast.error("You're offline. Please check your Internet Connection.", { + toastId: "offline-toast", + autoClose: false, + closeOnClick: false, + draggable: false, + }); + }; + + const handleOnline = () => { + toast.dismiss("offline-toast"); + }; + + window.addEventListener("offline", handleOffline); + window.addEventListener("online", handleOnline); + + return () => { + window.removeEventListener("offline", handleOffline); + window.removeEventListener("online", handleOnline); + }; + }, []); + useEffect(() => { const fetchUserData = async (userId) => { try { @@ -159,73 +186,144 @@ function AddExpense() { ); } return ( -
- {isSidebarOpen && ( -
setIsSidebarOpen(false)} /> - )} - - setIsSidebarOpen(false)} - user={user} - /> - -
-
-
-
-

Expense

- setIsSidebarOpen(!isSidebarOpen)} - onLogout={() => auth.signOut()} - /> -
-
-
- -
-
- {showSuccess && ( -
- - - - Expense added successfully! - - +
+
+ {isSidebarOpen && ( +
setIsSidebarOpen(false)} /> + )} + + setIsSidebarOpen(false)} + user={user} + /> + +
+
+
+
+

Expense

+ setIsSidebarOpen(!isSidebarOpen)} + onLogout={() => auth.signOut()} + />
- )} - - - - Add New Expense - Track your spending by adding a new expense - - -
- -
+
+
+ +
+
+ {showSuccess && ( +
+ + + + Expense added successfully! + + +
+ )} + + + + Add New Expense + Track your spending by adding a new expense + + + + +
+ ( + + Amount * + +
+ + +
+
+ +
+ )} + /> + + ( + + Date * + + + + + + + + + date > new Date() || date < new Date("1900-01-01") + } + initialFocus + /> + + + + + )} + /> +
+ ( - Amount * - -
- - -
-
+ Category * +
)} @@ -233,210 +331,143 @@ function AddExpense() { ( - Date * - - - - - - - - - date > new Date() || date < new Date("1900-01-01") - } - initialFocus - /> - - + Description + +