Skip to content

Commit

Permalink
Create bidding history page
Browse files Browse the repository at this point in the history
  • Loading branch information
ltan02 committed Apr 13, 2024
1 parent cf6f747 commit 2e6c075
Show file tree
Hide file tree
Showing 20 changed files with 568 additions and 178 deletions.
4 changes: 2 additions & 2 deletions backend/auction/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ class Auction(MainModel):
end_date = models.DateTimeField(null=False)
cover_image = models.CharField(max_length=500, null=True, blank=True)
is_published = models.BooleanField(default=False)
start_time = models.TimeField(null=False, default="00:00")
end_time = models.TimeField(null=False, default="23:59")
start_time = models.TimeField(null=False, default="00:00:00")
end_time = models.TimeField(null=False, default="23:59:59")

def __str__(self):
return (
Expand Down
4 changes: 4 additions & 0 deletions backend/auction/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@
BidderVerificationApiView,
CurrentAuctionApiView,
GetSavedUnitApiView,
PastAuctionsApiView,
SaveUnitApiView,
UpcomingAuctionApiView,
)

urlpatterns = [
path("", AuctionListApiView.as_view(), name="auction_list"),
path("/current", CurrentAuctionApiView.as_view(), name="current_auction_list"),
path("/upcoming", UpcomingAuctionApiView.as_view(), name="upcoming_auction_list"),
path("/past", PastAuctionsApiView.as_view(), name="past_auction_list"),
path("/<uuid:auction_id>", AuctionDetailApiView.as_view(), name="auction_detail"),
path("/<uuid:auction_id>/day", AuctionDayApiView.as_view(), name="auction_day"),
path(
Expand Down
161 changes: 150 additions & 11 deletions backend/auction/views.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from datetime import timedelta
from datetime import datetime, time, timedelta

from django.contrib.contenttypes.models import ContentType
from django.core.paginator import Paginator
Expand Down Expand Up @@ -92,13 +92,13 @@ def post(self, request, *args, **kwargs):
if serializer.is_valid():
auction = serializer.save()

start_date = auction.start_date.date()
end_date = auction.end_date.date()
start_datetime = datetime.combine(auction.start_date.date(), time())
end_datetime = datetime.combine(auction.end_date.date(), time())
delta = timedelta(days=1)
current_date = start_date
current_date = start_datetime

while current_date <= end_date:
AuctionDay.objects.create(auction=auction, date=current_date)
while current_date <= end_datetime:
AuctionDay.objects.create(auction=auction, date=current_date.date())
current_date += delta

return Response(serializer.data, status=status.HTTP_201_CREATED)
Expand Down Expand Up @@ -129,25 +129,164 @@ class CurrentAuctionApiView(APIView):

def get(self, request, *args, **kwargs):
"""
Get the current auction
Get the current auction with counts of different types of items.
"""
today = timezone.now().date()
current_auctions = Auction.objects.filter(
start_date__lte=today, end_date__gte=today
).prefetch_related(
Prefetch(
"days",
queryset=AuctionDay.objects.prefetch_related(
Prefetch(
"items",
queryset=AuctionItem.objects.select_related("content_type"),
)
),
)
)

if not current_auctions.exists():
return Response(
{"message": "No current auction found"},
status=status.HTTP_404_NOT_FOUND,
status=status.HTTP_204_NO_CONTENT,
)

current_auction = current_auctions.first()

serialized_data = AuctionSerializer(current_auction)
truck_count = 0
equipment_count = 0
trailer_count = 0

for day in current_auction.days.all():
for item in day.items.all():
if isinstance(item.content_object, Vehicle):
truck_count += 1
elif isinstance(item.content_object, Equipment):
equipment_count += 1
elif isinstance(item.content_object, Trailer):
trailer_count += 1

serialized_data = AuctionSerializer(current_auction).data
serialized_data.update(
{
"truck_count": truck_count,
"equipment_count": equipment_count,
"trailer_count": trailer_count,
}
)

return Response(
serialized_data,
status=status.HTTP_200_OK,
)


class PastAuctionsApiView(APIView):
permission_classes = [AllowAny]
authentication_classes = []

def get(self, request, *args, **kwargs):
"""
Get all past auctions with counts of different types of items.
"""
today = timezone.now().date()
past_auctions = Auction.objects.filter(end_date__lt=today).prefetch_related(
Prefetch(
"days",
queryset=AuctionDay.objects.prefetch_related(
Prefetch(
"items",
queryset=AuctionItem.objects.select_related("content_type"),
)
),
)
)

auctions_data = []
for auction in past_auctions:
truck_count = 0
equipment_count = 0
trailer_count = 0

for day in auction.days.all():
for item in day.items.all():
if isinstance(item.content_object, Vehicle):
truck_count += 1
elif isinstance(item.content_object, Equipment):
equipment_count += 1
elif isinstance(item.content_object, Trailer):
trailer_count += 1

auction_data = AuctionSerializer(auction).data
auction_data.update(
{
"truck_count": truck_count,
"equipment_count": equipment_count,
"trailer_count": trailer_count,
}
)

auctions_data.append(auction_data)

return Response(auctions_data, status=status.HTTP_200_OK)


class UpcomingAuctionApiView(APIView):
permission_classes = [AllowAny]
authentication_classes = []

def get(self, request, *args, **kwargs):
"""
Get the closest upcoming auction.
"""
today = timezone.now().date()
upcoming_auctions = (
Auction.objects.filter(start_date__gt=today)
.order_by("start_date")
.prefetch_related(
Prefetch(
"days",
queryset=AuctionDay.objects.prefetch_related(
Prefetch(
"items",
queryset=AuctionItem.objects.select_related("content_type"),
)
),
)
)
)

if not upcoming_auctions.exists():
return Response(
status=status.HTTP_204_NO_CONTENT,
)

closest_auction = upcoming_auctions.first()

truck_count = 0
equipment_count = 0
trailer_count = 0

for day in closest_auction.days.all():
for item in day.items.all():
if isinstance(item.content_object, Vehicle):
truck_count += 1
elif isinstance(item.content_object, Equipment):
equipment_count += 1
elif isinstance(item.content_object, Trailer):
trailer_count += 1

auction_data = AuctionSerializer(closest_auction).data
auction_data.update(
{
"truck_count": truck_count,
"equipment_count": equipment_count,
"trailer_count": trailer_count,
}
)

return Response(
serialized_data.data,
auction_data,
status=status.HTTP_200_OK,
)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react';

export default function DownloadStatementOfAccountButton({ onClick }) {
return (
<button
type="button"
onClick={onClick}
className="px-[49px] py-[10px] rounded-[10px] border-2 border-solid border-mv-green items-center justify-center text-mv-green text-sm font-normal"
>
Download Statement of Account
</button>
);
}
5 changes: 4 additions & 1 deletion frontend/src/components/cards/BiddedItemCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import AgricultureIcon from '@mui/icons-material/Agriculture';
import StarsOutlinedIcon from '@mui/icons-material/StarsOutlined';
import EmojiEventsIcon from '@mui/icons-material/EmojiEvents';
import BidNowButton from '../buttons/BidNowButton';
import { priceToString } from '../../utils/priceUtil';

export default function BiddedItemCard({
// eslint-disable-next-line no-unused-vars
Expand Down Expand Up @@ -81,7 +82,9 @@ export default function BiddedItemCard({
<p className="text-mv-black text-sm font-normal leading-5 tracking-[0.1px]">
{isTopBid ? 'Your bid:' : 'Highest bid:'}
</p>
<p className="text-mv-black text-[26px] font-medium leading-5 tracking-[0.1px] mt-[12px]">{`₱${price}`}</p>
<p className="text-mv-black text-[26px] font-medium leading-5 tracking-[0.1px] mt-[12px]">{`₱${priceToString(
price
)}`}</p>
{isTopBid ? (
<div className="flex gap-x-[5px] mt-[38px]">
<p className="text-mv-black text-lg font-semibold leading-[23px] tracking-[0.1px]">
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/components/cards/SavedItemCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import AgricultureIcon from '@mui/icons-material/Agriculture';
import StarsOutlinedIcon from '@mui/icons-material/StarsOutlined';
import BidNowButton from '../buttons/BidNowButton';
import RemoveFromListButton from '../buttons/RemoveFromListButton';
import { priceToString } from '../../utils/priceUtil';

export default function SavedItemCard({
// eslint-disable-next-line no-unused-vars
Expand Down Expand Up @@ -80,7 +81,9 @@ export default function SavedItemCard({
<p className="text-mv-black text-sm font-normal leading-5 tracking-[0.1px]">
Highest bid:
</p>
<p className="text-mv-black text-[26px] font-medium leading-5 tracking-[0.1px] mt-[12px]">{`₱${price}`}</p>
<p className="text-mv-black text-[26px] font-medium leading-5 tracking-[0.1px] mt-[12px]">{`₱${priceToString(
price
)}`}</p>
<div className="mt-[40px] w-[100%] flex flex-col gap-y-[14px]">
<BidNowButton onClick={() => {}} size="sm" />
<RemoveFromListButton size="sm" onClick={() => {}} />
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/components/cards/VehicleItemCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import AddToListButton from '../buttons/AddToListButton';
import ViewModelButton from '../buttons/ViewModelButton';
import QuickViewButton from '../buttons/QuickViewButton';
import QuickViewModal from '../modals/QuickViewModal';
import { priceToString } from '../../utils/priceUtil';

export default function VehicleItemCard({
vehicleId,
Expand Down Expand Up @@ -97,7 +98,9 @@ export default function VehicleItemCard({
</p>
</span>
<span className="mb-[5px]">
<p className="text-mv-black text-[28px] font-medium leading-5 tracking-[0.1px]">{`₱${price}`}</p>
<p className="text-mv-black text-[28px] font-medium leading-5 tracking-[0.1px]">{`₱${priceToString(
price
)}`}</p>
</span>
<ViewModelButton onClick={handleViewClick} />
<span className="mb-[2px]">
Expand Down
105 changes: 105 additions & 0 deletions frontend/src/components/cards/WonItemCard.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import React from 'react';
import { useNavigate } from 'react-router';
import DirectionsBusIcon from '@mui/icons-material/DirectionsBus';
import AgricultureIcon from '@mui/icons-material/Agriculture';
import StarsOutlinedIcon from '@mui/icons-material/StarsOutlined';
import ViewModelButton from '../buttons/ViewModelButton';
import { priceToString } from '../../utils/priceUtil';

export default function VehicleItemCard({
vehicleId,
description,
modelNumber,
engineNumber,
chassisNumber,
price,
imageUrl,
}) {
const navigate = useNavigate();

const handleViewClick = () => {
navigate(`/listings/${vehicleId}`);
};

return (
<div className="flex w-full h-[314px] rounded-[20px] shadow-searchBarShadow">
<div className="w-1/3 relative rounded-l-[20px]">
<img
className="w-full h-full rounded-l-[20px] object-cover object-center"
src={imageUrl}
alt={description}
/>
</div>
<div className="w-2/3 flex pl-[58px] pt-[39px] pb-[44.5px] pr-[45px]">
<div className="w-[70%] flex flex-col gap-y-[23px]">
<div
className="font-semibold text-xl text-mv-black tracking-[0.5px] leading-7"
style={{
display: '-webkit-box',
WebkitLineClamp: 3,
WebkitBoxOrient: 'vertical',
overflow: 'hidden',
textOverflow: 'ellipsis',
}}
>
{description}
</div>

<div className="inline-flex flex-row text-mv-black text-base">
<div className="flex flex-col gap-y-[23px] items-start">
<div className="flex flex-row items-center justify-start gap-x-[19px]">
<DirectionsBusIcon
className="text-dark-grey"
sx={{ fontSize: 25 }}
/>
<div className="flex flex-col leading-6 tracking-[0.5px]">
<p className="font-medium">Model</p>
<p className="font-normal">{modelNumber}</p>
</div>
</div>

<div className="flex flex-row items-center justify-start gap-x-[19px]">
<AgricultureIcon
className="text-dark-grey"
sx={{ fontSize: 25 }}
/>
<div className="flex flex-col leading-6 tracking-[0.5px]">
<p className="font-medium">Chassis</p>
<p className="font-normal">{chassisNumber}</p>
</div>
</div>
</div>

<div className="flex flex-col gap-y-[23px] items-start">
<div className="flex flex-row items-center justify-start gap-x-[19px]">
<StarsOutlinedIcon
className="text-dark-grey"
sx={{ fontSize: 25 }}
/>
<div className="flex flex-col leading-6 tracking-[0.5px]">
<p className="font-medium">Engine no.</p>
<p className="font-normal">{engineNumber}</p>
</div>
</div>
</div>
</div>
</div>
<div className="w-[30%] flex flex-col items-center justify-between gap-y-[13px]">
<div className="w-full flex flex-col items-center justify-center gap-y-[12px]">
<span>
<p className="text-mv-black text-base font-normal leading-5 tracking-[0.1px]">
Your bid
</p>
</span>
<span className="mb-[5px]">
<p className="text-mv-black text-[28px] font-medium leading-5 tracking-[0.1px]">{`₱${priceToString(
price
)}`}</p>
</span>
</div>
<ViewModelButton onClick={handleViewClick} />
</div>
</div>
</div>
);
}
Loading

0 comments on commit 2e6c075

Please sign in to comment.