Skip to content

jing0728/WGU-Java-Frameworks-D287

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

D287-Web-Based Spring Inventory Application

Student: Jingyuan Wang
Student ID: 012394928


C. Customize the HTML user interface for your customer's application. The user interface should include the shop name, the product names, and the names of the parts.

Changes Made to mainscreen.html

Title and Branding Changes

CHANGE - mainscreen.html
    Line 14 - <title>My Bicycle Shop</title>
    TO
    Line 14 - <title>Jingyuan Wang's Computer Supply Shop</title>

CHANGE - mainscreen.html
    Line 19 - <h1>Shop</h1>
    TO
    Line 21-23 - <h1 class="shop-title">🚴 Welcome to My Computer Supply Shop 🚴</h1>
                 <p class="shop-subtitle">Browse and manage all parts & products below</p>

Styling and Layout Enhancements

CHANGE - mainscreen.html
    Line 10 - (No custom CSS reference)
    TO
    Line 12 - <link rel="stylesheet" href="/css/demo.css"> <!--  Added custom CSS -->

CHANGE - mainscreen.html
    Line 15 - <body>
    TO
    Line 15 - <body class="shop-body"> <!--  Add body styling -->

CHANGE - mainscreen.html
    Line 17 - <div class="container">
    TO
    Line 17 - <div class="container mt-4">
             Line 18-21 - <div class="shop-header text-center mb-4"> <!-- Styled header -->
                          (Header content with styling classes)
                          </div>

Form and Button Styling Updates

CHANGE - mainscreen.html
    Line 24-30 - Basic form layout without Bootstrap flex classes
    TO
    Line 26-32 - <form th:action="@{/mainscreen}" class="d-flex mb-3 filter-form">
                 (Updated with Bootstrap flex and margin classes)

CHANGE - mainscreen.html
    Line 35-36 - <a th:href="@{/showFormAddInPart}" class="btn btn-primary btn-sm mb-3">Add Inhouse Part</a>
                 <a th:href="@{/showFormAddOutPart}" class="btn btn-primary btn-sm mb-3">Add Outsourced Part</a>
    TO
    Line 38-41 - <a th:href="@{/showFormAddInPart}" class="btn btn-info btn-sm me-2">Add Inhouse Part</a>
                 <a th:href="@{/showFormAddOutPart}" class="btn btn-info btn-sm">Add Outsourced Part</a>

Table Styling Improvements

CHANGE - mainscreen.html
    Line 37 - <table class="table table-bordered table-striped">
    TO
    Line 43 - <table class="table table-hover table-striped part-table">

CHANGE - mainscreen.html
    Line 38 - <thead class="thead-dark">
    TO
    Line 44 - <thead class="table-dark">

Section Headers

CHANGE - mainscreen.html
    Line 22 - <h2>Parts</h2>
    TO
    Line 24 - <h2 class="section-title">Parts</h2>

CHANGE - mainscreen.html
    Line 56 - <h2>Products</h2>
    TO
    Line 62 - <h2 class="section-title">Products</h2>

Action Button Color Updates

CHANGE - mainscreen.html
    Line 50-52 - <a th:href="@{/showPartFormForUpdate(partID=${tempPart.id})}" class="btn btn-primary btn-sm mb-3">Update</a>
                 <a th:href="@{/deletepart(partID=${tempPart.id})}" class="btn btn-primary btn-sm mb-3">Delete</a>
    TO
    Line 56-58 - <a th:href="@{/showPartFormForUpdate(partID=${tempPart.id})}" class="btn btn-warning btn-sm me-2">Update</a>
                 <a th:href="@{/deletepart(partID=${tempPart.id})}" class="btn btn-danger btn-sm">Delete</a>

Changes Made to demo.css

Custom CSS File Added

ADDED - demo.css (New File)
    Custom stylesheet with the following styling:
    
    .shop-body {
        background-color: #f4f8fc;
        font-family: "Segoe UI", sans-serif;
    }

    .shop-header {
        background-color: #e3f2fd;
        padding: 20px;
        border-radius: 10px;
        box-shadow: 0 2px 6px rgba(0,0,0,0.1);
    }

    .shop-title {
        font-size: 2.5rem;
        font-weight: bold;
        color: #1976d2;
    }

    .shop-subtitle {
        font-size: 1.1rem;
        color: #555;
    }

    .section-title {
        margin-top: 2rem;
        color: #0d6efd;
        border-bottom: 2px solid #0d6efd;
        padding-bottom: 4px;
    }

    .part-table, .product-table {
        font-size: 0.95rem;
    }

    .filter-form input[type="text"] {
        min-width: 300px;
    }

