Implemented automated time tracking with Clock In/Out functionality that integrates with the Time Logging Service. This eliminates manual time logging and provides live timer functionality for employees.
Location: entity/TimeSession.java
Tracks active clock-in sessions locally in Appointment Service:
id- UUID primary keyappointmentId- Links to appointmentemployeeId- Employee who clocked inclockInTime- Timestamp when work started (auto-set)clockOutTime- Timestamp when work endedactive- Boolean flag for active sessionstimeLogId- Reference to Time Logging Service entry
Location: repository/TimeSessionRepository.java
JPA repository with custom queries:
findByAppointmentIdAndActiveTrue()- Find active session for appointmentfindByAppointmentIdAndEmployeeIdAndActiveTrue()- Find specific employee's active sessionfindByEmployeeIdAndActiveTrue()- All active sessions for employeefindByEmployeeIdOrderByClockInTimeDesc()- Employee's session history
Location: dto/response/TimeSessionResponse.java
API response containing:
- Session details (id, appointmentId, employeeId)
- Time information (clockInTime, clockOutTime, active)
- Calculated fields:
elapsedSeconds- For live timer displayhoursWorked- Total hours when completed
Location: client/TimeLoggingClient.java
Added JWT-authenticated methods:
createTimeLog()- Creates entry in Time Logging Service, returns timeLogIdupdateTimeLog()- Updates existing log with actual hours workedcreateAuthHeaders()- Extracts JWT token for service-to-service auth
Location: service/impl/AppointmentServiceImpl.java
- Validates appointment exists and employee is assigned
- Checks for existing active session
- Creates time log in Time Logging Service (0 hours initially)
- Creates local TimeSession entity
- Updates appointment status to
IN_PROGRESS - Sends notification to customer
- Returns TimeSessionResponse with clockInTime
- Finds active TimeSession
- Sets clockOutTime to now
- Calculates hours worked:
(clockOutTime - clockInTime) / 60 minutes - Updates Time Logging Service with actual hours
- Marks TimeSession as inactive
- Updates appointment status to
COMPLETED - Sends completion notification with hours worked
- Returns TimeSessionResponse with total hours
- Retrieves active session if exists
- Calculates
elapsedSecondsfor live timer - Returns null if no active session
Location: controller/AppointmentController.java
| Endpoint | Method | Role | Description |
|---|---|---|---|
/appointments/{id}/clock-in |
POST | EMPLOYEE | Start time tracking |
/appointments/{id}/clock-out |
POST | EMPLOYEE | Stop time tracking |
/appointments/{id}/time-session |
GET | EMPLOYEE | Get active session (for timer) |
All endpoints require JWT authentication with X-User-Subject header.
Updated: acceptVehicleArrival() method
Now automatically calls clockIn() when employee accepts vehicle arrival, eliminating the need for manual clock-in after accepting work.
POST /appointments/{appointmentId}/clock-in
Headers:
Authorization: Bearer <JWT_TOKEN>
X-User-Subject: <employeeId>
Response:
{
"id": "uuid",
"appointmentId": "appt-123",
"employeeId": "emp-456",
"clockInTime": "2025-01-20T10:30:00",
"clockOutTime": null,
"active": true,
"elapsedSeconds": 0,
"hoursWorked": null
}GET /appointments/{appointmentId}/time-session
Headers:
Authorization: Bearer <JWT_TOKEN>
X-User-Subject: <employeeId>
Response:
{
"id": "uuid",
"appointmentId": "appt-123",
"employeeId": "emp-456",
"clockInTime": "2025-01-20T10:30:00",
"clockOutTime": null,
"active": true,
"elapsedSeconds": 3600, // Updated in real-time
"hoursWorked": null
}POST /appointments/{appointmentId}/clock-out
Headers:
Authorization: Bearer <JWT_TOKEN>
X-User-Subject: <employeeId>
Response:
{
"id": "uuid",
"appointmentId": "appt-123",
"employeeId": "emp-456",
"clockInTime": "2025-01-20T10:30:00",
"clockOutTime": "2025-01-20T12:45:00",
"active": false,
"elapsedSeconds": 8100,
"hoursWorked": 2.25
}Clock In:
Appointment Service → Time Logging Service
POST /time-logs
{
"employeeId": "emp-123",
"serviceId": "appt-456",
"hours": 0,
"description": "Work started...",
"date": "2025-01-20",
"workType": "SERVICE"
}
← Returns: { "id": "log-789", ... }
Clock Out:
Appointment Service → Time Logging Service
PUT /time-logs/log-789
{
"hours": 2.25,
"description": "Completed: 2.25 hours worked"
}
All Time Logging Service requests include:
Headers:
Authorization: Bearer <JWT_TOKEN>
X-User-Subject: <employeeId>
Token is extracted from SecurityContext OAuth2 authentication.
// Conditional rendering based on appointment status
{appointment.status === 'CONFIRMED' && !activeSession && (
<Button onClick={handleClockIn}>
<ClockIcon /> Clock In
</Button>
)}
{appointment.status === 'IN_PROGRESS' && activeSession && (
<div>
<Timer elapsedSeconds={activeSession.elapsedSeconds} />
<Button onClick={handleClockOut}>
<StopIcon /> Clock Out
</Button>
</div>
)}// Clock in
const handleClockIn = async () => {
const response = await fetch(
`/api/appointments/${appointmentId}/clock-in`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'X-User-Subject': employeeId
}
}
);
const session = await response.json();
setActiveSession(session);
};
// Clock out
const handleClockOut = async () => {
const response = await fetch(
`/api/appointments/${appointmentId}/clock-out`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'X-User-Subject': employeeId
}
}
);
const session = await response.json();
setActiveSession(null);
showNotification(`Work completed! ${session.hoursWorked} hours logged.`);
};const Timer: React.FC<{ elapsedSeconds: number }> = ({ elapsedSeconds }) => {
const [seconds, setSeconds] = useState(elapsedSeconds);
useEffect(() => {
const interval = setInterval(async () => {
// Fetch updated elapsed time from server
const response = await fetch(
`/api/appointments/${appointmentId}/time-session`,
{
headers: {
'Authorization': `Bearer ${token}`,
'X-User-Subject': employeeId
}
}
);
if (response.ok) {
const session = await response.json();
setSeconds(session.elapsedSeconds);
}
}, 1000); // Update every second
return () => clearInterval(interval);
}, []);
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const secs = seconds % 60;
return (
<div className="timer">
<span className="font-mono text-2xl">
{String(hours).padStart(2, '0')}:
{String(minutes).padStart(2, '0')}:
{String(secs).padStart(2, '0')}
</span>
<span className="text-sm text-gray-600">Time Elapsed</span>
</div>
);
};Call Time Logging Service endpoints:
// Today's hours
GET /api/time-logs/summary?period=daily&date=2025-01-20
// This week
GET /api/time-logs/summary?period=weekly&date=2025-01-20
// Overall stats
GET /api/time-logs/stats
// Recent logs
GET /api/time-logs?employeeId={id}&fromDate=2025-01-01&toDate=2025-01-31┌─────────────────────────────────────┐
│ Time Tracking Summary │
├─────────────────────────────────────┤
│ Today: 4.5 hours │
│ This Week: 22.0 hours │
│ This Month: 88.5 hours │
│ Total: 450.0 hours │
├─────────────────────────────────────┤
│ Recent Time Logs │
│ ┌────────────────────────────────┐ │
│ │ Jan 20 | Oil Change | 2.25h │ │
│ │ Jan 19 | Brake Repair | 3.5h │ │
│ │ Jan 18 | Inspection | 1.0h │ │
│ └────────────────────────────────┘ │
└─────────────────────────────────────┘
✅ One-click clock in/out ✅ Live timer shows exactly how long they've been working ✅ No manual time entry required ✅ Automatic status updates
✅ Accurate time tracking ✅ Real-time work status monitoring ✅ Automated customer notifications ✅ Historical time data for analytics
✅ Single source of truth (Time Logging Service) ✅ Local session tracking for quick queries ✅ JWT authentication for security ✅ Transaction-safe operations
- Clock in creates time log with 0 hours
- Clock in updates appointment status to IN_PROGRESS
- Clock in sends customer notification
- Cannot clock in twice for same appointment
- Get active session returns correct elapsed time
- Clock out calculates correct hours
- Clock out updates time log with actual hours
- Clock out updates appointment status to COMPLETED
- Clock out sends completion notification with hours
- Cannot clock out without active session
- Authorization: Only assigned employees can clock in/out
- JWT token properly propagated to Time Logging Service
- Frontend timer updates every second
- Summary page shows correct totals
-
Build and deploy Appointment Service
cd Appointment_Service mvn clean package docker build -t appointment-service .
-
Update Frontend
- Add Timer component to appointment details page
- Implement clock in/out buttons
- Create time logs summary page
-
Test Integration
- Test clock in → verify time log created
- Let timer run for 1 minute
- Clock out → verify hours logged correctly
- Check Time Logging Service database
-
Documentation
- Update API documentation
- Add user guide for employees
- Create admin monitoring dashboard
No additional configuration needed. Uses existing:
- JWT authentication setup
- Time Logging Service URL from WebClient config
- Database connection for TimeSession table
Table created automatically by JPA:
CREATE TABLE time_session (
id VARCHAR(255) PRIMARY KEY,
appointment_id VARCHAR(255) NOT NULL,
employee_id VARCHAR(255) NOT NULL,
clock_in_time TIMESTAMP NOT NULL,
clock_out_time TIMESTAMP,
active BOOLEAN NOT NULL DEFAULT true,
time_log_id VARCHAR(255) NOT NULL
);For issues or questions:
- Check logs:
docker logs appointment-service - Verify Time Logging Service connectivity
- Confirm JWT token in SecurityContext
- Check database for TimeSession entries