Skip to content
Merged
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
1 change: 1 addition & 0 deletions src/components/SampleData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export interface ProjectData {
spent: number;
vendor: string;
vendorId?: string;
createdAt?: string | { seconds: number };
reports: ProjectReport[];
}

Expand Down
42 changes: 39 additions & 3 deletions src/components/VendorProjectCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
import React from "react";
import { Link } from "react-router-dom";
import { ProjectData } from "./SampleData";
import ScheduleCompletionBar from "./forms/ScheduleCompletionBar";

interface VendorProjectCardProps {
project: ProjectData;
}

const VendorProjectCard: React.FC<VendorProjectCardProps> = ({ project }) => {
const { id, name, status, metric1, metric2, description, reports } = project;
const { id, name, status, description, reports, createdAt } = project;

// Sort reports by date (most recent first)
const sortedReports = [...reports].sort(
Expand All @@ -18,6 +19,9 @@ const VendorProjectCard: React.FC<VendorProjectCardProps> = ({ project }) => {
// Get the two most recent reports
const recentReports = sortedReports.slice(0, 2);

// Get the most recent report for schedule data
const latestReport = sortedReports[0];

return (
<div className="card mb-4 w-100 border-darker">
<div className="card-body p-4">
Expand Down Expand Up @@ -51,14 +55,46 @@ const VendorProjectCard: React.FC<VendorProjectCardProps> = ({ project }) => {
<div className="col-md-6">
<div className="card border-primary">
<div className="card-body text-center">
<span className="text-primary">{metric1}</span>
{/* Use ScheduleCompletion component */}
{latestReport?.scheduleData?.baseline?.expectedDate &&
latestReport?.date &&
createdAt ? (
<ScheduleCompletionBar
projectCreatedAt={
typeof createdAt === "string"
? createdAt
: new Date(
createdAt.seconds * 1000
).toLocaleDateString("en-US")
}
expectedBaselineDate={
latestReport.scheduleData.baseline.expectedDate
}
actualProjectedDate={
latestReport.scheduleData.current.projectedDate
}
reportDate={latestReport.date}
/>
) : (
<span className="text-muted">
No schedule data available
</span>
)}
</div>
</div>
</div>
<div className="col-md-6">
<div className="card border-primary">
<div className="card-body text-center">
<span className="text-primary">{metric2}</span>
<h6 className="text-muted mb-3">Total Reports</h6>
<div style={{ padding: "1px 0" }}>
<span
className="text-primary"
style={{ fontSize: "1rem", fontWeight: "500" }}
>
{reports.length}
</span>
</div>
</div>
</div>
</div>
Expand Down
55 changes: 55 additions & 0 deletions src/components/forms/ScheduleCompletionBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// src/components/forms/ScheduleCompletionBar.tsx
import React from "react";

interface ScheduleCompletionBarProps {
projectCreatedAt: string;
expectedBaselineDate: string;
actualProjectedDate: string;
reportDate: string;
}

const ScheduleCompletionBar: React.FC<ScheduleCompletionBarProps> = ({
projectCreatedAt,
expectedBaselineDate,
reportDate,
}) => {
// Parse dates
const createdDate = new Date(projectCreatedAt);
const baselineDate = new Date(expectedBaselineDate);
const currentDate = new Date(reportDate);

// Calculate total project duration (from creation to baseline completion)
const totalDuration = baselineDate.getTime() - createdDate.getTime();

// Calculate elapsed time (from creation to report date)
const elapsedTime = currentDate.getTime() - createdDate.getTime();

// Calculate percentage complete
const percentageComplete = Math.min(
Math.max((elapsedTime / totalDuration) * 100, 0),
100
);

return (
<div>
<h6 className="text-muted mb-3">Project Completion</h6>
<div className="progress" style={{ height: "25px" }}>
<div
className="progress-bar"
role="progressbar"
style={{
width: `${percentageComplete}%`,
backgroundColor: "#007bff",
}}
aria-valuenow={percentageComplete}
aria-valuemin={0}
aria-valuemax={100}
>
{Math.round(percentageComplete)}%
</div>
</div>
</div>
);
};

export default ScheduleCompletionBar;
2 changes: 0 additions & 2 deletions src/pages/ProjectDetailPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ const ProjectDetailPage: React.FC = () => {
const loadProject = async () => {
console.log("Looking for project with ID:", projectId);

// First, try to load from Firestore
try {
const projectRef = doc(db, "projects", projectId);
const projectSnap = await getDoc(projectRef);
Expand Down Expand Up @@ -54,7 +53,6 @@ const ProjectDetailPage: React.FC = () => {
reports: [],
} as ProjectData);

// Set up real-time listener for reports
const reportsRef = collection(db, "projects", projectId, "reports");
const unsubscribe = onSnapshot(reportsRef, (snapshot) => {
const reportsList = snapshot.docs.map(
Expand Down
4 changes: 3 additions & 1 deletion src/services/firebaseDataService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ export const fetchAllProjects = async (): Promise<ProjectData[]> => {
100
)
: 0;

//
const project: ProjectData = {
id: docSnapshot.id,
name: data.name || "Untitled Project",
Expand All @@ -271,6 +271,7 @@ export const fetchAllProjects = async (): Promise<ProjectData[]> => {
spent: data.spent || 0,
vendor: data.vendor || data.vendorName || "No Vendor Assigned",
vendorId: data.vendorId || undefined,
createdAt: data.createdAt || undefined,
reports: reports,
};

Expand Down Expand Up @@ -467,6 +468,7 @@ export const fetchProjectsByVendor = async (
spent: spentValue,
vendor: data.vendor || data.vendorName || "No Vendor Assigned",
vendorId: data.vendorId || undefined,
createdAt: data.createdAt || undefined,
reports: reports,
};

Expand Down