Skip to content

Commit

Permalink
outfit archive pages an outfit detail pages linked to db
Browse files Browse the repository at this point in the history
  • Loading branch information
ShiwenFang committed Apr 17, 2024
1 parent 92eaacc commit 412c5ff
Show file tree
Hide file tree
Showing 5 changed files with 216 additions and 64 deletions.
31 changes: 30 additions & 1 deletion back-end/db.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,35 @@ const clothing_item = new mongoose.Schema({
}, {collection: 'clothing'});

const Clothes = mongoose.model("Clothing", clothing_item);
export default {User, Clothes};

const outfitSchema = new mongoose.Schema({
outfitName: {
type: String,
required: true
},
outfitNotes: {
type: String,
required: false
},
items: [{
itemName: {
type: String,
required: true
},
itemType: {
type: String,
required: true
},
_id: false // Prevents Mongoose from creating an _id for each subdocument
}],
user: {
type: String,
required: true
}
}, { collection: 'outfit' });

const Outfit = mongoose.model("Outfit", outfitSchema);

export default {User, Clothes, Outfit};


128 changes: 110 additions & 18 deletions back-end/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -450,9 +450,48 @@ server.get('/accessories', auth, async (req, res) => {
}
});

server.get('/outfits', auth, (req, res) => {
return res.json(outfits);
})
// server.get('/outfits', auth, (req, res) => {
// return res.json(outfits);
// })

server.get('/outfits', auth, async (req, res) => {
try {
const userId = req.user.id.toString(); // Assuming user ID is in the user object
const outfits = await Outfit.find({ user: userId }); // Fetch outfits for the user

if (!outfits.length) {
return res.status(404).json({ message: 'No outfits found for this user.' });
}

// Initialize a promise array to handle multiple asynchronous operations
const outfitsWithImageLinks = await Promise.all(outfits.map(async (outfit) => {
// Map over items in each outfit to fetch corresponding clothes
const imageLinks = await Promise.all(outfit.items.map(async (item) => {
const clothingItem = await Clothes.findOne({
user: userId,
nameItem: item.itemName,
articleType: item.itemType
}).select('imgLink'); // Assuming 'imgLink' is the field name in Clothes schema

return clothingItem ? clothingItem.imgLink : null;
}));

// Filter out any null values if an item wasn't found
const filteredImageLinks = imageLinks.filter(link => link !== null);

return {
outfitName: outfit.outfitName,
//outfitNotes: outfit.outfitNotes,
imageLinks: filteredImageLinks
};
}));

res.json(outfitsWithImageLinks);
} catch (error) {
console.error('Server error when fetching outfits:', error);
res.status(500).json({ message: 'Internal Server Error' });
}
});

