second commit
This commit is contained in:
parent
0d337a818f
commit
bffdcf4bdf
16 changed files with 2366 additions and 4 deletions
30
webshop_app/mvc-emne2/js/app.js
Normal file
30
webshop_app/mvc-emne2/js/app.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
// Hovedinitialisering av applikasjonen når DOM er ferdig lastet
|
||||
// Setter opp initial tilstand og starter renderingsloopen
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Fjerner loading-indikator hvis den finnes
|
||||
// Defensiv sjekk fordi HTML-struktur kan endres
|
||||
const loadingElement = document.querySelector('.loading');
|
||||
if (loadingElement) {
|
||||
loadingElement.remove();
|
||||
}
|
||||
|
||||
// Laster lagret handlekurv fra localStorage før første rendering
|
||||
// Må skje først fordi andre operasjoner avhenger av handlekurvstatus
|
||||
initializeCart();
|
||||
|
||||
// Henter initial data fra model-lag
|
||||
const products = getAllProducts();
|
||||
const cart = getCart();
|
||||
const itemCount = getCartItemCount();
|
||||
|
||||
// Starter med produktoversikt som standardside
|
||||
// Viser brukeren det de mest sannsynlig ønsker å se først
|
||||
updateView('products', { products });
|
||||
|
||||
// Oppdaterer handlekurvteller i navigasjon basert på lagret data
|
||||
// Viktig for å vise korrekt status fra tidligere sessjon
|
||||
updateView('cart-count', { count: itemCount });
|
||||
|
||||
// Bekrefter vellykket initialisering i konsoll for debugging
|
||||
console.log('Nettbutikk MVC Demo initialisert!');
|
||||
});
|
95
webshop_app/mvc-emne2/js/controller.js
Normal file
95
webshop_app/mvc-emne2/js/controller.js
Normal file
|
@ -0,0 +1,95 @@
|
|||
// Laster og viser alle produkter - brukes ved app-initialisering og navigasjon
|
||||
// Henter data fra model og sender til view for rendering
|
||||
function loadProducts() {
|
||||
const products = getAllProducts();
|
||||
updateView('products', { products });
|
||||
}
|
||||
|
||||
// Viser detaljside for spesifikt produkt basert på ID
|
||||
// Kaller updateView som håndterer både gyldige og ugyldige produkter
|
||||
function showProductDetail(productId) {
|
||||
const product = getProductById(productId);
|
||||
updateView('product-detail', { product });
|
||||
}
|
||||
|
||||
// Hovedfunksjon for å legge produkter i handlekurv
|
||||
// Koordinerer mellom model (dataendring) og view (brukernotifikasjon + UI-oppdatering)
|
||||
function handleAddToCart(productId) {
|
||||
const success = addToCart(productId); // Model-operasjon som returnerer boolean
|
||||
if (success) {
|
||||
// Henter produktnavn for personlig notifikasjon
|
||||
const product = getProductById(productId);
|
||||
updateView('notification', { message: product.name + ' lagt til i handlekurv!' });
|
||||
// Oppdaterer handlekurvvisning og teller umiddelbart
|
||||
updateCartDisplay();
|
||||
} else {
|
||||
// Viser feilmelding hvis produktet ikke eksisterer
|
||||
updateView('notification', { message: 'Feil ved å legge produktet i handlekurven' });
|
||||
}
|
||||
}
|
||||
|
||||
// Fjerner produkt fra handlekurv med brukerbekreftelse
|
||||
// Kombinerer model-operasjon med brukernotifikasjon
|
||||
function handleRemoveFromCart(productId) {
|
||||
removeFromCart(productId); // Model-operasjon
|
||||
updateView('notification', { message: 'Vare fjernet fra handlekurv' });
|
||||
updateCartDisplay(); // Oppdaterer UI umiddelbart
|
||||
}
|
||||
|
||||
// Endrer antall av et produkt i handlekurv (+1 eller -1)
|
||||
// Inneholder defensiv sjekk selv om UI-logikk ikke burde kalle dette for ikke-eksisterende varer
|
||||
function changeQuantity(productId, change) {
|
||||
const cart = getCart();
|
||||
const item = cart.find(item => item.productId === productId);
|
||||
|
||||
// Sikkerhetsjekk - kun hvis varen faktisk finnes i handlekurven
|
||||
// Forhindrer feil hvis UI og model kommer ut av synk
|
||||
if (item) {
|
||||
const newQuantity = item.quantity + change;
|
||||
updateCartQuantity(productId, newQuantity); // Model håndterer edge cases (quantity <= 0)
|
||||
updateCartDisplay(); // Oppdaterer UI for å reflektere endringer
|
||||
}
|
||||
// Ingen else - ignorerer stille forsøk på å endre ikke-eksisterende varer
|
||||
}
|
||||
|
||||
// Tømmer hele handlekurven med bekreftelsdialog
|
||||
// Forhindrer utilsiktet tap av handlekurvinnhold
|
||||
function handleClearCart() {
|
||||
// Browser-standard confirm() for brukersikkerhet
|
||||
if (confirm('Er du sikker på at du vil tømme handlekurven?')) {
|
||||
clearCart(); // Model-operasjon
|
||||
updateView('notification', { message: 'Handlekurv tømt' });
|
||||
updateCartDisplay(); // Oppdaterer til tom handlekurv
|
||||
}
|
||||
// Ingen else - kansellering ignoreres stille
|
||||
}
|
||||
|
||||
// Navigerer til produktoversikt - brukes fra tilbake-knapp og hovednavigasjon
|
||||
// Laster ferske produktdata og rendrer siden
|
||||
function navigateToProducts() {
|
||||
const products = getAllProducts();
|
||||
updateView('products', { products });
|
||||
}
|
||||
|
||||
// Navigerer til handlekurvside med komplett handlekurvstatus
|
||||
// Samler all nødvendig data fra model i én operasjon
|
||||
function navigateToCart() {
|
||||
const cart = getCart();
|
||||
const total = getCartTotal();
|
||||
const itemCount = getCartItemCount();
|
||||
// Sender all handlekurvdata til view for komplett rendering
|
||||
updateView('cart', { cartItems: cart, total, itemCount });
|
||||
}
|
||||
|
||||
// Oppdaterer handlekurvvisning etter endringer (add, remove, quantity change)
|
||||
// Koordinerer mellom handlekurvside og navigationsteller
|
||||
function updateCartDisplay() {
|
||||
const cart = getCart();
|
||||
const total = getCartTotal();
|
||||
const itemCount = getCartItemCount();
|
||||
|
||||
// Oppdaterer handlekurvside (hvis bruker er på den siden)
|
||||
updateView('cart', { cartItems: cart, total, itemCount });
|
||||
// Oppdaterer også telleren i navigasjonen (synlig på alle sider)
|
||||
updateView('cart-count', { count: itemCount });
|
||||
}
|
195
webshop_app/mvc-emne2/js/model.js
Normal file
195
webshop_app/mvc-emne2/js/model.js
Normal file
|
@ -0,0 +1,195 @@
|
|||
// Statisk produktkatalog - bruker hardkodede data for å fokusere på MVC-arkitektur
|
||||
// fremfor backend-integrasjon i denne fasen av prosjektet
|
||||
const products = [
|
||||
{
|
||||
id: 1,
|
||||
name: "Trådløse Hodetelefoner",
|
||||
price: 99.99,
|
||||
description: "Høykvalitets trådløse hodetelefoner med støydemping og 30 timer batteritid.",
|
||||
image: "🎧", // Bruker emoji for enkelhet - unngår bildefilhåndtering
|
||||
category: "Elektronikk"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Smartklokke",
|
||||
price: 199.99,
|
||||
description: "Funksjonsrik smartklokke med treningssporing, pulsmåler og GPS.",
|
||||
image: "⌚",
|
||||
category: "Elektronikk"
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "Kaffetrakter",
|
||||
price: 79.99,
|
||||
description: "Programmerbar kaffetrakter med termokanne og automatisk bryggtimer.",
|
||||
image: "☕",
|
||||
category: "Hjem"
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "Ryggsekk",
|
||||
price: 49.99,
|
||||
description: "Slitesterke laptop-ryggsekk med flere rom og vannavstøtende materiale.",
|
||||
image: "🎒",
|
||||
category: "Livsstil"
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: "Skrivebordslampe",
|
||||
price: 39.99,
|
||||
description: "LED skrivebordslampe med justerbar lysstyrke og USB-ladeport.",
|
||||
image: "💡",
|
||||
category: "Hjem"
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: "Trådløs Mus",
|
||||
price: 29.99,
|
||||
description: "Ergonomisk trådløs mus med presis sporing og lang batteritid.",
|
||||
image: "🖱️",
|
||||
category: "Elektronikk"
|
||||
}
|
||||
];
|
||||
|
||||
// Handlekurv lagres i minnet som et array - enkelt å arbeide med og testbar
|
||||
// Hver handlekurvvare inneholder productId, quantity og en referanse til produktobjektet
|
||||
let cart = [];
|
||||
|
||||
// Getter-funksjoner for produktdata - gir kontrollert tilgang til data
|
||||
// og muliggjør fremtidig validering eller transformasjon
|
||||
function getAllProducts() {
|
||||
return products;
|
||||
}
|
||||
|
||||
// Konverterer id til integer fordi URL-parametere alltid er strings
|
||||
// Bruker find() for å returnere første match eller undefined hvis ikke funnet
|
||||
function getProductById(id) {
|
||||
return products.find(product => product.id === parseInt(id));
|
||||
}
|
||||
|
||||
// Legger til produkt i handlekurv med intelligent sammenslåing
|
||||
// quantity = 1 som standard fordi de fleste klikk legger til én vare
|
||||
function addToCart(productId, quantity = 1) {
|
||||
const product = getProductById(productId);
|
||||
// Kritisk validering - forhindrer at ugyldig produkter legges til
|
||||
// Returnerer false for å signalisere feil til kallende kode
|
||||
if (!product) return false;
|
||||
|
||||
// Sjekker om produktet allerede finnes for å unngå duplikater
|
||||
// Øker quantity istedenfor å lage nye poster - bedre brukeropplevelse
|
||||
const existingItem = cart.find(item => item.productId === productId);
|
||||
|
||||
if (existingItem) {
|
||||
// Øker eksisterende quantity - intuitivt for brukeren
|
||||
existingItem.quantity += quantity;
|
||||
} else {
|
||||
// Lager ny handlekurvpost med både ID og fullt produktobjekt
|
||||
// product-referansen unngår gjentatte oppslag senere
|
||||
cart.push({
|
||||
productId: productId, // For identifikasjon og sammenligning
|
||||
quantity: quantity, // Hvor mange brukeren vil ha
|
||||
product: product // Full produktinfo for visning
|
||||
});
|
||||
}
|
||||
|
||||
// Persisterer umiddelbart for å unngå tap av data
|
||||
saveCartToStorage();
|
||||
return true; // Signaliserer suksess til UI-lag
|
||||
}
|
||||
|
||||
// Fjerner hele produktlinjen fra handlekurv uavhengig av quantity
|
||||
// Bruker filter() for å lage nytt array - funksjonell programmering
|
||||
function removeFromCart(productId) {
|
||||
cart = cart.filter(item => item.productId !== productId);
|
||||
saveCartToStorage(); // Persister endring umiddelbart
|
||||
}
|
||||
|
||||
// Oppdaterer antall av eksisterende handlekurvvare
|
||||
// Håndterer edge case hvor quantity blir 0 eller negativ
|
||||
function updateCartQuantity(productId, quantity) {
|
||||
const item = cart.find(item => item.productId === productId);
|
||||
if (item) {
|
||||
// Forretningsregel: quantity <= 0 betyr fjern produktet helt
|
||||
// Mer intuitivt enn å ha 0 varer i handlekurven
|
||||
if (quantity <= 0) {
|
||||
removeFromCart(productId);
|
||||
} else {
|
||||
item.quantity = quantity;
|
||||
saveCartToStorage();
|
||||
}
|
||||
}
|
||||
// Ingen else - ignorerer oppdateringer til ikke-eksisterende varer
|
||||
}
|
||||
|
||||
// Returnerer hele handlekurv-arrayet for visning
|
||||
// Andre komponenter kan ikke direkte mutere cart-variabelen
|
||||
function getCart() {
|
||||
return cart;
|
||||
}
|
||||
|
||||
// Beregner total pris for alle varer i handlekurv
|
||||
// Bruker reduce() for elegant summering av pris * quantity
|
||||
function getCartTotal() {
|
||||
return cart.reduce((total, item) => {
|
||||
return total + (item.product.price * item.quantity);
|
||||
}, 0); // Starter med 0 som akkumulator
|
||||
}
|
||||
|
||||
// Teller totalt antall individuelle varer (ikke unike produkter)
|
||||
// Eksempel: 2 hodetelefoner + 3 kaffetraktere = 5 varer totalt
|
||||
function getCartItemCount() {
|
||||
return cart.reduce((count, item) => count + item.quantity, 0);
|
||||
}
|
||||
|
||||
// Tømmer hele handlekurven - brukes ved utsjekking eller manuell tømming
|
||||
// Setter cart til tomt array istedenfor å mutere eksisterende
|
||||
function clearCart() {
|
||||
cart = [];
|
||||
saveCartToStorage(); // Persister tom handlekurv
|
||||
}
|
||||
|
||||
// Lagrer handlekurv til nettleserens localStorage for persistering
|
||||
// Try-catch fordi localStorage kan feile i private browsing eller ved kvotaoverskridelse
|
||||
function saveCartToStorage() {
|
||||
try {
|
||||
// Konverterer JavaScript-objekt til JSON-string for lagring
|
||||
localStorage.setItem('webstore_cart', JSON.stringify(cart));
|
||||
} catch (error) {
|
||||
// Logger feil men lar appen fortsette å fungere
|
||||
// Graceful degradation - handlekurv fungerer fortsatt i denne sesjonen
|
||||
console.error('Feil ved lagring av handlekurv til localStorage:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Laster handlekurv fra localStorage og rekonstruerer produktreferanser
|
||||
// Håndterer scenarioer hvor lagret data kan være korrupt eller utdatert
|
||||
function loadCartFromStorage() {
|
||||
try {
|
||||
const savedCart = localStorage.getItem('webstore_cart');
|
||||
// Sjekker om data finnes før parsing - første gangs brukere har tom localStorage
|
||||
if (savedCart) {
|
||||
const parsedCart = JSON.parse(savedCart);
|
||||
// Rekonstruerer produktreferanser fordi localStorage kun lagrer JSON
|
||||
// Fallback til "Ukjent Produkt" hvis produktet er fjernet fra katalogen
|
||||
return parsedCart.map(item => ({
|
||||
...item,
|
||||
// Kritisk: Sikrer at produktreferanse alltid finnes
|
||||
// Håndterer tilfeller hvor produktkatalog endres mellom sesjoner
|
||||
product: products.find(p => p.id === item.productId) ||
|
||||
{ id: item.productId, name: 'Ukjent Produkt', price: 0 }
|
||||
}));
|
||||
}
|
||||
} catch (error) {
|
||||
// JSON parsing kan feile hvis data er korrupt
|
||||
// Logger feil og returnerer tom array som fallback
|
||||
console.error('Feil ved lasting av handlekurv fra localStorage:', error);
|
||||
}
|
||||
// Returnerer tom array som sikker fallback
|
||||
return [];
|
||||
}
|
||||
|
||||
// Initialiserer handlekurv ved appstart
|
||||
// Setter cart-variabelen basert på lagret data eller tom array
|
||||
function initializeCart() {
|
||||
cart = loadCartFromStorage();
|
||||
}
|
283
webshop_app/mvc-emne2/js/view.js
Normal file
283
webshop_app/mvc-emne2/js/view.js
Normal file
|
@ -0,0 +1,283 @@
|
|||
// Holder styr på gjeldende side for navigasjonslogikk
|
||||
let currentPage = 'products';
|
||||
// Lokal kopi av handlekurvantall for rask oppdatering uten model-kall
|
||||
let cartCount = 0;
|
||||
|
||||
/**
|
||||
* Sentral renderingsfunksjon - alle UI-oppdateringer går gjennom denne
|
||||
* Bruker switch-pattern for å håndtere forskjellige visningstyper
|
||||
* data-parameter bruker default empty object for å unngå undefined errors
|
||||
*/
|
||||
function updateView(viewType, data = {}) {
|
||||
const app = document.getElementById('app');
|
||||
|
||||
switch (viewType) {
|
||||
case 'products':
|
||||
currentPage = 'products';
|
||||
// Fallback til tom array hvis products er undefined - forhindrer krasj
|
||||
app.innerHTML = generateProductsPage(data.products || []);
|
||||
// Fester event listeners etter hver render fordi innerHTML erstatter alt
|
||||
attachEventListeners();
|
||||
break;
|
||||
case 'product-detail':
|
||||
currentPage = 'product-detail';
|
||||
app.innerHTML = generateProductDetailPage(data.product);
|
||||
attachEventListeners();
|
||||
break;
|
||||
case 'cart':
|
||||
currentPage = 'cart';
|
||||
// Flere fallbacks fordi handlekurvdata kan være incomplete
|
||||
app.innerHTML = generateCartPage(data.cartItems || [], data.total || 0, data.itemCount || 0);
|
||||
attachEventListeners();
|
||||
break;
|
||||
case 'cart-count':
|
||||
// Spesiell case - oppdaterer kun handlekurvteller uten full re-render
|
||||
// Mer effektivt enn å regenerere hele siden
|
||||
cartCount = data.count;
|
||||
updateCartCountInNav();
|
||||
break;
|
||||
case 'notification':
|
||||
// Brukernotifikasjoner håndteres separat fra hovedrendering
|
||||
showNotification(data.message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Genererer navigasjonsbar som inkluderes på alle sider
|
||||
// Returnerer HTML-string istedenfor DOM-manipulering for konsistens
|
||||
function generateNavbar() {
|
||||
return `
|
||||
<header>
|
||||
<nav class="navbar">
|
||||
<h1 class="logo">Nettbutikk</h1>
|
||||
<ul class="nav-links">
|
||||
<li><a href="#" data-page="products">Produkter</a></li>
|
||||
<li><a href="#" data-page="cart" class="cart-link">Handlekurv (<span id="cart-count">${cartCount}</span>)</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
`;
|
||||
}
|
||||
|
||||
// Genererer komplett produktlisteside med navbar og produktrutenett
|
||||
// Bruker template literals for lesbar HTML-struktur
|
||||
function generateProductsPage(products) {
|
||||
return `
|
||||
${generateNavbar()}
|
||||
<main>
|
||||
<section class="page active" role="main" aria-label="Produktoversikt">
|
||||
<header>
|
||||
<h2>Våre Produkter</h2>
|
||||
</header>
|
||||
<div class="products-grid" role="list" aria-label="Liste over produkter">
|
||||
${/* Mapper hvert produkt til HTML-kort og slår sammen til string */
|
||||
products.map(product => createProductCard(product)).join('')}
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
`;
|
||||
}
|
||||
|
||||
// Genererer produktdetaljside med error handling for ugyldig produkt
|
||||
// Håndterer both null/undefined produkter gracefully
|
||||
function generateProductDetailPage(product) {
|
||||
// Kritisk sjekk - håndterer tilfeller hvor bruker navigerer til ugyldig produkt-ID
|
||||
// Viser feilmelding istedenfor å krasje
|
||||
if (!product) {
|
||||
return `
|
||||
${generateNavbar()}
|
||||
<main>
|
||||
<section class="page active" role="main" aria-label="Produktdetaljer">
|
||||
<nav aria-label="Breadcrumb">
|
||||
<button class="back-btn" onclick="navigateToProducts()" aria-label="Gå tilbake til produktoversikt">← Tilbake til Produkter</button>
|
||||
</nav>
|
||||
<div role="alert" aria-live="polite">
|
||||
<p>Produktet ble ikke funnet</p>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
`;
|
||||
}
|
||||
|
||||
// Normal rendering med fullstendige produktdetaljer
|
||||
// Bruker semantic HTML og ARIA for tilgjengelighet
|
||||
return `
|
||||
${generateNavbar()}
|
||||
<main>
|
||||
<section class="page active" role="main" aria-label="Produktdetaljer">
|
||||
<nav aria-label="Breadcrumb">
|
||||
<button class="back-btn" onclick="navigateToProducts()" aria-label="Gå tilbake til produktoversikt">← Tilbake til Produkter</button>
|
||||
</nav>
|
||||
<article class="product-detail">
|
||||
<header>
|
||||
<div class="product-image" role="img" aria-label="Produktbilde: ${product.name}">${product.image}</div>
|
||||
<h1 class="product-name">${product.name}</h1>
|
||||
</header>
|
||||
<div class="product-info">
|
||||
<p class="product-price" aria-label="Pris: ${product.price.toFixed(2)} kroner">
|
||||
<span class="currency">kr</span> <span class="amount">${product.price.toFixed(2)}</span>
|
||||
</p>
|
||||
<p class="product-description">${product.description}</p>
|
||||
<p class="product-category">Kategori: <span>${product.category}</span></p>
|
||||
</div>
|
||||
<footer>
|
||||
<button class="btn btn-success" onclick="handleAddToCart(${product.id})" aria-label="Legg ${product.name} i handlekurv">
|
||||
Legg i handlekurv
|
||||
</button>
|
||||
</footer>
|
||||
</article>
|
||||
</section>
|
||||
</main>
|
||||
`;
|
||||
}
|
||||
|
||||
// Genererer handlekurvside med kondisjonell rendering basert på innhold
|
||||
// Håndterer både tom og fylt handlekurv med forskjellige UI-elementer
|
||||
function generateCartPage(cartItems, total, itemCount) {
|
||||
// Ternary operator for å vise forskjellig innhold basert på handlekurvstatus
|
||||
// Tom handlekurv får eget design med oppfordring til handling
|
||||
const cartContent = cartItems.length === 0 ?
|
||||
`<div class="empty-cart" role="status" aria-live="polite">
|
||||
<h3>Handlekurven din er tom</h3>
|
||||
<p>Legg til noen produkter for å komme i gang!</p>
|
||||
</div>` :
|
||||
// Mapper hver handlekurvvare til HTML og slår sammen
|
||||
cartItems.map(item => createCartItem(item)).join('');
|
||||
|
||||
// Viser kun total og handlingsknapper hvis handlekurven har varer
|
||||
// Unngår forvirrende UI-elementer når handlekurven er tom
|
||||
const cartTotal = itemCount === 0 ? '' :
|
||||
`<footer class="cart-total">
|
||||
<div class="total-amount" role="status" aria-live="polite" aria-label="Totalt beløp: ${total.toFixed(2)} kroner">
|
||||
Totalt: <span class="amount">${total.toFixed(2)} kr</span>
|
||||
</div>
|
||||
<div class="cart-actions">
|
||||
<button class="btn btn-primary" onclick="alert('Utsjekking er ikke implementert i denne demoen')" aria-label="Gå til kasse for å fullføre kjøp">Gå til kasse</button>
|
||||
<button class="btn btn-danger" onclick="handleClearCart()" aria-label="Tøm hele handlekurven">Tøm handlekurv</button>
|
||||
</div>
|
||||
</footer>`;
|
||||
|
||||
return `
|
||||
${generateNavbar()}
|
||||
<main>
|
||||
<section class="page active" role="main" aria-label="Handlekurv">
|
||||
<header>
|
||||
<h2>Handlekurv</h2>
|
||||
${/* Viser kun sammendrag hvis det finnes varer - bedre UX */
|
||||
itemCount > 0 ? `<p class="cart-summary" aria-live="polite">${itemCount} ${itemCount === 1 ? 'vare' : 'varer'} i handlekurven</p>` : ''}
|
||||
</header>
|
||||
<div class="cart-items" role="list" aria-label="Varer i handlekurv">
|
||||
${cartContent}
|
||||
</div>
|
||||
${cartTotal}
|
||||
</section>
|
||||
</main>
|
||||
`;
|
||||
}
|
||||
|
||||
// Fester event listeners til navigasjonselementer etter hver rendering
|
||||
// Må kalles etter innerHTML fordi det erstatter alle eksisterende listeners
|
||||
function attachEventListeners() {
|
||||
const productsLink = document.querySelector('[data-page="products"]');
|
||||
const cartLink = document.querySelector('[data-page="cart"]');
|
||||
|
||||
// Defensiv programming - sjekker at elementer eksisterer før listener-tilkobling
|
||||
// Forhindrer feil hvis HTML-struktur endres
|
||||
if (productsLink) {
|
||||
productsLink.addEventListener('click', function(e) {
|
||||
e.preventDefault(); // Forhindrer standard link-navigasjon
|
||||
navigateToProducts();
|
||||
});
|
||||
}
|
||||
|
||||
if (cartLink) {
|
||||
cartLink.addEventListener('click', function(e) {
|
||||
e.preventDefault(); // Forhindrer standard link-navigasjon
|
||||
navigateToCart();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Oppdaterer kun handlekurvtelleren uten full side-regenerering
|
||||
// Mer effektivt for hyppige oppdateringer som quantity-endringer
|
||||
function updateCartCountInNav() {
|
||||
const cartCountElement = document.getElementById('cart-count');
|
||||
// Sikkerhetsjekk - elementet kan mangle hvis navbar ikke er rendret ennå
|
||||
if (cartCountElement) {
|
||||
cartCountElement.textContent = cartCount;
|
||||
}
|
||||
}
|
||||
|
||||
// Removed dead code: showPage() and renderProducts() functions
|
||||
// These were remnants from old multi-page architecture
|
||||
|
||||
function createProductCard(product) {
|
||||
return `
|
||||
<article class="product-card" role="listitem" tabindex="0" onclick="showProductDetail(${product.id})"
|
||||
onkeydown="if(event.key==='Enter'||event.key===' ') showProductDetail(${product.id})"
|
||||
aria-label="Produkt: ${product.name}, Pris: ${product.price.toFixed(2)} kroner">
|
||||
<header>
|
||||
<div class="product-image" role="img" aria-label="Produktbilde: ${product.name}">${product.image}</div>
|
||||
<h3 class="product-name">${product.name}</h3>
|
||||
</header>
|
||||
<div class="product-info">
|
||||
<p class="product-price" aria-label="Pris: ${product.price.toFixed(2)} kroner">
|
||||
<span class="currency">kr</span> <span class="amount">${product.price.toFixed(2)}</span>
|
||||
</p>
|
||||
<p class="product-description">${product.description}</p>
|
||||
</div>
|
||||
<footer>
|
||||
<button class="btn btn-success" onclick="event.stopPropagation(); handleAddToCart(${product.id})"
|
||||
aria-label="Legg ${product.name} i handlekurv">
|
||||
Legg i handlekurv
|
||||
</button>
|
||||
</footer>
|
||||
</article>
|
||||
`;
|
||||
}
|
||||
|
||||
// Removed dead code: renderProductDetail() function
|
||||
// Replaced by generateProductDetailPage() in modern architecture
|
||||
|
||||
// Removed dead code: renderCart() function
|
||||
// Replaced by generateCartPage() in modern architecture
|
||||
|
||||
function createCartItem(item) {
|
||||
return `
|
||||
<article class="cart-item" role="listitem">
|
||||
<header class="cart-item-info">
|
||||
<h3 class="cart-item-name">${item.product.name}</h3>
|
||||
<p class="cart-item-price" aria-label="Pris per stykk: ${item.product.price.toFixed(2)} kroner">
|
||||
<span class="amount">${item.product.price.toFixed(2)}</span> <span class="currency">kr</span> per stk
|
||||
</p>
|
||||
</header>
|
||||
<div class="cart-item-controls">
|
||||
<div class="quantity-controls" role="group" aria-label="Antall ${item.product.name}">
|
||||
<button class="quantity-btn" onclick="changeQuantity(${item.productId}, -1)"
|
||||
aria-label="Reduser antall ${item.product.name}"
|
||||
${item.quantity <= 1 ? 'aria-describedby="min-quantity-warning"' : ''}>-</button>
|
||||
<span class="quantity" role="status" aria-live="polite" aria-label="${item.quantity} stykker">${item.quantity}</span>
|
||||
<button class="quantity-btn" onclick="changeQuantity(${item.productId}, 1)"
|
||||
aria-label="Øk antall ${item.product.name}">+</button>
|
||||
${item.quantity <= 1 ? '<span id="min-quantity-warning" class="sr-only">Minimum antall er 1</span>' : ''}
|
||||
</div>
|
||||
<div class="item-total" aria-label="Totalt for ${item.product.name}: ${(item.product.price * item.quantity).toFixed(2)} kroner">
|
||||
<span class="amount">${(item.product.price * item.quantity).toFixed(2)}</span> <span class="currency">kr</span>
|
||||
</div>
|
||||
<button class="btn btn-remove" onclick="handleRemoveFromCart(${item.productId})"
|
||||
aria-label="Fjern ${item.product.name} fra handlekurv">
|
||||
Fjern
|
||||
</button>
|
||||
</div>
|
||||
</article>
|
||||
`;
|
||||
}
|
||||
|
||||
// Removed dead code: renderCartTotal() and updateCartCount() functions
|
||||
// Replaced by generateCartPage() and updateCartCountInNav() in modern architecture
|
||||
|
||||
// Viser brukernotifikasjoner - bruker alert() for enkelhet i prototypefasen
|
||||
// I produksjon ville dette vært en toast-notifikasjon eller modal
|
||||
function showNotification(message) {
|
||||
alert(message);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue