Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

43 develop detailed view page for a donated item #59

Merged
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
4 changes: 4 additions & 0 deletions client-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@emotion/react": "^11.13.3",
"@emotion/styled": "^11.13.0",
"@fortawesome/fontawesome-free": "^6.5.1",
"@mui/icons-material": "^6.1.6",
"@mui/material": "^6.1.6",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
Expand Down
2 changes: 2 additions & 0 deletions client-app/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import StatusDisplayPage from './Components/StatusDisplayPage';
import Programs from './Components/Programs';
import AddProgramPage from './Components/AddProgramPage'; // Import AddProgramPage correctly
import NewItemForm from './Components/NewItemForm.tsx';
import DonatedItemDetails from './Components/DonatedItemDetails';
import AddDonor from './Components/AddDonor'; // Why is this here?

function App() {
Expand Down Expand Up @@ -43,6 +44,7 @@ function App() {
path="/addprogram"
element={<AddProgramPage onAddProgram={handleAddProgram} />}
/>
<Route path="/donations/:id" element={<DonatedItemDetails />} />
</Routes>
</div>
);
Expand Down
113 changes: 113 additions & 0 deletions client-app/src/Components/DonatedItemDetails.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import React, { useEffect, useState } from 'react';
import axios from 'axios';
import { useParams } from 'react-router-dom';
import PersonIcon from '@mui/icons-material/Person';
import CategoryIcon from '@mui/icons-material/Category';
import EventNoteIcon from '@mui/icons-material/EventNote';
import AssignmentTurnedInIcon from '@mui/icons-material/AssignmentTurnedIn';
Anjali0407-git marked this conversation as resolved.
Show resolved Hide resolved
import '../css/DonatedItemDetails.css'; // Import the new CSS file
import { Donor } from '../Modals/DonorModal';
import { Program } from '../Modals/ProgramModal';
import { DonatedItemStatus } from '../Modals/DonatedItemStatusModal';
import { DonatedItem } from '../Modals/DonatedItemModal';

const DonatedItemDetails: React.FC = () => {
const { id } = useParams<{ id: string }>();
const [donatedItem, setDonatedItem] = useState<DonatedItem | null>(null);
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<string>('');

useEffect(() => {
const fetchDonatedItemDetails = async () => {
try {

const API_BASE_URL = process.env.REACT_APP_BACKEND_API_BASE_URL || '';
console.log(API_BASE_URL);
console.log(id);
const response = await axios.get<DonatedItem>(`${API_BASE_URL}donatedItem/${id}`);

console.log(response);
setDonatedItem(response.data);
} catch (err) {
if (axios.isAxiosError(err)) {
setError(`Failed to fetch donated item details: ${err.response?.statusText || 'Server error'}`);
Anjali0407-git marked this conversation as resolved.
Show resolved Hide resolved
} else {
setError('An unexpected error occurred');
}
console.error('Error fetching donated item:', err);
} finally {
setLoading(false);
}
};

fetchDonatedItemDetails();
}, [id]);

const formatDate = (dateString: string) => {
const date = new Date(dateString);
return isNaN(date.getTime()) ? 'Invalid date' : date.toDateString();
};

if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
if (!donatedItem) return <div>No data available.</div>;

return (
<div className="donated-item-details-container">
<h1>Donated Item Details</h1>
<div className="details-grid">
{/* Left Column */}
<div className="left-column">
<section className="item-details-section">
<div className="section-header">
<CategoryIcon className="icon" />
<h2>Item Details</h2>
</div>
<p><strong>Type:</strong> {donatedItem.itemType}</p>
<p><strong>Status:</strong> {donatedItem.currentStatus}</p>
<p><strong>Donated On:</strong> {formatDate(donatedItem.dateDonated)}</p>
<p><strong>Last Updated:</strong> {formatDate(donatedItem.lastUpdated)}</p>
</section>

<section className="donated-item-status-section">
<div className="section-header">
<AssignmentTurnedInIcon className="icon" />
<h2>Donated Item Status</h2>
</div>
{donatedItem.statuses.map(status => (
<p key={status.id}><strong>Status:</strong> {status.type} - <strong>Modified on:</strong> {formatDate(status.dateModified)}</p>
))}
</section>
</div>

{/* Right Column */}
<div className="right-column">
<section className="donor-details-section">
<div className="section-header">
<PersonIcon className="icon" />
<h2>Donor Details</h2>
</div>
<p><strong>Name:</strong> {donatedItem.donor.firstName} {donatedItem.donor.lastName}</p>
<p><strong>Email:</strong> {donatedItem.donor.email}</p>
<p><strong>Contact:</strong> {donatedItem.donor.contact}</p>
<p><strong>Address:</strong> {donatedItem.donor.addressLine1}, {donatedItem.donor.addressLine2 ?? ''}</p>
<p><strong>City/State:</strong> {donatedItem.donor.city}, {donatedItem.donor.state} {donatedItem.donor.zipcode}</p>
</section>

<section className="program-details-section">
<div className="section-header">
<EventNoteIcon className="icon" />
<h2>Program Details</h2>
</div>
<p><strong>Name:</strong> {donatedItem.program.name}</p>
<p><strong>Description:</strong> {donatedItem.program.description}</p>
<p><strong>Start Date:</strong> {formatDate(donatedItem.program.startDate)}</p>
<p><strong>Aim and Cause:</strong> {donatedItem.program.aimAndCause}</p>
</section>
</div>
</div>
</div>
);
};

export default DonatedItemDetails;
14 changes: 14 additions & 0 deletions client-app/src/Modals/DonatedItemModal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Donor } from '../Modals/DonorModal';
import { Program } from '../Modals/ProgramModal';
import { DonatedItemStatus } from './DonatedItemStatusModal';

