timesheet session time update

This commit is contained in:
JaiPandey425 2025-12-10 16:08:04 +05:30
parent d4d6ff5344
commit 6c223f01c9
10 changed files with 101 additions and 47 deletions

View File

@ -1,10 +1,10 @@
[Interface] [Interface]
PrivateKey = CEzCd25ufxbOjvRhMqLfkcdP/OziG5i6OkXCfMZrI1I= PrivateKey = CEzCd25ufxbOjvRhMqLfkcdP/OziG5i6OkXCfMZrI1I=
Address = 10.0.0.2/24 Address = 10.0.0.2/32
DNS = 1.1.1.1 DNS = 1.1.1.1
[Peer] [Peer]
PublicKey = jG+7H5thEiNjxY1XqI1R9oyMAMTIegLwE3tZkS1Rj2U= PublicKey = jG+7H5thEiNjxY1XqI1R9oyMAMTIegLwE3tZkS1Rj2U=
Endpoint = 35.95.209.161:51820 Endpoint = 35.95.209.161:51820
AllowedIPs = 10.0.0.0/24 AllowedIPs = 10.0.0.0/32 0.0.0.0/0
PersistentKeepalive = 25 PersistentKeepalive = 25

View File

@ -4,7 +4,6 @@ import { getToken } from '../utils/tokenHelper.js';
import chalk from "chalk"; import chalk from "chalk";
import { sendTokenExpired } from '../ipc/ipcHandler.js'; import { sendTokenExpired } from '../ipc/ipcHandler.js';
import https from 'https'; import https from 'https';
import { getBaseURL } from './baseURL.js';
const TOKEN_KEY = 'ACCESS_TOKEN'; const TOKEN_KEY = 'ACCESS_TOKEN';
@ -13,7 +12,7 @@ const httpsAgent = new https.Agent({
}); });
const api: AxiosInstance = axios.create({ const api: AxiosInstance = axios.create({
baseURL: await getBaseURL(), // 🔁 your backend baseURL: 'https://quality-friendly-goldfish.ngrok-free.app/api/v1/va', // 🔁 your backend
httpsAgent httpsAgent
// timeout: 10000, // timeout: 10000,
}); });
@ -30,7 +29,7 @@ api.interceptors.request.use(
if (token) { if (token) {
config.headers['token'] = token; config.headers['token'] = token;
config.headers['ngrok-skip-browser-warning'] = 'true'; config.headers['ngrok-skip-browser-warning'] = 'true';
} }
return config; return config;
@ -55,7 +54,7 @@ api.interceptors.response.use(
(response: AxiosResponse) => response.data, (response: AxiosResponse) => response.data,
async (error) => { async (error) => {
console.log(error, "Line 46") console.log(error , "Line 46")
const status = error?.response?.status; const status = error?.response?.status;
console.error('[Axios Error]', status); console.error('[Axios Error]', status);

View File

@ -1,4 +1,4 @@
let CURRENT_BASE_URL = "https://portal-sf.docva.com"; let CURRENT_BASE_URL = "https://quality-friendly-goldfish.ngrok-free.app/api/v1/va";
export const getBaseURL = () => CURRENT_BASE_URL; export const getBaseURL = () => CURRENT_BASE_URL;
export const setBaseURL = (url: string) => { export const setBaseURL = (url: string) => {

View File

@ -71,7 +71,7 @@ export const handleLogin = async (credentials: { email: string, password: string
try { try {
console.log("➡️ Calling API..."); console.log("➡️ Calling API...");
const response = await axios.post( const response = await axios.post(
`${baseURL}/api/v1/va/login`, `https://quality-friendly-goldfish.ngrok-free.app/api/v1/va/login`,
{ userDetails: { ...credentials } }, { userDetails: { ...credentials } },
{ headers: { "ngrok-skip-browser-warning": 69420 }, httpsAgent } { headers: { "ngrok-skip-browser-warning": 69420 }, httpsAgent }
); );

View File

@ -123,9 +123,9 @@ const App: React.FC = () => {
path="/login" path="/login"
element={ element={
<PublicRoute user={user}> <PublicRoute user={user}>
<VpnGuard> {/* <VpnGuard> */}
<Login /> <Login />
</VpnGuard> {/* </VpnGuard> */}
</PublicRoute> </PublicRoute>
} }
/> />

View File

@ -130,14 +130,14 @@ const Dashboard: React.FC = () => {
}; };
const fetchProjects = async () => { const fetchProjects = async () => {
setIsRefreshing(true) setIsRefreshing(true);
try { try {
console.log("📡 Fetching projects...Dashboard"); console.log("📡 Fetching projects...Dashboard");
const res = await window.electron.getProjectList(); const res = await window.electron.getProjectList();
console.log(" Project list response in dashboard: ", res.data?.[0]); console.log("Project list response in dashboard: ", res.data?.[0]);
if (res?.success && Array.isArray(res.data)) { if (res?.success && Array.isArray(res.data)) {
// Store data redux store // Store data in redux store
dispatch(setProjectsDataSlice(res.data)); dispatch(setProjectsDataSlice(res.data));
dispatch( dispatch(
@ -152,35 +152,67 @@ const Dashboard: React.FC = () => {
}) })
); );
// Auto-select first project // Check if we already have a selected project that's being tracked
if (res.data.length > 0) { const currentlySelected = selectedProject;
const firstProject = res.data[0]; const currentlyTracking = isTracking;
// Find if the currently selected project still exists in the fresh data
let projectToSelect = null;
console.log("Line 158", firstProject) if (currentlySelected && currentlyTracking) {
// Select the project // We're tracking a project - try to preserve it
dispatch(setSelectedProjectSlice(firstProject)); const existingProject = res.data.find(
(p: Project) => p.projectId === currentlySelected.projectId
const initialElapsed = parseTimeStringToSeconds(firstProject.totalWorkTime);
// Initialize tracking data for first project
dispatch(
initializeProjectTracking({
projectId: firstProject.projectId,
initialTime: initialElapsed,
})
); );
if (existingProject) {
projectToSelect = existingProject;
}
} }
fetchProfile() // If no project is selected OR tracked project no longer exists, auto-select first project
if (!projectToSelect && res.data.length > 0) {
projectToSelect = res.data[0];
}
if (projectToSelect) {
console.log("Line 158", projectToSelect);
// Select the project
dispatch(setSelectedProjectSlice(projectToSelect));
// Only initialize tracking if it doesn't exist already
const trackingExists = projectTrackingMap[projectToSelect.projectId];
if (!trackingExists) {
const initialElapsed = parseTimeStringToSeconds(projectToSelect.totalWorkTime);
dispatch(
initializeProjectTracking({
projectId: projectToSelect.projectId,
initialTime: initialElapsed,
})
);
} else {
// Update project data but preserve tracking state
dispatch(
updateProjectTracking({
projectId: projectToSelect.projectId,
data: {
...trackingExists,
// Update elapsed time from server if needed
elapsedTime: parseTimeStringToSeconds(projectToSelect.totalWorkTime),
}
})
);
}
}
fetchProfile();
} else { } else {
console.warn("⚠️ Unexpected response:", res); console.warn("⚠️ Unexpected response:", res);
} }
} catch (err) { } catch (err) {
console.error("❌ Error fetching projects:", err); console.error("❌ Error fetching projects:", err);
} finally { } finally {
setIsRefreshing(false) setIsRefreshing(false);
} }
}; };
@ -247,7 +279,7 @@ const Dashboard: React.FC = () => {
}; };
return ( return (
<main className="flex-1 overflow-hidden no-scrollbar max-h-[85vh]"> <main className="flex-1 overflow-hidden no-scrollbar ">
{/* Header */} {/* Header */}
<div className="mb-2"> <div className="mb-2">
<h1 className="text-md font-bold">Dashboard</h1> <h1 className="text-md font-bold">Dashboard</h1>
@ -336,15 +368,16 @@ const Dashboard: React.FC = () => {
{/* 🔹 Sticky Header */} {/* 🔹 Sticky Header */}
<div className="sticky top-0 z-20 bg-base-100 border-b border-base-content/10 p-3 flex justify-between items-center"> <div className="sticky top-0 z-20 bg-base-100 border-b border-base-content/10 p-3 flex justify-between items-center">
<p className="text-md font-bold">Projects</p> <p className="text-md font-bold">Projects</p>
{!selectedData?.isTracking &&
<button <button
onClick={handleRefresh} onClick={handleRefresh}
disabled={isRefreshing} disabled={isRefreshing }
className="btn btn-xs btn-ghost gap-1 text-warning" className="btn btn-xs btn-ghost gap-1 text-warning"
title="Refresh projects" title="Refresh projects"
> >
<RefreshCw className={`w-3 h-3 text-warning ${isRefreshing ? 'animate-spin' : ''}`} /> <RefreshCw className={`w-3 h-3 text-warning ${isRefreshing ? 'animate-spin' : ''}`} />
Refresh Refresh
</button> </button>}
</div> </div>
{/* 🔹 Table or Empty State or Skeleton */} {/* 🔹 Table or Empty State or Skeleton */}
@ -368,7 +401,7 @@ const Dashboard: React.FC = () => {
minHeight: "auto", minHeight: "auto",
}} }}
> >
<table className="table table-zebra w-full text-[12px]"> <table className="table table-zebra w-full text-[12px] ">
<thead className="sticky top-0 bg-base-200 z-10"> <thead className="sticky top-0 bg-base-200 z-10">
<tr> <tr>
<th>#</th> <th>#</th>

View File

@ -23,7 +23,7 @@ const HelpSupport: React.FC = () => {
<Mail size={18} className="text-primary" /> <Mail size={18} className="text-primary" />
<div> <div>
<p className="text-xs text-base-content/70">Email</p> <p className="text-xs text-base-content/70">Email</p>
<p className="font-medium text-sm">support@vatechcorp.com</p> <p className="font-medium text-sm">support@docva.com</p>
</div> </div>
</div> </div>
@ -31,7 +31,7 @@ const HelpSupport: React.FC = () => {
<Phone size={18} className="text-primary" /> <Phone size={18} className="text-primary" />
<div> <div>
<p className="text-xs text-base-content/70">Phone</p> <p className="text-xs text-base-content/70">Phone</p>
<p className="font-medium text-sm">+91 98765 43210</p> <p className="font-medium text-sm">+XXXX XXXXX</p>
</div> </div>
</div> </div>

View File

@ -84,7 +84,7 @@ const PaysSlips = () => {
<th>Date</th> <th>Date</th>
<th>Total Hours</th> <th>Total Hours</th>
<th>Total Pay</th> <th>Total Pay</th>
<th>Action</th> {/* <th>Action</th> */}
</tr> </tr>
</thead> </thead>
@ -111,13 +111,13 @@ const PaysSlips = () => {
<td>${row.totalPayAmount.toFixed(2)}</td> <td>${row.totalPayAmount.toFixed(2)}</td>
<td> {/* <td>
<button <button
className="btn btn-xs btn-primary" className="btn btn-xs btn-primary"
> >
View View
</button> </button>
</td> </td> */}
</tr> </tr>
))} ))}
</tbody> </tbody>

View File

@ -1,6 +1,7 @@
// src/ui/pages/Settings/ChangePasswordModal.tsx // src/ui/pages/Settings/ChangePasswordModal.tsx
import React, { useState } from "react"; import React, { useState } from "react";
import { Eye, EyeOff } from "lucide-react"; import { Eye, EyeOff } from "lucide-react";
import { useToast } from "../../helper/ToastContext";
interface ChangePasswordModalProps { interface ChangePasswordModalProps {
isOpen: boolean; isOpen: boolean;
@ -19,12 +20,13 @@ const ChangePasswordModal: React.FC<ChangePasswordModalProps> = ({
const [showOld, setShowOld] = useState(false); const [showOld, setShowOld] = useState(false);
const [showNew, setShowNew] = useState(false); const [showNew, setShowNew] = useState(false);
const [showConfirm, setShowConfirm] = useState(false); const [showConfirm, setShowConfirm] = useState(false);
const { showToast } = useToast();
const handleSubmit = async (e: React.FormEvent) => { const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault();
if (newPassword !== confirmPassword) { if (newPassword !== confirmPassword) {
alert("New passwords do not match!"); showToast("New passwords do not match!" , "error");
return; return;
} }
@ -40,17 +42,17 @@ const ChangePasswordModal: React.FC<ChangePasswordModalProps> = ({
const response = await window.electron.changePassword(payload); const response = await window.electron.changePassword(payload);
if (response.success) { if (response.success) {
alert("Password changed successfully!"); showToast("Password changed successfully!" , "success");
setOldPassword("") setOldPassword("")
setNewPassword("") setNewPassword("")
setConfirmPassword("") setConfirmPassword("")
onClose(); onClose();
} else { } else {
alert(response.message || "Failed to change password."); showToast((response.message || "Failed to change password.") , "error");
} }
} catch (error) { } catch (error) {
console.error("Password change error:", error); console.error("Password change error:", error);
alert("Error changing password."); showToast("Error changing password." , "error");
} finally { } finally {
setLoading(false); setLoading(false);
} }

View File

@ -1,3 +1,4 @@
import { DateTime } from "luxon";
import React from "react"; import React from "react";
interface TabActivity { interface TabActivity {
@ -27,6 +28,21 @@ interface Props {
const ProjectLogsModal: React.FC<Props> = ({ isOpen, onClose, projectName, logs, loading }) => { const ProjectLogsModal: React.FC<Props> = ({ isOpen, onClose, projectName, logs, loading }) => {
if (!isOpen) return null; if (!isOpen) return null;
const getTimeDifference = (from: string, to: string) => {
const start = DateTime.fromISO(from);
const end = DateTime.fromISO(to);
const diff = end.diff(start, ["hours", "minutes", "seconds"]);
return {
hours: diff.hours,
minutes: diff.minutes,
seconds: Math.floor(diff.seconds),
formatted: `${diff.hours}h ${diff.minutes}m ${Math.floor(diff.seconds)}s`
};
};
return ( return (
<dialog className="modal modal-open"> <dialog className="modal modal-open">
<div className="modal-box max-h-[97vh] max-w-4xl bg-base-100 flex flex-col"> <div className="modal-box max-h-[97vh] max-w-4xl bg-base-100 flex flex-col">
@ -46,7 +62,11 @@ const ProjectLogsModal: React.FC<Props> = ({ isOpen, onClose, projectName, logs,
<div key={idx} className="border border-base-300 overflow-hidden"> <div key={idx} className="border border-base-300 overflow-hidden">
<div className="bg-base-200 p-2 font-semibold flex justify-between items-center"> <div className="bg-base-200 p-2 font-semibold flex justify-between items-center">
<span className="text-[12px]"> <span className="text-[12px]">
🕒 Session {idx + 1} — {session.totalSessionTime} 🕒 Session {idx + 1} — {getTimeDifference(
new Date(session.from).toISOString(),
new Date(session.to).toISOString()
).formatted}
</span> </span>
<span className="text-xs text-gray-500"> <span className="text-xs text-gray-500">
{new Date(session.from).toLocaleTimeString()} -{" "} {new Date(session.from).toLocaleTimeString()} -{" "}
@ -69,10 +89,10 @@ const ProjectLogsModal: React.FC<Props> = ({ isOpen, onClose, projectName, logs,
<tr key={tabIdx} className="text-[10px]"> <tr key={tabIdx} className="text-[10px]">
<td>{tabIdx + 1}</td> <td>{tabIdx + 1}</td>
<td className="truncate max-w-[150px]" title={tab.title}> <td className="truncate max-w-[150px]" title={tab.title}>
{tab.title} <br/> {tab.title} <br />
<span className="text-slate-500">{tab.appName}</span> <span className="text-slate-500">{tab.appName}</span>
</td> </td>
<td><span className="text-green-500">{new Date(tab.from).toLocaleTimeString()}</span> <br/><span className="text-red-600">{new Date(tab.to).toLocaleTimeString()}</span></td> <td><span className="text-green-500">{new Date(tab.from).toLocaleTimeString()}</span> <br /><span className="text-red-600">{new Date(tab.to).toLocaleTimeString()}</span></td>
<td>{tab.durationFormatted}</td> <td>{tab.durationFormatted}</td>
</tr> </tr>
))} ))}