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:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user