fix: Remove unnecessary whitespace in table headers across multiple components

This commit is contained in:
Chop
2025-06-21 21:56:20 +02:00
parent f83cc8e564
commit 96da5212d4
4 changed files with 100 additions and 53 deletions

View File

@@ -195,12 +195,12 @@ export default function ProjectListPage() {
</th> </th>
<th className="text-left px-2 py-3 font-semibold text-xs text-gray-700 w-24"> <th className="text-left px-2 py-3 font-semibold text-xs text-gray-700 w-24">
Status Status
</th> </th>{" "}
<th className="text-left px-2 py-3 font-semibold text-xs text-gray-700 w-20"> <th className="text-left px-2 py-3 font-semibold text-xs text-gray-700 w-20">
Actions Actions
</th> </th>
</tr> </tr>
</thead>{" "} </thead>
<tbody> <tbody>
{filteredProjects.map((project, index) => ( {filteredProjects.map((project, index) => (
<tr <tr

View File

@@ -105,10 +105,9 @@ export default function DropdownTestPage() {
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Project Status Project Status
</th> </th>
</tr> </tr>{" "}
</thead> </thead>
<tbody className="bg-white divide-y divide-gray-200"> <tbody className="bg-white divide-y divide-gray-200">
{" "}
<tr> <tr>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900"> <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
Test Task in Table Test Task in Table

View File

@@ -1,6 +1,6 @@
"use client"; "use client";
import { useState, useEffect } from "react"; import { useState, useEffect, Fragment } from "react";
import { Card, CardHeader, CardContent } from "./ui/Card"; import { Card, CardHeader, CardContent } from "./ui/Card";
import Button from "./ui/Button"; import Button from "./ui/Button";
import Badge from "./ui/Badge"; import Badge from "./ui/Badge";
@@ -35,7 +35,7 @@ export default function ProjectTasksList() {
}; };
fetchAllTasks(); fetchAllTasks();
}, []); // Calculate task status based on date_added and max_wait_days }, []); // Calculate task status based on date_added and max_wait_days
const getTaskStatus = (task) => { const getTaskStatus = (task) => {
if (task.status === "completed" || task.status === "cancelled") { if (task.status === "completed" || task.status === "cancelled") {
return { type: "completed", days: 0 }; return { type: "completed", days: 0 };
@@ -43,8 +43,12 @@ export default function ProjectTasksList() {
try { try {
// For in-progress tasks, use date_started if available and valid, otherwise fall back to date_added // For in-progress tasks, use date_started if available and valid, otherwise fall back to date_added
let referenceDate; let referenceDate;
console.log(task.date_started) console.log(task.date_started);
if (task.status === "in_progress" && task.date_started && task.date_started.trim() !== "") { if (
task.status === "in_progress" &&
task.date_started &&
task.date_started.trim() !== ""
) {
// Handle the format "2025-06-20 08:40:38" // Handle the format "2025-06-20 08:40:38"
referenceDate = new Date(task.date_started); referenceDate = new Date(task.date_started);
} else { } else {
@@ -65,9 +69,17 @@ export default function ProjectTasksList() {
if (task.status === "in_progress") { if (task.status === "in_progress") {
if (daysRemaining < 0) { if (daysRemaining < 0) {
return { type: "overdue", days: Math.abs(daysRemaining), daysRemaining: daysRemaining }; return {
type: "overdue",
days: Math.abs(daysRemaining),
daysRemaining: daysRemaining,
};
} else { } else {
return { type: "in_progress", days: daysRemaining, daysRemaining: daysRemaining }; return {
type: "in_progress",
days: daysRemaining,
daysRemaining: daysRemaining,
};
} }
} }
@@ -81,7 +93,12 @@ export default function ProjectTasksList() {
return { type: "pending", days: maxWaitDays - daysElapsed }; return { type: "pending", days: maxWaitDays - daysElapsed };
} }
} catch (error) { } catch (error) {
console.error("Error parsing date:", task.date_added, task.date_started, error); console.error(
"Error parsing date:",
task.date_added,
task.date_started,
error
);
return { type: "pending", days: 0 }; return { type: "pending", days: 0 };
} }
}; };
@@ -120,18 +137,25 @@ export default function ProjectTasksList() {
// Sort in_progress tasks by time left (urgent first - less time left comes first) // Sort in_progress tasks by time left (urgent first - less time left comes first)
groups.in_progress.sort((a, b) => { groups.in_progress.sort((a, b) => {
// If both have valid time remaining, sort by days remaining (ascending - urgent first) // If both have valid time remaining, sort by days remaining (ascending - urgent first)
if (!isNaN(a.statusInfo.daysRemaining) && !isNaN(b.statusInfo.daysRemaining)) { if (
!isNaN(a.statusInfo.daysRemaining) &&
!isNaN(b.statusInfo.daysRemaining)
) {
return a.statusInfo.daysRemaining - b.statusInfo.daysRemaining; return a.statusInfo.daysRemaining - b.statusInfo.daysRemaining;
} }
// If one has invalid time, sort by date_started as fallback // If one has invalid time, sort by date_started as fallback
try { try {
const dateA = a.date_started ? new Date(a.date_started) : new Date(a.date_added); const dateA = a.date_started
const dateB = b.date_started ? new Date(b.date_started) : new Date(b.date_added); ? new Date(a.date_started)
: new Date(a.date_added);
const dateB = b.date_started
? new Date(b.date_started)
: new Date(b.date_added);
return dateA - dateB; // Oldest started first return dateA - dateB; // Oldest started first
} catch (error) { } catch (error) {
return 0; return 0;
} }
}); // Sort completed tasks by date_completed if available, otherwise by date_added (most recently completed first) }); // Sort completed tasks by date_completed if available, otherwise by date_added (most recently completed first)
groups.completed.sort((a, b) => { groups.completed.sort((a, b) => {
try { try {
// Try to use date_completed first // Try to use date_completed first
@@ -143,7 +167,7 @@ export default function ProjectTasksList() {
// If only one has date_completed, prioritize it // If only one has date_completed, prioritize it
if (a.date_completed && !b.date_completed) return -1; if (a.date_completed && !b.date_completed) return -1;
if (!a.date_completed && b.date_completed) return 1; if (!a.date_completed && b.date_completed) return 1;
// Fall back to date_added for both // Fall back to date_added for both
const dateA = new Date(a.date_added); const dateA = new Date(a.date_added);
const dateB = new Date(b.date_added); const dateB = new Date(b.date_added);
@@ -225,7 +249,8 @@ export default function ProjectTasksList() {
if (days > 7) return "danger"; if (days > 7) return "danger";
if (days > 3) return "warning"; if (days > 3) return "warning";
return "high"; return "high";
}; const TaskRow = ({ task, showTimeLeft = false }) => ( };
const TaskRow = ({ task, showTimeLeft = false }) => (
<tr className="hover:bg-gray-50 border-b border-gray-200"> <tr className="hover:bg-gray-50 border-b border-gray-200">
<td className="px-4 py-3"> <td className="px-4 py-3">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
@@ -236,49 +261,58 @@ export default function ProjectTasksList() {
</div> </div>
</td> </td>
<td className="px-4 py-3"> <td className="px-4 py-3">
{" "}
<Link <Link
href={`/projects/${task.project_id}`} href={`/projects/${task.project_id}`}
className="text-blue-600 hover:text-blue-800 font-medium" className="text-blue-600 hover:text-blue-800 font-medium"
> >
{task.project_name} {task.project_name}
</Link> </Link>
</td> <td className="px-4 py-3 text-sm text-gray-600">{task.city || 'N/A'}</td> </td>
<td className="px-4 py-3 text-sm text-gray-600">{task.address || 'N/A'}</td> <td className="px-4 py-3 text-sm text-gray-600">{task.city || "N/A"}</td>
<td className="px-4 py-3 text-sm text-gray-600">
{task.address || "N/A"}
</td>
{showTimeLeft && ( {showTimeLeft && (
<td className="px-4 py-3"> <td className="px-4 py-3">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
{task.statusInfo && task.statusInfo.type === "in_progress" && ( {task.statusInfo && task.statusInfo.type === "in_progress" && (
<Badge <Badge
variant={task.statusInfo.daysRemaining <= 2 ? "warning" : "secondary"} variant={
task.statusInfo.daysRemaining <= 2 ? "warning" : "secondary"
}
size="sm" size="sm"
> >
{!isNaN(task.statusInfo.daysRemaining) ? ( {!isNaN(task.statusInfo.daysRemaining)
task.statusInfo.daysRemaining > 0 ? task.statusInfo.daysRemaining > 0
? `${task.statusInfo.daysRemaining}d left` ? `${task.statusInfo.daysRemaining}d left`
: `${Math.abs(task.statusInfo.daysRemaining)}d overdue` : `${Math.abs(task.statusInfo.daysRemaining)}d overdue`
) : ( : "Calculating..."}
"Calculating..."
)}
</Badge>
)}
{task.statusInfo && task.statusInfo.type === "overdue" && task.status === "in_progress" && (
<Badge
variant="danger"
size="sm"
>
{!isNaN(task.statusInfo.daysRemaining) ? `${Math.abs(task.statusInfo.daysRemaining)}d overdue` : "Overdue"}
</Badge> </Badge>
)} )}
{task.statusInfo &&
task.statusInfo.type === "overdue" &&
task.status === "in_progress" && (
<Badge variant="danger" size="sm">
{!isNaN(task.statusInfo.daysRemaining)
? `${Math.abs(task.statusInfo.daysRemaining)}d overdue`
: "Overdue"}
</Badge>
)}
</div> </div>
</td> </td>
)} <td className="px-4 py-3 text-sm text-gray-500"> )}
<td className="px-4 py-3 text-sm text-gray-500">
{task.status === "completed" && task.date_completed ? ( {task.status === "completed" && task.date_completed ? (
<div> <div>
<div> <div>
Completed: {(() => { Completed:{" "}
{(() => {
try { try {
const completedDate = new Date(task.date_completed); const completedDate = new Date(task.date_completed);
return formatDistanceToNow(completedDate, { addSuffix: true }); return formatDistanceToNow(completedDate, {
addSuffix: true,
});
} catch (error) { } catch (error) {
return task.date_completed; return task.date_completed;
} }
@@ -288,7 +322,8 @@ export default function ProjectTasksList() {
) : task.status === "in_progress" && task.date_started ? ( ) : task.status === "in_progress" && task.date_started ? (
<div> <div>
<div> <div>
Started: {(() => { Started:{" "}
{(() => {
try { try {
const startedDate = new Date(task.date_started); const startedDate = new Date(task.date_started);
return formatDistanceToNow(startedDate, { addSuffix: true }); return formatDistanceToNow(startedDate, { addSuffix: true });
@@ -313,7 +348,8 @@ export default function ProjectTasksList() {
</td> </td>
<td className="px-4 py-3 text-sm text-gray-500"> <td className="px-4 py-3 text-sm text-gray-500">
{task.max_wait_days} days {task.max_wait_days} days
</td> <td className="px-4 py-3"> </td>
<td className="px-4 py-3">
<TaskStatusDropdownSimple <TaskStatusDropdownSimple
task={task} task={task}
size="sm" size="sm"
@@ -321,7 +357,8 @@ export default function ProjectTasksList() {
/> />
</td> </td>
</tr> </tr>
); const TaskTable = ({ tasks, showGrouped = false, showTimeLeft = false }) => { );
const TaskTable = ({ tasks, showGrouped = false, showTimeLeft = false }) => {
const filteredTasks = filterTasks(tasks); const filteredTasks = filterTasks(tasks);
const groupedTasks = groupTasksByName(filteredTasks); const groupedTasks = groupTasksByName(filteredTasks);
const colSpan = showTimeLeft ? "8" : "7"; const colSpan = showTimeLeft ? "8" : "7";
@@ -333,25 +370,27 @@ export default function ProjectTasksList() {
<tr> <tr>
<th className="px-4 py-3 text-left text-sm font-medium text-gray-700"> <th className="px-4 py-3 text-left text-sm font-medium text-gray-700">
Task Name Task Name
</th> </th>{" "}
<th className="px-4 py-3 text-left text-sm font-medium text-gray-700"> <th className="px-4 py-3 text-left text-sm font-medium text-gray-700">
Project Project
</th> <th className="px-4 py-3 text-left text-sm font-medium text-gray-700"> </th>
<th className="px-4 py-3 text-left text-sm font-medium text-gray-700">
City City
</th> </th>
<th className="px-4 py-3 text-left text-sm font-medium text-gray-700"> <th className="px-4 py-3 text-left text-sm font-medium text-gray-700">
Address Address
</th> </th>{" "}
{showTimeLeft && ( {showTimeLeft && (
<th className="px-4 py-3 text-left text-sm font-medium text-gray-700"> <th className="px-4 py-3 text-left text-sm font-medium text-gray-700">
Time Left Time Left
</th> </th>
)} <th className="px-4 py-3 text-left text-sm font-medium text-gray-700"> )}
<th className="px-4 py-3 text-left text-sm font-medium text-gray-700">
Date Info Date Info
</th> </th>
<th className="px-4 py-3 text-left text-sm font-medium text-gray-700"> <th className="px-4 py-3 text-left text-sm font-medium text-gray-700">
Max Wait Max Wait
</th> </th>{" "}
<th className="px-4 py-3 text-left text-sm font-medium text-gray-700"> <th className="px-4 py-3 text-left text-sm font-medium text-gray-700">
Actions Actions
</th> </th>
@@ -359,7 +398,7 @@ export default function ProjectTasksList() {
</thead> </thead>
<tbody> <tbody>
{Object.entries(groupedTasks).map(([groupName, groupTasks]) => ( {Object.entries(groupedTasks).map(([groupName, groupTasks]) => (
<> <Fragment key={`group-fragment-${groupName}`}>
{showGrouped && groupName !== "All Tasks" && ( {showGrouped && groupName !== "All Tasks" && (
<tr key={`group-${groupName}`}> <tr key={`group-${groupName}`}>
<td <td
@@ -371,9 +410,13 @@ export default function ProjectTasksList() {
</tr> </tr>
)} )}
{groupTasks.map((task) => ( {groupTasks.map((task) => (
<TaskRow key={task.id} task={task} showTimeLeft={showTimeLeft} /> <TaskRow
key={task.id}
task={task}
showTimeLeft={showTimeLeft}
/>
))} ))}
</> </Fragment>
))} ))}
</tbody> </tbody>
</table> </table>
@@ -424,7 +467,9 @@ export default function ProjectTasksList() {
<div className="text-sm text-gray-600">Completed</div> <div className="text-sm text-gray-600">Completed</div>
</CardContent> </CardContent>
</Card> </Card>
</div> {/* Search and Controls */} <SearchBar </div>{" "}
{/* Search and Controls */}{" "}
<SearchBar
searchTerm={searchTerm} searchTerm={searchTerm}
onSearchChange={(e) => setSearchTerm(e.target.value)} onSearchChange={(e) => setSearchTerm(e.target.value)}
placeholder="Search tasks, projects, city, or address..." placeholder="Search tasks, projects, city, or address..."
@@ -437,7 +482,9 @@ export default function ProjectTasksList() {
filters={ filters={
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<label className="text-sm font-medium text-gray-700">Group by:</label> <label className="text-sm font-medium text-gray-700">
Group by:
</label>
<Select <Select
value={groupBy} value={groupBy}
onChange={(e) => setGroupBy(e.target.value)} onChange={(e) => setGroupBy(e.target.value)}
@@ -449,7 +496,8 @@ export default function ProjectTasksList() {
</div> </div>
</div> </div>
} }
/> {/* Task Tables */} />{" "}
{/* Task Tables */}
<div className="space-y-8"> <div className="space-y-8">
{/* Pending Tasks */} {/* Pending Tasks */}
<div> <div>

View File

@@ -380,10 +380,10 @@ export default function ProjectTasksSection({ projectId }) {
</th> </th>
<th className="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> <th className="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Priority Priority
</th> </th>{" "}
<th className="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> <th className="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Max Wait Max Wait
</th>{" "} </th>
<th className="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> <th className="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Date Started Date Started
</th> </th>