get-frontend/webshop_app/mvc-emne2
2025-07-01 20:42:02 +02:00
..
js second commit 2025-07-01 20:42:02 +02:00
ACCESSIBILITY.md second commit 2025-07-01 20:42:02 +02:00
flow-diagram.md second commit 2025-07-01 20:42:02 +02:00
index.html second commit 2025-07-01 20:42:02 +02:00
README-NO.md second commit 2025-07-01 20:42:02 +02:00
README.md second commit 2025-07-01 20:42:02 +02:00
styles.css second commit 2025-07-01 20:42:02 +02:00

MVC Webshop Application

A pure JavaScript implementation of a webshop using the Model-View-Controller (MVC) architectural pattern. This educational project demonstrates core web development concepts without external frameworks.

🏗️ Architecture Overview

This application follows the MVC pattern with a centralized view rendering system:

  • Model: Handles data management and business logic
  • View: Manages HTML generation and DOM manipulation
  • Controller: Coordinates user interactions and data flow

📊 UML Diagrams

Class Diagram

classDiagram
    class Model {
        -products: Array~Product~
        -cart: Array~CartItem~
        +getAllProducts() Array~Product~
        +getProductById(id) Product
        +addToCart(productId, quantity) boolean
        +removeFromCart(productId) void
        +updateCartQuantity(productId, quantity) void
        +getCart() Array~CartItem~
        +getCartTotal() number
        +getCartItemCount() number
        +clearCart() void
        +saveCartToStorage() void
        +loadCartFromStorage() Array~CartItem~
        +initializeCart() void
    }
    
    class View {
        -currentPage: string
        -cartCount: number
        +updateView(viewType, data) void
        +generateNavbar() string
        +generateProductsPage(products) string
        +generateProductDetailPage(product) string
        +generateCartPage(cartItems, total, itemCount) string
        +createProductCard(product) string
        +createCartItem(item) string
        +attachEventListeners() void
        +updateCartCountInNav() void
        +showNotification(message) void
    }
    
    class Controller {
        +loadProducts() void
        +showProductDetail(productId) void
        +handleAddToCart(productId) void
        +handleRemoveFromCart(productId) void
        +changeQuantity(productId, change) void
        +handleClearCart() void
        +navigateToProducts() void
        +navigateToCart() void
        +updateCartDisplay() void
    }
    
    class Product {
        +id: number
        +name: string
        +price: number
        +description: string
        +image: string
        +category: string
    }
    
    class CartItem {
        +productId: number
        +quantity: number
        +product: Product
    }
    
    class App {
        +initialize() void
    }
    
    Controller --> Model : uses
    Controller --> View : updates
    View --> Controller : sends events
    Model --> Product : contains
    Model --> CartItem : manages
    CartItem --> Product : references
    App --> Controller : initializes
    App --> Model : sets up
    App --> View : starts

Sequence Diagram - Add to Cart Flow

sequenceDiagram
    participant U as User
    participant V as View
    participant C as Controller
    participant M as Model
    
    U->>V: Click "Add to Cart"
    V->>C: handleAddToCart(productId)
    C->>M: addToCart(productId)
    M->>M: getProductById(productId)
    M->>M: Find existing item or create new
    M->>M: saveCartToStorage()
    M-->>C: return true/false
    
    alt Success
        C->>M: getProductById(productId)
        M-->>C: return product
        C->>V: updateView('notification', {message})
        C->>C: updateCartDisplay()
        C->>M: getCart()
        M-->>C: return cart
        C->>M: getCartTotal()
        M-->>C: return total
        C->>M: getCartItemCount()
        M-->>C: return itemCount
        C->>V: updateView('cart', {cartItems, total, itemCount})
        C->>V: updateView('cart-count', {count})
        V->>V: generateCartPage() or updateCartCountInNav()
        V-->>U: Updated UI with notification
    else Failure
        C->>V: updateView('notification', {message: 'Error'})
        V-->>U: Error message
    end

📁 Project Structure

mvc-emne2/
├── index.html          # Main HTML file with single app container
├── styles.css          # Application styling
├── js/
│   ├── app.js          # Application initialization
│   ├── model.js        # Data layer and business logic
│   ├── view.js         # UI rendering and HTML generation
│   └── controller.js   # User interaction handling
├── flow-diagram.md     # Application flow visualization
└── README.md          # This documentation

