Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions backend/app/Services/AdminService.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
use App\Models\Order;
use App\Events\OrderPlaced;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Http\Request;
use App\Models\Product;

class AdminService
{
Expand Down
48 changes: 19 additions & 29 deletions backend/tests/Unit/ForeignKeyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,25 +57,15 @@ public function test_all_foreign_key_relationships_exist(): void
*/
private function assertForeignKeyExists(string $table, string $column, string $referencedTable, string $referencedColumn): void
{
$database = env('DB_DATABASE');

$foreignKeys = DB::select("
SELECT
COLUMN_NAME, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME
FROM
information_schema.KEY_COLUMN_USAGE
WHERE
TABLE_SCHEMA = ? AND
TABLE_NAME = ? AND
REFERENCED_TABLE_NAME IS NOT NULL
", [$database, $table]);
// Use SQLite's PRAGMA foreign_key_list to get foreign key information
$foreignKeys = DB::select("PRAGMA foreign_key_list({$table})");

$foreignKeyExists = false;
foreach ($foreignKeys as $foreignKey) {
if (
$foreignKey->COLUMN_NAME === $column &&
$foreignKey->REFERENCED_TABLE_NAME === $referencedTable &&
$foreignKey->REFERENCED_COLUMN_NAME === $referencedColumn
$foreignKey->from === $column &&
$foreignKey->table === $referencedTable &&
$foreignKey->to === $referencedColumn
) {
$foreignKeyExists = true;
break;
Expand Down Expand Up @@ -120,20 +110,20 @@ public function test_foreign_key_columns_have_indexes(): void
*/
private function assertIndexExists(string $table, string $column): void
{
$database = env('DB_DATABASE');

$indexes = DB::select("
SELECT
COLUMN_NAME
FROM
information_schema.STATISTICS
WHERE
TABLE_SCHEMA = ? AND
TABLE_NAME = ? AND
COLUMN_NAME = ?
", [$database, $table, $column]);

$indexExists = count($indexes) > 0;
// Use SQLite's PRAGMA index_list to get index information
$indexes = DB::select("PRAGMA index_list({$table})");

$indexExists = false;
foreach ($indexes as $index) {
// Get the columns for each index
$indexInfo = DB::select("PRAGMA index_info({$index->name})");
foreach ($indexInfo as $columnInfo) {
if ($columnInfo->name === $column) {
$indexExists = true;
break 2; // Break out of both loops
}
}
}

$this->assertTrue($indexExists, "Index on {$table}.{$column} should exist");
}
Expand Down
28 changes: 26 additions & 2 deletions frontend/src/components/Card/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import { useAuth } from "../../contexts/AuthContext";
export const Card = ({ title, price, onViewMore, product }) => {
const { isAuthenticated, user } = useAuth();
const navigate = useNavigate();

// Debug: Log the product data structure
console.log('Card component product data:', product);

const handleAddToCart = () => {
console.log('handleAddToCart called');
Expand All @@ -24,10 +27,14 @@ export const Card = ({ title, price, onViewMore, product }) => {

console.log('User is authenticated, proceeding with add to cart');
const productData = {
id: product?.id || Date.now(), // fallback ID if product object not available
id: product?.id || Date.now(),
title,
price,
image: perfumeImg // using default image for now
image: perfumeImg,
// Add complete product information for cart
brand: product?.brand?.name || 'Unknown',
size: product?.variants?.[0]?.size_ml ? `${product.variants[0].size_ml}ml` : 'N/A',
product: product // Store full product object for future reference
};

console.log('Product data to add:', productData);
Expand All @@ -46,6 +53,23 @@ export const Card = ({ title, price, onViewMore, product }) => {
<img src={perfumeImg} alt="Perfume" className={styles.image} />
<div className={styles.content}>
<h2 className={styles.title}>{title}</h2>

{/* Brand Information */}
<div className={styles.info}>
<Caption
caption={`Brand: ${product?.brand?.name || 'Unknown'}`}
variant="small"
/>
</div>

{/* Size Information */}
<div className={styles.info}>
<Caption
caption={`Size: ${product?.variants?.[0]?.size_ml ? `${product.variants[0].size_ml}ml` : 'N/A'}`}
variant="small"
/>
</div>

<Caption variant="big" caption={price} />
<div className={styles.actions}>
<Button
Expand Down
6 changes: 6 additions & 0 deletions frontend/src/components/Card/index.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@
background: linear-gradient(135deg, #fff 0%, #fafafa 100%);
}

.info {
margin: 0.25rem 0;
opacity: 0.8;
font-size: 0.875rem;
}

.title {
font-size: 1.25rem;
font-weight: 600;
Expand Down
43 changes: 27 additions & 16 deletions frontend/src/components/Header/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Link, useNavigate } from "react-router-dom";
import { CgProfile } from "react-icons/cg";
import { FaShoppingCart, FaChevronDown } from "react-icons/fa";
import { LuBellRing } from "react-icons/lu";
import { MdLogout, MdHistory } from "react-icons/md";
import { MdLogout, MdHistory, MdDashboard } from "react-icons/md";
import authService from "../../services/authService";
import { getLocalCartItemCount } from "../../services/cartService";
import NotificationModal from "../../features/Notification/components/NotificationModal";
Expand All @@ -19,8 +19,7 @@ const Header = () => {
const navigate = useNavigate();
const dropdownRef = useRef(null);


useEffect(() => {
useEffect(() => {
if (!user) {
setNotificationCount(0);
return;
Expand Down Expand Up @@ -140,20 +139,31 @@ const Header = () => {
<CgProfile className={styles.profileIcon} />
<span className={styles.userName}>Hi, {user.name}</span>
<FaChevronDown
className={`${styles.dropdownArrow} ${showDropdown ? styles.rotated : ""
}`}
className={`${styles.dropdownArrow} ${
showDropdown ? styles.rotated : ""
}`}
/>
</div>

{showDropdown && (
<div className={styles.dropdown}>
<Link
to="/order-history"
className={styles.dropdownItem}
onClick={() => setShowDropdown(false)}
>
<MdHistory /> History
</Link>
{user.role === "admin" ? (
<Link
to="/admin/orders"
className={styles.dropdownItem}
onClick={() => setShowDropdown(false)}
>
<MdDashboard /> Dashboard
</Link>
) : (
<Link
to="/order-history"
className={styles.dropdownItem}
onClick={() => setShowDropdown(false)}
>
<MdHistory /> History
</Link>
)}
<button
className={styles.dropdownItem}
onClick={handleLogout}
Expand All @@ -175,14 +185,15 @@ const Header = () => {
</li>

<li className={styles.cartIconContainer}>
<Link className={styles.cartLink}>
<LuBellRing onClick={() => setIsNotificationOpen(true)}/>
<Link className={styles.cartLink}>
<LuBellRing onClick={() => setIsNotificationOpen(true)} />
{notificationCount > 0 && (
<span className={styles.cartBadge}>{notificationCount}</span>
<span className={styles.cartBadge}>
{notificationCount}
</span>
)}
</Link>
</li>

</>
) : (
<>
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/Header/index.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
display: flex;
justify-content: space-between;
align-items: center;
padding: 1vh 2vw;
padding: 2vh 2vw;
background-color: var(--primary-color);
flex-wrap: wrap;
position: sticky;
top: 0;
z-index: 1000;
z-index: 12;
box-shadow: 0 2px 8px rgba(21, 48, 87, 0.1);
}

Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/Layout/index.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
.main {
flex: 1;
padding-top: 0;
padding-bottom: 24px;
/* padding-bottom: 24px; */
}
43 changes: 29 additions & 14 deletions frontend/src/components/StatusLabel/index.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,51 @@
}

.pending {
border: 1px solid #f7b90f;
color: #f7b90f;
/* border: 1px solid #f7b90f; */
color: #f7f7f7;
background-color: #f7b90f8b;
font-weight: bold;
}

.paid {
border: 1px solid #10b981;
color: #10b981;
/* border: 1px solid #10b981; */
color: #f7f7f7;
background-color: #10b981ae;
font-weight: bold;
}

.packed {
border: 1px solid #3b82f6;
color: #3b82f6;
/* border: 1px solid #3b82f6; */
color: #f7f7f7;
background-color: #3b83f6a8;
font-weight: bold;
}

.shipped {
border: 1px solid #6b7280;
color: #6b7280;
/* border: 1px solid #6b7280; */
color: #f7f7f7;
background-color: #6b72809f;
font-weight: bold;
}

.delivered {
border: 1px solid #59af59;
color: #59af59;
/* border: 1px solid #59af59; */
color: #f7f7f7;
background-color: #59af599a;

font-weight: bold;
}

.cancelled {
border: 1px solid #fb2448;
color: #fb2448;
/* border: 1px solid #fb2448; */
color: #f7f7f7;
font-weight: bold;
background-color: #fb2448a9;
}

.default {
border: 1px solid #6b7280;
color: #6b7280;
/* border: 1px solid #6b7280; */
color: #f7f7f7;
font-weight: bold;
background-color: #6b7280a0;
}
Loading
Loading