Skip to content

어드민: 예약 목록 필터 + 일괄 승인/거절 - #151

Merged
hegelty merged 5 commits into
mainfrom
feature/place-reservation-filter-and-bulk-accept
Aug 21, 2026
Merged

어드민: 예약 목록 필터 + 일괄 승인/거절#151
hegelty merged 5 commits into
mainfrom
feature/place-reservation-filter-and-bulk-accept

Conversation

@joon363

@joon363 joon363 commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

무엇을 하는 PR인가

어드민 예약 화면에 필터 UI를 넣고, 예약 대기 목록에서 일괄 승인 / 일괄 거절을 할 수 있게 합니다.

API: PoApper/popo-nest-api#216 (먼저 배포되어야 합니다)

화면별 변경

장소/장비 예약 목록 (/place/reservation, /equipment/reservation)
image

  • 장소 / 상태 / 정렬 / 예약일 범위 / 제목 필터 추가. 검색 버튼을 눌러야 조회하도록 입력 중인 필터와 적용된 필터를 분리했습니다.
image
  • 서버 페이지네이션. 총 건수는 목록과 같은 필터로 계산합니다.

예약 대기 목록 (/reservation)
image

  • 장소·장비 두 탭에 같은 필터를 붙였습니다. 리소스 선택만 장소 / 소유 기관으로 다르고, 기간(다가오는·지난·전체) / 정렬 / 제목은 동일합니다. 이 화면은 정의상 심사중만 보므로 상태 칸은 빼고 심사중으로 고정했습니다.
  • 페이지네이션은 두지 않았습니다. 목록을 페이지로 쪼개면 일괄 승인이 화면에 보이는 20건에만 걸려서, 이 화면의 목적과 어긋납니다. 대신 기본 기간을 "다가오는 예약"으로 두어 이미 끝난 예약 수천 건을 통째로 불러오지 않게 했습니다.
  • 일괄 거절 버튼 추가. 되돌리기 번거로운 동작이라 확인창을 한 번 띄웁니다.

일괄 처리 UX
image

  • 승인/거절 결과를 승인 N건, 건너뜀 M건 + 건너뛴 예약의 제목·일시·사유로 보여줍니다. 조용히 사라지는 건이 없습니다.
  • 장소 대기 목록은 시간이 겹치는 예약에 "중복" 표시를 하고, 중복 없는 예약만 선택 버튼을 제공합니다.
  • 처리 중에는 어떤 동작이 진행 중인지 버튼별로 로딩을 표시하고 중복 클릭을 막습니다.

구조 정리

  • components/place/place.reservation.filter.jsxcomponents/reservation/reservation.filter.jsx. 리소스 선택(placeId / owner)과 표시할 칸을 props로 받아 장소 목록과 대기 목록이 함께 씁니다.
  • 필터 → 쿼리 파라미터 변환을 utils/reservation-filter.js로 분리했습니다. 날짜 input의 YYYY-MM-DD를 백엔드가 기대하는 YYYYMMDD로 바꾸는 처리도 여기 모여 있습니다.
  • utils/use-bulk-accept.js가 승인/거절 두 동작을 함께 다룹니다.

DB / 환경변수

둘 다 변경 없습니다.

검증

  • npx next lint — 이 브랜치가 건드리는 파일 기준 에러 0. pre-commit 통과.
  • 로컬 더미 데이터로 필터 조합, 정렬, 중복 예약 일괄 승인 시 skip 표시를 확인했습니다.

리뷰 포인트

  • 대기 목록에서 기간을 "전체"로 바꾸면 심사중 예약을 전부 한 번에 불러옵니다. 운영 데이터 규모에서 느리다면 그때 상한을 두는 편이 좋겠습니다. 지금은 일괄 처리 편의를 우선했습니다.
  • 장비 예약 목록(/equipment/reservation)에는 아직 필터를 붙이지 않았습니다. API는 준비돼 있으니 필요하면 후속 PR로 올리겠습니다.

추가 반영 (외부 QA 대응)

운영 담당자 QA 피드백을 받아 아래를 추가했습니다.

예약 생성일을 목록에 노출

중복 건을 처리하려면 popo 앱에서 겹치는 예약을 찾고 → 어드민 탭에서 이름으로 대조하고 → 각 상세 모달을 열어 생성일을 비교해야 했습니다. 이제 목록에서 끝납니다.

  • 장소·장비 대기 테이블에 예약 생성일 컬럼 추가. 반려 사유 문의가 왔을 때 이 화면을 그대로 캡처해 보낼 수 있습니다.
  • 중복 팝업이 겹치는 예약 전부를 신청이 빠른 순으로 나열하고, 각 건의 생성 일시와 ← 선순위 표시를 보여줍니다.

정렬 기본값을 생성일로

승인은 신청 순으로 처리하는 것이 원칙인데 대기 목록이 예약일 순으로 정렬돼 있어, 같은 날짜끼리 묶여 처리하게 되고 실제 신청 순서가 보이지 않았습니다. 기본 정렬을 생성일 오래된순으로 바꿨습니다(다른 정렬로 변경 가능).

일괄 처리 후 전체 새로고침 제거