D. Add an "About" page to the application to describe your chosen customer's company to web viewers and include navigation to and from the "About" page and the main screen.

CREATE - about.html

<!-- File: about.html -->
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>About Us - Jingyuan Wang's Computer Supply Store</title>
    <link rel="stylesheet" href="/css/demo.css">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"
          integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
</head>
<body class="shop-body">
<div class="container mt-4">
    <div class="shop-header text-center mb-4">
        <h1 class="shop-title">About Jingyuan Wang's Computer Supply Store</h1>
    </div>
    <p class="lead">
        Jingyuan Wang's Computer Supply Store is your one-stop destination for high-quality computer components and accessories.
        Whether you're a professional IT technician, a student building your first PC, or a company upgrading equipment—
        we've got you covered. From motherboards and graphics cards to in-house support and custom builds, we deliver excellence and service.
    </p>

    <p>
        Founded in 2025, our mission has always been to make computing easier, faster, and more affordable for everyone.
        We proudly support both local customers and nationwide shipping.
    </p>

    <a href="/" class="btn btn-primary mt-3">← Back to Shop</a>
</div>
</body>
</html>

INSERT - mainscreen.html

Line 106-110
<!-- Add inside <div class="shop-header text-center mb-4"> -->
<p class="shop-subtitle">
    Browse and manage all parts & products below |
    <a href="/about" class="link-primary text-decoration-none">About Us</a>
</p>

INSERT - MainScreenControllerr.java

Line 55-58
    @GetMapping("/about")
    public String showAboutPage() {
        return "about";
    }

E. Add a sample inventory appropriate for your chosen store to the application. You should have five parts and five products in your sample inventory and should not overwrite existing data in the database.

INSERT - BootStrapData.java, Line 41-125

if (partRepository.count() == 0 && productRepository.count() == 0) {
    OutsourcedPart processor = new OutsourcedPart();
    processor.setCompanyName("Intel Corp");
    processor.setName("Intel Core i7 Processor");
    processor.setInv(25);
    processor.setPrice(349.99);
    outsourcedPartRepository.save(processor);

    OutsourcedPart memory = new OutsourcedPart();
    memory.setCompanyName("Corsair");
    memory.setName("16GB DDR4 RAM");
    memory.setInv(40);
    memory.setPrice(89.99);
    outsourcedPartRepository.save(memory);

    OutsourcedPart storage = new OutsourcedPart();
    storage.setCompanyName("Samsung");
    storage.setName("1TB SSD Drive");
    storage.setInv(30);
    storage.setPrice(129.99);
    outsourcedPartRepository.save(storage);

    OutsourcedPart graphics = new OutsourcedPart();
    graphics.setCompanyName("NVIDIA");
    graphics.setName("GeForce RTX 3060");
    graphics.setInv(15);
    graphics.setPrice(299.99);
    outsourcedPartRepository.save(graphics);

    OutsourcedPart motherboard = new OutsourcedPart();
    motherboard.setCompanyName("ASUS");
    motherboard.setName("Gaming Motherboard");
    motherboard.setInv(20);
    motherboard.setPrice(179.99);
    outsourcedPartRepository.save(motherboard);

    List<OutsourcedPart> existingParts = (List<OutsourcedPart>) outsourcedPartRepository.findAll();
    boolean duplicateFound = false;
    for (OutsourcedPart part : existingParts) {
        if (part.getName().equals("16GB DDR4 RAM") && part.getCompanyName().equals("Corsair")) {
            duplicateFound = true;
            break;
        }
    }

    if (duplicateFound) {
        OutsourcedPart memoryMultiPack = new OutsourcedPart();
        memoryMultiPack.setCompanyName("Corsair");
        memoryMultiPack.setName("32GB DDR4 RAM Multi-Pack (2x16GB)");
        memoryMultiPack.setInv(20);
        memoryMultiPack.setPrice(169.99);
        outsourcedPartRepository.save(memoryMultiPack);
    }

    Product gamingPC = new Product("Gaming Desktop Computer", 1299.99, 8);
    productRepository.save(gamingPC);

    Product officePC = new Product("Office Desktop Computer", 799.99, 12);
    productRepository.save(officePC);

    Product laptop = new Product("Business Laptop", 999.99, 15);
    productRepository.save(laptop);

    Product monitor = new Product("27-inch 4K Monitor", 349.99, 20);
    productRepository.save(monitor);

    Product keyboard = new Product("Mechanical Gaming Keyboard", 129.99, 35);
    productRepository.save(keyboard);

    List<Product> existingProducts = (List<Product>) productRepository.findAll();
    boolean productDuplicateFound = false;
    for (Product product : existingProducts) {
        if (product.getName().equals("Mechanical Gaming Keyboard")) {
            productDuplicateFound = true;
            break;
        }
    }

    if (productDuplicateFound) {
        Product keyboardMultiPack = new Product("Gaming Keyboard & Mouse Combo Pack", 199.99, 15);
        productRepository.save(keyboardMultiPack);
    }

    System.out.println("Sample inventory added successfully!");
}