🔧 Key Components

1. Model (model.js)

Purpose: Manages application data and persistence

Key Functions:

  • getAllProducts() - Returns product catalog
  • getProductById(id) - Retrieves specific product
  • addToCart(productId, quantity) - Adds items to cart
  • removeFromCart(productId) - Removes items from cart
  • updateCartQuantity(productId, quantity) - Updates item quantities
  • getCart() - Returns current cart contents
  • getCartTotal() - Calculates cart total price
  • getCartItemCount() - Returns total item count
  • clearCart() - Empties the cart
  • saveCartToStorage() / loadCartFromStorage() - LocalStorage persistence

Data Structure:

// Product
{
    id: number,
    name: string,
    price: number,
    description: string,
    image: string (emoji),
    category: string
}

// Cart Item
{
    productId: number,
    quantity: number,
    product: Product
}

2. View (view.js)

Purpose: Handles all UI rendering through a centralized updateView function

Core Architecture:

function updateView(viewType, data = {}) {
    switch (viewType) {
        case 'products': // Render product listing
        case 'product-detail': // Render single product
        case 'cart': // Render shopping cart
        case 'cart-count': // Update cart badge
        case 'notification': // Show user messages
    }
}

Key Features:

  • Single Entry Point: All rendering goes through updateView()
  • Complete Page Generation: Generates entire page HTML including navigation
  • Event Listener Management: Automatically attaches event listeners after each render
  • Template Functions: Modular HTML generation functions

HTML Generation Functions:

  • generateNavbar() - Creates navigation header
  • generateProductsPage(products) - Product listing page
  • generateProductDetailPage(product) - Product detail page
  • generateCartPage(cartItems, total, itemCount) - Shopping cart page
  • createProductCard(product) - Individual product card
  • createCartItem(item) - Individual cart item

3. Controller (controller.js)

Purpose: Handles user interactions and coordinates between Model and View

Navigation Functions:

  • navigateToProducts() - Shows product listing
  • navigateToCart() - Shows shopping cart
  • showProductDetail(productId) - Shows product details

Cart Management:

  • handleAddToCart(productId) - Adds product to cart
  • handleRemoveFromCart(productId) - Removes product from cart
  • changeQuantity(productId, change) - Updates product quantity
  • handleClearCart() - Empties entire cart
  • updateCartDisplay() - Refreshes cart view and counter

Key Design Patterns:

  • All functions call updateView() for UI updates
  • User notifications are handled consistently
  • Cart state is automatically persisted after changes

4. Application Initialization (app.js)

Purpose: Bootstraps the application

Initialization Flow:

  1. Wait for DOM to load
  2. Initialize cart from localStorage
  3. Load products from model
  4. Render initial products page
  5. Update cart counter
document.addEventListener('DOMContentLoaded', function() {
    initializeCart();
    
    const products = getAllProducts();
    const itemCount = getCartItemCount();
    
    updateView('products', { products });
    updateView('cart-count', { count: itemCount });
    
    console.log('Nettbutikk MVC Demo initialisert!');
});

🔄 Data Flow

1. Application Startup

HTML Load → DOMContentLoaded → initializeCart() → loadCartFromStorage() → 
getAllProducts() → updateView('products') → generateProductsPage() → attachEventListeners()

2. User Interactions

User Click → Controller Function → Model Operation → updateView() → 
HTML Generation → Event Listener Attachment → UI Update

3. Cart Operations

Add to Cart → addToCart() → saveCartToStorage() → updateCartDisplay() → 
updateView('cart') + updateView('cart-count') → showNotification()

🎨 View Rendering System

Centralized Rendering

All UI updates flow through the single updateView() function:

updateView('products', { products: [...] });
updateView('product-detail', { product: {...} });
updateView('cart', { cartItems: [...], total: 0, itemCount: 0 });
updateView('cart-count', { count: 0 });
updateView('notification', { message: 'Success!' });

Complete Page Generation

Unlike traditional DOM manipulation, this system generates complete page HTML:

