va-tracker/src/ui/pages/Dashboard.tsx

507 lines
14 KiB
TypeScript

import React, { useState, useEffect, useRef } from "react";
import { Clock, TrendingUp, Play, Square, ProjectorIcon, Pause } from "lucide-react";
const projectsData = [
{
id: 1,
projectName: "Employee Tracker",
clientName: "TechCorp Pvt Ltd",
status: "Active",
action: "Start Tracking",
},
{
id: 2,
projectName: "Sales Dashboard",
clientName: "BlueEdge Analytics",
status: "Pending",
action: "Start Tracking",
},
{
id: 3,
projectName: "Inventory System",
clientName: "LogiFlow Inc",
status: "Completed",
action: "View Report",
},
{
id: 4,
projectName: "CRM Upgrade",
clientName: "NextWave Solutions",
status: "On Hold",
action: "Resume",
},
{
id: 5,
projectName: "Mobile App Redesign",
clientName: "PixelFusion",
status: "Active",
action: "Start Tracking",
},
{
id: 6,
projectName: "E-Commerce Backend",
clientName: "ShopEasy",
status: "Pending",
action: "Start Tracking",
},
{
id: 7,
projectName: "Finance Tracker",
clientName: "MoneyWise",
status: "Active",
action: "Start Tracking",
},
{
id: 8,
projectName: "Helpdesk Portal",
clientName: "ServePro",
status: "Completed",
action: "View Report",
},
{
id: 9,
projectName: "Marketing Automation",
clientName: "AdBoost",
status: "Active",
action: "Start Tracking",
},
{
id: 10,
projectName: "Data Pipeline",
clientName: "CloudWave",
status: "On Hold",
action: "Resume",
},
{
id: 11,
projectName: "Support Chatbot",
clientName: "SmartServe",
status: "Active",
action: "Start Tracking",
},
{
id: 12,
projectName: "Payroll System",
clientName: "PeopleFirst HR",
status: "Pending",
action: "Start Tracking",
},
{
id: 13,
projectName: "IoT Dashboard",
clientName: "SensorGrid",
status: "Completed",
action: "View Report",
},
{
id: 14,
projectName: "Security Portal",
clientName: "SafeNet",
status: "Active",
action: "Start Tracking",
},
{
id: 15,
projectName: "Social Media Tool",
clientName: "Buzzify",
status: "Pending",
action: "Start Tracking",
},
{
id: 16,
projectName: "AI Model Trainer",
clientName: "NeuroTech",
status: "On Hold",
action: "Resume",
},
{
id: 17,
projectName: "Project Tracker",
clientName: "TaskFlow",
status: "Active",
action: "Start Tracking",
},
{
id: 18,
projectName: "Booking Portal",
clientName: "GoTravel",
status: "Completed",
action: "View Report",
},
{
id: 19,
projectName: "Time Logger",
clientName: "Clockify Pro",
status: "Active",
action: "Start Tracking",
},
{
id: 20,
projectName: "Analytics Engine",
clientName: "DataStream",
status: "Pending",
action: "Start Tracking",
},
];
interface WidgetProps {
title: string;
value: string | number;
subtitle?: string;
icon?: React.ReactNode;
trend?: number;
color?: "primary" | "secondary" | "accent" | "success" | "warning" | "error" | "info";
}
interface Project {
id: number;
projectName: string;
clientName: string;
}
// Widget Component
const Widget: React.FC<WidgetProps> = ({
title,
value,
subtitle,
icon,
trend,
color = "primary",
}) => {
return (
<div className="card bg-base-100 shadow-sm hover:shadow-lg transition-shadow">
<div className="card-body">
<div className="flex items-start justify-between">
<div className="flex-1">
<h3 className=" font-medium text-base-content/60">{title}</h3>
<p className="text-sm font-bold mt-2">{value}</p>
</div>
<div className={`p-3 bg-${color}/10 rounded-lg`}>
<div className={`text-${color}`}>{icon}</div>
</div>
</div>
</div>
</div>
);
};
// Main Dashboard Component
const Dashboard: React.FC = () => {
const [selectedProject, setSelectedProject] = useState<Project | null>(null);
const [isTracking, setIsTracking] = useState(false);
const [isPaused, setIsPaused] = useState(false);
const [elapsedTime, setElapsedTime] = useState(0); // in seconds
const [startTime, setStartTime] = useState<Date | null>(null);
const [endTime, setEndTime] = useState<Date | null>(null);
const timerRef = useRef<ReturnType<typeof setInterval> | null>(null);
// Load cached project or auto-select single project
useEffect(() => {
const cachedProject = localStorage.getItem("activeProject");
if (cachedProject) {
const parsed = JSON.parse(cachedProject) as Project;
setSelectedProject(parsed);
} else if (projectsData.length === 1) {
setSelectedProject(projectsData[0]);
}
}, []);
// Format elapsed time (hh:mm:ss)
const formatTime = (seconds: number) => {
const h = Math.floor(seconds / 3600);
const m = Math.floor((seconds % 3600) / 60);
const s = seconds % 60;
return `${h}h ${m}m ${s}s`;
};
// Start tracking manually
const handleStartTracking = () => {
if (!selectedProject) return;
setIsTracking(true);
setIsPaused(false);
setElapsedTime(0);
setStartTime(new Date());
setEndTime(null);
// Cache selected project
localStorage.setItem("activeProject", JSON.stringify(selectedProject));
};
// ⏱ Timer logic
useEffect(() => {
if (isTracking && !isPaused) {
timerRef.current = setInterval(() => {
setElapsedTime((prev) => prev + 1);
}, 1000);
} else if (timerRef.current) {
clearInterval(timerRef.current);
}
return () => {
if (timerRef.current) clearInterval(timerRef.current);
};
}, [isTracking, isPaused]);
// ⏸ Pause/Resume button handler
const handlePauseResume = () => {
setIsPaused((prev) => !prev);
};
// Stop tracking
const handleStopTracking = () => {
if (!selectedProject) return;
setIsTracking(false);
setIsPaused(false);
setEndTime(new Date());
if (timerRef.current) clearInterval(timerRef.current);
// Keep project in view, just stop tracking.
// Remove tracking state but keep the selected project cached.
const summary = {
project: selectedProject.projectName,
totalTime: formatTime(elapsedTime),
start: startTime?.toLocaleTimeString(),
end: new Date().toLocaleTimeString(),
};
console.log("✅ Final Tracking Summary:", summary);
// Save the stopped state for reference
localStorage.setItem(
"activeProject",
JSON.stringify({
...selectedProject,
lastSession: summary,
})
);
// Reset timer values but keep selected project
setElapsedTime(0);
};
// 🧾 When user picks a project manually
const handleProjectSelect = (id: number) => {
if (isTracking) return;
const project = projectsData.find((item) => item.id === id);
if (project) {
setSelectedProject(project);
localStorage.setItem("activeProject", JSON.stringify(project));
}
};
return (
<main className="flex-1 overflow-y-auto md:px-0.5 md:py-0 no-scrollbar">
{/* Welcome Section */}
<div className="mb-4">
<h1 className="text-lg font-bold">Dashboard</h1>
<button
onClick={async () => {
await window.electron.uploadScreenshot();
}}
>
ScreenShot Test
</button>
</div>
{/* Widgets Grid */}
<div className="mb-4">
<div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-3 gap-4 ">
<Widget
title="Logged Hours Today"
value="6h 24m"
subtitle="Today's total time"
icon={<Clock size={24} />}
trend={12}
color="primary"
/>
<Widget
title="Logged Hours This Week"
value="4h 48m"
subtitle="75% productivity"
icon={<TrendingUp size={24} />}
trend={8}
color="success"
/>
<Widget
title="Active Project"
value={selectedProject ? selectedProject.projectName : "—"}
subtitle="Until day ends"
icon={<ProjectorIcon size={24} />}
color="warning"
/>
</div>
</div>
{/* Tracking Card */}
<div className="w-full mb-4">
<div className="text-md mb-2 font-bold flex justify-between items-center">
<span>Tracking</span>
{isTracking && (
<button
className="btn btn-xs btn-error rounded-full"
onClick={handleStopTracking}
>
<Square className="w-3 h-3" />
Stop Tracking
</button>
)}
</div>
<div className="card bg-base-100 shadow-sm border border-base-200 transition-all duration-300 w-full">
<div className="card-body">
{selectedProject ? (
<>
<div className="flex flex-col sm:flex-row items-center justify-between gap-4">
<div className="text-center sm:text-left">
<p className="text-sm font-extrabold text-success tracking-tight">
{formatTime(elapsedTime)}
</p>
<p className="text-xs text-base-content/70 mt-1">
{isTracking ? "Current Session" : "Ready to Start"}
</p>
</div>
{!isTracking ? (
<button
onClick={handleStartTracking}
className="btn btn-sm btn-success rounded-xl hover:scale-105 transition-transform duration-200 w-full sm:w-auto"
>
<Play className="w-3 h-3" />
Start Tracking
</button>
) : (
<button
onClick={handlePauseResume}
className={`btn btn-sm rounded-xl transition-transform duration-200 w-full sm:w-auto ${isPaused ? "btn-primary" : "btn-warning"
}`}
>
{isPaused ? (
<>
<Play className="w-3 h-3" />
Resume
</>
) : (
<>
<Pause className="w-3 h-3" />
Pause
</>
)}
</button>
)}
</div>
<div className="divider my-2"></div>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-3 text-sm">
{/* Project Name */}
<div className="flex flex-col sm:flex-row sm:items-center sm:gap-2">
<span className="text-base-content/70">Project:</span>
<span className="font-semibold text-base-content truncate">
{selectedProject.projectName}
</span>
</div>
{/* Client Name */}
<div className="flex flex-col sm:flex-row sm:items-center sm:gap-2">
<span className="text-base-content/70">Client:</span>
<span className="truncate">{selectedProject.clientName}</span>
</div>
{/* Status */}
<div className="flex flex-col sm:flex-row sm:items-center sm:gap-2">
<span className="text-base-content/70">Status:</span>
<span
className={`badge ${isPaused
? "badge-warning"
: isTracking
? "badge-success"
: "badge-neutral"
}`}
>
{isPaused ? "Paused" : isTracking ? "Active" : "Ready"}
</span>
</div>
{/* Start Time */}
<div className="flex flex-col sm:flex-row sm:items-center sm:gap-2">
<span className="text-base-content/70">Start Time:</span>
<span>{startTime?.toLocaleTimeString() || "—"}</span>
</div>
</div>
</>
) : (
<p className="text-sm text-base-content/60 text-center">
Select a project to start tracking.
</p>
)}
</div>
</div>
</div>
{/* Projects Table */}
<div>
<p className="text-md mb-2 font-bold">Projects</p>
<div
className="overflow-y-auto no-scrollbar overflow-x-auto rounded-box border border-base-content/10 bg-base-100 shadow-md"
style={{ minHeight: "100px", maxHeight: "30vh" }}
>
<table className="table table-zebra w-full text-sm">
<thead className="sticky top-0 bg-base-200 z-10">
<tr>
<th>#</th>
<th>Project Name</th>
<th>Client Name</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{projectsData.map((project) => (
<tr key={project.id} className="align-middle">
<th>{project.id}</th>
<td>{project.projectName}</td>
<td>{project.clientName}</td>
<td>
<div
className={`badge badge-sm whitespace-nowrap ${project.status === "Active"
? "badge-success"
: project.status === "Pending"
? "badge-warning"
: project.status === "Completed"
? "badge-info"
: "badge-neutral"
}`}
>
{project.status}
</div>
</td>
<td>
<div
className={`badge badge-sm whitespace-nowrap ${project.action === "View Report"
? "badge-info"
: project.action === "Resume"
? "badge-secondary"
: "badge-primary"
}`}
>
Select
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</main>
);
};
export default Dashboard;