feat: Add date formatting utility functions and integrate them across components
This commit is contained in:
@@ -9,6 +9,7 @@ import Badge from "@/components/ui/Badge";
|
||||
import PageContainer from "@/components/ui/PageContainer";
|
||||
import PageHeader from "@/components/ui/PageHeader";
|
||||
import { LoadingState } from "@/components/ui/States";
|
||||
import { formatDate } from "@/lib/utils";
|
||||
|
||||
export default function ContractDetailsPage() {
|
||||
const params = useParams();
|
||||
@@ -135,7 +136,6 @@ export default function ContractDetailsPage() {
|
||||
{contract.contract_number}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{contract.contract_name && (
|
||||
<div>
|
||||
<span className="text-sm font-medium text-gray-500 block mb-1">
|
||||
@@ -146,7 +146,6 @@ export default function ContractDetailsPage() {
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{contract.customer_contract_number && (
|
||||
<div>
|
||||
<span className="text-sm font-medium text-gray-500 block mb-1">
|
||||
@@ -157,7 +156,6 @@ export default function ContractDetailsPage() {
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{contract.customer && (
|
||||
<div>
|
||||
<span className="text-sm font-medium text-gray-500 block mb-1">
|
||||
@@ -168,7 +166,6 @@ export default function ContractDetailsPage() {
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{contract.investor && (
|
||||
<div>
|
||||
<span className="text-sm font-medium text-gray-500 block mb-1">
|
||||
@@ -178,30 +175,24 @@ export default function ContractDetailsPage() {
|
||||
{contract.investor}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
)}{" "}
|
||||
{contract.date_signed && (
|
||||
<div>
|
||||
<span className="text-sm font-medium text-gray-500 block mb-1">
|
||||
Date Signed
|
||||
</span>
|
||||
<p className="text-gray-900 font-medium">
|
||||
{new Date(contract.date_signed).toLocaleDateString(
|
||||
"en-US"
|
||||
)}
|
||||
{formatDate(contract.date_signed)}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{contract.finish_date && (
|
||||
<div>
|
||||
<span className="text-sm font-medium text-gray-500 block mb-1">
|
||||
Finish Date
|
||||
</span>
|
||||
<p className="text-gray-900 font-medium">
|
||||
{new Date(contract.finish_date).toLocaleDateString(
|
||||
"en-US"
|
||||
)}
|
||||
{formatDate(contract.finish_date)}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
@@ -350,10 +341,8 @@ export default function ContractDetailsPage() {
|
||||
d="M6 2a1 1 0 00-1 1v1H4a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-1V3a1 1 0 10-2 0v1H7V3a1 1 0 00-1-1zm0 5a1 1 0 000 2h8a1 1 0 100-2H6z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
{new Date(project.finish_date).toLocaleDateString(
|
||||
"en-US"
|
||||
)}
|
||||
</svg>{" "}
|
||||
{formatDate(project.finish_date)}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center">
|
||||
|
||||
@@ -10,6 +10,7 @@ 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 { formatDate } from "@/lib/utils";
|
||||
|
||||
export default function ContractsMainPage() {
|
||||
const [contracts, setContracts] = useState([]);
|
||||
@@ -485,11 +486,9 @@ export default function ContractsMainPage() {
|
||||
</svg>
|
||||
<span className="font-medium text-gray-700">
|
||||
Zawarcie:
|
||||
</span>
|
||||
</span>{" "}
|
||||
<span className="text-gray-600">
|
||||
{new Date(
|
||||
contract.date_signed
|
||||
).toLocaleDateString("pl-PL")}
|
||||
{formatDate(contract.date_signed)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
@@ -510,11 +509,9 @@ export default function ContractsMainPage() {
|
||||
</svg>
|
||||
<span className="font-medium text-gray-700">
|
||||
Zakończenie:
|
||||
</span>
|
||||
</span>{" "}
|
||||
<span className="text-gray-600">
|
||||
{new Date(
|
||||
contract.finish_date
|
||||
).toLocaleDateString("pl-PL")}
|
||||
{formatDate(contract.finish_date)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
endOfWeek,
|
||||
format,
|
||||
} from "date-fns";
|
||||
import { formatDate } from "@/lib/utils";
|
||||
|
||||
export default function Home() {
|
||||
const [stats, setStats] = useState({
|
||||
@@ -767,7 +768,7 @@ export default function Home() {
|
||||
{deadline.type === "project"
|
||||
? deadline.city
|
||||
: deadline.customer}{" "}
|
||||
• {deadline.date}
|
||||
• {formatDate(deadline.date)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -839,7 +840,7 @@ export default function Home() {
|
||||
</div>
|
||||
{project.finish_date && (
|
||||
<p className="text-xs text-gray-500 mt-1">
|
||||
Due: {project.finish_date}
|
||||
Due: {formatDate(project.finish_date)}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -9,6 +9,7 @@ import Button from "@/components/ui/Button";
|
||||
import Badge from "@/components/ui/Badge";
|
||||
import Link from "next/link";
|
||||
import { differenceInCalendarDays, parseISO } from "date-fns";
|
||||
import { formatDate } from "@/lib/utils";
|
||||
import PageContainer from "@/components/ui/PageContainer";
|
||||
import PageHeader from "@/components/ui/PageHeader";
|
||||
import ProjectStatusDropdown from "@/components/ProjectStatusDropdown";
|
||||
@@ -172,13 +173,15 @@ export default async function ProjectViewPage({ params }) {
|
||||
<p className="text-gray-900 font-medium">
|
||||
{project.unit || "N/A"}
|
||||
</p>
|
||||
</div>
|
||||
</div>{" "}
|
||||
<div>
|
||||
<span className="text-sm font-medium text-gray-500 block mb-1">
|
||||
Deadline
|
||||
</span>
|
||||
<p className="text-gray-900 font-medium">
|
||||
{project.finish_date || "N/A"}
|
||||
{project.finish_date
|
||||
? formatDate(project.finish_date)
|
||||
: "N/A"}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
@@ -10,6 +10,7 @@ import PageContainer from "@/components/ui/PageContainer";
|
||||
import PageHeader from "@/components/ui/PageHeader";
|
||||
import SearchBar from "@/components/ui/SearchBar";
|
||||
import { LoadingState } from "@/components/ui/States";
|
||||
import { formatDate } from "@/lib/utils";
|
||||
|
||||
export default function ProjectListPage() {
|
||||
const [projects, setProjects] = useState([]);
|
||||
@@ -245,12 +246,14 @@ export default function ProjectListPage() {
|
||||
title={project.plot}
|
||||
>
|
||||
{project.plot || "N/A"}
|
||||
</td>
|
||||
</td>{" "}
|
||||
<td
|
||||
className="px-2 py-3 text-xs text-gray-600 truncate"
|
||||
title={project.finish_date}
|
||||
>
|
||||
{project.finish_date || "N/A"}
|
||||
{project.finish_date
|
||||
? formatDate(project.finish_date)
|
||||
: "N/A"}
|
||||
</td>
|
||||
<td className="px-2 py-3 text-xs text-gray-600 text-center font-semibold">
|
||||
{project.project_type === "design"
|
||||
|
||||
@@ -8,6 +8,7 @@ import Badge from "@/components/ui/Badge";
|
||||
import TaskStatusDropdownSimple from "@/components/TaskStatusDropdownSimple";
|
||||
import { Input } from "@/components/ui/Input";
|
||||
import { formatDistanceToNow, parseISO } from "date-fns";
|
||||
import { formatDate } from "@/lib/utils";
|
||||
import PageContainer from "@/components/ui/PageContainer";
|
||||
import PageHeader from "@/components/ui/PageHeader";
|
||||
import SearchBar from "@/components/ui/SearchBar";
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useRouter } from "next/navigation";
|
||||
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";
|
||||
|
||||
export default function ContractForm() {
|
||||
const [form, setForm] = useState({
|
||||
@@ -133,11 +134,11 @@ export default function ContractForm() {
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Date Signed
|
||||
</label>
|
||||
</label>{" "}
|
||||
<Input
|
||||
type="date"
|
||||
name="date_signed"
|
||||
value={form.date_signed || ""}
|
||||
value={formatDateForInput(form.date_signed)}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
@@ -145,11 +146,11 @@ export default function ContractForm() {
|
||||
<div className="md:col-span-2">
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Finish Date
|
||||
</label>
|
||||
</label>{" "}
|
||||
<Input
|
||||
type="date"
|
||||
name="finish_date"
|
||||
value={form.finish_date || ""}
|
||||
value={formatDateForInput(form.finish_date)}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useRouter } from "next/navigation";
|
||||
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";
|
||||
|
||||
export default function ProjectForm({ initialData = null }) {
|
||||
const [form, setForm] = useState({
|
||||
@@ -214,11 +215,11 @@ export default function ProjectForm({ initialData = null }) {
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Finish Date
|
||||
</label>
|
||||
</label>{" "}
|
||||
<Input
|
||||
type="date"
|
||||
name="finish_date"
|
||||
value={form.finish_date || ""}
|
||||
value={formatDateForInput(form.finish_date)}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
parseISO,
|
||||
formatDistanceToNow,
|
||||
} from "date-fns";
|
||||
import { formatDate } from "@/lib/utils";
|
||||
|
||||
export default function ProjectTasksDashboard() {
|
||||
const [allTasks, setAllTasks] = useState([]);
|
||||
|
||||
@@ -6,6 +6,7 @@ import TaskStatusDropdownSimple from "./TaskStatusDropdownSimple";
|
||||
import { Card, CardHeader, CardContent } from "./ui/Card";
|
||||
import Button from "./ui/Button";
|
||||
import Badge from "./ui/Badge";
|
||||
import { formatDate } from "@/lib/utils";
|
||||
|
||||
export default function ProjectTasksSection({ projectId }) {
|
||||
const [projectTasks, setProjectTasks] = useState([]);
|
||||
@@ -444,7 +445,7 @@ export default function ProjectTasksSection({ projectId }) {
|
||||
</td>{" "}
|
||||
<td className="px-4 py-4 text-sm text-gray-600">
|
||||
{task.date_started
|
||||
? new Date(task.date_started).toLocaleDateString()
|
||||
? formatDate(task.date_started)
|
||||
: "Not started"}
|
||||
</td>{" "}
|
||||
<td className="px-4 py-4">
|
||||
@@ -519,15 +520,11 @@ export default function ProjectTasksSection({ projectId }) {
|
||||
</div>
|
||||
<p className="text-sm text-gray-800">
|
||||
{note.note}
|
||||
</p>
|
||||
</p>{" "}
|
||||
<p className="text-xs text-gray-500 mt-1">
|
||||
{new Date(
|
||||
note.note_date
|
||||
).toLocaleDateString()}{" "}
|
||||
at{" "}
|
||||
{new Date(
|
||||
note.note_date
|
||||
).toLocaleTimeString()}
|
||||
{formatDate(note.note_date, {
|
||||
includeTime: true,
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
{!note.is_system && (
|
||||
|
||||
@@ -38,3 +38,53 @@ export const getDeadlineText = (daysRemaining) => {
|
||||
if (daysRemaining > 0) return `${daysRemaining} days left`;
|
||||
return `${Math.abs(daysRemaining)} days overdue`;
|
||||
};
|
||||
|
||||
export const formatDate = (date, options = {}) => {
|
||||
if (!date) return "";
|
||||
|
||||
try {
|
||||
const dateObj = typeof date === "string" ? new Date(date) : date;
|
||||
|
||||
if (isNaN(dateObj.getTime())) {
|
||||
return "Invalid date";
|
||||
}
|
||||
|
||||
// Default to DD.MM.YYYY format
|
||||
const day = String(dateObj.getDate()).padStart(2, "0");
|
||||
const month = String(dateObj.getMonth() + 1).padStart(2, "0");
|
||||
const year = dateObj.getFullYear();
|
||||
|
||||
if (options.includeTime) {
|
||||
const hours = String(dateObj.getHours()).padStart(2, "0");
|
||||
const minutes = String(dateObj.getMinutes()).padStart(2, "0");
|
||||
return `${day}.${month}.${year} ${hours}:${minutes}`;
|
||||
}
|
||||
|
||||
return `${day}.${month}.${year}`;
|
||||
} catch (error) {
|
||||
console.error("Error formatting date:", error);
|
||||
return "Invalid date";
|
||||
}
|
||||
};
|
||||
|
||||
export const formatDateForInput = (date) => {
|
||||
if (!date) return "";
|
||||
|
||||
try {
|
||||
const dateObj = typeof date === "string" ? new Date(date) : date;
|
||||
|
||||
if (isNaN(dateObj.getTime())) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Format as YYYY-MM-DD for HTML date inputs
|
||||
const year = dateObj.getFullYear();
|
||||
const month = String(dateObj.getMonth() + 1).padStart(2, "0");
|
||||
const day = String(dateObj.getDate()).padStart(2, "0");
|
||||
|
||||
return `${year}-${month}-${day}`;
|
||||
} catch (error) {
|
||||
console.error("Error formatting date for input:", error);
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user