time-tracker/docu/code_examples/react_component.tsx

136 lines
4.7 KiB
TypeScript

// presentation/components/timeTracker/Timer/Timer.tsx
import React, { useState, useEffect } from 'react';
import { useTimeTracking } from '../../../hooks/useTimeTracking';
import { pipe, Option, fromNullable } from '../../../../utils/fp/option';
import { Button } from '../../common/Button';
import { formatDuration } from '../../../../utils/date/dateUtils';
interface TimerProps {
onComplete?: (duration: number) => void;
}
export const Timer: React.FC<TimerProps> = ({ onComplete }) => {
const [isRunning, setIsRunning] = useState(false);
const [startTime, setStartTime] = useState<Option<Date>>(Option.none());
const [elapsedTime, setElapsedTime] = useState(0);
const [selectedProject, setSelectedProject] = useState<Option<string>>(Option.none());
const [selectedActivity, setSelectedActivity] = useState<Option<string>>(Option.none());
const { lastTimeEntry, projects, activities } = useTimeTracking();
// Beim ersten Rendering die letzte Zeitbuchung laden
useEffect(() => {
pipe(
fromNullable(lastTimeEntry),
Option.map(entry => {
setSelectedProject(Option.some(entry.projectId));
setSelectedActivity(Option.some(entry.activityId));
})
);
}, [lastTimeEntry]);
// Timer-Logik
useEffect(() => {
let interval: NodeJS.Timeout | null = null;
if (isRunning) {
interval = setInterval(() => {
const now = new Date();
pipe(
startTime,
Option.map(start => {
const diff = now.getTime() - start.getTime();
setElapsedTime(Math.floor(diff / 1000));
})
);
}, 1000);
} else if (interval) {
clearInterval(interval);
}
return () => {
if (interval) clearInterval(interval);
};
}, [isRunning, startTime]);
// Timer starten
const handleStart = () => {
setStartTime(Option.some(new Date()));
setIsRunning(true);
};
// Timer stoppen
const handleStop = () => {
setIsRunning(false);
// Prüfen, ob Projekt und Aktivität ausgewählt wurden
const projectId = pipe(
selectedProject,
Option.getOrElse(() => '')
);
const activityId = pipe(
selectedActivity,
Option.getOrElse(() => '')
);
if (projectId && activityId && onComplete) {
onComplete(elapsedTime);
}
// Timer zurücksetzen
setElapsedTime(0);
setStartTime(Option.none());
};
return (
<div className="bg-white rounded-lg shadow-md p-4">
<div className="text-4xl text-center font-mono mb-4">
{formatDuration(elapsedTime)}
</div>
<div className="grid grid-cols-2 gap-4 mb-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Projekt
</label>
<select
className="w-full border border-gray-300 rounded-md px-3 py-2"
value={pipe(selectedProject, Option.getOrElse(() => ''))}
onChange={(e) => setSelectedProject(Option.some(e.target.value))}
disabled={isRunning}
>
<option value="">Projekt auswählen</option>
{projects.map((project) => (
<option key={project.id} value={project.id}>
{project.name}
</option>
))}
</select>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Tätigkeit
</label>
<select
className="w-full border border-gray-300 rounded-md px-3 py-2"
value={pipe(selectedActivity, Option.getOrElse(() => ''))}
onChange={(e) => setSelectedActivity(Option.some(e.target.value))}
disabled={isRunning}
>
<option value="">Tätigkeit auswählen</option>
{activities.map((activity) => (
<option key={activity.id} value={activity.id}>
{activity.name}
</option>
))}
</select>
</div>
</div>
<div className="flex justify-center">
</div>
</div>
);
};