window.location.reload()가 통계 4종을 getServerSideProps로 다시 받아오게 만들어 눈에 띄게 느렸습니다. 상태 변경으로 통계가 달라지지 않으므로 목록만 다시 부릅니다.

함께 보면 좋은 백엔드 변경

PoApper/popo-nest-api#216 에서 심사중 예약과 겹치는 신규 신청을 차단합니다. 앞으로 새 중복은 생기지 않지만, 기존에 쌓인 중복은 남아 있어 위 도구들은 계속 필요합니다.

joon363 and others added 3 commits August 10, 2026 17:40
총학 요청사항 2건에 대한 어드민 화면입니다.

1. 장소 예약 목록 필터
   - 장소, 상태, 예약일 기간, 예약 제목으로 검색하고 생성일/예약일 기준으로
     정렬할 수 있는 필터를 추가합니다.
   - 필터 조건이 count 조회에도 함께 전달되어 페이지네이션이 어긋나지 않습니다.
   - 검색 버튼을 누를 때만 조회하도록 입력 중인 필터와 적용된 필터를 분리했습니다.

2. 예약 대기 목록의 중복 예약 표시 및 일괄 승인 개선
   - 같은 장소, 같은 날짜에서 동시 예약 허용 개수를 넘기는 예약을 "중복"으로
     표시하고, 어떤 예약과 겹치는지 보여줍니다.
   - "중복 없는 예약만 선택" 버튼으로 겹치지 않는 예약만 골라 승인할 수 있습니다.
   - 일괄 승인 후 서버가 돌려준 승인/건너뜀 결과를 사유와 함께 보여줍니다.
   - 체크박스가 선택 상태를 반영하도록 controlled 컴포넌트로 바꿨습니다.
운영 환경에서 심사중 장소 예약이 6,850건까지 쌓여 예약 대기 목록이 매우 느리고,
일괄 승인이 사실상 동작하지 않는 문제를 고칩니다.

1. 로딩 시간 개선
   - 심사중 예약 전체를 한 번에 받아 클라이언트에서 정렬하던 방식을 없애고,
     서버 페이지네이션(한 페이지 20건)으로 바꿉니다.
   - 정렬은 서버에서 예약일 오름차순으로 처리해, 임박한 예약이 먼저 보입니다.

2. 지난 예약 처리 (기간 필터)
   - 기본값은 "다가오는 예약"이라 이미 끝난 예약은 보이지 않습니다.
   - 확인이 필요할 때를 위해 "지난 예약", "전체"를 고를 수 있는 기간 드롭다운을
     남겨둡니다. 안내 문구로 숨겨져 있다는 사실을 알려줍니다.

3. 일괄 승인
   - 아무것도 선택하지 않고 버튼을 누르면 빈 목록이 전송되어 "0개 승인" 이라는
     결과만 나왔습니다. 이제 선택된 건이 없으면 안내하고 요청하지 않습니다.
   - 체크박스를 controlled 로 바꾸고, 헤더에 "이 페이지 전체 선택"을 추가했습니다.
   - 버튼 라벨에 선택 건수를 표시하고, 요청 중에는 중복 클릭을 막습니다.
   - 장비 예약 대기 목록에도 일괄 승인을 추가했습니다. 기존에는 체크박스와
     버튼이 아예 없었습니다.
   - 선택/요청 로직은 useBulkAccept 훅으로 뽑아 장소와 장비가 공유합니다.
- Drop pagination from the waiting list. Splitting it into pages meant bulk
  accept only ever applied to the twenty rows currently on screen, which is
  the opposite of what the screen is for. It renders one list again.
- Reuse the same filter on both waiting-list tabs. place.reservation.filter
  becomes components/reservation/reservation.filter, which takes the
  resource select (장소 or 소유 기관) and the set of fields to show, so the
  place list keeps its status and date-range inputs while the waiting list
  gets 기간 / 정렬 / 제목 on top of the resource select.
- Add 예약 일괄 거절 next to 일괄 승인 in both wait tables. useBulkAccept
  now drives either action and tracks which one is in flight; reject asks
  for confirmation first.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Handling a duplicate meant opening popo, finding the competing booking,
matching it back by name in the admin tab, then opening each detail modal to
compare creation times. All of that is now on the list itself.

- Add a 예약 생성일 column to both wait tables. It doubles as the evidence an
  admin sends back when a rejected student asks who booked first.
- The 중복 popup lists every overlapping reservation ordered by application
  time, with each creation timestamp, and marks the one that will win.
- Default the wait list to 생성일 오래된순. Approving in application order is
  the rule, but the list was sorted by reservation date, which grouped rows
  by date instead and hid the order that actually matters.
- Refetch the list after a bulk action instead of window.location.reload().
  The reload also re-ran getServerSideProps for four statistics that a status
  change cannot affect, which is why the screen took so long to come back.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@hegelty

hegelty commented Aug 20, 2026

Copy link
Copy Markdown
Member

LGTM
PoApper/popo-nest-api#216 머지 후 같이 머지하도록 하겠습ㄴ디ㅏ

@hegelty
hegelty merged commit f95f69e into main Aug 21, 2026
1 check passed
@hegelty
hegelty deleted the feature/place-reservation-filter-and-bulk-accept branch August 21, 2026 04:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants