-
Notifications
You must be signed in to change notification settings - Fork 54
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add title to bid0 of all auctions * Add admin page and links between the two pages * Remove unecessary css * Remove Bootstrap prefixes from data attributes * Admin btn only visible if user doc has admin field * Populate winning column with usernames * Add debug log to show user read * Add is to admin button on admin page * Move admin functions to `admin.js` * Update README.md * Fix table CSS on main page * Move setup logic out of html files * Refactor reset functions to use `getItems()` * Move generateRandomAuctionData data to `demo.js` * Convert everything to data attributes * Document changes * Remove missed bootstrap prefix
- Loading branch information
Showing
10 changed files
with
439 additions
and
240 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
<!doctype html> | ||
<html class="no-js" lang="en"> | ||
|
||
<head> | ||
<meta charset="utf-8" /> | ||
<meta name="robots" content="NOINDEX, NOFOLLOW"> | ||
<!--add no follow tag to keep out of front facing index and search engines--> | ||
<meta name="description" content="An auction website"> | ||
<meta property="og:image" content="https://www.mellor.io/auction-website/img/banner.png"> | ||
<meta name="keywords" content="Online Auctions"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
<title>Markatplace</title> | ||
<link rel="icon" type="image/png" href="./img/favicon.ico"> | ||
|
||
<!-- Bootstrap --> | ||
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" | ||
integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ" crossorigin="anonymous"> | ||
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" | ||
integrity="sha384-ENjdO4Dr2bkBIFxQpeoTz1HIcje39Wm4jDKdf19U8gI4ddQ3GYNS7NTKfAdVQSZe" | ||
crossorigin="anonymous"></script> | ||
|
||
<!-- Custom CSS --> | ||
<link rel="stylesheet" href="./css/auction-website.css"> | ||
<link rel="preconnect" href="https://fonts.googleapis.com"> | ||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | ||
<link href="https://fonts.googleapis.com/css2?family=Roboto&display=swap" rel="stylesheet"> | ||
</head> | ||
|
||
<body> | ||
<!-- Navbar --> | ||
<nav class="navbar navbar-dark bg-primary"> | ||
<div class="container-fluid"> | ||
<a class="navbar-brand mb-0 h1 me-auto"> | ||
<img src="./img/logo.png" alt="" width="30" height="24" class="d-inline-block align-text-top"> | ||
The Markatplace | ||
</a> | ||
<div class="row row-cols-auto"> | ||
<a class="navbar-brand" id="username-display"></a> | ||
<a id="admin-button" class="btn btn-secondary me-2" href="index.html" role="button">Main</a> | ||
<button id="auth-button" class="btn btn-secondary me-2">Sign up</button> | ||
</div> | ||
</div> | ||
</nav> | ||
<!-- Grid of auction items --> | ||
<div id="table-container" class="container"> | ||
<table class="table"> | ||
<thead> | ||
<tr> | ||
<th scope="col">#</th> | ||
<th scope="col">Title</th> | ||
<th scope="col">Price</th> | ||
<th scope="col">Bids</th> | ||
<th scope="col">Winning</th> | ||
<th scope="col">Time left</th> | ||
</tr> | ||
</thead> | ||
<tbody></tbody> | ||
</table> | ||
|
||
<footer class="d-flex flex-wrap justify-content-between align-items-center py-3 my-4 border-top"> | ||
<div class="col-md-4 d-flex align-items-center"> | ||
<span class="text-muted">© 2022 Harry Mellor</span> | ||
</div> | ||
|
||
<ul class="nav col-md-4 justify-content-end list-unstyled d-flex"> | ||
<li class="ms-3"><a class="bi bi-github text-muted" href="https://github.com/HMellor/" width="24" | ||
height="24"></a></li> | ||
</ul> | ||
</footer> | ||
</div> | ||
|
||
<!-- Login popup --> | ||
<div id="login-modal" class="modal" tabindex="-1"> | ||
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable"> | ||
<div class="modal-content"> | ||
<div class="modal-header"> | ||
<h5 id="login-modal-title" class="modal-title">Sign up for Markatplace Auction</h5> | ||
</div> | ||
<div class="modal-body"> | ||
<p>We use anonymous authentication provided by Google. Your account is attached to your device signature.</p> | ||
<p>The username just lets us know who's bidding!</p> | ||
<form onsubmit="return false;"> | ||
<div class="form-floating mb-3"> | ||
<input type="username" class="form-control" id="username-input" placeholder="username"> | ||
<label for="username-input">Username</label> | ||
</div> | ||
<div class="modal-footer"> | ||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" | ||
aria-label="Cancel">Cancel</button> | ||
<button type="submit" class="btn btn-primary">Sign up</button> | ||
</div> | ||
</form> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
<!-- jQuery --> | ||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script> | ||
<!-- Create anonymous account --> | ||
<script type="module"> | ||
import { autoSignIn } from "./js/popups.js"; | ||
import { setupTable } from "./js/admin.js"; | ||
autoSignIn(); | ||
setupTable(); | ||
</script> | ||
</body> | ||
|
||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,6 @@ html * { | |
width: 100%; | ||
} | ||
|
||
.table { | ||
.card > .table { | ||
margin-bottom: 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
// Imports | ||
import { db } from "./firebase.js"; | ||
import { timeBetween, getItems } from "./auctions.js"; | ||
import { doc, setDoc, getDoc, updateDoc, deleteField, onSnapshot } from "https://www.gstatic.com/firebasejs/9.20.0/firebase-firestore.js"; | ||
|
||
let table = document.querySelector("tbody"); | ||
|
||
function createRow(item) { | ||
let row = document.createElement("tr") | ||
row.id = `auction-${item}` | ||
|
||
let header = document.createElement("th") | ||
header.scope = "row" | ||
header.innerText = item | ||
row.appendChild(header) | ||
|
||
row.appendChild(document.createElement("td")) | ||
row.appendChild(document.createElement("td")) | ||
row.appendChild(document.createElement("td")) | ||
row.appendChild(document.createElement("td")) | ||
row.appendChild(document.createElement("td")) | ||
|
||
return row | ||
} | ||
|
||
function dataListener() { | ||
// Listen for updates in active auctions | ||
onSnapshot(doc(db, "auction", "items"), (items) => { | ||
console.debug("dataListener() read from auction/items") | ||
// Parse flat document data into structured Object | ||
let data = {} | ||
for (const [key, details] of Object.entries(items.data())) { | ||
let [item, bid] = key.split("_").map(i => Number(i.match(/\d+/))) | ||
data[item] = data[item] || {} | ||
data[item][bid] = details | ||
} | ||
// Use structured Object to populate the row for each item | ||
for (const [item, bids] of Object.entries(data)) { | ||
let row = table.querySelector(`#auction-${item}`) | ||
if (row == null) { | ||
row = createRow(item) | ||
table.appendChild(row) | ||
} | ||
// Extract bid data | ||
let bidCount = Object.keys(bids).length - 1 | ||
row.children[1].innerText = bids[0].title | ||
row.children[2].innerText = `£${bids[bidCount].amount.toFixed(2)}` | ||
row.children[3].innerText = bidCount | ||
if (bids[bidCount].uid) { | ||
getDoc(doc(db, "users", bids[bidCount].uid)).then((user) => { | ||
row.children[4].innerText = user.get("name") | ||
console.debug("dataListener() read from users") | ||
}) | ||
} else { | ||
// Remove winner name if auction was reset | ||
row.children[4].innerText = "" | ||
} | ||
row.children[5].dataset.endTime = bids[0].endTime | ||
} | ||
}) | ||
} | ||
|
||
function setClocks() { | ||
let now = new Date().getTime() | ||
document.querySelectorAll("tbody > tr").forEach(row => { | ||
row.children[5].innerText = timeBetween(now, row.children[5].dataset.endTime); | ||
}) | ||
setTimeout(setClocks, 1000); | ||
} | ||
|
||
export function setupTable() { | ||
dataListener(); | ||
setClocks(); | ||
} | ||
|
||
function resetItem(i) { | ||
const docRef = doc(db, "auction", "items") | ||
getItems().then(items => { | ||
let initialState = {} | ||
getDoc(docRef).then((doc) => { | ||
console.debug("resetItem() read from auction/items") | ||
// Find all bids for item i | ||
let item = items[i] | ||
let keys = Object.keys(doc.data()).sort() | ||
keys.filter(key => key.includes(`item${i.toString().padStart(5, "0")}`)).forEach((key, idx) => { | ||
// Mark all except bid00000 to be deleted | ||
initialState[key] = idx ? deleteField() : { amount: item.startPrice, title: item.title, endTime: item.endTime } | ||
}) | ||
}).then(() => { | ||
updateDoc(docRef, initialState) | ||
console.debug("resetItem() write to from auction/items") | ||
}) | ||
}) | ||
} | ||
|
||
function resetAll() { | ||
getItems().then(items => { | ||
let initialState = {} | ||
items.forEach(item => { | ||
let field = `item${item.id.toString().padStart(5, "0")}_bid00000` | ||
initialState[field] = { amount: item.startPrice, title: item.title, endTime: item.endTime } | ||
}) | ||
setDoc(doc(db, "auction", "items"), initialState) | ||
console.debug("resetAll() write to auction/items") | ||
}) | ||
} | ||
|
||
window.resetItem = resetItem | ||
window.resetAll = resetAll |
Oops, something went wrong.