diff --git a/frontend/src/App.css b/frontend/src/App.css index f987250..168dec1 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -2,9 +2,18 @@ Farger og skrift kommer fra tema.css. */ #root { - max-width: 1100px; + width: 100%; + min-height: 100vh; + margin: 0; + padding: 0; +} + +main { + width: 100%; + max-width: 1400px; margin: 0 auto; - padding: 48px 24px; + padding: 32px; + box-sizing: border-box; } /* Søkefelt + knapp */ diff --git a/frontend/src/productDetails/ProductDetailsPage.css b/frontend/src/productDetails/ProductDetailsPage.css new file mode 100644 index 0000000..a24eb10 --- /dev/null +++ b/frontend/src/productDetails/ProductDetailsPage.css @@ -0,0 +1,527 @@ + +/* ===== PRODUKTDETALJSIDE ===== */ + +.product-details-page { + width: 100%; + max-width: 1100px; + margin: 0 auto; + padding: 48px 24px 80px; + box-sizing: border-box; + color: #171717; + text-align: left; +} + +.product-details-page *, +.product-details-page *::before, +.product-details-page *::after { + box-sizing: border-box; +} + +.product-details-page h1, +.product-details-page h2, +.product-details-page p, +.product-details-page figure, +.product-details-page ol, +.product-details-page dl { + margin: 0; +} + +.product-details-page h1, +.product-details-page h2 { + color: #171717; + text-align: left; +} + +/* ===== TILBAKE-LENKE ===== */ + +.product-details-page .product-back-navigation { + margin-bottom: 26px; +} + +.product-details-page .product-back-navigation a { + color: #d3045d; + text-decoration: none; + font-size: 11px; + font-weight: 600; + letter-spacing: 1px; +} + +.product-details-page .product-back-navigation a:hover { + text-decoration: underline; +} + +/* ===== HOVEDLAYOUT ===== */ + +.product-details-page .product-details-layout { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); + gap: 52px; + align-items: start; +} + +.product-details-page .product-details-left, +.product-details-page .product-details-right { + min-width: 0; + text-align: left; +} + +.product-details-page .product-details-left { + display: flex; + flex-direction: column; + gap: 30px; +} + +/* ===== PRODUKTBILDE ===== */ + +.product-details-page .product-figure { + margin: 0; +} + +.product-details-page .product-image { + width: 100%; + aspect-ratio: 1.2 / 1; + 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-details-page .product-image p { + color: #777; + font-size: 12px; + letter-spacing: 1px; +} + +.product-details-page .product-figure figcaption { + margin-top: 12px; + color: #777; + font-size: 10px; + letter-spacing: 1px; +} + +/* ===== INGREDIENSER OG ALLERGENER ===== */ + +.product-details-page .product-ingredients h2, +.product-details-page .product-allergens h2, +.product-details-page .product-nutrition h2 { + padding-bottom: 12px; + margin-bottom: 14px; + border-bottom: 1px solid #ddd; + font-size: 11px; + font-weight: 600; + letter-spacing: 0.5px; +} + +.product-details-page .section-letter { + color: #d3045d; + margin-right: 5px; +} + +.product-details-page .product-ingredients p { + font-size: 14px; + line-height: 1.7; + color: #555; +} + +.product-details-page .product-allergens p { + display: inline-block; + padding: 7px 10px; + border-radius: 4px; + background: #fce8ef; + color: #780b2b; + font-size: 11px; + font-weight: 600; +} + +/* ===== PRODUKTINFORMASJON ===== */ + +.product-details-page .product-info { + margin-bottom: 28px; +} + +.product-details-page .product-brand { + color: #d3045d; + font-size: 11px; + font-weight: 600; + letter-spacing: 1px; + margin-bottom: 8px; +} + +.product-details-page .product-name { + color: #171717; + font-size: 42px; + font-weight: 600; + line-height: 1.12; + letter-spacing: -1px; +} + +.product-details-page .product-category { + margin-top: 10px; + color: #777; + font-size: 11px; + letter-spacing: 1px; +} + +/* Prisområde */ + +.product-details-page .product-price-container { + display: flex; + align-items: baseline; + flex-wrap: wrap; + gap: 16px; + margin-top: 30px; + margin-bottom: 28px; +} + +.product-details-page .product-price { + display: inline-flex; + align-items: baseline; + gap: 5px; + margin: 0; + white-space: nowrap; + line-height: 1; +} + +.product-details-page .price-number { + font-size: 42px; + font-weight: 500; + color: #171717; +} + +.product-details-page .price-currency { + font-family: "Brush Script MT", "Apple Chancery", cursive; + font-size: 60px; /* Større */ + font-weight: 300; /* Tynnere */ + font-style: italic; + color: #780b2b; + line-height: 1; +} + +.product-details-page .product-cheapest-store { + margin: 0; + color: #777; + font-family: monospace; + font-size: 9px; + letter-spacing: 0.3px; +} + +.product-details-page .product-cheapest-store { + margin-top: 8px; + color: #777; + font-size: 10px; + letter-spacing: 0.5px; +} + +/* ===== PRISSAMMENLIGNING ===== */ + +.product-details-page .store-prices { + border: 1px solid #780b2b; + border-radius: 10px; + overflow: hidden; + box-shadow: 0 3px 0 rgba(120, 11, 43, 0.12); +} + +.product-details-page .store-prices-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + padding: 14px 16px; +} + +.product-details-page .store-prices-header h2 { + margin: 0; + padding: 0; + border: none; + font-size: 11px; + font-weight: 600; + letter-spacing: 1px; +} + +.product-details-page .store-prices-header span { + color: #777; + font-size: 10px; + white-space: nowrap; +} + +.product-details-page .store-prices-list { + margin: 0; + padding: 0; + list-style: none; +} + +.product-details-page .store-price-row { + display: grid; + grid-template-columns: 24px minmax(0, 1fr) 88px 90px; + align-items: center; + gap: 8px; + min-height: 46px; + padding: 12px 16px; + border-top: 1px solid #eee; +} + +.product-details-page .store-price-row.cheapest { + background: #fce8ef; +} + +.product-details-page .store-number { + color: #d3045d; + font-size: 10px; + font-weight: 700; +} + +.product-details-page .store-name { + color: #171717; + font-size: 13px; + font-weight: 500; +} + +.product-details-page .store-difference { + color: #888; + font-size: 10px; + text-align: right; + white-space: nowrap; +} + +.product-details-page .cheapest .store-difference { + justify-self: end; + padding: 5px 7px; + border-radius: 3px; + background: #d3045d; + color: white; + font-size: 9px; + font-weight: 700; +} + +.product-details-page .store-price { + color: #171717; + font-size: 13px; + font-weight: 600; + text-align: right; + white-space: nowrap; +} + +/* ===== HANDLELISTE ===== */ +.product-details-page .shopping-list-controls { + position: relative; + display: grid; + grid-template-columns: auto 1fr; + column-gap: 12px; + align-items: center; + + margin-top: 38px; + padding: 26px 26px 24px; + + border: 1px solid #171717; + border-left: 3px solid #d3045d; + background: white; + box-shadow: 0 12px 18px rgba(0, 0, 0, 0.08); +} + +.product-details-page .shopping-list-controls::before { + content: ""; + position: absolute; + width: 68px; + height: 16px; + top: -9px; + left: 50%; + transform: translateX(-50%) rotate(2deg); + background: #f6cada; +} + +.product-details-page .shopping-list-header { + grid-column: 1 / -1; +} + + +.product-details-page .shopping-list-header h2 { + color: #780b2b; + font-style: italic; + font-weight: 600; + line-height: 1.3; + font-size: 21px; +} + +.product-details-page .quantity-controls { + display: inline-flex; + align-items: center; + margin-top: 14px; + border: 1px solid #e5e5e5; + border-radius: 5px; + overflow: hidden; + grid-column: 1; + margin-top: 12px; +} + +.product-details-page .quantity-controls button { + min-width: 32px; + min-height: 32px; + padding: 4px; + background: white; + border: none; + color: #171717; + cursor: pointer; +} + +.product-details-page .quantity-controls button:hover:not(:disabled) { + background: #fce8ef; +} + +.product-details-page .quantity-controls button:disabled { + opacity: 0.4; + cursor: not-allowed; +} + +.product-details-page .quantity-controls output { + min-width: 24px; + text-align: center; + font-size: 12px; + color: #171717; +} + +.product-details-page .remove-product { + grid-column: 2; + justify-self: start; + + margin: 12px 0 0; + padding: 9px 12px; + + border: 1px solid #ddd; + border-radius: 5px; + background: white; + color: #171717; + + font-size: 11px; + cursor: pointer; +} + +.product-details-page .remove-product:hover { + background: #fce8ef; +} + +.product-details-page .shopping-list-controls label { + display: block; + margin-top: 24px; + margin-bottom: 10px; + color: #777; + font-size: 10px; + letter-spacing: 1px; +} + +.product-details-page .shopping-list-controls textarea { + display: block; + width: 100%; + min-height: 42px; + padding: 8px 0; + border: none; + border-bottom: 1px solid #f3b4cc; + border-radius: 0; + background: transparent; + color: #171717; + font: inherit; + font-size: 14px; + resize: vertical; + min-height: 40px; + height: 45px; +} + +.product-details-page .shopping-list-controls textarea::placeholder { + color: #888; + font-style: italic; +} + +.product-details-page .shopping-list-controls label, +.product-details-page .shopping-list-controls textarea { + grid-column: 1 / -1; +} + +.product-details-page button:focus-visible, +.product-details-page textarea:focus-visible, +.product-details-page a:focus-visible { + outline: 2px solid #780b2b; + outline-offset: 3px; +} + +/* ===== NÆRINGSINNHOLD ===== */ + +.product-details-page .product-nutrition { + margin-top: 38px; +} + +.product-details-page .product-nutrition dl { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + column-gap: 16px; + row-gap: 10px; + margin: 0; + font-size: 12px; +} + +.product-details-page .product-nutrition dt { + color: #666; +} + +.product-details-page .product-nutrition dd { + margin: 0; + color: #171717; + font-weight: 600; + text-align: right; +} + +.product-details-page .nutrition-subitem { + padding-left: 12px; + color: #999 !important; +} + +/* ===== RESPONSIVT DESIGN ===== */ + +@media (max-width: 768px) { + .product-details-page { + padding: 28px 16px 60px; + } + + .product-details-page .product-details-layout { + grid-template-columns: 1fr; + gap: 32px; + } + + .product-details-page .product-name { + font-size: 34px; + } + + .product-details-page .product-price { + font-size: 32px; + } +} + +@media (max-width: 480px) { + .product-details-page .store-price-row { + grid-template-columns: 20px minmax(0, 1fr) auto; + gap: 6px; + padding: 12px; + } + + .product-details-page .store-difference { + grid-column: 2; + grid-row: 2; + text-align: left; + justify-self: start; + } + + .product-details-page .store-price { + grid-column: 3; + grid-row: 1 / 3; + } + + .product-details-page .shopping-list-controls { + padding: 20px 16px; + } +} diff --git a/frontend/src/productDetails/ProductDetailsPage.tsx b/frontend/src/productDetails/ProductDetailsPage.tsx new file mode 100644 index 0000000..aace43a --- /dev/null +++ b/frontend/src/productDetails/ProductDetailsPage.tsx @@ -0,0 +1,191 @@ +import { useState } from "react"; +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() { + const [quantity, setQuantity] = useState(1); + const [isInList, setIsInList] = useState(true); + const [note, setNote] = useState(""); + return ( +
+
+ + {/* Venstre side: Produktbilde */} +
+
+
+

Mangler bilde

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

A — INGREDIENSER

+

Pasteurisert kumelk, salt, syrekultur, løpe.

+
+ +
+

B — ALLERGENER

+

MELK

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

TINE

+ +

Norvegia 26 %

+ +

500 G · OST

+ +
+

+ 79,90 + kr +

+ +

+ BILLIGST HOS KIWI · 159,80 KR/KG +

+
+
+
+

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. + ); + })} +
+
+
+ +
+

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

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