feat(i18n): Implement multilingual support with Polish and English translations

- Added translation context and provider for managing language state.
- Integrated translation functionality into existing components (TaskStatusDropdown, Navigation).
- Created LanguageSwitcher component for language selection.
- Updated task statuses and navigation labels to use translations.
- Added Polish translations for various UI elements, including navigation, tasks, projects, and contracts.
- Refactored utility functions to return localized strings for deadlines and date formatting.
This commit is contained in:
Chop
2025-07-27 22:01:15 +02:00
parent 9b6307eabe
commit e828aa660b
16 changed files with 1166 additions and 234 deletions

View File

@@ -1,8 +1,10 @@
"use client";
import React, { useState } from "react";
import { useTranslation } from "@/lib/i18n";
export default function NoteForm({ projectId }) {
const { t } = useTranslation();
const [note, setNote] = useState("");
const [status, setStatus] = useState(null);
@@ -17,10 +19,10 @@ export default function NoteForm({ projectId }) {
if (res.ok) {
setNote("");
setStatus("Note added");
setStatus(t("common.addNoteSuccess"));
window.location.reload();
} else {
setStatus("Failed to save note");
setStatus(t("common.addNoteError"));
}
}
@@ -29,7 +31,7 @@ export default function NoteForm({ projectId }) {
<textarea
value={note}
onChange={(e) => setNote(e.target.value)}
placeholder="Add a new note..."
placeholder={t("common.addNotePlaceholder")}
className="border p-2 w-full"
rows={3}
required
@@ -38,7 +40,7 @@ export default function NoteForm({ projectId }) {
type="submit"
className="bg-blue-600 text-white px-4 py-2 rounded"
>
Add Note
{t("common.addNote")}
</button>
{status && <p className="text-sm text-gray-600">{status}</p>}
</form>

View File

@@ -6,8 +6,10 @@ import { Card, CardHeader, CardContent } from "@/components/ui/Card";
import Button from "@/components/ui/Button";
import { Input } from "@/components/ui/Input";
import { formatDateForInput } from "@/lib/utils";
import { useTranslation } from "@/lib/i18n";
export default function ProjectForm({ initialData = null }) {
const { t } = useTranslation();
const [form, setForm] = useState({
contract_id: "",
project_name: "",
@@ -146,7 +148,7 @@ export default function ProjectForm({ initialData = null }) {
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Project Type <span className="text-red-500">*</span>
Typ projektu <span className="text-red-500">*</span>
</label>
<select
name="project_type"
@@ -155,17 +157,17 @@ export default function ProjectForm({ initialData = null }) {
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
required
>
<option value="design">Design (Projektowanie)</option>
<option value="construction">Construction (Realizacja)</option>
<option value="design">{t('projectType.design')}</option>
<option value="construction">{t('projectType.construction')}</option>
<option value="design+construction">
Design + Construction (Projektowanie + Realizacja)
{t('projectType.design+construction')}
</option>
</select>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Assigned To
{t('projects.assignedTo')}
</label>
<select
name="assigned_to"
@@ -173,7 +175,7 @@ export default function ProjectForm({ initialData = null }) {
onChange={handleChange}
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none 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})
@@ -186,92 +188,92 @@ export default function ProjectForm({ initialData = null }) {
{/* Basic Information Section */}
<div className="border-t pt-6">
<h3 className="text-lg font-medium text-gray-900 mb-4">
Basic Information
{t('projects.basicInformation')}
</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="md:col-span-2">
<label className="block text-sm font-medium text-gray-700 mb-2">
Project Name <span className="text-red-500">*</span>
{t('projects.projectName')} <span className="text-red-500">*</span>
</label>
<Input
type="text"
name="project_name"
value={form.project_name || ""}
onChange={handleChange}
placeholder="Enter project name"
placeholder={t('projects.enterProjectName')}
required
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
City
{t('projects.city')}
</label>
<Input
type="text"
name="city"
value={form.city || ""}
onChange={handleChange}
placeholder="Enter city"
placeholder={t('projects.enterCity')}
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Address
{t('projects.address')}
</label>
<Input
type="text"
name="address"
value={form.address || ""}
onChange={handleChange}
placeholder="Enter address"
placeholder={t('projects.enterAddress')}
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Plot
{t('projects.plot')}
</label>
<Input
type="text"
name="plot"
value={form.plot || ""}
onChange={handleChange}
placeholder="Enter plot number"
placeholder={t('projects.enterPlotNumber')}
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
District
{t('projects.district')}
</label>
<Input
type="text"
name="district"
value={form.district || ""}
onChange={handleChange}
placeholder="Enter district"
placeholder={t('projects.enterDistrict')}
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Unit
{t('projects.unit')}
</label>
<Input
type="text"
name="unit"
value={form.unit || ""}
onChange={handleChange}
placeholder="Enter unit"
placeholder={t('projects.enterUnit')}
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Finish Date
</label>{" "}
{t('projects.finishDate')}
</label>
<Input
type="date"
name="finish_date"
@@ -316,33 +318,33 @@ export default function ProjectForm({ initialData = null }) {
<div className="md:col-span-2">
<label className="block text-sm font-medium text-gray-700 mb-2">
Contact Information
{t('projects.contact')}
</label>
<Input
type="text"
name="contact"
value={form.contact || ""}
onChange={handleChange}
placeholder="Enter contact details"
placeholder={t('projects.placeholders.contact')}
/>
</div>
<div className="md:col-span-2">
<label className="block text-sm font-medium text-gray-700 mb-2">
Coordinates
{t('projects.coordinates')}
</label>
<Input
type="text"
name="coordinates"
value={form.coordinates || ""}
onChange={handleChange}
placeholder="e.g., 49.622958,20.629562"
placeholder={t('projects.placeholders.coordinates')}
/>
</div>
<div className="md:col-span-2">
<label className="block text-sm font-medium text-gray-700 mb-2">
Notes
{t('projects.notes')}
</label>
<textarea
name="notes"
@@ -350,7 +352,7 @@ export default function ProjectForm({ initialData = null }) {
onChange={handleChange}
rows={4}
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
placeholder="Enter any additional notes"
placeholder={t('projects.placeholders.notes')}
/>
</div>
</div>
@@ -364,7 +366,7 @@ export default function ProjectForm({ initialData = null }) {
onClick={() => router.back()}
disabled={loading}
>
Cancel
{t('common.cancel')}
</Button>
<Button type="submit" variant="primary" disabled={loading}>
{loading ? (
@@ -389,7 +391,7 @@ export default function ProjectForm({ initialData = null }) {
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></path>
</svg>
{isEdit ? "Updating..." : "Creating..."}
{isEdit ? t('projects.updating') : t('projects.creating')}
</>
) : (
<>
@@ -408,7 +410,7 @@ export default function ProjectForm({ initialData = null }) {
d="M5 13l4 4L19 7"
/>
</svg>
Update Project
{t('projects.updateProject')}
</>
) : (
<>
@@ -425,7 +427,7 @@ export default function ProjectForm({ initialData = null }) {
d="M12 4v16m8-8H4"
/>
</svg>
Create Project
{t('projects.createProject')}
</>
)}
</>

View File

@@ -3,12 +3,14 @@
import { useState, useEffect, useRef } from "react";
import { createPortal } from "react-dom";
import Badge from "@/components/ui/Badge";
import { useTranslation } from "@/lib/i18n";
export default function ProjectStatusDropdown({
project,
size = "md",
showDropdown = true,
}) {
const { t } = useTranslation();
const [status, setStatus] = useState(project.project_status);
const [loading, setLoading] = useState(false);
const [isOpen, setIsOpen] = useState(false);
@@ -21,19 +23,19 @@ export default function ProjectStatusDropdown({
const statusConfig = {
registered: {
label: "Registered",
label: t("projectStatus.registered"),
variant: "secondary",
},
in_progress_design: {
label: "In Progress (Design)",
label: t("projectStatus.in_progress_design"),
variant: "primary",
},
in_progress_construction: {
label: "In Progress (Construction)",
label: t("projectStatus.in_progress_construction"),
variant: "primary",
},
fulfilled: {
label: "Completed",
label: t("projectStatus.fulfilled"),
variant: "success",
},
};

View File

@@ -7,8 +7,10 @@ import { Card, CardHeader, CardContent } from "./ui/Card";
import Button from "./ui/Button";
import Badge from "./ui/Badge";
import { formatDate } from "@/lib/utils";
import { useTranslation } from "@/lib/i18n";
export default function ProjectTasksSection({ projectId }) {
const { t } = useTranslation();
const [projectTasks, setProjectTasks] = useState([]);
const [loading, setLoading] = useState(true);
const [taskNotes, setTaskNotes] = useState({});
@@ -180,14 +182,14 @@ export default function ProjectTasksSection({ projectId }) {
if (res.ok) {
refetchTasks(); // Refresh the list
} else {
alert("Failed to update task status");
alert(t("errors.generic"));
}
} catch (error) {
alert("Error updating task status");
alert(t("errors.generic"));
}
};
const handleDeleteTask = async (taskId) => {
if (!confirm("Are you sure you want to delete this task?")) return;
if (!confirm(t("common.deleteConfirm"))) return;
try {
const res = await fetch(`/api/project-tasks/${taskId}`, {
method: "DELETE",
@@ -197,7 +199,7 @@ export default function ProjectTasksSection({ projectId }) {
refetchTasks(); // Refresh the list
} else {
const errorData = await res.json();
alert("Failed to delete task: " + (errorData.error || "Unknown error"));
alert(t("errors.generic") + ": " + (errorData.error || t("errors.unknown")));
}
} catch (error) {
alert("Error deleting task: " + error.message);
@@ -227,10 +229,10 @@ export default function ProjectTasksSection({ projectId }) {
setTaskNotes((prev) => ({ ...prev, [taskId]: notes }));
setNewNote((prev) => ({ ...prev, [taskId]: "" }));
} else {
alert("Failed to add note");
alert(t("errors.generic"));
}
} catch (error) {
alert("Error adding note");
alert(t("errors.generic"));
} finally {
setLoadingNotes((prev) => ({ ...prev, [taskId]: false }));
}
@@ -278,10 +280,10 @@ export default function ProjectTasksSection({ projectId }) {
refetchTasks();
handleCloseEditModal();
} else {
alert("Failed to update task");
alert(t("errors.generic"));
}
} catch (error) {
alert("Error updating task");
alert(t("errors.generic"));
}
};
@@ -327,10 +329,10 @@ export default function ProjectTasksSection({ projectId }) {
return (
<div className="space-y-6">
<div className="flex items-center justify-between">
<h2 className="text-xl font-semibold text-gray-900">Project Tasks</h2>
<h2 className="text-xl font-semibold text-gray-900">{t("tasks.title")}</h2>
<div className="flex items-center gap-3">
<Badge variant="default" className="text-sm">
{projectTasks.length} {projectTasks.length === 1 ? "task" : "tasks"}
{projectTasks.length} {projectTasks.length === 1 ? t("tasks.task") : t("tasks.tasks")}
</Badge>
<Button
variant="primary"
@@ -350,7 +352,7 @@ export default function ProjectTasksSection({ projectId }) {
d="M12 4v16m8-8H4"
/>
</svg>
Add Task
{t("tasks.newTask")}
</Button>
</div>
</div>{" "}
@@ -404,7 +406,7 @@ export default function ProjectTasksSection({ projectId }) {
{/* Current Tasks */}
<Card>
<CardHeader>
<h3 className="text-lg font-medium text-gray-900">Current Tasks</h3>
<h3 className="text-lg font-medium text-gray-900">{t("tasks.title")}</h3>
</CardHeader>
<CardContent className="p-0">
{loading ? (
@@ -430,10 +432,10 @@ export default function ProjectTasksSection({ projectId }) {
</svg>
</div>
<p className="text-gray-500 text-sm">
No tasks assigned to this project yet.
{t("tasks.noTasksMessage")}
</p>
<p className="text-gray-400 text-xs mt-1">
Add a task above to get started.
{t("tasks.addTaskMessage")}
</p>
</div>
) : (
@@ -442,22 +444,22 @@ export default function ProjectTasksSection({ projectId }) {
<thead className="bg-gray-50 border-b border-gray-200">
<tr>
<th className="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Task
{t("tasks.taskName")}
</th>
<th className="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Priority
{t("tasks.priority")}
</th>{" "}
<th className="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Max Wait
{t("tasks.maxWait")}
</th>
<th className="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Date Started
{t("tasks.dateStarted")}
</th>
<th className="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Status
{t("tasks.status")}
</th>
<th className="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider w-48">
Actions
{t("tasks.actions")}
</th>
</tr>
</thead>
@@ -503,16 +505,16 @@ export default function ProjectTasksSection({ projectId }) {
variant={getPriorityVariant(task.priority)}
size="sm"
>
{task.priority}
{t(`tasks.${task.priority}`)}
</Badge>
</td>
<td className="px-4 py-4 text-sm text-gray-600">
{task.max_wait_days} days
{task.max_wait_days} {t("tasks.days")}
</td>{" "}
<td className="px-4 py-4 text-sm text-gray-600">
{task.date_started
? formatDate(task.date_started)
: "Not started"}
: t("tasks.notStarted")}
</td>
<td className="px-4 py-4">
<TaskStatusDropdownSimple
@@ -564,7 +566,7 @@ export default function ProjectTasksSection({ projectId }) {
d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"
/>
</svg>
Edit
{t("common.edit")}
</Button>
<Button
variant="danger"
@@ -585,7 +587,7 @@ export default function ProjectTasksSection({ projectId }) {
d="19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
/>
</svg>
Delete
{t("common.delete")}
</Button>
</div>
</td>
@@ -596,7 +598,7 @@ export default function ProjectTasksSection({ projectId }) {
<td colSpan="6" className="px-4 py-3">
<div className="text-sm text-gray-700">
<span className="font-medium text-gray-900">
Description:
{t("tasks.description")}:
</span>
<p className="mt-1">{task.description}</p>
</div>
@@ -609,7 +611,7 @@ export default function ProjectTasksSection({ projectId }) {
<td colSpan="6" className="px-4 py-4">
<div className="space-y-3">
<h5 className="text-sm font-medium text-gray-900">
Notes ({taskNotes[task.id]?.length || 0})
{t("tasks.comments")} ({taskNotes[task.id]?.length || 0})
</h5>
{/* Existing Notes */}
@@ -629,7 +631,7 @@ export default function ProjectTasksSection({ projectId }) {
<div className="flex items-center gap-2 mb-1">
{note.is_system ? (
<span className="px-2 py-1 text-xs bg-blue-100 text-blue-700 rounded-full font-medium">
System
{t("admin.system")}
</span>
) : null}
{note.created_by_name && (
@@ -670,7 +672,7 @@ export default function ProjectTasksSection({ projectId }) {
<div className="flex gap-2">
<input
type="text"
placeholder="Add a note..."
placeholder={t("tasks.addComment")}
value={newNote[task.id] || ""}
onChange={(e) =>
setNewNote((prev) => ({
@@ -694,7 +696,7 @@ export default function ProjectTasksSection({ projectId }) {
!newNote[task.id]?.trim()
}
>
{loadingNotes[task.id] ? "Adding..." : "Add"}
{loadingNotes[task.id] ? t("common.saving") : t("common.add")}
</Button>
</div>
</div>
@@ -715,7 +717,7 @@ export default function ProjectTasksSection({ projectId }) {
<div className="bg-white rounded-lg p-6 w-full max-w-md mx-4">
<div className="flex items-center justify-between mb-4">
<h3 className="text-lg font-semibold text-gray-900">
Edit Task:{" "}
{t("tasks.editTask")}:{" "}
{editingTask?.custom_task_name || editingTask?.task_name}
</h3>
<button
@@ -740,7 +742,7 @@ export default function ProjectTasksSection({ projectId }) {
{/* Assignment */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Assigned To
{t("tasks.assignedTo")}
</label>
<select
value={editTaskForm.assigned_to}
@@ -752,7 +754,7 @@ export default function ProjectTasksSection({ projectId }) {
}
className="w-full px-3 py-2 border border-gray-300 rounded-md 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}
@@ -764,7 +766,7 @@ export default function ProjectTasksSection({ projectId }) {
{/* Priority */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Priority
{t("tasks.priority")}
</label>
<select
value={editTaskForm.priority}
@@ -776,18 +778,18 @@ export default function ProjectTasksSection({ projectId }) {
}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
>
<option value="">Select Priority</option>
<option value="low">Low</option>
<option value="normal">Normal</option>
<option value="high">High</option>
<option value="urgent">Urgent</option>
<option value="">{t("common.selectOption")}</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>
{/* Date Started */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Date Started
{t("tasks.dateStarted")}
</label>
<input
type="date"
@@ -805,7 +807,7 @@ export default function ProjectTasksSection({ projectId }) {
{/* Status */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Status
{t("tasks.status")}
</label>
<select
value={editTaskForm.status}
@@ -817,21 +819,21 @@ export default function ProjectTasksSection({ projectId }) {
}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
>
<option value="">Select Status</option>
<option value="pending">Pending</option>
<option value="in_progress">In Progress</option>
<option value="completed">Completed</option>
<option value="cancelled">Cancelled</option>
<option value="">{t("common.selectOption")}</option>
<option value="pending">{t("taskStatus.pending")}</option>
<option value="in_progress">{t("taskStatus.in_progress")}</option>
<option value="completed">{t("taskStatus.completed")}</option>
<option value="cancelled">{t("taskStatus.cancelled")}</option>
</select>
</div>
</div>
<div className="flex items-center justify-end gap-3 mt-6">
<Button variant="outline" onClick={handleCloseEditModal}>
Cancel
{t("common.cancel")}
</Button>
<Button variant="primary" onClick={handleUpdateTask}>
Update Task
{t("tasks.updateTask")}
</Button>
</div>
</div>

View File

@@ -3,13 +3,16 @@
import { useState, useRef, useEffect } from "react";
import { createPortal } from "react-dom";
import Badge from "@/components/ui/Badge";
import { useTranslation } from "@/lib/i18n";
export default function TaskStatusDropdown({
task,
size = "sm",
showDropdown = true,
onStatusChange,
}) { const [status, setStatus] = useState(task.status);
}) {
const { t } = useTranslation();
const [status, setStatus] = useState(task.status);
const [loading, setLoading] = useState(false);
const [isOpen, setIsOpen] = useState(false);
const [dropdownPosition, setDropdownPosition] = useState({ x: 0, y: 0, position: 'bottom' });
@@ -23,19 +26,19 @@ export default function TaskStatusDropdown({
const statusConfig = {
pending: {
label: "Pending",
label: t("taskStatus.pending"),
variant: "warning",
},
in_progress: {
label: "In Progress",
label: t("taskStatus.in_progress"),
variant: "primary",
},
completed: {
label: "Completed",
label: t("taskStatus.completed"),
variant: "success",
},
cancelled: {
label: "Cancelled",
label: t("taskStatus.cancelled"),
variant: "danger",
},
};

View File

@@ -0,0 +1,42 @@
"use client";
import { useTranslation } from '@/lib/i18n';
const LanguageSwitcher = ({ className = '' }) => {
const { language, changeLanguage, availableLanguages, t } = useTranslation();
const languageNames = {
pl: 'Polski',
en: 'English'
};
return (
<div className={`relative ${className}`}>
<select
value={language}
onChange={(e) => changeLanguage(e.target.value)}
className="appearance-none bg-white border border-gray-300 rounded-md px-3 py-1 pr-8 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
title={t('common.selectLanguage')}
>
{availableLanguages.map((lang) => (
<option key={lang} value={lang}>
{languageNames[lang] || lang.toUpperCase()}
</option>
))}
</select>
{/* Custom dropdown arrow */}
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-700">
<svg
className="fill-current h-4 w-4"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
>
<path d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" />
</svg>
</div>
</div>
);
};
export default LanguageSwitcher;

View File

@@ -3,10 +3,13 @@
import Link from "next/link";
import { usePathname } from "next/navigation";
import { useSession, signOut } from "next-auth/react";
import { useTranslation } from "@/lib/i18n";
import LanguageSwitcher from "./LanguageSwitcher";
const Navigation = () => {
const pathname = usePathname();
const { data: session, status } = useSession();
const { t } = useTranslation();
const isActive = (path) => {
if (path === "/") return pathname === "/";
@@ -18,16 +21,16 @@ const Navigation = () => {
};
const navItems = [
{ href: "/", label: "Dashboard" },
{ href: "/projects", label: "Projects" },
{ href: "/tasks/templates", label: "Task Templates" },
{ href: "/project-tasks", label: "Project Tasks" },
{ href: "/contracts", label: "Contracts" },
{ href: "/", label: t('navigation.dashboard') },
{ href: "/projects", label: t('navigation.projects') },
{ href: "/tasks/templates", label: t('navigation.taskTemplates') },
{ href: "/project-tasks", label: t('navigation.projectTasks') },
{ href: "/contracts", label: t('navigation.contracts') },
];
// Add admin-only items
if (session?.user?.role === 'admin') {
navItems.push({ href: "/admin/users", label: "User Management" });
navItems.push({ href: "/admin/users", label: t('navigation.userManagement') });
}
const handleSignOut = async () => {
@@ -45,13 +48,13 @@ const Navigation = () => {
<div className="flex items-center justify-between h-16">
<div className="flex items-center">
<Link href="/" className="text-xl font-bold text-gray-900">
Project Panel
{t('navigation.projectPanel')}
</Link>
</div>
<div className="flex items-center space-x-4">
{status === "loading" ? (
<div className="text-gray-500">Loading...</div>
<div className="text-gray-500">{t('navigation.loading')}</div>
) : session ? (
<>
<div className="flex space-x-8">
@@ -71,10 +74,12 @@ const Navigation = () => {
</div>
<div className="flex items-center space-x-4 ml-8 pl-8 border-l border-gray-200">
<LanguageSwitcher />
<div className="flex items-center space-x-2">
<div className="text-sm">
<div className="font-medium text-gray-900">{session.user.name}</div>
<div className="text-gray-500 capitalize">{session.user.role?.replace('_', ' ')}</div>
<div className="text-gray-500 capitalize">{t(`userRoles.${session.user.role}`) || session.user.role?.replace('_', ' ')}</div>
</div>
</div>
@@ -82,17 +87,20 @@ const Navigation = () => {
onClick={handleSignOut}
className="bg-gray-100 hover:bg-gray-200 text-gray-700 px-3 py-2 rounded-md text-sm font-medium transition-colors"
>
Sign Out
{t('navigation.signOut')}
</button>
</div>
</>
) : (
<Link
href="/auth/signin"
className="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-md text-sm font-medium transition-colors"
>
Sign In
</Link>
<>
<LanguageSwitcher />
<Link
href="/auth/signin"
className="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-md text-sm font-medium transition-colors"
>
{t('navigation.signIn')}
</Link>
</>
)}
</div>
</div>