server.get('/verify_login', auth, (req,res) => {
return res.json({'loggedIn': true})
Expand All @@ -462,15 +501,15 @@ const listener = server.listen(port, function () {
console.log(`Server running on port: ${port}`)
})

const findItemByName = (itemName) => {
// Combine all items into a single array
const allItems = [...shirts, ...pants, ...skirts, ...jackets, ...shoes, ...accessories];
// const findItemByName = (itemName) => {
// // Combine all items into a single array
// const allItems = [...shirts, ...pants, ...skirts, ...jackets, ...shoes, ...accessories];

// Find the item by name (case-insensitive comparison)
const item = allItems.find(item => item.name.toLowerCase() === itemName.toLowerCase());
// // Find the item by name (case-insensitive comparison)
// const item = allItems.find(item => item.name.toLowerCase() === itemName.toLowerCase());

return item; // This will be the item if found, or undefined if not found
};
// return item; // This will be the item if found, or undefined if not found
// };

// server.get('/item-detail/:itemName', (req, res) => {
// try {
Expand Down Expand Up @@ -532,20 +571,58 @@ server.delete('/delete-item/:itemName', auth, async (req, res) => {
}
});

const findOutfitByName = (outfitName) => {
// Assuming you have an array of outfits
const outfit = outfits.find(outfit => outfit.outfitName.toLowerCase() === outfitName.toLowerCase());
return outfit;
};
// const findOutfitByName = (outfitName) => {
// // Assuming you have an array of outfits
// const outfit = outfits.find(outfit => outfit.outfitName.toLowerCase() === outfitName.toLowerCase());
// return outfit;
// };

server.get('/outfit-detail/:outfitName', (req, res) => {
// server.get('/outfit-detail/:outfitName', (req, res) => {
// try {
// const { outfitName } = req.params;
// const decodedName = decodeURIComponent(outfitName);
// const outfit = findOutfitByName(decodedName);

// if (outfit) {
// res.json(outfit);
// } else {
// res.status(404).json({ message: 'Outfit not found' });
// }
// } catch (error) {
// console.error('Server error when fetching outfit details:', error);
// res.status(500).json({ message: 'Internal Server Error' });
// }
// });

server.get('/outfit-detail/:outfitName', auth, async (req, res) => {
try {
const { outfitName } = req.params;
const decodedName = decodeURIComponent(outfitName);
const outfit = findOutfitByName(decodedName);
const userId = req.user.id.toString(); // Assuming you're using the user's id stored in req.user by your authentication middleware

// Find the outfit by name and user
const outfit = await Outfit.findOne({
outfitName: decodedName,
user: userId
}).exec();

if (outfit) {
res.json(outfit);
// Now find all the items that are listed in the outfit's items
const itemsPromises = outfit.items.map(item =>
Clothes.findOne({
nameItem: item.itemName,
articleType: item.itemType,
user: userId
}).exec()
);

// Execute all the promises to get the items' details
const itemsDetails = await Promise.all(itemsPromises);

// Filter out any null results in case some items weren't found
const items = itemsDetails.filter(item => item !== null);

res.json({ outfit, items }); // Send both outfit details and items
} else {
res.status(404).json({ message: 'Outfit not found' });
}
Expand All @@ -555,6 +632,21 @@ server.get('/outfit-detail/:outfitName', (req, res) => {
}
});

server.delete('/outfit-detail/:outfitName', auth, async (req, res) => {
try {
const decodedName = decodeURIComponent(req.params.outfitName);
const userId = req.user.id.toString()
await Outfit.findOneAndDelete({
outfitName: decodedName,
user: userId
});
res.json({ message: 'Outfit deleted successfully' });
} catch (error) {
console.error('Server error when deleting outfit:', error);
res.status(500).json({ message: 'Internal Server Error' });
}
});

// Set up the storage configuration for multer
const storage = multer.diskStorage({
destination: (req, file, cb) => {
Expand Down
39 changes: 16 additions & 23 deletions front-end/src/screens/Archive.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,22 @@ const Archive = () => {
const [loginWarning, setLoginWarning] = useState(false);

useEffect(() => {
// Fetch the outfits from your server
const token = localStorage.getItem('token');
const config = {
const fetchOutfits = async () => {
const token = localStorage.getItem('token');
try {
const response = await axios.get('http://localhost:3001/outfits', {
headers: {
Authorization: `Bearer ${token}`
}
};
// fetch('http://localhost:3001/outfits', config)
// .then(response => response.json())
// .then(data => setOutfits(data))
// .catch(error => {
// console.log(error);
// setLoginWarning(true);
// }
// );
axios.get('http://localhost:3001/outfits', config)
.then( res => {
setOutfits(res.data)
})
.catch((e) => {
console.log(e)
setLoginWarning(true);
})
});
setOutfits(response.data); // Assume response.data is the array of outfits with image links
} catch (error) {
console.error('Error fetching outfits:', error);
setLoginWarning(true);
}
};

fetchOutfits();
}, []);

return (
Expand All @@ -45,10 +38,10 @@ const Archive = () => {
{outfits.map((outfit) => ( // Removed index, using outfitName as key
<div key={outfit.outfitName} className="Archive-item">
<div className="Archive-images">
{outfit.items.map((item) => ( // Removed itemIndex, key will be provided by the Link component
<Link to={`/outfit-detail/${encodeURIComponent(outfit.outfitName)}`} key={item.name} className="Archive-item-link">
{outfit.imageLinks.map((imgLink) => ( // Removed itemIndex, key will be provided by the Link component
<Link to={`/outfit-detail/${encodeURIComponent(outfit.outfitName)}`} key={imgLink} className="Archive-item-link">
<div className="Archive-image-container">
<img src={`http://localhost:3001${item.img}`} alt={item.name} className="Archive-image"/>
<img src={`http://localhost:3001${imgLink}`} alt={outfit.outfitName} className="Archive-image"/>
</div>
</Link>
))}
Expand Down
62 changes: 41 additions & 21 deletions front-end/src/screens/OutfitDetail.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,55 @@ import React, { useState, useEffect } from 'react';
import { useParams, useNavigate, Link } from 'react-router-dom';
import OverlayMenu from '../components/OverlayMenu';
import '../styles/OutfitDetail.css';
import axios from 'axios';

const OutfitDetail = () => {
const { outfitName } = useParams();
const navigate = useNavigate();
const [outfit, setOutfit] = useState(null);
const [outfitDetails, setOutfitDetails] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);

useEffect(() => {
const encodedOutfitName = encodeURIComponent(outfitName);
fetch(`http://localhost:3001/outfit-detail/${encodedOutfitName}`)
.then(response => {
if (!response.ok) throw new Error('Network response was not ok');
return response.json();
})
.then(data => {
setOutfit(data);
setLoading(false);
})
.catch(error => {
setError(error.toString());
setLoading(false);
});
const token = localStorage.getItem('token');
axios.get(`http://localhost:3001/outfit-detail/${encodedOutfitName}`, {
headers: {
Authorization: `Bearer ${token}`
}
})
.then(response => {
setOutfitDetails(response.data);
setLoading(false);
})
.catch(error => {
setError(error.toString());
setLoading(false);
});
}, [outfitName]);

const handleBackClick = () => navigate(-1);

if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
if (!outfit) return <div>Outfit not found</div>;
if (!outfitDetails) return <div>Outfit not found</div>;

const handleDeleteOutfit = async () => {
try {
const encodedOutfitName = encodeURIComponent(outfitName);
const token = localStorage.getItem('token');
await axios.delete(`http://localhost:3001/outfit-detail/${encodedOutfitName}`, {
headers: {
Authorization: `Bearer ${token}`
}
});
alert('Outfit deleted successfully');
navigate('/'); // or navigate to the appropriate page after deletion
} catch (error) {
setError('Error deleting outfit: ' + error.response.data.message);
}
};


return (
<div className="OutfitDetail">
Expand All @@ -41,22 +60,23 @@ const OutfitDetail = () => {
<h3>Outfit Details</h3>
</header>
<div className="OutfitDetail-container">
{outfit.items.map(item => (
<Link to={`/item-detail/${encodeURIComponent(item.name)}`} key={item.name} className="OutfitDetail-item-link">
{outfitDetails.items.map((item) => (
<Link to={`/item-detail/${encodeURIComponent(item.itemName)}`} key={item.itemName} className="OutfitDetail-item-link">
<div className="OutfitDetail-item">
<img src={`http://localhost:3001${item.img}`} alt={item.name} className="OutfitDetail-image" />
<img src={`http://localhost:3001${item.imgLink}`} alt={item.itemName} className="OutfitDetail-image" />
<div className="OutfitDetail-info">
<h3>{item.name}</h3>
<h3>{item.itemName}</h3>
<p>Brand: {item.brand}</p>
<p>Type: {item.type}</p>
</div>
</div>
</Link>
))}
<h2 className="OutfitDetail-name">{outfit.outfitName}</h2>
<p className="OutfitDetail-notes">{outfit.notes}</p>
<h2 className="OutfitDetail-name">{outfitDetails.outfit.outfitName}</h2>
<p className="OutfitDetail-notes">{outfitDetails.outfit.outfitNotes}</p>
</div>
<button onClick={handleBackClick} className="OutfitDetail-backButton">BACK</button>
<button onClick={handleDeleteOutfit} className="OutfitDetail-deleteButton">DELETE</button>
</div>
);
};
Expand Down
20 changes: 19 additions & 1 deletion front-end/src/styles/OutfitDetail.css
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,22 @@
.OutfitDetail-item-link {
text-decoration: none; /* Removes underline from links */
color: inherit; /* Keeps text color consistent with the rest of the item */
}
}
.OutfitDetail-deleteButton {
position: fixed; /* Fixed position */
bottom: 25px; /* Distance from the bottom */
left: 25px; /* Distance from the right */
padding: 10px 20px;
font-family: 'Cormorant Infant', serif;
font-size: 20px;
color: #000; /* Black text */
background-color: transparent; /* Transparent background */
border: none; /* No border */
text-decoration: underline; /* Underline text */
cursor: pointer;
}

.OutfitDetail-deleteButton:hover {
text-decoration: none; /* Remove underline on hover for effect */
}

0 comments on commit 412c5ff

Please sign in to comment.