From 3cb6872a88a39a1915c81488329729d8fc51c6a5 Mon Sep 17 00:00:00 2001 From: Arachne <66822642+Arachneee@users.noreply.github.com> Date: Thu, 25 Jul 2024 15:17:29 +0900 Subject: [PATCH 01/15] =?UTF-8?q?refactor:=20=EC=9D=B8=EC=9B=90=20?= =?UTF-8?q?=EB=B3=80=EB=8F=99=20=EC=9A=94=EC=B2=AD=20=ED=98=95=ED=83=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20(#117)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../request/MemberActionSaveRequest.java | 10 ---------- .../request/MemberActionsSaveRequest.java | 12 +++++++++--- .../presentation/MemberActionControllerTest.java | 11 ++++------- 3 files changed, 13 insertions(+), 20 deletions(-) delete mode 100644 server/src/main/java/server/haengdong/presentation/request/MemberActionSaveRequest.java diff --git a/server/src/main/java/server/haengdong/presentation/request/MemberActionSaveRequest.java b/server/src/main/java/server/haengdong/presentation/request/MemberActionSaveRequest.java deleted file mode 100644 index a69c51d41..000000000 --- a/server/src/main/java/server/haengdong/presentation/request/MemberActionSaveRequest.java +++ /dev/null @@ -1,10 +0,0 @@ -package server.haengdong.presentation.request; - -import server.haengdong.application.request.MemberActionSaveAppRequest; - -public record MemberActionSaveRequest(String name, String status) { - - public MemberActionSaveAppRequest toAppRequest() { - return new MemberActionSaveAppRequest(name, status); - } -} diff --git a/server/src/main/java/server/haengdong/presentation/request/MemberActionsSaveRequest.java b/server/src/main/java/server/haengdong/presentation/request/MemberActionsSaveRequest.java index bfe80f0ee..0b3fcebae 100644 --- a/server/src/main/java/server/haengdong/presentation/request/MemberActionsSaveRequest.java +++ b/server/src/main/java/server/haengdong/presentation/request/MemberActionsSaveRequest.java @@ -1,14 +1,20 @@ package server.haengdong.presentation.request; +import jakarta.validation.constraints.NotBlank; import java.util.List; import server.haengdong.application.request.MemberActionSaveAppRequest; import server.haengdong.application.request.MemberActionsSaveAppRequest; -public record MemberActionsSaveRequest(List actions) { +public record MemberActionsSaveRequest( + List members, + + @NotBlank + String status +) { public MemberActionsSaveAppRequest toAppRequest() { - List appRequests = actions.stream() - .map(MemberActionSaveRequest::toAppRequest) + List appRequests = members.stream() + .map(name -> new MemberActionSaveAppRequest(name, status)) .toList(); return new MemberActionsSaveAppRequest(appRequests); diff --git a/server/src/test/java/server/haengdong/presentation/MemberActionControllerTest.java b/server/src/test/java/server/haengdong/presentation/MemberActionControllerTest.java index be8b89182..ad8787cc6 100644 --- a/server/src/test/java/server/haengdong/presentation/MemberActionControllerTest.java +++ b/server/src/test/java/server/haengdong/presentation/MemberActionControllerTest.java @@ -20,7 +20,6 @@ import org.springframework.test.web.servlet.result.MockMvcResultMatchers; import server.haengdong.application.MemberActionService; import server.haengdong.application.response.CurrentMemberAppResponse; -import server.haengdong.presentation.request.MemberActionSaveRequest; import server.haengdong.presentation.request.MemberActionsSaveRequest; @WebMvcTest(MemberActionController.class) @@ -38,11 +37,8 @@ class MemberActionControllerTest { @DisplayName("참여자 행동을 추가한다.") @Test void saveMemberActionTest() throws Exception { - MemberActionsSaveRequest memberActionsSaveRequest = new MemberActionsSaveRequest(List.of( - new MemberActionSaveRequest("웨디", "IN"), - new MemberActionSaveRequest("소하", "IN"), - new MemberActionSaveRequest("토다리", "IN"), - new MemberActionSaveRequest("쿠키", "IN"))); + MemberActionsSaveRequest memberActionsSaveRequest = new MemberActionsSaveRequest( + List.of("웨디", "소하", "토다리", "쿠키"), "IN"); String requestBody = objectMapper.writeValueAsString(memberActionsSaveRequest); @@ -56,7 +52,8 @@ void saveMemberActionTest() throws Exception { @DisplayName("현재 참여 인원을 조회합니다.") @Test void getCurrentMembers() throws Exception { - List currentMemberAppResponses = List.of(new CurrentMemberAppResponse("소하"), new CurrentMemberAppResponse("토다리")); + List currentMemberAppResponses = List.of( + new CurrentMemberAppResponse("소하"), new CurrentMemberAppResponse("토다리")); given(memberActionService.getCurrentMembers(any())).willReturn(currentMemberAppResponses); From d943520138de321bfd74211348a5fd2599fe74b5 Mon Sep 17 00:00:00 2001 From: kunsanglee <85242378+kunsanglee@users.noreply.github.com> Date: Thu, 25 Jul 2024 18:05:13 +0900 Subject: [PATCH 02/15] =?UTF-8?q?=08feat:=20=EC=95=A1=EC=85=98=20=EC=9D=B4?= =?UTF-8?q?=EB=A0=A5=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20(#76)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 액션 이력 조회 기능 구현 Co-authored-by: 3juhwan <13selfesteem91@naver.com> * feat: 액션 이력 조회 반환 형식 변경 * test: 액션 이력 조회 테스트 삭제 --------- Co-authored-by: 3juhwan <13selfesteem91@naver.com> --- .../haengdong/application/EventService.java | 52 +++++++++++++++ .../response/ActionAppResponse.java | 54 ++++++++++++++++ .../presentation/EventController.java | 8 +++ .../presentation/response/ActionResponse.java | 20 ++++++ .../response/ActionsResponse.java | 28 +++++++++ .../presentation/response/StepResponse.java | 63 +++++++++++++++++++ .../application/EventServiceTest.java | 51 +++++++++++++++ .../response/StepResponseTest.java | 57 +++++++++++++++++ 8 files changed, 333 insertions(+) create mode 100644 server/src/main/java/server/haengdong/application/response/ActionAppResponse.java create mode 100644 server/src/main/java/server/haengdong/presentation/response/ActionResponse.java create mode 100644 server/src/main/java/server/haengdong/presentation/response/ActionsResponse.java create mode 100644 server/src/main/java/server/haengdong/presentation/response/StepResponse.java create mode 100644 server/src/test/java/server/haengdong/presentation/response/StepResponseTest.java diff --git a/server/src/main/java/server/haengdong/application/EventService.java b/server/src/main/java/server/haengdong/application/EventService.java index dee023fbc..94c9dbda1 100644 --- a/server/src/main/java/server/haengdong/application/EventService.java +++ b/server/src/main/java/server/haengdong/application/EventService.java @@ -1,10 +1,18 @@ package server.haengdong.application; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import server.haengdong.application.request.EventAppRequest; +import server.haengdong.application.response.ActionAppResponse; import server.haengdong.application.response.EventAppResponse; import server.haengdong.application.response.EventDetailAppResponse; +import server.haengdong.domain.action.BillAction; +import server.haengdong.domain.action.BillActionRepository; +import server.haengdong.domain.action.MemberAction; +import server.haengdong.domain.action.MemberActionRepository; import server.haengdong.domain.event.Event; import server.haengdong.domain.event.EventRepository; import server.haengdong.domain.event.EventTokenProvider; @@ -17,6 +25,8 @@ public class EventService { private final EventRepository eventRepository; private final EventTokenProvider eventTokenProvider; + private final BillActionRepository billActionRepository; + private final MemberActionRepository memberActionRepository; public EventAppResponse saveEvent(EventAppRequest request) { String token = eventTokenProvider.createToken(); @@ -32,4 +42,46 @@ public EventDetailAppResponse findEvent(String token) { return EventDetailAppResponse.of(event); } + + public List findActions(String token) { + Event event = eventRepository.findByToken(token).orElseThrow(() -> new IllegalArgumentException("")); + + List billActions = billActionRepository.findByAction_Event(event).stream() + .sorted(Comparator.comparing(BillAction::getSequence)).toList(); + List memberActions = memberActionRepository.findAllByEvent(event).stream() + .sorted(Comparator.comparing(MemberAction::getSequence)).toList(); + + return getActionAppResponses(billActions, memberActions); + } + + private List getActionAppResponses( + List billActions, + List memberActions + ) { + int billActionIndex = 0; + int memberActionIndex = 0; + List actionAppResponses = new ArrayList<>(); + + while (billActionIndex < billActions.size() && memberActionIndex < memberActions.size()) { + BillAction billAction = billActions.get(billActionIndex); + MemberAction memberAction = memberActions.get(memberActionIndex); + if (billAction.getSequence() < memberAction.getSequence()) { + actionAppResponses.add(ActionAppResponse.of(billAction)); + billActionIndex++; + } else { + actionAppResponses.add(ActionAppResponse.of(memberAction)); + memberActionIndex++; + } + } + while (billActionIndex < billActions.size()) { + BillAction billAction = billActions.get(billActionIndex++); + actionAppResponses.add(ActionAppResponse.of(billAction)); + } + while (memberActionIndex < memberActions.size()) { + MemberAction memberAction = memberActions.get(memberActionIndex++); + actionAppResponses.add(ActionAppResponse.of(memberAction)); + } + + return actionAppResponses; + } } diff --git a/server/src/main/java/server/haengdong/application/response/ActionAppResponse.java b/server/src/main/java/server/haengdong/application/response/ActionAppResponse.java new file mode 100644 index 000000000..a1abcc35b --- /dev/null +++ b/server/src/main/java/server/haengdong/application/response/ActionAppResponse.java @@ -0,0 +1,54 @@ +package server.haengdong.application.response; + +import server.haengdong.domain.action.BillAction; +import server.haengdong.domain.action.MemberAction; +import server.haengdong.domain.action.MemberActionStatus; + +public record ActionAppResponse( + Long actionId, + String name, + Long price, + Long sequence, + ActionType actionType +) { + + public static ActionAppResponse of(BillAction billAction) { + return new ActionAppResponse( + billAction.getAction().getId(), + billAction.getTitle(), + billAction.getPrice(), + billAction.getSequence(), + ActionType.BILL + ); + } + + public static ActionAppResponse of(MemberAction memberAction) { + MemberActionStatus status = memberAction.getStatus(); + + return new ActionAppResponse( + memberAction.getAction().getId(), + memberAction.getMemberName(), + null, + memberAction.getSequence(), + ActionType.of(status) + ); + } + + public String actionTypeName() { + return actionType.name(); + } + + public enum ActionType { + BILL, + IN, + OUT, + ; + + private static ActionType of(MemberActionStatus memberActionStatus) { + if (MemberActionStatus.IN == memberActionStatus) { + return IN; + } + return OUT; + } + } +} diff --git a/server/src/main/java/server/haengdong/presentation/EventController.java b/server/src/main/java/server/haengdong/presentation/EventController.java index 260253e8b..9fbe098ce 100644 --- a/server/src/main/java/server/haengdong/presentation/EventController.java +++ b/server/src/main/java/server/haengdong/presentation/EventController.java @@ -12,6 +12,7 @@ import server.haengdong.presentation.request.EventSaveRequest; import server.haengdong.presentation.response.EventDetailResponse; import server.haengdong.presentation.response.EventResponse; +import server.haengdong.presentation.response.StepResponse; @RequiredArgsConstructor @RestController @@ -32,4 +33,11 @@ public ResponseEntity findEvent(@PathVariable("eventId") St return ResponseEntity.ok(eventDetailResponse); } + + @GetMapping("/api/events/{eventId}/actions") + public ResponseEntity findActions(@PathVariable("eventId") String token) { + StepResponse stepResponse = StepResponse.of(eventService.findActions(token)); + + return ResponseEntity.ok(stepResponse); + } } diff --git a/server/src/main/java/server/haengdong/presentation/response/ActionResponse.java b/server/src/main/java/server/haengdong/presentation/response/ActionResponse.java new file mode 100644 index 000000000..a58e60186 --- /dev/null +++ b/server/src/main/java/server/haengdong/presentation/response/ActionResponse.java @@ -0,0 +1,20 @@ +package server.haengdong.presentation.response; + +import server.haengdong.application.response.ActionAppResponse; + +public record ActionResponse( + Long actionId, + String name, + Long price, + Long sequence +) { + + public static ActionResponse of(ActionAppResponse actionAppResponse) { + return new ActionResponse( + actionAppResponse.actionId(), + actionAppResponse.name(), + actionAppResponse.price(), + actionAppResponse.sequence() + ); + } +} diff --git a/server/src/main/java/server/haengdong/presentation/response/ActionsResponse.java b/server/src/main/java/server/haengdong/presentation/response/ActionsResponse.java new file mode 100644 index 000000000..188c48dc9 --- /dev/null +++ b/server/src/main/java/server/haengdong/presentation/response/ActionsResponse.java @@ -0,0 +1,28 @@ +package server.haengdong.presentation.response; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import server.haengdong.application.response.ActionAppResponse; + +public record ActionsResponse( + String type, + String stepName, + Set members, + List actions +) { + + public static ActionsResponse of(List actions, Set members) { + List actionResponses = actions.stream() + .map(ActionResponse::of) + .toList(); + + String actionType = actions.get(0).actionTypeName(); + return new ActionsResponse( + actionType, + null, + new HashSet<>(members), + actionResponses + ); + } +} diff --git a/server/src/main/java/server/haengdong/presentation/response/StepResponse.java b/server/src/main/java/server/haengdong/presentation/response/StepResponse.java new file mode 100644 index 000000000..76f92ad53 --- /dev/null +++ b/server/src/main/java/server/haengdong/presentation/response/StepResponse.java @@ -0,0 +1,63 @@ +package server.haengdong.presentation.response; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import server.haengdong.application.response.ActionAppResponse; + +public record StepResponse( + List steps +) { + + public static StepResponse of(List actions) { + List actionsResponse = new ArrayList<>(); + Set members = new HashSet<>(); + ActionAppResponse firstAction = getFirstAction(actions); + List group = new ArrayList<>(); + group.add(firstAction); + String currentActionType = firstAction.actionTypeName(); + members.add(firstAction.name()); + + for (int i = 1; i < actions.size(); i++) { + ActionAppResponse action = actions.get(i); + String typeName = action.actionTypeName(); + if (currentActionType.equals(typeName)) { + if (typeName.equals("IN")) { + members.add(action.name()); + } + if (typeName.equals("OUT")) { + members.remove(action.name()); + } + group.add(action); + continue; + } + if (currentActionType.equals("BILL")) { + actionsResponse.add(ActionsResponse.of(group, members)); + } else { + actionsResponse.add(ActionsResponse.of(group, Set.of())); + } + currentActionType = typeName; + group.clear(); + if (typeName.equals("IN")) { + members.add(action.name()); + } + if (typeName.equals("OUT")) { + members.remove(action.name()); + } + group.add(action); + } + + if (currentActionType.equals("BILL")) { + actionsResponse.add(ActionsResponse.of(group, members)); + } else { + actionsResponse.add(ActionsResponse.of(group, null)); + } + + return new StepResponse(actionsResponse); + } + + private static ActionAppResponse getFirstAction(List actions) { + return actions.get(0); + } +} diff --git a/server/src/test/java/server/haengdong/application/EventServiceTest.java b/server/src/test/java/server/haengdong/application/EventServiceTest.java index 31b1c650e..5a3e8bce3 100644 --- a/server/src/test/java/server/haengdong/application/EventServiceTest.java +++ b/server/src/test/java/server/haengdong/application/EventServiceTest.java @@ -2,8 +2,10 @@ import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; import static org.mockito.BDDMockito.given; +import java.util.List; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -11,8 +13,16 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import server.haengdong.application.request.EventAppRequest; +import server.haengdong.application.response.ActionAppResponse; import server.haengdong.application.response.EventAppResponse; import server.haengdong.application.response.EventDetailAppResponse; +import server.haengdong.domain.action.Action; +import server.haengdong.domain.action.ActionRepository; +import server.haengdong.domain.action.BillAction; +import server.haengdong.domain.action.BillActionRepository; +import server.haengdong.domain.action.MemberAction; +import server.haengdong.domain.action.MemberActionRepository; +import server.haengdong.domain.action.MemberActionStatus; import server.haengdong.domain.event.Event; import server.haengdong.domain.event.EventRepository; import server.haengdong.domain.event.EventTokenProvider; @@ -29,8 +39,20 @@ class EventServiceTest { @Autowired private EventRepository eventRepository; + @Autowired + private ActionRepository actionRepository; + + @Autowired + private BillActionRepository billActionRepository; + + @Autowired + private MemberActionRepository memberActionRepository; + @AfterEach void tearDown() { + billActionRepository.deleteAllInBatch(); + memberActionRepository.deleteAllInBatch(); + actionRepository.deleteAllInBatch(); eventRepository.deleteAllInBatch(); } @@ -56,4 +78,33 @@ void findEventTest() { assertThat(eventDetailAppResponse.eventName()).isEqualTo("행동대장 회식"); } + + @DisplayName("행사에 속한 모든 액션을 조회한다.") + @Test + void findActionsTest() { + Event event = new Event("행동대장 회식", "웨디_토큰"); + Action action = new Action(event, 1L); + MemberAction memberAction = new MemberAction(action, "토다리", MemberActionStatus.IN, 1L); + Action action1 = new Action(event, 2L); + MemberAction memberAction1 = new MemberAction(action1, "쿠키", MemberActionStatus.IN, 1L); + Action action2 = new Action(event, 3L); + BillAction billAction = new BillAction(action2, "뽕나무쟁이족발", 30000L); + eventRepository.save(event); + memberActionRepository.saveAll(List.of(memberAction, memberAction1)); + billActionRepository.save(billAction); + + List actionAppResponses = eventService.findActions("웨디_토큰"); + + assertThat(actionAppResponses).hasSize(3) + .extracting(ActionAppResponse::actionId, + ActionAppResponse::name, + ActionAppResponse::price, + ActionAppResponse::sequence, + ActionAppResponse::actionTypeName) + .containsExactly( + tuple(1L, "토다리", null, 1L, "IN"), + tuple(2L, "쿠키", null, 2L, "IN"), + tuple(3L, "뽕나무쟁이족발", 30000L, 3L, "BILL") + ); + } } diff --git a/server/src/test/java/server/haengdong/presentation/response/StepResponseTest.java b/server/src/test/java/server/haengdong/presentation/response/StepResponseTest.java new file mode 100644 index 000000000..aac78691c --- /dev/null +++ b/server/src/test/java/server/haengdong/presentation/response/StepResponseTest.java @@ -0,0 +1,57 @@ +package server.haengdong.presentation.response; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import server.haengdong.application.response.ActionAppResponse; +import server.haengdong.application.response.ActionAppResponse.ActionType; + +@SpringBootTest +class StepResponseTest { + + @Autowired + private ObjectMapper objectMapper; + + @DisplayName("") + @Test + void test() throws JsonProcessingException { + List actionAppResponse = new ArrayList<>(); + + // IN actions + ActionAppResponse actionAppResponse1 = new ActionAppResponse(3L, "망쵸", null, 3L, ActionType.IN); + actionAppResponse.add(actionAppResponse1); + ActionAppResponse actionAppResponse2 = new ActionAppResponse(4L, "백호", null, 4L, ActionType.IN); + actionAppResponse.add(actionAppResponse2); + + // BILL step 1 + ActionAppResponse actionAppResponse3 = new ActionAppResponse(1L, "감자탕", 10000L, 1L, ActionType.BILL); + actionAppResponse.add(actionAppResponse3); + ActionAppResponse actionAppResponse4 = new ActionAppResponse(2L, "인생네컷", 10000L, 2L, ActionType.BILL); + actionAppResponse.add(actionAppResponse4); + + // IN actions + ActionAppResponse actionAppResponse5 = new ActionAppResponse(5L, "소하", null, 5L, ActionType.IN); + actionAppResponse.add(actionAppResponse5); + ActionAppResponse actionAppResponse6 = new ActionAppResponse(6L, "웨디", null, 6L, ActionType.IN); + actionAppResponse.add(actionAppResponse6); + + // OUT actions + ActionAppResponse actionAppResponse7 = new ActionAppResponse(7L, "망쵸", null, 7L, ActionType.OUT); + actionAppResponse.add(actionAppResponse7); + ActionAppResponse actionAppResponse8 = new ActionAppResponse(8L, "백호", null, 8L, ActionType.OUT); + actionAppResponse.add(actionAppResponse8); + + // BILL step 2 + ActionAppResponse actionAppResponse9 = new ActionAppResponse(9L, "노래방", 20000L, 10L, ActionType.BILL); + actionAppResponse.add(actionAppResponse9); + + // StepResponse creation + StepResponse stepResponse = StepResponse.of(actionAppResponse); + System.out.println("stepResponse = " + stepResponse); + } +} From 74de2a68ea29560f781a195eb1b2ebe4800071a9 Mon Sep 17 00:00:00 2001 From: TaehunLee <85233397+Todari@users.noreply.github.com> Date: Thu, 25 Jul 2024 18:06:07 +0900 Subject: [PATCH 03/15] =?UTF-8?q?chore:=20frontend=20yml=20lint=20?= =?UTF-8?q?=EA=B3=BC=EC=A0=95=20=EC=88=98=EC=A0=95=20(#120)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/ISSUE_TEMPLATE/bug-template.md | 8 ++++++++ ...frontend-pll-request.yml => frontend-pull-request.yml} | 0 client/package.json | 4 ++-- 3 files changed, 10 insertions(+), 2 deletions(-) rename .github/workflows/{frontend-pll-request.yml => frontend-pull-request.yml} (100%) diff --git a/.github/ISSUE_TEMPLATE/bug-template.md b/.github/ISSUE_TEMPLATE/bug-template.md index e14ce93b3..4fab11f91 100644 --- a/.github/ISSUE_TEMPLATE/bug-template.md +++ b/.github/ISSUE_TEMPLATE/bug-template.md @@ -12,6 +12,14 @@ assignees: '' ## 🚨 버그 발생 상황 최대한 상세하게 작성해주세요. +### as-is + +현재 상황에 대해서 알려주세요. + +### to-be + +구현이 된 후 상황을 예상해 주세요. + ## 예상 결과 예상했던 정상적인 결과가 어떤 것인지 설명해주세요. diff --git a/.github/workflows/frontend-pll-request.yml b/.github/workflows/frontend-pull-request.yml similarity index 100% rename from .github/workflows/frontend-pll-request.yml rename to .github/workflows/frontend-pull-request.yml diff --git a/client/package.json b/client/package.json index fed2f5249..3c66313b4 100644 --- a/client/package.json +++ b/client/package.json @@ -6,7 +6,7 @@ "scripts": { "start": "webpack serve ", "build": "webpack --mode production", - "lint": "eslint 'src/**/*.{js,jsx,ts,tsx}' --fix", + "lint": "eslint 'src/**/*.{js,jsx,ts,tsx}'", "format": "prettier --write 'src/**/*.{js,jsx,ts,tsx,json,css,scss,md}'" }, "keywords": [], @@ -56,4 +56,4 @@ "npm": ">=10.7.0", "node": ">=20.15.1" } -} +} \ No newline at end of file From b9870edba09577c9d880c6ac32d04a92f90a84fb Mon Sep 17 00:00:00 2001 From: kunsanglee <85242378+kunsanglee@users.noreply.github.com> Date: Thu, 25 Jul 2024 18:24:30 +0900 Subject: [PATCH 04/15] =?UTF-8?q?fix:=20=EC=95=A1=EC=85=98=20=EC=9D=B4?= =?UTF-8?q?=EB=A0=A5=EC=9D=B4=20=EC=97=86=EB=8A=94=20=EA=B2=BD=EC=9A=B0=20?= =?UTF-8?q?=EB=B9=88=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EB=B0=98=ED=99=98=20?= =?UTF-8?q?(#122)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/haengdong/presentation/response/StepResponse.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/server/src/main/java/server/haengdong/presentation/response/StepResponse.java b/server/src/main/java/server/haengdong/presentation/response/StepResponse.java index 76f92ad53..354f019df 100644 --- a/server/src/main/java/server/haengdong/presentation/response/StepResponse.java +++ b/server/src/main/java/server/haengdong/presentation/response/StepResponse.java @@ -11,6 +11,9 @@ public record StepResponse( ) { public static StepResponse of(List actions) { + if (actions.isEmpty()) { + return new StepResponse(List.of()); + } List actionsResponse = new ArrayList<>(); Set members = new HashSet<>(); ActionAppResponse firstAction = getFirstAction(actions); From ff2632dccbfa63a980cc3cdb1bb98854e055278d Mon Sep 17 00:00:00 2001 From: kunsanglee <85242378+kunsanglee@users.noreply.github.com> Date: Thu, 25 Jul 2024 19:08:42 +0900 Subject: [PATCH 05/15] =?UTF-8?q?=08fix:=20=EC=95=A1=EC=85=98=20=EC=9D=B4?= =?UTF-8?q?=EB=A0=A5=20=EC=A1=B0=ED=9A=8C=20=EC=98=A4=EB=A5=98=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20(#124)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: HaengdongException 적용 안된 부분 적용 * fix: Transactional 추가 및 StepResponse 로직 수정 --- .../java/server/haengdong/application/EventService.java | 6 +++++- .../server/haengdong/domain/action/MemberActionStatus.java | 5 ++++- .../haengdong/presentation/response/StepResponse.java | 4 ---- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/server/src/main/java/server/haengdong/application/EventService.java b/server/src/main/java/server/haengdong/application/EventService.java index 94c9dbda1..9ad502606 100644 --- a/server/src/main/java/server/haengdong/application/EventService.java +++ b/server/src/main/java/server/haengdong/application/EventService.java @@ -5,6 +5,7 @@ import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import server.haengdong.application.request.EventAppRequest; import server.haengdong.application.response.ActionAppResponse; import server.haengdong.application.response.EventAppResponse; @@ -20,6 +21,7 @@ import server.haengdong.exception.HaengdongException; @RequiredArgsConstructor +@Transactional(readOnly = true) @Service public class EventService { @@ -28,6 +30,7 @@ public class EventService { private final BillActionRepository billActionRepository; private final MemberActionRepository memberActionRepository; + @Transactional public EventAppResponse saveEvent(EventAppRequest request) { String token = eventTokenProvider.createToken(); Event event = request.toEvent(token); @@ -44,7 +47,8 @@ public EventDetailAppResponse findEvent(String token) { } public List findActions(String token) { - Event event = eventRepository.findByToken(token).orElseThrow(() -> new IllegalArgumentException("")); + Event event = eventRepository.findByToken(token) + .orElseThrow(() -> new HaengdongException(HaengdongErrorCode.NOT_FOUND_EVENT)); List billActions = billActionRepository.findByAction_Event(event).stream() .sorted(Comparator.comparing(BillAction::getSequence)).toList(); diff --git a/server/src/main/java/server/haengdong/domain/action/MemberActionStatus.java b/server/src/main/java/server/haengdong/domain/action/MemberActionStatus.java index afcb31337..0a20817fd 100644 --- a/server/src/main/java/server/haengdong/domain/action/MemberActionStatus.java +++ b/server/src/main/java/server/haengdong/domain/action/MemberActionStatus.java @@ -1,6 +1,8 @@ package server.haengdong.domain.action; import java.util.Arrays; +import server.haengdong.exception.HaengdongErrorCode; +import server.haengdong.exception.HaengdongException; public enum MemberActionStatus { IN, @@ -11,6 +13,7 @@ public static MemberActionStatus of(String status) { return Arrays.stream(MemberActionStatus.values()) .filter(s -> s.name().equals(status)) .findFirst() - .orElseThrow(() -> new IllegalArgumentException("Invalid status: " + status)); + .orElseThrow(() -> new HaengdongException(HaengdongErrorCode.BAD_REQUEST, + "존재하지 않는 인원 변동 액션입니다.")); } } diff --git a/server/src/main/java/server/haengdong/presentation/response/StepResponse.java b/server/src/main/java/server/haengdong/presentation/response/StepResponse.java index 354f019df..54b2b06b9 100644 --- a/server/src/main/java/server/haengdong/presentation/response/StepResponse.java +++ b/server/src/main/java/server/haengdong/presentation/response/StepResponse.java @@ -37,8 +37,6 @@ public static StepResponse of(List actions) { } if (currentActionType.equals("BILL")) { actionsResponse.add(ActionsResponse.of(group, members)); - } else { - actionsResponse.add(ActionsResponse.of(group, Set.of())); } currentActionType = typeName; group.clear(); @@ -53,8 +51,6 @@ public static StepResponse of(List actions) { if (currentActionType.equals("BILL")) { actionsResponse.add(ActionsResponse.of(group, members)); - } else { - actionsResponse.add(ActionsResponse.of(group, null)); } return new StepResponse(actionsResponse); From 6be91dae0550909ff3eac2c6a625f402cae20a32 Mon Sep 17 00:00:00 2001 From: kunsanglee <85242378+kunsanglee@users.noreply.github.com> Date: Thu, 25 Jul 2024 19:24:48 +0900 Subject: [PATCH 06/15] =?UTF-8?q?fix:=20StepResponse=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=20(#126)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../haengdong/presentation/response/StepResponse.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/server/src/main/java/server/haengdong/presentation/response/StepResponse.java b/server/src/main/java/server/haengdong/presentation/response/StepResponse.java index 54b2b06b9..a8f3877ef 100644 --- a/server/src/main/java/server/haengdong/presentation/response/StepResponse.java +++ b/server/src/main/java/server/haengdong/presentation/response/StepResponse.java @@ -35,9 +35,7 @@ public static StepResponse of(List actions) { group.add(action); continue; } - if (currentActionType.equals("BILL")) { - actionsResponse.add(ActionsResponse.of(group, members)); - } + actionsResponse.add(ActionsResponse.of(group, members)); currentActionType = typeName; group.clear(); if (typeName.equals("IN")) { @@ -48,10 +46,7 @@ public static StepResponse of(List actions) { } group.add(action); } - - if (currentActionType.equals("BILL")) { - actionsResponse.add(ActionsResponse.of(group, members)); - } + actionsResponse.add(ActionsResponse.of(group, members)); return new StepResponse(actionsResponse); } From 38a28f71be90d376d7b3c818067fbc80a0053b0f Mon Sep 17 00:00:00 2001 From: kunsanglee <85242378+kunsanglee@users.noreply.github.com> Date: Fri, 26 Jul 2024 12:58:09 +0900 Subject: [PATCH 07/15] =?UTF-8?q?test:=20Gradle,=20Docker=20=EC=BA=90?= =?UTF-8?q?=EC=8B=B1=EC=9D=84=20=EC=9C=84=ED=95=9C=20Feature/#121=20test?= =?UTF-8?q?=20(#128)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: Github Actions Gradle, Docker Build 캐싱 * refactor: Docker 사용하는 포트 번호 수정 * refactor: Docker 사용하는 포트 번호 수정 * test * after cache * after cache2 --- .github/workflows/backend-pull-request.yml | 14 +++++++++++-- .github/workflows/backend-push.yml | 21 ++++++++++++++----- .../haengdong/HaengdongApplication.java | 2 ++ 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/.github/workflows/backend-pull-request.yml b/.github/workflows/backend-pull-request.yml index 113972929..ba6870a61 100644 --- a/.github/workflows/backend-pull-request.yml +++ b/.github/workflows/backend-pull-request.yml @@ -2,7 +2,7 @@ name: backend-pull-request on: pull_request: - branches: [ "main", "develop" ] + branches: [ "main", "develop", "feature/#121" ] paths: - 'server/**' @@ -18,6 +18,16 @@ jobs: - name: CheckOut uses: actions/checkout@v4 + - name: Gradle Caching + uses: actions/cache@v3 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + - name: Set up JDK 17 uses: actions/setup-java@v4 with: @@ -25,7 +35,7 @@ jobs: distribution: 'temurin' - name: Setup Gradle - uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 + uses: gradle/gradle-build-action@v2 - name: Test with Gradle Wrapper run: ./gradlew clean build diff --git a/.github/workflows/backend-push.yml b/.github/workflows/backend-push.yml index a35eaea7b..0a3b3e43f 100644 --- a/.github/workflows/backend-push.yml +++ b/.github/workflows/backend-push.yml @@ -2,7 +2,7 @@ name: backend-push on: push: - branches: [ "main", "develop" ] + branches: [ "main", "develop", "feature/#121" ] paths: - 'server/**' @@ -25,6 +25,16 @@ jobs: token: ${{secrets.CONFIG_SUBMODULE_TOKEN}} submodules: true + - name: Gradle Caching + uses: actions/cache@v3 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + - name: Set up JDK 17 uses: actions/setup-java@v4 with: @@ -32,7 +42,7 @@ jobs: distribution: 'temurin' - name: Setup Gradle - uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 + uses: gradle/gradle-build-action@v2 - name: Test with Gradle Wrapper run: ./gradlew test @@ -48,8 +58,9 @@ jobs: - name: Build and push run: | - docker buildx build --platform linux/arm64 -t \ - ${{ secrets.DOCKER_USERNAME }}/haengdong-backend-dev --push . + docker buildx build --platform linux/arm64 --cache-from=type=registry,ref=${{ secrets.DOCKER_USERNAME }}/haengdong-backend-dev:cache \ + --cache-to=type=registry,ref=${{ secrets.DOCKER_USERNAME }}/haengdong-backend-dev:cache,mode=max \ + -t ${{ secrets.DOCKER_USERNAME }}/haengdong-backend-dev --push . deploy: needs: build @@ -68,4 +79,4 @@ jobs: run: sudo docker pull ${{ secrets.DOCKER_USERNAME }}/haengdong-backend-dev - name: Docker run - run: sudo docker run -d -p 80:8080 --name haengdong-backend-dev haengdong/haengdong-backend-dev + run: sudo docker run -d -p 8080:8080 --name haengdong-backend-dev haengdong/haengdong-backend-dev diff --git a/server/src/main/java/server/haengdong/HaengdongApplication.java b/server/src/main/java/server/haengdong/HaengdongApplication.java index 31b6e46e7..7549a9784 100644 --- a/server/src/main/java/server/haengdong/HaengdongApplication.java +++ b/server/src/main/java/server/haengdong/HaengdongApplication.java @@ -8,6 +8,8 @@ public class HaengdongApplication { public static void main(String[] args) { SpringApplication.run(HaengdongApplication.class, args); + + } } From 1d2e71a716674d15e9b0a3e19235ee0f1d1d0dc4 Mon Sep 17 00:00:00 2001 From: kunsanglee <85242378+kunsanglee@users.noreply.github.com> Date: Fri, 26 Jul 2024 13:12:21 +0900 Subject: [PATCH 08/15] =?UTF-8?q?test:=20Gardle,=20Docker=20=EC=BA=90?= =?UTF-8?q?=EC=8B=B1=EC=9D=84=20=EC=9C=84=ED=95=9C=20Feature/#121=20test2?= =?UTF-8?q?=20(#130)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: Github Actions Gradle, Docker Build 캐싱 * refactor: Docker 사용하는 포트 번호 수정 * refactor: Docker 사용하는 포트 번호 수정 * after cache --- server/src/main/java/server/haengdong/HaengdongApplication.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/server/haengdong/HaengdongApplication.java b/server/src/main/java/server/haengdong/HaengdongApplication.java index 7549a9784..be22f61d0 100644 --- a/server/src/main/java/server/haengdong/HaengdongApplication.java +++ b/server/src/main/java/server/haengdong/HaengdongApplication.java @@ -11,5 +11,5 @@ public static void main(String[] args) { } - + } From 883884427ff87f4325acd2c5cf7e84be57adf291 Mon Sep 17 00:00:00 2001 From: Juhwan Kim <13selfesteem91@naver.com> Date: Fri, 26 Jul 2024 13:44:42 +0900 Subject: [PATCH 09/15] revert: gradle cache, docker cache (#133) --- .github/workflows/backend-pull-request.yml | 14 ++----------- .github/workflows/backend-push.yml | 21 +++++-------------- .../haengdong/HaengdongApplication.java | 4 +--- 3 files changed, 8 insertions(+), 31 deletions(-) diff --git a/.github/workflows/backend-pull-request.yml b/.github/workflows/backend-pull-request.yml index ba6870a61..113972929 100644 --- a/.github/workflows/backend-pull-request.yml +++ b/.github/workflows/backend-pull-request.yml @@ -2,7 +2,7 @@ name: backend-pull-request on: pull_request: - branches: [ "main", "develop", "feature/#121" ] + branches: [ "main", "develop" ] paths: - 'server/**' @@ -18,16 +18,6 @@ jobs: - name: CheckOut uses: actions/checkout@v4 - - name: Gradle Caching - uses: actions/cache@v3 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} - restore-keys: | - ${{ runner.os }}-gradle- - - name: Set up JDK 17 uses: actions/setup-java@v4 with: @@ -35,7 +25,7 @@ jobs: distribution: 'temurin' - name: Setup Gradle - uses: gradle/gradle-build-action@v2 + uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 - name: Test with Gradle Wrapper run: ./gradlew clean build diff --git a/.github/workflows/backend-push.yml b/.github/workflows/backend-push.yml index 0a3b3e43f..a35eaea7b 100644 --- a/.github/workflows/backend-push.yml +++ b/.github/workflows/backend-push.yml @@ -2,7 +2,7 @@ name: backend-push on: push: - branches: [ "main", "develop", "feature/#121" ] + branches: [ "main", "develop" ] paths: - 'server/**' @@ -25,16 +25,6 @@ jobs: token: ${{secrets.CONFIG_SUBMODULE_TOKEN}} submodules: true - - name: Gradle Caching - uses: actions/cache@v3 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} - restore-keys: | - ${{ runner.os }}-gradle- - - name: Set up JDK 17 uses: actions/setup-java@v4 with: @@ -42,7 +32,7 @@ jobs: distribution: 'temurin' - name: Setup Gradle - uses: gradle/gradle-build-action@v2 + uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 - name: Test with Gradle Wrapper run: ./gradlew test @@ -58,9 +48,8 @@ jobs: - name: Build and push run: | - docker buildx build --platform linux/arm64 --cache-from=type=registry,ref=${{ secrets.DOCKER_USERNAME }}/haengdong-backend-dev:cache \ - --cache-to=type=registry,ref=${{ secrets.DOCKER_USERNAME }}/haengdong-backend-dev:cache,mode=max \ - -t ${{ secrets.DOCKER_USERNAME }}/haengdong-backend-dev --push . + docker buildx build --platform linux/arm64 -t \ + ${{ secrets.DOCKER_USERNAME }}/haengdong-backend-dev --push . deploy: needs: build @@ -79,4 +68,4 @@ jobs: run: sudo docker pull ${{ secrets.DOCKER_USERNAME }}/haengdong-backend-dev - name: Docker run - run: sudo docker run -d -p 8080:8080 --name haengdong-backend-dev haengdong/haengdong-backend-dev + run: sudo docker run -d -p 80:8080 --name haengdong-backend-dev haengdong/haengdong-backend-dev diff --git a/server/src/main/java/server/haengdong/HaengdongApplication.java b/server/src/main/java/server/haengdong/HaengdongApplication.java index be22f61d0..31b6e46e7 100644 --- a/server/src/main/java/server/haengdong/HaengdongApplication.java +++ b/server/src/main/java/server/haengdong/HaengdongApplication.java @@ -8,8 +8,6 @@ public class HaengdongApplication { public static void main(String[] args) { SpringApplication.run(HaengdongApplication.class, args); - - } - + } From b0f0ffa14edda42c7f846b5ff59ba267226f3384 Mon Sep 17 00:00:00 2001 From: Juhwan Kim <13selfesteem91@naver.com> Date: Fri, 26 Jul 2024 13:51:32 +0900 Subject: [PATCH 10/15] =?UTF-8?q?refactor:=20=EC=95=A0=ED=94=8C=EB=A6=AC?= =?UTF-8?q?=EC=BC=80=EC=9D=B4=EC=85=98=20=EB=8F=84=EC=BB=A4=20=ED=8F=AC?= =?UTF-8?q?=ED=8A=B8=20=EB=B2=88=ED=98=B8=20=EC=88=98=EC=A0=95=20(#134)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/backend-push.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/backend-push.yml b/.github/workflows/backend-push.yml index a35eaea7b..b22b20aa7 100644 --- a/.github/workflows/backend-push.yml +++ b/.github/workflows/backend-push.yml @@ -68,4 +68,4 @@ jobs: run: sudo docker pull ${{ secrets.DOCKER_USERNAME }}/haengdong-backend-dev - name: Docker run - run: sudo docker run -d -p 80:8080 --name haengdong-backend-dev haengdong/haengdong-backend-dev + run: sudo docker run -d -p 8080:8080 --name haengdong-backend-dev haengdong/haengdong-backend-dev From d056d28b0e8e2d4c19466f51088c897ccdf66d6f Mon Sep 17 00:00:00 2001 From: kunsanglee <85242378+kunsanglee@users.noreply.github.com> Date: Fri, 26 Jul 2024 13:59:06 +0900 Subject: [PATCH 11/15] =?UTF-8?q?refactor:=20Docker=20=EB=B9=8C=EB=93=9C?= =?UTF-8?q?=20=EC=84=B1=EB=8A=A5=20=EA=B0=9C=EC=84=A0=20(#138)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/backend-push.yml | 2 +- server/Dockerfile | 10 +--------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/.github/workflows/backend-push.yml b/.github/workflows/backend-push.yml index b22b20aa7..be973a6a9 100644 --- a/.github/workflows/backend-push.yml +++ b/.github/workflows/backend-push.yml @@ -35,7 +35,7 @@ jobs: uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 - name: Test with Gradle Wrapper - run: ./gradlew test + run: ./gradlew clean build - name: Login to Docker Hub uses: docker/login-action@v3 diff --git a/server/Dockerfile b/server/Dockerfile index 66a2d0ab3..37e37d237 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -1,16 +1,8 @@ -FROM gradle:7.6.1-jdk17 AS build - -WORKDIR /app - -COPY . /app - -RUN gradle clean build -x test - FROM openjdk:17-jdk-slim WORKDIR /app -COPY --from=build /app/build/libs/*.jar /app/haengdong-0.0.1-SNAPSHOT.jar +COPY /build/libs/*.jar /app/haengdong-0.0.1-SNAPSHOT.jar EXPOSE 8080 ENTRYPOINT ["java"] From 14be9b3d8ec17289ecf78bd1dbb63485334e9b4a Mon Sep 17 00:00:00 2001 From: Arachne <66822642+Arachneee@users.noreply.github.com> Date: Fri, 26 Jul 2024 14:58:45 +0900 Subject: [PATCH 12/15] =?UTF-8?q?refactor:=20=EC=B0=B8=EC=97=AC=EC=9E=90?= =?UTF-8?q?=20=EC=A0=95=EC=82=B0=20=ED=98=84=ED=99=A9=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=20=EB=A6=AC=ED=8E=99=ED=86=A0=EB=A7=81=20(#110)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: 참여자 정산 현황 로직 수정 * refactor: forEach -> stream 변경 --- .../haengdong/application/ActionService.java | 13 +++---- .../domain/action/CurrentMembers.java | 39 ++++++++++++++++--- .../haengdong/domain/action/MemberAction.java | 4 ++ ...BillReports.java => MemberBillReport.java} | 36 +++++++---------- .../domain/action/CurrentMembersTest.java | 28 +++++++++++++ ...rtsTest.java => MemberBillReportTest.java} | 6 +-- 6 files changed, 88 insertions(+), 38 deletions(-) rename server/src/main/java/server/haengdong/domain/action/{MemberBillReports.java => MemberBillReport.java} (66%) rename server/src/test/java/server/haengdong/domain/action/{MemberBillReportsTest.java => MemberBillReportTest.java} (89%) diff --git a/server/src/main/java/server/haengdong/application/ActionService.java b/server/src/main/java/server/haengdong/application/ActionService.java index b28374bb9..bda638756 100644 --- a/server/src/main/java/server/haengdong/application/ActionService.java +++ b/server/src/main/java/server/haengdong/application/ActionService.java @@ -1,6 +1,5 @@ package server.haengdong.application; -import java.util.ArrayList; import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -9,7 +8,7 @@ import server.haengdong.domain.action.BillActionRepository; import server.haengdong.domain.action.MemberAction; import server.haengdong.domain.action.MemberActionRepository; -import server.haengdong.domain.action.MemberBillReports; +import server.haengdong.domain.action.MemberBillReport; import server.haengdong.domain.event.Event; import server.haengdong.domain.event.EventRepository; import server.haengdong.exception.HaengdongErrorCode; @@ -29,12 +28,10 @@ public List getMemberBillReports(String token) { List billActions = billActionRepository.findByAction_Event(event); List memberActions = memberActionRepository.findAllByEvent(event); - MemberBillReports memberBillReports = MemberBillReports.createByActions(billActions, memberActions); + MemberBillReport memberBillReport = MemberBillReport.createByActions(billActions, memberActions); - List memberBillReportResponses = new ArrayList<>(); - memberBillReports.getReports().forEach( - (member, price) -> memberBillReportResponses.add(new MemberBillReportAppResponse(member, price)) - ); - return memberBillReportResponses; + return memberBillReport.getReports().entrySet().stream() + .map(entry -> new MemberBillReportAppResponse(entry.getKey(), entry.getValue())) + .toList(); } } diff --git a/server/src/main/java/server/haengdong/domain/action/CurrentMembers.java b/server/src/main/java/server/haengdong/domain/action/CurrentMembers.java index e6843a404..298a8a965 100644 --- a/server/src/main/java/server/haengdong/domain/action/CurrentMembers.java +++ b/server/src/main/java/server/haengdong/domain/action/CurrentMembers.java @@ -1,19 +1,23 @@ package server.haengdong.domain.action; +import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Set; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -@Getter -@RequiredArgsConstructor(access = AccessLevel.PRIVATE) public class CurrentMembers { private final Set members; + public CurrentMembers() { + this(new HashSet<>()); + } + + private CurrentMembers(Set members) { + this.members = members; + } + public static CurrentMembers of(List memberActions) { List sortedMemberActions = getSortedMemberActions(memberActions); Set members = new HashSet<>(); @@ -34,4 +38,29 @@ private static List getSortedMemberActions(List memb .sorted(Comparator.comparing(MemberAction::getSequence)) .toList(); } + + public CurrentMembers addMemberAction(MemberAction memberAction) { + String memberName = memberAction.getMemberName(); + + Set currentMembers = new HashSet<>(members); + + if (memberAction.isIn()) { + currentMembers.add(memberName); + } else { + currentMembers.remove(memberName); + } + return new CurrentMembers(currentMembers); + } + + public boolean isEmpty() { + return members.isEmpty(); + } + + public int size() { + return members.size(); + } + + public Set getMembers() { + return Collections.unmodifiableSet(members); + } } diff --git a/server/src/main/java/server/haengdong/domain/action/MemberAction.java b/server/src/main/java/server/haengdong/domain/action/MemberAction.java index af340de3f..67387cbde 100644 --- a/server/src/main/java/server/haengdong/domain/action/MemberAction.java +++ b/server/src/main/java/server/haengdong/domain/action/MemberAction.java @@ -43,6 +43,10 @@ public boolean isSameName(String name) { return memberName.equals(name); } + public boolean isIn() { + return status == MemberActionStatus.IN; + } + public boolean isSameStatus(MemberActionStatus memberActionStatus) { return status == memberActionStatus; } diff --git a/server/src/main/java/server/haengdong/domain/action/MemberBillReports.java b/server/src/main/java/server/haengdong/domain/action/MemberBillReport.java similarity index 66% rename from server/src/main/java/server/haengdong/domain/action/MemberBillReports.java rename to server/src/main/java/server/haengdong/domain/action/MemberBillReport.java index aa928d2e4..dbf2b49ac 100644 --- a/server/src/main/java/server/haengdong/domain/action/MemberBillReports.java +++ b/server/src/main/java/server/haengdong/domain/action/MemberBillReport.java @@ -2,33 +2,31 @@ import static java.util.stream.Collectors.toMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.PriorityQueue; -import java.util.Set; import java.util.function.Function; import lombok.Getter; @Getter -public class MemberBillReports { +public class MemberBillReport { private final Map reports; - private MemberBillReports(Map reports) { + private MemberBillReport(Map reports) { this.reports = reports; } - public static MemberBillReports createByActions(List billActions, List memberActions) { + public static MemberBillReport createByActions(List billActions, List memberActions) { PriorityQueue sortedBillActions = new PriorityQueue<>(billActions); PriorityQueue sortedMemberActions = new PriorityQueue<>(memberActions); Map memberBillReports = initReports(memberActions); - Set currentMembers = new HashSet<>(); - + CurrentMembers currentMembers = new CurrentMembers(); while (!sortedBillActions.isEmpty() && !sortedMemberActions.isEmpty()) { if (isMemberActionTurn(sortedMemberActions, sortedBillActions)) { - addMemberAction(sortedMemberActions, currentMembers); + MemberAction memberAction = sortedMemberActions.poll(); + currentMembers = currentMembers.addMemberAction(memberAction); continue; } addBillAction(sortedBillActions, currentMembers, memberBillReports); @@ -38,7 +36,7 @@ public static MemberBillReports createByActions(List billActions, Li addBillAction(sortedBillActions, currentMembers, memberBillReports); } - return new MemberBillReports(memberBillReports); + return new MemberBillReport(memberBillReports); } private static Map initReports(List memberActions) { @@ -58,25 +56,19 @@ private static boolean isMemberActionTurn( return memberAction.getSequence() < billAction.getSequence(); } - private static void addMemberAction(PriorityQueue sortedMemberActions, Set currentMembers) { - MemberAction memberAction = sortedMemberActions.poll(); - String memberName = memberAction.getMemberName(); - if (memberAction.isSameStatus(MemberActionStatus.IN)) { - currentMembers.add(memberName); - return; - } - currentMembers.remove(memberAction.getMemberName()); - } - private static void addBillAction( PriorityQueue sortedBillActions, - Set currentMembers, + CurrentMembers currentMembers, Map memberBillReports ) { BillAction billAction = sortedBillActions.poll(); + if (currentMembers.isEmpty()) { + return; + } + Long pricePerMember = billAction.getPrice() / currentMembers.size(); - for (String currentMember : currentMembers) { - Long price = memberBillReports.getOrDefault(currentMember, 0L) + pricePerMember; + for (String currentMember : currentMembers.getMembers()) { + Long price = memberBillReports.get(currentMember) + pricePerMember; memberBillReports.put(currentMember, price); } } diff --git a/server/src/test/java/server/haengdong/domain/action/CurrentMembersTest.java b/server/src/test/java/server/haengdong/domain/action/CurrentMembersTest.java index 37ddca432..389ca70c1 100644 --- a/server/src/test/java/server/haengdong/domain/action/CurrentMembersTest.java +++ b/server/src/test/java/server/haengdong/domain/action/CurrentMembersTest.java @@ -5,6 +5,7 @@ import static server.haengdong.domain.action.MemberActionStatus.OUT; import java.util.List; +import java.util.Set; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import server.haengdong.domain.event.Event; @@ -27,4 +28,31 @@ void of() { assertThat(currentMembers.getMembers()) .containsExactlyInAnyOrder("망쵸", "웨디"); } + + @DisplayName("인원 변동 액션의 상태가 IN이면 현재 인원에 추가한다.") + @Test + void addMemberAction1() { + CurrentMembers currentMembers = new CurrentMembers(); + Event event = new Event("이벤트", "token"); + MemberAction memberAction = new MemberAction(new Action(event, 1L), "웨디", IN, 1L); + + CurrentMembers addedCurrentMembers = currentMembers.addMemberAction(memberAction); + Set members = addedCurrentMembers.getMembers(); + + assertThat(members).hasSize(1) + .containsExactly("웨디"); + } + + @DisplayName("인원 변동 액션의 상태가 OUT이면 현재 인원에서 제외한다.") + @Test + void addMemberAction2() { + Event event = new Event("이벤트", "token"); + MemberAction memberAction1 = new MemberAction(new Action(event, 1L), "웨디", IN, 1L); + CurrentMembers currentMembers = new CurrentMembers().addMemberAction(memberAction1); + MemberAction memberAction2 = new MemberAction(new Action(event, 1L), "웨디", OUT, 1L); + + CurrentMembers addedCurrentMembers = currentMembers.addMemberAction(memberAction2); + + assertThat(addedCurrentMembers.getMembers()).hasSize(0); + } } diff --git a/server/src/test/java/server/haengdong/domain/action/MemberBillReportsTest.java b/server/src/test/java/server/haengdong/domain/action/MemberBillReportTest.java similarity index 89% rename from server/src/test/java/server/haengdong/domain/action/MemberBillReportsTest.java rename to server/src/test/java/server/haengdong/domain/action/MemberBillReportTest.java index db0e2ff37..9ad27d5bc 100644 --- a/server/src/test/java/server/haengdong/domain/action/MemberBillReportsTest.java +++ b/server/src/test/java/server/haengdong/domain/action/MemberBillReportTest.java @@ -10,7 +10,7 @@ import org.junit.jupiter.api.Test; import server.haengdong.domain.event.Event; -class MemberBillReportsTest { +class MemberBillReportTest { @DisplayName("액션 목록으로 참가자 정산 리포트를 생성한다.") @Test @@ -29,9 +29,9 @@ void createByActions() { new MemberAction(new Action(event, 5L), "감자", OUT, 2L) ); - MemberBillReports memberBillReports = MemberBillReports.createByActions(billActions, memberActions); + MemberBillReport memberBillReport = MemberBillReport.createByActions(billActions, memberActions); - assertThat(memberBillReports.getReports()) + assertThat(memberBillReport.getReports()) .containsAllEntriesOf( Map.of( "감자", 20_000L, From 897746625bdaca74850137c765041ff4c7da4858 Mon Sep 17 00:00:00 2001 From: TaehunLee <85233397+Todari@users.noreply.github.com> Date: Sat, 27 Jul 2024 22:03:58 +0900 Subject: [PATCH 13/15] =?UTF-8?q?chore:=20storybook=20chromatic=20?= =?UTF-8?q?=EB=B0=B0=ED=8F=AC=20(#81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: storybook chromatic workfloe * style: EOL 제거 * chore: storybook 배포를 위한 workflow 설정 * chore: storybook 배포를 위한 workflow 설정 * chore: workflow node 설정 추가 * chore: lint flow path 변경 * chore: run lint 수정 * chore: eslint-config-prettier 추가 * chore: lint 적용 * chore: airbnb 제거 * chore: eslint 설정 수정 * chore: chromatic working directory 변경 --- .github/workflows/design-pull-request.yml | 61 +++ HDesign/eslint.config.mjs | 49 ++- HDesign/package-lock.json | 355 ++++++++---------- HDesign/package.json | 4 +- .../src/components/BillItem/BillItem.style.ts | 3 +- HDesign/src/components/BillItem/BillItem.tsx | 10 +- .../DragHandleItem/DragHandleItem.style.ts | 3 +- .../DragHandleItem/DragHandleItem.tsx | 12 +- .../ExpenseList/ExpenseList.style.ts | 3 +- .../components/ExpenseList/ExpenseList.tsx | 12 +- HDesign/src/components/Flex/Flex.tsx | 8 +- .../src/components/IconButton/IconButton.tsx | 1 - .../components/InOutItem/InOutItem.style.ts | 3 +- .../src/components/InOutItem/InOutItem.tsx | 8 +- HDesign/src/components/Input/Input.style.ts | 1 + .../src/components/Search/Search.stories.tsx | 5 +- HDesign/src/components/Search/Search.style.ts | 3 +- HDesign/src/components/Search/Search.tsx | 10 +- .../src/components/StepItem/StepItem.style.ts | 3 +- HDesign/src/components/StepItem/StepItem.tsx | 6 +- HDesign/src/components/Switch/Switch.tsx | 5 +- HDesign/src/components/Tab/Tab.stories.tsx | 3 +- HDesign/src/components/Tab/Tab.style.ts | 3 +- HDesign/src/components/Tab/Tab.tsx | 3 +- .../components/TextButton/TextButton.style.ts | 4 +- .../src/components/TextButton/TextButton.tsx | 7 +- .../src/components/TopNav/TopNav.stories.ts | 1 + HDesign/src/components/TopNav/TopNav.tsx | 10 +- HDesign/src/layouts/MainLayout.tsx | 5 +- 29 files changed, 339 insertions(+), 262 deletions(-) create mode 100644 .github/workflows/design-pull-request.yml diff --git a/.github/workflows/design-pull-request.yml b/.github/workflows/design-pull-request.yml new file mode 100644 index 000000000..4d54e8b39 --- /dev/null +++ b/.github/workflows/design-pull-request.yml @@ -0,0 +1,61 @@ +name: Storybook Deployment +run-name: ${{ github.actor }}의 스토리북 배포 +on: + pull_request: + branches: + - develop + paths: + - 'HDesign/**' + +jobs: + storybook: + runs-on: ubuntu-latest + outputs: + status: ${{ job.status }} + + defaults: + run: + shell: bash + working-directory: ./HDesign + + steps: + - name: checkout repository + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: cache dependencies + id: cache + uses: actions/cache@v3 + with: + path: "**/node_modules" + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}-storybook + + - name: Set up Node.js + uses: actions/setup-node@v2 + with: + node-version: '20.15.1' + + - name: depedency install + if: steps.cache.outputs.cache-hit != 'true' + run: npm ci + + - name: run lint + working-directory: ./HDesign + run: npm run lint + + - name: publish to chromatic + working-directory: ./HDesign + id: chromatic + uses: chromaui/action@v1 + with: + projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} + token: ${{ secrets.GITHUB_TOKEN }} + + - name: comment PR + uses: thollander/actions-comment-pull-request@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + message: "🚀storybook: ${{ steps.chromatic.outputs.storybookUrl }}" + \ No newline at end of file diff --git a/HDesign/eslint.config.mjs b/HDesign/eslint.config.mjs index b6040f04e..96b3da00a 100644 --- a/HDesign/eslint.config.mjs +++ b/HDesign/eslint.config.mjs @@ -21,8 +21,6 @@ const compat = new FlatCompat({ export default [ ...fixupConfigRules( compat.extends( - 'airbnb', - 'airbnb/hooks', 'eslint:recommended', 'plugin:react/recommended', 'plugin:@typescript-eslint/recommended', @@ -64,12 +62,38 @@ export default [ }, rules: { + 'no-use-before-define': 0, 'prettier/prettier': 'error', 'react/react-in-jsx-scope': 'off', 'react/prop-types': 'off', - 'react/jsx-uses-vars': 'error', - '@typescript-eslint/no-use-before-define': ['error'], - '@typescript-eslint/explicit-module-boundary-types': 'error', + 'import/prefer-default-export': 0, + 'import/no-named-as-default': 0, + 'import/namespace': 0, + 'import/extensions': 0, + 'import/no-cycle': 0, + 'react/no-unknown-property': 0, + 'react/jsx-filename-extension': [1, {extensions: ['.ts', '.tsx']}], + 'react/function-component-definition': 0, + 'react/jsx-props-no-spreading': 0, + 'react/jsx-key': 0, + 'react/button-has-type': 'off', + 'no-shadow': 0, + 'no-console': 0, + 'no-alert': 0, + 'react/no-children-prop': 'off', + 'react/no-array-index-key': 'off', + 'react-hooks/exhaustive-deps': 'off', + 'react-hooks/rules-of-hooks': 'off', + 'react/jsx-no-useless-fragment': 'off', + 'react/jsx-no-constructed-context-values': 'off', + 'jsx-a11y/click-events-have-key-events': 'off', + 'jsx-a11y/no-static-element-interactions': 'off', + + '@typescript-eslint/no-unused-vars': 0, + + // 'react/jsx-uses-vars': 'error', + // '@typescript-eslint/no-use-before-define': ['error'], + // '@typescript-eslint/explicit-module-boundary-types': 'error', 'import/order': [ 'error', @@ -89,6 +113,11 @@ export default [ group: 'internal', position: 'after', }, + { + pattern: '@layouts/*', + group: 'internal', + position: 'after', + }, { pattern: '@assets/*', group: 'internal', @@ -104,6 +133,16 @@ export default [ group: 'internal', position: 'after', }, + { + pattern: '@types/*', + group: 'internal', + position: 'after', + }, + { + pattern: '@utils/*', + group: 'internal', + position: 'after', + }, ], }, ], diff --git a/HDesign/package-lock.json b/HDesign/package-lock.json index 9a18cd5ff..d6b1de8f4 100644 --- a/HDesign/package-lock.json +++ b/HDesign/package-lock.json @@ -32,8 +32,8 @@ "@types/react-dom": "^18.3.0", "@typescript-eslint/eslint-plugin": "^7.16.0", "@typescript-eslint/parser": "^7.16.0", - "eslint": "^8.57.0", - "eslint-config-airbnb": "^19.0.4", + "eslint": "^9.7.0", + "eslint-config-prettier": "^9.1.0", "eslint-import-resolver-typescript": "^3.6.1", "eslint-plugin-import": "^2.29.1", "eslint-plugin-jsx-a11y": "^6.9.0", @@ -2723,30 +2723,21 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "node_modules/@eslint/config-array": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.17.1.tgz", + "integrity": "sha512-BlYOpej8AQ8Ev9xVqroV7a02JK3SkBAaN9GfMMH9W6Ch8FlQlkjGw4Ir7+FgYwfirivAf4t+GtzuAxqfukmISA==", "dev": true, "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" + "@eslint/object-schema": "^2.1.4", + "debug": "^4.3.1", + "minimatch": "^3.1.2" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "node_modules/@eslint/config-array/node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", @@ -2756,7 +2747,7 @@ "concat-map": "0.0.1" } }, - "node_modules/@eslint/eslintrc/node_modules/debug": { + "node_modules/@eslint/config-array/node_modules/debug": { "version": "4.3.5", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", @@ -2773,22 +2764,7 @@ } } }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "node_modules/@eslint/config-array/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", @@ -2800,49 +2776,36 @@ "node": "*" } }, - "node_modules/@eslint/eslintrc/node_modules/ms": { + "node_modules/@eslint/config-array/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "node_modules/@eslint/eslintrc/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "node_modules/@eslint/eslintrc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", + "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", "dev": true, - "engines": { - "node": ">=10" + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/js": { - "version": "9.7.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.7.0.tgz", - "integrity": "sha512-ChuWDQenef8OSFnvuxv0TCVxEwmu3+hPNKvM9B34qpM0rDRbjL8t5QkQeHHeAfsKQjuH9wS82WeCi1J/owatng==", - "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", - "deprecated": "Use @eslint/config-array instead", - "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" }, - "engines": { - "node": ">=10.10.0" + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", @@ -2852,7 +2815,7 @@ "concat-map": "0.0.1" } }, - "node_modules/@humanwhocodes/config-array/node_modules/debug": { + "node_modules/@eslint/eslintrc/node_modules/debug": { "version": "4.3.5", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", @@ -2869,7 +2832,19 @@ } } }, - "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", @@ -2881,12 +2856,30 @@ "node": "*" } }, - "node_modules/@humanwhocodes/config-array/node_modules/ms": { + "node_modules/@eslint/eslintrc/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "node_modules/@eslint/js": { + "version": "9.7.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.7.0.tgz", + "integrity": "sha512-ChuWDQenef8OSFnvuxv0TCVxEwmu3+hPNKvM9B34qpM0rDRbjL8t5QkQeHHeAfsKQjuH9wS82WeCi1J/owatng==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", + "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -2900,12 +2893,18 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "deprecated": "Use @eslint/object-schema instead", - "dev": true + "node_modules/@humanwhocodes/retry": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz", + "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } }, "node_modules/@jest/schemas": { "version": "29.6.3", @@ -6684,12 +6683,6 @@ "integrity": "sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==", "dev": true }, - "node_modules/confusing-browser-globals": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", - "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", - "dev": true - }, "node_modules/consola": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/consola/-/consola-3.2.3.tgz", @@ -7691,41 +7684,37 @@ } }, "node_modules/eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "version": "9.7.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.7.0.tgz", + "integrity": "sha512-FzJ9D/0nGiCGBf8UXO/IGLTgLVzIxze1zpfA8Ton2mjLovXdAPlYDv+MQDcqj3TmrhAGYfOpz9RfR+ent0AgAw==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", + "@eslint-community/regexpp": "^4.11.0", + "@eslint/config-array": "^0.17.0", + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "9.7.0", "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.3.0", "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", - "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", + "eslint-scope": "^8.0.2", + "eslint-visitor-keys": "^4.0.0", + "espree": "^10.1.0", + "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", + "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", @@ -7739,59 +7728,22 @@ "eslint": "bin/eslint.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-config-airbnb": { - "version": "19.0.4", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-19.0.4.tgz", - "integrity": "sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==", - "dev": true, - "dependencies": { - "eslint-config-airbnb-base": "^15.0.0", - "object.assign": "^4.1.2", - "object.entries": "^1.1.5" - }, - "engines": { - "node": "^10.12.0 || ^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^7.32.0 || ^8.2.0", - "eslint-plugin-import": "^2.25.3", - "eslint-plugin-jsx-a11y": "^6.5.1", - "eslint-plugin-react": "^7.28.0", - "eslint-plugin-react-hooks": "^4.3.0" + "url": "https://eslint.org/donate" } }, - "node_modules/eslint-config-airbnb-base": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz", - "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==", + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", "dev": true, - "dependencies": { - "confusing-browser-globals": "^1.0.10", - "object.assign": "^4.1.2", - "object.entries": "^1.1.5", - "semver": "^6.3.0" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" + "bin": { + "eslint-config-prettier": "bin/cli.js" }, "peerDependencies": { - "eslint": "^7.32.0 || ^8.2.0", - "eslint-plugin-import": "^2.25.2" - } - }, - "node_modules/eslint-config-airbnb-base/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" + "eslint": ">=7.0.0" } }, "node_modules/eslint-import-resolver-node": { @@ -8237,15 +8189,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, "node_modules/eslint/node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -8273,18 +8216,6 @@ } } }, - "node_modules/eslint/node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/eslint/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -8298,16 +8229,28 @@ } }, "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.2.tgz", + "integrity": "sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", + "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -8350,21 +8293,6 @@ "node": ">=10.13.0" } }, - "node_modules/eslint/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/eslint/node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -8428,30 +8356,30 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "node_modules/espree": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.1.0.tgz", + "integrity": "sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==", "dev": true, + "dependencies": { + "acorn": "^8.12.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.0.0" + }, "engines": { - "node": ">=10" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/eslint" } }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", + "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", "dev": true, - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -8692,15 +8620,28 @@ } }, "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "dependencies": { - "flat-cache": "^3.0.4" + "flat-cache": "^4.0.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16.0.0" + } + }, + "node_modules/file-entry-cache/node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" } }, "node_modules/file-loader": { diff --git a/HDesign/package.json b/HDesign/package.json index 42f857a3e..b077aa3ba 100644 --- a/HDesign/package.json +++ b/HDesign/package.json @@ -36,8 +36,8 @@ "@types/react-dom": "^18.3.0", "@typescript-eslint/eslint-plugin": "^7.16.0", "@typescript-eslint/parser": "^7.16.0", - "eslint": "^8.57.0", - "eslint-config-airbnb": "^19.0.4", + "eslint": "^9.7.0", + "eslint-config-prettier": "^9.1.0", "eslint-import-resolver-typescript": "^3.6.1", "eslint-plugin-import": "^2.29.1", "eslint-plugin-jsx-a11y": "^6.9.0", diff --git a/HDesign/src/components/BillItem/BillItem.style.ts b/HDesign/src/components/BillItem/BillItem.style.ts index dceb9af92..54531e095 100644 --- a/HDesign/src/components/BillItem/BillItem.style.ts +++ b/HDesign/src/components/BillItem/BillItem.style.ts @@ -1,6 +1,7 @@ -import {Theme} from '@/theme/theme.type'; import {css} from '@emotion/react'; +import {Theme} from '@/theme/theme.type'; + export const textStyle = (theme: Theme) => css({ color: theme.colors.black, diff --git a/HDesign/src/components/BillItem/BillItem.tsx b/HDesign/src/components/BillItem/BillItem.tsx index 0823d8add..bf5d916cd 100644 --- a/HDesign/src/components/BillItem/BillItem.tsx +++ b/HDesign/src/components/BillItem/BillItem.tsx @@ -1,12 +1,16 @@ /** @jsxImportSource @emotion/react */ import React from 'react'; -import {useTheme} from '@theme/HDesignProvider'; -import {BillItemProps} from './BillItem.type'; -import {textStyle} from './BillItem.style'; + import Text from '@components/Text/Text'; + +import {useTheme} from '@theme/HDesignProvider'; + import DragHandleItem from '../DragHandleItem/DragHandleItem'; import Flex from '../Flex/Flex'; +import {BillItemProps} from './BillItem.type'; +import {textStyle} from './BillItem.style'; + export const BillItem: React.FC = ({ name = '', price = 0, diff --git a/HDesign/src/components/DragHandleItem/DragHandleItem.style.ts b/HDesign/src/components/DragHandleItem/DragHandleItem.style.ts index a08dfc574..972050619 100644 --- a/HDesign/src/components/DragHandleItem/DragHandleItem.style.ts +++ b/HDesign/src/components/DragHandleItem/DragHandleItem.style.ts @@ -1,6 +1,7 @@ +import {css} from '@emotion/react'; + import {Theme} from '@/theme/theme.type'; import {ColorKeys} from '@/token/colors'; -import {css} from '@emotion/react'; export const dragHandleItemStyle = (theme: Theme, hasDragHandle: boolean, backgroundColor: ColorKeys) => css({ diff --git a/HDesign/src/components/DragHandleItem/DragHandleItem.tsx b/HDesign/src/components/DragHandleItem/DragHandleItem.tsx index 8ae6aeb8c..9c92a6c8f 100644 --- a/HDesign/src/components/DragHandleItem/DragHandleItem.tsx +++ b/HDesign/src/components/DragHandleItem/DragHandleItem.tsx @@ -1,12 +1,14 @@ /** @jsxImportSource @emotion/react */ import React from 'react'; +import {StrictPropsWithChildren} from '@/types/strictPropsWithChildren'; +import {COLORS, ColorKeys} from '@/token/colors'; + import {useTheme} from '@theme/HDesignProvider'; import IconButton from '../IconButton/IconButton'; -import {StrictPropsWithChildren} from '@/types/strictPropsWithChildren'; + import {dragHandleItemStyle, prefixStyle} from './DragHandleItem.style'; -import {COLORS, ColorKeys} from '@/token/colors'; interface DragHandleItemCustomProps { hasDragHandle?: boolean; @@ -15,12 +17,12 @@ interface DragHandleItemCustomProps { export type DragHandleItemProps = React.ComponentProps<'div'> & DragHandleItemCustomProps; -export const DragHandleItem = ({ +export function DragHandleItem({ hasDragHandle = false, backgroundColor = 'white', children, ...htmlProps -}: StrictPropsWithChildren) => { +}: StrictPropsWithChildren) { const {theme} = useTheme(); // TODO: (@toari) : 사람 수 많을 때 UX writing 처리 @@ -32,5 +34,5 @@ export const DragHandleItem = ({ ); -}; +} export default DragHandleItem; diff --git a/HDesign/src/components/ExpenseList/ExpenseList.style.ts b/HDesign/src/components/ExpenseList/ExpenseList.style.ts index 30d71de79..58c8ba7f5 100644 --- a/HDesign/src/components/ExpenseList/ExpenseList.style.ts +++ b/HDesign/src/components/ExpenseList/ExpenseList.style.ts @@ -1,6 +1,7 @@ -import {Theme} from '@/theme/theme.type'; import {css} from '@emotion/react'; +import {Theme} from '@/theme/theme.type'; + export const expenseItemStyle = () => css({ display: 'flex', diff --git a/HDesign/src/components/ExpenseList/ExpenseList.tsx b/HDesign/src/components/ExpenseList/ExpenseList.tsx index 0c1b8a771..8dbc71858 100644 --- a/HDesign/src/components/ExpenseList/ExpenseList.tsx +++ b/HDesign/src/components/ExpenseList/ExpenseList.tsx @@ -1,14 +1,16 @@ /** @jsxImportSource @emotion/react */ -import {useTheme} from '@theme/HDesignProvider'; import Text from '@components/Text/Text'; + import {Arrow} from '@assets/index'; +import {useTheme} from '@theme/HDesignProvider'; + import {ExpenseItemProps, ExpenseListProps} from './ExpenseList.type'; import {expenseItemStyle, expenseListStyle, expenseItemLeftStyle, TextStyle} from './ExpenseList.style'; // TODO: (@soha) 따로 파일 분리할까 고민중.. 여기서만 사용할 것 같긴 한데.. 흠 -const ExpenseItem = ({name, price}: ExpenseItemProps) => { +function ExpenseItem({name, price}: ExpenseItemProps) { const {theme} = useTheme(); return ( ); -}; +} -const ExpenseList = ({expenseList = []}: ExpenseListProps) => { +function ExpenseList({expenseList = []}: ExpenseListProps) { const {theme} = useTheme(); return (
@@ -32,6 +34,6 @@ const ExpenseList = ({expenseList = []}: ExpenseListProps) => { ))}
); -}; +} export default ExpenseList; diff --git a/HDesign/src/components/Flex/Flex.tsx b/HDesign/src/components/Flex/Flex.tsx index eb2530b31..0210669cc 100644 --- a/HDesign/src/components/Flex/Flex.tsx +++ b/HDesign/src/components/Flex/Flex.tsx @@ -1,7 +1,9 @@ /** @jsxImportSource @emotion/react */ +import {css} from '@emotion/react'; + import {StrictPropsWithChildren} from '@/types/strictPropsWithChildren'; import {changeCamelCaseToKebabCase} from '@/utils/ return str.replace(/([a-z])([A-Z])/changeCamelCaseToKebabCase'; -import {css} from '@emotion/react'; + import {FlexDirectionStrictType, FlexProps} from './Flex.type'; const flexStyle = ({ @@ -32,8 +34,8 @@ const flexStyle = ({ }); // TODO: (@weadie) 지정된 프롭 말고 다른 프롭도 가져올 수 있게 하자. -const Flex = ({children, ...props}: StrictPropsWithChildren) => { +function Flex({children, ...props}: StrictPropsWithChildren) { return
{children}
; -}; +} export default Flex; diff --git a/HDesign/src/components/IconButton/IconButton.tsx b/HDesign/src/components/IconButton/IconButton.tsx index 9e19ddf21..66d4c34b8 100644 --- a/HDesign/src/components/IconButton/IconButton.tsx +++ b/HDesign/src/components/IconButton/IconButton.tsx @@ -2,7 +2,6 @@ import {forwardRef} from 'react'; import {IconButtonProps} from '@components/IconButton/IconButton.type'; - import {InputDelete, Plus, Buljusa} from '@/assets'; const ICON = { diff --git a/HDesign/src/components/InOutItem/InOutItem.style.ts b/HDesign/src/components/InOutItem/InOutItem.style.ts index b14dedae8..3137d1908 100644 --- a/HDesign/src/components/InOutItem/InOutItem.style.ts +++ b/HDesign/src/components/InOutItem/InOutItem.style.ts @@ -1,6 +1,7 @@ -import {Theme} from '@/theme/theme.type'; import {css} from '@emotion/react'; +import {Theme} from '@/theme/theme.type'; + export const inOutItemStyle = (theme: Theme) => css({ display: 'flex', diff --git a/HDesign/src/components/InOutItem/InOutItem.tsx b/HDesign/src/components/InOutItem/InOutItem.tsx index 37fd8bbd5..7cc99622f 100644 --- a/HDesign/src/components/InOutItem/InOutItem.tsx +++ b/HDesign/src/components/InOutItem/InOutItem.tsx @@ -1,14 +1,16 @@ /** @jsxImportSource @emotion/react */ import React from 'react'; +import Text from '@components/Text/Text'; + import {useTheme} from '@theme/HDesignProvider'; -import {InOutItemProps} from './InOutItem.type'; -import {prefixStyle, textStyle} from './InOutItem.style'; -import Text from '@components/Text/Text'; import IconButton from '../IconButton/IconButton'; import DragHandleItem from '../DragHandleItem/DragHandleItem'; +import {InOutItemProps} from './InOutItem.type'; +import {prefixStyle, textStyle} from './InOutItem.style'; + export const InOutItem: React.FC = ({ names = [], inOutType = 'out', diff --git a/HDesign/src/components/Input/Input.style.ts b/HDesign/src/components/Input/Input.style.ts index be05abb5b..aeedfeb50 100644 --- a/HDesign/src/components/Input/Input.style.ts +++ b/HDesign/src/components/Input/Input.style.ts @@ -1,6 +1,7 @@ import {css} from '@emotion/react'; import {Theme} from '@theme/theme.type'; + import {InputType} from './Input.type'; const inputBoxBackgroundColorByInputType = (theme: Theme, inputType: InputType = 'input') => { diff --git a/HDesign/src/components/Search/Search.stories.tsx b/HDesign/src/components/Search/Search.stories.tsx index 40f7df4b1..d52f74053 100644 --- a/HDesign/src/components/Search/Search.stories.tsx +++ b/HDesign/src/components/Search/Search.stories.tsx @@ -1,6 +1,7 @@ -import React from 'react'; import type {Meta, StoryObj} from '@storybook/react'; +import React from 'react'; + import Search from '@components/Search/Search'; const meta = { @@ -23,7 +24,7 @@ const meta = { disabled: false, placeholder: 'placeholder', searchTerms: ['todari', 'cookie'], - setKeyword: keyword => console.log(keyword), + setKeyword: (keyword: string) => console.log(keyword), }, } satisfies Meta; diff --git a/HDesign/src/components/Search/Search.style.ts b/HDesign/src/components/Search/Search.style.ts index 413655bd5..230cf3e8e 100644 --- a/HDesign/src/components/Search/Search.style.ts +++ b/HDesign/src/components/Search/Search.style.ts @@ -1,6 +1,7 @@ -import {Theme} from '@/theme/theme.type'; import {css} from '@emotion/react'; +import {Theme} from '@/theme/theme.type'; + export const searchStyle = css({ position: 'relative', diff --git a/HDesign/src/components/Search/Search.tsx b/HDesign/src/components/Search/Search.tsx index 68fc02209..cecdffa52 100644 --- a/HDesign/src/components/Search/Search.tsx +++ b/HDesign/src/components/Search/Search.tsx @@ -1,11 +1,13 @@ /** @jsxImportSource @emotion/react */ +import Flex from '@components/Flex/Flex'; + +import {useTheme} from '@theme/HDesignProvider'; + import Input from '../Input/Input'; import {InputProps} from '../Input/Input.type'; -import {searchStyle, searchTermsStyle} from './Search.style'; + +import {searchStyle, searchTermsStyle, searchTermStyle} from './Search.style'; import useSearch from './useSearch'; -import {searchTermStyle} from './Search.style'; -import {useTheme} from '@theme/HDesignProvider'; -import Flex from '@components/Flex/Flex'; export interface SearchProps extends InputProps { searchTerms: string[]; diff --git a/HDesign/src/components/StepItem/StepItem.style.ts b/HDesign/src/components/StepItem/StepItem.style.ts index 0cf47c7bb..0258b47a4 100644 --- a/HDesign/src/components/StepItem/StepItem.style.ts +++ b/HDesign/src/components/StepItem/StepItem.style.ts @@ -1,6 +1,7 @@ -import {Theme} from '@/theme/theme.type'; import {css} from '@emotion/react'; +import {Theme} from '@/theme/theme.type'; + export const stepItemStyle = (theme: Theme) => css({ display: 'flex', diff --git a/HDesign/src/components/StepItem/StepItem.tsx b/HDesign/src/components/StepItem/StepItem.tsx index c85fe61bc..6f096db12 100644 --- a/HDesign/src/components/StepItem/StepItem.tsx +++ b/HDesign/src/components/StepItem/StepItem.tsx @@ -1,12 +1,14 @@ /** @jsxImportSource @emotion/react */ import {useTheme} from '@/theme/HDesignProvider'; -import {StepItemCustomProps} from './StepItem.type'; -import {nameStyle, personCountStyle, stepItemStyle, totalAmountStyle, totalTitleStyle} from './StepItem.style'; + import Text from '../Text/Text'; import BillItem from '../BillItem/BillItem'; import {BillItemCustomProps} from '../BillItem/BillItem.type'; import Flex from '../Flex/Flex'; +import {nameStyle, personCountStyle, stepItemStyle, totalAmountStyle, totalTitleStyle} from './StepItem.style'; +import {StepItemCustomProps} from './StepItem.type'; + export const StepItem: React.FC = ({ name = '', personCount = 0, diff --git a/HDesign/src/components/Switch/Switch.tsx b/HDesign/src/components/Switch/Switch.tsx index 3ac505ff6..17118a6fa 100644 --- a/HDesign/src/components/Switch/Switch.tsx +++ b/HDesign/src/components/Switch/Switch.tsx @@ -1,10 +1,11 @@ /** @jsxImportSource @emotion/react */ import TextButton from '../TextButton/TextButton'; + import {switchContainerStyle} from './Switch.style'; import {SwitchProps} from './Switch.type'; import {useSwitch} from './useSwitch'; -const Switch = ({value = '', initialValue, values, onChange}: SwitchProps) => { +function Switch({value = '', initialValue, values, onChange}: SwitchProps) { const {selectedValue, handleClick} = useSwitch({value, initialValue, values, onChange}); return ( @@ -21,6 +22,6 @@ const Switch = ({value = '', initialValue, values, onChange}: SwitchProps) => { ))} ); -}; +} export default Switch; diff --git a/HDesign/src/components/Tab/Tab.stories.tsx b/HDesign/src/components/Tab/Tab.stories.tsx index 06986fec2..e92495e1a 100644 --- a/HDesign/src/components/Tab/Tab.stories.tsx +++ b/HDesign/src/components/Tab/Tab.stories.tsx @@ -1,6 +1,7 @@ -import React from 'react'; import type {Meta, StoryObj} from '@storybook/react'; +import React from 'react'; + import Tab from '@components/Tab/Tab'; const meta = { diff --git a/HDesign/src/components/Tab/Tab.style.ts b/HDesign/src/components/Tab/Tab.style.ts index 2242261b5..b038fadd1 100644 --- a/HDesign/src/components/Tab/Tab.style.ts +++ b/HDesign/src/components/Tab/Tab.style.ts @@ -1,6 +1,7 @@ -import {Theme} from '@/theme/theme.type'; import {css} from '@emotion/react'; +import {Theme} from '@/theme/theme.type'; + export const tabStyle = css({ display: 'flex', flexDirection: 'column', diff --git a/HDesign/src/components/Tab/Tab.tsx b/HDesign/src/components/Tab/Tab.tsx index 313f62086..4981b9e92 100644 --- a/HDesign/src/components/Tab/Tab.tsx +++ b/HDesign/src/components/Tab/Tab.tsx @@ -2,11 +2,12 @@ import {css} from '@emotion/react'; import React, {useState} from 'react'; +import Text from '@components/Text/Text'; + import {useTheme} from '@theme/HDesignProvider'; import {tabListStyle, tabTextStyle, tabStyle, tabItemStyle, indicatorStyle} from './Tab.style'; import {TabsProps} from './Tab.type'; -import Text from '@components/Text/Text'; const Tab: React.FC = ({tabs}) => { const {theme} = useTheme(); diff --git a/HDesign/src/components/TextButton/TextButton.style.ts b/HDesign/src/components/TextButton/TextButton.style.ts index e2b602c62..6777b1eed 100644 --- a/HDesign/src/components/TextButton/TextButton.style.ts +++ b/HDesign/src/components/TextButton/TextButton.style.ts @@ -1,6 +1,8 @@ +import {css} from '@emotion/react'; + import {Theme} from '@/theme/theme.type'; + import {TextColor} from './TextButton.type'; -import {css} from '@emotion/react'; interface TextButtonStyleProps { textColor: TextColor; diff --git a/HDesign/src/components/TextButton/TextButton.tsx b/HDesign/src/components/TextButton/TextButton.tsx index c44b7a5c7..9d857c38d 100644 --- a/HDesign/src/components/TextButton/TextButton.tsx +++ b/HDesign/src/components/TextButton/TextButton.tsx @@ -1,8 +1,11 @@ /** @jsxImportSource @emotion/react */ import {forwardRef} from 'react'; -import {TextButtonProps} from './TextButton.type'; -import Text from '../Text/Text'; + import {useTheme} from '@/theme/HDesignProvider'; + +import Text from '../Text/Text'; + +import {TextButtonProps} from './TextButton.type'; import {textButtonStyle} from './TextButton.style'; export const TextButton: React.FC = forwardRef(function Button( diff --git a/HDesign/src/components/TopNav/TopNav.stories.ts b/HDesign/src/components/TopNav/TopNav.stories.ts index f37845346..f231e8bd7 100644 --- a/HDesign/src/components/TopNav/TopNav.stories.ts +++ b/HDesign/src/components/TopNav/TopNav.stories.ts @@ -1,6 +1,7 @@ import type {Meta, StoryObj} from '@storybook/react'; import TopNav from '@components/TopNav/TopNav'; + import Switch from '../Switch/Switch'; const meta = { diff --git a/HDesign/src/components/TopNav/TopNav.tsx b/HDesign/src/components/TopNav/TopNav.tsx index 75ce368ff..bcfa72260 100644 --- a/HDesign/src/components/TopNav/TopNav.tsx +++ b/HDesign/src/components/TopNav/TopNav.tsx @@ -1,12 +1,14 @@ import {useNavigate} from 'react-router-dom'; + import TextButton from '../TextButton/TextButton'; +import Switch from '../Switch/Switch'; + import {topNavStyle} from './TopNav.style'; import {TopNavProps} from './TopNav.type'; -import Switch from '../Switch/Switch'; // TODO: (@todari) navigation으로 인해 storybook 동작하지 않는 오류 해결해야함 // + 페이지 정하는 것에 따라, navigate 경로 수정해 줘야 함 -const TopNav = ({navType}: TopNavProps) => { +function TopNav({navType}: TopNavProps) { const navigate = useNavigate(); return (
@@ -15,10 +17,10 @@ const TopNav = ({navType}: TopNavProps) => { 뒤로가기 ) : ( - navigate('./')}> + navigate('./')} /> )}
); -}; +} export default TopNav; diff --git a/HDesign/src/layouts/MainLayout.tsx b/HDesign/src/layouts/MainLayout.tsx index 825aca175..10032b4d8 100644 --- a/HDesign/src/layouts/MainLayout.tsx +++ b/HDesign/src/layouts/MainLayout.tsx @@ -1,12 +1,13 @@ import {PropsWithChildren} from 'react'; + import {Flex} from '..'; interface MainLayoutInterface extends PropsWithChildren {} -export const MainLayout = ({children}: MainLayoutInterface) => { +export function MainLayout({children}: MainLayoutInterface) { return ( {children} ); -}; +} From e443f3b54dc7635785492b69cf8d93143a94e661 Mon Sep 17 00:00:00 2001 From: Soyeon Choe <77609591+soi-ha@users.noreply.github.com> Date: Sat, 27 Jul 2024 23:08:49 +0900 Subject: [PATCH 14/15] =?UTF-8?q?feat:=202=EC=B0=A8=20=EC=8A=A4=ED=94=84?= =?UTF-8?q?=EB=A6=B0=ED=8A=B8=20API=20=EC=97=B0=EA=B2=B0=20(#137)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: storybook 관련 dependency 설치 * feat: post api에도 response를 받을 수 있도록 수정 * refactor: parameter가 파스칼케이스인 부분을 카멜 케이스로 수정 * feat: 행사명을 가져오는 api 함수 구현 * feat: interface 수정에 따라 body 변경 * feat: 지출 내역을 추가하는 api 연결 * feat: 최초 참여자를 추가하는 api 연결 * feat: 참여자 수를 조정하는 api 연결 * refactor: name -> title로 파라미터명 수정 * feat: 이벤트아이디를 url에서 불러오는 훅 구현 * feat: 지출 내역, 인원 조정 api연결 후 provider로 전파 * feat: url에서 eventId를 받아오도록 추가 * feat: submit이벤트를 form 엘리먼트에 연결 * remove: 사용하지 않는 파일 제거 * feat: useContext를 사용해 총 가격을 불러오도록 연결 * feat: provider를 사용하기 위한 Layout 컴포넌트 추가 * feat: 디자인시스템 수정에 다른 컴포넌트 호출 형태 변경 * chore: 주석 추가 * remove: 사용하지 않는 파일 제거 * feat: router 에 home, admin 경로에서 띄울 컴포넌트 연결 * fix: 변경된 interface에 맞게 body 수정 * feat: 참여자 목록을 넘겨주지 않고, 참여자 타입 전달 * chore: 불필요한 props 삭제 * feat: 공백된 값 제거 * feat: 네비게이션을 위한 구현 * fix: useStepList훅이 context를 반환하도록 수정 * feat: TopNav 추가 * feat: Admin 페이지 구현 * feat: Home 페이지 구현 * chore: 디자인시스템 라이브러리 업데이트 * chore: await 추가 * feat: stepList를 호출하도록 api 연결 * chore: 사용하지 않는 변수 제거 * chore: lint 적용 * feat: steps를 꺼내서 return하도록 수정 * feat: 인원이 있어야 memberNameList를 갱신하도록 로직 작성 * feat: StepList 의 타입 작성 * design: 불필요한 padding 제거 * chore: 관리 탭에서 StepList를 보여주기 위해 임시로 조건문 제거 * feat: 홈 페이지에서 총 지출 금액 표시 * chore: 디자인 시스템 업데이트 * fix: meta tag 설정 - mixed content, scalable 등 * design: 메인 페이지 및 행사 생성 페이지 디자인 수정 * fix: 새로 고침하면 내역이 출력되지 않는 오류 수정 eventId의 변화에 따라 지출 내역을 다시 호출하도록 종속성을 연결하지 않아서 발생한 문제입니다. * chore: Content-Security-Policy 삭제 * fix: FixedButton disabled 속성 추가 * fix: 행사 이름 입력 페이지 FixedButton disalbed 추가 및 공백 제거 * style: lint 적용 * fix: 불필요한 인자를 넘겨주는 것 제거 * chore: 사용되지 않는 import 제거 * fix: 참여자별 지출 내역을 받아오는 api의 엔드포인트 올바르게 수정 * fix: eventId, 전체 검색 결과에 따라 검색 결과가 보여지도록 수정 * design: 이벤트 홈 타이틀과 탭 사이 공백 제거 * feat: 임시로 행사 이름을 표시하도록 수정 * style: 사용하지 않는 변수 및 import 제거 * design: 전역 스크롤바 숨김 처리 * design: 바텀 버튼만큼 contents 위로 올라오도록 변경 * rename: steList 타입 파일 useStepList 폴더로 이동 후 type.ts로 이름 변경 * chore: 디자인시스템 버전 업데이트 * feat: 검색창 placeholder 참여자 이름 추가 --------- Co-authored-by: 이태훈 Co-authored-by: 김진호 Co-authored-by: pakxe --- client/index.html | 3 +- client/package-lock.json | 8 +- client/package.json | 2 +- client/src/apis/fetcher.ts | 16 ++- client/src/apis/request/bill.ts | 8 +- client/src/apis/request/event.ts | 29 +++-- client/src/apis/request/member.ts | 14 ++- client/src/apis/request/report.ts | 4 +- client/src/apis/request/stepList.ts | 8 +- client/src/apis/requestPostEvent.ts | 2 +- .../MemberReportList/MemberReportList.tsx | 6 +- .../SetActionModalContent.tsx | 21 +--- .../SetActionModalContent/SetPurchase.tsx | 16 ++- .../UpdateParticipants.tsx | 24 ++--- .../SetInitialParticipants.tsx | 16 ++- client/src/components/StepList/StepList.tsx | 11 +- client/src/constants/routerUrls.ts | 2 +- client/src/hooks/useDynamicInputPairs.tsx | 17 +-- client/src/hooks/useEventId/useEventId.tsx | 25 +++++ client/src/hooks/useNavSwitch.tsx | 37 +++++++ .../useSearchMemberReportList.tsx | 22 ++-- .../stepList.ts => hooks/useStepList/type.ts} | 16 +-- client/src/hooks/useStepList/useStepList.tsx | 91 ++++++++++++---- client/src/index.css | 5 + client/src/pages/Create/Complete.tsx | 13 +-- client/src/pages/Create/Name.tsx | 32 ++++-- client/src/pages/CreateEvent/CreateEvent.tsx | 27 ----- .../{Event.style.ts => Admin/Admin.style.ts} | 1 + client/src/pages/Event/Admin/Admin.tsx | 97 +++++++++++++++++ client/src/pages/Event/Admin/index.ts | 1 + client/src/pages/Event/Event.tsx | 102 ------------------ client/src/pages/Event/EventLayout.tsx | 23 ++++ client/src/pages/Event/Home/Home.tsx | 40 +++++++ client/src/pages/Event/Home/index.ts | 1 + client/src/pages/Event/index.ts | 2 +- client/src/pages/Home/HomeContent.tsx | 21 ---- client/src/pages/Home/HomeLayout.tsx | 20 ---- client/src/pages/Home/index.ts | 1 - client/src/pages/Main/Main.tsx | 4 +- client/src/router.tsx | 14 +-- client/src/type.d.ts | 18 ++++ 41 files changed, 479 insertions(+), 341 deletions(-) create mode 100644 client/src/hooks/useEventId/useEventId.tsx create mode 100644 client/src/hooks/useNavSwitch.tsx rename client/src/{types/stepList.ts => hooks/useStepList/type.ts} (64%) delete mode 100644 client/src/pages/CreateEvent/CreateEvent.tsx rename client/src/pages/Event/{Event.style.ts => Admin/Admin.style.ts} (85%) create mode 100644 client/src/pages/Event/Admin/Admin.tsx create mode 100644 client/src/pages/Event/Admin/index.ts delete mode 100644 client/src/pages/Event/Event.tsx create mode 100644 client/src/pages/Event/EventLayout.tsx create mode 100644 client/src/pages/Event/Home/Home.tsx create mode 100644 client/src/pages/Event/Home/index.ts delete mode 100644 client/src/pages/Home/HomeContent.tsx delete mode 100644 client/src/pages/Home/HomeLayout.tsx delete mode 100644 client/src/pages/Home/index.ts create mode 100644 client/src/type.d.ts diff --git a/client/index.html b/client/index.html index 05702360a..97edcf3ca 100644 --- a/client/index.html +++ b/client/index.html @@ -2,7 +2,8 @@ - + + 행동대장 diff --git a/client/package-lock.json b/client/package-lock.json index 8bc10061f..91269318a 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -12,7 +12,7 @@ "@emotion/react": "^11.11.4", "@types/dotenv-webpack": "^7.0.7", "dotenv-webpack": "^8.1.0", - "haengdong-design": "^0.1.29", + "haengdong-design": "^0.1.35", "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.24.1" @@ -6274,9 +6274,9 @@ "dev": true }, "node_modules/haengdong-design": { - "version": "0.1.29", - "resolved": "https://registry.npmjs.org/haengdong-design/-/haengdong-design-0.1.29.tgz", - "integrity": "sha512-yBxmS8PBYOMG6scumzeey2LQMSvlB5kqyd/pIP/ID4WRkvwVo8ScFYO6iV8odFINt4I1Dx2aC/itdk74Pyvv4A==", + "version": "0.1.35", + "resolved": "https://registry.npmjs.org/haengdong-design/-/haengdong-design-0.1.35.tgz", + "integrity": "sha512-JhlZgZXzYfCMV57glEOiI+YKGstGAAOhBOKGad6Mugio/vNVUmInf3/eJPEy/10SM9reizYsSCt7eoyj71ZaSA==", "dependencies": { "@emotion/react": "^11.11.4", "@svgr/webpack": "^8.1.0", diff --git a/client/package.json b/client/package.json index 3c66313b4..cb8e9a714 100644 --- a/client/package.json +++ b/client/package.json @@ -47,7 +47,7 @@ "@emotion/react": "^11.11.4", "@types/dotenv-webpack": "^7.0.7", "dotenv-webpack": "^8.1.0", - "haengdong-design": "^0.1.29", + "haengdong-design": "^0.1.35", "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.24.1" diff --git a/client/src/apis/fetcher.ts b/client/src/apis/fetcher.ts index 8df81a2c2..f0bdb9887 100644 --- a/client/src/apis/fetcher.ts +++ b/client/src/apis/fetcher.ts @@ -39,7 +39,7 @@ export const requestGet = async ({headers = {}, ...args}: RequestProps): Prom headers, }); - const data: T = await response.json(); + const data: T = await response!.json(); return data; }; @@ -51,8 +51,16 @@ export const requestPut = ({headers = {}, ...args}: RequestProps) => { return fetcher({method: 'PUT', headers, ...args}); }; -export const requestPost = ({headers = {}, ...args}: RequestProps) => { - return fetcher({method: 'POST', headers, ...args}); +export const requestPost = async ({headers = {}, ...args}: RequestProps): Promise => { + const response = await fetcher({method: 'POST', headers, ...args}); + + const contentType = response!.headers.get('Content-Type'); + if (contentType && contentType.includes('application/json')) { + const data: T = await response!.json(); + return data; + } + + return; }; export const requestDelete = ({headers = {}, ...args}: RequestProps) => { @@ -98,8 +106,8 @@ const errorHandler = async (url: string, options: Options) => { return response; } catch (error) { console.error(error); - throw new Error('아 에러났다;; 인생이 행복해질거에요 에러덕분에요ㅎㅎ'); // throw new ErrorWithHeader(errorMessageHeader, getErrorMessage(error)); + return; } }; diff --git a/client/src/apis/request/bill.ts b/client/src/apis/request/bill.ts index 2e364b944..c07fd758a 100644 --- a/client/src/apis/request/bill.ts +++ b/client/src/apis/request/bill.ts @@ -1,20 +1,18 @@ -import {Bill} from 'types/stepList'; - import {BASE_URL} from '@apis/baseUrl'; import {TEMP_PREFIX} from '@apis/tempPrefix'; import {requestPost} from '@apis/fetcher'; import {WithEventId} from '@apis/withEventId.type'; type RequestAddBillList = { - BillList: Bill[]; + billList: Bill[]; }; -export const requestAddBillList = async ({eventId, BillList}: WithEventId) => { +export const requestAddBillList = async ({eventId, billList}: WithEventId) => { await requestPost({ baseUrl: BASE_URL.HD, endpoint: `${TEMP_PREFIX}/${eventId}/actions/bills`, body: { - actions: BillList, + actions: billList, }, }); }; diff --git a/client/src/apis/request/event.ts b/client/src/apis/request/event.ts index f6ff6d56b..5822267b9 100644 --- a/client/src/apis/request/event.ts +++ b/client/src/apis/request/event.ts @@ -1,18 +1,29 @@ -import {StepList} from 'types/stepList'; - -import {BASE_URL} from '@apis/baseUrl'; import {TEMP_PREFIX} from '@apis/tempPrefix'; -import {requestGet} from '@apis/fetcher'; +import {requestGet, requestPost} from '@apis/fetcher'; +import {WithEventId} from '@apis/withEventId.type'; type RequestCreateNewEvent = { - name: string; + eventName: string; +}; + +type ResponseCreateNewEvent = { + eventId: string; }; -export const requestCreateNewEvent = async ({name}: RequestCreateNewEvent) => { +export const requestCreateNewEvent = async ({eventName}: RequestCreateNewEvent) => { // TODO: (@weadie) 뼈대만 둔 것. header값을 꺼내오는 로직이 필요하다. 또는 바디에 달라고 부탁할 수 있다. - return await requestGet({ - baseUrl: BASE_URL.HD, + return requestPost({ endpoint: TEMP_PREFIX, - body: {name}, + body: {eventName}, + }); +}; + +type ResponseGetEventName = { + eventName: string; +}; + +export const requestGetEventName = async ({eventId}: WithEventId) => { + return requestGet({ + endpoint: `${TEMP_PREFIX}/${eventId}`, }); }; diff --git a/client/src/apis/request/member.ts b/client/src/apis/request/member.ts index 252c85e11..d23563f34 100644 --- a/client/src/apis/request/member.ts +++ b/client/src/apis/request/member.ts @@ -1,20 +1,24 @@ -import {Member} from 'types/stepList'; - import {BASE_URL} from '@apis/baseUrl'; import {TEMP_PREFIX} from '@apis/tempPrefix'; import {requestPost} from '@apis/fetcher'; import {WithEventId} from '@apis/withEventId.type'; type RequestUpdateMemberList = { - MemberList: Member[]; + memberNameList: string[]; + type: MemberType; }; -export const requestUpdateMemberList = async ({eventId, MemberList}: WithEventId) => { +export const requestUpdateMemberList = async ({ + eventId, + type, + memberNameList, +}: WithEventId) => { await requestPost({ baseUrl: BASE_URL.HD, endpoint: `${TEMP_PREFIX}/${eventId}/actions/members`, body: { - actions: MemberList, + members: memberNameList, + status: type, }, }); }; diff --git a/client/src/apis/request/report.ts b/client/src/apis/request/report.ts index 9b714752a..faa6c9185 100644 --- a/client/src/apis/request/report.ts +++ b/client/src/apis/request/report.ts @@ -1,5 +1,3 @@ -import {MemberReport} from 'types/stepList'; - import {BASE_URL} from '@apis/baseUrl'; import {TEMP_PREFIX} from '@apis/tempPrefix'; import {requestGet} from '@apis/fetcher'; @@ -12,7 +10,7 @@ type ResponseMemberReportList = { export const requestMemberReportList = async ({eventId}: WithEventId) => { const {reports} = await requestGet({ baseUrl: BASE_URL.HD, - endpoint: `${TEMP_PREFIX}/${eventId}/actions`, + endpoint: `${TEMP_PREFIX}/${eventId}/actions/reports`, }); return reports; diff --git a/client/src/apis/request/stepList.ts b/client/src/apis/request/stepList.ts index 60ad385e4..e09cb67fd 100644 --- a/client/src/apis/request/stepList.ts +++ b/client/src/apis/request/stepList.ts @@ -1,15 +1,15 @@ -import {Bill, Member, StepList} from 'types/stepList'; - import {BASE_URL} from '@apis/baseUrl'; import {TEMP_PREFIX} from '@apis/tempPrefix'; -import {requestGet, requestPost} from '@apis/fetcher'; +import {requestGet} from '@apis/fetcher'; import {WithEventId} from '@apis/withEventId.type'; // TODO: (@weadie) 현재 토큰을 어떻게 관리할지.. 계속 사용되는데 export const requestStepList = async ({eventId}: WithEventId) => { // TODO: (@weadie) response가 어떻게 오는지 안나와서 data로만 써뒀어요. - return await requestGet({ + const {steps} = await requestGet({ baseUrl: BASE_URL.HD, endpoint: `${TEMP_PREFIX}/${eventId}/actions`, }); + + return steps; }; diff --git a/client/src/apis/requestPostEvent.ts b/client/src/apis/requestPostEvent.ts index cf6b4716e..c03ae02af 100644 --- a/client/src/apis/requestPostEvent.ts +++ b/client/src/apis/requestPostEvent.ts @@ -5,7 +5,7 @@ interface RequestPostEventProps { } export const requestPostEvent = async ({name}: RequestPostEventProps) => { - requestPost({ + await requestPost({ headers: {'Content-Type': 'application/json'}, body: {name}, endpoint: '/api/events', diff --git a/client/src/components/MemberReportList/MemberReportList.tsx b/client/src/components/MemberReportList/MemberReportList.tsx index ebf1cf72e..1004540fa 100644 --- a/client/src/components/MemberReportList/MemberReportList.tsx +++ b/client/src/components/MemberReportList/MemberReportList.tsx @@ -5,15 +5,15 @@ import useSearchMemberReportList from '@hooks/useSearchMemberReportList/useSearc const MemberReportList = () => { const [name, setName] = useState(''); - const {memberReportSearchList} = useSearchMemberReportList({name, eventId: '므와아아아'}); + const {memberReportSearchList} = useSearchMemberReportList({name}); const changeName = ({target}: React.ChangeEvent) => { setName(target.value); }; return ( - - + + ); diff --git a/client/src/components/Modal/SetActionModalContent/SetActionModalContent.tsx b/client/src/components/Modal/SetActionModalContent/SetActionModalContent.tsx index 93a917652..b3ccd64c9 100644 --- a/client/src/components/Modal/SetActionModalContent/SetActionModalContent.tsx +++ b/client/src/components/Modal/SetActionModalContent/SetActionModalContent.tsx @@ -1,8 +1,6 @@ import {useState} from 'react'; import {BottomSheet, Switch} from 'haengdong-design'; -import {InOutType, ParticipantType, PurchaseInformation} from '@pages/Event/Event'; - import SetPurchase from './SetPurchase'; import UpdateParticipants from './UpdateParticipants'; import {setActionModalContentStyle, setActionModalContentSwitchContainerStyle} from './SetActionModalContent.style'; @@ -10,29 +8,21 @@ import {setActionModalContentStyle, setActionModalContentSwitchContainerStyle} f export type ActionType = '지출' | '인원'; interface SetActionModalContentProps { - participants: string[]; openBottomSheet: boolean; - setOpenBottomSheet: React.Dispatch>; setOrder: React.Dispatch>; } -const SetActionModalContent = ({ - participants, - openBottomSheet, - - setOpenBottomSheet, - setOrder, -}: SetActionModalContentProps) => { +const SetActionModalContent = ({openBottomSheet, setOpenBottomSheet, setOrder}: SetActionModalContentProps) => { const [action, setAction] = useState('지출'); - const [participantAction, setParticipantAction] = useState('탈주'); + const [inOutAction, setInOutAction] = useState('탈주'); const handleActionTypeChange = (value: string) => { setAction(value as ActionType); }; const handleParticipantTypeChange = (value: string) => { - setParticipantAction(value as InOutType); + setInOutAction(value as InOutType); }; return ( @@ -41,15 +31,14 @@ const SetActionModalContent = ({
{action === '인원' && ( - + )}
{action === '지출' && } {action === '인원' && ( )} diff --git a/client/src/components/Modal/SetActionModalContent/SetPurchase.tsx b/client/src/components/Modal/SetActionModalContent/SetPurchase.tsx index 515c048dd..a83a697a7 100644 --- a/client/src/components/Modal/SetActionModalContent/SetPurchase.tsx +++ b/client/src/components/Modal/SetActionModalContent/SetPurchase.tsx @@ -1,5 +1,7 @@ import {Input, FixedButton} from 'haengdong-design'; +import {useStepList} from '@hooks/useStepList/useStepList'; + import useDynamicInputPairs from '@hooks/useDynamicInputPairs'; import {setPurchaseInputStyle, setPurchaseStyle, setPurchaseInputContainerStyle} from './SetPurchase.style'; @@ -10,11 +12,14 @@ interface SetPurchaseProps { } const SetPurchase = ({setOpenBottomSheet, setOrder}: SetPurchaseProps) => { - const {inputPairs, inputRefs, handleInputChange, handleInputBlur} = useDynamicInputPairs(); + const {inputPairs, inputRefs, handleInputChange, handleInputBlur, getNonEmptyInputPairs} = useDynamicInputPairs(); + const {addBill} = useStepList(); const handleSetPurchaseSubmit = () => { setOrder(prev => prev + 1); - // TODO: (@soha) api 요청시 inputPairs를 보내면 됨 + + // TODO: (@weadie) 요청 실패시 오류 핸들 필요 + addBill(getNonEmptyInputPairs()); setOpenBottomSheet(false); }; @@ -25,8 +30,8 @@ const SetPurchase = ({setOpenBottomSheet, setOrder}: SetPurchaseProps) => {
handleInputChange(index, 'name', e.target.value)} + value={pair.title} + onChange={e => handleInputChange(index, 'title', e.target.value)} onBlur={() => handleInputBlur(index)} placeholder="지출 내역" ref={el => (inputRefs.current[index * 2] = el)} @@ -43,7 +48,8 @@ const SetPurchase = ({setOpenBottomSheet, setOrder}: SetPurchaseProps) => { ))}
diff --git a/client/src/components/Modal/SetActionModalContent/UpdateParticipants.tsx b/client/src/components/Modal/SetActionModalContent/UpdateParticipants.tsx index 72b86faa7..5adc3cd43 100644 --- a/client/src/components/Modal/SetActionModalContent/UpdateParticipants.tsx +++ b/client/src/components/Modal/SetActionModalContent/UpdateParticipants.tsx @@ -1,31 +1,22 @@ import {Input, FixedButton} from 'haengdong-design'; -import {useState} from 'react'; -import {InOutType} from '@pages/Event/Event'; +import {useStepList} from '@hooks/useStepList/useStepList'; import useDynamicInput from '@hooks/useDynamicAdditionalInput'; import {updateParticipantsInputStyle, updateParticipantsStyle} from './UpdateParticipants.style'; interface UpdateParticipantsProps { - participants: string[]; - participantAction: InOutType; + inOutAction: MemberType; setOpenBottomSheet: React.Dispatch>; } -const UpdateParticipants = ({participantAction, participants, setOpenBottomSheet}: UpdateParticipantsProps) => { +const UpdateParticipants = ({inOutAction, setOpenBottomSheet}: UpdateParticipantsProps) => { const {inputs, inputRefs, handleInputChange, handleInputBlur, getNonEmptyInputs} = useDynamicInput(); + const {updateMemberList} = useStepList(); const handleUpdateParticipantsSubmit = () => { - const newParticipants = () => { - if (participantAction === '탈주') { - return participants.filter(participant => !getNonEmptyInputs().includes(participant)); - } else { - return [...participants, ...getNonEmptyInputs()]; - } - }; - - // TODO: (@soha) api 요청시 newParticipants()를 보내면 됨 + updateMemberList({memberNameList: getNonEmptyInputs(), type: inOutAction}); setOpenBottomSheet(false); }; @@ -46,8 +37,9 @@ const UpdateParticipants = ({participantAction, participants, setOpenBottomSheet ))} diff --git a/client/src/components/Modal/SetInitialParticipants/SetInitialParticipants.tsx b/client/src/components/Modal/SetInitialParticipants/SetInitialParticipants.tsx index 15a0b0460..e5dda7c96 100644 --- a/client/src/components/Modal/SetInitialParticipants/SetInitialParticipants.tsx +++ b/client/src/components/Modal/SetInitialParticipants/SetInitialParticipants.tsx @@ -1,5 +1,7 @@ import {Text, Input, BottomSheet, FixedButton} from 'haengdong-design'; +import {useStepList} from '@hooks/useStepList/useStepList'; + import useDynamicInput from '@hooks/useDynamicAdditionalInput'; import {setInitialParticipantsInputGroupStyle, setInitialParticipantsStyle} from './SetInitialParticipants.style'; @@ -7,19 +9,14 @@ import {setInitialParticipantsInputGroupStyle, setInitialParticipantsStyle} from interface SetInitialParticipantsProps { openBottomSheet: boolean; setOpenBottomSheet: React.Dispatch>; - setParticipants: React.Dispatch>; } -const SetInitialParticipants = ({ - openBottomSheet, - setOpenBottomSheet, - setParticipants, -}: SetInitialParticipantsProps) => { +const SetInitialParticipants = ({openBottomSheet, setOpenBottomSheet}: SetInitialParticipantsProps) => { const {inputs, inputRefs, handleInputChange, handleInputBlur, getNonEmptyInputs} = useDynamicInput(); + const {updateMemberList} = useStepList(); const handleSubmit = () => { - setParticipants(getNonEmptyInputs()); - // TODO: (@soha) api 요청시 getNonEmptyInputs() 보낼 형태 생성 + updateMemberList({memberNameList: getNonEmptyInputs(), type: 'IN'}); setOpenBottomSheet(false); }; @@ -42,7 +39,8 @@ const SetInitialParticipants = ({ diff --git a/client/src/components/StepList/StepList.tsx b/client/src/components/StepList/StepList.tsx index 76dedc41b..6e9961f99 100644 --- a/client/src/components/StepList/StepList.tsx +++ b/client/src/components/StepList/StepList.tsx @@ -1,17 +1,22 @@ import {Flex, InOutItem, StepItem} from 'haengdong-design'; import {useStepList} from '@hooks/useStepList/useStepList'; -import {MemberType} from 'types/stepList'; const StepList = () => { const {stepList} = useStepList(); // TODO: (@weadie) if else 구문이 지저분하므로 리펙터링이 필요합니다. return ( - + {stepList.map(step => { if (step.type === 'BILL') { - return ; + return ( + + ); } else if (step.type === 'IN' || step.type === 'OUT') { return name)} />; } else { diff --git a/client/src/constants/routerUrls.ts b/client/src/constants/routerUrls.ts index 228e25c7c..c57ab181e 100644 --- a/client/src/constants/routerUrls.ts +++ b/client/src/constants/routerUrls.ts @@ -2,7 +2,7 @@ export const ROUTER_URLS = { main: '', eventCreateName: '/event/create/name', eventCreateComplete: '/event/create/complete', - event: '/event', + event: '/event', // TODO: (@weadie) baseurl을 어떻게 관리할 것인가? eventManage: '/event/:eventId/admin', home: '/event/:eventId/home', }; diff --git a/client/src/hooks/useDynamicInputPairs.tsx b/client/src/hooks/useDynamicInputPairs.tsx index db5347dd8..b6f86b000 100644 --- a/client/src/hooks/useDynamicInputPairs.tsx +++ b/client/src/hooks/useDynamicInputPairs.tsx @@ -1,12 +1,10 @@ import {useEffect, useRef, useState} from 'react'; -import {PurchaseInformation} from '@pages/Event/Event'; - const useDynamicInputPairs = () => { - const [inputPairs, setInputPairs] = useState([{name: '', price: 0}]); + const [inputPairs, setInputPairs] = useState([{title: '', price: 0}]); const inputRefs = useRef<(HTMLInputElement | null)[]>([]); - const handleInputChange = (index: number, field: 'name' | 'price', value: string) => { + const handleInputChange = (index: number, field: 'title' | 'price', value: string) => { const newInputPairs = [...inputPairs]; newInputPairs[index] = { ...newInputPairs[index], @@ -17,13 +15,17 @@ const useDynamicInputPairs = () => { const handleInputBlur = (index: number) => { const currentPair = inputPairs[index]; - if (currentPair.name.trim() === '' && currentPair.price === 0) { + if (currentPair.title.trim() === '' && currentPair.price === 0) { setInputPairs(prev => prev.filter((_, i) => i !== index)); - } else if (currentPair.name.trim() !== '' && currentPair.price !== 0 && index === inputPairs.length - 1) { - setInputPairs(prev => [...prev, {name: '', price: 0}]); + } else if (currentPair.title.trim() !== '' && currentPair.price !== 0 && index === inputPairs.length - 1) { + setInputPairs(prev => [...prev, {title: '', price: 0}]); } }; + const getNonEmptyInputPairs = () => { + return inputPairs.filter(currentPair => currentPair.title.trim() !== '' && currentPair.price !== 0); + }; + useEffect(() => { if (inputRefs.current.length > 0) { const lastInputPair = inputRefs.current.slice(-2); @@ -33,6 +35,7 @@ const useDynamicInputPairs = () => { return { inputPairs, + getNonEmptyInputPairs, inputRefs, handleInputChange, handleInputBlur, diff --git a/client/src/hooks/useEventId/useEventId.tsx b/client/src/hooks/useEventId/useEventId.tsx new file mode 100644 index 000000000..ca3775f8e --- /dev/null +++ b/client/src/hooks/useEventId/useEventId.tsx @@ -0,0 +1,25 @@ +import {useEffect, useState} from 'react'; +import {useLocation} from 'react-router-dom'; + +const useEventId = () => { + const [eventId, setEventId] = useState(''); + const location = useLocation(); + + const extractIdFromUrl = (url: string) => { + const regex = /\/event\/([a-zA-Z0-9-]+)\//; + const match = url.match(regex); + return match ? match[1] : null; + }; + + const curEventId = extractIdFromUrl(location.pathname) ?? ''; + + useEffect(() => { + setEventId(curEventId); + }, []); + + return { + eventId, + }; +}; + +export default useEventId; diff --git a/client/src/hooks/useNavSwitch.tsx b/client/src/hooks/useNavSwitch.tsx new file mode 100644 index 000000000..7ecd3def8 --- /dev/null +++ b/client/src/hooks/useNavSwitch.tsx @@ -0,0 +1,37 @@ +import {useState} from 'react'; +import {useLocation, useNavigate} from 'react-router-dom'; + +const PATH_TABLE: Record = { + 홈: 'home', + 관리: 'admin', +}; + +const PATH_DISPLAY_TABLE: Record = { + home: '홈', + admin: '관리', +}; + +const useNavSwitch = () => { + const paths = ['홈', '관리']; + const location = useLocation(); + const navigate = useNavigate(); + + const pathArray = location.pathname.split('/'); + const basePath = pathArray.slice(0, -1).join('/'); + const lastPath = pathArray[pathArray.length - 1]; + + const [nav, setNav] = useState(PATH_DISPLAY_TABLE[lastPath]); + + const onChange = (displayName: string) => { + setNav(displayName); + navigate(`${basePath}/${PATH_TABLE[displayName]}`); + }; + + return { + nav, + paths, + onChange, + }; +}; + +export default useNavSwitch; diff --git a/client/src/hooks/useSearchMemberReportList/useSearchMemberReportList.tsx b/client/src/hooks/useSearchMemberReportList/useSearchMemberReportList.tsx index 589317f7d..02c07b80a 100644 --- a/client/src/hooks/useSearchMemberReportList/useSearchMemberReportList.tsx +++ b/client/src/hooks/useSearchMemberReportList/useSearchMemberReportList.tsx @@ -1,41 +1,37 @@ import {useEffect, useState} from 'react'; -import {MemberReport} from 'types/stepList'; import {requestMemberReportList} from '@apis/request/report'; - -import {WithEventId} from '@apis/withEventId.type'; - -import memberReportSearchJsonList from '@mocks/memberReportSearchList.json'; - -const memberReportSearchMockList = memberReportSearchJsonList as MemberReport[]; +import useEventId from '@hooks/useEventId/useEventId'; type UseSearchMemberReportListParams = { name: string; }; -const useSearchMemberReportList = ({name, eventId}: WithEventId) => { - const [memberReportList, setMemberReportList] = useState(memberReportSearchMockList); +const useSearchMemberReportList = ({name}: UseSearchMemberReportListParams) => { + const [memberReportList, setMemberReportList] = useState([]); const [memberReportSearchList, setMemberReportSearchList] = useState([]); + const {eventId} = useEventId(); useEffect(() => { const fetchMemberReportList = async () => { // TODO: (@weadie) cors 고쳐지면 주석 풀게요. - // const memberReportListData = await requestMemberReportList({eventId}); + // TODO: (@weadie) eventId에 의존하는 두 개의 훅에 대한 리펙토링 필요 + if (eventId === '') return; - const memberReportListData = memberReportSearchMockList; + const memberReportListData = await requestMemberReportList({eventId}); setMemberReportList(memberReportListData); }; fetchMemberReportList(); - }, []); + }, [eventId]); // TODO: (@weadie) 글자가 완성될 때마다 아래 로직이 실행되어야 합니다. useEffect(() => { if (name === '') setMemberReportSearchList(memberReportList); setMemberReportSearchList(memberReportList.filter(memberReport => memberReport.name.includes(name))); - }, [name]); + }, [name, memberReportList]); return { memberReportSearchList, diff --git a/client/src/types/stepList.ts b/client/src/hooks/useStepList/type.ts similarity index 64% rename from client/src/types/stepList.ts rename to client/src/hooks/useStepList/type.ts index f106306e9..6d9b78de1 100644 --- a/client/src/types/stepList.ts +++ b/client/src/hooks/useStepList/type.ts @@ -1,11 +1,7 @@ -export type MemberType = 'IN' | 'OUT'; - -// TODO: (@weadie) 준 데이터 형식에서 steps를 빼내 flat하게 사용중. 일관성있게 하는게 좋긴 하나 사용시 번거로움이 있을 거라고 판단. -export type StepList = (MemberStep | BillStep)[]; - export type Step = { type: MemberType | 'BILL'; stepName: string | null; + members: string[]; actions: (BillAction | MemberAction)[]; }; @@ -40,13 +36,3 @@ export type Member = { name: string; status: MemberType; }; - -export type Bill = { - title: string; - price: number; -}; - -export type MemberReport = { - name: string; - price: number; -}; diff --git a/client/src/hooks/useStepList/useStepList.tsx b/client/src/hooks/useStepList/useStepList.tsx index 9adb2d684..52d2a78d6 100644 --- a/client/src/hooks/useStepList/useStepList.tsx +++ b/client/src/hooks/useStepList/useStepList.tsx @@ -1,25 +1,65 @@ -import {PropsWithChildren, createContext, useEffect, useState} from 'react'; +import {PropsWithChildren, createContext, useContext, useEffect, useState} from 'react'; -import {BillAction, MemberType, StepList} from 'types/stepList'; +import useEventId from '@hooks/useEventId/useEventId'; +import {requestAddBillList} from '@apis/request/bill'; +import {requestUpdateMemberList} from '@apis/request/member'; +import {requestStepList} from '@apis/request/stepList'; -import stepListJsonData from '@mocks/stepList.json'; +import {BillAction, BillStep, MemberStep} from './type.ts'; -const stepListMockData = stepListJsonData as StepList; +interface StepListContextProps { + stepList: (BillStep | MemberStep)[]; + getTotalPrice: () => number; + addBill: (billList: Bill[]) => Promise; + updateMemberList: ({type, memberNameList}: {type: MemberType; memberNameList: string[]}) => Promise; + memberNameList: string[]; +} -const useStepList = () => { - const [stepList, setStepList] = useState(stepListMockData); +export const StepListContext = createContext(null); // TODO: (@weadie) 인자를 어떻게 줘야 하는지 고민하기. + +const StepListProvider = ({children}: PropsWithChildren) => { + const [stepList, setStepList] = useState<(BillStep | MemberStep)[]>([]); + const [memberNameList, setNameMemberList] = useState([]); + + const {eventId} = useEventId(); useEffect(() => { + if (eventId === '') return; + + refreshStepList(); + // TODO: (@weadie) useEffect를 꼭 써야하는가? - // 초기 리스트 불러서 setActionList - }, []); + }, [eventId]); + + const refreshStepList = async () => { + const stepList = await requestStepList({eventId}); + + if (stepList.length !== 0) { + setNameMemberList(stepList[stepList.length - 1].members); + } - const updateMemberList = (type: MemberType, memberList: string[]) => { - // api를 호출하고 set한다 + setStepList(stepList); }; - const addBill = (title: string, price: number) => { - // api를 호출하고 set한다. + const updateMemberList = async ({type, memberNameList}: {type: MemberType; memberNameList: string[]}) => { + try { + await requestUpdateMemberList({eventId, type, memberNameList}); + + // TODO: (@weadie) 클라이언트 단에서 멤버 목록을 관리하기 위한 로직. 개선이 필요하다. + if (type === 'IN') setNameMemberList(prev => [...prev, ...memberNameList]); + if (type === 'OUT') setNameMemberList(prev => prev.filter(name => !memberNameList.includes(name))); + + refreshStepList(); + } catch (error) { + alert(error); + } + }; + + const addBill = async (billList: Bill[]) => { + // TODO: (@weadie) 에러 처리 + await requestAddBillList({eventId, billList}); + + refreshStepList(); }; const calculateBillSum = (actions: BillAction[]) => { @@ -35,15 +75,28 @@ const useStepList = () => { }, 0); }; - return {stepList, getTotalPrice}; + return ( + + {children} + + ); }; -const StepListContext = createContext([]); // TODO: (@weadie) 인자를 어떻게 줘야 하는지 고민하기. - -const StepListProvider = ({children}: PropsWithChildren) => { - const {stepList} = useStepList(); +export const useStepList = () => { + const context = useContext(StepListContext); - return {children}; + if (!context) { + throw new Error('useStepList는 StepListProvider 내에서 사용되어야 합니다.'); + } + return context; }; -export {useStepList, StepListProvider}; +export default StepListProvider; diff --git a/client/src/index.css b/client/src/index.css index 8ebdb5ba8..2f63176f6 100644 --- a/client/src/index.css +++ b/client/src/index.css @@ -2,6 +2,11 @@ body { max-width: 768px; height: 100lvh; margin: 0 auto; + + overflow-y: scroll; + &::-webkit-scrollbar { + display: none; + } } section { diff --git a/client/src/pages/Create/Complete.tsx b/client/src/pages/Create/Complete.tsx index b4496d0cd..786979607 100644 --- a/client/src/pages/Create/Complete.tsx +++ b/client/src/pages/Create/Complete.tsx @@ -1,6 +1,6 @@ import {useEffect, useState} from 'react'; import {useLocation, useNavigate} from 'react-router-dom'; -import {FixedButton, MainLayout, Title} from 'haengdong-design'; +import {FixedButton, MainLayout, Title, TopNav} from 'haengdong-design'; import {ROUTER_URLS} from '@constants/routerUrls'; @@ -11,11 +11,12 @@ const CompleteCreateEvent = () => { useEffect(() => { const getUrl = async () => { - const eventTitle = location.search; - // console.log(eventTitle); + // TODO: (@weadie) eventId를 location에서 불러오는 로직 함수로 분리해서 재사용 + const params = new URLSearchParams(location.search); + const eventId = params.get('eventId'); - // const url = await fetch(); - setUrl('hangsapage'); + // TODO: (@weadie) eventId가 없는 경우에 대한 처리 필요 + setUrl(eventId ?? ''); }; getUrl(); @@ -23,7 +24,7 @@ const CompleteCreateEvent = () => { return ( - {/* */} + } /> + <TopNav> + <Back /> + </TopNav> <Title title="행사 이름 입력" description="시작할 행사 이름을 입력해 주세요." /> - <form onSubmit={submitEventTitle}> + <form onSubmit={submitEventName} style={{padding: '0 1rem'}}> <Input - value={eventTitle} - onChange={event => setEventTitle(event.target.value)} + value={eventName} + onChange={event => setEventName(event.target.value)} + onBlur={() => setEventName(eventName.trim())} placeholder="ex) 행동대장 야유회" /> - <FixedButton onClick={() => navigate(ROUTER_URLS.eventCreateComplete)}>행동 개시!</FixedButton> + <FixedButton disabled={!eventName.length}>행동 개시!</FixedButton> </form> </MainLayout> ); diff --git a/client/src/pages/CreateEvent/CreateEvent.tsx b/client/src/pages/CreateEvent/CreateEvent.tsx deleted file mode 100644 index 9697200e0..000000000 --- a/client/src/pages/CreateEvent/CreateEvent.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import {useState} from 'react'; -import {useNavigate} from 'react-router-dom'; - -import {ROUTER_URLS} from '@constants/routerUrls'; - -const CreateEvent = () => { - const [eventTitle, setEventTitle] = useState(''); - const navigate = useNavigate(); - - const submitEventTitle = (event: React.FormEvent<HTMLFormElement>) => { - event.preventDefault(); - navigate(`${ROUTER_URLS.eventCreateName}?${new URLSearchParams({title: eventTitle})}`); - }; - - return ( - <section> - <h1>행사 생성하기</h1> - <h3>시작할 행사 이름을 입력해 주세요.</h3> - <form onSubmit={submitEventTitle}> - <input value={eventTitle} onChange={event => setEventTitle(event.target.value)} /> - <button>행동 개시!</button> - </form> - </section> - ); -}; - -export default CreateEvent; diff --git a/client/src/pages/Event/Event.style.ts b/client/src/pages/Event/Admin/Admin.style.ts similarity index 85% rename from client/src/pages/Event/Event.style.ts rename to client/src/pages/Event/Admin/Admin.style.ts index 15e2443b4..2025006d4 100644 --- a/client/src/pages/Event/Event.style.ts +++ b/client/src/pages/Event/Admin/Admin.style.ts @@ -6,4 +6,5 @@ export const ReceiptStyle = () => flexDirection: 'column', gap: '8px', padding: '0 8px', + paddingBottom: '8.75rem', }); diff --git a/client/src/pages/Event/Admin/Admin.tsx b/client/src/pages/Event/Admin/Admin.tsx new file mode 100644 index 000000000..bda03da55 --- /dev/null +++ b/client/src/pages/Event/Admin/Admin.tsx @@ -0,0 +1,97 @@ +import {useEffect, useState} from 'react'; +import {Title, FixedButton} from 'haengdong-design'; + +import StepList from '@components/StepList/StepList'; +import {useStepList} from '@hooks/useStepList/useStepList'; +import {requestGetEventName} from '@apis/request/event'; +import useEventId from '@hooks/useEventId/useEventId'; + +import {SetActionModalContent, SetInitialParticipants} from '@components/Modal'; + +import {ReceiptStyle} from './Admin.style'; + +export type PurchaseInformation = { + title: string; + price: number; +}; + +export type ParticipantType = { + name: string; + type: InOutType; +}; + +interface ModalRenderingProps { + memberNameList: string[]; + openBottomSheet: boolean; + setOrder: React.Dispatch<React.SetStateAction<number>>; + setOpenBottomSheet: React.Dispatch<React.SetStateAction<boolean>>; +} + +const ModalRendering = ({memberNameList, openBottomSheet, setOrder, setOpenBottomSheet}: ModalRenderingProps) => { + switch (memberNameList.length) { + case 0: + return <SetInitialParticipants setOpenBottomSheet={setOpenBottomSheet} openBottomSheet={openBottomSheet} />; + + default: + return ( + <SetActionModalContent + setOrder={setOrder} + setOpenBottomSheet={setOpenBottomSheet} + openBottomSheet={openBottomSheet} + /> + ); + } +}; + +const Admin = () => { + const [openBottomSheet, setOpenBottomSheet] = useState(false); + const [order, setOrder] = useState<number>(1); + + // TODO: (@weadie) eventName이 새로고침시 공간이 없다가 생겨나 레이아웃이 움직이는 문제 + const [eventName, setEventName] = useState(' '); + + const {getTotalPrice, memberNameList} = useStepList(); + + const {eventId} = useEventId(); + + // TODO: (@weadie) 아래 로직을 훅으로 분리합니다. + useEffect(() => { + if (eventId === '') return; + + const getEventName = async () => { + const {eventName} = await requestGetEventName({eventId: eventId ?? ''}); + + setEventName(eventName); + }; + + getEventName(); + }, [eventId]); + + return ( + <> + <Title + title={eventName} + description="“초기인원 설정하기” 버튼을 눌러서 행사 초기 인원을 설정해 주세요." + price={getTotalPrice()} + /> + <section css={ReceiptStyle}> + <StepList /> + {/* TODO: (@soha) 추후 버튼 width 화면에 맞게 수정 */} + <FixedButton + children={memberNameList.length === 0 ? '초기인원 설정하기' : '행동 추가하기'} + onClick={() => setOpenBottomSheet(prev => !prev)} + /> + {openBottomSheet && ( + <ModalRendering + memberNameList={memberNameList} + setOrder={setOrder} + setOpenBottomSheet={setOpenBottomSheet} + openBottomSheet={openBottomSheet} + /> + )} + </section> + </> + ); +}; + +export default Admin; diff --git a/client/src/pages/Event/Admin/index.ts b/client/src/pages/Event/Admin/index.ts new file mode 100644 index 000000000..9799a7485 --- /dev/null +++ b/client/src/pages/Event/Admin/index.ts @@ -0,0 +1 @@ +export {default as AdminPage} from './Admin'; diff --git a/client/src/pages/Event/Event.tsx b/client/src/pages/Event/Event.tsx deleted file mode 100644 index 79c1f9950..000000000 --- a/client/src/pages/Event/Event.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import {useState} from 'react'; -import {TopNav, Title, FixedButton, StepItem, InOutItem, MainLayout} from 'haengdong-design'; - -import StepList from '@components/StepList/StepList'; -import {useStepList} from '@hooks/useStepList/useStepList'; -import {StepListProvider} from '@hooks/useStepList/useStepList'; - -import {SetActionModalContent, SetInitialParticipants} from '@components/Modal'; - -import {ReceiptStyle} from './Event.style'; - -export type PurchaseInformation = { - name: string; - price: number; -}; - -export type InOutType = '늦참' | '탈주'; - -export type ParticipantType = { - name: string; - type: InOutType; -}; - -interface ModalRenderingProps { - participants: string[]; - openBottomSheet: boolean; - - setOrder: React.Dispatch<React.SetStateAction<number>>; - setOpenBottomSheet: React.Dispatch<React.SetStateAction<boolean>>; - setParticipants: React.Dispatch<React.SetStateAction<string[]>>; -} - -const ModalRendering = ({ - participants, - setOrder, - setOpenBottomSheet, - setParticipants, - openBottomSheet, -}: ModalRenderingProps) => { - switch (participants.length) { - case 0: - return ( - <SetInitialParticipants - setParticipants={setParticipants} - setOpenBottomSheet={setOpenBottomSheet} - openBottomSheet={openBottomSheet} - /> - ); - - default: - return ( - <SetActionModalContent - setOrder={setOrder} - participants={participants} - setOpenBottomSheet={setOpenBottomSheet} - openBottomSheet={openBottomSheet} - /> - ); - } -}; - -const Event = () => { - const [openBottomSheet, setOpenBottomSheet] = useState(false); - const [participants, setParticipants] = useState<string[]>([]); - const [order, setOrder] = useState<number>(0); - - const {getTotalPrice} = useStepList(); - - return ( - <MainLayout backgroundColor="gray"> - <TopNav navType={'home'} /> - <Title - title="행동대장 야유회" - description="“초기인원 설정하기” 버튼을 눌러서 행사 초기 인원을 설정해 주세요." - price={getTotalPrice()} - /> - <section css={ReceiptStyle}> - {order > 0 && ( - // TODO: (@soha) order가 0일때 기본 Step 뜨기 - <StepListProvider> - <StepList /> - </StepListProvider> - )} - {/* TODO: (@soha) 추후 버튼 width 화면에 맞게 수정 */} - <FixedButton - children={participants.length === 0 ? '초기인원 설정하기' : '행동 추가하기'} - onClick={() => setOpenBottomSheet(prev => !prev)} - /> - {openBottomSheet && - ModalRendering({ - participants, - setOrder, - setParticipants, - setOpenBottomSheet, - openBottomSheet, - })} - </section> - </MainLayout> - ); -}; - -export default Event; diff --git a/client/src/pages/Event/EventLayout.tsx b/client/src/pages/Event/EventLayout.tsx new file mode 100644 index 000000000..2b2b439de --- /dev/null +++ b/client/src/pages/Event/EventLayout.tsx @@ -0,0 +1,23 @@ +import {MainLayout, TopNav, Switch} from 'haengdong-design'; +import {Outlet} from 'react-router-dom'; + +import StepListProvider from '@hooks/useStepList/useStepList'; + +import useNavSwitch from '@hooks/useNavSwitch'; + +const EventLayout = () => { + const {nav, paths, onChange} = useNavSwitch(); + + return ( + <StepListProvider> + <MainLayout backgroundColor="gray"> + <TopNav> + <Switch value={nav} values={paths} onChange={onChange} /> + </TopNav> + <Outlet /> + </MainLayout> + </StepListProvider> + ); +}; + +export default EventLayout; diff --git a/client/src/pages/Event/Home/Home.tsx b/client/src/pages/Event/Home/Home.tsx new file mode 100644 index 000000000..8ff195cb8 --- /dev/null +++ b/client/src/pages/Event/Home/Home.tsx @@ -0,0 +1,40 @@ +import {Tab, Tabs, Title} from 'haengdong-design'; +import {useEffect, useState} from 'react'; + +import MemberReportList from '@components/MemberReportList/MemberReportList'; +import StepList from '@components/StepList/StepList'; +import {useStepList} from '@hooks/useStepList/useStepList'; +import useEventId from '@hooks/useEventId/useEventId'; +import {requestGetEventName} from '@apis/request/event'; + +const HomeContent = () => { + const {getTotalPrice} = useStepList(); + const {eventId} = useEventId(); + + // TODO: (@soha) 행사 이름 나중에 따로 분리해야 함 + const [eventName, setEventName] = useState(' '); + + useEffect(() => { + if (eventId === '') return; + + const getEventName = async () => { + const {eventName} = await requestGetEventName({eventId: eventId ?? ''}); + + setEventName(eventName); + }; + + getEventName(); + }, [eventId]); + + return ( + <div> + <Title title={eventName} price={getTotalPrice()} /> + <Tabs tabsContainerStyle={{gap: '1rem'}}> + <Tab label="전체 지출 내역" content={<StepList />} /> + <Tab label="참여자 별 내역" content={<MemberReportList />} /> + </Tabs> + </div> + ); +}; + +export default HomeContent; diff --git a/client/src/pages/Event/Home/index.ts b/client/src/pages/Event/Home/index.ts new file mode 100644 index 000000000..75e27b8a3 --- /dev/null +++ b/client/src/pages/Event/Home/index.ts @@ -0,0 +1 @@ +export {default as HomePage} from './Home'; diff --git a/client/src/pages/Event/index.ts b/client/src/pages/Event/index.ts index fa85db27a..af2aa805d 100644 --- a/client/src/pages/Event/index.ts +++ b/client/src/pages/Event/index.ts @@ -1 +1 @@ -export {default as EventPage} from './Event'; +export {default as EventPage} from './EventLayout'; diff --git a/client/src/pages/Home/HomeContent.tsx b/client/src/pages/Home/HomeContent.tsx deleted file mode 100644 index 171729636..000000000 --- a/client/src/pages/Home/HomeContent.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import {Tab, Tabs, Title} from 'haengdong-design'; - -import MemberReportList from '@components/MemberReportList/MemberReportList'; -import StepList from '@components/StepList/StepList'; -import {useStepList} from '@hooks/useStepList/useStepList'; - -const HomeContent = () => { - const {getTotalPrice} = useStepList(); - - return ( - <div> - <Title title="행동대장 야유회" price={getTotalPrice()} /> - <Tabs> - <Tab label="전체 지출 내역" content={<StepList />} /> - <Tab label="참여자 별 내역" content={<MemberReportList />} /> - </Tabs> - </div> - ); -}; - -export default HomeContent; diff --git a/client/src/pages/Home/HomeLayout.tsx b/client/src/pages/Home/HomeLayout.tsx deleted file mode 100644 index 8cae579d6..000000000 --- a/client/src/pages/Home/HomeLayout.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import {Flex, MainLayout, TopNav} from 'haengdong-design'; - -import {StepListProvider} from '@hooks/useStepList/useStepList'; - -import HomeContent from './HomeContent'; - -const HomeLayout = () => { - return ( - <StepListProvider> - <MainLayout backgroundColor="gray"> - <TopNav navType="home" /> - <Flex flexDirection="column" gap="1rem"> - <HomeContent /> - </Flex> - </MainLayout> - </StepListProvider> - ); -}; - -export default HomeLayout; diff --git a/client/src/pages/Home/index.ts b/client/src/pages/Home/index.ts deleted file mode 100644 index fde362f5c..000000000 --- a/client/src/pages/Home/index.ts +++ /dev/null @@ -1 +0,0 @@ -export {default as HomePage} from './HomeLayout'; diff --git a/client/src/pages/Main/Main.tsx b/client/src/pages/Main/Main.tsx index 1cb9f6b26..dfd7c406e 100644 --- a/client/src/pages/Main/Main.tsx +++ b/client/src/pages/Main/Main.tsx @@ -1,5 +1,5 @@ import {useNavigate} from 'react-router-dom'; -import {FixedButton, MainLayout, Title} from 'haengdong-design'; +import {FixedButton, MainLayout, Title, TopNav} from 'haengdong-design'; import {ROUTER_URLS} from '@constants/routerUrls'; @@ -8,7 +8,7 @@ const Main = () => { return ( <MainLayout> - {/* <TopNav navType="back" /> */} + <TopNav children={<></>} /> <Title title="행동대장" description="랜딩페이지입니다." /> <FixedButton onClick={() => navigate(ROUTER_URLS.eventCreateName)}>행사 생성하기</FixedButton> </MainLayout> diff --git a/client/src/router.tsx b/client/src/router.tsx index a84ea18e5..70f7a1e71 100644 --- a/client/src/router.tsx +++ b/client/src/router.tsx @@ -1,9 +1,11 @@ import {createBrowserRouter} from 'react-router-dom'; +import {AdminPage} from '@pages/Event/Admin'; +import {HomePage} from '@pages/Event/Home'; + import {MainPage} from '@pages/Main'; import {CreateNamePage, CreateCompletePage} from '@pages/Create'; import {EventPage} from '@pages/Event'; -import {HomePage} from '@pages/Home'; import {ROUTER_URLS} from '@constants/routerUrls'; @@ -28,12 +30,12 @@ const router = createBrowserRouter([ element: <CreateCompletePage />, }, { - path: ROUTER_URLS.eventManage, + path: ROUTER_URLS.event, element: <EventPage />, - }, - { - path: ROUTER_URLS.home, - element: <HomePage />, + children: [ + {path: ROUTER_URLS.eventManage, element: <AdminPage />}, + {path: ROUTER_URLS.home, element: <HomePage />}, + ], }, ], }, diff --git a/client/src/type.d.ts b/client/src/type.d.ts new file mode 100644 index 000000000..a81318e4c --- /dev/null +++ b/client/src/type.d.ts @@ -0,0 +1,18 @@ +type MemberType = 'IN' | 'OUT'; + +type InOutType = '늦참' | '탈주'; + +type MemberReport = { + name: string; + price: number; +}; + +type Bill = { + title: string; + price: number; +}; + +// TODO: (@weadie) 준 데이터 형식에서 steps를 빼내 flat하게 사용중. 일관성있게 하는게 좋긴 하나 사용시 번거로움이 있을 거라고 판단. +type StepList = { + steps: (MemberStep | BillStep)[]; +}; From 4d8697a1214debd729e92766ca8c642b59de6e29 Mon Sep 17 00:00:00 2001 From: JUHA <84626225+khabh@users.noreply.github.com> Date: Sun, 28 Jul 2024 16:44:04 +0900 Subject: [PATCH 15/15] =?UTF-8?q?refactor:=20=EB=8F=84=EC=BB=A4=20?= =?UTF-8?q?=EA=B3=84=EC=A0=95=20=EA=B4=80=EB=A0=A8=20=EC=A0=95=EB=B3=B4=20?= =?UTF-8?q?secrets=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95=20(#139)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/backend-push.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/backend-push.yml b/.github/workflows/backend-push.yml index be973a6a9..d385a69c8 100644 --- a/.github/workflows/backend-push.yml +++ b/.github/workflows/backend-push.yml @@ -68,4 +68,4 @@ jobs: run: sudo docker pull ${{ secrets.DOCKER_USERNAME }}/haengdong-backend-dev - name: Docker run - run: sudo docker run -d -p 8080:8080 --name haengdong-backend-dev haengdong/haengdong-backend-dev + run: sudo docker run -d -p 8080:8080 --name haengdong-backend-dev ${{ secrets.DOCKER_USERNAME }}/haengdong-backend-dev