F. Add a "Buy Now" button to your product list. Your "Buy Now" button must meet each of the following parameters:

• The "Buy Now" button must be next to the buttons that update and delete products.

INSERT - MainScreen.html, Line 100-101
<a th:href="@{/buyproduct(productID=${tempProduct.id})}" class="btn btn-success btn-sm"
   onclick="if(!(confirm('Are you sure you want to purchase this product?')))return false">Buy Now</a>

• The button should decrement the inventory of that product by one. It should not affect the inventory of any of the associated parts.

INSERT - AddProductController.java, Line 176-187
@GetMapping("/buyproduct")
public String buyProduct(@RequestParam("productID") int theId, Model theModel) {
    ProductService productService = context.getBean(ProductServiceImpl.class);
    Product product = productService.findById(theId);
    if (product.getInv() > 0) {
        product.setInv(product.getInv() - 1);
        productService.save(product);
        return "confirmationbuysuccess";
    } else {
        return "confirmationbuyfailure";
    }
}

• Display a message that indicates the success or failure of a purchase.

Create confirmationbuysuccess.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta http-equiv="refresh"
          content="3;URL='mainscreen'">
    <meta charset="UTF-8">
    <title>Purchase Successful</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">
<div class="container mt-5">
    <div class="row justify-content-center">
        <div class="col-md-6">
            <div class="alert alert-success text-center" role="alert">
                <h1 class="alert-heading">✅ Purchase Successful!</h1>
                <p>Your product has been purchased successfully. The inventory has been updated.</p>
                <hr>
                <p class="mb-0">You will be redirected to the main screen in 3 seconds...</p>
            </div>
            <div class="text-center">
                <a href="/mainscreen" class="btn btn-primary">← Back to Main Screen</a>
            </div>
        </div>
    </div>
</div>
</body>
</html>

Create confirmationbuyfailure.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta http-equiv="refresh"
          content="3;URL='mainscreen'">
    <meta charset="UTF-8">
    <title>Purchase Failed</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">
<div class="container mt-5">
    <div class="row justify-content-center">
        <div class="col-md-6">
            <div class="alert alert-danger text-center" role="alert">
                <h1 class="alert-heading">❌ Purchase Failed!</h1>
                <p>Sorry, this product is currently out of stock. The purchase could not be completed.</p>
                <hr>
                <p class="mb-0">You will be redirected to the main screen in 3 seconds...</p>
            </div>
            <div class="text-center">
                <a href="/mainscreen" class="btn btn-primary">← Back to Main Screen</a>
            </div>
        </div>
    </div>
</div>
</body>
</html>

G. Modify the parts to track maximum and minimum inventory by doing the following:

• Add additional fields to the part entity for maximum and minimum inventory.

INSERT - Part.java, LINES 29-31 
    @Min(value = 0, message = "Minimum inventory must be >= 0")
    int minimum;
    @Min(value = 0, message = "Maximum inventory must be >= 0") 
    int maximum;

INSERT - Part.java, LINES 45-47
    public Part(String name, double price, int inv, int minimum, int maximum) {
        this.name = name;
        this.price = price;
        this.inv = inv;
        this.minimum = minimum;
        this.maximum = maximum;
    }

INSERT - Part.java, LINES 110-119 
    public int getMinimum() {
        return minimum;
    }

    public void setMinimum(int minimum) {
        this.minimum = minimum;
    }

    public int getMaximum() {
        return maximum;
    }

    public void setMaximum(int maximum) {
        this.maximum = maximum;
    }

• Modify the sample inventory to include the maximum and minimum fields.

INSERT - InhousePart.java, LINES 17-20 
    public InhousePart() {
        this.minimum = 0;
        this.maximum = 100;
    }

