Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 77 additions & 25 deletions templates/projects-timeline/components/projects/projects-timeline.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";

import { useMemo } from "react";
import { startOfWeek, addDays, eachDayOfInterval, isSameDay } from "date-fns";
import { startOfWeek, addDays, eachDayOfInterval, startOfDay } from "date-fns";
import { Search, Triangle } from "lucide-react";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
Expand All @@ -27,17 +27,29 @@ export function ProjectsTimeline() {

const currentWeek = useMemo(() => {
const weekStart = startOfWeek(currentWeekStart, { weekStartsOn: 1 });
return eachDayOfInterval({
const days = eachDayOfInterval({
start: weekStart,
end: addDays(weekStart, 6),
});
// Normalize all days to start of day for consistent comparison
return days.map(day => startOfDay(day));
}, [currentWeekStart]);

const today = new Date();
const todayIndex = currentWeek.findIndex((day) => isSameDay(day, today));
const today = startOfDay(new Date());
const todayIndex = currentWeek.findIndex((day) => {
const normalizedDay = startOfDay(day);
return normalizedDay.getTime() === today.getTime();
});

// Debug: log today indicator info
console.log('Today indicator:', {
today: today.toISOString().split('T')[0],
todayIndex,
currentWeek: currentWeek.map(d => d.toISOString().split('T')[0]),
isInWeek: todayIndex !== -1
});

const projects = filteredProjects();
const columnWidth = 162;
const cardHeight = 108;
const gapBetweenCards = 30;
const horizontalPadding = 20;
Expand All @@ -57,26 +69,49 @@ export function ProjectsTimeline() {
const projectsWithPositions: ProjectInfo[] = [];

projects.forEach((project) => {
const startDate = new Date(project.startDate);
const endDate = new Date(project.endDate);
// Normalize dates to start of day to ensure accurate comparison
// Handle both Date objects and date strings (from serialization)
const rawStartDate = project.startDate instanceof Date
? project.startDate
: new Date(project.startDate);
const rawEndDate = project.endDate instanceof Date
? project.endDate
: new Date(project.endDate);

const startDate = startOfDay(rawStartDate);
const endDate = startOfDay(rawEndDate);

const startIndex = currentWeek.findIndex((day) =>
isSameDay(day, startDate)
);
const endIndex = currentWeek.findIndex((day) => isSameDay(day, endDate));
// Compare dates by date string (YYYY-MM-DD) to avoid timezone issues
const startDateStr = startDate.toISOString().split('T')[0];
const endDateStr = endDate.toISOString().split('T')[0];

const startIndex = currentWeek.findIndex((day) => {
const dayStr = startOfDay(day).toISOString().split('T')[0];
return dayStr === startDateStr;
});
const endIndex = currentWeek.findIndex((day) => {
const dayStr = startOfDay(day).toISOString().split('T')[0];
return dayStr === endDateStr;
});

// Check if project overlaps with the current week at all
const weekStart = startOfDay(currentWeek[0]);
const weekEnd = startOfDay(currentWeek[currentWeek.length - 1]);
const projectOverlapsWeek = startDate <= weekEnd && endDate >= weekStart;

if (!projectOverlapsWeek) {
// Project doesn't overlap with this week at all, skip it
return;
}

// If both dates are outside the week but it overlaps, it spans the full week
if (startIndex === -1 && endIndex === -1) {
const startBeforeWeek = startDate < currentWeek[0];
const endAfterWeek = endDate > currentWeek[currentWeek.length - 1];

if (startBeforeWeek && endAfterWeek) {
projectsWithPositions.push({
project,
startIndex: 0,
endIndex: currentWeek.length - 1,
spanDays: currentWeek.length,
});
}
projectsWithPositions.push({
project,
startIndex: 0,
endIndex: currentWeek.length - 1,
spanDays: currentWeek.length,
});
return;
}

Expand All @@ -85,6 +120,24 @@ export function ProjectsTimeline() {
endIndex === -1 ? currentWeek.length - 1 : endIndex;
const spanDays = actualEndIndex - actualStartIndex + 1;

// Debug logging for all projects
console.log(`Project: ${project.title}`, {
rawStart: project.startDate,
rawEnd: project.endDate,
normalizedStart: startDate.toISOString().split('T')[0],
normalizedEnd: endDate.toISOString().split('T')[0],
startIndex,
endIndex,
actualStartIndex,
actualEndIndex,
spanDays,
startCol: actualStartIndex + 1,
endCol: actualEndIndex + 2,
gridColumn: `${actualStartIndex + 1} / ${actualEndIndex + 2}`,
weekDays: currentWeek.map(d => d.toISOString().split('T')[0]),
weekDayIndices: currentWeek.map((d, i) => ({ index: i, date: d.toISOString().split('T')[0] }))
});

projectsWithPositions.push({
project,
startIndex: actualStartIndex,
Expand Down Expand Up @@ -190,7 +243,7 @@ export function ProjectsTimeline() {
<div
className="absolute z-20 pointer-events-none"
style={{
left: `${todayIndex * columnWidth + columnWidth / 2}px`,
left: `${((todayIndex + 0.5) / 7) * 100}%`,
top: 0,
bottom: 0,
transform: "translateX(-50%)",
Expand All @@ -210,9 +263,8 @@ export function ProjectsTimeline() {
)}

<div
className="grid w-full flex-1"
className="grid grid-cols-7 flex-1 w-full"
style={{
gridTemplateColumns: `repeat(7, ${columnWidth}px)`,
gridAutoRows: `${cardHeight}px`,
gap: `${gapBetweenCards}px 0`,
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ interface TimelineWeekHeaderProps {

export function TimelineWeekHeader({ weekDays }: TimelineWeekHeaderProps) {
return (
<div className="flex border-b border-border sticky top-0 z-30 bg-background w-max min-w-full">
<div className="grid grid-cols-7 border-b border-border sticky top-0 z-30 bg-background w-full">
{weekDays.map((day) => (
<div
key={day.toISOString()}
className="flex-1 border-r border-border last:border-r-0 text-center py-2.5 min-w-[162px]"
className="border-r border-border last:border-r-0 text-center py-2.5"
>
<div className="text-sm font-medium">
{format(day, "EEE dd")}
Expand Down
125 changes: 119 additions & 6 deletions templates/projects-timeline/mock-data/projects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,123 @@ function generateProjectsForDateRange(
return allProjects;
}

const startDate = new Date(2020, 0, 1);
const endDate = new Date(2070, 11, 31);
// Create a fixed dataset for debugging
// Get the current week (Monday to Sunday)
function getCurrentWeekStart(): Date {
const today = new Date();
const day = today.getDay();
const diff = today.getDate() - day + (day === 0 ? -6 : 1); // Adjust to Monday
const monday = new Date(today.setDate(diff));
monday.setHours(0, 0, 0, 0);
return monday;
}

function createFixedProjects(): Project[] {
const weekStart = getCurrentWeekStart();

// Create dates for the current week (Monday = day 0, Sunday = day 6)
const monday = new Date(weekStart);
monday.setHours(0, 0, 0, 0);

const tuesday = new Date(monday);
tuesday.setDate(tuesday.getDate() + 1);

const wednesday = new Date(monday);
wednesday.setDate(wednesday.getDate() + 2);

const thursday = new Date(monday);
thursday.setDate(thursday.getDate() + 3);

const friday = new Date(monday);
friday.setDate(friday.getDate() + 4);

const saturday = new Date(monday);
saturday.setDate(saturday.getDate() + 5);

const sunday = new Date(monday);
sunday.setDate(sunday.getDate() + 6);

return [
{
id: "proj-1",
title: "Frontend Optimization",
startDate: monday, // Mon
endDate: saturday, // Sat (6 days: Mon-Sat)
priority: "high",
status: "in-progress",
assignedUsers: ["user1", "user2", "user3"],
color: "blue",
},
{
id: "proj-2",
title: "Network Security",
startDate: tuesday, // Tue
endDate: thursday, // Thu (3 days: Tue-Thu)
priority: "medium",
status: "in-progress",
assignedUsers: ["user4", "user5"],
color: "red",
},
{
id: "proj-3",
title: "Database Migration",
startDate: wednesday, // Wed
endDate: friday, // Fri (3 days: Wed-Fri)
priority: "high",
status: "in-progress",
assignedUsers: ["user1", "user6"],
color: "purple",
},
{
id: "proj-4",
title: "API Integration",
startDate: monday, // Mon
endDate: wednesday, // Wed (3 days: Mon-Wed)
priority: "low",
status: "in-progress",
assignedUsers: ["user2", "user3", "user4"],
color: "green",
},
{
id: "proj-5",
title: "UI Design",
startDate: thursday, // Thu
endDate: sunday, // Sun (4 days: Thu-Sun)
priority: "medium",
status: "in-progress",
assignedUsers: ["user5", "user6"],
color: "orange",
},
{
id: "proj-6",
title: "Bug Fixes",
startDate: friday, // Fri
endDate: saturday, // Sat (2 days: Fri-Sat)
priority: "high",
status: "in-progress",
assignedUsers: ["user1"],
color: "yellow",
},
{
id: "proj-7",
title: "Code Review",
startDate: monday, // Mon
endDate: sunday, // Sun (7 days: full week)
priority: "low",
status: "in-progress",
assignedUsers: ["user2", "user3", "user4", "user5"],
color: "cyan",
},
];
}

// Use fixed dataset for consistent debugging
export const projects: Project[] = createFixedProjects();

export const projects: Project[] = generateProjectsForDateRange(
startDate,
endDate
);
// Uncomment below to use random data instead:
// const startDate = new Date(2020, 0, 1);
// const endDate = new Date(2070, 11, 31);
// export const projects: Project[] = generateProjectsForDateRange(
// startDate,
// endDate
// );