sync
@@ -0,0 +1,12 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>Day 21</title>
|
||||||
|
<link rel="stylesheet" href="styles.css">
|
||||||
|
<script src="script.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h2>Hello cruel world!</h2>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
let h2 = document.querySelector('h2');
|
||||||
|
h2.innerText = 'The DOM is Magical';
|
||||||
|
});
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
body {
|
||||||
|
background-color: indigo;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>Day 22</title>
|
||||||
|
<link rel="stylesheet" href="styles.css">
|
||||||
|
<script src="script.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="square"></div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
height: 100vh;
|
||||||
|
width: 100%;
|
||||||
|
border: 1px solid red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.square {
|
||||||
|
position: absolute;
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
background-color: pink;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
div {
|
||||||
|
height: 100px;
|
||||||
|
width: 100px;
|
||||||
|
background-color: black;
|
||||||
|
border: 1px solid white;
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>Dummy Title</title>
|
||||||
|
<link href="css/styles.css" type="text/css" rel="stylesheet">
|
||||||
|
<script src="js/index.js" defer></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<?php
|
||||||
|
for ($i = 0; $i < 100; $i++)
|
||||||
|
echo '<div></div>';
|
||||||
|
?>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
document.querySelectorAll('div').forEach((d) => {
|
||||||
|
d.style.backgroundColor = 'black';
|
||||||
|
d.addEventListener('click', populate);
|
||||||
|
})
|
||||||
|
|
||||||
|
function populate() {
|
||||||
|
if (this.style.backgroundColor === 'black') {
|
||||||
|
this.style.backgroundColor = 'red';
|
||||||
|
this.style.borderRadius = '50%';
|
||||||
|
} else {
|
||||||
|
this.style.backgroundColor = 'black';
|
||||||
|
this.style.borderRadius = '0%';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
div {
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
height: 100px;
|
||||||
|
width: 100px;
|
||||||
|
background-color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>Dummy Title</title>
|
||||||
|
<link href="css/styles.css" type="text/css" rel="stylesheet">
|
||||||
|
<script src="js/index.js" defer></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<button>Button</button>
|
||||||
|
<div></div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
const div = document.querySelector('div');
|
||||||
|
const button = document.querySelector('button')
|
||||||
|
|
||||||
|
let times = 1;
|
||||||
|
button.addEventListener('click', (e) => {
|
||||||
|
let px = 300 * times;
|
||||||
|
times++;
|
||||||
|
div.style.top = px + 'px';
|
||||||
|
});
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'functions/init.php';
|
||||||
|
|
||||||
|
if (!isset($_SESSION['id']))
|
||||||
|
header('Location: catalog.php');
|
||||||
|
|
||||||
|
// UI Components
|
||||||
|
foreach (glob('components/cart/*.php') as $file)
|
||||||
|
require_once $file;
|
||||||
|
|
||||||
|
require_once 'components/head.php';
|
||||||
|
|
||||||
|
require_once 'components/foot.php';
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
include_once 'functions/init.php';
|
||||||
|
include_once 'functions/catalog.php';
|
||||||
|
|
||||||
|
// UI Components
|
||||||
|
foreach (glob('components/products/*.php') as $file)
|
||||||
|
require_once $file;
|
||||||
|
|
||||||
|
include_once 'components/head.php';
|
||||||
|
|
||||||
|
echo '<h1>Catalog</h1>';
|
||||||
|
|
||||||
|
echo '<div id="catalog" class="std_width center">';
|
||||||
|
echo catalog_grid(fetch_catalog());
|
||||||
|
echo '</div>';
|
||||||
|
|
||||||
|
include_once 'components/foot.php';
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
function cart_item($item)
|
||||||
|
{
|
||||||
|
$item = '
|
||||||
|
<div class="row">
|
||||||
|
<div class="square img_container">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row space_between">
|
||||||
|
<p>' . $item['description'] . '</p>
|
||||||
|
<select>
|
||||||
|
<input type="number">
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
';
|
||||||
|
|
||||||
|
return $item;
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
</body>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
$scripts = '';
|
||||||
|
switch ($_SERVER['SCRIPT_NAME']) {
|
||||||
|
case '/index.php':
|
||||||
|
$scripts .= '<script src="js/login.js"></script>';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo $scripts;
|
||||||
|
?>
|
||||||
|
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="css/base.css">
|
||||||
|
<link rel="stylesheet" href="css/colors.css">
|
||||||
|
<?php
|
||||||
|
switch ($_SERVER['SCRIPT_NAME']) {
|
||||||
|
case '/create-account.php':
|
||||||
|
echo '<script src="js/create-account.js" defer></script>';
|
||||||
|
break;
|
||||||
|
case '/product.php':
|
||||||
|
if (!isset($_SESSION['id']))
|
||||||
|
echo '<script src="js/login-modal.js" defer></script>';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<?php include_once 'nav.php'; ?>
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
$nav = '
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<div class="row space_between">
|
||||||
|
<div>
|
||||||
|
<li><a href="catalog.php">Products</a></li>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="absolute_h_center">
|
||||||
|
<a href="index.php"><h4>ACME Store</h4></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
' . (isset($_SESSION['id']) ? '<li><a href="cart.php">Cart</a></li>' : '') . '
|
||||||
|
' . (isset($_SESSION['id']) ? '<li><a href="logout.php">Logout</a></li>' : '') . '
|
||||||
|
' . (!isset($_SESSION['id']) ? '<li><a href="index.php">Login</a></li>' : '') . '
|
||||||
|
' . (!isset($_SESSION['id']) ? '<li><a href="create-account.php">Signup</a></li>' : '') . '
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
';
|
||||||
|
|
||||||
|
echo $nav;
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
function product_card($product)
|
||||||
|
{
|
||||||
|
$card = '
|
||||||
|
<a class="card column center full_width padding" href="product.php?id=' . $product['id'] . '">
|
||||||
|
<div class="img_container">
|
||||||
|
<img src="img/' . $product['image'] . '">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<h4>' . $product['name'] . '</h4>
|
||||||
|
<h5>' . $product['price'] . '</h5>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
';
|
||||||
|
|
||||||
|
return $card;
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
function catalog_grid($products)
|
||||||
|
{
|
||||||
|
$grid = '
|
||||||
|
<div class="grid center">
|
||||||
|
';
|
||||||
|
|
||||||
|
foreach ($products as $p)
|
||||||
|
$grid .= product_card($p);
|
||||||
|
|
||||||
|
$grid .= '
|
||||||
|
</div>
|
||||||
|
';
|
||||||
|
|
||||||
|
return $grid;
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
function product_page($product)
|
||||||
|
{
|
||||||
|
$page = '
|
||||||
|
<div class="product_page_card column center full_width padding">
|
||||||
|
<div class="img_container">
|
||||||
|
<img src="img/' . $product['image'] . '">
|
||||||
|
</div>
|
||||||
|
<div class="column">
|
||||||
|
<div class="row space_between">
|
||||||
|
<h3>' . $product['name'] . '</h3>
|
||||||
|
<h3>' . $product['price'] . '</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>' . $product['description'] . '</p>
|
||||||
|
<form class="row" id="add_to_cart">
|
||||||
|
<input type="hidden" name="is_logged_in" value="' . (isset($_SESSION['id']) ? 'true' : 'false') . '">
|
||||||
|
<div class="column margin">
|
||||||
|
<label for="quantity">Quantity:</label>
|
||||||
|
<input type="number" value="1" id="quantity" name="quantity" min="1">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input type="submit" value="Add to Cart" class="margin">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal">
|
||||||
|
<div class="modal_content">
|
||||||
|
' . login_form() . '
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
';
|
||||||
|
|
||||||
|
return $page;
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
function login_form()
|
||||||
|
{
|
||||||
|
$form = '
|
||||||
|
<div class="card center">
|
||||||
|
<form method="post" id="login_form">
|
||||||
|
<div class="column center">
|
||||||
|
<div class="column">
|
||||||
|
<label for="username">Username:</label>
|
||||||
|
<input id="username" name="username" autofocus required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="column">
|
||||||
|
<label for="password">Password:</label>
|
||||||
|
<input id="password" type="password" name="password" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a href="create-account.php">Signup Instead</a>
|
||||||
|
|
||||||
|
<input type="submit" class="button submit" id="login_submit">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
';
|
||||||
|
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
function signup_form()
|
||||||
|
{
|
||||||
|
$form = '
|
||||||
|
<div class="card center">
|
||||||
|
<form method="post" class="column center">
|
||||||
|
<div class="column">
|
||||||
|
<label for="username">Username:</label>
|
||||||
|
<input id="username" name="username" autofocus required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="column">
|
||||||
|
<label for="password">Password:</label>
|
||||||
|
<input id="password" type="password" name="password" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="column">
|
||||||
|
<label for="v_password">Verify Password:</label>
|
||||||
|
<input id="v_password" type="password" name="v_password" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a href="index.php">Login Instead</a>
|
||||||
|
|
||||||
|
<div class="row full_width">
|
||||||
|
<input type="submit" class="button submit">
|
||||||
|
<input type="reset" class="button">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="column center error">
|
||||||
|
<p id="err_username"></p>
|
||||||
|
<p id="err_match"></p>
|
||||||
|
<p id="err_length"></p>
|
||||||
|
<p id="err_num"></p>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
';
|
||||||
|
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "violet/catalog",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Violet\\Catalog\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Josh Ashton",
|
||||||
|
"email": "me@joshashton.dev"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"vlucas/phpdotenv": "^5.6"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'functions/init.php';
|
||||||
|
require_once 'functions/user.php';
|
||||||
|
|
||||||
|
if (isset($_POST['username']) && isset($_POST['password']) && isset($_POST['v_password'])) {
|
||||||
|
if (!signup($_POST['username'], $_POST['password'], $_POST['v_password']))
|
||||||
|
echo '<p>Username is taken, or password do not match.</p>';
|
||||||
|
else {
|
||||||
|
header('Location: catalog.php');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once 'components/head.php';
|
||||||
|
require_once 'components/user/signup.php';
|
||||||
|
|
||||||
|
echo signup_form();
|
||||||
|
|
||||||
|
require_once 'components/foot.php';
|
||||||
|
|||||||
@@ -0,0 +1,216 @@
|
|||||||
|
/********** GENERAL **********/
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 6vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 4vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 3vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-size: 2vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
h5,
|
||||||
|
h6,
|
||||||
|
p,
|
||||||
|
a,
|
||||||
|
label {
|
||||||
|
font-size: 1vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********** NAV BAR **********/
|
||||||
|
nav {
|
||||||
|
margin-bottom: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav ul {}
|
||||||
|
|
||||||
|
nav ul li {
|
||||||
|
display: inline-block;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav ul li a {
|
||||||
|
display: block;
|
||||||
|
padding: 15px 15px 15px 15px;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********** LAYOUT CLASSES **********/
|
||||||
|
.absolute_h_center {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.center {
|
||||||
|
text-align: center;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center_children {
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.space_between {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 20% 20% 20% 20%;
|
||||||
|
column-gap: 75px;
|
||||||
|
row-gap: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********** CARDS **********/
|
||||||
|
.card {
|
||||||
|
display: block;
|
||||||
|
width: 250px;
|
||||||
|
height: 250px;
|
||||||
|
border: 1px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product_page_card {
|
||||||
|
display: block;
|
||||||
|
border: 1px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card .img_container img {
|
||||||
|
min-width: 60%;
|
||||||
|
max-width: 60%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product_page_card .img_container img {
|
||||||
|
min-width: 30%;
|
||||||
|
max-width: 30%;
|
||||||
|
height: auto;
|
||||||
|
border: 1px solid red;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********** MARGINS & PADDING **********/
|
||||||
|
.padding {
|
||||||
|
padding: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.padding-top {
|
||||||
|
padding-top: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.padding-right {
|
||||||
|
padding-right: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.padding-bottom {
|
||||||
|
padding-bottom: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.padding-left {
|
||||||
|
padding-left: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.margin {
|
||||||
|
margin: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.margin-top {
|
||||||
|
margin-top: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.margin-right {
|
||||||
|
margin-right: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.margin-bottom {
|
||||||
|
margin-bottom: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.margin-left {
|
||||||
|
margin-left: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********** SIZING **********/
|
||||||
|
.std_width {
|
||||||
|
width: 70%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.full_width {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********** MODALS **********/
|
||||||
|
.modal {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
z-index: 1;
|
||||||
|
padding-top: 100px;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.fade-in {
|
||||||
|
display: block;
|
||||||
|
animation: fade-in 0.7s ease-in forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.fade-out {
|
||||||
|
display: none;
|
||||||
|
animation: fade-out 0.7s ease-out forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal_content {
|
||||||
|
display: fit-content;
|
||||||
|
margin: auto;
|
||||||
|
width: 40%;
|
||||||
|
height: max-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade-in {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade-out {
|
||||||
|
0% {
|
||||||
|
opacity: 1;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
:root {
|
||||||
|
--bg: #EBF2FA;
|
||||||
|
--gold: #FFD700;
|
||||||
|
--brwn: #F4A460;
|
||||||
|
--blue: #1E90FF;
|
||||||
|
--red: #DC143C;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav {
|
||||||
|
background-color: var(--bg);
|
||||||
|
border-bottom: 2px solid var(--gold);
|
||||||
|
}
|
||||||
|
|
||||||
|
nav *,
|
||||||
|
nav *:visited {
|
||||||
|
color: var(--blue);
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 800;
|
||||||
|
transition-duration: 500ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav ul li a:hover {
|
||||||
|
background-color: var(--gold);
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: var(--bg);
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
function pretty_dump($arg)
|
||||||
|
{
|
||||||
|
ob_start();
|
||||||
|
echo '<pre>';
|
||||||
|
print_r($arg);
|
||||||
|
echo '</pre>';
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
function random_string()
|
||||||
|
{
|
||||||
|
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||||
|
$rand_string = '';
|
||||||
|
for ($i = 0; $i < 512; $i++)
|
||||||
|
$rand_string = $characters[rand(0, strlen($characters) - 1)];
|
||||||
|
|
||||||
|
return $rand_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function verify_hash($arg, $hash_salt)
|
||||||
|
{
|
||||||
|
list($salt_hex, $hash) = explode('.', $hash_salt, 2);
|
||||||
|
$salt = hex2bin($salt_hex);
|
||||||
|
$calculated_hash = hash('sha512', $salt . $arg . $_ENV['SALT']);
|
||||||
|
return hash_equals($hash, $calculated_hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hash_salt($arg = null)
|
||||||
|
{
|
||||||
|
if (!$arg)
|
||||||
|
$arg = random_string();
|
||||||
|
|
||||||
|
$salt1 = random_bytes(256);
|
||||||
|
$salt2 = $_ENV['SALT'];
|
||||||
|
|
||||||
|
return bin2hex($salt1) . '.' . hash('sha512', $salt1 . $arg . $salt2);
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
function fetch_catalog()
|
||||||
|
{
|
||||||
|
$conn = get_connection();
|
||||||
|
$result = $conn->query('SELECT * FROM product');
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetch_product($id)
|
||||||
|
{
|
||||||
|
$result = exec_stmt('SELECT * FROM product WHERE id = ?', 'i', $id);
|
||||||
|
return $result->fetch_assoc();
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
// Use environment variables for the database password and IP address.
|
||||||
|
$db = $_ENV['DATABASE'];
|
||||||
|
$db_user = $_ENV['DATABASE_USER'];
|
||||||
|
$db_pass = $_ENV['DATABASE_PASSWORD'];
|
||||||
|
$ip_addr = $_ENV['IP_ADDRESS'];
|
||||||
|
|
||||||
|
// Make some constants
|
||||||
|
if ($_SERVER['HTTP_HOST'] == 'localhost:2440') {
|
||||||
|
define('HOST', 'localhost');
|
||||||
|
define('USER', $db_user);
|
||||||
|
define('PASS', $db_pass);
|
||||||
|
define('DB', $db);
|
||||||
|
} else {
|
||||||
|
define('HOST', $ip_addr);
|
||||||
|
define('USER', $db_user);
|
||||||
|
define('PASS', $db_pass);
|
||||||
|
define('DB', $db);
|
||||||
|
}
|
||||||
|
|
||||||
|
function exec_stmt($stmt, $param_types, ...$args)
|
||||||
|
{
|
||||||
|
$conn = get_connection();
|
||||||
|
$stmt = $conn->prepare($stmt);
|
||||||
|
$stmt->bind_param($param_types, ...$args);
|
||||||
|
$stmt->execute();
|
||||||
|
$result = $stmt->get_result();
|
||||||
|
|
||||||
|
if ($result)
|
||||||
|
return $result;
|
||||||
|
else
|
||||||
|
return mysqli_insert_id($conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_connection()
|
||||||
|
{
|
||||||
|
$conn = new mysqli(HOST, USER, PASS, DB);
|
||||||
|
return $conn;
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
header("Content-Security-Policy: default-src 'self'; font-src 'self' https://www.nerdfonts.com/assets/fonts/Symbols-2048-em%20Nerd%20Font%20Complete.woff2; style-src 'self' 'unsafe-inline' https://nerdfonts.com/assets/css/webfont.css https://www.nerdfonts.com/assets/css/webfont.css; script-src 'self' 'unsafe-inline'; img-src 'self';");
|
||||||
|
|
||||||
|
// Initialize Composer.
|
||||||
|
require_once 'vendor/autoload.php';
|
||||||
|
require_once 'functions/base.php';
|
||||||
|
|
||||||
|
// Load environment variables.
|
||||||
|
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__ . '/../');
|
||||||
|
$dotenv->safeLoad();
|
||||||
|
|
||||||
|
require_once 'functions/db.php';
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
if (isset($_POST['form_id'])) {
|
||||||
|
require_once '../vendor/autoload.php';
|
||||||
|
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__ . '/../');
|
||||||
|
$dotenv->safeLoad();
|
||||||
|
|
||||||
|
require_once 'base.php';
|
||||||
|
require_once 'db.php';
|
||||||
|
|
||||||
|
switch ($_POST['form_id']) {
|
||||||
|
case 'create-account':
|
||||||
|
if (isset($_POST['username'])) {
|
||||||
|
$result['exists'] = (username_exists(urldecode($_POST['username'])) ? 'True' : 'False');
|
||||||
|
echo json_encode($result);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function username_exists($username)
|
||||||
|
{
|
||||||
|
$result = exec_stmt('SELECT username FROM user WHERE username = ?', 's', $username)->fetch_assoc();
|
||||||
|
|
||||||
|
if (isset($result['username']))
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function login($username, $password)
|
||||||
|
{
|
||||||
|
$user = exec_stmt('SELECT id FROM user WHERE username = ? AND password = ?', 'ss', $username, $password)->fetch_assoc();
|
||||||
|
|
||||||
|
if (!isset($user['id']))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
$_SESSION['id'] = $user['id'];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function signup($username, $password, $verify_password)
|
||||||
|
{
|
||||||
|
echo 'test';
|
||||||
|
if (username_exists($username))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
echo 'username=' . $username;
|
||||||
|
|
||||||
|
if ($password != $verify_password)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
$user_id = exec_stmt('INSERT INTO user (username, password) VALUES (?, ?)', 'ss', $username, $password);
|
||||||
|
|
||||||
|
echo 'user_id=' . $user_id;
|
||||||
|
|
||||||
|
if (!isset($user_id))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
$_SESSION['id'] = $user_id;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
$head = <<<EOL
|
|
||||||
<head>
|
|
||||||
<title>E-Commerce</title>
|
|
||||||
<link rel="stylesheet" href="css/styles.css">
|
|
||||||
</head>
|
|
||||||
EOL;
|
|
||||||
|
|
||||||
echo $head;
|
|
||||||
|
After Width: | Height: | Size: 127 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 7.0 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 8.1 KiB |
|
After Width: | Height: | Size: 8.3 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 143 KiB |
|
After Width: | Height: | Size: 154 KiB |
|
After Width: | Height: | Size: 318 KiB |
|
After Width: | Height: | Size: 250 KiB |
|
After Width: | Height: | Size: 581 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 117 KiB |
|
After Width: | Height: | Size: 89 KiB |
|
After Width: | Height: | Size: 380 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 222 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 7.9 KiB |
|
After Width: | Height: | Size: 7.8 KiB |
@@ -1,9 +1,20 @@
|
|||||||
<?php
|
<?php
|
||||||
echo '<!doctype html>';
|
include_once 'functions/init.php';
|
||||||
echo '<html lang="en">';
|
require_once 'functions/user.php';
|
||||||
include('head.php');
|
|
||||||
|
|
||||||
echo '<body>';
|
if (isset($_POST['username']) && isset($_POST['password'])) {
|
||||||
include('nav.php');
|
if (login($_POST['username'], $_POST['password']))
|
||||||
echo '</body>';
|
header('Location: catalog.php');
|
||||||
echo '</html>';
|
else
|
||||||
|
echo '<p>Username and/or password are incorrect!</p>';
|
||||||
|
}
|
||||||
|
|
||||||
|
include_once 'components/head.php';
|
||||||
|
include_once 'components/user/login.php';
|
||||||
|
|
||||||
|
if (isset($_SESSION['id'])) {
|
||||||
|
echo '<h1>Welcome to the store</h1>';
|
||||||
|
} else
|
||||||
|
echo login_form();
|
||||||
|
|
||||||
|
include_once 'components/foot.php';
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
const username_field = document.querySelector('#username');
|
||||||
|
const password_field = document.querySelector('#password');
|
||||||
|
const v_password_field = document.querySelector('#v_password');
|
||||||
|
const submit_button = document.querySelector('input[type="submit"]');
|
||||||
|
|
||||||
|
const err_username = document.querySelector('#err_username');
|
||||||
|
const err_match = document.querySelector('#err_match');
|
||||||
|
const err_length = document.querySelector('#err_length');
|
||||||
|
const err_num = document.querySelector('#err_num');
|
||||||
|
|
||||||
|
function handle_input() {
|
||||||
|
const password = password_field.value;
|
||||||
|
const v_password = v_password_field.value;
|
||||||
|
if (password != v_password && v_password != '')
|
||||||
|
err_match.innerText = 'Passwords do not match.';
|
||||||
|
else
|
||||||
|
err_match.innerText = '';
|
||||||
|
|
||||||
|
|
||||||
|
if (password.length < 8)
|
||||||
|
err_length.innerText = 'Password must be at least 8 characters long.';
|
||||||
|
else
|
||||||
|
err_length.innerText = '';
|
||||||
|
|
||||||
|
const num_regex = /\d/;
|
||||||
|
if (!num_regex.test(password))
|
||||||
|
err_num.innerText = 'Password must have at least 1 digit.';
|
||||||
|
else
|
||||||
|
err_num.innerText = '';
|
||||||
|
|
||||||
|
if (err_match.innerText == '' && err_length.innerText == '' && err_num.innerText == '' && err_username == '' && v_password != '')
|
||||||
|
submit_button.disabled = false;
|
||||||
|
else
|
||||||
|
submit_button.disabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handle_username() {
|
||||||
|
const username = username_field.value;
|
||||||
|
|
||||||
|
fetch('functions/user.php', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
},
|
||||||
|
body: 'form_id=create-account&username=' + username,
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
if (!response.ok) {
|
||||||
|
console.log('Failed to check if username exists!');
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.json(); // Parse response body as JSON
|
||||||
|
})
|
||||||
|
.then(data => {
|
||||||
|
if (data['exists'] == 'True')
|
||||||
|
err_username.innerText = 'Username is taken.';
|
||||||
|
else
|
||||||
|
err_username.innerText = '';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
username_field.addEventListener('blur', handle_username);
|
||||||
|
password_field.addEventListener('input', handle_input);
|
||||||
|
v_password_field.addEventListener('input', handle_input);
|
||||||
|
submit_button.disabled = true;
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
const add_to_cart = document.querySelector('input[type="submit"]');
|
||||||
|
|
||||||
|
const modal = document.querySelector('.modal');
|
||||||
|
const username = document.querySelector('#username');
|
||||||
|
const password = document.querySelector('#password');
|
||||||
|
const login_button = document.querySelector('#login_submit');
|
||||||
|
|
||||||
|
|
||||||
|
add_to_cart.addEventListener('click', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (modal.classList[0] === "fade-in") {
|
||||||
|
modal.classList.remove("fade-in");
|
||||||
|
modal.classList.add("fade-out");
|
||||||
|
} else {
|
||||||
|
modal.classList.remove("fade-out");
|
||||||
|
modal.classList.add("fade-in");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
modal.addEventListener('click', () => {
|
||||||
|
if (modal.classList[0] === "fade-in") {
|
||||||
|
modal.classList.remove("fade-in");
|
||||||
|
modal.classList.add("fade-out");
|
||||||
|
} else {
|
||||||
|
modal.classList.remove("fade-out");
|
||||||
|
modal.classList.add("fade-in");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
});
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
include_once 'functions/init.php';
|
||||||
|
|
||||||
|
foreach (array_keys($_SESSION) as $key)
|
||||||
|
unset($_SESSION[$key]);
|
||||||
|
|
||||||
|
header('Location: .');
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
<?php
|
|
||||||
$nav = <<<EOL
|
|
||||||
<div id="nav">
|
|
||||||
<ul id="nav-ul">
|
|
||||||
<li id="nav-ul-li">
|
|
||||||
<a href="#">
|
|
||||||
Home
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li id="nav-ul-li">
|
|
||||||
<a href="#">
|
|
||||||
Cart
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
EOL;
|
|
||||||
|
|
||||||
echo $nav;
|
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
if (!isset($_GET['id']))
|
||||||
|
header('Location: catalog.php');
|
||||||
|
|
||||||
|
require_once 'functions/init.php';
|
||||||
|
require_once 'functions/catalog.php';
|
||||||
|
|
||||||
|
// UI Components
|
||||||
|
foreach (glob('components/products/*.php') as $file)
|
||||||
|
require_once $file;
|
||||||
|
|
||||||
|
require_once 'components/user/login.php';
|
||||||
|
|
||||||
|
require_once 'components/head.php';
|
||||||
|
|
||||||
|
echo '<div id="product_page" class="std_width center">';
|
||||||
|
$product = fetch_product($_GET['id']);
|
||||||
|
echo product_page($product);
|
||||||
|
echo '</div>';
|
||||||
|
|
||||||
|
require_once 'components/foot.php';
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
mariadb -u root -p < setup.sql
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
CREATE DATABASE IF NOT EXISTS catalog;
|
||||||
|
USE catalog;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS user
|
||||||
|
(
|
||||||
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
username VARCHAR(50) NOT NULL UNIQUE,
|
||||||
|
password VARCHAR(1024) NOT NULL,
|
||||||
|
login_token VARCHAR(1024) NOT NULL UNIQUE,
|
||||||
|
salt VARCHAR(256) NOT NULL,
|
||||||
|
INDEX (login_token)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS product
|
||||||
|
(
|
||||||
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
name VARCHAR(50) NOT NULL UNIQUE,
|
||||||
|
description VARCHAR(500) NOT NULL,
|
||||||
|
image VARCHAR(100),
|
||||||
|
price FLOAT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO product (name, description, image, price) VALUES
|
||||||
|
("Anvil", "A heavy steel block used for hammering and shaping metal.", "anvil.jpg", 75.00),
|
||||||
|
("Axle Grease", "A thick lubricant used to reduce friction in axles and other moving parts.", "axle_grease.jpg", 8.50),
|
||||||
|
("Atom Re-Arranger", "A device of questionable scientific validity that purports to rearrange atoms.", "atom_rearranger.jpg", 499.99),
|
||||||
|
("Bed Springs", "Coiled metal springs used in mattresses and furniture for support.", "bed_springs.jpeg", 35.75),
|
||||||
|
("Bird Seed", "A mixture of seeds formulated as food for pet birds.", "bird_seed.jpg", 12.20),
|
||||||
|
("Blasting Powder", "An explosive material used for demolition and mining. Handle with extreme caution!", "blasting_powder.JPG", 150.00),
|
||||||
|
("Cork", "The bark of the cork oak tree, often used for bottle stoppers and insulation.", "cork.jpg", 2.50),
|
||||||
|
("Disintegration Pistol", "A fictional weapon capable of breaking down matter at a molecular level.", "disintegration_pistol.png", 299.00),
|
||||||
|
("Earthquake Pills", "Pills that supposedly induce seismic activity. Effects not guaranteed.", "earthquake_pills.jpg", 55.00),
|
||||||
|
("Female Roadrunner costume", "A feathered costume designed to resemble a female roadrunner.", "roadrunner_costume_female.jpg", 62.99),
|
||||||
|
("Giant Rubber Band", "An oversized elastic band, useful for large-scale stretching or launching.", "giant_rubber_band.jpg", 19.95),
|
||||||
|
("Instant Girl", "A product that claims to instantly create a companion. Results may vary significantly.", "instant_girl.jpg", 78.30),
|
||||||
|
("Iron Carrot", "A carrot made of iron. Purpose unclear.", "iron_carrot.jpeg", 22.50),
|
||||||
|
("Jet Propelled Unicycle", "A single-wheeled vehicle powered by a jet engine. For experienced riders only.", "jet_propelled_unicycle.jpg", 599.00),
|
||||||
|
("Out-Board Motor", "A detachable motor mounted on the outside of a boat's transom.", "outboard_motor.jpg", 210.00),
|
||||||
|
("Railroad Track", "A section of steel rail used for train tracks.", "railroad_track.jpeg", 315.50),
|
||||||
|
("Rocket Sled", "A sled propelled by a rocket engine, designed for high-speed travel on rails.", "rocket_sled.jpg", 850.00),
|
||||||
|
("Super Outfit", "A costume that allegedly grants the wearer extraordinary abilities. Claims not verified.", "super_outfit.jpeg", 95.00),
|
||||||
|
("Time Space Gun", "A fictional device that manipulates the fabric of spacetime. Use with caution.", "time_space_gun.jpeg", 675.00),
|
||||||
|
("X-Ray", "An electromagnetic radiation with high energy, often used for medical imaging.", "x_ray.jpeg", 1.00);
|
||||||