Skip to content

Commit

Permalink
Section 13 started
Browse files Browse the repository at this point in the history
  • Loading branch information
LastBrainiac committed Jan 1, 2025
1 parent 667c3b2 commit cab1a0e
Show file tree
Hide file tree
Showing 19 changed files with 432 additions and 73 deletions.
26 changes: 26 additions & 0 deletions devcerts/carsties.com.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
-----BEGIN CERTIFICATE-----
MIIEeDCCAuCgAwIBAgIQDJ1VXjHZbJZQOAtG2R75KjANBgkqhkiG9w0BAQsFADCB
iTEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMS8wLQYDVQQLDCZERVNL
VE9QLU8yQ0U1MEpcQXR0aWxhQERFU0tUT1AtTzJDRTUwSjE2MDQGA1UEAwwtbWtj
ZXJ0IERFU0tUT1AtTzJDRTUwSlxBdHRpbGFAREVTS1RPUC1PMkNFNTBKMB4XDTIz
MDcyNzE4MjU0N1oXDTI1MTAyNzE5MjU0N1owWjEnMCUGA1UEChMebWtjZXJ0IGRl
dmVsb3BtZW50IGNlcnRpZmljYXRlMS8wLQYDVQQLDCZERVNLVE9QLU8yQ0U1MEpc
QXR0aWxhQERFU0tUT1AtTzJDRTUwSjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBALUwibUNNYNMTE93b2C4V6azPR9a8OH7yyOnVbz7huHNTbWnpQ+LvX5d
ABnsyktreaGINUmXeAwnY1NZO0HfrKE9kkmggYEdDN5p8faM0P5m+s1r6L6RS+fk
TIaiWlrtrtSK28GGmRQ9U53w581oZsH6Pl5MFFaNgnmjwdI/NC8aqQFyB61x3muO
BvuVtVYOhLQPqMh0PN2Twze+RsSlxzoiuy8dqUtrx4A+LFVXmMdJi20BOp2o1DfV
urhoMeRiCAuKIrKBa3lwg1ZY4Lq8IBuVimcoGUgHr2zBLK/37Bmh5cjxqxZpjL6g
8nFoVoG760R2DYWaY22t5PluxGwoXtMCAwEAAaOBiTCBhjAOBgNVHQ8BAf8EBAMC
BaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwHwYDVR0jBBgwFoAUn3NJo+WrnF2huXiY
j9KsVuNJCTwwPgYDVR0RBDcwNYIQYXBwLmNhcnN0aWVzLmNvbYIQYXBpLmNhcnN0
aWVzLmNvbYIPaWQuY2Fyc3RpZXMuY29tMA0GCSqGSIb3DQEBCwUAA4IBgQABKsfW
BB1YGBDD+Pbci15Gn2RZpUsqrjzCmRRp/mX8jQEGSSwyiHeJZx2ZugZB6s1dlidg
RwN85XisjcBjhMs/fb55v0zVeKFnfXyUtPAikMxPCNBl620EzAcgOQx+mRA523Uc
T4FdY2I0EEF29e89ybs+mF2mloBaEW05+tnsmSGJuX2NyTl7oPwAkmJ4kMBNlDYT
XkS7toxDB2wz/vJhtlF/bVHZNRHXmkQSkA+7U1yK1udEfYc8sYkUxijzK/SnSU2r
7WOAdxM8KLD1+ja4W/nIXJxuCdFsRx2rDjKfUZfzTCoPKy+coVrP+Lm3PzRxlFF0
z9yjUHyLKBUr79MbEUAiZ/nWOIP4+EQs93oG7AxxWW1b8lDcy5uRk9Gtk7rY7qS5
WVZ+zQ/f7upARGips0cnSEEIvBBlDEiL3CNhI4Ir3SjoIi8Ws6vvO3QSmbQ/1I6I
Lwh3jY+pLJD9cPznpZCOAb4da+2MECZ/k6eqQ3Nz1lUwLc/jEOWONfOmwZM=
-----END CERTIFICATE-----
28 changes: 28 additions & 0 deletions devcerts/carsties.com.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC1MIm1DTWDTExP
d29guFemsz0fWvDh+8sjp1W8+4bhzU21p6UPi71+XQAZ7MpLa3mhiDVJl3gMJ2NT
WTtB36yhPZJJoIGBHQzeafH2jND+ZvrNa+i+kUvn5EyGolpa7a7UitvBhpkUPVOd
8OfNaGbB+j5eTBRWjYJ5o8HSPzQvGqkBcgetcd5rjgb7lbVWDoS0D6jIdDzdk8M3
vkbEpcc6IrsvHalLa8eAPixVV5jHSYttATqdqNQ31bq4aDHkYggLiiKygWt5cINW
WOC6vCAblYpnKBlIB69swSyv9+wZoeXI8asWaYy+oPJxaFaBu+tEdg2FmmNtreT5
bsRsKF7TAgMBAAECggEAKhCVt9nUNaJOCRjyW6MQflwYcaIcUGqzk5hyuEgw3VVb
mDPOh1hq6t5OerZVlMLj3xOPQ4GN1kyCq8QIlNeH7Xss7jj8N+x1o2E3UoUItDdW
PMCzrSJwW+rNtWgV0AxkTWXbdQd5nJBMTFu0hSSazqf5OM0y35FE3FafVd9kjxad
xyZZMvP2x8NQLUV/U344uJaN5bliwMQqLwlF0PJtWzknKs9ylzf8XrClSTNVcYhH
V9ekmNFbf1RhASlB6H56PEhi96FRGJ5hmM8E5npj8GKSCu6DaVx+EKtm1cz8UWlX
KyYyNyCDuG3tsYO3hOq20BCxnR/YKyjymSoR2UTBWQKBgQDAT5rmWRWy/mm5bp42
25uY90/vyN+Zyurh2PKQdjAla88ez9ptxUD4UIKoLtcb9cAglT5IZyow9jGfeMud
EH/0S6LBHh4Rsvt2m8AHSq4c/jxPJxk6IDfX8Eq1mgSdJygHnJHzjJZBMO3koEaE
pnTeEvSTncLpSncUwUrQXTn7PwKBgQDxMgxtcWo5MbZqpiBaZ5QL8cuK9YfxSsYs
pOqwu8maX/5D68uyiVZpPwbPjE12OaQN4UBW1qP8KulwTyMd7RB8hqt6ae683Jdv
+rVsiOJganOFww1Vtcl9yWXd4tq5bdireEjPscG/qtA+gKHLQBNAGB/X3L75Z9OY
8hWrFJdbbQKBgE+/cyO8o4PzfpI37gIftXmMbSipaxgSjq6TUZtYd4S9HUnQteq5
VsTiVhw6OFfO5Bpe6VIK/fMJ1PAlnoIGepkq2FYvEBcU2rSHx+z+Kg+52faU5yA0
hhPVAJYoRWuJ9k6Zveh6T0C/cpdRY4zUnjgNMkEYteAaUiFYOpcHYejvAoGAc9od
XAhJBHXbb/ihu6ra0q3una14Aa9v5wvD6uYYXP1UzV0aN4R+LAlpBRIYNvxSz5Pt
aoiotbwK6rYoYPx4mce54A+366FtYA0lTZ+mVLdtC8f3xL6PrMqrElwfwUeA9kPN
fW2daanrYso4bL83P+cWjIHsfTGg0msde1JlrTUCgYEAvA85aR+obFYV76jt/xJw
5ON/Kr1X4V+zJ6jjlqCVycoVAnFp3GZyVjsSFj3ckGwca+mzmibwO2OEW9auH0PQ
G2kRjyQcKf7xRUk+DFaCLllUc/n/hq3LPOJNrhcyvp+En6MYYI162nqcImOMQpLW
X9iQ7cY26j7iXlnP0izaoi0=
-----END PRIVATE KEY-----
Binary file added devcerts/mkcert.exe
Binary file not shown.
10 changes: 9 additions & 1 deletion frontend/web-app/app/actions/auctionActions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use server'

