diff --git a/build-all.sh b/build-all.sh new file mode 100644 index 0000000..691e010 --- /dev/null +++ b/build-all.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# Windows +GOOS=windows GOARCH=amd64 go build -o ./bin/win/wireguard-go.exe ./wireguard-go + +# Linux +GOOS=linux GOARCH=amd64 go build -o ./bin/linux/wireguard-go ./wireguard-go + +# macOS Intel +GOOS=darwin GOARCH=amd64 go build -o ./bin/mac/wireguard-go ./wireguard-go + +# macOS ARM +GOOS=darwin GOARCH=arm64 go build -o ./bin/mac/wireguard-go-arm ./wireguard-go diff --git a/package.json b/package.json index bd32b08..d764e76 100644 --- a/package.json +++ b/package.json @@ -70,6 +70,14 @@ { "from": "src/assets", "to": "assets" + }, + { + "from": "resources/wireguard-install", + "to": "wireguard-install" + }, + { + "from": "resources/wireguard", + "to": "wireguard" } ], "mac": { @@ -86,6 +94,7 @@ } }, "win": { + "requestedExecutionLevel": "requireAdministrator", "target": [ { "target": "nsis", @@ -129,4 +138,4 @@ "vite": "^7.2.6", "vitest": "^4.0.15" } -} +} \ No newline at end of file diff --git a/resources/wireguard-install/wireguard-installer.exe b/resources/wireguard-install/wireguard-installer.exe new file mode 100644 index 0000000..032922d Binary files /dev/null and b/resources/wireguard-install/wireguard-installer.exe differ diff --git a/resources/wireguard/mac/wg0.conf b/resources/wireguard/mac/wg0.conf new file mode 100644 index 0000000..5053efb --- /dev/null +++ b/resources/wireguard/mac/wg0.conf @@ -0,0 +1,10 @@ +[Interface] +PrivateKey = CEzCd25ufxbOjvRhMqLfkcdP/OziG5i6OkXCfMZrI1I= +Address = 10.0.0.2/24 +DNS = 1.1.1.1 + +[Peer] +PublicKey = jG+7H5thEiNjxY1XqI1R9oyMAMTIegLwE3tZkS1Rj2U= +Endpoint = 35.95.209.161:51820 +AllowedIPs = 10.0.0.0/24 +PersistentKeepalive = 25 diff --git a/resources/wireguard/windows/wg0.conf b/resources/wireguard/windows/wg0.conf new file mode 100644 index 0000000..5053efb --- /dev/null +++ b/resources/wireguard/windows/wg0.conf @@ -0,0 +1,10 @@ +[Interface] +PrivateKey = CEzCd25ufxbOjvRhMqLfkcdP/OziG5i6OkXCfMZrI1I= +Address = 10.0.0.2/24 +DNS = 1.1.1.1 + +[Peer] +PublicKey = jG+7H5thEiNjxY1XqI1R9oyMAMTIegLwE3tZkS1Rj2U= +Endpoint = 35.95.209.161:51820 +AllowedIPs = 10.0.0.0/24 +PersistentKeepalive = 25 diff --git a/src/electron/api/axiosClient.ts b/src/electron/api/axiosClient.ts index fbed677..95d6d3e 100644 --- a/src/electron/api/axiosClient.ts +++ b/src/electron/api/axiosClient.ts @@ -12,7 +12,7 @@ const httpsAgent = new https.Agent({ }); const api: AxiosInstance = axios.create({ - baseURL: 'https://daniela-nonprofessed-boastfully.ngrok-free.app/api/v1/va', // 🔁 your backend + baseURL: 'https://quality-friendly-goldfish.ngrok-free.app/api/v1/va', // 🔁 your backend httpsAgent // timeout: 10000, }); diff --git a/src/electron/ipc/ipcHandler.ts b/src/electron/ipc/ipcHandler.ts index 3c31f29..5ec569b 100644 --- a/src/electron/ipc/ipcHandler.ts +++ b/src/electron/ipc/ipcHandler.ts @@ -3,6 +3,7 @@ import { getToken, saveToken, clearToken } from "../utils/tokenHelper.js"; import { getActiveApp, handleLogin, takeScreenshot, startWindowPolling, getRunningAppsList, captureSpecificApp } from "../utils/utilityFunctions.js"; import api from "../api/axiosClient.js"; import { getMainWindow } from '../main.js'; +import { startVPN, stopVPN } from "../vpn.js"; @@ -407,7 +408,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 +494,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"); @@ -505,4 +547,10 @@ export const sendTokenExpired = () => { }; -; +ipcMain.handle("vpn-connect", async () => { + return startVPN(); +}); + +ipcMain.handle("vpn-disconnect", async () => { + stopVPN(); +}); \ No newline at end of file diff --git a/src/electron/main.ts b/src/electron/main.ts index 2e6f53d..9dbe4d0 100644 --- a/src/electron/main.ts +++ b/src/electron/main.ts @@ -8,6 +8,9 @@ import "./ipc/ipcHandler.js"; import path from "path"; import { fileURLToPath } from "url"; +import * as fs from "fs"; +import { execFile } from "child_process"; + app.setAppUserModelId("com.tracker.app"); const __filename = fileURLToPath(import.meta.url); @@ -52,12 +55,26 @@ app.on("second-instance", () => { } }); + +// CHECKING IF WIREGUARD EXIST - IF NOT INSTALLING IN CLIENT MACHINE +const wgPath = "C:\\Program Files\\WireGuard\\wireguard.exe"; + +if (!fs.existsSync(wgPath)) { + const installer = path.join(process.resourcesPath, "wireguard-install", "wireguard-installer.exe"); + + execFile(installer, ["/quiet"], (err) => { + if (err) console.error("WireGuard install failed:", err); + else console.log("WireGuard installed successfully."); + }); +} +// CHECKING IF WIREGUARD EXIST - IF NOT INSTALLING IN CLIENT MACHINE + + // --------------------------- // MAIN APP READY // --------------------------- app.on("ready", async () => { - splashWindow = new BrowserWindow({ width: 300, height: 300, @@ -90,8 +107,8 @@ app.on("ready", async () => { // alwaysOnTop: true, // skipTaskbar: true, autoHideMenuBar: true, - frame: true, - show: false, + frame: true, + show: false, icon: resolveIcon(), webPreferences: { devTools: true, @@ -102,14 +119,14 @@ app.on("ready", async () => { }); - - // win.setAlwaysOnTop(true, 'screen-saver'); - // win.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true }); - // win.setFullScreenable(false); - + + // win.setAlwaysOnTop(true, 'screen-saver'); + // win.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true }); + // win.setFullScreenable(false); + // Cross-platform safe always-on-top const isMac = process.platform === "darwin"; - + if (isMac) { win.setAlwaysOnTop(true, "floating"); // best for macOS win.setVisibleOnAllWorkspaces(true); // NO fullscreen override @@ -117,7 +134,7 @@ app.on("ready", async () => { win.setAlwaysOnTop(true, "normal"); // safest for Windows + Zoom win.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true }); win.setFullScreenable(false); -} + } mainWindow = win; diff --git a/src/electron/preload.cts b/src/electron/preload.cts index 668496d..1de0eea 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"); @@ -59,6 +65,9 @@ contextBridge.exposeInMainWorld('electron', { getLeaveList: () => ipcRenderer.invoke("get-leave-list"), getAdjustmentsList:(vaId: string) => ipcRenderer.invoke("get-adjustments-list",vaId), + connectVPN: () => ipcRenderer.invoke("vpn-connect"), + disconnectVPN: () => ipcRenderer.invoke("vpn-disconnect"), + } satisfies Window['electron']); // function ipcInvoke( diff --git a/src/electron/vpn.ts b/src/electron/vpn.ts new file mode 100644 index 0000000..19596b9 --- /dev/null +++ b/src/electron/vpn.ts @@ -0,0 +1,223 @@ +import { app } from "electron"; +import { exec, execFile } from "child_process"; +import fs from "fs"; +import os from "os"; +import path from "path"; + +// +// ------------------ HELPERS ------------------ +// + +// Promise wrapper for exec() +function execPromise(cmd: string): Promise { + return new Promise((resolve, reject) => { + exec(cmd, (err, stdout, stderr) => { + if (err) return reject(stderr || err); + resolve(stdout); + }); + }); +} + +// Detect dev vs production +function isDev() { + return !app.isPackaged; +} + +// +// ------------------ PATH RESOLVERS ------------------ +// + +// WireGuard installer path (Windows only) +// function getWireGuardInstallerPath(): string { +// return isDev() +// ? path.join(process.cwd(), "resources", "wireguard-install", "wireguard-installer.exe") +// : path.join(process.resourcesPath, "wireguard-install", "wireguard-installer.exe"); +// } + +function getWireGuardInstallerPath(): string { + const devPath = path.join(process.cwd(), "resources", "wireguard-install", "wireguard-installer.exe"); + + // When running "npm run dev", use devPath ALWAYS + if (!app.isPackaged) { + console.log("DEV MODE Installer Path:", devPath); + return devPath; + } + + // Production build path + const prodPath = path.join(process.resourcesPath, "wireguard-install", "wireguard-installer.exe"); + console.log("PROD MODE Installer Path:", prodPath); + return prodPath; +} + + +// OS-specific wg0.conf +function getConfigPath(): string { + const base = isDev() + ? path.join(process.cwd(), "resources", "wireguard") + : path.join(process.resourcesPath, "wireguard"); + + if (process.platform === "win32") { + return path.join(base, "windows", "wg0.conf"); + } + + if (process.platform === "darwin") { + return path.join(base, "macos", "wg0.conf"); + } + + throw new Error("Unsupported OS"); +} + +// Detect installed WireGuard.exe on Windows +function findWireGuardCLI(): string | null { + const possiblePaths = [ + "C:\\Program Files\\WireGuard\\wireguard.exe", + "C:\\Program Files (x86)\\WireGuard\\wireguard.exe" + ]; + + for (const p of possiblePaths) { + if (fs.existsSync(p)) return p; + } + + return null; +} + +// +// ------------------ WINDOWS INSTALL LOGIC ------------------ +// + +// Install WireGuard silently +async function ensureWireGuardInstalled(): Promise { + let cli = findWireGuardCLI(); + if (cli) return cli; + + // Installer path + const installer = getWireGuardInstallerPath(); + if (!fs.existsSync(installer)) { + throw new Error("WireGuard installer not found in resources."); + } + + console.log("WireGuard not found. Installing silently..."); + await execPromise(`"${installer}" /quiet`); + + cli = findWireGuardCLI(); + if (!cli) throw new Error("WireGuard installation failed."); + + console.log("WireGuard installed successfully:", cli); + return cli; +} + +// +// ------------------ WINDOWS CONNECT/DISCONNECT ------------------ +// + +// async function connectWindows(): Promise { +// const cli = await ensureWireGuardInstalled(); +// const conf = getConfigPath(); + +// // Disable GUI auto popup +// await execPromise( +// `reg add "HKCU\\Software\\WireGuard" /v SuppressTray /t REG_DWORD /d 1 /f` +// ); + +// return new Promise((resolve, reject) => { +// execFile(cli, ["/installtunnelservice", conf], (err) => { +// if (err) return reject("Failed to connect VPN: " + err); +// resolve("Windows VPN connected without GUI"); +// }); +// }); +// } + +// let wgRuntimeProcess: any = null; + +// async function connectWindows(): Promise { +// const cli = await ensureWireGuardInstalled(); +// const conf = getConfigPath(); + +// return new Promise((resolve, reject) => { +// wgRuntimeProcess = execFile(cli, ["/tunnelservice", conf], (err) => { +// if (err) return reject("VPN connection error: " + err); +// }); + +// // Wait a bit for interface to come up +// setTimeout(() => resolve("Windows VPN connected silently"), 800); +// }); +// } + + +async function connectWindows(): Promise { + const cli = await ensureWireGuardInstalled(); + const conf = getConfigPath(); + + // Disable GUI popup and silent mode (ignore errors) + await execPromise(`reg add "HKCU\\Software\\WireGuard" /v HideTrayIcon /t REG_DWORD /d 1 /f`).catch(() => {}); + await execPromise(`reg add "HKCU\\Software\\WireGuard" /v SuppressTray /t REG_DWORD /d 1 /f`).catch(() => {}); + await execPromise(`reg delete "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run" /v WireGuard /f`).catch(() => {}); + + // Start background service + await execPromise(`sc start WireGuardManager`).catch(() => {}); + + return new Promise((resolve, reject) => { + execFile(cli, ["/installtunnelservice", conf], (err) => { + if (err) return reject("VPN connection failed: " + err); + resolve("Windows VPN connected silently"); + }); + }); +} + +// function disconnectWindows(): Promise { +// const cli = findWireGuardCLI(); +// if (!cli) return Promise.reject("WireGuard CLI not found"); + +// return execPromise(`"${cli}" /uninstalltunnelservice wg0`); +// } + +async function disconnectWindows(): Promise { + const cli = findWireGuardCLI(); + if (!cli) throw new Error("WireGuard CLI not found"); + + // 1. Remove the installed tunnel service + await execPromise(`"${cli}" /uninstalltunnelservice wg0`); + + // 2. Ensure background service is still running (optional clean-up) + // Windows sometimes marks the service as "stopped" after uninstall + await execPromise(`sc stop WireGuardTunnel$wg0`).catch(() => { }); + await execPromise(`sc stop WireGuardManager`).catch(() => { }); + + return "Windows VPN disconnected"; +} + + +// +// ------------------ MACOS CONNECT/DISCONNECT ------------------ +// + +function connectMac(): Promise { + const conf = getConfigPath(); + return execPromise(`sudo wg-quick up "${conf}"`).then(() => "macOS VPN connected"); +} + +function disconnectMac(): Promise { + return execPromise(`sudo wg-quick down wg0`).then(() => "macOS VPN disconnected"); +} + +// +// ------------------ PUBLIC API ------------------ +// + +export function startVPN(): Promise { + const platform = os.platform(); + + if (platform === "win32") return connectWindows(); + if (platform === "darwin") return connectMac(); + + return Promise.reject("Unsupported OS"); +} + +export function stopVPN(): Promise { + const platform = os.platform(); + + if (platform === "win32") return disconnectWindows(); + if (platform === "darwin") return disconnectMac(); + + return Promise.reject("Unsupported OS"); +} 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..fc02701 100644 --- a/src/ui/layout/DashboardLayout.tsx +++ b/src/ui/layout/DashboardLayout.tsx @@ -260,11 +260,11 @@ const DashboardLayout = () => { ); // STEP 6: Update UI (restore window) - console.log("🪟 STEP 6: Restoring window..."); - await window.electron.toggleCloseBtn(true); - await window.electron.resizeWindow(500, 600); - toggleWindowSize.current = true; - console.log("✅ Window restored"); + console.log("🪟 STEP 6: Restoring window..."); + await window.electron.toggleCloseBtn(true); + await window.electron.resizeWindow(500, 600); + toggleWindowSize.current = true; + console.log("✅ Window restored"); // Allow React to process the state update await new Promise(resolve => setTimeout(resolve, 50)); @@ -331,7 +331,7 @@ const DashboardLayout = () => { } } - + } catch (error) { console.error("❌ Error refreshing project list:", error); @@ -727,6 +727,24 @@ const DashboardLayout = () => { return ( <>
+ + + + {/* Header */} {isTracking &&
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..273cad3 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; @@ -234,6 +241,9 @@ interface Window { success: boolean; data?: any; }>; + + connectVPN: () => Promise; + disconnectVPN: () => Promise; }; }