Add translations for various project management features

Integrate i18n translation keys for project template page elements, including column titles, buttons, dialogs, and input fields, within the ProjectTemplate component.

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/HdLXOOf
This commit is contained in:
paul-nothaft
2025-10-24 11:00:26 +00:00
parent 57f09310ca
commit 102315fa7c
2 changed files with 51 additions and 56 deletions
-4
View File
@@ -14,10 +14,6 @@ run = ["npm", "run", "start"]
localPort = 5000 localPort = 5000
externalPort = 80 externalPort = 80
[[ports]]
localPort = 33611
externalPort = 4200
[[ports]] [[ports]]
localPort = 35345 localPort = 35345
externalPort = 3002 externalPort = 3002
+51 -52
View File
@@ -127,6 +127,7 @@ export default function ProjectTemplate({
onCreateTasks, onCreateTasks,
onNavigateToSettings onNavigateToSettings
}: ProjectTemplateProps) { }: ProjectTemplateProps) {
const { t } = useTranslation();
const [projects, setProjects] = useState<Project[]>(defaultProjects); const [projects, setProjects] = useState<Project[]>(defaultProjects);
const [selectedProject, setSelectedProject] = useState<Project | null>(null); const [selectedProject, setSelectedProject] = useState<Project | null>(null);
const [draggedProject, setDraggedProject] = useState<string | null>(null); const [draggedProject, setDraggedProject] = useState<string | null>(null);
@@ -497,7 +498,7 @@ export default function ProjectTemplate({
}; };
const handleDeleteLabel = (id: string) => { const handleDeleteLabel = (id: string) => {
if (confirm('Are you sure you want to delete this label?')) { if (confirm(t('projectTemplate.deleteConfirm'))) {
deleteLabelMutation.mutate(id); deleteLabelMutation.mutate(id);
} }
}; };
@@ -517,13 +518,11 @@ export default function ProjectTemplate({
}; };
const projectColumns = [ const projectColumns = [
{ id: 'planning', title: 'Planning', status: 'planning' as const, color: 'bg-slate-100' }, { id: 'planning', title: t('projectTemplate.planning'), status: 'planning' as const, color: 'bg-slate-100' },
{ id: 'active', title: 'Active', status: 'active' as const, color: 'bg-blue-100' }, { id: 'active', title: t('projectTemplate.active'), status: 'active' as const, color: 'bg-blue-100' },
{ id: 'finished', title: 'Finished', status: 'finished' as const, color: 'bg-green-100' } { id: 'finished', title: t('projectTemplate.finished'), status: 'finished' as const, color: 'bg-green-100' }
]; ];
const { t } = useTranslation();
return ( return (
<div className="space-y-6"> <div className="space-y-6">
{/* Header */} {/* Header */}
@@ -550,14 +549,14 @@ export default function ProjectTemplate({
className="h-11" className="h-11"
> >
<Plus className="w-4 h-4 mr-2" /> <Plus className="w-4 h-4 mr-2" />
New Project {t('projectTemplate.newProject')}
</Button> </Button>
</div> </div>
<Tabs defaultValue="projects" className="space-y-3 sm:space-y-4"> <Tabs defaultValue="projects" className="space-y-3 sm:space-y-4">
<TabsList className="w-full"> <TabsList className="w-full">
<TabsTrigger value="projects" data-testid="tab-projects" className="flex-1">Projects</TabsTrigger> <TabsTrigger value="projects" data-testid="tab-projects" className="flex-1">{t('projectTemplate.projects')}</TabsTrigger>
<TabsTrigger value="labels" data-testid="tab-labels" className="flex-1">Labels</TabsTrigger> <TabsTrigger value="labels" data-testid="tab-labels" className="flex-1">{t('projectTemplate.labels')}</TabsTrigger>
</TabsList> </TabsList>
<TabsContent value="projects" className="space-y-4 sm:space-y-6"> <TabsContent value="projects" className="space-y-4 sm:space-y-6">
@@ -845,29 +844,29 @@ export default function ProjectTemplate({
}}> }}>
<DialogContent className="sm:max-w-3xl max-h-[90vh] overflow-y-auto"> <DialogContent className="sm:max-w-3xl max-h-[90vh] overflow-y-auto">
<DialogHeader> <DialogHeader>
<DialogTitle>Create New Project</DialogTitle> <DialogTitle>{t('projectTemplate.createProject')}</DialogTitle>
<DialogDescription> <DialogDescription>
Create a new project with tasks, timeline, and organize your work efficiently. {t('projectTemplate.description')}
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<div className="space-y-6 py-4"> <div className="space-y-6 py-4">
{/* Project Details */} {/* Project Details */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-4"> <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-2"> <div className="space-y-2">
<label className="text-sm font-medium">Project Name</label> <label className="text-sm font-medium">{t('projectTemplate.projectName')}</label>
<Input <Input
value={newProjectName} value={newProjectName}
onChange={(e) => setNewProjectName(e.target.value)} onChange={(e) => setNewProjectName(e.target.value)}
placeholder="Enter project name" placeholder={t('projectTemplate.projectNamePlaceholder')}
data-testid="input-project-name" data-testid="input-project-name"
/> />
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<label className="text-sm font-medium">Description (Optional)</label> <label className="text-sm font-medium">{t('projectTemplate.projectDescription')}</label>
<Input <Input
value={newProjectDescription} value={newProjectDescription}
onChange={(e) => setNewProjectDescription(e.target.value)} onChange={(e) => setNewProjectDescription(e.target.value)}
placeholder="Enter project description" placeholder={t('projectTemplate.projectDescriptionPlaceholder')}
data-testid="input-project-description" data-testid="input-project-description"
/> />
</div> </div>
@@ -875,7 +874,7 @@ export default function ProjectTemplate({
{/* Planned End Date */} {/* Planned End Date */}
<div className="space-y-2"> <div className="space-y-2">
<label className="text-sm font-medium">Planned End Date (Optional)</label> <label className="text-sm font-medium">{t('projectTemplate.plannedEndDate')}</label>
<Popover open={isPlannedEndDateCalendarOpen} onOpenChange={setIsPlannedEndDateCalendarOpen}> <Popover open={isPlannedEndDateCalendarOpen} onOpenChange={setIsPlannedEndDateCalendarOpen}>
<PopoverTrigger asChild> <PopoverTrigger asChild>
<Button <Button
@@ -884,7 +883,7 @@ export default function ProjectTemplate({
data-testid="button-select-planned-end-date" data-testid="button-select-planned-end-date"
> >
<CalendarIcon className="mr-2 h-4 w-4" /> <CalendarIcon className="mr-2 h-4 w-4" />
{newPlannedEndDate ? newPlannedEndDate.toLocaleDateString() : "Select planned end date"} {newPlannedEndDate ? newPlannedEndDate.toLocaleDateString() : t('projectTemplate.selectPlannedEndDate')}
</Button> </Button>
</PopoverTrigger> </PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start"> <PopoverContent className="w-auto p-0" align="start">
@@ -908,7 +907,7 @@ export default function ProjectTemplate({
className="text-xs text-muted-foreground hover:text-destructive" className="text-xs text-muted-foreground hover:text-destructive"
data-testid="button-clear-create-planned-end-date" data-testid="button-clear-create-planned-end-date"
> >
Clear date {t('projectTemplate.clearDate')}
</Button> </Button>
)} )}
</div> </div>
@@ -916,7 +915,7 @@ export default function ProjectTemplate({
{/* Tasks Section */} {/* Tasks Section */}
<div className="space-y-4"> <div className="space-y-4">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<h3 className="text-lg font-semibold">Project Tasks</h3> <h3 className="text-lg font-semibold">{t('projectTemplate.projectTasks')}</h3>
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
@@ -924,7 +923,7 @@ export default function ProjectTemplate({
data-testid="button-add-task" data-testid="button-add-task"
> >
<Plus className="w-4 h-4 mr-2" /> <Plus className="w-4 h-4 mr-2" />
Add Task {t('projectTemplate.addTask')}
</Button> </Button>
</div> </div>
@@ -976,7 +975,7 @@ export default function ProjectTemplate({
{projectTasks.length === 0 && ( {projectTasks.length === 0 && (
<div className="text-center text-muted-foreground text-sm py-8 border-2 border-dashed border-muted rounded-lg"> <div className="text-center text-muted-foreground text-sm py-8 border-2 border-dashed border-muted rounded-lg">
No tasks added yet. Click "Add Task" to get started. {t('projectTemplate.noLabelsDescription')}
</div> </div>
)} )}
</div> </div>
@@ -986,32 +985,32 @@ export default function ProjectTemplate({
<Card className="p-4 border-primary"> <Card className="p-4 border-primary">
<div className="space-y-4"> <div className="space-y-4">
<h4 className="font-semibold text-sm"> <h4 className="font-semibold text-sm">
{editingTaskIndex !== null ? 'Edit Task' : 'New Task'} {editingTaskIndex !== null ? t('projectTemplate.edit') : t('projectTemplate.addTask')}
</h4> </h4>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4"> <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-2"> <div className="space-y-2">
<label className="text-sm font-medium">Task Title</label> <label className="text-sm font-medium">{t('projectTemplate.taskTitle')}</label>
<Input <Input
value={taskForm.title || ''} value={taskForm.title || ''}
onChange={(e) => setTaskForm({...taskForm, title: e.target.value})} onChange={(e) => setTaskForm({...taskForm, title: e.target.value})}
placeholder="Enter task title" placeholder={t('projectTemplate.enterTaskTitle')}
data-testid="input-task-title" data-testid="input-task-title"
/> />
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<label className="text-sm font-medium">Description</label> <label className="text-sm font-medium">{t('projectTemplate.description')}</label>
<Input <Input
value={taskForm.description || ''} value={taskForm.description || ''}
onChange={(e) => setTaskForm({...taskForm, description: e.target.value})} onChange={(e) => setTaskForm({...taskForm, description: e.target.value})}
placeholder="Enter task description" placeholder={t('projectTemplate.enterTaskDescription')}
data-testid="input-task-description" data-testid="input-task-description"
/> />
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<label className="text-sm font-medium">Priority</label> <label className="text-sm font-medium">{t('projectTemplate.priority')}</label>
<Select <Select
value={taskForm.priority || 'medium'} value={taskForm.priority || 'medium'}
onValueChange={(value: ProjectTask['priority']) => onValueChange={(value: ProjectTask['priority']) =>
@@ -1022,15 +1021,15 @@ export default function ProjectTemplate({
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectItem value="low">Low</SelectItem> <SelectItem value="low">{t('priority.low')}</SelectItem>
<SelectItem value="medium">Medium</SelectItem> <SelectItem value="medium">{t('priority.medium')}</SelectItem>
<SelectItem value="high">High</SelectItem> <SelectItem value="high">{t('priority.high')}</SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<label className="text-sm font-medium">Status</label> <label className="text-sm font-medium">{t('projectTemplate.status')}</label>
<Select <Select
value={taskForm.status || 'todo'} value={taskForm.status || 'todo'}
onValueChange={(value: ProjectTask['status']) => onValueChange={(value: ProjectTask['status']) =>
@@ -1041,15 +1040,15 @@ export default function ProjectTemplate({
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectItem value="todo">To Do</SelectItem> <SelectItem value="todo">{t('status.todo')}</SelectItem>
<SelectItem value="inProgress">In Progress</SelectItem> <SelectItem value="inProgress">{t('status.inProgress')}</SelectItem>
<SelectItem value="done">Done</SelectItem> <SelectItem value="done">{t('status.done')}</SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<label className="text-sm font-medium">Estimated Hours</label> <label className="text-sm font-medium">{t('projectTemplate.estimatedHours')}</label>
<Input <Input
type="number" type="number"
value={taskForm.estimatedHours || ''} value={taskForm.estimatedHours || ''}
@@ -1067,7 +1066,7 @@ export default function ProjectTemplate({
onClick={handleCancelTaskEdit} onClick={handleCancelTaskEdit}
className="flex-1" className="flex-1"
> >
Cancel {t('projectTemplate.cancel')}
</Button> </Button>
<Button <Button
onClick={editingTaskIndex !== null ? handleSaveTask : handleAddTask} onClick={editingTaskIndex !== null ? handleSaveTask : handleAddTask}
@@ -1075,7 +1074,7 @@ export default function ProjectTemplate({
className="flex-1" className="flex-1"
data-testid="button-save-task" data-testid="button-save-task"
> >
{editingTaskIndex !== null ? 'Save Changes' : 'Add Task'} {editingTaskIndex !== null ? t('projectTemplate.save') : t('projectTemplate.addTask')}
</Button> </Button>
</div> </div>
</div> </div>
@@ -1094,7 +1093,7 @@ export default function ProjectTemplate({
}} }}
className="flex-1" className="flex-1"
> >
Cancel {t('projectTemplate.cancel')}
</Button> </Button>
<Button <Button
onClick={handleCreateProject} onClick={handleCreateProject}
@@ -1102,7 +1101,7 @@ export default function ProjectTemplate({
className="flex-1" className="flex-1"
data-testid="button-save-project" data-testid="button-save-project"
> >
Create Project ({projectTasks.length} tasks) {t('projectTemplate.createProject')} ({projectTasks.length} {t('projectTemplate.tasks', { count: projectTasks.length })})
</Button> </Button>
</div> </div>
</DialogContent> </DialogContent>
@@ -1118,7 +1117,7 @@ export default function ProjectTemplate({
}}> }}>
<DialogContent className="sm:max-w-3xl max-h-[90vh] overflow-y-auto"> <DialogContent className="sm:max-w-3xl max-h-[90vh] overflow-y-auto">
<DialogHeader> <DialogHeader>
<DialogTitle>Edit Project</DialogTitle> <DialogTitle>{t('projectTemplate.edit')}</DialogTitle>
</DialogHeader> </DialogHeader>
{selectedProject && ( {selectedProject && (
<div className="space-y-6 py-4"> <div className="space-y-6 py-4">
@@ -1422,7 +1421,7 @@ export default function ProjectTemplate({
}} }}
className="flex-1" className="flex-1"
> >
Cancel {t('projectTemplate.cancel')}
</Button> </Button>
<Button <Button
onClick={handleSaveEditedProject} onClick={handleSaveEditedProject}
@@ -1430,7 +1429,7 @@ export default function ProjectTemplate({
className="flex-1" className="flex-1"
data-testid="button-update-project" data-testid="button-update-project"
> >
Update Project ({projectTasks.length} tasks) {t('projectTemplate.update')} ({projectTasks.length} {t('projectTemplate.tasks', { count: projectTasks.length })})
</Button> </Button>
</div> </div>
</DialogContent> </DialogContent>
@@ -1439,22 +1438,22 @@ export default function ProjectTemplate({
<TabsContent value="labels" className="space-y-6"> <TabsContent value="labels" className="space-y-6">
{/* Labels Section Header */} {/* Labels Section Header */}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<h3 className="text-md font-medium">Task Labels</h3> <h3 className="text-md font-medium">{t('settings.labels.title')}</h3>
<Dialog open={isLabelDialogOpen} onOpenChange={setIsLabelDialogOpen}> <Dialog open={isLabelDialogOpen} onOpenChange={setIsLabelDialogOpen}>
<DialogTrigger asChild> <DialogTrigger asChild>
<Button variant="outline" size="sm" data-testid="button-create-label"> <Button variant="outline" size="sm" data-testid="button-create-label">
<Plus className="w-4 h-4 mr-2" /> <Plus className="w-4 h-4 mr-2" />
Create Label {t('projectTemplate.createLabel')}
</Button> </Button>
</DialogTrigger> </DialogTrigger>
<DialogContent> <DialogContent>
<DialogHeader> <DialogHeader>
<DialogTitle>{editingLabel ? 'Edit Label' : 'Create New Label'}</DialogTitle> <DialogTitle>{editingLabel ? t('projectTemplate.editLabel') : t('projectTemplate.createNewLabel')}</DialogTitle>
</DialogHeader> </DialogHeader>
<div className="space-y-4"> <div className="space-y-4">
<div> <div>
<Input <Input
placeholder="Label name" placeholder={t('projectTemplate.labelNamePlaceholder')}
value={labelName} value={labelName}
onChange={(e) => setLabelName(e.target.value)} onChange={(e) => setLabelName(e.target.value)}
data-testid="input-label-name" data-testid="input-label-name"
@@ -1490,7 +1489,7 @@ export default function ProjectTemplate({
className="flex-1" className="flex-1"
data-testid="button-cancel-label" data-testid="button-cancel-label"
> >
Cancel {t('projectTemplate.cancel')}
</Button> </Button>
<Button <Button
onClick={handleSaveLabel} onClick={handleSaveLabel}
@@ -1498,7 +1497,7 @@ export default function ProjectTemplate({
className="flex-1" className="flex-1"
data-testid="button-save-label" data-testid="button-save-label"
> >
{editingLabel ? 'Update' : 'Create'} {editingLabel ? t('projectTemplate.update') : t('projectTemplate.create')}
</Button> </Button>
</div> </div>
</div> </div>
@@ -1509,7 +1508,7 @@ export default function ProjectTemplate({
{/* Labels List */} {/* Labels List */}
{labelsLoading ? ( {labelsLoading ? (
<div className="flex items-center justify-center p-8"> <div className="flex items-center justify-center p-8">
<div className="text-muted-foreground">Loading labels...</div> <div className="text-muted-foreground">{t('projectTemplate.loadingLabels')}</div>
</div> </div>
) : ( ) : (
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3"> <div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
@@ -1556,9 +1555,9 @@ export default function ProjectTemplate({
{labels.length === 0 && ( {labels.length === 0 && (
<div className="col-span-full flex flex-col items-center justify-center p-8 text-center"> <div className="col-span-full flex flex-col items-center justify-center p-8 text-center">
<Tag className="w-12 h-12 text-muted-foreground mb-4" /> <Tag className="w-12 h-12 text-muted-foreground mb-4" />
<h3 className="font-medium text-muted-foreground mb-2">No labels yet</h3> <h3 className="font-medium text-muted-foreground mb-2">{t('projectTemplate.noLabelsYet')}</h3>
<p className="text-sm text-muted-foreground mb-4"> <p className="text-sm text-muted-foreground mb-4">
Create your first label to organize your tasks by color and category. {t('projectTemplate.noLabelsDescription')}
</p> </p>
<Button <Button
variant="outline" variant="outline"
@@ -1566,7 +1565,7 @@ export default function ProjectTemplate({
data-testid="button-create-first-label" data-testid="button-create-first-label"
> >
<Plus className="w-4 h-4 mr-2" /> <Plus className="w-4 h-4 mr-2" />
Create Label {t('projectTemplate.createLabel')}
</Button> </Button>
</div> </div>
)} )}