From efd11fc84aa89eaf088d64921237065b352e1fe8 Mon Sep 17 00:00:00 2001 From: Tara Aqeel Date: Tue, 13 Oct 2026 16:54:56 +0200 Subject: [PATCH 01/17] feat: add initial product details page layout --- .../src/productDetails/ProductDetailsPage.css | 70 +++++++++++++++++++ .../src/productDetails/ProductDetailsPage.tsx | 29 ++++++++ 2 files changed, 99 insertions(+) create mode 100644 frontend/src/productDetails/ProductDetailsPage.css create mode 100644 frontend/src/productDetails/ProductDetailsPage.tsx diff --git a/frontend/src/productDetails/ProductDetailsPage.css b/frontend/src/productDetails/ProductDetailsPage.css new file mode 100644 index 0000000..d48e663 --- /dev/null +++ b/frontend/src/productDetails/ProductDetailsPage.css @@ -0,0 +1,70 @@ +.product-details-page { + max-width: 1100px; + margin: 0 auto; + padding: 48px 24px; +} + +.product-details-layout { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 52px; +} + +.product-image { + width: 100%; + aspect-ratio: 4 / 3; + border: 1px solid #780b2b; + border-radius: 10px; + + display: flex; + justify-content: center; + align-items: center; + + background-color: #fafafa; + background-image: + linear-gradient(#e8e8e8 1px, transparent 1px), + linear-gradient(90deg, #e8e8e8 1px, transparent 1px); + background-size: 20px 20px; +} + +.product-image p { + font-size: 12px; + color: #666; + letter-spacing: 2px; +} + +.product-brand { + color: #d3045d; + font-size: 12px; + font-weight: bold; + letter-spacing: 2px; +} + +.product-name { + font-size: 44px; + font-weight: 600; + margin: 10px 0; +} + +.product-category { + font-size: 12px; + color: #777; + letter-spacing: 2px; +} + +.product-price { + font-size: 38px; + font-weight: 500; + margin-top: 32px; +} + +@media (max-width: 768px) { + .product-details-layout { + grid-template-columns: 1fr; + gap: 24px; + } + + .product-name { + font-size: 32px; + } +} \ No newline at end of file diff --git a/frontend/src/productDetails/ProductDetailsPage.tsx b/frontend/src/productDetails/ProductDetailsPage.tsx new file mode 100644 index 0000000..d51886f --- /dev/null +++ b/frontend/src/productDetails/ProductDetailsPage.tsx @@ -0,0 +1,29 @@ +import "./ProductDetailsPage.css"; + +export default function ProductDetailsPage() { + return ( +
+
+ + {/* Venstre side: Produktbilde */} +
+
+

Mangler bilde

+
+
+ + {/* Høyre side: Produktinformasjon */} +
+

TINE

+ +

Norvegia 26 %

+ +

500 G · OST

+ +

79,90 kr

+
+ +
+
+ ); +} \ No newline at end of file From 4403db97f56edab7bb874f74ded803d101cc6129 Mon Sep 17 00:00:00 2001 From: Tara Aqeel Date: Tue, 13 Oct 2026 17:09:51 +0200 Subject: [PATCH 02/17] feat: add ingredients, allergens and nutrition info to product details --- .../src/productDetails/ProductDetailsPage.css | 144 ++++++++++++++++++ .../src/productDetails/ProductDetailsPage.tsx | 96 +++++++++++- 2 files changed, 235 insertions(+), 5 deletions(-) diff --git a/frontend/src/productDetails/ProductDetailsPage.css b/frontend/src/productDetails/ProductDetailsPage.css index d48e663..719f166 100644 --- a/frontend/src/productDetails/ProductDetailsPage.css +++ b/frontend/src/productDetails/ProductDetailsPage.css @@ -67,4 +67,148 @@ .product-name { font-size: 32px; } +} + +/* Prissammenligning */ + +.store-prices { + margin-top: 28px; + border: 1px solid #780b2b; + border-radius: 10px; + overflow: hidden; +} + +.store-prices-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 16px; +} + +.store-prices-header h2 { + margin: 0; + font-size: 11px; + letter-spacing: 1px; +} + +.store-prices-header span { + color: #777; + font-size: 10px; +} + +.store-price-row { + display: grid; + grid-template-columns: 25px 1fr 100px 90px; + align-items: center; + gap: 8px; + padding: 16px; + border-top: 1px solid #eee; +} + +.store-price-row.cheapest { + background-color: #fce8ef; +} + +.store-number { + color: #d3045d; + font-size: 11px; + font-weight: bold; +} + +.store-name { + font-size: 14px; + font-weight: 500; +} + +.store-difference { + font-size: 10px; + color: #777; + text-align: right; +} + +.cheapest .store-difference { + color: #d3045d; + font-weight: bold; +} + +.store-price { + font-size: 14px; + font-weight: 600; + text-align: right; +} + +@media (max-width: 600px) { + .store-price-row { + grid-template-columns: 20px minmax(0, 1fr) auto; + gap: 6px; + padding: 12px; + } + + .store-difference { + grid-column: 2; + text-align: left; + } + + .store-price { + grid-column: 3; + grid-row: 1 / 3; + } +} + +.product-details-left { + display: flex; + flex-direction: column; + gap: 24px; +} + +.product-image { + margin: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.product-image figcaption { + font-size: 11px; + margin-top: 12px; + color: #777; +} + +.product-ingredients, +.product-allergens, +.product-nutrition { + border-top: 1px solid #ddd; + padding-top: 16px; +} + +.product-ingredients h2, +.product-allergens h2, +.product-nutrition h2 { + font-size: 12px; + letter-spacing: 1px; + margin-bottom: 12px; +} + +.product-ingredients p, +.product-allergens p { + font-size: 14px; + line-height: 1.6; +} + +.product-nutrition dl { + display: grid; + grid-template-columns: 1fr auto; + gap: 12px; + font-size: 13px; +} + +.product-nutrition dt { + color: #555; +} + +.product-nutrition dd { + margin: 0; + font-weight: 600; + text-align: right; } \ No newline at end of file diff --git a/frontend/src/productDetails/ProductDetailsPage.tsx b/frontend/src/productDetails/ProductDetailsPage.tsx index d51886f..bcae7cc 100644 --- a/frontend/src/productDetails/ProductDetailsPage.tsx +++ b/frontend/src/productDetails/ProductDetailsPage.tsx @@ -1,15 +1,63 @@ import "./ProductDetailsPage.css"; +type StorePrice = { + store: string; + price: number; +}; + +const storePrices: StorePrice[] = [ + { store: "Kiwi", price: 79.9 }, + { store: "Rema 1000", price: 81.4 }, + { store: "Extra", price: 84.9 }, + { store: "Meny", price: 92.9 }, + { store: "Spar", price: 94.5 }, +]; + export default function ProductDetailsPage() { return (
-
+
{/* Venstre side: Produktbilde */}
-
+

Mangler bilde

-
+
FIG. 5 — NORVEGIA 26 %, 500 G
+ + +
+

A — INGREDIENSER

+

Pasteurisert kumelk, salt, syrekultur, løpe.

+
+ +
+

B — ALLERGENER

+

MELK

+
+ +
+

C — NÆRINGSINNHOLD PER 100 G

+ +
+
Energi
+
1 469 kJ / 353 kcal
+ +
Fett
+
26 g
+ +
Hvorav mettede fettsyrer
+
17 g
+ +
Karbohydrater
+
0 g
+ +
Protein
+
27 g
+ +
Salt
+
1,2 g
+
+
{/* Høyre side: Produktinformasjon */} @@ -20,10 +68,48 @@ export default function ProductDetailsPage() {

500 G · OST

-

79,90 kr

+

79,90 kr

+
+
+

PRIS I BUTIKKENE

+ {storePrices.length} KJEDER +
+ +
    + {[...storePrices] + .sort((a, b) => a.price - b.price) + .map((item, index, sortedPrices) => { + const cheapestPrice = sortedPrices[0].price; + const difference = item.price - cheapestPrice; + + return ( +
  1. + + {String(index + 1).padStart(2, "0")} + + + {item.store} + + + {index === 0 + ? "BILLIGST" + : `+${difference.toFixed(2).replace(".", ",")} KR`} + + + + {item.price.toFixed(2).replace(".", ",")} kr + +
  2. + ); + })} +
+
-
+
); } \ No newline at end of file From 253228fd48652af282bfd606f44e5c181db8d4b7 Mon Sep 17 00:00:00 2001 From: Tara Aqeel Date: Tue, 13 Oct 2026 17:40:31 +0200 Subject: [PATCH 03/17] feat: add shopping list controls to product details --- .../src/productDetails/ProductDetailsPage.tsx | 60 ++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/frontend/src/productDetails/ProductDetailsPage.tsx b/frontend/src/productDetails/ProductDetailsPage.tsx index bcae7cc..141a4ec 100644 --- a/frontend/src/productDetails/ProductDetailsPage.tsx +++ b/frontend/src/productDetails/ProductDetailsPage.tsx @@ -1,3 +1,4 @@ +import { useState } from "react"; import "./ProductDetailsPage.css"; type StorePrice = { @@ -14,6 +15,9 @@ const storePrices: StorePrice[] = [ ]; export default function ProductDetailsPage() { + const [quantity, setQuantity] = useState(1); + const [isInList, setIsInList] = useState(true); + const [note, setNote] = useState(""); return (
@@ -107,8 +111,62 @@ export default function ProductDetailsPage() { })}
- +
+
+

+ {isInList ? "Står på lappen din" : "Ikke på lappen din"} +

+ +
+ + + {isInList ? quantity : 0} + + +
+
+ {isInList && ( + + )} + + + +