feat: Implement internationalization for task management components

- Added translation support for task-related strings in ProjectTaskForm and ProjectTasksSection components.
- Integrated translation for navigation items in the Navigation component.
- Created ProjectCalendarWidget component with Polish translations for project statuses and deadlines.
- Developed Tooltip component for enhanced user experience with tooltips.
- Established a field change history logging system in the database with associated queries.
- Enhanced task update logging to include translated status and priority changes.
- Introduced server-side translations for system messages to improve localization.
This commit is contained in:
2025-09-11 15:49:07 +02:00
parent 50adc50a24
commit 0dd988730f
24 changed files with 1945 additions and 114 deletions

View File

@@ -3,8 +3,10 @@
import { useState, useEffect } from "react";
import Button from "./ui/Button";
import Badge from "./ui/Badge";
import { useTranslation } from "@/lib/i18n";
export default function ProjectTaskForm({ projectId, onTaskAdded }) {
const { t } = useTranslation();
const [taskTemplates, setTaskTemplates] = useState([]);
const [users, setUsers] = useState([]);
const [taskType, setTaskType] = useState("template"); // "template" or "custom"
@@ -67,10 +69,10 @@ export default function ProjectTaskForm({ projectId, onTaskAdded }) {
setAssignedTo("");
if (onTaskAdded) onTaskAdded();
} else {
alert("Failed to add task to project.");
alert(t("tasks.addTaskError"));
}
} catch (error) {
alert("Error adding task to project.");
alert(t("tasks.addTaskError"));
} finally {
setIsSubmitting(false);
}
@@ -79,7 +81,7 @@ export default function ProjectTaskForm({ projectId, onTaskAdded }) {
<form onSubmit={handleSubmit} className="space-y-6">
<div>
<label className="block text-sm font-medium text-gray-700 mb-3">
Task Type
{t("tasks.taskType")}
</label>
<div className="flex space-x-6">
<label className="flex items-center">
@@ -90,7 +92,7 @@ export default function ProjectTaskForm({ projectId, onTaskAdded }) {
onChange={(e) => setTaskType(e.target.value)}
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300"
/>
<span className="ml-2 text-sm text-gray-900">From Template</span>
<span className="ml-2 text-sm text-gray-900">{t("tasks.fromTemplate")}</span>
</label>
<label className="flex items-center">
<input
@@ -100,7 +102,7 @@ export default function ProjectTaskForm({ projectId, onTaskAdded }) {
onChange={(e) => setTaskType(e.target.value)}
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300"
/>
<span className="ml-2 text-sm text-gray-900">Custom Task</span>
<span className="ml-2 text-sm text-gray-900">{t("tasks.customTask")}</span>
</label>
</div>
</div>
@@ -108,7 +110,7 @@ export default function ProjectTaskForm({ projectId, onTaskAdded }) {
{taskType === "template" ? (
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Select Task Template
{t("tasks.selectTemplate")}
</label>{" "}
<select
value={selectedTemplate}
@@ -116,10 +118,10 @@ export default function ProjectTaskForm({ projectId, onTaskAdded }) {
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
required
>
<option value="">Choose a task template...</option>
<option value="">{t("tasks.chooseTemplate")}</option>
{taskTemplates.map((template) => (
<option key={template.task_id} value={template.task_id}>
{template.name} ({template.max_wait_days} days)
{template.name} ({template.max_wait_days} {t("tasks.days")})
</option>
))}
</select>
@@ -128,20 +130,20 @@ export default function ProjectTaskForm({ projectId, onTaskAdded }) {
<div className="space-y-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Task Name
{t("tasks.taskName")}
</label>
<input
type="text"
value={customTaskName}
onChange={(e) => setCustomTaskName(e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="Enter custom task name..."
placeholder={t("tasks.enterTaskName")}
required
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Max Wait Days
{t("tasks.maxWait")}
</label>
<input
type="number"
@@ -154,13 +156,13 @@ export default function ProjectTaskForm({ projectId, onTaskAdded }) {
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Description
{t("tasks.description")}
</label>
<textarea
value={customDescription}
onChange={(e) => setCustomDescription(e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="Enter task description (optional)..."
placeholder={t("tasks.enterDescription")}
rows={3}
/>
</div>
@@ -169,14 +171,14 @@ export default function ProjectTaskForm({ projectId, onTaskAdded }) {
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Assign To <span className="text-gray-500 text-xs">(optional)</span>
{t("tasks.assignedTo")} <span className="text-gray-500 text-xs">({t("common.optional")})</span>
</label>
<select
value={assignedTo}
onChange={(e) => setAssignedTo(e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
>
<option value="">Unassigned</option>
<option value="">{t("projects.unassigned")}</option>
{users.map((user) => (
<option key={user.id} value={user.id}>
{user.name} ({user.email})
@@ -187,17 +189,17 @@ export default function ProjectTaskForm({ projectId, onTaskAdded }) {
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Priority
{t("tasks.priority")}
</label>
<select
value={priority}
onChange={(e) => setPriority(e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
>
<option value="low">Low</option>
<option value="normal">Normal</option>
<option value="high">High</option>
<option value="urgent">Urgent</option>
<option value="low">{t("tasks.low")}</option>
<option value="normal">{t("tasks.normal")}</option>
<option value="high">{t("tasks.high")}</option>
<option value="urgent">{t("tasks.urgent")}</option>
</select>
</div>
@@ -211,7 +213,7 @@ export default function ProjectTaskForm({ projectId, onTaskAdded }) {
(taskType === "custom" && !customTaskName.trim())
}
>
{isSubmitting ? "Adding..." : "Add Task"}
{isSubmitting ? t("tasks.adding") : t("tasks.addTask")}
</Button>
</div>
</form>