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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ __pycache__/

__pycache__/

db.sqlite3

# Editor directories and files
.vscode/*
Expand Down
18 changes: 18 additions & 0 deletions backend/app/migrations/0005_alter_listing_size.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.1.1 on 2024-09-15 09:35

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('app', '0004_listing_size'),
]

operations = [
migrations.AlterField(
model_name='listing',
name='size',
field=models.CharField(default='N/A', max_length=40),
),
]
2 changes: 1 addition & 1 deletion backend/app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ class Listing(models.Model):
price = models.PositiveIntegerField()
imageURLs = models.TextField() # Storing the AWS URLs as a comma-separated string
active = models.BooleanField(default=True) # represents whether listing is active or not
size = models.CharField(max_length=20, default='N/A')
size = models.CharField(max_length=40, default='N/A')
4 changes: 2 additions & 2 deletions backend/app/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#arguments: 1. url pattern, 2. function to be called (from views.py)
#3. name which can be used to reference
path('post/', createListing, name='createListing'),
path('patch/', updateListing, name='updateListing'),
path('patch/<int:id>/', updateListing, name='updateListing'),
path('get/', getListing, name='getListing'),
path('delete/<int:id>', deleteListing, name='deleteListing'),
path('delete/<int:id>/', deleteListing, name='deleteListing'),
]
13 changes: 0 additions & 13 deletions backend/app/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,19 +55,6 @@ def getListing(request):
serializer = ListingSerializer(listings, many=True)
#return the serialized listings
return Response(serializer.data)

# @api_view(['PATCH'])
# def joinListing(request):
# if request.method == 'PATCH':
# id = request.data.get('id')

# if not id:
# return Response({'error': 'ID is required'}, status=status.HTTP_400_BAD_REQUEST)

# listing = get_object_or_404(Listing, id=id)
# listing.save()

# return Response({'message': 'Successfully updated the listing'}, status=status.HTTP_200_OK)

# delete a listing
@api_view(['DELETE'])
Expand Down
Binary file modified backend/db.sqlite3
Binary file not shown.
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React</title>
<title> GradGoods </title>
</head>
<body>
<div id="root"></div>
Expand Down
1 change: 1 addition & 0 deletions src/components/ItemCard.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from "react";
import { Card, CardBody, Image, Stack, Heading, Text } from "@chakra-ui/react";
import axios from "axios";

// item fields:
// title, imageUrl, category,
Expand Down
4 changes: 2 additions & 2 deletions src/components/navbar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ export default function Navbar() {
GradGoods
</Link>
</div>
<div className="flex items-center space-x-4">
<div className="flex items-center space-x-8">
<SignedIn>
<Link to="/postListing" className="text-lg text-gray-800 transition duration-300 ease-in-out hover:text-green-500">
Sell
🍃 Sell
</Link>{" "}
{/* Use Link instead of a */}
<UserButton>
Expand Down
181 changes: 85 additions & 96 deletions src/pages/profile.jsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import React, { useState, useEffect } from "react";
import { useUser } from "@clerk/clerk-react";
import Navbar from "../components/navbar";
import { Heading, Divider } from "@chakra-ui/react";
import { Heading, Divider, Button } from "@chakra-ui/react";
import axios from "axios";
import placeholder from "../assets/placeholder.jpeg";
import ItemCard from "../components/ItemCard";
import { useNavigate } from "react-router-dom";
import OpenAI from "openai";
Expand All @@ -21,10 +20,9 @@ export default function Profile() {
const [userListings, setUserListings] = useState([]);
const [userActiveListings, setUserActiveListings] = useState([]);
const [userInactiveListings, setUserInactiveListings] = useState([]);

const [isMarkingComplete, setIsMarkingComplete] = useState(true);
const [active, setActive] = useState(true);

// for inactive listings, add up all the prices to calculate money earned
const calculateEarnings = () => {
return userInactiveListings.reduce(
(total, listing) => total + (listing.price || 0),
Expand All @@ -33,132 +31,120 @@ export default function Profile() {
};

useEffect(() => {
if (isSignedIn && user) {
if (isSignedIn && isLoaded && user) {
const fetchData = async () => {
try {
const response = await axios.get("http://127.0.0.1:8000/api/get/");
const database = response.data;
if (user) {
setListings(response.data);
const userJoinedListings = database.filter(
(listing) => listing.owner === user.username
);
setUserListings(userJoinedListings);

const activeListings = userJoinedListings.filter(
(listing) => listing.active === true
);
setUserActiveListings(activeListings);

const inactiveListings = userJoinedListings.filter(
(listing) => listing.active === false
);
setUserInactiveListings(inactiveListings);
}
const userJoinedListings = database.filter(
(listing) => listing.owner === user.username
);
setUserListings(userJoinedListings);

const activeListings = userJoinedListings.filter(
(listing) => listing.active === true
);
console.log(activeListings);
setUserActiveListings(activeListings);

const inactiveListings = userJoinedListings.filter(
(listing) => listing.active === false
);
setUserInactiveListings(inactiveListings);
} catch (error) {
console.error("Error fetching Data:", error);
if (error.response) {
console.error("Response data:", error.response.data);
console.error("Response status:", error.response.status);
console.error("Response headers:", error.response.headers);
} else if (error.request) {
console.error("Request data:", error.request);
} else {
console.error("Error message:", error.message);
}
console.error("Error fetching data:", error);
}
};

const getPounds = async (item) => {
const completion = await openai.chat.completions.create({
model: "gpt-4o-mini",
messages: [
{ role: "system", content: "You are a helpful assistant." },
{
role: "user",
content: `Tell me with an integer number how many pounds a ${item} would weigh. Return your response as just a number and nothing else. If you don't understand for any given object, just return 0.`,
},
],
});
console.log(completion.choices[0].message.content);
};

fetchData();
userInactiveListings.forEach((listing) => {
const p = getPounds(listing.price);
setPounds(pounds + p);
});
calculateEarnings();
}
}, [isSignedIn, user]); // Dependencies

useEffect(() => {}, [active]);

if (!isSignedIn) {
return <div>Not signed in</div>;
}
}, [isSignedIn, isLoaded, user, isMarkingComplete]);

const handleCardClick = (id) => {
navigate(`/listing/${id}`);
if (!isMarkingComplete) {
markComplete(id);
} else {
navigate(`/listing/${id}`);
}
};

const { imageUrl } = user;
const params = new URLSearchParams();
params.set("height", "200");
params.set("width", "200");
params.set("quality", "100");
params.set("fit", "crop");
const imageSrc = `${imageUrl}?${params.toString()}`;
const markComplete = async (id) => {
try {
const response = await axios.patch(`http://127.0.0.1:8000/api/patch/${id}/`, {
active: false // The field to update
});
console.log("Listing marked complete:", response.data);

const updatedListings = listings.map((listing) =>
listing.id === id ? { ...listing, active: false } : listing
);
setListings(updatedListings);
setUserActiveListings(updatedListings.filter(listing => listing.active));
setUserInactiveListings(updatedListings.filter(listing => !listing.active));

setIsMarkingComplete(true);
} catch (error) {
console.error("Error marking listing as complete:", error);
}
};

return (
<div>
<div className="fixed top-0 left-0 w-full z-50">
<Navbar />
</div>
<div className="mt-24 mx-[5vw]">
<Heading
className="text-left mb-3"
as="h2"
p={0}
noOfLines={1}
>
<Heading className="text-left mb-3" as="h2" p={0} noOfLines={1}>
My Account
</Heading>
<div className="flex flex-row justify-between">
<div className="flex flex-row">
<img
src={imageSrc}
alt="pfp"
className="mr-6"
style={{
width: "150px",
height: "150px",
borderRadius: "50%",
objectFit: "cover",
}}
/>
<div className="flex items-center">
<Heading>{user.username}</Heading>
</div>
{isLoaded && user ? (
<>
<img
src={user.imageUrl || placeholder}
alt="Profile picture"
className="mr-6"
style={{
width: "150px",
height: "150px",
borderRadius: "50%",
objectFit: "cover",
}}
/>
<div className="flex items-center">
<Heading>{user?.username || "User"}</Heading>
</div>
</>
) : (
<div>Loading...</div>
)}
</div>

<div className="flex flex-col items-end">
{/* Dynamically display total earnings */}
<h1 className="text-3xl font-bold">
{" "}
💰 Earnings: ${calculateEarnings()}
</h1>
<h1 className="text-3xl font-bold text-green-500">
🍃 ${pounds} of waste saved
</h1>
<h1 className="text-3xl font-bold"> 💰 Earnings: ${calculateEarnings()}</h1>
<h1 className="text-3xl font-bold text-green-500"> 🍃 ${pounds} of waste saved</h1>
</div>
</div>

{/* Button aligned to the right above the divider */}
<div className="flex justify-end mb-2">
<button
className="rounded-md w-36 h-10 bg-green-500 text-white font-bold"
onClick={() => setIsMarkingComplete(false)}
>
Mark Complete
</button>
</div>
<Divider />

<div className="flex flex-row w-full h-full py-8">
<div className="flex flex-row w-1/4 h-full">
<div className="flex flex-col space-y-2">
<Heading mb={2} size="lg">My Listings</Heading>
<Heading mb={2} size="lg">
My Listings
</Heading>
<p
onClick={() => setActive(true)}
className={`text-lg font-bold hover:cursor-pointer transition ease-in-out duration-300 hover:text-green-500 ${
Expand All @@ -181,8 +167,11 @@ export default function Profile() {
<div className="flex gap-6 items-center flex-wrap">
{active
? userActiveListings.map((listing, index) => (
<div onClick={() => handleCardClick(listing.id)}>
<ItemCard item={listing} key={index} />
<div
onClick={() => handleCardClick(listing.id)}
key={index}
>
<ItemCard item={listing} />
</div>
))
: userInactiveListings.map((listing, index) => (
Expand Down