function generateProductsPage(products) {
    return `
        ${generateNavbar()}
        <main>
            <section class="page active">
                <h2>Våre Produkter</h2>
                <div class="products-grid">
                    ${products.map(product => createProductCard(product)).join('')}
                </div>
            </section>
        </main>
    `;
}

Event Listener Management

After each render, event listeners are reattached:

function attachEventListeners() {
    const productsLink = document.querySelector('[data-page="products"]');
    const cartLink = document.querySelector('[data-page="cart"]');
    
    if (productsLink) {
        productsLink.addEventListener('click', function(e) {
            e.preventDefault();
            navigateToProducts();
        });
    }
    // ... more listeners
}

💾 Data Persistence

LocalStorage Integration

Cart data is automatically persisted:

function saveCartToStorage() {
    try {
        localStorage.setItem('webstore_cart', JSON.stringify(cart));
    } catch (error) {
        console.error('Feil ved lagring av handlekurv til localStorage:', error);
    }
}

function loadCartFromStorage() {
    try {
        const savedCart = localStorage.getItem('webstore_cart');
        if (savedCart) {
            const parsedCart = JSON.parse(savedCart);
            return parsedCart.map(item => ({
                ...item,
                product: products.find(p => p.id === item.productId) || 
                         { id: item.productId, name: 'Ukjent Produkt', price: 0 }
            }));
        }
    } catch (error) {
        console.error('Feil ved lasting av handlekurv fra localStorage:', error);
    }
    return [];
}

🚀 Usage

Running the Application

  1. Open index.html in a web browser
  2. The application initializes automatically
  3. Navigate between products, product details, and cart
  4. Cart state persists across browser sessions

Key Features

  • Product Browsing: View all products in a grid layout
  • Product Details: Click any product for detailed view
  • Shopping Cart: Add, remove, and modify quantities
  • Persistent Cart: Cart contents saved in localStorage
  • Responsive Design: Works on desktop and mobile
  • User Notifications: Feedback for all user actions

🎯 Educational Benefits

MVC Pattern Demonstration

  • Clear separation of concerns
  • Model handles data, View handles presentation, Controller handles logic
  • Demonstrates how components interact in MVC architecture

Modern JavaScript Features

  • ES6+ syntax (arrow functions, template literals, destructuring)
  • LocalStorage API usage
  • Event handling patterns
  • Functional programming concepts

Web Development Fundamentals

  • DOM manipulation techniques
  • Event listener management
  • Client-side state management
  • HTML generation and templating
  • CSS styling integration

🔧 Technical Decisions

Why Centralized Rendering?

  • Consistency: All UI updates follow the same pattern
  • Maintainability: Easy to track and debug UI changes
  • Scalability: Simple to add new view types
  • State Management: Clear data flow from model to view

Why Complete Page Generation?

  • Simplicity: No complex DOM diffing or state tracking
  • Reliability: Fresh render eliminates stale state issues
  • Performance: Modern browsers handle innerHTML efficiently
  • Debugging: Easy to see exactly what HTML is generated

Why LocalStorage?

  • Persistence: Cart survives browser restarts
  • Simplicity: No server-side complexity
  • Performance: Instant load times
  • Educational: Demonstrates client-side storage concepts

🎨 Styling Architecture

The application uses a clean, modern CSS design:

  • Responsive grid layouts
  • Card-based product display
  • Consistent button and form styling
  • Mobile-friendly navigation
  • Smooth transitions and hover effects

🔮 Future Enhancements

This MVC implementation serves as a foundation for progressive enhancement:

  1. Phase 2: Add TypeScript for type safety
  2. Phase 3: Implement Vue.js for reactive UI
  3. Phase 4: Add user authentication
  4. Phase 5: Integrate payment processing
  5. Phase 6: Add product search and filtering
  6. Phase 7: Implement real backend API

📚 Learning Outcomes

By studying this codebase, you'll understand:

  • MVC architectural pattern implementation
  • Client-side state management techniques
  • Modern JavaScript development practices
  • HTML generation and templating approaches
  • Event-driven programming patterns
  • LocalStorage API usage
  • Responsive web design principles

This project is part of the GET Frontend Development curriculum, demonstrating core web development concepts through practical implementation.