Compare commits

..

3 Commits

6 changed files with 38 additions and 25 deletions

1
.gitignore vendored
View File

@ -30,3 +30,4 @@ nextSteps.txt
/playwright-report/
/blob-report/
/playwright/.cache/
resources

View File

@ -1,7 +1,7 @@
[Interface]
PrivateKey = OPasvZsdAdU46BJLXOLMqy4BgZq/UvKtjTRpTTVSGX8=
Address = 10.0.0.96/32
PrivateKey = QBPeUFxS5EVEZT2QRhMgSW4gUXG2WoUTlUFg/p9ZEEk=
Address = 10.0.0.97/32
DNS = 1.1.1.1
[Peer]

View File

@ -152,6 +152,14 @@ const Dashboard: React.FC = () => {
})
);
// FIX: Check if we have any projects
if (res.data.length === 0) {
// No projects available, clear selected project
dispatch(setSelectedProjectSlice(null));
// fetchProfile();
return;
}
// Check if we already have a selected project that's being tracked
const currentlySelected = selectedProject;
const currentlyTracking = isTracking;
@ -208,9 +216,13 @@ const Dashboard: React.FC = () => {
fetchProfile();
} else {
console.warn("⚠️ Unexpected response:", res);
// FIX: Also clear selected project on unexpected response
dispatch(setSelectedProjectSlice(null));
}
} catch (err) {
console.error("❌ Error fetching projects:", err);
// FIX: Clear selected project on error
dispatch(setSelectedProjectSlice(null));
} finally {
setIsRefreshing(false);
}
@ -218,6 +230,7 @@ const Dashboard: React.FC = () => {
useEffect(() => {
fetchProjects();
fetchProfile()
}, []);
const handleRefresh = async () => {
@ -285,6 +298,7 @@ const Dashboard: React.FC = () => {
<h1 className="text-md font-bold">Dashboard</h1>
</div>
{/* Tracking Section */}
{/* Tracking Section */}
<div className="w-full mb-4">
<div className="card bg-base-100 border border-base-200 shadow-sm card-sm">
<div className="card-body">
@ -305,11 +319,6 @@ const Dashboard: React.FC = () => {
<p className="text-lg font-extrabold text-success">
{formatTime(displayTime)}
</p>
{/* <p className={`text-lg font-extrabold ${isLimitReached ? 'text-error' : 'text-success'}`}>
{formatTime(displayTime)}
{isLimitReached && " ⚠️"}
</p> */}
<div>
<span className="text-base-content/70">Project:</span>{" "}
<span className="font-semibold">
@ -356,7 +365,9 @@ const Dashboard: React.FC = () => {
</>
) : (
<p className="text-sm text-base-content/60 text-center">
Select a project below to start tracking.
{projectsData.length === 0
? "No projects available."
: "Select a project below to start tracking."}
</p>
)}
</div>
@ -368,16 +379,16 @@ const Dashboard: React.FC = () => {
{/* 🔹 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">
<p className="text-md font-bold">Projects</p>
{!selectedData?.isTracking &&
<button
onClick={handleRefresh}
disabled={isRefreshing }
className="btn btn-xs btn-ghost gap-1 text-warning"
title="Refresh projects"
>
<RefreshCw className={`w-3 h-3 text-warning ${isRefreshing ? 'animate-spin' : ''}`} />
Refresh
</button>}
{!selectedData?.isTracking &&
<button
onClick={handleRefresh}
disabled={isRefreshing}
className="btn btn-xs btn-ghost gap-1 text-warning"
title="Refresh projects"
>
<RefreshCw className={`w-3 h-3 text-warning ${isRefreshing ? 'animate-spin' : ''}`} />
Refresh
</button>}
</div>
{/* 🔹 Table or Empty State or Skeleton */}

View File

@ -9,6 +9,8 @@ const PaysSlips = () => {
const [error, setError] = useState<string | null>(null);
const [rows, setRows] = useState<any[]>([]);
useEffect(() => {
if (!projectSummary?.vaId) return;

View File

@ -64,13 +64,11 @@ const Timesheets: React.FC = () => {
const [lastRefreshed, setLastRefreshed] = useState<Date | null>(null);
const [initialLoading, setInitialLoading] = useState(true);
const [tick, setTick] = useState(0);
const { selectedProject } =
useSelector((state: any) => state.projects);
const { selectedProject } = useSelector((state: any) => state.projects);
const selectedProjectId = selectedProject?.projectId ?? null;
const activeProjects = projectsData.filter(p => p.status === "ACTIVE");
console.log("selectedProject", selectedProject.projectId)
useEffect(() => {
const interval = setInterval(() => {
@ -225,13 +223,13 @@ const Timesheets: React.FC = () => {
<td>{formatWorkTime(entry.totalWorkTime)}</td>
<td>
<button
disabled={(formatWorkTime(entry.totalWorkTime) === "0h 0m" || (isTracking && entry.projectId === selectedProject.projectId))}
disabled={(formatWorkTime(entry.totalWorkTime) === "0h 0m" || (isTracking && entry.projectId === selectedProjectId))}
onClick={() =>
handleViewLogs(entry.projectId, entry.projectName)
}
className="btn btn-ghost btn-xs"
>
{(formatWorkTime(entry.totalWorkTime) === "0h 0m" || (isTracking && entry.projectId === selectedProject.projectId)) ? (
{(formatWorkTime(entry.totalWorkTime) === "0h 0m" || (isTracking && entry.projectId === selectedProjectId)) ? (
<EyeOff size={16} />
) : (
<Eye size={16} />

View File

@ -7,7 +7,7 @@ interface Props {
const VpnGuard: React.FC<Props> = ({ children }) => {
const VPN_API = "http://10.0.0.1:3000";
const VPN_API = "http://10.0.0.1";
const DEFAULT_API = "https://portal-sf.docva.com"
const [checking, setChecking] = useState(true);
@ -66,6 +66,7 @@ const VpnGuard: React.FC<Props> = ({ children }) => {
console.log("vpnRes", vpnRes);
if (vpnRes) {
// Run VPN check again AFTER URL changes
await checkVPN();
} else {