INSERT - OutsourcedPart.java, LINES 17-20 
    public OutsourcedPart() {
        this.minimum = 0;
        this.maximum = 100;
    }

MODIFY - BootStrapData.java, LINES 36-80 
    OutsourcedPart processor = new OutsourcedPart();
    processor.setCompanyName("Intel Corp");
    processor.setName("Intel Core i7 Processor");
    processor.setInv(25);
    processor.setPrice(349.99);
    processor.setMinimum(5);
    processor.setMaximum(50);
    outsourcedPartRepository.save(processor);

    OutsourcedPart memory = new OutsourcedPart();
    memory.setCompanyName("Corsair");
    memory.setName("16GB DDR4 RAM");
    memory.setInv(40);
    memory.setPrice(89.99);
    memory.setMinimum(10);
    memory.setMaximum(100);
    outsourcedPartRepository.save(memory);

    OutsourcedPart storage = new OutsourcedPart();
    storage.setCompanyName("Samsung");
    storage.setName("1TB SSD Drive");
    storage.setInv(30);
    storage.setPrice(129.99);
    storage.setMinimum(5);
    storage.setMaximum(75);
    outsourcedPartRepository.save(storage);

    OutsourcedPart graphics = new OutsourcedPart();
    graphics.setCompanyName("NVIDIA");
    graphics.setName("GeForce RTX 3060");
    graphics.setInv(15);
    graphics.setPrice(299.99);
    graphics.setMinimum(3);
    graphics.setMaximum(25);
    outsourcedPartRepository.save(graphics);

    OutsourcedPart motherboard = new OutsourcedPart();
    motherboard.setCompanyName("ASUS");
    motherboard.setName("Gaming Motherboard");
    motherboard.setInv(20);
    motherboard.setPrice(179.99);
    motherboard.setMinimum(5);
    motherboard.setMaximum(40);
    outsourcedPartRepository.save(motherboard);

• Add to the InhousePartForm and OutsourcedPartForm forms additional text inputs for the inventory so the user can set the maximum and minimum values.

INSERT - mainscreen.html, LINES 38-39 
    <th>Minimum</th>
    <th>Maximum</th>

INSERT - mainscreen.html, LINES 48-49
    <td th:text="${tempPart.minimum}">1</td>
    <td th:text="${tempPart.maximum}">1</td>

INSERT - InhousePartForm.html, LINES 22-24 
    <p><input type="text" th:field="*{minimum}" placeholder="Minimum Inventory" class="form-control mb-4 col-4"/></p>
    <p th:if="${#fields.hasErrors('minimum')}" th:errors="*{minimum}">Minimum Error</p>
    
    <p><input type="text" th:field="*{maximum}" placeholder="Maximum Inventory" class="form-control mb-4 col-4"/></p>
    <p th:if="${#fields.hasErrors('maximum')}" th:errors="*{maximum}">Maximum Error</p>

INSERT - OutsourcedPartForm.html, LINES 22-24 
    <p><input type="text" th:field="*{minimum}" placeholder="Minimum Inventory" class="form-control mb-4 col-4"/></p>
    <p th:if="${#fields.hasErrors('minimum')}" th:errors="*{minimum}">Minimum Error</p>
    
    <p><input type="text" th:field="*{maximum}" placeholder="Maximum Inventory" class="form-control mb-4 col-4"/></p>
    <p th:if="${#fields.hasErrors('maximum')}" th:errors="*{maximum}">Maximum Error</p>

• Rename the file the persistent storage is saved to.

CHANGE - application.properties, LINE 6
    FROM: spring.datasource.url=jdbc:h2:file:~/spring-boot-h2-db102
    TO:   spring.datasource.url=jdbc:h2:file:~/spring-boot-h2-db-inventory

• Modify the code to enforce that the inventory is between or at the minimum and maximum value.

INSERT - Part.java, LINES 121-127 
    public void validateLimits() {
        if (this.inv < this.minimum) {
            this.inv = this.minimum;
        } else if (this.inv > this.maximum) {
            this.inv = this.maximum;
        }
    }

MODIFY - InhousePartServiceImpl.java, LINE 54 
    public void save(InhousePart thePart) {
        thePart.validateLimits();
        partRepository.save(thePart);
    }

MODIFY - OutsourcedPartServiceImpl.java, LINE 54 
    public void save(OutsourcedPart thePart) {
        thePart.validateLimits();
        partRepository.save(thePart);
    }

