i'm a beginner in react. i have a simple time tracking app. it has a simple interface.
the problem is i have list of all employees which i am getting from the backend api in asp.net core. when i click on the employee there is screen with the checkin and checkout button where i can checkin and checkout my time. time is shown in the end. but i want persist it there
i am handling condition. when i checkin for first time the checkout button will enable I can do the checkout. by the time when i press checkout, the checkout time is noted and checkout button is disabled and checkin is enabled. when i checkin and i move to checkout i refresh or go to back button it will not persist in my state. i want to make that happen.
how to persist my button state with local storage? i have tried but nothing is working with local storage. it cannot stored my state. i want to persist my state like when i checkin or checkout or go backbutton and then came it will show me or select the latest recent button.
how put the work in the employeetimetracking.js?
import React, { useEffect, useState } from 'react';
import { Link, useParams } from 'react-router-dom';
import { fetchEmployeeById,checkIn,checkOut,fetchEmployeeStatus} from '../api/Employeeapi'; // Import your fetchEmployeeById method
function EmployeeTimeTracking() {
const { employeeId } = useParams(); // Get the employee ID from the URL
const [employeeName, setEmployeeName] = useState("Employee"); // Default name
const [status, setStatus] = useState('checkOut');
const [checkInTime, setCheckInTime] = useState(null);
const [checkOutTime, setCheckOutTime] = useState(null);
const [timeTrackingId, setTimeTrackingId] = useState(null); // To store the check-in ID
// set the message for to display success and error msg
//const[message,setmessage]=useState("");
useEffect(() => {
// Fetch employee data by ID
const fetchEmployee = async () => {
try {
const employee = await fetchEmployeeById(employeeId); // Call your fetchEmployeeById method
setEmployeeName(employee.name); // Assuming `name` is the field in the response
} catch (error) {
console.error("Error fetching employee:", error);
}
};
const fetchStatus = async () => {
try {
const data = await fetchEmployeeStatus(employeeId); // Call the fetchEmployeeStatus function
setStatus(data.status);
setCheckInTime(data.checkInTime ? new Date(data.checkInTime).toLocaleTimeString("en-US", { timeZone: "America/New_York" }) : null);
setCheckOutTime(data.checkOutTime ? new Date(data.checkOutTime).toLocaleTimeString("en-US", { timeZone: "America/New_York" }) : null);
} catch (error) {
console.error("Error fetching employee status:", error);
}
};
fetchStatus();
fetchEmployee();
}, [employeeId]);
// Check-in function
const handleCheckIn = async () => {
try {
const data = await checkIn(employeeId);
setTimeTrackingId(data.id); // Store the ID of the check-in record
setCheckInTime(new Date().toLocaleTimeString("en-US", { timeZone: "America/New_York" }));
setStatus('checkedIn');
} catch (error) {
console.error("Check-in failed:", error);
}
};
const handleCheckOut = async () => {
try {
await checkOut(timeTrackingId);
setCheckOutTime(new Date().toLocaleTimeString("en-US", { timeZone: "America/New_York" }));
setStatus('checkedOut');
} catch (error) {
console.error("Check-out failed:", error);
}
};
return (
<div className="p-3 max-w-md mx-auto">
{/* Back Button */}
<Link to="/" className="text-blue-500 absolute left-2 top-0 hover:underline">
← Back to Employee List
</Link>
<div className="p-4 max-w-md mx-auto">
<h2 className="text-2xl font-bold mb-4 text-center">
Time Tracking for {employeeName} {/* Display employee name */}
</h2>
<div className="flex flex-col space-y-4 items-center">
<button
onClick={handleCheckIn}
disabled={status === 'checkedIn'}
className={`px-4 py-2 w-full sm:w-1/2 rounded ${status === 'checkedIn' ? 'bg-gray-300 cursor-not-allowed' : 'bg-green-500 hover:bg-green-600'} text-white font-semibold transition`}
>
Check In
</button>
<button
onClick={handleCheckOut}
disabled={status !== 'checkedIn'}
className={`px-4 py-2 w-full sm:w-1/2 rounded ${status !== 'checkedIn' ? 'bg-gray-300 cursor-not-allowed' : 'bg-red-500 hover:bg-red-600'} text-white font-semibold transition`}
>
Check Out
</button>
</div>
{/* {message && (
<div className="text-center mb-4 p-2 rounded bg-green-100 text-green-700">
{message}
</div>
)} */}
<div className="mt-6 text-center">
<p className="text-lg">Check-In Time: {checkInTime || 'Not Checked In'}</p>
<p className="text-lg">Check-Out Time: {checkOutTime || 'Not Checked Out'}</p>
</div>
</div>
</div>
);
}
export default EmployeeTimeTracking;
EmployeeTimeTracking.js
Employeeapi.js
import axios from 'axios';
const API_URL = 'http://localhost:5049/api/timetraking';
// export const fetchEmployeeTimeTracking = async (employeeId) => {
// const response = await axios.get(`${API_URL}/timetrackingid/${employeeId}`);
// return response.data;
// };
// Fetch all employees
export const fetchEmployees = async () => {
try {
const response = await axios.get(`${API_URL}/employees`); // Make sure this matches your backend
return response.data; // Return the data directly
} catch (error) {
console.error("Error fetching employees:", error);
console.log(`API URL for employees: ${API_URL}/employees`);
throw error; // Rethrow the error so it can be caught in the component
}
};
export const fetchEmployeeById = async (id) => {
try {
const response = await axios.get(`${API_URL}/employees/${id}`); // Adjust the URL as needed
// console.log("Employee ID from URL:", id); // Log the ID
return response.data;
} catch (error) {
console.error(`Error fetching employee with ID ${id}:`, error);
throw error; // Rethrow the error for handling in the component
}
};
export const checkIn = async (employeeId) => {
try {
const response = await axios.post(`${API_URL}/checkin`, {
employeeId: employeeId, // Pass any other required data here
});
return response.data;
} catch (error) {
console.error("Error during check-in:", error);
throw error;
}
};
// Check-out an employee
export const checkOut = async (timeTrackingId) => {
try {
const response = await axios.post(`${API_URL}/checkout/${timeTrackingId}`);
return response.data;
} catch (error) {
console.error("Error during check-out:", error);
throw error;
}
};
// Fetch employee status by employee ID
export const fetchEmployeeStatus = async (employeeId) => {
try {
const response = await axios.get(`${API_URL}/status/${employeeId}`); // Adjusting to match your endpoint
return response.data; // Return the data directly
} catch (error) {
console.error(`Error fetching status for employee ${employeeId}:`, error);
throw error; // Rethrow the error for handling in the component
}
};
Employeelist.js
import React, {useEffect, useState} from 'react';
import { Link } from 'react-router-dom';
import { fetchEmployees } from '../api/Employeeapi';
// const employees = [
// { id: 1, name: 'Alice' },
// { id: 2, name: 'Bob' },
// { id: 3, name: 'Charlie' },
// { id: 4, name: 'Leon' },
// ];
function EmployeeList() {
const [employees, setEmployees] = useState([]);
const [error, setError] = useState(null);
useEffect(() => {
const loadEmployees = async () => {
try {
const data = await fetchEmployees();
setEmployees(data);
} catch (err) {
setError("Failed to load employees.");
}
};
loadEmployees();
}, []);
if (error) {
return <div>Error: {error}</div>;
}
return (
<div className="p-4 max-w-2xl mx-auto">
<h1 className="text-2xl font-bold mb-4 text-center">Employee List</h1>
<div className="grid gap-4 grid-cols-1 sm:grid-cols-2">
{employees.map(emp => (
<Link
key={emp.id}
to={`/employee/${emp.id}`}
className="p-4 bg-blue-100 rounded shadow hover:bg-blue-200 transition"
>
<p className="text-lg font-semibold">{emp.name}</p>
</Link>
))}
</div>
</div>
);
}
export default EmployeeList;
So you want to keep the state of your buttons even after refresh or page change. For this you need to use localStorage
.
I added a few blocks to save the state of your variables in localStorage
instead of the state (which is lost when you change page).
import React, { useEffect, useState } from 'react';
import { Link, useParams } from 'react-router-dom';
import { fetchEmployeeById, checkIn, checkOut, fetchEmployeeStatus } from '../api/Employeeapi';
function EmployeeTimeTracking() {
const { employeeId } = useParams();
const [employeeName, setEmployeeName] = useState("Employee");
const [status, setStatus] = useState('checkOut');
const [checkInTime, setCheckInTime] = useState(null);
const [checkOutTime, setCheckOutTime] = useState(null);
const [timeTrackingId, setTimeTrackingId] = useState(null);
useEffect(() => {
// Fetch employee data by ID
const fetchEmployee = async () => {
try {
const employee = await fetchEmployeeById(employeeId);
setEmployeeName(employee.name);
} catch (error) {
console.error("Error fetching employee:", error);
}
};
// ADDED THIS: Load status from localStorage
const savedStatus = localStorage.getItem(`status_${employeeId}`);
const savedCheckInTime = localStorage.getItem(`checkInTime_${employeeId}`);
const savedCheckOutTime = localStorage.getItem(`checkOutTime_${employeeId}`);
const savedTimeTrackingId = localStorage.getItem(`timeTrackingId_${employeeId}`);
if (savedStatus) setStatus(savedStatus);
if (savedCheckInTime) setCheckInTime(savedCheckInTime);
if (savedCheckOutTime) setCheckOutTime(savedCheckOutTime);
if (savedTimeTrackingId) setTimeTrackingId(savedTimeTrackingId);
// Fetch current status from server
const fetchStatus = async () => {
try {
const data = await fetchEmployeeStatus(employeeId);
setStatus(data.status);
setCheckInTime(data.checkInTime ? new Date(data.checkInTime).toLocaleTimeString("en-US", { timeZone: "America/New_York" }) : null);
setCheckOutTime(data.checkOutTime ? new Date(data.checkOutTime).toLocaleTimeString("en-US", { timeZone: "America/New_York" }) : null);
setTimeTrackingId(data.id);
// ADDED THIS: Store these values in localStorage for persistence
localStorage.setItem(`status_${employeeId}`, data.status);
if (data.checkInTime) localStorage.setItem(`checkInTime_${employeeId}`, data.checkInTime);
if (data.checkOutTime) localStorage.setItem(`checkOutTime_${employeeId}`, data.checkOutTime);
if (data.id) localStorage.setItem(`timeTrackingId_${employeeId}`, data.id);
} catch (error) {
console.error("Error fetching employee status:", error);
}
};
fetchStatus();
fetchEmployee();
}, [employeeId]);
const handleCheckIn = async () => {
try {
const data = await checkIn(employeeId);
const currentTime = new Date().toLocaleTimeString("en-US", { timeZone: "America/New_York" });
setTimeTrackingId(data.id);
setCheckInTime(currentTime);
setStatus('checkedIn');
// ADDED THIS: Save to localStorage
localStorage.setItem(`status_${employeeId}`, 'checkedIn');
localStorage.setItem(`checkInTime_${employeeId}`, currentTime);
localStorage.setItem(`timeTrackingId_${employeeId}`, data.id);
} catch (error) {
console.error("Check-in failed:", error);
}
};
const handleCheckOut = async () => {
try {
await checkOut(timeTrackingId);
const currentTime = new Date().toLocaleTimeString("en-US", { timeZone: "America/New_York" });
setCheckOutTime(currentTime);
setStatus('checkedOut');
// ADDED THIS: Update localStorage
localStorage.setItem(`status_${employeeId}`, 'checkedOut');
localStorage.setItem(`checkOutTime_${employeeId}`, currentTime);
} catch (error) {
console.error("Check-out failed:", error);
}
};
return (
<div className="p-3 max-w-md mx-auto">
<Link to="/" className="text-blue-500 absolute left-2 top-0 hover:underline">
← Back to Employee List
</Link>
<div className="p-4 max-w-md mx-auto">
<h2 className="text-2xl font-bold mb-4 text-center">
Time Tracking for {employeeName}
</h2>
<div className="flex flex-col space-y-4 items-center">
<button
onClick={handleCheckIn}
disabled={status === 'checkedIn'}
className={`px-4 py-2 w-full sm:w-1/2 rounded ${status === 'checkedIn' ? 'bg-gray-300 cursor-not-allowed' : 'bg-green-500 hover:bg-green-600'} text-white font-semibold transition`}
>
Check In
</button>
<button
onClick={handleCheckOut}
disabled={status !== 'checkedIn'}
className={`px-4 py-2 w-full sm:w-1/2 rounded ${status !== 'checkedIn' ? 'bg-gray-300 cursor-not-allowed' : 'bg-red-500 hover:bg-red-600'} text-white font-semibold transition`}
>
Check Out
</button>
</div>
<div className="mt-6 text-center">
<p className="text-lg">Check-In Time: {checkInTime || 'Not Checked In'}</p>
<p className="text-lg">Check-Out Time: {checkOutTime || 'Not Checked Out'}</p>
</div>
</div>
</div>
);
}
export default EmployeeTimeTracking;
Maybe you should consider splitting this component and make the buttons independent components. It could help you have a clearer view of everything.