Skip to content

Commit

Permalink
142 성별 나이별 검색 필터링 기능 프론트 연동 (#147)
Browse files Browse the repository at this point in the history
* feat: 필터링 기능 프론트 반영

* refactor: 검색바 분리
  • Loading branch information
dongjoo0-0 committed Aug 29, 2023
1 parent 4f7478c commit 9858dfe
Show file tree
Hide file tree
Showing 7 changed files with 208 additions and 121 deletions.
1 change: 1 addition & 0 deletions src/main/resources/static/css/common/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@
@import "./highlight-text.css";
@import "./primary-button.css";
@import "./radio.css";
@import "./search.css";

@import "./utils.css";
55 changes: 55 additions & 0 deletions src/main/resources/static/css/common/search.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
.search-container {
display: flex;
justify-content: center;
align-items: center;
max-width: 500px;
margin: 0 auto;
}

.search-input {
width: 70%;
height: 58px;
padding: 10px;
border: 1px solid #dddddd;
border-radius: 3px;
font-size: 1.5em;
}

.search-button {
width: 15%;
text-align: center;
border-radius: 3px;
background: #2ac1bc;
font-size: 20px;
color: white;
}



select {
background-color: white;
border: thin solid;
border-radius: 4px;
display: inline-block;
font: inherit;
line-height: 1.5em;
padding: 0.5em 3.5em 0.5em 1em;

margin: 0;
box-sizing: border-box;
-webkit-appearance: none;
-moz-appearance: none;
background-image:
linear-gradient(45deg, transparent 50%, gray 50%),
linear-gradient(135deg, gray 50%, transparent 50%),
linear-gradient(to right, #ccc, #ccc);
background-position:
calc(100% - 20px) calc(1em + 2px),
calc(100% - 15px) calc(1em + 2px),
calc(100% - 2.5em) 0.5em;
background-size:
5px 5px,
5px 5px,
1px 1.5em;
background-repeat: no-repeat;
}
11 changes: 8 additions & 3 deletions src/main/resources/static/css/page/product.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
justify-content: center;

flex-wrap: wrap;
gap: 20px;
padding: 50px 100px;
gap: 30px;
padding: 50px 250px;
}

.product-image {
Expand All @@ -17,10 +17,11 @@
}

.product-info__name {
font-size: 12px;
font-size: 1.25em;
}

.product-info__price {
font-size: 1em;
}

.product-detail-container {
Expand Down Expand Up @@ -75,3 +76,7 @@
:disabled {
background: #ccc;
}

.product-item {
width: min-content;
}
47 changes: 19 additions & 28 deletions src/main/resources/static/js/search.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,24 @@
const search = () => {
const keyword = document.getElementById("keyword").value.trim();
const form = document.getElementById('search-form');

if (keyword === "") {
alert("검색어를 입력해주세요.");
return;
}
form.addEventListener('submit', (event) => {
event.preventDefault();

window.location.href = '/search?keyword='.concat(keyword).concat("&sort=id,desc");
}
const searchInput = document.getElementById("searchInput").value;
const sortSelect = document.getElementById("sortSelect");
const selectedSort = sortSelect.options[sortSelect.selectedIndex].value;
let queryParams = `keyword=${searchInput}&sort=${selectedSort}`;

const selectSortKey = (sortKey) => {
const buttons = document.getElementsByName("sort-select-button");
let button;
if (sortKey === "id,desc") {
button = buttons[0];
} else if(sortKey === "price,desc") {
button = buttons[1];
} else if(sortKey === "price,asc") {
button = buttons[2];
} else if(sortKey === "orderCount,desc") {
button = buttons[3];
} else if (sortKey === "rate,desc") {
button = buttons[4];
} else {
console.error("Invalid Sort: ".concat(sortKey));
return;
if (selectedSort === "orderCount,desc" || selectedSort === "rate,desc") {
const gender = document.getElementById("gender").value;
const birthYearRange = document.getElementById("birthYearRange").value;
if (gender && birthYearRange) {
queryParams += `&gender=${gender}&birthYearRange=${birthYearRange}`;
}
else if (gender || birthYearRange) {
alert("성별과 나이대 모두 선택해주세요.");
}
}

button.style.fontWeight = "bold";
button.style.color = "white";
button.style.backgroundColor = "darkslategray";
}
window.location.href = `/search?${queryParams}`;
});

9 changes: 2 additions & 7 deletions src/main/resources/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,9 @@
<body>
<div class="root">
<div th:replace="~{layouts/header :: header}"></div>
<label class="product-search-container">
<input id="keyword" placeholder="상품을 검색하세요" th:type="text">
<input onclick="search()" th:type="submit" value="검색">
</label>
<div th:replace="~{layouts/searchbar :: searchbar}"></div>

<section class="product-container">
<div class="product-list">
</div>
</section>
<div class="pagination"></div>
</div>
Expand Down Expand Up @@ -60,7 +55,7 @@
}

pagination(currentPage, totalPage, size, '.pagination', "");
document.querySelector('.product-list').innerHTML = element;
document.querySelector('.product-container').innerHTML = element;
}).catch((error) => {
console.error(error);
});
Expand Down
118 changes: 118 additions & 0 deletions src/main/resources/templates/layouts/searchbar.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<meta content="IE=edge" http-equiv="X-UA-Compatible"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<link rel="stylesheet" th:href="@{/css/index.css}">
<title>Shopping</title>
</head>
<body>
<section class="search-container" th:fragment="searchbar">
<form class="full-width mt-30" id="search-form">
<div class="full-width flex justify-center">
<label for="searchInput"></label>
<input class="search-input" id="searchInput" placeholder="검색어를 입력하세요" required type="text">
<button class="search-button" id="searchButton" type="submit">검색</button>
</div>

