Compare commits

..

No commits in common. "4b146ff5e97084ba3cd666a73f5597e27e3a8c80" and "cf71247e9327e4043a71e7ead22922b1d868e170" have entirely different histories.

8 changed files with 23 additions and 185 deletions

View File

@ -1,7 +1,5 @@
[Interface]
PrivateKey = gPqf7ScXv2xtLkZz5a8qa7RNJ7uU5TQUo/rTfcprMWE=
Address = 10.0.0.172/32
PrivateKey = wHjS7rJYnAGhlvj+XYLefAzki1uRM2xz95IxRML2q24=
Address = 10.0.0.169/32
DNS = 1.1.1.1

View File

@ -204,11 +204,11 @@ ipcMain.handle("get-project-logs", async (event, { projectID, fromDate, toDate }
// 🧠 Append query params only if dates are provided
if (fromDate && toDate) {
// const params = new URLSearchParams({
// from: fromDate,
// to: toDate,
// });
url += `/${fromDate}/${toDate}`;
const params = new URLSearchParams({
from: fromDate,
to: toDate,
});
url += `?${params.toString()}`;
}
console.log("url", url)
@ -511,24 +511,15 @@ ipcMain.handle("update-payment", async (event, data: Record<string, any>) => {
paymentMethod: data.paymentMethod,
};
// paymentMethod: 'payoneeraccount',
// payoneerAccount: 'check@gmail.com'
// Add payment-specific fields
if (data.paymentMethod === "wise" && data.wiseEmail) {
userDetails.wiseEmail = data.wiseEmail;
// Clear veemAccount when switching to wise
userDetails.veemAccount = "";
userDetails.payoneerAccount = ""
} else if (data.paymentMethod === "veem" && data.veemAccount) {
userDetails.veemAccount = data.veemAccount;
// Clear wiseEmail when switching to veem
userDetails.wiseEmail = "";
userDetails.payoneerAccount = ""
} else if (data.paymentMethod === "payoneeraccount" && data.payoneerAccount) {
userDetails.payoneerAccount = data.payoneerAccount;
userDetails.veemAccount = "";
userDetails.wiseEmail = ""
}
// Build final payload

View File

@ -592,7 +592,7 @@ const DashboardLayout = () => {
if (!selectedProject?.projectId) return
console.log(selectedProject?.projectId, "SelectedProject ID")
socketRef.current = io("http://dreadingly-unfighting-pandora.ngrok-free.dev", {
socketRef.current = io("https://portal-sf.docva.com", {
transports: ["websocket"],
secure: true,
rejectUnauthorized: false,

View File

@ -15,11 +15,10 @@ const ChangePaymentModal: React.FC<ChangePaymentModalProps> = ({
}) => {
const dispatch = useDispatch();
const { profile } = useSelector((state: RootState) => state.profile);
const [method, setMethod] = useState(profile?.paymentMethod || "");
const [wiseEmail, setWiseEmail] = useState(profile?.wiseEmail || "");
const [veemAccount, setVeemAccount] = useState(profile?.veemAccount || "");
const [payoneerAccount, setPayoneerAccount] = useState(profile?.payoneerAccount || "");
const [loading, setLoading] = useState(false);
const [error, setError] = useState("");
@ -29,7 +28,6 @@ const ChangePaymentModal: React.FC<ChangePaymentModalProps> = ({
setMethod(profile?.paymentMethod || "");
setWiseEmail(profile?.wiseEmail || "");
setVeemAccount(profile?.veemAccount || "");
setPayoneerAccount(profile?.payoneerAccount || "")
setError("");
}
}, [isOpen]);
@ -48,16 +46,12 @@ const ChangePaymentModal: React.FC<ChangePaymentModalProps> = ({
return setError("Wise email is required.");
}
if (method === "wise" && !/^\S+@\S+\.\S+$/.test(wiseEmail)) {
return setError("Enter a valid Wise email.");
}
if (method === "veem" && !veemAccount.trim()) {
return setError("Veem email is required.");
}
if (method === "payoneeraccount" && !payoneerAccount.trim()) {
return setError("Payoneer email is required.");
}
if ((method === "wise" || "veem" || "payoneeraccount") && !/^\S+@\S+\.\S+$/.test(wiseEmail)) {
return setError("Enter a valid email.");
return setError("Veem account number is required.");
}
setLoading(true);
@ -73,8 +67,6 @@ const ChangePaymentModal: React.FC<ChangePaymentModalProps> = ({
updateData.wiseEmail = wiseEmail.trim();
} else if (method === "veem") {
updateData.veemAccount = veemAccount.trim();
}else if (method === "payoneeraccount") {
updateData.payoneerAccount = payoneerAccount.trim();
}
console.log("Sending to electron:", updateData);
@ -142,7 +134,6 @@ const ChangePaymentModal: React.FC<ChangePaymentModalProps> = ({
<option value="">Select method</option>
<option value="wise">Wise</option>
<option value="veem">Veem</option>
<option value="payoneeraccount">Payoneer account</option>
</select>
</div>
@ -178,22 +169,6 @@ const ChangePaymentModal: React.FC<ChangePaymentModalProps> = ({
</div>
)}
{method === "payoneeraccount" && (
<div className="form-control mb-4">
<label className="label">
<span className="text-sm font-medium">Payoneer Account Number</span>
</label>
<input
type="text"
className="input input-bordered input-sm"
placeholder="Enter Payoneer account"
value={payoneerAccount}
onChange={(e) => setPayoneerAccount(e.target.value)}
/>
</div>
)}
<div className="modal-action">
<button className="btn btn-sm btn-ghost" onClick={onClose}>
Cancel

View File

@ -107,7 +107,7 @@ const Settings: React.FC = () => {
{profile?.veemAccount && (
<div className="p-2 rounded bg-base-200/40">
<p className="text-xs text-base-content/60 flex items-center gap-1 mb-0.5">
🏦 Veem Email
🏦 Veem Account
</p>
<p className="text-xs font-medium truncate" title={profile?.veemAccount || "N/A"}>
{profile?.veemAccount || "N/A"}
@ -126,19 +126,6 @@ const Settings: React.FC = () => {
</div>
)}
{profile?.payoneerAccount && (
<div className="p-2 rounded bg-base-200/40">
<p className="text-xs text-base-content/60 flex items-center gap-1 mb-0.5 ">
🏦 Payoneer Account
</p>
<p className="text-xs font-medium overflow-auto no-scrollbar" title={profile?.payoneerAccount || "N/A"}>
{profile?.payoneerAccount || "N/A"}
</p>
</div>
)}
{/* Location - Full Width */}
<div className="p-2 rounded bg-base-200/40 col-span-2 sm:col-span-3">
<p className="text-xs text-base-content/60 flex items-center gap-1 mb-0.5">

View File

@ -1,6 +1,5 @@
import { ChevronLeft, ChevronRight } from "lucide-react";
import { DateTime } from "luxon";
import React, { useEffect, useState } from "react";
import React from "react";
interface TabActivity {
title: string;
@ -22,44 +21,13 @@ interface Props {
isOpen: boolean;
onClose: () => void;
projectName: string;
logs: ProjectSession[];
loading: boolean;
setLoading: React.Dispatch<React.SetStateAction<boolean>>;
setLogs: React.Dispatch<React.SetStateAction<ProjectSession[]>>;
projectID: string | null;
}
const ProjectLogsModal: React.FC<Props> = ({ isOpen, onClose, projectName, logs, loading, setLoading,
setLogs,
projectID, }) => {
const ProjectLogsModal: React.FC<Props> = ({ isOpen, onClose, projectName, logs, loading }) => {
if (!isOpen) return null;
const [selectedDate, setSelectedDate] = useState<Date>(new Date());
const isFirstRender = React.useRef(true);
useEffect(() => {
// ⛔ skip first render (parent already fetched)
if (isFirstRender.current) {
isFirstRender.current = false;
return;
}
// ✅ fetch only when date changes
fetchLogs(selectedDate);
}, [selectedDate]);
useEffect(() => {
if (!isOpen) {
isFirstRender.current = true;
}
}, [isOpen]);
const getTimeDifference = (from: string, to: string) => {
const start = DateTime.fromISO(from);
@ -75,81 +43,12 @@ const ProjectLogsModal: React.FC<Props> = ({ isOpen, onClose, projectName, logs,
};
};
const getDayRange = (date: Date) => {
const d = DateTime.fromJSDate(date);
return {
fromDate: d.toFormat("yyyy-MM-dd"),
toDate: d.toFormat("yyyy-MM-dd"),
};
};
const handlePrevDay = () => {
const prev = new Date(selectedDate);
prev.setDate(prev.getDate() - 1);
setSelectedDate(prev); // triggers useEffect
};
const handleNextDay = () => {
const next = new Date(selectedDate);
next.setDate(next.getDate() + 1);
setSelectedDate(next); // triggers useEffect
};
const fetchLogs = async (date: Date) => {
if (!projectID) return;
try {
setLoading(true);
// HERE
const { fromDate, toDate } = getDayRange(date);
const res = await window.electron.getProjectLogs(
projectID,
fromDate,
toDate
);
if (res?.success && Array.isArray(res.data)) {
setLogs(res.data[0]?.sessions || []);
} else {
setLogs([]);
}
} catch (err) {
console.error("Failed to fetch logs", err);
setLogs([]);
} finally {
setLoading(false);
}
};
return (
<dialog className="modal modal-open">
<div className="modal-box max-h-[97vh] max-w-4xl bg-base-100 flex flex-col">
<div className="flex justify-between items-center mb-3">
<div className="font-bold text-xs">
🗂️ Project Logs — <span className="text-primary">{projectName}</span>
</div>
<div className="flex items-center gap-2 text-xs">
<button className="btn btn-xs btn-primary p-1" onClick={handlePrevDay}><ChevronLeft className="" size={12}/></button>
<span className="font-semibold">
{DateTime.fromJSDate(selectedDate).toFormat("dd LLL yyyy")}
</span>
<button className="btn btn-xs btn-primary p-1" onClick={handleNextDay}><ChevronRight className="" size={12}/></button>
</div>
</div>
<h3 className="font-bold text-xs mb-3">
🗂️ Project Logs — <span className="text-primary">{projectName}</span>
</h3>
{loading ? (
<div className="flex justify-center items-center flex-grow">

View File

@ -35,13 +35,6 @@ const timesheetData: TimesheetEntry[] = [
{ id: 5, date: "2025-11-01", project: "AI Model Trainer", hours: 8, status: "Approved", remarks: "Normal day" },
];
interface ProjectSession {
from: string;
to: string;
totalSessionTime: string;
tabs: TabActivity[];
}
interface Project {
projectId: string;
projectName: string;
@ -62,8 +55,8 @@ const Timesheets: React.FC = () => {
console.log(" Timesheet in component:", windowLogs);
const [projectsData, setProjectsData] = useState<Project[]>([]);
const [logs, setLogs] = useState<ProjectSession[]>([]);
const [selectedProjectLogId, setSelectedProjectLogId] = useState<string | null>(null);
const [logs, setLogs] = useState([]);
// const [selectedProject, setSelectedProject] = useState<string | null>(null);
const [selectedProjectName, setSelectedProjectName] = useState<string>("");
const [modalOpen, setModalOpen] = useState(false);
const [loading, setLoading] = useState(false);
@ -129,7 +122,7 @@ const Timesheets: React.FC = () => {
try {
setModalOpen(true);
setLoading(true);
setSelectedProjectLogId(projectID);
// setSelectedProject(projectID);
setSelectedProjectName(projectName);
console.log("projectID", projectID)
@ -297,9 +290,6 @@ const Timesheets: React.FC = () => {
projectName={selectedProjectName}
logs={logs}
loading={loading}
setLoading={setLoading}
setLogs={setLogs}
projectID={selectedProjectLogId}
/>
</main>
);

View File

@ -1,5 +1,5 @@
import React, { useEffect, useState } from "react";
import { Shield, ShieldAlert, RefreshCw, Wifi, Power, X } from "lucide-react";
import { Shield, ShieldAlert, RefreshCw, Wifi, Power } from "lucide-react";
interface Props {
children: React.ReactNode;
@ -8,7 +8,7 @@ interface Props {
const VpnGuard: React.FC<Props> = ({ children }) => {
const VPN_API = "http://10.0.0.1";
const DEFAULT_API = "http://dreadingly-unfighting-pandora.ngrok-free.dev"
const DEFAULT_API = "https://portal-sf.docva.com"
const [checking, setChecking] = useState(true);
const [vpnConnected, setVpnConnected] = useState(true);
@ -21,7 +21,6 @@ const VpnGuard: React.FC<Props> = ({ children }) => {
const res = await window.electron?.checkRegion();
console.log("VpnGuard", res);
if (res.status === 200) {
const baseRes = await window.electron.updateBaseURL(DEFAULT_API);
setVpnConnected(true);
} else {
setVpnConnected(false);
@ -131,7 +130,6 @@ const VpnGuard: React.FC<Props> = ({ children }) => {
<div className="min-h-screen flex flex-col justify-center items-center bg-base-200 p-4">
<div className="max-w-md w-full">
<div className="card bg-base-100 shadow-2xl">
<X className="mt-2 mr-2 ml-auto text-slate-300 hover:text-slate-400" onClick={() => setVpnConnected(true)} />
<div className="card-body items-center text-center">
{/* Icon */}