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

@@ -12,8 +12,10 @@ import PageContainer from "@/components/ui/PageContainer";
import PageHeader from "@/components/ui/PageHeader";
import { LoadingState } from "@/components/ui/States";
import { formatDate } from "@/lib/utils";
import { useTranslation } from "@/lib/i18n";
export default function UserManagementPage() {
const { t } = useTranslation();
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState("");
@@ -54,7 +56,7 @@ export default function UserManagementPage() {
};
const handleDeleteUser = async (userId) => {
if (!confirm("Are you sure you want to delete this user?")) return;
if (!confirm(t('admin.deleteUser') + "?")) return;
try {
const response = await fetch(`/api/admin/users/${userId}`, {
@@ -141,7 +143,7 @@ export default function UserManagementPage() {
return (
<PageContainer>
<PageHeader title="User Management" description="Manage system users and permissions">
<PageHeader title={t('admin.userManagement')} description={t('admin.subtitle')}>
<Button
variant="primary"
onClick={() => setShowCreateForm(true)}
@@ -149,7 +151,7 @@ export default function UserManagementPage() {
<svg className="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
</svg>
Add User
{t('admin.newUser')}
</Button>
</PageHeader>

View File

@@ -11,8 +11,10 @@ import SearchBar from "@/components/ui/SearchBar";
import FilterBar from "@/components/ui/FilterBar";
import { LoadingState } from "@/components/ui/States";
import { formatDate } from "@/lib/utils";
import { useTranslation } from "@/lib/i18n";
export default function ContractsMainPage() {
const { t } = useTranslation();
const [contracts, setContracts] = useState([]);
const [loading, setLoading] = useState(true);
const [searchTerm, setSearchTerm] = useState("");
@@ -133,13 +135,13 @@ export default function ContractsMainPage() {
const getStatusBadge = (status) => {
switch (status) {
case "active":
return <Badge variant="success">Aktywna</Badge>;
return <Badge variant="success">{t('contracts.active')}</Badge>;
case "completed":
return <Badge variant="secondary">Zakończona</Badge>;
return <Badge variant="secondary">{t('common.completed')}</Badge>;
case "ongoing":
return <Badge variant="primary">W trakcie</Badge>;
return <Badge variant="primary">{t('contracts.withoutEndDate')}</Badge>;
default:
return <Badge>Nieznany</Badge>;
return <Badge>{t('common.unknown')}</Badge>;
}
};
@@ -170,17 +172,17 @@ export default function ContractsMainPage() {
return (
<PageContainer>
<PageHeader
title="Umowy"
description="Zarządzaj swoimi umowami i kontraktami"
title={t('contracts.title')}
description={t('contracts.subtitle')}
>
<Link href="/contracts/new">
<Button variant="primary" size="lg">
<span className="mr-2"></span>
Nowa umowa
{t('contracts.newContract')}
</Button>
</Link>
</PageHeader>
<LoadingState message="Ładowanie umów..." />
<LoadingState message={t('navigation.loading')} />
</PageContainer>
);
}
@@ -225,13 +227,13 @@ export default function ContractsMainPage() {
return (
<PageContainer>
<PageHeader
title="Umowy"
description="Zarządzaj swoimi umowami i kontraktami"
title={t('contracts.title')}
description={t('contracts.subtitle')}
>
<Link href="/contracts/new">
<Button variant="primary" size="lg">
<span className="mr-2"></span>
Nowa umowa
{t('contracts.newContract')}
</Button>
</Link>{" "}
</PageHeader>

View File

@@ -2,6 +2,7 @@ import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import Navigation from "@/components/ui/Navigation";
import { AuthProvider } from "@/components/auth/AuthProvider";
import { TranslationProvider } from "@/lib/i18n";
const geistSans = Geist({
variable: "--font-geist-sans",
@@ -14,20 +15,22 @@ const geistMono = Geist_Mono({
});
export const metadata = {
title: "Project Management Panel",
description: "Professional project management dashboard",
title: "Panel Zarządzania Projektami",
description: "Profesjonalny panel zarządzania projektami",
};
export default function RootLayout({ children }) {
return (
<html lang="en">
<html lang="pl">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
<AuthProvider>
<Navigation />
<main>{children}</main>
</AuthProvider>
<TranslationProvider initialLanguage="pl">
<AuthProvider>
<Navigation />
<main>{children}</main>
</AuthProvider>
</TranslationProvider>
</body>
</html>
);

View File

@@ -10,6 +10,7 @@ import TaskStatusDropdownSimple from "@/components/TaskStatusDropdownSimple";
import PageContainer from "@/components/ui/PageContainer";
import PageHeader from "@/components/ui/PageHeader";
import { LoadingState } from "@/components/ui/States";
import { useTranslation } from "@/lib/i18n";
import {
differenceInCalendarDays,
parseISO,
@@ -26,6 +27,7 @@ import TaskStatusChart from "@/components/ui/TaskStatusChart";
export default function Home() {
const { data: session, status } = useSession();
const { t } = useTranslation();
const [stats, setStats] = useState({
totalProjects: 0,
activeProjects: 0,
@@ -258,17 +260,17 @@ export default function Home() {
return (
<PageContainer>
<PageHeader
title="Dashboard"
description="Overview of your projects, contracts, and tasks"
title={t('dashboard.title')}
description={t('dashboard.subtitle')}
/>
<LoadingState message="Loading dashboard data..." />
<LoadingState message={t('common.loading')} />
</PageContainer>
);
}
// Show loading state while session is being fetched
if (status === "loading") {
return <LoadingState message="Loading authentication..." />;
return <LoadingState message={t('navigation.loading')} />;
}
// Show sign-in prompt if not authenticated
@@ -277,16 +279,16 @@ export default function Home() {
<PageContainer>
<div className="text-center py-12">
<h1 className="text-4xl font-bold text-gray-900 mb-6">
Welcome to Project Management Panel
Witamy w Panelu Zarządzania Projektami
</h1>
<p className="text-xl text-gray-600 mb-8">
Please sign in to access the project management system.
Zaloguj się, aby uzyskać dostęp do systemu zarządzania projektami.
</p>
<Link
href="/auth/signin"
className="inline-flex items-center px-6 py-3 border border-transparent text-base font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700"
>
Sign In
{t('auth.signIn')}
</Link>
</div>
</PageContainer>
@@ -296,8 +298,8 @@ export default function Home() {
return (
<PageContainer>
<PageHeader
title={`Welcome back, ${session.user.name}!`}
description="Overview of your projects, contracts, and tasks"
title={`Witaj ponownie, ${session.user.name}!`}
description={t('dashboard.subtitle')}
>
<div className="flex items-center gap-3">
<Link href="/projects/new">
@@ -315,7 +317,7 @@ export default function Home() {
d="M12 4v16m8-8H4"
/>
</svg>
New Project
{t('projects.newProject')}
</Button>
</Link>
<Link href="/contracts/new">
@@ -333,7 +335,7 @@ export default function Home() {
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
/>
</svg>
New Contract
{t('contracts.newContract')}
</Button>
</Link>
</div>
@@ -347,13 +349,13 @@ export default function Home() {
<div className="flex items-center justify-between">
<div>
<p className="text-xs font-medium text-gray-600 uppercase tracking-wide">
Total Projects
{t('dashboard.totalProjects')}
</p>
<p className="text-2xl font-bold text-gray-900">
{stats.totalProjects}
</p>
<p className="text-xs text-green-600 mt-1">
{stats.activeProjects} active
{stats.activeProjects} {t('dashboard.activeProjects').toLowerCase()}
</p>
</div>
<div className="w-10 h-10 bg-blue-100 rounded-lg flex items-center justify-center">
@@ -380,13 +382,13 @@ export default function Home() {
<div className="flex items-center justify-between">
<div>
<p className="text-xs font-medium text-gray-600 uppercase tracking-wide">
Contracts
{t('dashboard.totalContracts')}
</p>
<p className="text-2xl font-bold text-gray-900">
{stats.totalContracts}
</p>
<p className="text-xs text-green-600 mt-1">
{stats.activeContracts} active
{stats.activeContracts} {t('dashboard.activeContracts').toLowerCase()}
</p>
</div>
<div className="w-10 h-10 bg-green-100 rounded-lg flex items-center justify-center">
@@ -413,12 +415,12 @@ export default function Home() {
<div className="flex items-center justify-between">
<div>
<p className="text-xs font-medium text-gray-600 uppercase tracking-wide">
Pending Tasks
{t('dashboard.pendingTasks')}
</p>
<p className="text-2xl font-bold text-gray-900">
{stats.pendingTasks}
</p>
<p className="text-xs text-gray-500 mt-1">Awaiting start</p>
<p className="text-xs text-gray-500 mt-1">Oczekują rozpoczęcia</p>
</div>
<div className="w-10 h-10 bg-yellow-100 rounded-lg flex items-center justify-center">
<svg
@@ -444,12 +446,12 @@ export default function Home() {
<div className="flex items-center justify-between">
<div>
<p className="text-xs font-medium text-gray-600 uppercase tracking-wide">
In Progress
{t('dashboard.inProgressTasks')}
</p>
<p className="text-2xl font-bold text-gray-900">
{stats.inProgressTasks}
</p>
<p className="text-xs text-blue-600 mt-1">Active tasks</p>
<p className="text-xs text-blue-600 mt-1">Aktywne zadania</p>
</div>
<div className="w-10 h-10 bg-blue-100 rounded-lg flex items-center justify-center">
<svg
@@ -475,12 +477,12 @@ export default function Home() {
<div className="flex items-center justify-between">
<div>
<p className="text-xs font-medium text-gray-600 uppercase tracking-wide">
Completed
{t('dashboard.completedTasks')}
</p>
<p className="text-2xl font-bold text-gray-900">
{stats.completedTasks}
</p>
<p className="text-xs text-green-600 mt-1">Tasks done</p>
<p className="text-xs text-green-600 mt-1">Zadania wykonane</p>
</div>
<div className="w-10 h-10 bg-green-100 rounded-lg flex items-center justify-center">
<svg

View File

@@ -25,9 +25,9 @@ export default async function ProjectViewPage({ params }) {
<PageContainer>
<Card>
<CardContent className="text-center py-8">
<p className="text-red-600 text-lg">Project not found.</p>
<p className="text-red-600 text-lg">Projekt nie został znaleziony.</p>
<Link href="/projects" className="mt-4 inline-block">
<Button variant="primary">Back to Projects</Button>
<Button variant="primary">Powrót do projektów</Button>
</Link>
</CardContent>
</Card>
@@ -55,10 +55,10 @@ export default async function ProjectViewPage({ params }) {
{daysRemaining !== null && (
<Badge variant={getDeadlineVariant(daysRemaining)} size="md">
{daysRemaining === 0
? "Due Today"
? "Termin dzisiaj"
: daysRemaining > 0
? `${daysRemaining} days left`
: `${Math.abs(daysRemaining)} days overdue`}
? `${daysRemaining} dni pozostało`
: `${Math.abs(daysRemaining)} dni po terminie`}
</Badge>
)}
<Link href="/projects">
@@ -76,7 +76,7 @@ export default async function ProjectViewPage({ params }) {
d="M15 19l-7-7 7-7"
/>
</svg>
Back to Projects
Powrót do projektów
</Button>
</Link>
<Link href={`/projects/${id}/edit`}>
@@ -94,7 +94,7 @@ export default async function ProjectViewPage({ params }) {
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 Project
Edytuj projekt
</Button>
</Link>
</div>
@@ -108,7 +108,7 @@ export default async function ProjectViewPage({ params }) {
{" "}
<div className="flex items-center justify-between">
<h2 className="text-xl font-semibold text-gray-900">
Project Information
Informacje o projekcie
</h2>
<Badge
variant={
@@ -123,12 +123,12 @@ export default async function ProjectViewPage({ params }) {
size="sm"
>
{project.project_type === "design"
? "Design (P)"
? "Projektowanie (P)"
: project.project_type === "construction"
? "Construction (R)"
? "Realizacja (R)"
: project.project_type === "design+construction"
? "Design + Construction (P+R)"
: "Unknown"}
? "Projektowanie + Realizacja (P+R)"
: "Nieznany"}
</Badge>
</div>
</CardHeader>
@@ -136,7 +136,7 @@ export default async function ProjectViewPage({ params }) {
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<span className="text-sm font-medium text-gray-500 block mb-1">
Location
Lokalizacja
</span>
<p className="text-gray-900 font-medium">
{project.city || "N/A"}
@@ -144,7 +144,7 @@ export default async function ProjectViewPage({ params }) {
</div>
<div>
<span className="text-sm font-medium text-gray-500 block mb-1">
Address
Adres
</span>
<p className="text-gray-900 font-medium">
{project.address || "N/A"}
@@ -152,7 +152,7 @@ export default async function ProjectViewPage({ params }) {
</div>
<div>
<span className="text-sm font-medium text-gray-500 block mb-1">
Plot
Działka
</span>
<p className="text-gray-900 font-medium">
{project.plot || "N/A"}
@@ -160,7 +160,7 @@ export default async function ProjectViewPage({ params }) {
</div>
<div>
<span className="text-sm font-medium text-gray-500 block mb-1">
District
Dzielnica
</span>
<p className="text-gray-900 font-medium">
{project.district || "N/A"}
@@ -168,7 +168,7 @@ export default async function ProjectViewPage({ params }) {
</div>
<div>
<span className="text-sm font-medium text-gray-500 block mb-1">
Unit
Jednostka
</span>
<p className="text-gray-900 font-medium">
{project.unit || "N/A"}
@@ -176,7 +176,7 @@ export default async function ProjectViewPage({ params }) {
</div>{" "}
<div>
<span className="text-sm font-medium text-gray-500 block mb-1">
Deadline
Termin zakończenia
</span>
<p className="text-gray-900 font-medium">
{project.finish_date
@@ -194,7 +194,7 @@ export default async function ProjectViewPage({ params }) {
</div>
<div>
<span className="text-sm font-medium text-gray-500 block mb-1">
Investment Number
Numer inwestycji
</span>
<p className="text-gray-900 font-medium">
{project.investment_number || "N/A"}
@@ -205,7 +205,7 @@ export default async function ProjectViewPage({ params }) {
{project.contact && (
<div className="border-t pt-4">
<span className="text-sm font-medium text-gray-500 block mb-1">
Contact
Kontakt
</span>
<p className="text-gray-900 font-medium">{project.contact}</p>
</div>
@@ -214,7 +214,7 @@ export default async function ProjectViewPage({ params }) {
{project.coordinates && (
<div className="border-t pt-4">
<span className="text-sm font-medium text-gray-500 block mb-1">
Coordinates
Współrzędne
</span>
<p className="text-gray-900 font-medium font-mono text-sm">
{formatCoordinates(project.coordinates)}
@@ -237,14 +237,14 @@ export default async function ProjectViewPage({ params }) {
<Card>
<CardHeader>
<h2 className="text-xl font-semibold text-gray-900">
Contract Details
Szczegóły umowy
</h2>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<span className="text-sm font-medium text-gray-500 block mb-1">
Contract Number
Numer umowy
</span>
<p className="text-gray-900 font-medium">
{project.contract_number || "N/A"}
@@ -252,7 +252,7 @@ export default async function ProjectViewPage({ params }) {
</div>
<div>
<span className="text-sm font-medium text-gray-500 block mb-1">
Contract Name
Nazwa umowy
</span>
<p className="text-gray-900 font-medium">
{project.contract_name || "N/A"}
@@ -260,7 +260,7 @@ export default async function ProjectViewPage({ params }) {
</div>
<div>
<span className="text-sm font-medium text-gray-500 block mb-1">
Customer
Klient
</span>
<p className="text-gray-900 font-medium">
{project.customer || "N/A"}
@@ -268,7 +268,7 @@ export default async function ProjectViewPage({ params }) {
</div>
<div>
<span className="text-sm font-medium text-gray-500 block mb-1">
Investor
Inwestor
</span>
<p className="text-gray-900 font-medium">
{project.investor || "N/A"}
@@ -284,21 +284,21 @@ export default async function ProjectViewPage({ params }) {
<Card>
<CardHeader>
<h2 className="text-lg font-semibold text-gray-900">
Project Status
Status projektu
</h2>
</CardHeader>
<CardContent className="space-y-4">
{" "}
<div>
<span className="text-sm font-medium text-gray-500 block mb-2">
Current Status
Aktualny status
</span>
<ProjectStatusDropdown project={project} size="md" />
</div>
{daysRemaining !== null && (
<div className="border-t pt-4">
<span className="text-sm font-medium text-gray-500 block mb-2">
Timeline
Harmonogram
</span>
<div className="text-center">
<Badge
@@ -306,10 +306,10 @@ export default async function ProjectViewPage({ params }) {
size="lg"
>
{daysRemaining === 0
? "Due Today"
? "Termin dzisiaj"
: daysRemaining > 0
? `${daysRemaining} days remaining`
: `${Math.abs(daysRemaining)} days overdue`}
? `${daysRemaining} dni pozostało`
: `${Math.abs(daysRemaining)} dni po terminie`}
</Badge>
</div>
</div>
@@ -321,7 +321,7 @@ export default async function ProjectViewPage({ params }) {
<Card>
<CardHeader>
<h2 className="text-lg font-semibold text-gray-900">
Quick Actions
Szybkie akcje
</h2>
</CardHeader>
<CardContent className="space-y-3">
@@ -344,7 +344,7 @@ export default async function ProjectViewPage({ params }) {
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 Project
Edytuj projekt
</Button>
</Link>{" "}
<Link href="/projects" className="block">
@@ -366,7 +366,7 @@ export default async function ProjectViewPage({ params }) {
d="M15 19l-7-7 7-7"
/>
</svg>
Back to Projects
Powrót do projektów
</Button>
</Link>
<Link href="/projects/map" className="block">
@@ -388,7 +388,7 @@ export default async function ProjectViewPage({ params }) {
d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-1.447-.894L15 4m0 13V4m0 0L9 7"
/>
</svg>
View All on Map
Zobacz wszystkie na mapie
</Button>
</Link>
</CardContent>
@@ -404,7 +404,7 @@ export default async function ProjectViewPage({ params }) {
{" "}
<div className="flex items-center justify-between">
<h2 className="text-xl font-semibold text-gray-900">
Project Location
Lokalizacja projektu
</h2>
{project.coordinates && (
<Link
@@ -428,7 +428,7 @@ export default async function ProjectViewPage({ params }) {
d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-1.447-.894L15 4m0 13V4m0 0L9 7"
/>
</svg>
View on Full Map
Zobacz na pełnej mapie
</Button>
</Link>
)}
@@ -454,7 +454,7 @@ export default async function ProjectViewPage({ params }) {
{/* Notes Section */}
<Card>
<CardHeader>
<h2 className="text-xl font-semibold text-gray-900">Notes</h2>
<h2 className="text-xl font-semibold text-gray-900">Notatki</h2>
</CardHeader>
<CardContent>
<div className="mb-6">
@@ -475,10 +475,10 @@ export default async function ProjectViewPage({ params }) {
</svg>
</div>
<h3 className="text-lg font-medium text-gray-900 mb-2">
No notes yet
Brak notatek
</h3>
<p className="text-gray-500">
Add your first note using the form above.
Dodaj swoją pierwszą notatkę używając formularza powyżej.
</p>
</div>
) : (

View File

@@ -11,8 +11,10 @@ import PageHeader from "@/components/ui/PageHeader";
import SearchBar from "@/components/ui/SearchBar";
import { LoadingState } from "@/components/ui/States";
import { formatDate } from "@/lib/utils";
import { useTranslation } from "@/lib/i18n";
export default function ProjectListPage() {
const { t } = useTranslation();
const [projects, setProjects] = useState([]);
const [searchTerm, setSearchTerm] = useState("");
const [filteredProjects, setFilteredProjects] = useState([]);
@@ -45,7 +47,7 @@ export default function ProjectListPage() {
}, [searchTerm, projects]);
async function handleDelete(id) {
const confirmed = confirm("Are you sure you want to delete this project?");
const confirmed = confirm(t('projects.deleteConfirm'));
if (!confirmed) return;
const res = await fetch(`/api/projects/${id}`, {
@@ -61,7 +63,7 @@ export default function ProjectListPage() {
};
return (
<PageContainer>
<PageHeader title="Projects" description="Manage and track your projects">
<PageHeader title={t('projects.title')} description={t('projects.subtitle')}>
<div className="flex gap-2">
<Link href="/projects/map">
<Button variant="outline" size="lg">
@@ -78,7 +80,7 @@ export default function ProjectListPage() {
d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4m0 0L9 7"
/>
</svg>
Map View
Widok mapy
</Button>
</Link>
<Link href="/projects/new">
@@ -96,7 +98,7 @@ export default function ProjectListPage() {
d="M12 4v16m8-8H4"
/>
</svg>
Add Project
{t('projects.newProject')}
</Button>
</Link>
</div>
@@ -105,9 +107,9 @@ export default function ProjectListPage() {
<SearchBar
searchTerm={searchTerm}
onSearchChange={handleSearchChange}
placeholder="Search by project name, WP, plot, or investment number..."
placeholder={t('projects.searchPlaceholder')}
resultsCount={filteredProjects.length}
resultsText="projects"
resultsText="projektów"
/>
{filteredProjects.length === 0 && searchTerm ? (
<Card>
@@ -126,14 +128,13 @@ export default function ProjectListPage() {
</svg>
</div>
<h3 className="text-lg font-medium text-gray-900 mb-2">
No projects found
{t('common.noResults')}
</h3>
<p className="text-gray-500 mb-6">
No projects match your search criteria. Try adjusting your search
terms.
Brak projektów pasujących do kryteriów wyszukiwania. Spróbuj zmienić wyszukiwane frazy.
</p>
<Button variant="outline" onClick={() => setSearchTerm("")}>
Clear Search
Wyczyść wyszukiwanie
</Button>
</CardContent>
</Card>
@@ -154,13 +155,13 @@ export default function ProjectListPage() {
</svg>
</div>
<h3 className="text-lg font-medium text-gray-900 mb-2">
No projects yet
{t('projects.noProjects')}
</h3>
<p className="text-gray-500 mb-6">
Get started by creating your first project
{t('projects.noProjectsMessage')}
</p>
<Link href="/projects/new">
<Button variant="primary">Create First Project</Button>
<Button variant="primary">Utwórz pierwszy projekt</Button>
</Link>
</CardContent>
</Card>
@@ -170,34 +171,34 @@ export default function ProjectListPage() {
<thead>
<tr className="bg-gray-100 border-b">
<th className="text-left px-2 py-3 font-semibold text-xs text-gray-700 w-32">
No.
Nr.
</th>
<th className="text-left px-2 py-3 font-semibold text-xs text-gray-700">
Project Name
{t('projects.projectName')}
</th>
<th className="text-left px-2 py-3 font-semibold text-xs text-gray-700 w-40">
WP
</th>
<th className="text-left px-2 py-3 font-semibold text-xs text-gray-700 w-20">
City
{t('projects.city')}
</th>
<th className="text-left px-2 py-3 font-semibold text-xs text-gray-700 w-40">
Address
{t('projects.address')}
</th>
<th className="text-left px-2 py-3 font-semibold text-xs text-gray-700 w-20">
Plot
{t('projects.plot')}
</th>
<th className="text-left px-2 py-3 font-semibold text-xs text-gray-700 w-24">
Finish
{t('projects.finishDate')}
</th>
<th className="text-left px-2 py-3 font-semibold text-xs text-gray-700 w-12">
Type
Typ
</th>
<th className="text-left px-2 py-3 font-semibold text-xs text-gray-700 w-24">
Status
</th>
<th className="text-left px-2 py-3 font-semibold text-xs text-gray-700 w-20">
Actions
Akcje
</th>
</tr>
</thead>
@@ -282,7 +283,7 @@ export default function ProjectListPage() {
size="sm"
className="text-xs px-2 py-1"
>
View
Wyświetl
</Button>
</Link>
</td>

View File

@@ -14,8 +14,10 @@ import PageHeader from "@/components/ui/PageHeader";
import SearchBar from "@/components/ui/SearchBar";
import FilterBar from "@/components/ui/FilterBar";
import { LoadingState } from "@/components/ui/States";
import { useTranslation } from "@/lib/i18n";
export default function ProjectTasksPage() {
const { t } = useTranslation();
const [allTasks, setAllTasks] = useState([]);
const [filteredTasks, setFilteredTasks] = useState([]);
const [searchTerm, setSearchTerm] = useState("");
@@ -148,25 +150,25 @@ export default function ProjectTasksPage() {
const filterOptions = [
{
label: "Status",
label: t('tasks.status'),
value: statusFilter,
onChange: (e) => setStatusFilter(e.target.value),
options: [
{ value: "all", label: "All" },
{ value: "pending", label: "Pending" },
{ value: "in_progress", label: "In Progress" },
{ value: "completed", label: "Completed" },
{ value: "all", label: t('common.all') },
{ value: "pending", label: t('taskStatus.pending') },
{ value: "in_progress", label: t('taskStatus.in_progress') },
{ value: "completed", label: t('taskStatus.completed') },
],
},
{
label: "Priority",
label: t('tasks.priority'),
value: priorityFilter,
onChange: (e) => setPriorityFilter(e.target.value),
options: [
{ value: "all", label: "All" },
{ value: "high", label: "High" },
{ value: "normal", label: "Normal" },
{ value: "low", label: "Low" },
{ value: "all", label: t('common.all') },
{ value: "high", label: t('tasks.high') },
{ value: "normal", label: t('tasks.medium') },
{ value: "low", label: t('tasks.low') },
],
},
];
@@ -174,8 +176,8 @@ export default function ProjectTasksPage() {
return (
<PageContainer>
<PageHeader
title="Project Tasks"
description="Monitor and manage tasks across all projects"
title={t('tasks.title')}
description={t('tasks.subtitle')}
/>
<SearchBar
searchTerm={searchTerm}
@@ -206,7 +208,7 @@ export default function ProjectTasksPage() {
</svg>
</div>
<div className="ml-4">
<p className="text-sm font-medium text-gray-600">Total Tasks</p>
<p className="text-sm font-medium text-gray-600">{t('dashboard.totalTasks')}</p>
<p className="text-2xl font-bold text-gray-900">
{statusCounts.all}
</p>
@@ -233,7 +235,7 @@ export default function ProjectTasksPage() {
</svg>
</div>
<div className="ml-4">
<p className="text-sm font-medium text-gray-600">Pending</p>
<p className="text-sm font-medium text-gray-600">{t('taskStatus.pending')}</p>
<p className="text-2xl font-bold text-gray-900">
{statusCounts.pending}
</p>
@@ -260,7 +262,7 @@ export default function ProjectTasksPage() {
</svg>
</div>
<div className="ml-4">
<p className="text-sm font-medium text-gray-600">In Progress</p>
<p className="text-sm font-medium text-gray-600">{t('taskStatus.in_progress')}</p>
<p className="text-2xl font-bold text-gray-900">
{statusCounts.in_progress}
</p>
@@ -287,7 +289,7 @@ export default function ProjectTasksPage() {
</svg>
</div>
<div className="ml-4">
<p className="text-sm font-medium text-gray-600">Completed</p>
<p className="text-sm font-medium text-gray-600">{t('taskStatus.completed')}</p>
<p className="text-2xl font-bold text-gray-900">
{statusCounts.completed}
</p>