diff --git a/src/electron/ipc/ipcHandler.ts b/src/electron/ipc/ipcHandler.ts index 3c31f29..edaa1df 100644 --- a/src/electron/ipc/ipcHandler.ts +++ b/src/electron/ipc/ipcHandler.ts @@ -407,7 +407,7 @@ ipcMain.handle("get-payslips", async (event, vaId: string) => { console.log("get-payslips", vaId) try { const response = await api.get(`/paySlip/${vaId}`); - console.log(response.data , "Line 409") + console.log(response.data, "Line 409") return { success: true, data: response.data || [], @@ -493,6 +493,47 @@ ipcMain.handle("get-adjustments-list", async (event, vaId: string) => { }); +ipcMain.handle("update-payment", async (event, data: Record) => { + console.log("data-update-payment", data); + + // Build userDetails object + const userDetails: Record = { + paymentMethod: data.paymentMethod, + }; + + // Add payment-specific fields + if (data.paymentMethod === "wise" && data.wiseEmail) { + userDetails.wiseEmail = data.wiseEmail; + // Clear veemAccount when switching to wise + userDetails.veemAccount = ""; + } else if (data.paymentMethod === "veem" && data.veemAccount) { + userDetails.veemAccount = data.veemAccount; + // Clear wiseEmail when switching to veem + userDetails.wiseEmail = ""; + } + + // Build final payload + const payload = { + userDetails: userDetails, + }; + + console.log("Update Payment payload", payload); + + try { + const response = await api.patch("/profile", payload); + console.log("Update-payment-response", response); + return { success: true, data: response?.data?.userDetails }; + } catch (error: any) { + console.error("❌ Update payment error:", error.message); + return { + success: false, + message: error.response?.data?.message || "Payment update failed", + }; + } +}); + + + export const sendTokenExpired = () => { console.log("⚠️ Sending token-expired event to renderer"); diff --git a/src/electron/preload.cts b/src/electron/preload.cts index 668496d..870cf71 100644 --- a/src/electron/preload.cts +++ b/src/electron/preload.cts @@ -34,6 +34,12 @@ contextBridge.exposeInMainWorld('electron', { changePassword: (data: { oldPassword: string; password: string; cPassword: string }) => ipcRenderer.invoke("change-password", data), + updatePayment: (data: { + paymentMethod: string; + wiseEmail?: string; + veemAccount?: string; + }) => ipcRenderer.invoke('update-payment', data), + onTokenExpired: (callback) => { // Remove old listeners ipcRenderer.removeAllListeners("token-expired"); diff --git a/src/electron/utils/utilityFunctions.ts b/src/electron/utils/utilityFunctions.ts index c09a7ab..0a15c7d 100644 --- a/src/electron/utils/utilityFunctions.ts +++ b/src/electron/utils/utilityFunctions.ts @@ -68,7 +68,7 @@ export const handleLogin = async (credentials: { email: string, password: string try { console.log("➡️ Calling API..."); const response = await axios.post( - 'https://quality-friendly-goldfish.ngrok-free.app/api/v1/va/login', + 'https://daniela-nonprofessed-boastfully.ngrok-free.app/api/v1/va/login', { userDetails: { ...credentials } }, { headers: { "ngrok-skip-browser-warning": 69420 }, httpsAgent } ); diff --git a/src/ui/helper/ToastContext.tsx b/src/ui/helper/ToastContext.tsx new file mode 100644 index 0000000..4aa1289 --- /dev/null +++ b/src/ui/helper/ToastContext.tsx @@ -0,0 +1,99 @@ +// contexts/ToastContext.tsx +import React, { createContext, useContext, useState, ReactNode } from 'react'; + +interface Toast { + id: number; + message: string; + type: 'success' | 'error' | 'info' | 'warning'; +} + +interface ToastContextType { + showToast: (message: string, type?: Toast['type']) => void; + hideToast: (id: number) => void; +} + +const ToastContext = createContext(undefined); + +export const ToastProvider: React.FC<{ children: ReactNode }> = ({ children }) => { + const [toasts, setToasts] = useState([]); + + const showToast = (message: string, type: Toast['type'] = 'success') => { + const id = Date.now(); + setToasts(prev => [...prev, { id, message, type }]); + + // Auto remove after 5 seconds + setTimeout(() => { + hideToast(id); + }, 3000); + }; + + const hideToast = (id: number) => { + setToasts(prev => prev.filter(toast => toast.id !== id)); + }; + + return ( + + {children} + + + ); +}; + +export const useToast = () => { + const context = useContext(ToastContext); + if (!context) { + throw new Error('useToast must be used within a ToastProvider'); + } + return context; +}; + +const ToastContainer: React.FC<{ + toasts: Toast[]; + hideToast: (id: number) => void; +}> = ({ toasts, hideToast }) => { + const getTypeStyles = (type: Toast['type']) => { + switch (type) { + case 'success': + return 'bg-success text-success-content'; + case 'error': + return 'bg-error text-error-content'; + case 'warning': + return 'bg-warning text-warning-content'; + case 'info': + return 'bg-info text-info-content'; + default: + return 'bg-neutral text-neutral-content'; + } + }; + + return ( +
+ {toasts.map((toast) => ( +
+
hideToast(toast.id)} + > +
+

+ {toast.message} +

+
+ +
+
+ ))} +
+); +}; \ No newline at end of file diff --git a/src/ui/layout/DashboardLayout.tsx b/src/ui/layout/DashboardLayout.tsx index 1359372..46d39d8 100644 --- a/src/ui/layout/DashboardLayout.tsx +++ b/src/ui/layout/DashboardLayout.tsx @@ -588,7 +588,7 @@ const DashboardLayout = () => { if (!selectedProject?.projectId) return console.log(selectedProject?.projectId, "SelectedProject ID") - socketRef.current = io("https://quality-friendly-goldfish.ngrok-free.app", { + socketRef.current = io("https://daniela-nonprofessed-boastfully.ngrok-free.app", { transports: ["websocket"], secure: true, rejectUnauthorized: false, diff --git a/src/ui/main.tsx b/src/ui/main.tsx index 9171cd7..5a91adf 100644 --- a/src/ui/main.tsx +++ b/src/ui/main.tsx @@ -4,9 +4,12 @@ import App from './App' import './index.css' import { Provider } from "react-redux"; import { store } from "./store"; +import { ToastProvider } from './helper/ToastContext'; ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( - + + + ) diff --git a/src/ui/pages/Dashboard.tsx b/src/ui/pages/Dashboard.tsx index 5506739..8130cbe 100644 --- a/src/ui/pages/Dashboard.tsx +++ b/src/ui/pages/Dashboard.tsx @@ -133,7 +133,7 @@ const Dashboard: React.FC = () => { try { console.log("📡 Fetching projects...Dashboard"); const res = await window.electron.getProjectList(); - console.log(" Project list response in dashboard:", res); + console.log(" Project list response in dashboard: ", res.data?.[0]); if (res?.success && Array.isArray(res.data)) { // Store data redux store @@ -152,9 +152,11 @@ const Dashboard: React.FC = () => { ); // Auto-select first project - if (res.data.length > 0 && !selectedProject) { + if (res.data.length > 0) { const firstProject = res.data[0]; + + console.log("Line 158" , firstProject) // Select the project dispatch(setSelectedProjectSlice(firstProject)); diff --git a/src/ui/pages/LeaveAndAdjustments/tabs/AdjustmentTab.tsx b/src/ui/pages/LeaveAndAdjustments/tabs/AdjustmentTab.tsx index 516127f..7e99b90 100644 --- a/src/ui/pages/LeaveAndAdjustments/tabs/AdjustmentTab.tsx +++ b/src/ui/pages/LeaveAndAdjustments/tabs/AdjustmentTab.tsx @@ -5,6 +5,7 @@ import { quillModules } from '../../../utils/quillConfig'; import Quill from 'quill'; import 'quill/dist/quill.snow.css'; import { useNavigate } from 'react-router-dom'; +import { useToast } from '../../../helper/ToastContext'; interface Project { projectId: string; @@ -50,6 +51,7 @@ const AdjustmentTab: React.FC = ({ projects }) => { const [isSubmitting, setIsSubmitting] = useState(false); const quillRef = useRef(null); const navigate = useNavigate() + const { showToast } = useToast(); // Field validation functions const validateProjectId = (projectId: string): string | undefined => { @@ -460,9 +462,14 @@ const AdjustmentTab: React.FC = ({ projects }) => { if (res?.success) { console.log("Timesheet Created Successfully"); - alert(res.message); resetForm(); - navigate('/dashboard/leave-adjustments') + // Show success toast instead of alert + showToast("PTO Request submitted successfully!", "success"); + + // Navigate after a small delay + setTimeout(() => { + navigate('/dashboard/leave-adjustments'); + }, 300); } else { setFormErrors(prev => ({ ...prev, @@ -475,6 +482,7 @@ const AdjustmentTab: React.FC = ({ projects }) => { ...prev, general: err.message || "Something went wrong while saving timesheet" })); + showToast(err.message || "Something went wrong while saving timesheet", "error"); } finally { setIsSubmitting(false); } diff --git a/src/ui/pages/LeaveAndAdjustments/tabs/PtoLeaveTab.tsx b/src/ui/pages/LeaveAndAdjustments/tabs/PtoLeaveTab.tsx index 1bd14a1..6a271f2 100644 --- a/src/ui/pages/LeaveAndAdjustments/tabs/PtoLeaveTab.tsx +++ b/src/ui/pages/LeaveAndAdjustments/tabs/PtoLeaveTab.tsx @@ -5,6 +5,7 @@ import ReactQuill from "react-quill-new"; import Quill from "quill"; import "quill/dist/quill.snow.css"; import { useNavigate } from "react-router-dom"; +import { useToast } from "../../../helper/ToastContext"; interface LeaveFormData { fromDate: string; @@ -26,6 +27,9 @@ const PtoLeaveTab: React.FC<{ const quillRef = useRef(null); const navigate = useNavigate() + const { showToast } = useToast(); + + console.log("ptoLeave", ptoLeave) const [errors, setErrors] = useState>({}); useEffect(() => { @@ -200,20 +204,26 @@ const PtoLeaveTab: React.FC<{ if (result.success) { resetForm(); - alert("PTO Request submitted!"); - navigate('/dashboard/leave-adjustments') + // Show success toast instead of alert + showToast("PTO Request submitted successfully!", "success"); + + // Navigate after a small delay + setTimeout(() => { + navigate('/dashboard/leave-adjustments'); + }, 300); } else { setServerError(result.message || "Something went wrong"); + showToast(result.message || "Failed to submit PTO request", "error"); } + }; return ( <> - +
- {/* From Date */}
- {errors.comment &&

{errors.comment}

} + {errors.comment &&

{errors.comment}

}
{serverError && ( -

{serverError}

- )} +

{serverError}

+ )} +

+ Change Payment Method +

- -
- - - ); + {/* Error */} + {error && ( +
{error}
+ )} + + {/* Dropdown */} +
+ + + +
+ + {/* Wise Email */} + {method === "wise" && ( +
+ + setWiseEmail(e.target.value)} + /> +
+ )} + + {/* Veem Account */} + {method === "veem" && ( +
+ + setVeemAccount(e.target.value)} + /> +
+ )} + +
+ + + +
+ + + ); }; export default ChangePaymentModal; diff --git a/src/ui/pages/Settings/Settings.tsx b/src/ui/pages/Settings/Settings.tsx index c8a8f4a..458cf81 100644 --- a/src/ui/pages/Settings/Settings.tsx +++ b/src/ui/pages/Settings/Settings.tsx @@ -104,24 +104,24 @@ const Settings: React.FC = () => { - {profile?.paymentMethod === "wise" && ( + {profile?.veemAccount && (

- 💳 Wise + 🏦 Veem Account

-

- {maskLast5(profile?.wiseEmail)} +

+ {profile?.veemAccount || "N/A"}

)} - {profile?.paymentMethod === "veem" && ( + {profile?.wiseEmail&& (
-

- 🏦 Veem +

+ 🏦 Wise Email

-

- {profile?.veemAccount || "N/A"} +

+ {profile?.wiseEmail || "N/A"}

)} diff --git a/types.d.ts b/types.d.ts index 1c693fb..8f20363 100644 --- a/types.d.ts +++ b/types.d.ts @@ -154,16 +154,16 @@ interface Window { vaId: string; projectId: string; type: string; - time:string + time: string }) => Promise; stopProjectTracking: (data: { vaId: string; projectId: string; type: string; - time:string + time: string }) => Promise; trackingTabsOpeningClosing: (data: { - id?: string; + id?: string; logId: string; projectId: string; title: string; @@ -193,6 +193,13 @@ interface Window { cPassword: string; }) => Promise<{ success: boolean; message?: string; data?: any }>; + // In your electron API handler + updatePayment: (data: { + paymentMethod: string; + wiseEmail?: string; + veemAccount?: string; + }) => Promise<{ success: boolean; message?: string; data?: any }>; + onTokenExpired: (callback: () => void) => void;