Add missing translations for task management features
Integrates i18next for internationalization, adding translated strings for task details, search, filters, and calendar views in both English and German. Replit-Commit-Author: Agent Replit-Commit-Session-Id: ceced2fc-aa46-458d-ba87-ddd4b7bb1518 Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/659922a9-0087-461c-90dd-6d9a58b81d4d/ceced2fc-aa46-458d-ba87-ddd4b7bb1518/E3NLe8e
This commit is contained in:
@@ -10,6 +10,7 @@ import { Separator } from '@/components/ui/separator';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { Clock, Timer, FileText, Plus, Edit2, Save, X, Calendar, Tag } from 'lucide-react';
|
||||
import { Task, Label } from '@shared/schema';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
interface TimeEntry {
|
||||
id: string;
|
||||
@@ -33,6 +34,8 @@ export default function TaskDetailsModal({
|
||||
onSave,
|
||||
labels = []
|
||||
}: TaskDetailsModalProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
// Edit form state
|
||||
const [editedTitle, setEditedTitle] = useState('');
|
||||
const [editedDescription, setEditedDescription] = useState('');
|
||||
@@ -73,7 +76,7 @@ export default function TaskDetailsModal({
|
||||
id: 'tracked-time',
|
||||
date: new Date(),
|
||||
timeSpent: task.timeTracked,
|
||||
description: 'Previously tracked time'
|
||||
description: t('taskDetails.previouslyTrackedTime')
|
||||
}]);
|
||||
} else {
|
||||
setTimeEntries([]);
|
||||
@@ -237,7 +240,7 @@ export default function TaskDetailsModal({
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center gap-2">
|
||||
<FileText className="w-5 h-5" />
|
||||
Task Details
|
||||
{t('taskDetails.title')}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
@@ -257,14 +260,14 @@ export default function TaskDetailsModal({
|
||||
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<Badge variant="outline" className={`text-xs ${getPriorityColor(task.priority)}`}>
|
||||
{task.priority} priority
|
||||
{t(`taskDetails.priorityValue.${task.priority}`)}
|
||||
</Badge>
|
||||
<Badge variant="outline" className={`text-xs ${getStatusColor(task.status)}`}>
|
||||
{task.status}
|
||||
{t(`taskDetails.statusValue.${task.status}`)}
|
||||
</Badge>
|
||||
{getTotalTrackedTime() > 0 && (
|
||||
<Badge variant="outline" className="text-xs">
|
||||
{formatMinutesToHours(getTotalTrackedTime())} tracked
|
||||
{formatMinutesToHours(getTotalTrackedTime())} {t('taskDetails.tracked')}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
@@ -275,41 +278,41 @@ export default function TaskDetailsModal({
|
||||
<TabsList className="grid w-full grid-cols-3">
|
||||
<TabsTrigger value="edit" data-testid="tab-edit">
|
||||
<Edit2 className="w-4 h-4 mr-2" />
|
||||
Edit
|
||||
{t('taskDetails.editTab')}
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="notes" data-testid="tab-notes">
|
||||
<FileText className="w-4 h-4 mr-2" />
|
||||
Notes
|
||||
{t('taskDetails.notesTab')}
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="time" data-testid="tab-time">
|
||||
<Clock className="w-4 h-4 mr-2" />
|
||||
Time Tracking
|
||||
{t('taskDetails.timeTab')}
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
{/* Edit Tab */}
|
||||
<TabsContent value="edit" className="space-y-4">
|
||||
<Card className="p-4">
|
||||
<h4 className="font-medium mb-4">Edit Task</h4>
|
||||
<h4 className="font-medium mb-4">{t('taskDetails.editTask')}</h4>
|
||||
<div className="space-y-4">
|
||||
{/* Title */}
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-2">Title</label>
|
||||
<label className="text-sm font-medium block mb-2">{t('taskDetails.titleLabel')}</label>
|
||||
<Input
|
||||
value={editedTitle}
|
||||
onChange={(e) => setEditedTitle(e.target.value)}
|
||||
placeholder="Task title"
|
||||
placeholder={t('taskDetails.titlePlaceholder')}
|
||||
data-testid="input-task-title"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Description */}
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-2">Description</label>
|
||||
<label className="text-sm font-medium block mb-2">{t('taskDetails.description')}</label>
|
||||
<Textarea
|
||||
value={editedDescription}
|
||||
onChange={(e) => setEditedDescription(e.target.value)}
|
||||
placeholder="Task description"
|
||||
placeholder={t('taskDetails.descriptionPlaceholder')}
|
||||
rows={3}
|
||||
data-testid="input-task-description"
|
||||
/>
|
||||
@@ -318,29 +321,29 @@ export default function TaskDetailsModal({
|
||||
{/* Status and Priority */}
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-2">Status</label>
|
||||
<label className="text-sm font-medium block mb-2">{t('taskDetails.status')}</label>
|
||||
<Select value={editedStatus} onValueChange={(value) => setEditedStatus(value as 'todo' | 'inProgress' | 'done')}>
|
||||
<SelectTrigger data-testid="select-status">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="todo">To Do</SelectItem>
|
||||
<SelectItem value="inProgress">In Progress</SelectItem>
|
||||
<SelectItem value="done">Done</SelectItem>
|
||||
<SelectItem value="todo">{t('taskDetails.statusValue.todo')}</SelectItem>
|
||||
<SelectItem value="inProgress">{t('taskDetails.statusValue.inProgress')}</SelectItem>
|
||||
<SelectItem value="done">{t('taskDetails.statusValue.done')}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-2">Priority</label>
|
||||
<label className="text-sm font-medium block mb-2">{t('taskDetails.priority')}</label>
|
||||
<Select value={editedPriority} onValueChange={(value) => setEditedPriority(value as 'low' | 'medium' | 'high')}>
|
||||
<SelectTrigger data-testid="select-priority">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="low">Low</SelectItem>
|
||||
<SelectItem value="medium">Medium</SelectItem>
|
||||
<SelectItem value="high">High</SelectItem>
|
||||
<SelectItem value="low">{t('taskDetails.priorityValue.low')}</SelectItem>
|
||||
<SelectItem value="medium">{t('taskDetails.priorityValue.medium')}</SelectItem>
|
||||
<SelectItem value="high">{t('taskDetails.priorityValue.high')}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
@@ -348,13 +351,13 @@ export default function TaskDetailsModal({
|
||||
|
||||
{/* Label */}
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-2">Label</label>
|
||||
<label className="text-sm font-medium block mb-2">{t('taskDetails.label')}</label>
|
||||
<Select value={editedLabelId || 'none'} onValueChange={(value) => setEditedLabelId(value === 'none' ? null : value)}>
|
||||
<SelectTrigger data-testid="input-label-selector">
|
||||
<SelectValue placeholder="Select a label" />
|
||||
<SelectValue placeholder={t('taskDetails.selectLabel')} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="none">No Label</SelectItem>
|
||||
<SelectItem value="none">{t('taskDetails.noLabel')}</SelectItem>
|
||||
{labels.map((label) => (
|
||||
<SelectItem key={label.id} value={label.id}>
|
||||
<div className="flex items-center gap-2">
|
||||
@@ -372,7 +375,7 @@ export default function TaskDetailsModal({
|
||||
|
||||
{/* Due Date */}
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-2">Due Date</label>
|
||||
<label className="text-sm font-medium block mb-2">{t('taskDetails.dueDate')}</label>
|
||||
<Input
|
||||
type="date"
|
||||
value={editedDueDate}
|
||||
@@ -389,7 +392,7 @@ export default function TaskDetailsModal({
|
||||
data-testid="button-save-task-edit"
|
||||
>
|
||||
<Save className="w-4 h-4 mr-1" />
|
||||
Save Changes
|
||||
{t('taskDetails.save')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -400,7 +403,7 @@ export default function TaskDetailsModal({
|
||||
<TabsContent value="notes" className="space-y-4">
|
||||
<Card className="p-4">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h4 className="font-medium">Task Notes</h4>
|
||||
<h4 className="font-medium">{t('taskDetails.taskNotes')}</h4>
|
||||
{!isEditingNotes && (
|
||||
<Button
|
||||
variant="outline"
|
||||
@@ -409,7 +412,7 @@ export default function TaskDetailsModal({
|
||||
data-testid="button-edit-notes"
|
||||
>
|
||||
<Edit2 className="w-4 h-4 mr-1" />
|
||||
Edit
|
||||
{t('taskDetails.edit')}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
@@ -419,7 +422,7 @@ export default function TaskDetailsModal({
|
||||
<Textarea
|
||||
value={notes}
|
||||
onChange={(e) => setNotes(e.target.value)}
|
||||
placeholder="Add your notes for this task..."
|
||||
placeholder={t('taskDetails.notesPlaceholder')}
|
||||
rows={4}
|
||||
data-testid="textarea-task-notes"
|
||||
/>
|
||||
@@ -430,7 +433,7 @@ export default function TaskDetailsModal({
|
||||
data-testid="button-save-notes"
|
||||
>
|
||||
<Save className="w-4 h-4 mr-1" />
|
||||
Save
|
||||
{t('taskDetails.save')}
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
@@ -442,7 +445,7 @@ export default function TaskDetailsModal({
|
||||
data-testid="button-cancel-notes"
|
||||
>
|
||||
<X className="w-4 h-4 mr-1" />
|
||||
Cancel
|
||||
{t('taskDetails.cancel')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -454,7 +457,7 @@ export default function TaskDetailsModal({
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-sm text-muted-foreground italic">
|
||||
No notes added yet. Click "Edit" to add notes.
|
||||
{t('taskDetails.noNotesYet')}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
@@ -468,7 +471,7 @@ export default function TaskDetailsModal({
|
||||
<Card className="p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h4 className="font-medium">Total Time Tracked</h4>
|
||||
<h4 className="font-medium">{t('taskDetails.totalTimeTracked')}</h4>
|
||||
<p className="text-2xl font-mono text-primary" data-testid="text-total-time">
|
||||
{formatMinutesToHours(getTotalTrackedTime())}
|
||||
</p>
|
||||
@@ -479,7 +482,7 @@ export default function TaskDetailsModal({
|
||||
data-testid="button-add-time"
|
||||
>
|
||||
<Plus className="w-4 h-4 mr-1" />
|
||||
Add Time
|
||||
{t('taskDetails.addTime')}
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
@@ -489,7 +492,7 @@ export default function TaskDetailsModal({
|
||||
<Card className="p-4">
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<h4 className="font-medium">Add Time Entry</h4>
|
||||
<h4 className="font-medium">{t('taskDetails.addTimeEntry')}</h4>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
@@ -504,11 +507,11 @@ export default function TaskDetailsModal({
|
||||
<TabsList className="grid w-full grid-cols-2">
|
||||
<TabsTrigger value="clock" data-testid="tab-clock-picker">
|
||||
<Clock className="w-4 h-4 mr-2" />
|
||||
Clock
|
||||
{t('taskDetails.clock')}
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="manual" data-testid="tab-manual-input">
|
||||
<Timer className="w-4 h-4 mr-2" />
|
||||
Manual
|
||||
{t('taskDetails.manual')}
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user