import { Auction, PagedResult } from "@/types";
import { Auction, Bid, PagedResult } from "@/types";
import { fetchWrapper } from "@/lib/fetchWrapper";
import { FieldValues } from "react-hook-form";
import { revalidatePath } from "next/cache";
Expand Down Expand Up @@ -32,4 +32,12 @@ export async function updateAuction(data: FieldValues, id: string) {

export async function deleteAuction(id:string) {
return await fetchWrapper.del(`auctions/${id}`);
}

export async function getBidsForAuction(id: string): Promise<Bid[]> {
return await fetchWrapper.get(`bids/${id}`);
}

export async function placeBidForAuction(auctionId: string, amount: number) {
return await fetchWrapper.post(`bids?auctionId=${auctionId}&amount=${amount}`, {})
}
4 changes: 4 additions & 0 deletions frontend/web-app/app/auctions/AuctionCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import CountdownTimer from './CountdownTimer'
import CarImage from './CarImage'
import { Auction } from '@/types'
import Link from 'next/link'
import CurrentBid from './CurrentBid'

type Props = {
auction: Auction
Expand All @@ -17,6 +18,9 @@ export default function AuctionCard({ auction }: Props) {
<div className='absolute bottom-2 left-2'>
<CountdownTimer auctionEnd={auction.auctionEnd} />
</div>
<div className='absolute top-2 right-2'>
<CurrentBid reservePrice={auction.reservePrice} amount={auction.currentHighBid} />
</div>
</div>
</div>
<div className='flex justify-between items-center mt-4'>
Expand Down
20 changes: 20 additions & 0 deletions frontend/web-app/app/auctions/CurrentBid.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react'

type Props = {
amount?: number
reservePrice: number
}

export default function CurrentBid({ amount, reservePrice }: Props) {
const text = amount ? '$' + amount : 'No bids';
const color = amount ? amount > reservePrice ? 'bg-green-600' : 'bg-amber-600' : 'bg-red-600'

return (
<div className={`
border-2 border-white text-white py-1 px-2 rounded-lg flex
justify-center ${color}
`}>
{text}
</div>
)
}
80 changes: 47 additions & 33 deletions frontend/web-app/app/auctions/Listings.tsx
Original file line number Diff line number Diff line change
@@ -1,42 +1,52 @@
'use client'

import React, { useEffect, useState } from 'react';
import AuctionCard from './AuctionCard';
import { Auction, PagedResult } from '@/types';
import AppPagination from '../components/AppPagination';
import { getData } from '../actions/auctionActions';
import Filters from './Filters';
import { useParamsStore } from '@/hooks/useParamsStore';
import { shallow } from 'zustand/shallow';
import qs from 'query-string';
import EmptyFilter from '../components/EmptyFilter';
"use client";

import React, { useEffect, useState } from "react";
import AuctionCard from "./AuctionCard";
import { Auction, PagedResult } from "@/types";
import AppPagination from "../components/AppPagination";
import { getData } from "../actions/auctionActions";
import Filters from "./Filters";
import { useParamsStore } from "@/hooks/useParamsStore";
import qs from "query-string";
import EmptyFilter from "../components/EmptyFilter";
import { useAuctionStore } from "@/hooks/useAuctionStore";
import { useShallow } from "zustand/react/shallow";

export default function Listings() {
const [data, setData] = useState<PagedResult<Auction>>();
const params = useParamsStore(state => ({
pageNumber: state.pageNumber,
pageSize: state.pageSize,
searchTerm: state.searchTerm,
orderBy: state.orderBy,
filterBy: state.filterBy,
seller: state.seller,
winner: state.winner
}), shallow);
const setParams = useParamsStore(state => state.setParams);
const url = qs.stringifyUrl({ url: '', query: params });
const [loading, setLoading] = useState(true);
const params = useParamsStore(useShallow((state) => ({
pageNumber: state.pageNumber,
pageSize: state.pageSize,
searchTerm: state.searchTerm,
orderBy: state.orderBy,
filterBy: state.filterBy,
seller: state.seller,
winner: state.winner,
})));

const data = useAuctionStore(useShallow((state) => ({
auctions: state.auctions,
totalCount: state.totalCount,
pageCount: state.pageCount,
})));

const setData = useAuctionStore(state => state.setData);

const setParams = useParamsStore((state) => state.setParams);
const url = qs.stringifyUrl({ url: "", query: params });

function setPageNumber(pageNumber: number) {
setParams({ pageNumber });
}

useEffect(() => {
getData(url).then(data => {
getData(url).then((data) => {
setData(data);
})
}, [url]);
setLoading(false);
});
}, [url, setData]);

if (!data) return <h3>Loading...</h3>
if (loading) return <h3>Loading...</h3>;

return (
<>
Expand All @@ -45,16 +55,20 @@ export default function Listings() {
<EmptyFilter showReset />
) : (
<>
<div className='grid grid-cols-4 gap-6'>
{data.results.map((auction) => (
<div className="grid grid-cols-4 gap-6">
{data.auctions.map((auction) => (
<AuctionCard auction={auction} key={auction.id} />
))}
</div>
<div className='flex justify-center mt-4'>
<AppPagination currentPage={params.pageNumber} pageCount={data.pageCount} pageChanged={setPageNumber} />
<div className="flex justify-center mt-4">
<AppPagination
currentPage={params.pageNumber}
pageCount={data.pageCount}
pageChanged={setPageNumber}
/>
</div>
</>
)}
</>
)
);
}
42 changes: 42 additions & 0 deletions frontend/web-app/app/auctions/details/[id]/BidForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
'use client'

type Props = {
auctionId: string;
highBid: number;
}

import { placeBidForAuction } from '@/app/actions/auctionActions';
import { useBidStore } from '@/hooks/useBidStore';
import { numberWithCommas } from '@/lib/numberWithComma';
import React from 'react'
import { FieldValues, useForm } from 'react-hook-form';
import { toast } from 'react-hot-toast';

export default function BidForm({ auctionId, highBid }: Props) {
const {register, handleSubmit, reset, formState: {errors}} = useForm();
const addBid = useBidStore(state => state.addBid);

function onSubmit(data: FieldValues) {
if (data.amount <= highBid) {
reset();
return toast.error('Bid must be at least $' + numberWithCommas(highBid + 1))
}

placeBidForAuction(auctionId, +data.amount).then(bid => {
if (bid.error) throw bid.error;
addBid(bid);
reset();
}).catch(err => toast.error(err.message));
}

return (
<form onSubmit={handleSubmit(onSubmit)} className='flex items-center border-2 rounded-lg py-2'>
<input
type="number"
{...register('amount')}
className='flex-grow bg-transparent focus:outline-none border-transparent focus:border-transparent focus:ring-0 text-sm text-gray-600'
placeholder={`Enter your bid (minimum bid is $${numberWithCommas(highBid + 1)})`}
/>
</form>
)
}
55 changes: 55 additions & 0 deletions frontend/web-app/app/auctions/details/[id]/BidItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { numberWithCommas } from '@/lib/numberWithComma';
import { Bid } from '@/types'
import { format } from 'date-fns';
import React from 'react'

type Props = {
bid: Bid
}

export default function BidItem({ bid }: Props) {
function getBidInfo() {
let bgColor = '';
let text = '';
switch (bid.bidStatus) {
case 'Accepted':
bgColor = 'bg-green-200'
text = 'Bid accepted'
break;
case 'AcceptedBelowReserve':
bgColor = 'bg-amber-500'
text = 'Reserve not met'
break;
case 'TooLow':
bgColor = 'bg-red-200'
text = 'Bid was too low'
break
default:
bgColor = 'bg-red-200'
text = 'Bid placed after auction finished'
break;
}
return {bgColor, text}
}

return (
<div className={`
border-gray-300 border-2 px-3 py-2 rounded-lg
flex justify-between items-center mb-2
${getBidInfo().bgColor}
`}>
<div className='flex flex-col'>
<span>Bidder: {bid.bidder}</span>
<span className='text-gray-700 text-sm'>
Time: {format(new Date(bid.bidTime), 'dd MMM yyyy h:mm a')}
</span>
</div>
<div className='flex flex-col text-right'>
<div className='text-xl font-semibold'>${numberWithCommas(bid.amount)}</div>
<div className='flex flex-row items-center'>
<span>{getBidInfo().text}</span>
</div>
</div>
</div>
)
}
Loading

0 comments on commit cab1a0e

Please sign in to comment.