diff --git a/src/main/java/com/ssginc/nojam/stock/controller/StockController.java b/src/main/java/com/ssginc/nojam/stock/controller/StockController.java new file mode 100644 index 0000000..f2fe03b --- /dev/null +++ b/src/main/java/com/ssginc/nojam/stock/controller/StockController.java @@ -0,0 +1,87 @@ +package com.ssginc.nojam.stock.controller; + +import com.ssginc.nojam.stock.dto.HeadStockViewDTO; +import com.ssginc.nojam.stock.mapper.StockMapper; +import com.ssginc.nojam.stock.service.StockService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; + +import java.util.List; + +/** + * @author Queue-ri + */ + +@Controller +@RequiredArgsConstructor +@RequestMapping("stock") +public class StockController { + private final StockMapper stockMapper; + + private final StockService stockService; + + @GetMapping("/head/view") + public String viewHeadStockDefault(RedirectAttributes redirectAttributes) { + redirectAttributes.addAttribute("pdx", 1); + return "redirect:/stock/head/view/{pdx}"; + } + + @GetMapping("/head/view/{pdx}") + public String viewHeadStock(@PathVariable("pdx") int pdx, Model model) { + int pageIdx = (pdx-1) * 50; + + int totalRow = stockMapper.countAllHeadStock(); // 총 totalRow개의 검색 결과 + int pageBlockSize = 5; // 1 2 3 4 5 + int pageNavSize = (int)Math.ceil((double)totalRow / pageBlockSize); // << >> 를 몇 번 할 수 있는지 + + List<HeadStockViewDTO> hsList = stockMapper.get50HeadStock(pageIdx); + + model.addAttribute("hsList", hsList); + model.addAttribute("totalRow", totalRow); + model.addAttribute("pageBlockSize", pageBlockSize); + model.addAttribute("pageNavSize", pageNavSize); + + return "stock/head-view"; + } + + @GetMapping("/head/view/query") + public String viewHeadStockQueryDefault(RedirectAttributes redirectAttributes) { + redirectAttributes.addAttribute("pdx", 1); + return "redirect:/stock/head/view/{pdx}"; + } + + @GetMapping("/head/view/query/{pdx}") + public String viewHeadStockQuery(@PathVariable("pdx") int pdx, @RequestParam("category") String category, @RequestParam("value") String value, Model model) { + int pageIdx = (pdx-1) * 50; + + List<HeadStockViewDTO> hsList = null; + int totalRow = -1; + if (category.isEmpty()) { + hsList = stockMapper.get50HeadStock(pageIdx); + totalRow = stockMapper.countAllHeadStock(); // 총 totalRow개의 검색 결과 + } + else { + hsList = stockMapper.get50FilteredHeadStock(category, value, pageIdx); + totalRow = stockMapper.countFilteredHeadStock(category, value); // 총 totalRow개의 검색 결과 + } + int pageBlockSize = 5; // 1 2 3 4 5 + int pageNavSize = (int)Math.ceil((double)totalRow / pageBlockSize); // << >> 를 몇 번 할 수 있는지 + + model.addAttribute("hsList", hsList); + + model.addAttribute("totalRow", totalRow); + model.addAttribute("pageBlockSize", pageBlockSize); + model.addAttribute("pageNavSize", pageNavSize); + + model.addAttribute("category", category); + model.addAttribute("value", value); + + return "stock/head-view-query"; + } +} diff --git a/src/main/java/com/ssginc/nojam/stock/dto/HeadStockViewDTO.java b/src/main/java/com/ssginc/nojam/stock/dto/HeadStockViewDTO.java new file mode 100644 index 0000000..b2d2fe4 --- /dev/null +++ b/src/main/java/com/ssginc/nojam/stock/dto/HeadStockViewDTO.java @@ -0,0 +1,28 @@ +package com.ssginc.nojam.stock.dto; + +import com.ssginc.nojam.crawl.vo.ItemVO; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +/** + * @author Queue-ri + */ + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class HeadStockViewDTO { + private String stockId; + private Long itemId; + private String name; + private String category1; + private String category2; + private int price; + private Integer stock; + private LocalDateTime lastModifiedAt; +} diff --git a/src/main/java/com/ssginc/nojam/stock/mapper/StockMapper.java b/src/main/java/com/ssginc/nojam/stock/mapper/StockMapper.java index 7ad6f23..8a3ca52 100644 --- a/src/main/java/com/ssginc/nojam/stock/mapper/StockMapper.java +++ b/src/main/java/com/ssginc/nojam/stock/mapper/StockMapper.java @@ -1,9 +1,12 @@ package com.ssginc.nojam.stock.mapper; import com.ssginc.nojam.crawl.vo.ItemVO; +import com.ssginc.nojam.stock.dto.HeadStockViewDTO; import com.ssginc.nojam.stock.vo.BranchStockVO; import com.ssginc.nojam.stock.vo.HeadStockVO; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; import java.util.List; @@ -17,4 +20,8 @@ public interface StockMapper { List<ItemVO> getAllItem(); int insertHeadStockList(List<HeadStockVO> headStockList); int insertBranchStockList(List<BranchStockVO> branchStockList); + List<HeadStockViewDTO> get50HeadStock(int startIdx); + int countAllHeadStock(); + int countFilteredHeadStock(@Param("category") String category, @Param("value") String value); + List<HeadStockViewDTO> get50FilteredHeadStock(@Param("category") String category, @Param("value") String value, @Param("startIdx") int startIdx); } diff --git a/src/main/java/com/ssginc/nojam/stock/vo/HeadStockVO.java b/src/main/java/com/ssginc/nojam/stock/vo/HeadStockVO.java index 18387e3..8439c3a 100644 --- a/src/main/java/com/ssginc/nojam/stock/vo/HeadStockVO.java +++ b/src/main/java/com/ssginc/nojam/stock/vo/HeadStockVO.java @@ -1,5 +1,6 @@ package com.ssginc.nojam.stock.vo; +import com.ssginc.nojam.crawl.vo.ItemVO; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; diff --git a/src/main/resources/mapper/stockMapper.xml b/src/main/resources/mapper/stockMapper.xml index bd17b3b..71f9cd8 100644 --- a/src/main/resources/mapper/stockMapper.xml +++ b/src/main/resources/mapper/stockMapper.xml @@ -31,4 +31,43 @@ </foreach> </insert> + <!-- 본사 재고 50개 paging 조회 --> + <select id="get50HeadStock" + resultType="com.ssginc.nojam.stock.dto.HeadStockViewDTO" + parameterType="int"> + SELECT * FROM ( + SELECT hs.stock_id, hs.item_id, it.name, it.category1, it.category2, it.price, hs.stock, hs.last_modified_at FROM head_stock hs + JOIN item it USING(item_id) + ) res + ORDER BY stock_id ASC + LIMIT ${startIdx}, 50 + </select> + + <!-- 본사 재고 개수 조회 --> + <select id="countAllHeadStock" + resultType="int"> + SELECT COUNT(*) + FROM head_stock + </select> + + <!-- 본사 재고 필터링 후 개수 조회 --> + <select id="countFilteredHeadStock" + resultType="int"> + SELECT COUNT(*) + FROM head_stock + JOIN item USING(item_id) + WHERE ${category} LIKE '%${value}%' + </select> + + <!-- 본사 재고 필터링 후 조회 --> + <select id="get50FilteredHeadStock" + resultType="com.ssginc.nojam.stock.dto.HeadStockViewDTO"> + SELECT * + FROM head_stock + JOIN item USING(item_id) + WHERE ${category} LIKE '%${value}%' + ORDER BY stock_id ASC + LIMIT ${startIdx}, 50 + </select> + </mapper> \ No newline at end of file diff --git a/src/main/resources/static/img/dropdown-arrow.svg b/src/main/resources/static/img/dropdown-arrow.svg new file mode 100644 index 0000000..4ab099f --- /dev/null +++ b/src/main/resources/static/img/dropdown-arrow.svg @@ -0,0 +1,3 @@ +<svg width="12" height="7" viewBox="0 0 12 7" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M0 0L6 7L12 0H0Z" fill="#A09F9F"/> +</svg> diff --git a/src/main/resources/static/img/search-icon.svg b/src/main/resources/static/img/search-icon.svg new file mode 100644 index 0000000..665730b --- /dev/null +++ b/src/main/resources/static/img/search-icon.svg @@ -0,0 +1,3 @@ +<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M16 16L12.4584 12.4521M14.4211 7.71053C14.4211 9.49027 13.7141 11.1971 12.4556 12.4556C11.1971 13.7141 9.49027 14.4211 7.71053 14.4211C5.93078 14.4211 4.22394 13.7141 2.96547 12.4556C1.707 11.1971 1 9.49027 1 7.71053C1 5.93078 1.707 4.22394 2.96547 2.96547C4.22394 1.707 5.93078 1 7.71053 1C9.49027 1 11.1971 1.707 12.4556 2.96547C13.7141 4.22394 14.4211 5.93078 14.4211 7.71053Z" stroke="#161616" stroke-width="2" stroke-linecap="round"/> +</svg> diff --git a/src/main/resources/templates/stock/head-view-query.html b/src/main/resources/templates/stock/head-view-query.html new file mode 100644 index 0000000..3fcc710 --- /dev/null +++ b/src/main/resources/templates/stock/head-view-query.html @@ -0,0 +1,505 @@ +<!DOCTYPE html> +<html lang="en" + xmlns:th="http://www.thymeleaf.org" + xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"> +<head> + <link href="https://cdn.jsdelivr.net/gh/sun-typeface/SUIT@2/fonts/static/woff2/SUIT.css" rel="stylesheet"> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>재고 관리 - 재고 조회</title> + <!-- Bootstrap CSS --> + <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"> + <style> + @import url('https://fonts.googleapis.com/css2?family=Lufga:wght@400;600&family=SUIT:wght@400;600&display=swap'); + + * { + margin: 0; + padding: 0; + box-sizing: border-box; + } + body { + font-family: 'SUIT', Arial, sans-serif; + display: flex; + height: 100vh; + } + .sidebar { + width: 250px; + background-color: #161616; + color: #fff; + display: flex; + flex-direction: column; + justify-content: space-between; + padding: 20px 10px; + } + .logo { + display: flex; + align-items: center; + justify-content: center; + gap: 10px; + margin-bottom: 30px; + } + .logo img { + width: 40px; + height: 40px; + } + .logo span { + font-family: 'Lufga', Arial, sans-serif; + font-size: 24px; + font-weight: 600; + } + .logo .highlight { + color: #FFD028; + } + .menu { + flex-grow: 1; + } + .menu-item { + padding: 15px 20px; + cursor: pointer; + font-family: 'SUIT', Arial, sans-serif; + font-weight: 600; + font-size: 16px; + transition: background-color 0.3s ease; + margin-bottom: 10px; + } + .menu-item:hover, + .menu-item.active { + background-color: #3A3A3A; + border-radius: 5px; + } + .submenu { + margin-left: 15px; + display: none; + } + .submenu-item { + padding: 10px 20px; + cursor: pointer; + font-family: 'SUIT', Arial, sans-serif; + font-size: 14px; + transition: background-color 0.3s ease; + margin-bottom: 8px; + } + .submenu-item:hover { + background-color: #484848; + border-radius: 5px; + } + .menu-item.active + .submenu { + display: block; + } + .user-info-button { + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px 15px; + background-color: #222222; + color: #fff; + border: 1px solid #303030; + border-radius: 8px; + cursor: pointer; + transition: background-color 0.3s ease; + } + .user-info-button:hover { + background-color: #303030; + } + .user-info { + display: flex; + align-items: center; + gap: 10px; + } + .user-info img { + width: 40px; + height: 40px; + border-radius: 50%; + } + .logout-icon { + display: flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + border-radius: 50%; + transition: background-color 0.3s ease; + } + .logout-icon:hover { + background-color: #303030; + } + .logout-icon img { + width: 20px; + height: 20px; + } + .content { + flex-grow: 1; + background-color: #F5F6FA; + } + .wrapper { + display: flex; + flex-direction: column; + width: 100%; + height: 100%; + padding: 36px 32px 36px 32px; + } + .title-area { + margin-bottom: 38px; + } + .main-title { + font-family: 'SUIT', sans-serif; + font-weight: 700; + font-size: 32px; + letter-spacing: -2px; + color: #0D183F; + margin-right: 32px; + } + .sub-title { + font-family: 'SUIT', sans-serif; + font-weight: 500; + font-size: 20px; + letter-spacing: -2px; + color: #0D183F; + } + .mid-area { + display: flex; + margin-bottom: 28px; + } + .search-area { + display: flex; + } + .category-dropdown { + display: flex; + width: 296px; + height: 60px; + font-size: 20px; + border-radius: 8px; + padding: 17px 32px; + color: #A09F9F; + background-color: #FFFFFF; + margin-right: 16px; + letter-spacing: -2px; + cursor: pointer; + } + /*.category-dropdown:after {*/ + /* content: '';*/ + /* display: block;*/ + /* width: 2px;*/ + /* height: 100%;*/ + /* position: absolute;*/ + /* top: 0;*/ + /* right: 35px;*/ + /* background: #ffd5d5;*/ + /*}*/ + .category-dropdown .optionList { + position: absolute; + top: 190px; + left: 282px; + width: 296px; + background: #FFFFFF; + color: #161616; + list-style-type: none; + padding: 0; + border-radius: 6px; + overflow: hidden; + max-height: 0; + transition: .3s ease-in; + } + .category-dropdown.active .optionList { + max-height: 500px; + } + + .category-dropdown .optionItem { + border-bottom: 1px dashed #A09F9F; + padding: 16px 32px; + transition: .1s; + } + .category-dropdown .optionItem:hover { + background: #FFFFFF; + } + .category-dropdown .optionItem:last-child { + border-bottom: 0 none; + } + .category-dropdown span { + position: relative; + margin-left: 0; + } + .category-dropdown img { + width: 12px; + height: 7px; + margin: auto 0 auto auto; + } + .input-box { + margin-right: 16px; + } + input { + width: 296px; + height: 60px; + font-size: 20px; + border: 0; + outline: none; + border-radius: 8px; + padding: 17px 32px; + background-color: #FFFFFF; + } + .search-button { + display: flex; + width: 108px; + height: 60px; + border-radius: 8px; + background: #FFD028; + padding: 17px 24px; + cursor: pointer; + } + .search-button img { + width: 15px; + height: 15px; + margin: auto 10px auto 0; + } + .search-button span { + margin: auto auto; + color: #161616; + font-size: 20px; + } + .info-area { + display: flex; + margin-left: auto; + margin-right: 0; + } + .info-area span { + margin-top: auto; + } + .table-area { + flex: 1; + background: #FFFFFF; + border-radius: 8px; + padding: 32px 28px; + } + table { + margin: auto; + } + table tbody { + display: block; + max-height: 400px; + overflow-y: scroll; + } + table thead, table tbody tr { + display: table; + width: 100%; + } + table thead th { + width:12%; + } + table tbody td { + width:12%; + } + .paging-wrapper { + display: flex; + } + .pagination { + margin: auto; + } + </style> +</head> +<body> +<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script> +<div class="sidebar"> + <div> + <div class="logo"> + <img src="/img/NobrandIcon.PNG" alt="Logo"> + <span><span class="highlight">N</span>oJam</span> + </div> + <div class="menu"> + <div class="menu-item active" onclick="toggleSubmenu(this, 'dashboard-submenu')">Dashboard</div> + <div class="menu-item" onclick="toggleSubmenu(this, 'inventory-submenu')">재고관리</div> + <div class="submenu" id="inventory-submenu"> + <div class="submenu-item">재고 전체 조회</div> + <div class="submenu-item">재고 필터링 조회</div> + <div class="submenu-item">재고 조정</div> + <div class="submenu-item">부족 재고 조회</div> + </div> + <div class="menu-item" onclick="toggleSubmenu(this, 'shipping-submenu')">출고관리</div> + <div class="submenu" id="shipping-submenu"> + <div class="submenu-item">출고 전체 조회</div> + <div class="submenu-item">출고 필터링 조회</div> + <div class="submenu-item">출고 등록</div> + <div class="submenu-item">출고 완료 처리</div> + </div> + <div class="menu-item" onclick="toggleSubmenu(this, 'order-submenu')">발주관리</div> + <div class="submenu" id="order-submenu"> + <div class="submenu-item">발주 내역 조회</div> + <div class="submenu-item">발주 등록</div> + <div class="submenu-item">발주 취소</div> + </div> + <div class="menu-item" onclick="toggleSubmenu(this, 'system-submenu')">시스템</div> + <div class="submenu" id="system-submenu"> + <div class="submenu-item">지점 정보 조회</div> + <div class="submenu-item">지점 정보 저장</div> + <div class="submenu-item">지점 회원 관리</div> + </div> + <div class="menu-item" onclick="location.href='user-info.html'">사용이력관리</div> + </div> + </div> + <div> + <div class="user-info-button"> + <div class="user-info"> + <img src="defaultUserIcon.png" alt="User"> + <div> + <div>Queue-ri</div> + <div>본사</div> + </div> + </div> + <div class="logout-icon" onclick="logout(event)"> + <img src="logoutIcon.png" alt="Logout"> + </div> + </div> + </div> +</div> +<div class="content"> + <div class="wrapper"> + <div class="title-area"> + <span class="main-title">상품 재고 관리</span> + <span class="sub-title">본사 창고</span> + </div> + <div class="mid-area"> + <div class="search-area"> + <div class="category-dropdown"> + <span>카테고리 선택</span> + <img src="/img/dropdown-arrow.svg"> + <ul class="optionList"> + <li class="optionItem">상품ID</li> + <li class="optionItem">상품명</li> + <li class="optionItem">대분류</li> + <li class="optionItem">소분류</li> + </ul> + </div> + <div class="input-box"><input type="text"></div> + <div class="search-button"><img src="/img/search-icon.svg"><span>검색</span></div> + </div> + <div class="info-area"> + <span>총 <b>[[${totalRow}]]</b>건이 검색되었습니다.</span> + </div> + </div> + <div class="table-area"> + <div class="table-wrapper"> + <table class="table"> + <thead> + <tr> + <th scope="col">재고ID</th> + <th scope="col">상품ID</th> + <th scope="col">상품명</th> + <th scope="col">대분류</th> + <th scope="col">소분류</th> + <th scope="col">가격</th> + <th scope="col">재고</th> + <th scope="col">최종수정일</th> + </tr> + </thead> + <tbody> + <tr th:each="hs : ${hsList}"> + <td th:text="${hs.stockId}"></td> + <td th:text="${hs.itemId}"></td> + <td th:text="${hs.name}"></td> + <td th:text="${hs.category1}"></td> + <td th:text="${hs.category2}"></td> + <td th:text="${hs.price}"></td> + <td th:text="${hs.stock}"></td> + <td th:text="${hs.lastModifiedAt}"></td> + </tr> + + </tbody> + </table> + </div> + + <div class="paging-wrapper"> + <ul class="pagination"> + <li class="page-item"> + <a class="page-link" href="#" aria-label="Previous"> + <span aria-hidden="true">«</span> + </a> + </li> + <th:block th:each="num : ${#numbers.sequence(1,5)}"> + <li class="page-item"><a class="page-link" th:href="@{/stock/head/view/query/{num}(num=${num}, category=${category}, value=${value})}" th:text="${num}"></a></li> +<!-- <li class="page-item"><a class="page-link" th:href="@{/stock/head/view/query/2(category=${category}, value=${value})}">2</a></li>--> +<!-- <li class="page-item"><a class="page-link" th:href="@{/stock/head/view/query/3(category=${category}, value=${value})}">3</a></li>--> +<!-- <li class="page-item"><a class="page-link" th:href="@{/stock/head/view/query/4(category=${category}, value=${value})}">4</a></li>--> +<!-- <li class="page-item"><a class="page-link" th:href="@{/stock/head/view/query/5(category=${category}, value=${value})}">5</a></li>--> + </th:block> + <li class="page-item"> + <a class="page-link" href="#" aria-label="Next"> + <span aria-hidden="true">»</span> + </a> + </li> + </ul> + </div> + + </div> + </div> +</div> + +<script> + function toggleSubmenu(element, submenuId) { + const submenus = document.querySelectorAll('.submenu'); + submenus.forEach(submenu => submenu.style.display = 'none'); + + const menuItems = document.querySelectorAll('.menu-item'); + menuItems.forEach(item => item.classList.remove('active')); + + element.classList.add('active'); + const submenu = document.getElementById(submenuId); + if (submenu) submenu.style.display = 'block'; + } + + function logout(event) { + event.stopPropagation(); + alert('로그아웃 되었습니다.'); + location.href = 'login.html'; + } + + /* query selector 목록 */ + const dropdown = document.querySelector('.category-dropdown'); + const options = document.querySelectorAll('.optionItem'); + const search_button = document.querySelector('.search-button'); + const text_input = document.querySelector('input'); + + // 클릭한 옵션의 텍스트를 라벨 안에 넣음 + const handleSelect = (item) => { + dropdown.firstElementChild.innerHTML = item.textContent; + dropdown.parentNode.classList.remove('active'); + + console.log(dropdown.firstElementChild.innerHTML) + console.log(item.textContent) + } + // 옵션 클릭시 클릭한 옵션을 넘김 + options.forEach(option => { + option.addEventListener('click', () => handleSelect(option)) + }) + + // dropdown 클릭시 옵션 목록이 열림/닫힘 + dropdown.addEventListener('click', () => { + if (dropdown.classList.contains('active')) { + dropdown.classList.remove('active'); + } else { + dropdown.classList.add('active'); + } + }) + + search_button.addEventListener('click', () => { + const selected = dropdown.firstElementChild.innerHTML; + const value = text_input.value; + let category = ''; + console.log(selected); + console.log(value); + + if (selected == '상품ID') category = 'item_id'; + else if (selected == '상품명') category = 'name'; + else if (selected == '대분류') category = 'category1'; + else if (selected == '소분류') category = 'category2'; + else if (selected == '카테고리 선택') category = ''; + else console.error('응 아니야'); + + if (category == '' && value != '') { + alert("카테고리를 선택해주세요."); + return false; + } + + location.href = "?category=" + category + "&value=" + value; + }) +</script> +</body> +</html> \ No newline at end of file diff --git a/src/main/resources/templates/stock/head-view.html b/src/main/resources/templates/stock/head-view.html new file mode 100644 index 0000000..58297d1 --- /dev/null +++ b/src/main/resources/templates/stock/head-view.html @@ -0,0 +1,339 @@ +<!DOCTYPE html> +<html lang="en" + xmlns:th="http://www.thymeleaf.org" + xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"> +<head> + <link href="https://cdn.jsdelivr.net/gh/sun-typeface/SUIT@2/fonts/static/woff2/SUIT.css" rel="stylesheet"> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>재고 관리 - 재고 조회</title> + <!-- Bootstrap CSS --> + <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"> + <style> + @import url('https://fonts.googleapis.com/css2?family=Lufga:wght@400;600&family=SUIT:wght@400;600&display=swap'); + + * { + margin: 0; + padding: 0; + box-sizing: border-box; + } + body { + font-family: 'SUIT', Arial, sans-serif; + display: flex; + height: 100vh; + } + .sidebar { + width: 250px; + background-color: #161616; + color: #fff; + display: flex; + flex-direction: column; + justify-content: space-between; + padding: 20px 10px; + } + .logo { + display: flex; + align-items: center; + justify-content: center; + gap: 10px; + margin-bottom: 30px; + } + .logo img { + width: 40px; + height: 40px; + } + .logo span { + font-family: 'Lufga', Arial, sans-serif; + font-size: 24px; + font-weight: 600; + } + .logo .highlight { + color: #FFD028; + } + .menu { + flex-grow: 1; + } + .menu-item { + padding: 15px 20px; + cursor: pointer; + font-family: 'SUIT', Arial, sans-serif; + font-weight: 600; + font-size: 16px; + transition: background-color 0.3s ease; + margin-bottom: 10px; + } + .menu-item:hover, + .menu-item.active { + background-color: #3A3A3A; + border-radius: 5px; + } + .submenu { + margin-left: 15px; + display: none; + } + .submenu-item { + padding: 10px 20px; + cursor: pointer; + font-family: 'SUIT', Arial, sans-serif; + font-size: 14px; + transition: background-color 0.3s ease; + margin-bottom: 8px; + } + .submenu-item:hover { + background-color: #484848; + border-radius: 5px; + } + .menu-item.active + .submenu { + display: block; + } + .user-info-button { + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px 15px; + background-color: #222222; + color: #fff; + border: 1px solid #303030; + border-radius: 8px; + cursor: pointer; + transition: background-color 0.3s ease; + } + .user-info-button:hover { + background-color: #303030; + } + .user-info { + display: flex; + align-items: center; + gap: 10px; + } + .user-info img { + width: 40px; + height: 40px; + border-radius: 50%; + } + .logout-icon { + display: flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + border-radius: 50%; + transition: background-color 0.3s ease; + } + .logout-icon:hover { + background-color: #303030; + } + .logout-icon img { + width: 20px; + height: 20px; + } + .content { + flex-grow: 1; + background-color: #F5F6FA; + } + .wrapper { + display: flex; + flex-direction: column; + width: 100%; + height: 100%; + padding: 36px 32px 36px 32px; + } + .title-area { + margin-bottom: 38px; + } + .main-title { + font-family: 'SUIT', sans-serif; + font-weight: 700; + font-size: 32px; + letter-spacing: -2px; + color: #0D183F; + margin-right: 32px; + } + .sub-title { + font-family: 'SUIT', sans-serif; + font-weight: 500; + font-size: 20px; + letter-spacing: -2px; + color: #0D183F; + } + .mid-area { + display: flex; + margin-bottom: 28px; + } + .info-area { + display: flex; + margin-left: auto; + margin-right: 0; + } + .info-area span { + margin-top: auto; + } + .table-area { + flex: 1; + background: #FFFFFF; + border-radius: 8px; + padding: 32px 28px; + } + table tbody { + display: block; + max-height: 55vh; + overflow-y: scroll; + } + table thead, table tbody tr { + display: table; + width: 100%; + } + .paging-wrapper { + display: flex; + } + .pagination { + margin: auto; + } + </style> +</head> +<body> +<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script> +<div class="sidebar"> + <div> + <div class="logo"> + <img src="/img/NobrandIcon.PNG" alt="Logo"> + <span><span class="highlight">N</span>oJam</span> + </div> + <div class="menu"> + <div class="menu-item active" onclick="toggleSubmenu(this, 'dashboard-submenu')">Dashboard</div> + <div class="menu-item" onclick="toggleSubmenu(this, 'inventory-submenu')">재고관리</div> + <div class="submenu" id="inventory-submenu"> + <div class="submenu-item">재고 전체 조회</div> + <div class="submenu-item">재고 필터링 조회</div> + <div class="submenu-item">재고 조정</div> + <div class="submenu-item">부족 재고 조회</div> + </div> + <div class="menu-item" onclick="toggleSubmenu(this, 'shipping-submenu')">출고관리</div> + <div class="submenu" id="shipping-submenu"> + <div class="submenu-item">출고 전체 조회</div> + <div class="submenu-item">출고 필터링 조회</div> + <div class="submenu-item">출고 등록</div> + <div class="submenu-item">출고 완료 처리</div> + </div> + <div class="menu-item" onclick="toggleSubmenu(this, 'order-submenu')">발주관리</div> + <div class="submenu" id="order-submenu"> + <div class="submenu-item">발주 내역 조회</div> + <div class="submenu-item">발주 등록</div> + <div class="submenu-item">발주 취소</div> + </div> + <div class="menu-item" onclick="toggleSubmenu(this, 'system-submenu')">시스템</div> + <div class="submenu" id="system-submenu"> + <div class="submenu-item">지점 정보 조회</div> + <div class="submenu-item">지점 정보 저장</div> + <div class="submenu-item">지점 회원 관리</div> + </div> + <div class="menu-item" onclick="location.href='user-info.html'">사용이력관리</div> + </div> + </div> + <div> + <div class="user-info-button"> + <div class="user-info"> + <img src="defaultUserIcon.png" alt="User"> + <div> + <div>Queue-ri</div> + <div>본사</div> + </div> + </div> + <div class="logout-icon" onclick="logout(event)"> + <img src="logoutIcon.png" alt="Logout"> + </div> + </div> + </div> +</div> +<div class="content"> + <div class="wrapper"> + <div class="title-area"> + <span class="main-title">상품 재고 관리</span> + <span class="sub-title">본사 창고</span> + </div> + <div class="mid-area"> + + <div class="info-area"> + <span>총 <b>[[${totalRow}]]</b>건이 검색되었습니다.</span> + </div> + </div> + <div class="table-area"> + <div class="table-wrapper"> + <table class="table"> + <thead> + <tr> + <th scope="col">재고ID</th> + <th scope="col">상품ID</th> + <th scope="col">상품명</th> + <th scope="col">대분류</th> + <th scope="col">소분류</th> + <th scope="col">가격</th> + <th scope="col">재고</th> + <th scope="col">최종수정일</th> + </tr> + </thead> + <tbody> + <tr th:each="hs : ${hsList}"> + <td th:text="${hs.stockId}"></td> + <td th:text="${hs.itemId}"></td> + <td th:text="${hs.name}"></td> + <td th:text="${hs.category1}"></td> + <td th:text="${hs.category2}"></td> + <td th:text="${hs.price}"></td> + <td th:text="${hs.stock}"></td> + <td th:text="${hs.lastModifiedAt}"></td> + </tr> + + </tbody> + </table> + </div> + + <div class="paging-wrapper"> + <ul class="pagination"> + <li class="page-item"> + <a class="page-link" href="#" aria-label="Previous"> + <span aria-hidden="true">«</span> + </a> + </li> + <th:block th:each="num : ${#numbers.sequence(1,5)}"> + <li class="page-item"><a class="page-link" th:href="@{/stock/head/view/{num}(num=${num})}" th:text="${num}"></a></li> + </th:block> +<!-- <li class="page-item"><a class="page-link" href="/stock/head/view/1">1</a></li>--> +<!-- <li class="page-item"><a class="page-link" href="/stock/head/view/2">2</a></li>--> +<!-- <li class="page-item"><a class="page-link" href="/stock/head/view/3">3</a></li>--> +<!-- <li class="page-item"><a class="page-link" href="/stock/head/view/4">4</a></li>--> +<!-- <li class="page-item"><a class="page-link" href="/stock/head/view/5">5</a></li>--> + <li class="page-item"> + <a class="page-link" href="#" aria-label="Next"> + <span aria-hidden="true">»</span> + </a> + </li> + </ul> + </div> + + </div> + </div> +</div> + +<script> + function toggleSubmenu(element, submenuId) { + const submenus = document.querySelectorAll('.submenu'); + submenus.forEach(submenu => submenu.style.display = 'none'); + + const menuItems = document.querySelectorAll('.menu-item'); + menuItems.forEach(item => item.classList.remove('active')); + + element.classList.add('active'); + const submenu = document.getElementById(submenuId); + if (submenu) submenu.style.display = 'block'; + } + + function logout(event) { + event.stopPropagation(); + alert('로그아웃 되었습니다.'); + location.href = 'login.html'; + } + +</script> +</body> +</html> \ No newline at end of file