<div class="mt-30 flex justify-around">
<div>
<label for="sortSelect">정렬</label>
<div class="select">
<select id="sortSelect">
<option selected value="id,desc">최신순</option>
<option value="price,desc">가격 높은순</option>
<option value="price,asc">가격 낮은순</option>
<option value="orderCount,desc">주문 많은순</option>
<option value="rate,desc">별점 높은순</option>
</select>
</div>
</div>

<div>
<label for="gender">성별</label>
<div class="select">
<select id="gender" name="gender">
<option selected></option>
<option value="MALE">남자</option>
<option value="FEMALE">여자</option>
</select>
</div>
</div>

<div>
<label for="birthYearRange">나이대</label>
<div class="select">
<select id="birthYearRange" name="birthYearRange">
<option selected></option>
<option value="UNDER_TEENS">10대 이하</option>
<option value="EARLY_TWENTIES">20대 초반</option>
<option value="MID_TWENTIES">20대 중반</option>
<option value="LATE_TWENTIES">20대 후반</option>
<option value="THIRTIES">30대</option>
<option value="OVER_FORTIES">40대 이상</option>
</select>
</div>
</div>

</div>
</form>
<script>
document.addEventListener("DOMContentLoaded", function () {
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.size === 0) {
return;
}

document.getElementById("searchInput").value = urlParams.get("keyword");
document.getElementById("sortSelect").value = urlParams.get("sort");
document.getElementById("gender").value = urlParams.get("gender");
document.getElementById("birthYearRange").value = urlParams.get("birthYearRange");

fetch('/api/v1/products/search?' + urlParams, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
}).then((response) => {
return response.json();
}).then((data) => {
const currentPage = data.currentPage;
const totalPage = data.totalPage;
const size = data.size;

let element = '';
for (let i = 0; i < Math.min(size, data.contents.length); i++) {
let product = data.contents[i];
element += `
<div class="product-item">
<a href="/product/${product.id}">
<img class="w-280 h-280 cover-image" src="assets/${product.imageFileName}"/>
</a>
<div class="flex justify-between w-280 p-5">
<div class="product-info">
<span class="product-info__name">${product.name}</span>
<span class="product-info__price">${product.price}</span>
</div>
<button type="submit" class="product-btn" ${product.stock <= 0 ? 'disabled' : ''} onclick="addCartItem(${product.id})">
<img src="assets/svgs/cart.svg" alt="장바구니"/>
</button>
</div>
</div>
`;
}

pagination(currentPage, totalPage, size, '.pagination', urlParams);
document.querySelector('.product-container').innerHTML = element;
}).catch((error) => {
console.error(error);
});
});
</script>
<script th:src="@{/js/pagination.js}"></script>
<script th:src="@{/js/cart.js}"></script>
<script th:src="@{/js/search.js}"></script>
</section>
</body>
</html>
88 changes: 5 additions & 83 deletions src/main/resources/templates/search.html
Original file line number Diff line number Diff line change
@@ -1,98 +1,20 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns="http://www.w3.org/1999/html">
<html lang="en" xmlns="http://www.w3.org/1999/html" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta content="IE=edge" http-equiv="X-UA-Compatible"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<link rel="stylesheet" th:href="@{/css/index.css}">
<title>Shopping</title>
</head>
<body>
<div class="root">
<div th:replace="~{layouts/header :: header}"></div>
<label class="product-search-container">
<input th:type="text" placeholder="상품을 검색하세요" id="keyword">
<input th:type="submit" value="검색" onclick="search()">
</label>
<div class="product-search-result">
<span th:text="${param.keyword}" style="font-weight: bold; color: darkslategray"></span>
에 대한 검색 결과입니다
</div>
<div class="product-sort-container">
<button type="button"
th:onclick="|location.href='@{/search(keyword=${param.keyword}, sort=${value})}'|"
th:with="value='id,desc'"
name="sort-select-button">최신순</button>
<button type="button"
th:onclick="|location.href='@{/search(keyword=${param.keyword}, sort=${value})}'|"
th:with="value='price,desc'"
name="sort-select-button">가격 높은순</button>
<button type="button"
th:onclick="|location.href='@{/search(keyword=${param.keyword}, sort=${value})}'|"
th:with="value='price,asc'"
name="sort-select-button">가격 낮은순</button>
<button type="button"
th:onclick="|location.href='@{/search(keyword=${param.keyword}, sort=${value})}'|"
th:with="value='orderCount,desc'"
name="sort-select-button">주문 많은순</button>
<button type="button"
th:onclick="|location.href='@{/search(keyword=${param.keyword}, sort=${value})}'|"
th:with="value='rate,desc'"
name="sort-select-button">별점 높은순</button>
</div>
<div th:replace="~{layouts/searchbar :: searchbar}"></div>

<section class="product-container">
<div class="product-list">
</div>
</section>
<div class="pagination"></div>
</div>
</body>
<script>
document.addEventListener("DOMContentLoaded", function () {
const urlParams = new URLSearchParams(window.location.search);

fetch('/api/v1/products/search?' + urlParams, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
}).then((response) => {
return response.json();
}).then((data) => {
const currentPage = data.currentPage;
const totalPage = data.totalPage;
const size = data.size;

let element = '';
for (let i = 0; i < Math.min(size, data.contents.length); i++) {
let product = data.contents[i];
element += `
<div>
<a href="/product/${product.id}">
<img class="w-280 h-280 cover-image" src="assets/${product.imageFileName}"/>
</a>
<div class="flex justify-between w-280 p-5">
<div class="product-info">
<span class="product-info__name">${product.name}</span>
<span class="product-info__price">${product.price}</span>
</div>
<button type="submit" class="product-btn" ${product.stock <= 0 ? 'disabled' : ''} onclick="addCartItem(${product.id})">
<img src="assets/svgs/cart.svg" alt="장바구니"/>
</button>
</div>
</div>
`;
}

selectSortKey(urlParams.get("sort"));
pagination(currentPage, totalPage, size, '.pagination', urlParams);
document.querySelector('.product-list').innerHTML = element;
}).catch((error) => {
console.error(error);
});
});
</script>
<script th:src="@{/js/pagination.js}"></script>
<script th:src="@{/js/cart.js}"></script>
<script th:src="@{/js/search.js}"></script>
</html>

0 comments on commit 9858dfe

Please sign in to comment.