V2
This commit is contained in:
parent
bd0cd9042d
commit
ab1ae8b39b
765
assets/css/main.css
Normal file
765
assets/css/main.css
Normal file
@ -0,0 +1,765 @@
|
||||
/* Global Styles & Variables */
|
||||
:root {
|
||||
--turquoise: #40E0D0;
|
||||
--green: #228B22;
|
||||
--coral: #FF6B6B;
|
||||
--white: #FFFFFF;
|
||||
--off-white: #f8f9fa;
|
||||
--text-color: #333;
|
||||
--light-gray: #f2f2f2;
|
||||
--medium-gray: #ccc;
|
||||
--dark-gray: #555;
|
||||
|
||||
--font-heading: 'Inter', sans-serif;
|
||||
--font-body: 'Open Sans', sans-serif;
|
||||
|
||||
--shadow-soft: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||
--border-radius: 16px;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--font-body);
|
||||
color: var(--text-color);
|
||||
background-color: var(--white); /* Changed to white for a cleaner base */
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-family: var(--font-heading);
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.5px; /* Reduced for better readability */
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: var(--coral);
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--turquoise);
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* Header styles */
|
||||
header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 15px 30px;
|
||||
background-color: var(--white);
|
||||
border-bottom: 1px solid var(--light-gray);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-family: var(--font-heading);
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.address-container {
|
||||
cursor: pointer;
|
||||
padding: 8px 12px;
|
||||
border-radius: 20px;
|
||||
background-color: var(--off-white);
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
.address-container:hover {
|
||||
background-color: var(--light-gray);
|
||||
}
|
||||
|
||||
.address-display {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.user-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.user-actions a {
|
||||
margin-left: 20px;
|
||||
font-weight: 600;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
/* Modal styles */
|
||||
.modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
z-index: 1000;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
background-color: rgba(0,0,0,0.5);
|
||||
backdrop-filter: blur(5px);
|
||||
}
|
||||
.modal-content {
|
||||
background-color: var(--white);
|
||||
margin: 15% auto;
|
||||
padding: 30px;
|
||||
border: none;
|
||||
width: 90%;
|
||||
max-width: 500px;
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
|
||||
text-align: center;
|
||||
position: relative;
|
||||
}
|
||||
.close-button {
|
||||
color: var(--medium-gray);
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
right: 20px;
|
||||
font-size: 32px;
|
||||
font-weight: bold;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
.close-button:hover,
|
||||
.close-button:focus {
|
||||
color: var(--dark-gray);
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
#modal-address-input {
|
||||
width: calc(100% - 40px);
|
||||
padding: 12px;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 20px;
|
||||
border: 1px solid var(--light-gray);
|
||||
border-radius: 8px;
|
||||
font-size: 16px;
|
||||
}
|
||||
#save-address-button {
|
||||
background-color: var(--coral);
|
||||
color: white;
|
||||
padding: 12px 25px;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
#save-address-button:hover {
|
||||
background-color: #ff4f4f;
|
||||
}
|
||||
|
||||
|
||||
/* Animations */
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(-10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
.fade-in {
|
||||
animation: fadeIn 0.5s ease-in-out forwards;
|
||||
}
|
||||
|
||||
/* Hero Section */
|
||||
.hero {
|
||||
position: relative;
|
||||
height: 400px;
|
||||
background-image: url('../pasted-20251014-230507-170c4564.jpg');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
color: var(--white);
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.hero::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.hero-content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.hero h1 {
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 1rem;
|
||||
text-shadow: 0 2px 4px rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
.search-bar {
|
||||
width: 100%;
|
||||
padding: 1rem;
|
||||
font-size: 1rem;
|
||||
border-radius: 50px;
|
||||
border: none;
|
||||
box-shadow: var(--shadow-soft);
|
||||
padding-left: 3rem;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23767676' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-search'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E");
|
||||
background-repeat: no-repeat;
|
||||
background-position: 1rem center;
|
||||
}
|
||||
|
||||
/* Restaurant Grid */
|
||||
.page-title {
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.restaurant-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.restaurant-card {
|
||||
background-color: var(--white);
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: var(--shadow-soft);
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease-in-out;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.restaurant-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 8px 20px rgba(0,0,0,0.12);
|
||||
}
|
||||
|
||||
.restaurant-card img {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.restaurant-card-content {
|
||||
padding: 1.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.restaurant-card-content h3 {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.restaurant-card-content p {
|
||||
color: var(--dark-gray);
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.rating-display {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.rating-display .star {
|
||||
color: #FFC107;
|
||||
margin-right: 0.25rem;
|
||||
}
|
||||
|
||||
.rating-display .rating-count {
|
||||
font-size: 0.9rem;
|
||||
color: var(--dark-gray);
|
||||
margin-left: 0.5rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
padding: 3rem 0;
|
||||
margin-top: 3rem;
|
||||
background-color: var(--off-white);
|
||||
border-top: 1px solid var(--light-gray);
|
||||
}
|
||||
|
||||
/* Restaurant Menu Page */
|
||||
.restaurant-hero-menu {
|
||||
position: relative;
|
||||
height: 300px;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
padding: 2rem;
|
||||
color: var(--white);
|
||||
border-radius: var(--border-radius);
|
||||
overflow: hidden;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.restaurant-hero-menu::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(to top, rgba(0,0,0,0.7) 0%, rgba(0,0,0,0) 100%);
|
||||
}
|
||||
|
||||
.restaurant-hero-menu-content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.restaurant-hero-menu h1 {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
margin: 0 0 0.5rem;
|
||||
text-shadow: 0 2px 4px rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.restaurant-hero-menu p {
|
||||
font-size: 1rem;
|
||||
margin: 0.25rem 0;
|
||||
text-shadow: 0 1px 3px rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
/* Menu Grid */
|
||||
.menu-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.menu-item-card {
|
||||
background-color: var(--white);
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: var(--shadow-soft);
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease-in-out;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.menu-item-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 8px 20px rgba(0,0,0,0.12);
|
||||
}
|
||||
|
||||
.menu-item-card img {
|
||||
width: 100%;
|
||||
height: 180px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.menu-item-card-content {
|
||||
padding: 1.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.menu-item-card-content h3 {
|
||||
font-size: 1.15rem;
|
||||
font-weight: 700;
|
||||
margin: 0 0 0.5rem;
|
||||
}
|
||||
|
||||
.menu-item-card-content .description {
|
||||
font-size: 0.9rem;
|
||||
color: var(--dark-gray);
|
||||
margin-bottom: 1rem;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.menu-item-card-content .price {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 700;
|
||||
color: var(--text-color);
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
/* Rating Section on Menu Page */
|
||||
.rating-form-container {
|
||||
background-color: var(--white);
|
||||
padding: 2rem;
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: var(--shadow-soft);
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.rating-form-container h2 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.rate-form .stars {
|
||||
font-size: 2rem;
|
||||
color: var(--medium-gray);
|
||||
cursor: pointer;
|
||||
direction: rtl;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.rate-form .stars > label {
|
||||
color: var(--medium-gray);
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
.rate-form .stars > input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.rate-form .stars > input:checked ~ label,
|
||||
.rate-form .stars:not(:hover) > input:checked ~ label {
|
||||
color: #FFC107 !important;
|
||||
}
|
||||
|
||||
.rate-form .stars > label:hover,
|
||||
.rate-form .stars > label:hover ~ label {
|
||||
color: #FFC107 !important;
|
||||
}
|
||||
|
||||
.rate-form button {
|
||||
background-color: var(--coral);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
margin-top: 1rem;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
.rate-form button:hover {
|
||||
background-color: #ff4f4f;
|
||||
}
|
||||
|
||||
/* Auth Forms */
|
||||
.auth-container {
|
||||
max-width: 450px;
|
||||
margin: 4rem auto;
|
||||
padding: 3rem;
|
||||
background-color: var(--white);
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: var(--shadow-soft);
|
||||
}
|
||||
|
||||
.auth-container h1 {
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 600;
|
||||
color: var(--dark-gray);
|
||||
}
|
||||
|
||||
.form-group input {
|
||||
width: 100%;
|
||||
padding: 0.8rem 1rem;
|
||||
border: 1px solid var(--medium-gray);
|
||||
border-radius: 8px;
|
||||
box-sizing: border-box;
|
||||
font-size: 1rem;
|
||||
transition: border-color 0.3s;
|
||||
}
|
||||
|
||||
.form-group input:focus {
|
||||
outline: none;
|
||||
border-color: var(--coral);
|
||||
}
|
||||
|
||||
.btn-submit {
|
||||
width: 100%;
|
||||
padding: 0.9rem;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
background-color: var(--coral);
|
||||
color: white;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.btn-submit:hover {
|
||||
background-color: #ff4f4f;
|
||||
}
|
||||
|
||||
.form-footer {
|
||||
text-align: center;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.form-footer a {
|
||||
color: var(--coral);
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.form-footer a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Add to Cart Form */
|
||||
.add-to-cart-form {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.add-to-cart-form .form-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.quantity-input {
|
||||
width: 60px;
|
||||
padding: 0.5rem;
|
||||
text-align: center;
|
||||
border: 1px solid var(--medium-gray);
|
||||
border-radius: 8px;
|
||||
font-size: 1rem;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.add-to-cart-btn {
|
||||
flex-grow: 1;
|
||||
background-color: var(--coral);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.6rem 1rem;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.add-to-cart-btn:hover {
|
||||
background-color: #ff4f4f;
|
||||
}
|
||||
|
||||
/* Cart Icon */
|
||||
.cart-icon {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
margin-right: 20px;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.cart-item-count {
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
right: -8px;
|
||||
background-color: var(--coral);
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
padding: 2px 6px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Cart Page */
|
||||
.cart-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 2rem 0;
|
||||
box-shadow: var(--shadow-soft);
|
||||
border-radius: var(--border-radius);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.cart-table thead {
|
||||
background-color: var(--off-white);
|
||||
}
|
||||
|
||||
.cart-table th, .cart-table td {
|
||||
padding: 1rem 1.5rem;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.cart-table th {
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
text-transform: uppercase;
|
||||
color: var(--dark-gray);
|
||||
}
|
||||
|
||||
.cart-table tbody tr {
|
||||
border-bottom: 1px solid var(--light-gray);
|
||||
}
|
||||
|
||||
.cart-table tbody tr:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.cart-table .update-form {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.cart-table .quantity-input {
|
||||
width: 50px;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.cart-table .update-btn {
|
||||
background-color: var(--turquoise);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.4rem 0.8rem;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.cart-table .update-btn:hover {
|
||||
background-color: #38d8c9;
|
||||
}
|
||||
|
||||
.cart-table .remove-link {
|
||||
color: var(--coral);
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.cart-total {
|
||||
text-align: right;
|
||||
margin-top: 1.5rem;
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.cart-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
margin-top: 2rem;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.clear-cart-btn, .checkout-btn {
|
||||
padding: 0.8rem 1.5rem;
|
||||
border-radius: 8px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
font-size: 0.9rem;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.clear-cart-btn {
|
||||
background-color: transparent;
|
||||
border: 2px solid var(--medium-gray);
|
||||
color: var(--dark-gray);
|
||||
}
|
||||
|
||||
.clear-cart-btn:hover {
|
||||
background-color: var(--light-gray);
|
||||
border-color: var(--dark-gray);
|
||||
}
|
||||
|
||||
.checkout-btn {
|
||||
background-color: var(--coral);
|
||||
color: white;
|
||||
border: 2px solid var(--coral);
|
||||
}
|
||||
|
||||
.checkout-btn:hover {
|
||||
background-color: #ff4f4f;
|
||||
border-color: #ff4f4f;
|
||||
}
|
||||
|
||||
/* Checkout Page */
|
||||
.checkout-summary {
|
||||
max-width: 700px;
|
||||
margin: 2rem auto;
|
||||
padding: 2rem;
|
||||
background-color: var(--white);
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: var(--shadow-soft);
|
||||
}
|
||||
|
||||
.checkout-summary h2 {
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.checkout-form {
|
||||
text-align: center;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
/* Order Confirmation Page */
|
||||
.order-confirmation {
|
||||
max-width: 600px;
|
||||
margin: 4rem auto;
|
||||
padding: 3rem;
|
||||
text-align: center;
|
||||
background-color: var(--white);
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: var(--shadow-soft);
|
||||
}
|
||||
|
||||
.order-confirmation h1 {
|
||||
color: var(--green);
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.order-confirmation p {
|
||||
font-size: 1.1rem;
|
||||
color: var(--dark-gray);
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.order-confirmation .btn {
|
||||
background-color: var(--coral);
|
||||
color: white;
|
||||
padding: 0.8rem 2rem;
|
||||
border-radius: 8px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
font-size: 1rem;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.order-confirmation .btn:hover {
|
||||
background-color: #ff4f4f;
|
||||
}
|
||||
BIN
assets/images/hero.jpg
Normal file
BIN
assets/images/hero.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 319 KiB |
98
assets/js/main.js
Normal file
98
assets/js/main.js
Normal file
@ -0,0 +1,98 @@
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const addressDisplay = document.getElementById('address-display');
|
||||
const addressContainer = document.querySelector('.address-container');
|
||||
const modal = document.getElementById('address-modal');
|
||||
const closeButton = document.querySelector('.close-button');
|
||||
const saveAddressButton = document.getElementById('save-address-button');
|
||||
const modalAddressInput = document.getElementById('modal-address-input');
|
||||
|
||||
// Load and display saved address
|
||||
const savedAddress = localStorage.getItem('deliveryAddress');
|
||||
if (savedAddress) {
|
||||
addressDisplay.textContent = savedAddress;
|
||||
} else {
|
||||
addressDisplay.textContent = 'Enter delivery address';
|
||||
}
|
||||
|
||||
// Open modal
|
||||
if (addressContainer) {
|
||||
addressContainer.addEventListener('click', function() {
|
||||
if (modal) {
|
||||
modal.style.display = 'block';
|
||||
modalAddressInput.value = localStorage.getItem('deliveryAddress') || '';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Close modal
|
||||
if(closeButton) {
|
||||
closeButton.addEventListener('click', function() {
|
||||
if (modal) modal.style.display = 'none';
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
window.addEventListener('click', function(event) {
|
||||
if (event.target == modal) {
|
||||
modal.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
// Save address
|
||||
if(saveAddressButton) {
|
||||
saveAddressButton.addEventListener('click', function() {
|
||||
const newAddress = modalAddressInput.value.trim();
|
||||
if (newAddress) {
|
||||
localStorage.setItem('deliveryAddress', newAddress);
|
||||
addressDisplay.textContent = newAddress;
|
||||
modal.style.display = 'none';
|
||||
} else {
|
||||
alert('Please enter an address.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Search functionality for index.php
|
||||
const searchInput = document.getElementById('search-input');
|
||||
if (searchInput) {
|
||||
const restaurantGrid = document.getElementById('restaurant-grid');
|
||||
const restaurantCards = restaurantGrid.querySelectorAll('a.restaurant-card');
|
||||
|
||||
searchInput.addEventListener('keyup', function () {
|
||||
const searchTerm = searchInput.value.toLowerCase();
|
||||
|
||||
restaurantCards.forEach(card => {
|
||||
const name = card.dataset.name || '';
|
||||
const cuisine = card.dataset.cuisine || '';
|
||||
|
||||
if (name.includes(searchTerm) || cuisine.includes(searchTerm)) {
|
||||
card.style.display = 'flex';
|
||||
} else {
|
||||
card.style.display = 'none';
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Search functionality for menu.php
|
||||
const menuSearchInput = document.getElementById('menu-search-input');
|
||||
if (menuSearchInput) {
|
||||
const menuGrid = document.getElementById('menu-grid');
|
||||
const menuItems = menuGrid.querySelectorAll('.menu-item-card');
|
||||
|
||||
menuSearchInput.addEventListener('keyup', function () {
|
||||
const searchTerm = menuSearchInput.value.toLowerCase();
|
||||
|
||||
menuItems.forEach(card => {
|
||||
const name = card.dataset.name || '';
|
||||
const description = card.dataset.description || '';
|
||||
|
||||
if (name.includes(searchTerm) || description.includes(searchTerm)) {
|
||||
card.style.display = 'flex';
|
||||
} else {
|
||||
card.style.display = 'none';
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
BIN
assets/pasted-20251014-230144-ecd85886.webp
Normal file
BIN
assets/pasted-20251014-230144-ecd85886.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
BIN
assets/pasted-20251014-230507-170c4564.jpg
Normal file
BIN
assets/pasted-20251014-230507-170c4564.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 779 KiB |
80
cart.php
Normal file
80
cart.php
Normal file
@ -0,0 +1,80 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'db/config.php';
|
||||
include 'header.php';
|
||||
|
||||
$cart_items = [];
|
||||
$total_price = 0;
|
||||
|
||||
if (!empty($_SESSION['cart'])) {
|
||||
$menu_item_ids = array_keys($_SESSION['cart']);
|
||||
$placeholders = implode(',', array_fill(0, count($menu_item_ids), '?'));
|
||||
|
||||
$stmt = db()->prepare("SELECT * FROM menu_items WHERE id IN ($placeholders)");
|
||||
$stmt->execute($menu_item_ids);
|
||||
$db_items = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
foreach ($db_items as $item) {
|
||||
$quantity = $_SESSION['cart'][$item['id']];
|
||||
$item_total = $item['price'] * $quantity;
|
||||
$total_price += $item_total;
|
||||
$cart_items[] = [
|
||||
'id' => $item['id'],
|
||||
'name' => $item['name'],
|
||||
'price' => $item['price'],
|
||||
'quantity' => $quantity,
|
||||
'total' => $item_total
|
||||
];
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<main>
|
||||
<div class="container">
|
||||
<h1>Your Cart</h1>
|
||||
<?php if (empty($cart_items)): ?>
|
||||
<p>Your cart is empty.</p>
|
||||
<?php else: ?>
|
||||
<table class="cart-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Item</th>
|
||||
<th>Price</th>
|
||||
<th>Quantity</th>
|
||||
<th>Total</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($cart_items as $item): ?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($item['name']) ?></td>
|
||||
<td>$<?= htmlspecialchars(number_format($item['price'], 2)) ?></td>
|
||||
<td>
|
||||
<form action="cart_actions.php" method="POST" class="update-form">
|
||||
<input type="hidden" name="action" value="update">
|
||||
<input type="hidden" name="menu_item_id" value="<?= $item['id'] ?>">
|
||||
<input type="number" name="quantity" value="<?= $item['quantity'] ?>" min="1" class="quantity-input">
|
||||
<button type="submit" class="update-btn">Update</button>
|
||||
</form>
|
||||
</td>
|
||||
<td>$<?= htmlspecialchars(number_format($item['total'], 2)) ?></td>
|
||||
<td>
|
||||
<a href="cart_actions.php?action=remove&menu_item_id=<?= $item['id'] ?>" class="remove-link">Remove</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="cart-total">
|
||||
<h3>Total: $<?= htmlspecialchars(number_format($total_price, 2)) ?></h3>
|
||||
</div>
|
||||
<div class="cart-actions">
|
||||
<a href="cart_actions.php?action=clear" class="clear-cart-btn">Clear Cart</a>
|
||||
<a href="checkout.php" class="checkout-btn">Proceed to Checkout</a>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
56
cart_actions.php
Normal file
56
cart_actions.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
require_once 'db/config.php';
|
||||
|
||||
// Initialize cart if it doesn't exist
|
||||
if (!isset($_SESSION['cart'])) {
|
||||
$_SESSION['cart'] = [];
|
||||
$_SESSION['cart_restaurant'] = null;
|
||||
}
|
||||
|
||||
$action = $_POST['action'] ?? $_GET['action'] ?? null;
|
||||
$menu_item_id = $_POST['menu_item_id'] ?? $_GET['menu_item_id'] ?? null;
|
||||
$restaurant_id = $_POST['restaurant_id'] ?? null;
|
||||
$quantity = $_POST['quantity'] ?? 1;
|
||||
|
||||
switch ($action) {
|
||||
case 'add':
|
||||
if ($menu_item_id && $restaurant_id && $quantity > 0) {
|
||||
// If cart is not empty and new item is from a different restaurant, clear the cart
|
||||
if (!empty($_SESSION['cart']) && $_SESSION['cart_restaurant'] != $restaurant_id) {
|
||||
$_SESSION['cart'] = [];
|
||||
}
|
||||
|
||||
$_SESSION['cart_restaurant'] = $restaurant_id;
|
||||
|
||||
// Add or update item in cart
|
||||
if (isset($_SESSION['cart'][$menu_item_id])) {
|
||||
$_SESSION['cart'][$menu_item_id] += $quantity;
|
||||
} else {
|
||||
$_SESSION['cart'][$menu_item_id] = $quantity;
|
||||
}
|
||||
}
|
||||
header('Location: menu.php?id=' . $restaurant_id);
|
||||
exit;
|
||||
|
||||
case 'update':
|
||||
if ($menu_item_id && $quantity > 0) {
|
||||
$_SESSION['cart'][$menu_item_id] = $quantity;
|
||||
}
|
||||
header('Location: cart.php');
|
||||
exit;
|
||||
|
||||
case 'remove':
|
||||
if ($menu_item_id) {
|
||||
unset($_SESSION['cart'][$menu_item_id]);
|
||||
}
|
||||
header('Location: cart.php');
|
||||
exit;
|
||||
|
||||
case 'clear':
|
||||
$_SESSION['cart'] = [];
|
||||
$_SESSION['cart_restaurant'] = null;
|
||||
header('Location: cart.php');
|
||||
exit;
|
||||
}
|
||||
100
checkout.php
Normal file
100
checkout.php
Normal file
@ -0,0 +1,100 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'db/config.php';
|
||||
|
||||
// If user is not logged in, redirect to login page
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
// If cart is empty, redirect to cart page
|
||||
if (empty($_SESSION['cart'])) {
|
||||
header('Location: cart.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$cart_items = [];
|
||||
$total_price = 0;
|
||||
$restaurant_id = $_SESSION['cart_restaurant'];
|
||||
|
||||
$menu_item_ids = array_keys($_SESSION['cart']);
|
||||
$placeholders = implode(',', array_fill(0, count($menu_item_ids), '?'));
|
||||
|
||||
$stmt = db()->prepare("SELECT * FROM menu_items WHERE id IN ($placeholders)");
|
||||
$stmt->execute($menu_item_ids);
|
||||
$db_items = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
foreach ($db_items as $item) {
|
||||
$quantity = $_SESSION['cart'][$item['id']];
|
||||
$item_total = $item['price'] * $quantity;
|
||||
$total_price += $item_total;
|
||||
$cart_items[] = [
|
||||
'id' => $item['id'],
|
||||
'name' => $item['name'],
|
||||
'price' => $item['price'],
|
||||
'quantity' => $quantity,
|
||||
'total' => $item_total
|
||||
];
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$user_id = $_SESSION['user_id'];
|
||||
|
||||
// Insert into orders table
|
||||
$stmt = db()->prepare("INSERT INTO orders (user_id, restaurant_id, total_price, status) VALUES (?, ?, ?, ?)");
|
||||
$stmt->execute([$user_id, $restaurant_id, $total_price, 'pending']);
|
||||
$order_id = db()->lastInsertId();
|
||||
|
||||
// Insert into order_items table
|
||||
$stmt = db()->prepare("INSERT INTO order_items (order_id, menu_item_id, quantity, price) VALUES (?, ?, ?, ?)");
|
||||
foreach ($cart_items as $item) {
|
||||
$stmt->execute([$order_id, $item['id'], $item['quantity'], $item['price']]);
|
||||
}
|
||||
|
||||
// Clear the cart
|
||||
$_SESSION['cart'] = [];
|
||||
$_SESSION['cart_restaurant'] = null;
|
||||
|
||||
// Redirect to a confirmation page
|
||||
header('Location: order_confirmation.php?id=' . $order_id);
|
||||
exit;
|
||||
}
|
||||
|
||||
include 'header.php';
|
||||
?>
|
||||
|
||||
<main>
|
||||
<div class="container">
|
||||
<h1>Checkout</h1>
|
||||
<div class="checkout-summary">
|
||||
<h2>Order Summary</h2>
|
||||
<table class="cart-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Item</th>
|
||||
<th>Quantity</th>
|
||||
<th>Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($cart_items as $item): ?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($item['name']) ?></td>
|
||||
<td><?= $item['quantity'] ?></td>
|
||||
<td>$<?= htmlspecialchars(number_format($item['total'], 2)) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="cart-total">
|
||||
<h3>Total: $<?= htmlspecialchars(number_format($total_price, 2)) ?></h3>
|
||||
</div>
|
||||
<form action="checkout.php" method="POST" class="checkout-form">
|
||||
<button type="submit" class="checkout-btn">Place Order</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
7
footer.php
Normal file
7
footer.php
Normal file
@ -0,0 +1,7 @@
|
||||
<footer>
|
||||
<div class="container">
|
||||
<p>© <?php echo date("Y"); ?> Majuro Eats. All Rights Reserved.</p>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
50
header.php
Normal file
50
header.php
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
session_start();
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Majuro Eats</title>
|
||||
<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=Inter:wght@400;500;600;700&family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=<?php echo time(); ?>">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<header>
|
||||
<a href="/" class="logo">Majuro Eats</a>
|
||||
<div class="address-container">
|
||||
<span id="address-display">Enter delivery address</span>
|
||||
</div>
|
||||
<div class="user-actions">
|
||||
<?php
|
||||
$cart_item_count = isset($_SESSION['cart']) ? count($_SESSION['cart']) : 0;
|
||||
?>
|
||||
<a href="cart.php" class="cart-icon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="21" r="1"></circle><circle cx="20" cy="21" r="1"></circle><path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"></path></svg>
|
||||
<span class="cart-item-count"><?= $cart_item_count ?></span>
|
||||
</a>
|
||||
<?php if (isset($_SESSION['user_id'])): ?>
|
||||
<span>Welcome, <?php echo htmlspecialchars($_SESSION['user_name']); ?></span>
|
||||
<a href="logout.php">Logout</a>
|
||||
<?php else: ?>
|
||||
<a href="login.php">Login</a>
|
||||
<a href="signup.php">Sign Up</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- The Modal -->
|
||||
<div id="address-modal" class="modal">
|
||||
<div class="modal-content">
|
||||
<span class="close-button">×</span>
|
||||
<h2>Enter your delivery address</h2>
|
||||
<input type="text" id="modal-address-input" placeholder="e.g. Uliga, Majuro">
|
||||
<button id="save-address-button">Save Address</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="assets/js/main.js?v=<?php echo time(); ?>" defer></script>
|
||||
25
includes/pexels.php
Normal file
25
includes/pexels.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
function pexels_key() {
|
||||
$k = getenv('PEXELS_KEY');
|
||||
return $k && strlen($k) > 0 ? $k : 'Vc99rnmOhHhJAbgGQoKLZtsaIVfkeownoQNbTj78VemUjKh08ZYRbf18';
|
||||
}
|
||||
function pexels_get($url) {
|
||||
$ch = curl_init();
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HTTPHEADER => [ 'Authorization: '. pexels_key() ],
|
||||
CURLOPT_TIMEOUT => 15,
|
||||
]);
|
||||
$resp = curl_exec($ch);
|
||||
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
if ($code >= 200 && $code < 300 && $resp) return json_decode($resp, true);
|
||||
return null;
|
||||
}
|
||||
function download_to($srcUrl, $destPath) {
|
||||
$data = file_get_contents($srcUrl);
|
||||
if ($data === false) return false;
|
||||
if (!is_dir(dirname($destPath))) mkdir(dirname($destPath), 0775, true);
|
||||
return file_put_contents($destPath, $data) !== false;
|
||||
}
|
||||
286
index.php
286
index.php
@ -1,232 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>MajuroEats - Order Food Online</title>
|
||||
<meta name="description" content="Your one-stop destination for ordering food from the best local restaurants in Majuro.">
|
||||
<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=Poppins:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--primary-color: #EB1A01; /* DoorDash Red */
|
||||
--secondary-color: #333333; /* Dark Gray */
|
||||
--background-color: #F7F7F7; /* Light Gray */
|
||||
--surface-color: #FFFFFF; /* White */
|
||||
--text-color: #333333;
|
||||
--text-light: #767676;
|
||||
--border-color: #EEEEEE;
|
||||
--border-radius: 0.5rem;
|
||||
--spacing-unit: 1rem;
|
||||
}
|
||||
<?php include 'header.php'; ?>
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Poppins', sans-serif;
|
||||
background-color: var(--background-color);
|
||||
color: var(--text-color);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 0 var(--spacing-unit);
|
||||
}
|
||||
|
||||
header {
|
||||
background-color: var(--surface-color);
|
||||
padding: var(--spacing-unit) 0;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.header-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 0 var(--spacing-unit);
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-size: 1.75rem;
|
||||
font-weight: 700;
|
||||
color: var(--secondary-color);
|
||||
}
|
||||
|
||||
.logo span {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.address-search-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: var(--background-color);
|
||||
border-radius: 50px;
|
||||
padding: 0.5rem 1rem;
|
||||
flex-grow: 1;
|
||||
margin: 0 2rem;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.address-search-container input {
|
||||
border: none;
|
||||
background: transparent;
|
||||
outline: none;
|
||||
font-size: 1rem;
|
||||
width: 100%;
|
||||
font-family: 'Poppins', sans-serif;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.header-actions .icon-button {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
main {
|
||||
margin-top: calc(var(--spacing-unit) * 2);
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 2rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: var(--spacing-unit);
|
||||
}
|
||||
|
||||
.restaurant-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
|
||||
gap: calc(var(--spacing-unit) * 2);
|
||||
}
|
||||
|
||||
.restaurant-card {
|
||||
background-color: var(--surface-color);
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.08);
|
||||
overflow: hidden;
|
||||
transition: all 0.2s ease-in-out;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.restaurant-card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 8px 16px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.restaurant-card img {
|
||||
width: 100%;
|
||||
height: 180px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.restaurant-card-content {
|
||||
padding: var(--spacing-unit);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.restaurant-card-content h3 {
|
||||
font-size: 1.15rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: calc(var(--spacing-unit) / 4);
|
||||
}
|
||||
|
||||
.restaurant-card-content p {
|
||||
color: var(--text-light);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.restaurant-card-details {
|
||||
margin-top: auto;
|
||||
padding-top: var(--spacing-unit);
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-light);
|
||||
}
|
||||
.restaurant-card-details p {
|
||||
margin-bottom: calc(var(--spacing-unit) / 4);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.restaurant-card-details svg {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin-right: 8px;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
padding: calc(var(--spacing-unit) * 3) 0;
|
||||
margin-top: calc(var(--spacing-unit) * 3);
|
||||
background-color: var(--surface-color);
|
||||
border-top: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
/* Search bar specific styles */
|
||||
.search-section {
|
||||
margin-bottom: calc(var(--spacing-unit) * 2);
|
||||
}
|
||||
|
||||
.search-bar {
|
||||
width: 100%;
|
||||
padding: calc(var(--spacing-unit) * 0.9);
|
||||
font-size: 1rem;
|
||||
border-radius: 50px;
|
||||
border: 1px solid var(--border-color);
|
||||
font-family: 'Poppins', sans-serif;
|
||||
background-color: var(--surface-color);
|
||||
padding-left: 2.5rem;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23767676' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-search'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E");
|
||||
background-repeat: no-repeat;
|
||||
background-position: 1rem center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<header>
|
||||
<div class="header-container">
|
||||
<div class="logo">Majuro<span>Eats</span></div>
|
||||
<div class="address-search-container">
|
||||
<input type="text" placeholder="Enter delivery address">
|
||||
<main>
|
||||
<section class="hero">
|
||||
<div class="hero-content">
|
||||
<h1>Order from Majuro's best</h1>
|
||||
<input type="text" id="search-input" class="search-bar" placeholder="Search restaurants or cuisines...">
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<button class="icon-button">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-user"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path><circle cx="12" cy="7" r="4"></circle></svg>
|
||||
</button>
|
||||
<button class="icon-button">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-shopping-bag"><path d="M6 2L3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"></path><line x1="3" y1="6" x2="21" y2="6"></line><path d="M16 10a4 4 0 0 1-8 0"></path></svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="container">
|
||||
<section class="search-section">
|
||||
<input type="text" id="search-input" class="search-bar" placeholder="Search restaurants, cuisines, dishes...">
|
||||
</section>
|
||||
|
||||
<h2 class="page-title">Featured Restaurants</h2>
|
||||
<div class="container">
|
||||
<h2 class="page-title">All Restaurants</h2>
|
||||
|
||||
<section class="restaurant-list">
|
||||
<div class="restaurant-grid" id="restaurant-grid">
|
||||
@ -234,62 +17,39 @@
|
||||
require_once 'db/config.php';
|
||||
try {
|
||||
$pdo = db();
|
||||
$stmt = $pdo->query("SELECT name, cuisine, image_url, address, phone, opening_hours FROM restaurants ORDER BY name");
|
||||
$stmt = $pdo->query("SELECT id, name, cuisine, image_url, rating, rating_count FROM restaurants ORDER BY name");
|
||||
$restaurants = $stmt->fetchAll();
|
||||
|
||||
foreach ($restaurants as $restaurant) {
|
||||
echo '<div class="restaurant-card" data-name="' . htmlspecialchars(strtolower($restaurant['name'])) . '" data-cuisine="' . htmlspecialchars(strtolower($restaurant['cuisine'])) . '">';
|
||||
echo '<a href="menu.php?id=' . htmlspecialchars($restaurant['id']) . '" class="restaurant-card" data-name="' . htmlspecialchars(strtolower($restaurant['name'])) . '" data-cuisine="' . htmlspecialchars(strtolower($restaurant['cuisine'])) . '">';
|
||||
echo '<img src="' . htmlspecialchars($restaurant['image_url']) . '" alt="' . htmlspecialchars($restaurant['name']) . '">';
|
||||
echo '<div class="restaurant-card-content">';
|
||||
echo '<div>';
|
||||
echo '<h3>' . htmlspecialchars($restaurant['name']) . '</h3>';
|
||||
echo '<p>' . htmlspecialchars($restaurant['cuisine']) . '</p>';
|
||||
if (isset($restaurant['rating']) && $restaurant['rating'] > 0) {
|
||||
echo '<div class="rating-display">';
|
||||
echo '<span class="star">★</span>';
|
||||
echo '<span>' . htmlspecialchars(number_format($restaurant['rating'], 1)) . '</span>';
|
||||
echo '<span class="rating-count">(' . htmlspecialchars($restaurant['rating_count']) . ' ratings)</span>';
|
||||
echo '</div>';
|
||||
echo '<div class="restaurant-card-details">';
|
||||
if (!empty($restaurant['address'])) {
|
||||
echo '<p><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-map-pin"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path><circle cx="12" cy="10" r="3"></circle></svg>' . htmlspecialchars($restaurant['address']) . '</p>';
|
||||
}
|
||||
if (!empty($restaurant['opening_hours'])) {
|
||||
echo '<p><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-clock"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg>' . htmlspecialchars($restaurant['opening_hours']) . '</p>';
|
||||
} else {
|
||||
echo '<div class="rating-display"><span class="rating-count">No ratings yet</span></div>';
|
||||
}
|
||||
echo '</div>';
|
||||
echo '</div>';
|
||||
echo '</div>';
|
||||
echo '</a>';
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
echo '<p>Error: Could not fetch restaurants from the database.</p>';
|
||||
// Optionally log the error: error_log($e->getMessage());
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<p>© <?php echo date("Y"); ?> MajuroEats. All Rights Reserved.</p>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const searchInput = document.getElementById('search-input');
|
||||
const restaurantCards = document.querySelectorAll('.restaurant-card');
|
||||
|
||||
searchInput.addEventListener('keyup', function () {
|
||||
const searchTerm = searchInput.value.toLowerCase();
|
||||
|
||||
restaurantCards.forEach(card => {
|
||||
const name = card.dataset.name;
|
||||
const cuisine = card.dataset.cuisine;
|
||||
|
||||
if (name.includes(searchTerm) || cuisine.includes(searchTerm)) {
|
||||
card.style.display = 'flex';
|
||||
} else {
|
||||
card.style.display = 'none';
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<footer>
|
||||
<p>© <?php echo date("Y"); ?> Majuro Eats. All Rights Reserved.</p>
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
23
login.php
Normal file
23
login.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php include 'header.php'; ?>
|
||||
|
||||
<main>
|
||||
<div class="auth-container">
|
||||
<h1>Login</h1>
|
||||
<form action="login_process.php" method="POST">
|
||||
<div class="form-group">
|
||||
<label for="email">Email</label>
|
||||
<input type="email" id="email" name="email" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">Password</label>
|
||||
<input type="password" id="password" name="password" required>
|
||||
</div>
|
||||
<button type="submit" class="btn-submit">Login</button>
|
||||
</form>
|
||||
<div class="form-footer">
|
||||
<p>Don't have an account? <a href="signup.php">Sign up</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
37
login_process.php
Normal file
37
login_process.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'db/config.php';
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] == "POST") {
|
||||
$email = $_POST['email'];
|
||||
$password = $_POST['password'];
|
||||
|
||||
if (empty($email) || empty($password)) {
|
||||
die('Please fill all required fields.');
|
||||
}
|
||||
|
||||
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
die('Invalid email format.');
|
||||
}
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
|
||||
$sql = "SELECT * FROM users WHERE email = ?";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute([$email]);
|
||||
$user = $stmt->fetch();
|
||||
|
||||
if ($user && password_verify($password, $user['password'])) {
|
||||
$_SESSION['user_id'] = $user['id'];
|
||||
$_SESSION['user_name'] = $user['name'];
|
||||
header("Location: index.php");
|
||||
exit;
|
||||
} else {
|
||||
die('Invalid email or password.');
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
die("Could not connect to the database $dbname :" . $e->getMessage());
|
||||
}
|
||||
}
|
||||
?>
|
||||
7
logout.php
Normal file
7
logout.php
Normal file
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
session_start();
|
||||
session_unset();
|
||||
session_destroy();
|
||||
header("Location: index.php");
|
||||
exit;
|
||||
?>
|
||||
85
menu.php
Normal file
85
menu.php
Normal file
@ -0,0 +1,85 @@
|
||||
<?php
|
||||
require_once 'db/config.php';
|
||||
|
||||
$restaurant_id = $_GET['id'] ?? null;
|
||||
$restaurant = null;
|
||||
$menu_items = [];
|
||||
|
||||
if ($restaurant_id) {
|
||||
$stmt = db()->prepare("SELECT * FROM restaurants WHERE id = ?");
|
||||
$stmt->execute([$restaurant_id]);
|
||||
$restaurant = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
if ($restaurant) {
|
||||
$stmt = db()->prepare("SELECT * FROM menu_items WHERE restaurant_id = ?");
|
||||
$stmt->execute([$restaurant_id]);
|
||||
$menu_items = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
include 'header.php';
|
||||
?>
|
||||
|
||||
<main>
|
||||
<div class="container">
|
||||
<?php if ($restaurant): ?>
|
||||
<section class="restaurant-hero-menu" style="background-image: url('<?= htmlspecialchars($restaurant['image_url']) ?>');">
|
||||
<div class="restaurant-hero-menu-content">
|
||||
<h1><?= htmlspecialchars($restaurant['name']) ?></h1>
|
||||
<p><?= htmlspecialchars($restaurant['cuisine']) ?></p>
|
||||
<div class="rating-display">
|
||||
<span class="star">★</span>
|
||||
<span><?= htmlspecialchars(number_format($restaurant['rating'], 1)) ?></span>
|
||||
<span class="rating-count">(<?= htmlspecialchars($restaurant['rating_count']) ?> ratings)</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="menu-search-container">
|
||||
<input type="text" id="menu-search-input" class="search-bar" placeholder="Search menu items...">
|
||||
</div>
|
||||
|
||||
<section class="menu-grid" id="menu-grid">
|
||||
<?php foreach ($menu_items as $item): ?>
|
||||
<div class="menu-item-card" data-name="<?= htmlspecialchars(strtolower($item['name'])) ?>" data-description="<?= htmlspecialchars(strtolower($item['description'])) ?>">
|
||||
<img src="<?= htmlspecialchars($item['image_url']) ?>" alt="<?= htmlspecialchars($item['name']) ?>">
|
||||
<div class="menu-item-card-content">
|
||||
<h3><?= htmlspecialchars($item['name']) ?></h3>
|
||||
<p class="description"><?= htmlspecialchars($item['description']) ?></p>
|
||||
<p class="price">$<?= htmlspecialchars(number_format($item['price'], 2)) ?></p>
|
||||
<form action="cart_actions.php" method="POST" class="add-to-cart-form">
|
||||
<input type="hidden" name="action" value="add">
|
||||
<input type="hidden" name="menu_item_id" value="<?= $item['id'] ?>">
|
||||
<input type="hidden" name="restaurant_id" value="<?= $restaurant['id'] ?>">
|
||||
<div class="form-row">
|
||||
<input type="number" name="quantity" value="1" min="1" class="quantity-input">
|
||||
<button type="submit" class="add-to-cart-btn">Add to Cart</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</section>
|
||||
|
||||
<section class="rating-form-container">
|
||||
<h2>Rate your experience</h2>
|
||||
<form action="rate.php" method="POST" class="rate-form">
|
||||
<input type="hidden" name="restaurant_id" value="<?= $restaurant['id'] ?>">
|
||||
<div class="stars">
|
||||
<input type="radio" id="star5" name="rating" value="5"><label for="star5">★</label>
|
||||
<input type="radio" id="star4" name="rating" value="4"><label for="star4">★</label>
|
||||
<input type="radio" id="star3" name="rating" value="3"><label for="star3">★</label>
|
||||
<input type="radio" id="star2" name="rating" value="2"><label for="star2">★</label>
|
||||
<input type="radio" id="star1" name="rating" value="1"><label for="star1">★</label>
|
||||
</div>
|
||||
<button type="submit">Submit Rating</button>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<?php else: ?>
|
||||
<p>Restaurant not found.</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php include 'footer.php'; // Assuming you might create a footer file ?>
|
||||
18
migrations/20251014_create_orders_tables.sql
Normal file
18
migrations/20251014_create_orders_tables.sql
Normal file
@ -0,0 +1,18 @@
|
||||
CREATE TABLE IF NOT EXISTS `orders` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`user_id` INT NOT NULL,
|
||||
`restaurant_id` INT NOT NULL,
|
||||
`total_price` DECIMAL(10, 2) NOT NULL,
|
||||
`status` VARCHAR(50) NOT NULL DEFAULT 'pending',
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `order_items` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`order_id` INT NOT NULL,
|
||||
`menu_item_id` INT NOT NULL,
|
||||
`quantity` INT NOT NULL,
|
||||
`price` DECIMAL(10, 2) NOT NULL,
|
||||
FOREIGN KEY (`order_id`) REFERENCES `orders`(`id`)
|
||||
);
|
||||
25
order_confirmation.php
Normal file
25
order_confirmation.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'db/config.php';
|
||||
include 'header.php';
|
||||
|
||||
if (!isset($_SESSION['order_id'])) {
|
||||
header('Location: index.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
$order_id = $_SESSION['order_id'];
|
||||
unset($_SESSION['order_id']);
|
||||
|
||||
?>
|
||||
|
||||
<main class="container">
|
||||
<div class="order-confirmation">
|
||||
<h1>Thank You for Your Order!</h1>
|
||||
<p>Your order has been placed successfully.</p>
|
||||
<p>Your Order ID is: <strong><?php echo htmlspecialchars($order_id); ?></strong></p>
|
||||
<a href="index.php" class="btn btn-primary">Continue Shopping</a>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
37
rate.php
Normal file
37
rate.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
require_once 'db/config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$restaurant_id = $_POST['restaurant_id'] ?? null;
|
||||
$new_rating = $_POST['rating'] ?? null;
|
||||
|
||||
if ($restaurant_id && $new_rating) {
|
||||
try {
|
||||
$pdo = db();
|
||||
|
||||
// Get current rating and count
|
||||
$stmt = $pdo->prepare("SELECT rating, rating_count FROM restaurants WHERE id = ?");
|
||||
$stmt->execute([$restaurant_id]);
|
||||
$restaurant = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($restaurant) {
|
||||
$current_total_rating = $restaurant['rating'] * $restaurant['rating_count'];
|
||||
$new_total_rating = $current_total_rating + $new_rating;
|
||||
$new_rating_count = $restaurant['rating_count'] + 1;
|
||||
$new_average_rating = $new_total_rating / $new_rating_count;
|
||||
|
||||
// Update restaurant with new rating
|
||||
$update_stmt = $pdo->prepare("UPDATE restaurants SET rating = ?, rating_count = ? WHERE id = ?");
|
||||
$update_stmt->execute([$new_average_rating, $new_rating_count, $restaurant_id]);
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
// Log error, but don't show to user
|
||||
error_log("Rating update failed: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Redirect back to the menu page
|
||||
header('Location: menu.php?id=' . $restaurant_id);
|
||||
exit;
|
||||
?>
|
||||
27
signup.php
Normal file
27
signup.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php include 'header.php'; ?>
|
||||
|
||||
<main>
|
||||
<div class="auth-container">
|
||||
<h1>Create Account</h1>
|
||||
<form action="signup_process.php" method="POST">
|
||||
<div class="form-group">
|
||||
<label for="name">Name</label>
|
||||
<input type="text" id="name" name="name" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="email">Email</label>
|
||||
<input type="email" id="email" name="email" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">Password</label>
|
||||
<input type="password" id="password" name="password" required>
|
||||
</div>
|
||||
<button type="submit" class="btn-submit">Sign Up</button>
|
||||
</form>
|
||||
<div class="form-footer">
|
||||
<p>Already have an account? <a href="login.php">Log in</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
46
signup_process.php
Normal file
46
signup_process.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'db/config.php';
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] == "POST") {
|
||||
$name = $_POST['name'];
|
||||
$email = $_POST['email'];
|
||||
$password = $_POST['password'];
|
||||
|
||||
if (empty($name) || empty($email) || empty($password)) {
|
||||
die('Please fill all required fields.');
|
||||
}
|
||||
|
||||
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
die('Invalid email format.');
|
||||
}
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
|
||||
$sql = "SELECT id FROM users WHERE email = ?";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute([$email]);
|
||||
if ($stmt->fetch()) {
|
||||
die('Email already exists.');
|
||||
}
|
||||
|
||||
$password_hash = password_hash($password, PASSWORD_BCRYPT);
|
||||
|
||||
$sql = "INSERT INTO users (name, email, password) VALUES (?, ?, ?)";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
|
||||
if ($stmt->execute([$name, $email, $password_hash])) {
|
||||
$user_id = $pdo->lastInsertId();
|
||||
$_SESSION['user_id'] = $user_id;
|
||||
$_SESSION['user_name'] = $name;
|
||||
header("Location: index.php");
|
||||
exit;
|
||||
} else {
|
||||
die("Error: Could not execute the query.");
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
die("Could not connect to the database $dbname :" . $e->getMessage());
|
||||
}
|
||||
}
|
||||
?>
|
||||
Loading…
x
Reference in New Issue
Block a user