export interface DonatedItem {
id: number;
itemType: string;
currentStatus: string;
dateDonated: string;
lastUpdated: string;
donor: Donor;
program: Program;
statuses: DonatedItemStatus[];
}
5 changes: 5 additions & 0 deletions client-app/src/Modals/DonatedItemStatusModal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface DonatedItemStatus {
id: number;
type: string;
dateModified: string;
}
13 changes: 13 additions & 0 deletions client-app/src/Modals/DonorModal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export interface Donor {
id: number;
firstName: string;
lastName: string;
contact: string;
email: string;
addressLine1: string;
addressLine2?: string;
state: string;
city: string;
zipcode: string;
emailOptIn: boolean;
}
7 changes: 7 additions & 0 deletions client-app/src/Modals/ProgramModal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export interface Program {
id: number;
name: string;
description: string;
startDate: string;
aimAndCause: string;
}
100 changes: 100 additions & 0 deletions client-app/src/css/DonatedItemDetails.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
.donated-item-details-container {
Anjali0407-git marked this conversation as resolved.
Show resolved Hide resolved
max-width: 1200px;
margin: auto;
padding: 20px;
background-color: #f3f6f9;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}

/* Two-column layout for the main details */
.details-grid {
display: grid;
grid-template-columns: 7fr 3fr; /* 70% for left column, 30% for right column */
gap: 20px;
}

/* Section styling */
.section-header {
display: flex;
align-items: center;
padding: 8px;
border-radius: 5px;
color: #fff;
}

.section-header h2 {
font-size: 20px;
margin: 0;
}

.section-header .icon {
font-size: 28px;
margin-right: 8px;
}

/* Left Column Styling (70% width) */
.left-column .item-details-section,
.left-column .status-logs-section {
padding: 15px;
border-radius: 8px;
margin-bottom: 20px;
}

.left-column .item-details-section {
background-color: #f7e7b9;
}

.left-column .item-details-section .section-header {
background-color: #d9b867;
}


.left-column .donated-item-status-section {
background-color: #badaf6;
}

.left-column .donated-item-status-section .section-header {
background-color: #7ca3c7;
}

/* Right Column Styling (30% width) */
.right-column .donor-details-section,
.right-column .program-details-section {
padding: 15px;
border-radius: 8px;
margin-bottom: 20px;
}

.right-column .donor-details-section {
background-color: #c8e0c9;
}

.right-column .donor-details-section .section-header {
background-color: #8aa88b;
}

.right-column .program-details-section {
background-color: #d5e5f3;
}

.right-column .program-details-section .section-header {
background-color: #7ca3c7;
}

/* Paragraph and text styling */
p {
margin: 8px 0;
font-size: 16px;
}

strong {
color: #555;
}

/* Responsive design for smaller screens */
@media (max-width: 768px) {
.details-grid {
grid-template-columns: 1fr; /* Stacks sections vertically on small screens */
}
}
2 changes: 1 addition & 1 deletion server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ Create a `.env` file and define the necessary environment variables:
DATABASE_URL="postgresql://username:password@localhost:5432/dbname"
PORT=5000
AZURE_STORAGE_ACCOUNT_NAME="mdmaproject"
AZURE_STORAGE_ACCESS_KEY="<enter-azure-storage-access-key>">
AZURE_STORAGE_ACCESS_KEY="<enter-azure-storage-access-key>"
```

Replace `username`, `password`, and `dbname` with your PostgreSQL username, password, and the name of the database you created.
Expand Down
66 changes: 56 additions & 10 deletions server/src/routes/donatedItemRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import prisma from '../prismaClient'; // Import Prisma client
import { donatedItemValidator } from '../validators/donatedItemValidator'; // Import the validator
import { validateDonor } from '../services/donorService';
import { validateProgram } from '../services/programService';
import { validateDonatedItem } from '../services/donatedItemService';
import { uploadToStorage, getFileExtension } from '../services/donatedItemService';
import { date } from 'joi';

const router = Router();
const upload = multer({ storage: multer.memoryStorage() });
Expand Down Expand Up @@ -58,8 +60,7 @@ router.post('/', [upload.array('imageFiles'), donatedItemValidator], async (req:
donatedItem: newItem,
donatedItemStatus: newStatus,
});



} catch (error) {
console.error('Error creating donated item:', error);
res.status(500).json({ message: 'Error creating donated item' });
Expand All @@ -69,17 +70,62 @@ router.post('/', [upload.array('imageFiles'), donatedItemValidator], async (req:
// GET /donatedItem - Fetch all donated items
router.get('/', async (req: Request, res: Response) => {
try {
const items = await prisma.donatedItem.findMany({
const donatedItems = await prisma.donatedItem.findMany({
include: {
donor: true, // Include all donor details
program: true, // Include all program details
statuses: {
orderBy: {
dateModified: 'asc' // Ensure they are ordered chronologically
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There seems to be an odd interaction where it sometimes gives updates that it should not

}
}
}
});
res.json(donatedItems);
} catch (error) {
if (error instanceof Error) {
console.error('Error fetching donated item:', error.message);
res.status(error.message.includes('must be an integer') ? 400 : 404).json({ error: error.message });
} else {
console.error('Error fetching donated item:', 'Unknown error');
res.status(500).json({ error: 'Unknown error' });
}

}
});


// GET /donatedItem - Fetch donated item by ID
router.get('/:id', async (req: Request, res: Response) => {
try {
const donatedItemId = parseInt(req.params.id);
await validateDonatedItem(donatedItemId);
const donatedItem = await prisma.donatedItem.findUnique({
where: { id: donatedItemId },
include: {
statuses: true, // Include related status updates
program:true,
donor:true,
},
donor: true, // Include all donor details
program: true, // Include all program details
statuses: {
orderBy: {
dateModified: 'asc' // Ensure they are ordered chronologically
}
}
}
});
res.json(items);

if (!donatedItem) {
return res.status(404).json({ error: `Donated item with ID ${donatedItemId} not found` });
}
res.json(donatedItem);
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Error fetching donated items' });
if (error instanceof Error) {
console.error('Error fetching donated item:', error.message);
res.status(error.message.includes('must be an integer') ? 400 : 404).json({ error: error.message });
} else {
console.error('Error fetching donated item:', 'Unknown error');
res.status(500).json({ error: 'Unknown error' });
}

}
});

Expand Down
Loading
Loading