MODIFY - PartServiceImpl.java, LINE 54 
    public void save(Part thePart) {
        thePart.validateLimits();
        partRepository.save(thePart);
    }

H. Add validation for between or at the maximum and minimum fields. The validation must include the following:

• Display error messages for low inventory when adding and updating parts if the inventory is less than the minimum number of parts.

CREATE - ValidInventoryRange.java (new annotation)
    @Constraint(validatedBy = {InventoryRangeValidator.class})
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ValidInventoryRange {
        String message() default "Inventory must be between minimum and maximum values";
        Class<?>[] groups() default {};
        Class<? extends Payload>[] payload() default {};
    }

CREATE - InventoryRangeValidator.java (new validator)
    Validates that inventory is between minimum and maximum values

MODIFY - Part.java, ADD @ValidInventoryRange annotation to class

• Display error messages for low inventory when adding and updating products lowers the part inventory below the minimum.

CREATE - ValidPartInventoryAfterProductUpdate.java (new annotation)
    Validates that part inventory won't go below minimum when product inventory increases

CREATE - PartInventoryAfterProductUpdateValidator.java (new validator)
    Checks if increasing product inventory would cause part inventory to go below minimum

MODIFY - Product.java, ADD @ValidPartInventoryAfterProductUpdate annotation to class

• Display error messages when adding and updating parts if the inventory is greater than the maximum.

MODIFY - InventoryRangeValidator.java
    Include validation for inventory exceeding maximum in the same validator

I. Add at least two unit tests for the maximum and minimum fields to the PartTest class in the test package.

MODIFY - PartTest.java, ADD the following test methods after existing tests:

TEST 1 - testSetAndGetMinimum() (LINES 110-121)
• Tests minimum field setter and getter for both InhousePart and OutsourcedPartVerifies minimum values can be set and retrieved correctlyTests different minimum values for each part type

TEST 2 - testSetAndGetMaximum() (LINES 128-139)
• Tests maximum field setter and getter for both InhousePart and OutsourcedPartVerifies maximum values can be set and retrieved correctlyTests different maximum values for each part type

TEST 3 - testValidateLimitsEnforcesMinimum() (LINES 146-159)
• Tests validateLimits() method when inventory is below minimumVerifies inventory gets adjusted to minimum value for both part typesUses different minimum values to ensure proper functionality

TEST 4 - testValidateLimitsEnforcesMaximum() (LINES 166-179)
• Tests validateLimits() method when inventory is above maximumVerifies inventory gets adjusted to maximum value for both part typesUses different maximum values to ensure proper functionality

TEST 5 - testValidateLimitsWithinBounds() (LINES 186-199)
• Tests validateLimits() method when inventory is within valid rangeVerifies inventory remains unchanged when valid for both part typesEnsures method doesn't modify valid inventory values

TEST 6 - testValidateLimitsAtMinimumBoundary() (LINES 206-219)
• Tests boundary condition when inventory exactly equals minimumVerifies edge case handling for both InhousePart and OutsourcedPartEnsures inventory stays at minimum when already at boundary

TEST 7 - testValidateLimitsAtMaximumBoundary() (LINES 226-239)
• Tests boundary condition when inventory exactly equals maximumVerifies edge case handling for both InhousePart and OutsourcedPartEnsures inventory stays at maximum when already at boundary

TEST 8 - testMinimumCanBeZero() (LINES 246-253)
• Tests that minimum can be set to zero for both part typesVerifies edge case for minimum boundary valueEnsures no artificial lower limits on minimum field

J. Remove the class files for any unused validators in order to clean your code.

NO FILES TO DELETE - All validators are currently in use:

✓ DeletePartValidator.java - USED by @ValidDeletePart annotation on Part.java
✓ EnufPartsValidator.java - USED by @ValidEnufParts annotation on Product.java  
✓ PriceProductValidator.java - USED by @ValidProductPrice annotation on Product.java
✓ InventoryRangeValidator.java - USED by @ValidInventoryRange annotation on Part.java
✓ PartInventoryAfterProductUpdateValidator.java - USED by @ValidPartInventoryAfterProductUpdate annotation on Product.java

✓ ValidDeletePart.java - USED on Part class
✓ ValidEnufParts.java - USED on Product class
✓ ValidProductPrice.java - USED on Product class  
✓ ValidInventoryRange.java - USED on Part class
✓ ValidPartInventoryAfterProductUpdate.java - USED on Product class

CONCLUSION: No unused validator files found. All validators are actively used in the application.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors