Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: Deploy To EC2
on:
push:
branches:
- integrate-fe-be
- feat/filter-vacancy

jobs:
deploy:
Expand Down
16 changes: 12 additions & 4 deletions src/main/resources/static/css/date.css
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@
color: black;
}


/* 토요일: 파란색 */
.date-buttons button.day.saturday .day-date,
.date-buttons button.day.saturday .day-week {
Expand All @@ -83,10 +82,19 @@
color: #ff0000;
}

/* 선택된 날짜 버튼 스타일 */
/* 선택된 날짜 버튼 스타일 (파란색 배경 + 흰색 텍스트)*/
.date-buttons button.day.selected {
background-color: #007bff; /* 파란색 배경 */
color: white; /* 텍스트 흰색 */
background-color: #007bff;
}
.date-buttons button.day.selected .day-date,
.date-buttons button.day.selected .day-week {
color: white;
}
.date-buttons button.day.saturday.selected .day-date,
.date-buttons button.day.saturday.selected .day-week,
.date-buttons button.day.sunday.selected .day-date,
.date-buttons button.day.sunday.selected .day-week {
color: white;
}

/* 좌우 화살표 버튼 */
Expand Down
30 changes: 30 additions & 0 deletions src/main/resources/static/css/filter.css
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,34 @@
border: none;
border-radius: 12px;
cursor: pointer;
}

#vacancyRange {
width: 100%; /* 부모 영역 다 채우기 */
max-width: 300px; /* 너무 길어지지 않도록 제한 */
margin-top: 8px;

}

/* 슬라이더 트랙 (크롬, 사파리 등) */
#vacancyRange::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 20px;
height: 20px;
border-radius: 50%;
background: #007bff;
cursor: pointer;
border: none;
box-shadow: 0 0 2px rgba(0, 0, 0, 0.3);
}

/* 파이어폭스 대응 */
#vacancyRange::-moz-range-thumb {
width: 20px;
height: 20px;
border-radius: 50%;
background: #007bff;
border: none;
cursor: pointer;
}
71 changes: 49 additions & 22 deletions src/main/resources/static/js/filter.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
const filterState = {}; // ex: { gender: { all: [...], unchecked: [...] }, platform: { ... } }
let minVacancy = 0

const vacancySlider = document.getElementById("vacancyRange");
const vacancyDisplay = document.getElementById("vacancyValue");

vacancySlider.addEventListener("input", () => {
vacancyDisplay.textContent = vacancySlider.value === "0" ? "모든 매치" : `${vacancySlider.value}인 이상 신청 가능`;
})

// 필터 상태 초기화 (최초 1회), 전역 필터링 상태 저장 -> renderMatches() 호출 시 전역 필터 상태 기반 필터링
function initFilterState(modalId) {
Expand Down Expand Up @@ -37,22 +45,36 @@ function saveFilterState(modalId) {

document.addEventListener("DOMContentLoaded", () => {
// 필터 모달 초기화 (모달 id 기준)
// 모든 .modal-overlay를 자동 탐색하여 초기화
document.querySelectorAll(".modal-overlay").forEach(modal => {
initFilterState(modal.id);

// 멀티 필터 초기화
document.querySelectorAll('[data-multifilter][data-modal-target]').forEach(btn => {
const modalSelector = btn.getAttribute("data-modal-target"); // ex: "#genderModal"
const modal = document.querySelector(modalSelector);
if (modal) {
initFilterState(modal.id);
}
});

// 모달 열기 버튼
document.querySelectorAll("[data-modal-target]").forEach(btn => {
btn.addEventListener("click", () => {
const selector = btn.getAttribute("data-modal-target");
const modal = document.querySelector(selector);
if (modal) {
if(!modal) return;

//멀티 필터 모달일 시,
if (btn.hasAttribute("data-multi-filter")) {
renderFilterState(modal.id); // 상태 복원
modal.classList.add("active");
//CSS .modal-overlay.active -> 화면에 display 되도록, modal-overlay는 선택시 나타나는 전체 회색배경.
//이 배경이 active 되면 내부 modal-content도 함께 나타난다.
}

if (modal.id === "vacancyModal") {
vacancySlider.value = minVacancy;
vacancyDisplay.textContent = vacancySlider.value === "0" ? "모든 매치" : `${vacancySlider.value}인 이상 신청 가능`;
}

//CSS .modal-overlay.active -> 화면에 display 되도록, modal-overlay는 선택시 나타나는 전체 회색배경.
//이 배경이 active 되면 내부 modal-content도 함께 나타난다.
modal.classList.add("active");
});
});

Expand All @@ -61,7 +83,7 @@ document.addEventListener("DOMContentLoaded", () => {
btn.addEventListener("click", () => {
const modal = btn.closest(".modal-overlay");
if (modal) {
//닫기 버튼 클릭시 체크 박스 상태는 저장하지 않는다.
//닫기 버튼 클릭시 상태는 저장하지 않는다.
modal.classList.remove("active");
}
});
Expand All @@ -71,7 +93,7 @@ document.addEventListener("DOMContentLoaded", () => {
document.querySelectorAll(".modal-overlay").forEach(modal => {
modal.addEventListener("click", (e) => {
if (e.target === modal) {
//바깥 클릭으로 닫을시 체크 박스 상태는 저장하지 않는다.
//바깥 클릭으로 닫을시 상태는 저장하지 않는다.
modal.classList.remove("active");
}
});
Expand All @@ -81,20 +103,24 @@ document.addEventListener("DOMContentLoaded", () => {
document.querySelectorAll(".modal-overlay .apply-btn").forEach(btn => {
btn.addEventListener("click", () => {
const modal = btn.closest(".modal-overlay");
if (modal) {
saveFilterState(modal.id); // 상태 저장
modal.classList.remove("active");
if(!modal) return;

const triggerBtn = document.querySelector(`[data-modal-target="#${modal.id}"]`);
if(!triggerBtn) return;

let isFiltered = false;
if(triggerBtn.hasAttribute("data-multifilter")) {
saveFilterState(modal.id);
isFiltered = filterState[modal.id]?.unchecked?.length > 0;
} else if(modal.id === "vacancyModal") {
minVacancy = vacancySlider.value;
isFiltered = minVacancy > 0;
}

// 필터링 선택 버튼 활성화 (적용 사항 있을시)
const triggerBtn = document.querySelector(`[data-modal-target="#${modal.id}"]`);
const isFiltered = filterState[modal.id].unchecked.length > 0
if (triggerBtn) {
triggerBtn.classList.toggle("active", isFiltered);
}
modal.classList.remove("active");
triggerBtn.classList.toggle("active", isFiltered);

renderMatches();
console.log(`[${modal.id}] 필터 적용됨:`, getActiveValues(modal.id));
}
renderMatches();
});
});
})
Expand All @@ -109,7 +135,8 @@ function applyFilters(matchList) {
return matchList
.filter(match => activeGenders.includes(match.sex))
.filter(match => activePlatforms.includes(match.platform))
.filter(match => !(hideFull && match.isFull)); //마감가리기 활성화 && 마감된 매치일 시 필터링
.filter(match => !(hideFull && match.vacancy === 0)) //마감가리기 활성화 && 마감된 매치일 시 필터링
.filter(match => match.vacancy >= minVacancy);
}

// 현재 적용된 항목만 가져오는 헬퍼 함수
Expand Down
3 changes: 2 additions & 1 deletion src/main/resources/static/js/home.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ function fetchMatches(date) {
.then(data => {
allMatches = data.map(match => ({
...match,
isFull: parseInt(match.cur_player) >= parseInt(match.max_player) //마감 여부 추가. '마감가리기' 필터용
vacancy: parseInt(match.max_player ?? "999") - parseInt(match.cur_player ?? "0")
//인원 정보가 없을 경우, 잔여인원을 크게 설정하여 필터링 되지 않도록 한다.
}));
renderMatches();
})
Expand Down
29 changes: 26 additions & 3 deletions src/main/resources/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,18 @@
</select>

<!-- 필터 열기 버튼들 -->
<button class="filter-chip" data-modal-target="#genderModal">
<button class="filter-chip" data-modal-target="#genderModal" data-multifilter>
성별 <img src="/img/filter-chip-arrow.svg" class="filter-chip-arrow">
</button>
<button class="filter-chip" data-modal-target="#platformModal">
<button class="filter-chip" data-modal-target="#platformModal" data-multifilter>
플랫폼 <img src="/img/filter-chip-arrow.svg" class="filter-chip-arrow">
</button>
<button class="filter-chip" data-modal-target="#vacancyModal" data-singlefilter data-filter-key="minVacancy">
잔여인원 <img src="/img/filter-chip-arrow.svg" class="filter-chip-arrow">
</button>

<!-- 마감 가리기 버튼 -->
<button id="hideFullToggle" class="hide-btn">마감 가리기</button>

<!-- 성별 모달-->
<div id="genderModal" class="modal-overlay">
Expand Down Expand Up @@ -95,7 +101,24 @@
</div>
</div>

<button id="hideFullToggle" class="hide-btn">마감 가리기</button>
<!-- 잔여인원 모달-->
<div id="vacancyModal" class="modal-overlay">
<div class="modal-content">
<div class="modal-header">
<span>잔여인원</span>
<button class="close-btn">✕</button>
</div>
<div class="modal-body">
<label for="vacancyRange">
<span id="vacancyValue" class="value-display"></span>
</label>
<input type="range" id="vacancyRange" min="0" max="20" value="0">
</div>
<div class="modal-footer">
<button id="applyVacancyFilter" class="apply-btn" >적용하기</button>
</div>
</div>
</div>
</div>

<div id="match-list">
Expand Down