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