diff --git a/README.md b/README.md new file mode 100644 index 00000000..005647aa --- /dev/null +++ b/README.md @@ -0,0 +1,98 @@ +

+ +

+ +
+ ๐Ÿ“– ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋Š” ์šฐ๋ฆฌ์˜ ๊ณต๊ฐ„, ์šฐ๋ฆฌ๊ฐ€ ์ฒดํฌํ•˜์ž! +
+ +
+ +
+ +[![Application](http://img.shields.io/badge/Application-blue?style=flat-square&logo=googlechrome&logoColor=white&link=https://https://gongcheck.day//)](https://gongcheck.day/) +[![Tech Blog](http://img.shields.io/badge/Tech%20Blog-green?style=flat-square&logo=github&logoColor=white&link=https://gong-check.github.io/dev-blog/)](https://gong-check.github.io/dev-blog/) + +
+ +
+ +![](https://img.shields.io/github/workflow/status/woowacourse-teams/2022-gong-check/frontend?label=frontend&logo=github&style=flat-square) +![](https://img.shields.io/github/workflow/status/woowacourse-teams/2022-gong-check/sonarqube%20backend?label=backend&logo=github&style=flat-square) +![](https://img.shields.io/github/workflow/status/woowacourse-teams/2022-gong-check/sonar%20imagestorage?label=imagestorage&logo=github&style=flat-square) + +
+ +## ๐Ÿ›  Tech Stacks + +### Front-End + +Screen Shot 2022-08-27 at 4 45 07 PM + +### Back-End + +Screen Shot 2022-08-26 at 1 04 01 AM + +### Infra + +Screen Shot 2022-08-26 at 1 05 05 AM + +
+ +## ๐Ÿ›ฐ Infrastructures + +### Architecture + +Screen Shot 2022-08-27 at 4 45 57 PM + +### CI/CD Pipeline + +Screen Shot 2022-08-27 at 4 46 27 PM + +
+ +## ๐Ÿซ‚ Members + +| [์ฝ”์นด์ฝœ๋ผ](https://github.com/intae92) | [์˜จ์Šคํƒ€](https://github.com/cks3066) | [์–ด์ธ์˜ค](https://github.com/awesomeo184) | [์˜ค๋ฆฌ](https://github.com/jinyoungchoi95) | [์ฟผ๋ฆฌ์น˜](https://github.com/meatsby) | [์ฐฌ](https://github.com/kimchan123) | [๋ฒ”๊ณ ๋ž˜](https://github.com/cndqjacndqja) | +| :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | +| Avatar | Avatar | Avatar | Avatar | Avatar | Avatar | Avatar | +| ํ”„๋ก ํŠธ์—”๋“œ | ํ”„๋ก ํŠธ์—”๋“œ | ๋ฐฑ์—”๋“œ | ๋ฐฑ์—”๋“œ | ๋ฐฑ์—”๋“œ | ๋ฐฑ์—”๋“œ | ๋ฐฑ์—”๋“œ | + +
+ +## ๐ŸŒŒ Team Culture + +### ๐Ÿ›Ž ์†Œํ†ต + +- ๋ชจ๋ฅด๋Š” ๊ฒƒ์„ ๋ถ€๋„๋Ÿฌ์›Œํ•˜์ง€ ๋ง์•„์š”. +- ์งˆ๋ฌธ์€ ์ž์œ ๋กญ๊ฒŒ, ์ฃผ์žฅ์€ ๊ทผ๊ฑฐ์žˆ๊ฒŒ ํ•ด์ฃผ์„ธ์š”. +- ๊ฐ ์Šคํ”„๋ฆฐํŠธ๋งˆ๋‹ค ์ฐํ•˜๊ฒŒ ํšŒ๊ณ ํ•ด์š”! + +### ๐Ÿ“Œ ๊ณต์œ  + +- ๋งค์ผ ์ถœ๊ทผํ•˜๊ณ  ๋ฐ์ผ๋ฆฌ ๋ฏธํŒ…์„ ์ง„ํ–‰ํ•ด์š”. +- ์ฃผ ๋‹จ์œ„๋กœ ํŒ€ ์ „์ฒด์˜ To-do ๋ฆฌ์ŠคํŠธ๋ฅผ ๊ณต์œ ํ•ด์š”. +- ๋งค์ผ ํ‡ด๊ทผ ์ „์— ๊ฐ„๋‹จํžˆ ์ฒดํฌ์•„์›ƒ ๋ฏธํŒ…์„ ์ง„ํ–‰ํ•ด์š”. + +### ๐Ÿ”— ์•ฝ์† + +- ์ˆ˜ํ‰์ ์ธ ๊ด€๊ณ„๋กœ ์„œ๋กœ๋ฅผ ์กด์ค‘ํ•ด์ฃผ์„ธ์š”. +- 10AM-6PM ์‹œ๊ฐ„์€ ์ž˜ ์ง€์ผœ์ฃผ์„ธ์š”. +- ๋Šฆ์œผ๋ฉด ๋ฒŒ์น™์ด ์žˆ์–ด์š”! โ˜•๏ธ + +### ๐Ÿ– ์ด์Šˆ ๊ด€๋ฆฌ + +- ์ƒ๊ฐ๋‚˜๋Š” ์ด์Šˆ๊ฐ€ ์žˆ์œผ๋ฉด ๋ฐ”๋กœ๋ฐ”๋กœ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”. +- ์‚ฌ์†Œํ•ด๋„ ๊ดœ์ฐฎ์•„์š”. +- ์ž์„ธํ•˜๊ฒŒ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”. +- ์ด์Šˆ ํ˜•์‹์— ๋„ˆ๋ฌด ๋ถ€๋‹ด ๊ฐ–์ง€๋ง์•„์š”. + +### ๐Ÿ”Ž ์ฝ”๋“œ ๋ฆฌ๋ทฐ + +- ๋ชจ๋“  ์‚ฌ๋žŒ์ด Approve ํ•ด์•ผ Merge ํ•  ์ˆ˜ ์žˆ์–ด์š”. +- PR ์€ ์ผ๊ณผ ์ค‘์—๋งŒ ๋‚ ๋ ค์ฃผ์„ธ์š”. +- ํ’ˆ์งˆ ๋†’์€ ์ฝ”๋“œ ๋ฆฌ๋ทฐ๋ฅผ ์œ„ํ•ด ๊ตฌ์ฒด์ ์œผ๋กœ PR ์„ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”. + +### ๐Ÿ“ ํŒ€ ๋ธ”๋กœ๊ทธ + +- ์„œ๋กœ ๋ฐฐ์šด ๋‚ด์šฉ์„ ์ผ์ฃผ์ผ์— ํ•œ ๋ฒˆ์”ฉ ๊ธ€๋กœ ์ž‘์„ฑํ•ด์„œ ๊ณต์œ ํ•ด์š”. diff --git a/backend/src/main/java/com/woowacourse/gongcheck/core/application/SpaceService.java b/backend/src/main/java/com/woowacourse/gongcheck/core/application/SpaceService.java index 8f625352..0017b94e 100644 --- a/backend/src/main/java/com/woowacourse/gongcheck/core/application/SpaceService.java +++ b/backend/src/main/java/com/woowacourse/gongcheck/core/application/SpaceService.java @@ -46,6 +46,12 @@ public SpaceService(final HostRepository hostRepository, final SpaceRepository s this.runningTaskRepository = runningTaskRepository; } + public SpaceResponse findSpace(final Long hostId, final Long spaceId) { + Host host = hostRepository.getById(hostId); + Space space = spaceRepository.getByHostAndId(host, spaceId); + return SpaceResponse.from(space); + } + public SpacesResponse findSpaces(final Long hostId) { Host host = hostRepository.getById(hostId); List spaces = spaceRepository.findAllByHost(host); @@ -67,23 +73,6 @@ public Long createSpace(final Long hostId, final SpaceCreateRequest request) { .getId(); } - public SpaceResponse findSpace(final Long hostId, final Long spaceId) { - Host host = hostRepository.getById(hostId); - Space space = spaceRepository.getByHostAndId(host, spaceId); - return SpaceResponse.from(space); - } - - @Transactional - public void changeSpace(final Long hostId, final Long spaceId, final SpaceChangeRequest request) { - Host host = hostRepository.getById(hostId); - Space space = spaceRepository.getByHostAndId(host, spaceId); - Name changeName = new Name(request.getName()); - checkDuplicateSpaceName(changeName, host, space); - - space.changeName(changeName); - space.changeImageUrl(request.getImageUrl()); - } - @Transactional public void removeSpace(final Long hostId, final Long spaceId) { Host host = hostRepository.getById(hostId); @@ -101,6 +90,17 @@ public void removeSpace(final Long hostId, final Long spaceId) { spaceRepository.deleteById(spaceId); } + @Transactional + public void changeSpace(final Long hostId, final Long spaceId, final SpaceChangeRequest request) { + Host host = hostRepository.getById(hostId); + Space space = spaceRepository.getByHostAndId(host, spaceId); + Name changeName = new Name(request.getName()); + checkDuplicateSpaceName(changeName, host, space); + + space.changeName(changeName); + space.changeImageUrl(request.getImageUrl()); + } + private void checkDuplicateSpaceName(final Name spaceName, final Host host) { if (spaceRepository.existsByHostAndName(host, spaceName)) { String message = String.format("์ด๋ฏธ ์กด์žฌํ•˜๋Š” ์ด๋ฆ„์ž…๋‹ˆ๋‹ค. hostId = %d, spaceName = %s", host.getId(), diff --git a/backend/src/main/java/com/woowacourse/gongcheck/exception/ControllerAdvice.java b/backend/src/main/java/com/woowacourse/gongcheck/exception/ControllerAdvice.java index 32987e8a..72157d6a 100644 --- a/backend/src/main/java/com/woowacourse/gongcheck/exception/ControllerAdvice.java +++ b/backend/src/main/java/com/woowacourse/gongcheck/exception/ControllerAdvice.java @@ -11,6 +11,7 @@ import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.context.request.async.AsyncRequestTimeoutException; @RestControllerAdvice @Slf4j @@ -51,6 +52,11 @@ public ResponseEntity handleInfrastructureException(final CustomE return ResponseEntity.internalServerError().body(ErrorResponse.from(e.getErrorCode())); } + @ExceptionHandler(AsyncRequestTimeoutException.class) + public void escapeFromAsyncRequestTimeoutException() { + // do nothing + } + @ExceptionHandler(Exception.class) public ResponseEntity handleInternalServerError(final Exception e) { log.error("Stack Trace : {}", extractStackTrace(e)); diff --git a/backend/src/test/java/com/woowacourse/gongcheck/core/application/HostServiceTest.java b/backend/src/test/java/com/woowacourse/gongcheck/core/application/HostServiceTest.java index b5276855..2308edec 100644 --- a/backend/src/test/java/com/woowacourse/gongcheck/core/application/HostServiceTest.java +++ b/backend/src/test/java/com/woowacourse/gongcheck/core/application/HostServiceTest.java @@ -10,7 +10,6 @@ import com.woowacourse.gongcheck.core.domain.host.Host; import com.woowacourse.gongcheck.core.presentation.request.SpacePasswordChangeRequest; import com.woowacourse.gongcheck.exception.NotFoundException; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; @@ -19,7 +18,7 @@ import org.springframework.beans.factory.annotation.Autowired; @ApplicationTest -@DisplayName("HostService ํด๋ž˜์Šค") +@DisplayName("HostService ํด๋ž˜์Šค์˜") @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) class HostServiceTest { @@ -36,48 +35,35 @@ class HostServiceTest { class changeSpacePassword_๋ฉ”์†Œ๋“œ๋Š” { @Nested - class ์กด์žฌํ•˜๋Š”_Host์˜_id์™€_์ˆ˜์ •ํ• _ํŒจ์Šค์›Œ๋“œ๋ฅผ_๋ฐ›๋Š”_๊ฒฝ์šฐ { + class ์กด์žฌํ•˜๋Š”_HostId์™€_์ˆ˜์ •ํ• _ํŒจ์Šค์›Œ๋“œ๋ฅผ_์ž…๋ ฅ๋ฐ›๋Š”_๊ฒฝ์šฐ { - private static final String ORIGIN_PASSWORD = "1234"; private static final String CHANGING_PASSWORD = "4567"; - private static final long GITHUB_ID = 1234L; - private SpacePasswordChangeRequest spacePasswordChangeRequest; - private Long hostId; - - @BeforeEach - void setUp() { - spacePasswordChangeRequest = new SpacePasswordChangeRequest(CHANGING_PASSWORD); - hostId = repository.save(Host_์ƒ์„ฑ(ORIGIN_PASSWORD, GITHUB_ID)) - .getId(); - } + private final Host host = repository.save(Host_์ƒ์„ฑ("1234", 1L)); + private final SpacePasswordChangeRequest spacePasswordChangeRequest = new SpacePasswordChangeRequest( + CHANGING_PASSWORD); @Test void ํŒจ์Šค์›Œ๋“œ๋ฅผ_์ˆ˜์ •ํ•œ๋‹ค() { - hostService.changeSpacePassword(hostId, spacePasswordChangeRequest); - Host actual = repository.getById(Host.class, hostId); + hostService.changeSpacePassword(host.getId(), spacePasswordChangeRequest); + Host actual = repository.getById(Host.class, host.getId()); assertThat(actual.getSpacePassword().getValue()).isEqualTo(CHANGING_PASSWORD); } } @Nested - class ์กด์žฌํ•˜์ง€_์•Š๋Š”_Host์˜_id๋ฅผ_๋ฐ›๋Š”_๊ฒฝ์šฐ { + class ์กด์žฌํ•˜์ง€_์•Š๋Š”_HostId๋ฅผ_์ž…๋ ฅ๋ฐ›๋Š”_๊ฒฝ์šฐ { private static final String CHANGING_PASSWORD = "4567"; + private static final long NON_EXIST_HOST_ID = 0L; - private SpacePasswordChangeRequest spacePasswordChangeRequest; - private Long hostId; - - @BeforeEach - void setUp() { - spacePasswordChangeRequest = new SpacePasswordChangeRequest(CHANGING_PASSWORD); - hostId = 0L; - } + private final SpacePasswordChangeRequest spacePasswordChangeRequest = new SpacePasswordChangeRequest( + CHANGING_PASSWORD); @Test void ์˜ˆ์™ธ๋ฅผ_๋ฐœ์ƒ์‹œํ‚จ๋‹ค() { - assertThatThrownBy(() -> hostService.changeSpacePassword(hostId, spacePasswordChangeRequest)) + assertThatThrownBy(() -> hostService.changeSpacePassword(NON_EXIST_HOST_ID, spacePasswordChangeRequest)) .isInstanceOf(NotFoundException.class) .hasMessageContaining("์กด์žฌํ•˜์ง€ ์•Š๋Š” ํ˜ธ์ŠคํŠธ์ž…๋‹ˆ๋‹ค."); } @@ -88,31 +74,26 @@ void setUp() { class createEntranceCode_๋ฉ”์†Œ๋“œ๋Š” { @Nested - class ์กด์žฌํ•˜๋Š”_Host์˜_id๋ฅผ_๋ฐ›๋Š”_๊ฒฝ์šฐ { + class ์กด์žฌํ•˜๋Š”_HostId๋ฅผ_์ž…๋ ฅ๋ฐ›๋Š”_๊ฒฝ์šฐ { - private Long hostId; - private String expected; - - @BeforeEach - void setUp() { - hostId = repository.save(Host_์ƒ์„ฑ("1234", 1111L)) - .getId(); - expected = entranceCodeProvider.createEntranceCode(hostId); - } + private final Host host = repository.save(Host_์ƒ์„ฑ("1234", 1L)); + private final String expected = entranceCodeProvider.createEntranceCode(host.getId()); @Test - void ์ž…์žฅ์ฝ”๋“œ๋ฅผ_๋ฐ˜ํ™˜ํ•œ๋‹ค() { - String actual = hostService.createEntranceCode(hostId); + void entranceCode๋ฅผ_๋ฐ˜ํ™˜ํ•œ๋‹ค() { + String actual = hostService.createEntranceCode(host.getId()); assertThat(actual).isEqualTo(expected); } } @Nested - class ์กด์žฌํ•˜์ง€_์•Š๋Š”_Host์˜_id๋ฅผ_๋ฐ›๋Š”_๊ฒฝ์šฐ { + class ์กด์žฌํ•˜์ง€_์•Š๋Š”_HostId๋ฅผ_์ž…๋ ฅ๋ฐ›๋Š”_๊ฒฝ์šฐ { + + private static final long NON_EXIST_HOST_ID = 0L; @Test void ์˜ˆ์™ธ๋ฅผ_๋ฐœ์ƒ์‹œํ‚จ๋‹ค() { - assertThatThrownBy(() -> hostService.createEntranceCode(0L)) + assertThatThrownBy(() -> hostService.createEntranceCode(NON_EXIST_HOST_ID)) .isInstanceOf(NotFoundException.class) .hasMessageContaining("์กด์žฌํ•˜์ง€ ์•Š๋Š” ํ˜ธ์ŠคํŠธ์ž…๋‹ˆ๋‹ค."); } diff --git a/backend/src/test/java/com/woowacourse/gongcheck/core/application/SpaceServiceTest.java b/backend/src/test/java/com/woowacourse/gongcheck/core/application/SpaceServiceTest.java index 08fa625d..2e8c108b 100644 --- a/backend/src/test/java/com/woowacourse/gongcheck/core/application/SpaceServiceTest.java +++ b/backend/src/test/java/com/woowacourse/gongcheck/core/application/SpaceServiceTest.java @@ -18,13 +18,16 @@ import com.woowacourse.gongcheck.core.domain.job.Job; import com.woowacourse.gongcheck.core.domain.section.Section; import com.woowacourse.gongcheck.core.domain.space.Space; +import com.woowacourse.gongcheck.core.domain.space.SpaceRepository; import com.woowacourse.gongcheck.core.domain.task.RunningTask; import com.woowacourse.gongcheck.core.domain.task.Task; +import com.woowacourse.gongcheck.core.domain.vo.Name; +import com.woowacourse.gongcheck.core.presentation.request.SpaceChangeRequest; import com.woowacourse.gongcheck.core.presentation.request.SpaceCreateRequest; import com.woowacourse.gongcheck.exception.BusinessException; import com.woowacourse.gongcheck.exception.NotFoundException; +import java.time.LocalDateTime; import java.util.List; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; @@ -43,37 +46,101 @@ class SpaceServiceTest { @Autowired private SupportRepository repository; + @Autowired + private SpaceRepository spaceRepository; + @Nested - class findSpaces_๋ฉ”์„œ๋“œ๋Š” { + class findSpace_๋ฉ”์„œ๋“œ๋Š” { + + @Nested + class ์ž…๋ ฅ๋ฐ›์€_Host๊ฐ€_์ž…๋ ฅ๋ฐ›์€_Space๋ฅผ_์†Œ์œ ํ•˜๊ณ _์žˆ๋Š”_๊ฒฝ์šฐ { + + private static final String SPACE_NAME = "์ž ์‹ค ์บ ํผ์Šค"; + + private final Host host = repository.save(Host_์ƒ์„ฑ("1234", 1L)); + private final Space space = repository.save(Space_์ƒ์„ฑ(host, SPACE_NAME)); + + @Test + void Space_์‘๋‹ต์„_๋ฐ˜ํ™˜ํ•œ๋‹ค() { + SpaceResponse actual = spaceService.findSpace(host.getId(), space.getId()); + + assertAll( + () -> assertThat(actual.getId()).isEqualTo(space.getId()), + () -> assertThat(actual.getName()).isEqualTo(SPACE_NAME) + ); + } + } @Nested - class ์กด์žฌํ•˜์ง€_์•Š๋Š”_Host_id๋ฅผ_์ž…๋ ฅ๋ฐ›๋Š”_๊ฒฝ์šฐ { + class ์ž…๋ ฅ๋ฐ›์€_Host๊ฐ€_์ž…๋ ฅ๋ฐ›์€_Space๋ฅผ_์†Œ์œ ํ•˜๊ณ _์žˆ์ง€_์•Š์€_๊ฒฝ์šฐ { + + private final Host host = repository.save(Host_์ƒ์„ฑ("1234", 1L)); + private final Host anotherHost = repository.save(Host_์ƒ์„ฑ("1234", 2L)); + private final Space space = repository.save(Space_์ƒ์„ฑ(anotherHost, "์ž ์‹ค ์บ ํผ์Šค")); @Test void ์˜ˆ์™ธ๋ฅผ_๋ฐœ์ƒ์‹œํ‚จ๋‹ค() { - assertThatThrownBy(() -> spaceService.findSpaces(0L)) + assertThatThrownBy(() -> spaceService.findSpace(host.getId(), space.getId())) + .isInstanceOf(NotFoundException.class) + .hasMessageContaining("์กด์žฌํ•˜์ง€ ์•Š๋Š” ๊ณต๊ฐ„์ž…๋‹ˆ๋‹ค."); + } + } + + @Nested + class ์กด์žฌํ•˜์ง€_์•Š๋Š”_Host_id๋ฅผ_์ž…๋ ฅ๋ฐ›์€_๊ฒฝ์šฐ { + + private static final long NON_EXIST_HOST_ID = 0L; + + private final Host host = repository.save(Host_์ƒ์„ฑ("1234", 1L)); + private final Space dummySpace = repository.save(Space_์ƒ์„ฑ(host, "์ž ์‹ค ์บ ํผ์Šค")); + + @Test + void ์˜ˆ์™ธ๋ฅผ_๋ฐœ์ƒ์‹œํ‚จ๋‹ค() { + assertThatThrownBy(() -> spaceService.findSpace(NON_EXIST_HOST_ID, dummySpace.getId())) .isInstanceOf(NotFoundException.class) .hasMessageContaining("์กด์žฌํ•˜์ง€ ์•Š๋Š” ํ˜ธ์ŠคํŠธ์ž…๋‹ˆ๋‹ค."); } } @Nested - class ์กด์žฌํ•˜๋Š”_Host์˜_id๋ฅผ_์ž…๋ ฅ๋ฐ›์€_๊ฒฝ์šฐ { + class ์กด์žฌํ•˜์ง€_์•Š๋Š”_Space_id๋ฅผ_์ž…๋ ฅ๋ฐ›์€_๊ฒฝ์šฐ { + + private static final long NON_EXIST_SPACE_ID = 0L; - private Host host; - private SpacesResponse expected; + private final Host host = repository.save(Host_์ƒ์„ฑ("1234", 1L)); - @BeforeEach - void setUp() { - host = repository.save(Host_์ƒ์„ฑ("1234", 1234L)); + @Test + void ์˜ˆ์™ธ๋ฅผ_๋ฐœ์ƒ์‹œํ‚จ๋‹ค() { + assertThatThrownBy(() -> spaceService.findSpace(host.getId(), NON_EXIST_SPACE_ID)) + .isInstanceOf(NotFoundException.class) + .hasMessageContaining("์กด์žฌํ•˜์ง€ ์•Š๋Š” ๊ณต๊ฐ„์ž…๋‹ˆ๋‹ค."); + } + } + } - Space space_1 = Space_์ƒ์„ฑ(host, "์ž ์‹ค ์บ ํผ์Šค"); - Space space_2 = Space_์ƒ์„ฑ(host, "์„ ๋ฆ‰ ์บ ํผ์Šค"); - Space space_3 = Space_์ƒ์„ฑ(host, "์–‘ํ‰๊ฐ™์€๋ฐฉ"); - List spaces = repository.saveAll(List.of(space_1, space_2, space_3)); + @Nested + class findSpaces_๋ฉ”์„œ๋“œ๋Š” { - expected = SpacesResponse.from(spaces); + @Nested + class ์กด์žฌํ•˜์ง€_์•Š๋Š”_HostId๋ฅผ_์ž…๋ ฅ๋ฐ›๋Š”_๊ฒฝ์šฐ { + + private static final long NON_EXIST_HOST_ID = 0L; + + @Test + void ์˜ˆ์™ธ๋ฅผ_๋ฐœ์ƒ์‹œํ‚จ๋‹ค() { + assertThatThrownBy(() -> spaceService.findSpaces(NON_EXIST_HOST_ID)) + .isInstanceOf(NotFoundException.class) + .hasMessageContaining("์กด์žฌํ•˜์ง€ ์•Š๋Š” ํ˜ธ์ŠคํŠธ์ž…๋‹ˆ๋‹ค."); } + } + + @Nested + class ์˜ฌ๋ฐ”๋ฅธ_์ž…๋ ฅ์„_๋ฐ›๋Š”_๊ฒฝ์šฐ { + + private final Host host = repository.save(Host_์ƒ์„ฑ("1234", 1L)); + private final SpacesResponse expected = SpacesResponse.from( + repository.saveAll(List.of(Space_์ƒ์„ฑ(host, "์ž ์‹ค ์บ ํผ์Šค"), Space_์ƒ์„ฑ(host, "์„ ๋ฆ‰ ์บ ํผ์Šค"), + Space_์ƒ์„ฑ(host, "์–‘ํ‰๊ฐ™์€๋ฐฉ")))); @Test void ํ•ด๋‹น_Host๊ฐ€_์†Œ์œ ํ•œ_Space๋ฅผ_์‘๋‹ต์œผ๋กœ_๋ฐ˜ํ™˜ํ•œ๋‹ค() { @@ -90,17 +157,12 @@ void setUp() { class createSpace_๋ฉ”์„œ๋“œ๋Š” { @Nested - class Host๊ฐ€_์ž…๋ ฅ๋ฐ›์€_Space_์ด๋ฆ„๊ณผ_๊ฐ™์€_Space๋ฅผ_์ด๋ฏธ_๊ฐ€์ง€๊ณ _์žˆ๋Š”_๊ฒฝ์šฐ { + class ์ž…๋ ฅ๋ฐ›์€_Space_์ด๋ฆ„์ด_Host๊ฐ€_์†Œ์œ ํ•œ_Space์˜_์ด๋ฆ„๊ณผ_์ค‘๋ณต๋˜๋Š”_๊ฒฝ์šฐ { - private Host host; - private SpaceCreateRequest request; - - @BeforeEach - void setUp() { - host = repository.save(Host_์ƒ์„ฑ("1234", 1234L)); - Space space = repository.save(Space_์ƒ์„ฑ(host, "์ž ์‹ค ์บ ํผ์Šค")); - request = new SpaceCreateRequest(space.getName().getValue(), "https://image.gongcheck.shop/123sdf5"); - } + private final Host host = repository.save(Host_์ƒ์„ฑ("1234", 1L)); + private final SpaceCreateRequest request = new SpaceCreateRequest( + repository.save(Space_์ƒ์„ฑ(host, "์ž ์‹ค ์บ ํผ์Šค")).getName().getValue(), + "https://image.gongcheck.shop/123sdf5"); @Test void ์˜ˆ์™ธ๋ฅผ_๋ฐœ์ƒ์‹œํ‚จ๋‹ค() { @@ -111,16 +173,12 @@ void setUp() { } @Nested - class ์กด์žฌํ•˜์ง€_์•Š๋Š”_Host_id๋ฅผ_์ž…๋ ฅ๋ฐ›์€_๊ฒฝ์šฐ { + class ์กด์žฌํ•˜์ง€_์•Š๋Š”_HostId๋ฅผ_์ž…๋ ฅ๋ฐ›๋Š”_๊ฒฝ์šฐ { private static final long NON_EXIST_HOST_ID = 0L; - private SpaceCreateRequest request; - - @BeforeEach - void setUp() { - request = new SpaceCreateRequest("์ด๊ฒƒ์€ ์œ ์ผํ•œ Space์ด๋ฆ„", "https://image.gongcheck.shop/123sdf5"); - } + private final SpaceCreateRequest request = new SpaceCreateRequest("์ด๊ฒƒ์€ ์œ ์ผํ•œ Space์ด๋ฆ„", + "https://image.gongcheck.shop/123sdf5"); @Test void ์˜ˆ์™ธ๋ฅผ_๋ฐœ์ƒ์‹œํ‚จ๋‹ค() { @@ -131,19 +189,13 @@ void setUp() { } @Nested - class ์ž…๋ ฅ๋ฐ›์€_Host๊ฐ€_์กด์žฌํ•˜๋Š”_๊ฒฝ์šฐ { + class ์˜ฌ๋ฐ”๋ฅธ_์ž…๋ ฅ์„_๋ฐ›๋Š”_๊ฒฝ์šฐ { private static final String SPACE_NAME = "์ž ์‹ค ์บ ํผ์Šค"; private static final String SPACE_IMAGE_URL = "https://image.gongcheck.shop/123sdf5"; - private Host host; - private SpaceCreateRequest request; - - @BeforeEach - void setUp() { - host = repository.save(Host_์ƒ์„ฑ("1234", 1234L)); - request = new SpaceCreateRequest(SPACE_NAME, SPACE_IMAGE_URL); - } + private final Host host = repository.save(Host_์ƒ์„ฑ("1234", 1L)); + private final SpaceCreateRequest request = new SpaceCreateRequest(SPACE_NAME, SPACE_IMAGE_URL); @Test void Space๋ฅผ_์ƒ์„ฑํ•œ๋‹ค() { @@ -159,193 +211,159 @@ void setUp() { } @Nested - class findSpace_๋ฉ”์„œ๋“œ๋Š” { + class removeSpace_๋ฉ”์„œ๋“œ๋Š” { @Nested - class Space_๋ชฉ๋ก์ด_์กด์žฌํ•˜๋Š”_๊ฒฝ์šฐ { - - private static final String SPACE_NAME = "์ž ์‹ค ์บ ํผ์Šค"; - - private Host host; - private Space space; + class ์ž…๋ ฅ๋ฐ›์€_Host_id๊ฐ€_์กด์žฌํ•˜์ง€_์•Š๋Š”_๊ฒฝ์šฐ { - @BeforeEach - void setUp() { - host = repository.save(Host_์ƒ์„ฑ("1234", 2345L)); - space = repository.save(Space_์ƒ์„ฑ(host, SPACE_NAME)); - } + private static final long NON_EXIST_HOST_ID = 0L; + private static final long DUMMY_SPACE_ID = 1L; @Test - void Job_๋ชฉ๋ก์„_์กฐํšŒํ•œ๋‹ค() { - SpaceResponse actual = spaceService.findSpace(host.getId(), space.getId()); - - assertAll( - () -> assertThat(actual.getId()).isEqualTo(space.getId()), - () -> assertThat(actual.getName()).isEqualTo(SPACE_NAME) - ); + void ์˜ˆ์™ธ๋ฅผ_๋ฐœ์ƒ์‹œํ‚จ๋‹ค() { + assertThatThrownBy(() -> spaceService.removeSpace(NON_EXIST_HOST_ID, DUMMY_SPACE_ID)) + .isInstanceOf(NotFoundException.class) + .hasMessageContaining("์กด์žฌํ•˜์ง€ ์•Š๋Š” ํ˜ธ์ŠคํŠธ์ž…๋‹ˆ๋‹ค."); } } @Nested - class ์ž…๋ ฅ๋ฐ›์€_Host๊ฐ€_์ž…๋ ฅ๋ฐ›์€_Space๋ฅผ_๊ฐ€์ง€๊ณ _์žˆ์ง€_์•Š์€_๊ฒฝ์šฐ { - - private Space space; - private Host anotherHost; + class ์ž…๋ ฅ๋ฐ›์€_Host๊ฐ€_์ž…๋ ฅ๋ฐ›์€_Space๋ฅผ_์†Œ์œ ํ•˜๊ณ _์žˆ์ง€_์•Š์€_๊ฒฝ์šฐ { - @BeforeEach - void setUp() { - Host host = repository.save(Host_์ƒ์„ฑ("1234", 1234L)); - space = repository.save(Space_์ƒ์„ฑ(host, "์ž ์‹ค ์บ ํผ์Šค")); - anotherHost = repository.save(Host_์ƒ์„ฑ("1234", 2345L)); - } + private final Host host = repository.save(Host_์ƒ์„ฑ("1234", 1L)); + private final Host anotherHost = repository.save(Host_์ƒ์„ฑ("1234", 2L)); + private final Space space = repository.save(Space_์ƒ์„ฑ(anotherHost, "์ž ์‹ค ์บ ํผ์Šค")); @Test void ์˜ˆ์™ธ๋ฅผ_๋ฐœ์ƒ์‹œํ‚จ๋‹ค() { - assertThatThrownBy(() -> spaceService.findSpace(anotherHost.getId(), space.getId())) + assertThatThrownBy(() -> spaceService.removeSpace(host.getId(), space.getId())) .isInstanceOf(NotFoundException.class) .hasMessageContaining("์กด์žฌํ•˜์ง€ ์•Š๋Š” ๊ณต๊ฐ„์ž…๋‹ˆ๋‹ค."); } } @Nested - class ์กด์žฌํ•˜์ง€_์•Š๋Š”_Host_id๋ฅผ_์ž…๋ ฅ๋ฐ›์€_๊ฒฝ์šฐ { - - private static final long NON_EXIST_HOST_ID = 0L; + class ์˜ฌ๋ฐ”๋ฅธ_์ž…๋ ฅ์„_๋ฐ›๋Š”_๊ฒฝ์šฐ { - private Space space; - - @BeforeEach - void setUp() { - Host host = repository.save(Host_์ƒ์„ฑ("1234", 1234L)); - space = repository.save(Space_์ƒ์„ฑ(host, "์ž ์‹ค ์บ ํผ์Šค")); - } + private final Host host = repository.save(Host_์ƒ์„ฑ("1234", 1L)); + private final Space space = repository.save(Space_์ƒ์„ฑ(host, "์ž ์‹ค ์บ ํผ์Šค")); + private final Job job = repository.save(Job_์ƒ์„ฑ(space, "์ฒญ์†Œ")); + private final Section section = repository.save(Section_์ƒ์„ฑ(job, "๋Œ€๊ฐ•์˜์‹ค")); + private final Task task = repository.save(Task_์ƒ์„ฑ(section, "์ฑ…์ƒ ๋‹ฆ๊ธฐ")); + private final RunningTask runningTask = repository.save(RunningTask_์ƒ์„ฑ(task.getId(), false)); @Test - void ์˜ˆ์™ธ๋ฅผ_๋ฐœ์ƒ์‹œํ‚จ๋‹ค() { - assertThatThrownBy(() -> spaceService.findSpace(NON_EXIST_HOST_ID, space.getId())) - .isInstanceOf(NotFoundException.class) - .hasMessageContaining("์กด์žฌํ•˜์ง€ ์•Š๋Š” ํ˜ธ์ŠคํŠธ์ž…๋‹ˆ๋‹ค."); + void ํ•ด๋‹น_Space_๋ฐ_๊ด€๋ จ๋œ_Job_Section_Task_RunningTask๋ฅผ_์‚ญ์ œํ•œ๋‹ค() { + spaceService.removeSpace(host.getId(), space.getId()); + + assertAll( + () -> assertThat(repository.findById(Space.class, space.getId())).isEmpty(), + () -> assertThat(repository.findById(Job.class, job.getId())).isEmpty(), + () -> assertThat(repository.findById(Section.class, section.getId())).isEmpty(), + () -> assertThat(repository.findById(Task.class, task.getId())).isEmpty(), + () -> assertThat(repository.findById(RunningTask.class, runningTask.getTaskId())).isEmpty() + ); } } + } + + @Nested + class changeSpace_๋ฉ”์„œ๋“œ๋Š” { @Nested - class ์กด์žฌํ•˜์ง€_์•Š๋Š”_Space_id๋ฅผ_์ž…๋ ฅ๋ฐ›์€_๊ฒฝ์šฐ { + class ์ž…๋ ฅ๋ฐ›์€_Host๊ฐ€_์กด์žฌํ•˜์ง€_์•Š๋Š”_๊ฒฝ์šฐ { - private Host host; + private static final long NON_EXIST_HOST_ID = 0L; + private static final long DUMMY_SPACE_ID = 1L; - @BeforeEach - void setUp() { - host = repository.save(Host_์ƒ์„ฑ("1234", 1234L)); - } + private final SpaceChangeRequest spaceChangeRequest = new SpaceChangeRequest("์ž ์‹ค ์บ ํผ์Šค", "changeImageUrl"); @Test void ์˜ˆ์™ธ๋ฅผ_๋ฐœ์ƒ์‹œํ‚จ๋‹ค() { - assertThatThrownBy(() -> spaceService.findSpace(host.getId(), 0L)) + assertThatThrownBy( + () -> spaceService.changeSpace(NON_EXIST_HOST_ID, DUMMY_SPACE_ID, spaceChangeRequest)) .isInstanceOf(NotFoundException.class) - .hasMessageContaining("์กด์žฌํ•˜์ง€ ์•Š๋Š” ๊ณต๊ฐ„์ž…๋‹ˆ๋‹ค."); + .hasMessageContaining("์กด์žฌํ•˜์ง€ ์•Š๋Š” ํ˜ธ์ŠคํŠธ์ž…๋‹ˆ๋‹ค."); } } @Nested - class ์ž…๋ ฅ๋ฐ›์€_Host๊ฐ€_์ž…๋ ฅ๋ฐ›์€_Space๋ฅผ_์†Œ์œ ํ•˜๊ณ _์žˆ๋Š”_๊ฒฝ์šฐ { - - private static final String SPACE_NAME = "์ž ์‹ค ์บ ํผ์Šค"; + class ์ž…๋ ฅ๋ฐ›์€_Space๊ฐ€_์กด์žฌํ•˜์ง€_์•Š๋Š”_๊ฒฝ์šฐ { - private Host host; - private Space space; + private static final long NON_EXIST_SPACE_ID = 0L; - @BeforeEach - void setUp() { - host = repository.save(Host_์ƒ์„ฑ("1234", 1234L)); - space = repository.save(Space_์ƒ์„ฑ(host, SPACE_NAME)); - } + private final Host host = repository.save(Host_์ƒ์„ฑ("1234", 1L)); + private final SpaceChangeRequest spaceChangeRequest = new SpaceChangeRequest("์ž ์‹ค ์บ ํผ์Šค", "changeImageUrl"); @Test - void Space_์‘๋‹ต์„_๋ฐ˜ํ™˜ํ•œ๋‹ค() { - SpaceResponse actual = spaceService.findSpace(host.getId(), space.getId()); - - assertAll( - () -> assertThat(actual.getId()).isEqualTo(space.getId()), - () -> assertThat(actual.getName()).isEqualTo(SPACE_NAME) - ); + void ์˜ˆ์™ธ๋ฅผ_๋ฐœ์ƒ์‹œํ‚จ๋‹ค() { + assertThatThrownBy( + () -> spaceService.changeSpace(host.getId(), NON_EXIST_SPACE_ID, spaceChangeRequest)) + .isInstanceOf(NotFoundException.class) + .hasMessageContaining("์กด์žฌํ•˜์ง€ ์•Š๋Š” ๊ณต๊ฐ„์ž…๋‹ˆ๋‹ค."); } } - } - - @Nested - class removeSpace_๋ฉ”์„œ๋“œ๋Š” { @Nested - class ์ž…๋ ฅ๋ฐ›์€_Host_id๊ฐ€_์กด์žฌํ•˜์ง€_์•Š๋Š”_๊ฒฝ์šฐ { - - private static final long NON_EXIST_HOST_ID = 0L; + class ์ž…๋ ฅ๋ฐ›์€_Host๊ฐ€_์ž…๋ ฅ๋ฐ›์€_Space๋ฅผ_์†Œ์œ ํ•˜์ง€_์•Š์€_๊ฒฝ์šฐ { - private Space space; - - @BeforeEach - void setUp() { - Host host = repository.save(Host_์ƒ์„ฑ("1234", 1234L)); - space = repository.save(Space_์ƒ์„ฑ(host, "์ž ์‹ค ์บ ํผ์Šค")); - } + private final Host host = repository.save(Host_์ƒ์„ฑ("1234", 1L)); + private final Host anotherHost = repository.save(Host_์ƒ์„ฑ("1234", 2L)); + private final Space space = repository.save(Space_์ƒ์„ฑ(anotherHost, "์ž ์‹ค ์บ ํผ์Šค")); + private final SpaceChangeRequest spaceChangeRequest = new SpaceChangeRequest("changeName", + "changeImageUrl"); @Test void ์˜ˆ์™ธ๋ฅผ_๋ฐœ์ƒ์‹œํ‚จ๋‹ค() { - assertThatThrownBy(() -> spaceService.removeSpace(NON_EXIST_HOST_ID, space.getId())) + assertThatThrownBy( + () -> spaceService.changeSpace(host.getId(), space.getId(), spaceChangeRequest)) .isInstanceOf(NotFoundException.class) - .hasMessageContaining("์กด์žฌํ•˜์ง€ ์•Š๋Š” ํ˜ธ์ŠคํŠธ์ž…๋‹ˆ๋‹ค."); + .hasMessageContaining("์กด์žฌํ•˜์ง€ ์•Š๋Š” ๊ณต๊ฐ„์ž…๋‹ˆ๋‹ค."); } } @Nested - class ์ž…๋ ฅ๋ฐ›์€_Host๊ฐ€_์ž…๋ ฅ๋ฐ›์€_Space๋ฅผ_์†Œ์œ ํ•˜๊ณ _์žˆ์ง€_์•Š์€_๊ฒฝ์šฐ { + class ์ž…๋ ฅ๋ฐ›์€_Space์˜_์ด๋ฆ„์ด_์ž…๋ ฅ๋ฐ›์€_Host๊ฐ€_์†Œ์œ ํ•œ_Space_์ด๋ฆ„๊ณผ_์ค‘๋ณต๋˜๋Š”_๊ฒฝ์šฐ { - private Host anotherHost; - private Space space; + private static final String SPACE_NAME = "์ž ์‹ค ์บ ํผ์Šค"; - @BeforeEach - void setUp() { - Host host = repository.save(Host_์ƒ์„ฑ("1234", 1234L)); - anotherHost = repository.save(Host_์ƒ์„ฑ("1234", 4567L)); - space = repository.save(Space_์ƒ์„ฑ(host, "์ž ์‹ค ์บ ํผ์Šค")); - } + private final Host host = repository.save(Host_์ƒ์„ฑ("1234", 1L)); + private final Space existSpace = repository.save(Space_์ƒ์„ฑ(host, SPACE_NAME)); + private final Space spaceToChangeName = repository.save(Space_์ƒ์„ฑ(host, "์„ ๋ฆ‰ ์บ ํผ์Šค")); + private final SpaceChangeRequest spaceChangeRequest = new SpaceChangeRequest(SPACE_NAME, "image"); @Test void ์˜ˆ์™ธ๋ฅผ_๋ฐœ์ƒ์‹œํ‚จ๋‹ค() { - assertThatThrownBy(() -> spaceService.removeSpace(anotherHost.getId(), space.getId())) - .isInstanceOf(NotFoundException.class); + assertThatThrownBy( + () -> spaceService.changeSpace(host.getId(), spaceToChangeName.getId(), spaceChangeRequest)) + .isInstanceOf(BusinessException.class) + .hasMessageContaining("์ด๋ฏธ ์กด์žฌํ•˜๋Š” ์ด๋ฆ„์ž…๋‹ˆ๋‹ค."); } } @Nested - class ์ž…๋ ฅ๋ฐ›์€_Space_id๊ฐ€_์กด์žฌํ•˜๋ฉด { - - private Host host; - private Space space; - private Job job; - private Section section; - private Task task; - private RunningTask runningTask; - - @BeforeEach - void setUp() { - host = repository.save(Host_์ƒ์„ฑ("1234", 1234L)); - space = repository.save(Space_์ƒ์„ฑ(host, "์ž ์‹ค ์บ ํผ์Šค")); - job = repository.save(Job_์ƒ์„ฑ(space, "์ฒญ์†Œ")); - section = repository.save(Section_์ƒ์„ฑ(job, "๋Œ€๊ฐ•์˜์‹ค")); - task = repository.save(Task_์ƒ์„ฑ(section, "์ฑ…์ƒ ๋‹ฆ๊ธฐ")); - runningTask = repository.save(RunningTask_์ƒ์„ฑ(task.getId(), false)); - } + class ์˜ฌ๋ฐ”๋ฅธ_์ž…๋ ฅ์„_๋ฐ›๋Š”_๊ฒฝ์šฐ { + + private static final String CHANGE_NAME = "์ž ์‹ค ์บ ํผ์Šค"; + private static final String CHANGE_IMG_URL = "changeUrl"; + + private final Host host = repository.save(Host_์ƒ์„ฑ("1234", 1L)); + private final Space space = repository.save(Space.builder() + .host(host) + .name(new Name("์ž ์‹ค ์บ ํผ์Šค")) + .imageUrl("imageUrl") + .createdAt(LocalDateTime.now()) + .build()); + private final SpaceChangeRequest spaceChangeRequest = new SpaceChangeRequest(CHANGE_NAME, CHANGE_IMG_URL); @Test - void ํ•ด๋‹น_Space_๋ฐ_๊ด€๋ จ๋œ_Job_Section_Task_RunningTask๋ฅผ_์‚ญ์ œํ•œ๋‹ค() { - spaceService.removeSpace(host.getId(), space.getId()); + void Space_์ด๋ฆ„์„_๋ณ€๊ฒฝํ•œ๋‹ค() { + spaceService.changeSpace(host.getId(), space.getId(), spaceChangeRequest); + Space actual = spaceRepository.getByHostAndId(host, space.getId()); assertAll( - () -> assertThat(repository.findById(Space.class, space.getId())).isEmpty(), - () -> assertThat(repository.findById(Job.class, job.getId())).isEmpty(), - () -> assertThat(repository.findById(Section.class, section.getId())).isEmpty(), - () -> assertThat(repository.findById(Task.class, task.getId())).isEmpty(), - () -> assertThat(repository.findById(RunningTask.class, runningTask.getTaskId())).isEmpty() + () -> assertThat(actual.getName()).isEqualTo(new Name(CHANGE_NAME)), + () -> assertThat(actual.getImageUrl()).isEqualTo(CHANGE_IMG_URL) ); } } diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 06b24265..b29f4ea2 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "@react-icons/all-files": "^4.1.0", "axios": "^0.27.2", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -41,7 +42,7 @@ "babel-cli": "^6.26.0", "babel-loader": "^8.2.5", "babel-plugin-module-resolver": "^4.1.0", - "clean-webpack-plugin": "^4.0.0", + "compression-webpack-plugin": "^10.0.0", "cross-env": "^7.0.3", "cypress": "^10.3.1", "cypress-react-selector": "^3.0.0", @@ -54,10 +55,10 @@ "html-webpack-plugin": "^5.5.0", "nanoid": "^4.0.0", "prettier": "^2.7.1", - "react-icons": "^4.4.0", "start-server-and-test": "^1.14.0", "ts-loader": "^9.3.1", "typescript": "^4.7.4", + "url-loader": "^4.1.1", "webpack": "^5.73.0", "webpack-cli": "^4.10.0", "webpack-dev-server": "^4.9.3" @@ -2887,6 +2888,14 @@ "read-package-json-fast": "^2.0.1" } }, + "node_modules/@react-icons/all-files": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@react-icons/all-files/-/all-files-4.1.0.tgz", + "integrity": "sha512-hxBI2UOuVaI3O/BhQfhtb4kcGn9ft12RWAFVMUeNjqqhLsHvFtzIkFaptBJpFDANTKoDfdVoHTKZDlwKCACbMQ==", + "peerDependencies": { + "react": "*" + } + }, "node_modules/@sideway/address": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", @@ -6206,21 +6215,6 @@ "node": ">=6" } }, - "node_modules/clean-webpack-plugin": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-4.0.0.tgz", - "integrity": "sha512-WuWE1nyTNAyW5T7oNyys2EN0cfP2fdRxhxnIQWiAp0bMabPdHhoGxM8A6YL2GhqwgrPnnaemVE7nv5XJ2Fhh2w==", - "dev": true, - "dependencies": { - "del": "^4.1.1" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "webpack": ">=4.0.0 <6.0.0" - } - }, "node_modules/cli-boxes": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", @@ -6590,6 +6584,79 @@ "node": ">= 0.8.0" } }, + "node_modules/compression-webpack-plugin": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/compression-webpack-plugin/-/compression-webpack-plugin-10.0.0.tgz", + "integrity": "sha512-wLXLIBwpul/ALcm7Aj+69X0pYT3BYt6DdPn3qrgBIh9YejV9Bju9ShhlAsjujLyWMo6SAweFIWaUoFmXZNuNrg==", + "dev": true, + "dependencies": { + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "node_modules/compression-webpack-plugin/node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/compression-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/compression-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/compression-webpack-plugin/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/compression/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -7318,91 +7385,6 @@ "node": ">=0.10.0" } }, - "node_modules/del": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", - "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", - "dev": true, - "dependencies": { - "@types/glob": "^7.1.1", - "globby": "^6.1.0", - "is-path-cwd": "^2.0.0", - "is-path-in-cwd": "^2.0.0", - "p-map": "^2.0.0", - "pify": "^4.0.1", - "rimraf": "^2.6.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/del/node_modules/array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", - "dev": true, - "dependencies": { - "array-uniq": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/del/node_modules/globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", - "dev": true, - "dependencies": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/del/node_modules/globby/node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/del/node_modules/p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/del/node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/del/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -10877,39 +10859,6 @@ "node": ">=8" } }, - "node_modules/is-path-cwd": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", - "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/is-path-in-cwd": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", - "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", - "dev": true, - "dependencies": { - "is-path-inside": "^2.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/is-path-inside": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", - "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", - "dev": true, - "dependencies": { - "path-is-inside": "^1.0.2" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/is-plain-obj": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", @@ -14300,27 +14249,6 @@ "node": ">=0.10.0" } }, - "node_modules/pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", - "dev": true, - "dependencies": { - "pinkie": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", @@ -14913,15 +14841,6 @@ "react": ">=16.13.1" } }, - "node_modules/react-icons": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.4.0.tgz", - "integrity": "sha512-fSbvHeVYo/B5/L4VhB7sBA1i2tS8MkT0Hb9t2H1AVPkwGfVHLJCqyr2Py9dKMxsyM63Eng1GkdZfbWj+Fmv8Rg==", - "dev": true, - "peerDependencies": { - "react": "*" - } - }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -17932,6 +17851,33 @@ "deprecated": "Please see https://github.com/lydell/urix#deprecated", "dev": true }, + "node_modules/url-loader": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", + "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "mime-types": "^2.1.27", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "file-loader": "*", + "webpack": "^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "file-loader": { + "optional": true + } + } + }, "node_modules/url-parse-lax": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", @@ -22264,6 +22210,12 @@ "read-package-json-fast": "^2.0.1" } }, + "@react-icons/all-files": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@react-icons/all-files/-/all-files-4.1.0.tgz", + "integrity": "sha512-hxBI2UOuVaI3O/BhQfhtb4kcGn9ft12RWAFVMUeNjqqhLsHvFtzIkFaptBJpFDANTKoDfdVoHTKZDlwKCACbMQ==", + "requires": {} + }, "@sideway/address": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", @@ -24943,15 +24895,6 @@ "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true }, - "clean-webpack-plugin": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-4.0.0.tgz", - "integrity": "sha512-WuWE1nyTNAyW5T7oNyys2EN0cfP2fdRxhxnIQWiAp0bMabPdHhoGxM8A6YL2GhqwgrPnnaemVE7nv5XJ2Fhh2w==", - "dev": true, - "requires": { - "del": "^4.1.1" - } - }, "cli-boxes": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", @@ -25262,6 +25205,57 @@ } } }, + "compression-webpack-plugin": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/compression-webpack-plugin/-/compression-webpack-plugin-10.0.0.tgz", + "integrity": "sha512-wLXLIBwpul/ALcm7Aj+69X0pYT3BYt6DdPn3qrgBIh9YejV9Bju9ShhlAsjujLyWMo6SAweFIWaUoFmXZNuNrg==", + "dev": true, + "requires": { + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + } + } + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -25802,74 +25796,6 @@ "isobject": "^3.0.1" } }, - "del": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", - "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", - "dev": true, - "requires": { - "@types/glob": "^7.1.1", - "globby": "^6.1.0", - "is-path-cwd": "^2.0.0", - "is-path-in-cwd": "^2.0.0", - "p-map": "^2.0.0", - "pify": "^4.0.1", - "rimraf": "^2.6.3" - }, - "dependencies": { - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", - "dev": true, - "requires": { - "array-uniq": "^1.0.1" - } - }, - "globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true - } - } - }, - "p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", - "dev": true - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } - } - }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -28537,30 +28463,6 @@ "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", "dev": true }, - "is-path-cwd": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", - "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", - "dev": true - }, - "is-path-in-cwd": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", - "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", - "dev": true, - "requires": { - "is-path-inside": "^2.1.0" - } - }, - "is-path-inside": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", - "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", - "dev": true, - "requires": { - "path-is-inside": "^1.0.2" - } - }, "is-plain-obj": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", @@ -31181,21 +31083,6 @@ "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", - "dev": true - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", - "dev": true, - "requires": { - "pinkie": "^2.0.0" - } - }, "pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", @@ -31628,13 +31515,6 @@ "@babel/runtime": "^7.12.5" } }, - "react-icons": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.4.0.tgz", - "integrity": "sha512-fSbvHeVYo/B5/L4VhB7sBA1i2tS8MkT0Hb9t2H1AVPkwGfVHLJCqyr2Py9dKMxsyM63Eng1GkdZfbWj+Fmv8Rg==", - "dev": true, - "requires": {} - }, "react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -33969,6 +33849,17 @@ "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==", "dev": true }, + "url-loader": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", + "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==", + "dev": true, + "requires": { + "loader-utils": "^2.0.0", + "mime-types": "^2.1.27", + "schema-utils": "^3.0.0" + } + }, "url-parse-lax": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 2bf45369..069b03ee 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -3,11 +3,11 @@ "version": "1.0.0", "description": "Gong Check Project", "scripts": { - "start": "cross-env NODE_ENV=development webpack serve", - "build": "cross-env NODE_ENV=production webpack", - "build-local": "cross-env NODE_ENV=development webpack", - "build-staging": "cross-env NODE_ENV=staging webpack", - "build-release": "cross-env NODE_ENV=production webpack", + "start": "cross-env NODE_ENV=development webpack serve --config webpack.dev.js", + "build": "cross-env NODE_ENV=production webpack --config webpack.prod.js", + "build:local": "cross-env NODE_ENV=development webpack --config webpack.dev.js", + "build:staging": "cross-env NODE_ENV=staging webpack --config webpack.prod.js", + "build:release": "cross-env NODE_ENV=production webpack --config webpack.prod.js", "lhci": "lhci autorun", "prettier": "npx prettier --write 'src/**/*.{tsx,ts,css,html,json}'", "cypress:open": "cypress open", @@ -22,6 +22,7 @@ "author": "", "license": "ISC", "dependencies": { + "@react-icons/all-files": "^4.1.0", "axios": "^0.27.2", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -54,7 +55,7 @@ "babel-cli": "^6.26.0", "babel-loader": "^8.2.5", "babel-plugin-module-resolver": "^4.1.0", - "clean-webpack-plugin": "^4.0.0", + "compression-webpack-plugin": "^10.0.0", "cross-env": "^7.0.3", "cypress": "^10.3.1", "cypress-react-selector": "^3.0.0", @@ -67,10 +68,10 @@ "html-webpack-plugin": "^5.5.0", "nanoid": "^4.0.0", "prettier": "^2.7.1", - "react-icons": "^4.4.0", "start-server-and-test": "^1.14.0", "ts-loader": "^9.3.1", "typescript": "^4.7.4", + "url-loader": "^4.1.1", "webpack": "^5.73.0", "webpack-cli": "^4.10.0", "webpack-dev-server": "^4.9.3" diff --git a/frontend/public/index.html b/frontend/public/index.html index bee50722..0db3c929 100644 --- a/frontend/public/index.html +++ b/frontend/public/index.html @@ -5,6 +5,13 @@ + Gong Check diff --git a/frontend/src/Transitions.tsx b/frontend/src/Transitions.tsx index 03d3e975..8765f0d6 100644 --- a/frontend/src/Transitions.tsx +++ b/frontend/src/Transitions.tsx @@ -10,7 +10,7 @@ interface TransitionsProps { const childFactoryCreator = (props: { classNames: string }) => (child: React.ReactElement) => cloneElement(child, props); -const Transitions: React.FC = ({ transition = 'right', pageKey, children }) => ( +const Transitions: React.FC = ({ transition, pageKey, children }) => ( {children} diff --git a/frontend/src/assets/createSpace-280w.webp b/frontend/src/assets/createSpace-280w.webp new file mode 100644 index 00000000..0d26c0bb Binary files /dev/null and b/frontend/src/assets/createSpace-280w.webp differ diff --git a/frontend/src/assets/createSpace-360w.webp b/frontend/src/assets/createSpace-360w.webp new file mode 100644 index 00000000..aa54ad85 Binary files /dev/null and b/frontend/src/assets/createSpace-360w.webp differ diff --git a/frontend/src/assets/createSpace-540w.webp b/frontend/src/assets/createSpace-540w.webp new file mode 100644 index 00000000..24a9769e Binary files /dev/null and b/frontend/src/assets/createSpace-540w.webp differ diff --git a/frontend/src/assets/createSpace-800w.webp b/frontend/src/assets/createSpace-800w.webp new file mode 100644 index 00000000..079ba11f Binary files /dev/null and b/frontend/src/assets/createSpace-800w.webp differ diff --git a/frontend/src/assets/createSpace-fallback.jpg b/frontend/src/assets/createSpace-fallback.jpg new file mode 100644 index 00000000..cc8e5ece Binary files /dev/null and b/frontend/src/assets/createSpace-fallback.jpg differ diff --git a/frontend/src/assets/dashboard-280w.webp b/frontend/src/assets/dashboard-280w.webp new file mode 100644 index 00000000..44fbea8f Binary files /dev/null and b/frontend/src/assets/dashboard-280w.webp differ diff --git a/frontend/src/assets/dashboard-360w.webp b/frontend/src/assets/dashboard-360w.webp new file mode 100644 index 00000000..d3a00d45 Binary files /dev/null and b/frontend/src/assets/dashboard-360w.webp differ diff --git a/frontend/src/assets/dashboard-540w.webp b/frontend/src/assets/dashboard-540w.webp new file mode 100644 index 00000000..ccd07d2b Binary files /dev/null and b/frontend/src/assets/dashboard-540w.webp differ diff --git a/frontend/src/assets/dashboard-800w.webp b/frontend/src/assets/dashboard-800w.webp new file mode 100644 index 00000000..677deb05 Binary files /dev/null and b/frontend/src/assets/dashboard-800w.webp differ diff --git a/frontend/src/assets/dashboard-fallback.jpg b/frontend/src/assets/dashboard-fallback.jpg new file mode 100644 index 00000000..39307209 Binary files /dev/null and b/frontend/src/assets/dashboard-fallback.jpg differ diff --git a/frontend/src/assets/defaultSpaceImage.webp b/frontend/src/assets/defaultSpaceImage.webp new file mode 100644 index 00000000..41b86716 Binary files /dev/null and b/frontend/src/assets/defaultSpaceImage.webp differ diff --git a/frontend/src/assets/edit-280w.webp b/frontend/src/assets/edit-280w.webp new file mode 100644 index 00000000..e006b271 Binary files /dev/null and b/frontend/src/assets/edit-280w.webp differ diff --git a/frontend/src/assets/edit-360w.webp b/frontend/src/assets/edit-360w.webp new file mode 100644 index 00000000..794106c7 Binary files /dev/null and b/frontend/src/assets/edit-360w.webp differ diff --git a/frontend/src/assets/edit-540w.webp b/frontend/src/assets/edit-540w.webp new file mode 100644 index 00000000..b72f35b6 Binary files /dev/null and b/frontend/src/assets/edit-540w.webp differ diff --git a/frontend/src/assets/edit-800w.webp b/frontend/src/assets/edit-800w.webp new file mode 100644 index 00000000..23f5c28b Binary files /dev/null and b/frontend/src/assets/edit-800w.webp differ diff --git a/frontend/src/assets/edit-fallback.jpg b/frontend/src/assets/edit-fallback.jpg new file mode 100644 index 00000000..6986a8d6 Binary files /dev/null and b/frontend/src/assets/edit-fallback.jpg differ diff --git a/frontend/src/assets/edit2-280w.webp b/frontend/src/assets/edit2-280w.webp new file mode 100644 index 00000000..1a5cf63b Binary files /dev/null and b/frontend/src/assets/edit2-280w.webp differ diff --git a/frontend/src/assets/edit2-360w.webp b/frontend/src/assets/edit2-360w.webp new file mode 100644 index 00000000..e59e9ebd Binary files /dev/null and b/frontend/src/assets/edit2-360w.webp differ diff --git a/frontend/src/assets/edit2-540w.webp b/frontend/src/assets/edit2-540w.webp new file mode 100644 index 00000000..8e8d42af Binary files /dev/null and b/frontend/src/assets/edit2-540w.webp differ diff --git a/frontend/src/assets/edit2-800w.webp b/frontend/src/assets/edit2-800w.webp new file mode 100644 index 00000000..7e64ceb4 Binary files /dev/null and b/frontend/src/assets/edit2-800w.webp differ diff --git a/frontend/src/assets/edit2-fallback.jpg b/frontend/src/assets/edit2-fallback.jpg new file mode 100644 index 00000000..ac7a9108 Binary files /dev/null and b/frontend/src/assets/edit2-fallback.jpg differ diff --git a/frontend/src/assets/emptyFolder.webp b/frontend/src/assets/emptyFolder.webp new file mode 100644 index 00000000..013ca9d8 Binary files /dev/null and b/frontend/src/assets/emptyFolder.webp differ diff --git a/frontend/src/assets/homeCover-360w.webp b/frontend/src/assets/homeCover-360w.webp new file mode 100644 index 00000000..944995ac Binary files /dev/null and b/frontend/src/assets/homeCover-360w.webp differ diff --git a/frontend/src/assets/homeCover-480w.webp b/frontend/src/assets/homeCover-480w.webp new file mode 100644 index 00000000..fb7a4421 Binary files /dev/null and b/frontend/src/assets/homeCover-480w.webp differ diff --git a/frontend/src/assets/homeCover-fallback.png b/frontend/src/assets/homeCover-fallback.png new file mode 100644 index 00000000..7b7ca249 Binary files /dev/null and b/frontend/src/assets/homeCover-fallback.png differ diff --git a/frontend/src/assets/homeCover.webp b/frontend/src/assets/homeCover.webp new file mode 100644 index 00000000..0f3ae963 Binary files /dev/null and b/frontend/src/assets/homeCover.webp differ diff --git a/frontend/src/assets/logoTitle.webp b/frontend/src/assets/logoTitle.webp new file mode 100644 index 00000000..3cf4a487 Binary files /dev/null and b/frontend/src/assets/logoTitle.webp differ diff --git a/frontend/src/assets/mobileView1-160w.webp b/frontend/src/assets/mobileView1-160w.webp new file mode 100644 index 00000000..15c69939 Binary files /dev/null and b/frontend/src/assets/mobileView1-160w.webp differ diff --git a/frontend/src/assets/mobileView1-240w.webp b/frontend/src/assets/mobileView1-240w.webp new file mode 100644 index 00000000..279bdb3f Binary files /dev/null and b/frontend/src/assets/mobileView1-240w.webp differ diff --git a/frontend/src/assets/mobileView1-320w.webp b/frontend/src/assets/mobileView1-320w.webp new file mode 100644 index 00000000..b0021123 Binary files /dev/null and b/frontend/src/assets/mobileView1-320w.webp differ diff --git a/frontend/src/assets/mobileView1-480w.webp b/frontend/src/assets/mobileView1-480w.webp new file mode 100644 index 00000000..00f2e717 Binary files /dev/null and b/frontend/src/assets/mobileView1-480w.webp differ diff --git a/frontend/src/assets/mobileView1.png b/frontend/src/assets/mobileView1-fallback.png similarity index 100% rename from frontend/src/assets/mobileView1.png rename to frontend/src/assets/mobileView1-fallback.png diff --git a/frontend/src/assets/mobileView2-160w.webp b/frontend/src/assets/mobileView2-160w.webp new file mode 100644 index 00000000..9cac1f1a Binary files /dev/null and b/frontend/src/assets/mobileView2-160w.webp differ diff --git a/frontend/src/assets/mobileView2-240w.webp b/frontend/src/assets/mobileView2-240w.webp new file mode 100644 index 00000000..83d30f98 Binary files /dev/null and b/frontend/src/assets/mobileView2-240w.webp differ diff --git a/frontend/src/assets/mobileView2-320w.webp b/frontend/src/assets/mobileView2-320w.webp new file mode 100644 index 00000000..856b5208 Binary files /dev/null and b/frontend/src/assets/mobileView2-320w.webp differ diff --git a/frontend/src/assets/mobileView2-480w.webp b/frontend/src/assets/mobileView2-480w.webp new file mode 100644 index 00000000..2640abd9 Binary files /dev/null and b/frontend/src/assets/mobileView2-480w.webp differ diff --git a/frontend/src/assets/mobileView2.png b/frontend/src/assets/mobileView2-fallback.png similarity index 100% rename from frontend/src/assets/mobileView2.png rename to frontend/src/assets/mobileView2-fallback.png diff --git a/frontend/src/assets/mobileView3-160w.webp b/frontend/src/assets/mobileView3-160w.webp new file mode 100644 index 00000000..a22b3c89 Binary files /dev/null and b/frontend/src/assets/mobileView3-160w.webp differ diff --git a/frontend/src/assets/mobileView3-240w.webp b/frontend/src/assets/mobileView3-240w.webp new file mode 100644 index 00000000..a794016d Binary files /dev/null and b/frontend/src/assets/mobileView3-240w.webp differ diff --git a/frontend/src/assets/mobileView3-320w.webp b/frontend/src/assets/mobileView3-320w.webp new file mode 100644 index 00000000..b1596085 Binary files /dev/null and b/frontend/src/assets/mobileView3-320w.webp differ diff --git a/frontend/src/assets/mobileView3-480w.webp b/frontend/src/assets/mobileView3-480w.webp new file mode 100644 index 00000000..40ae1923 Binary files /dev/null and b/frontend/src/assets/mobileView3-480w.webp differ diff --git a/frontend/src/assets/mobileView3.png b/frontend/src/assets/mobileView3-fallback.png similarity index 100% rename from frontend/src/assets/mobileView3.png rename to frontend/src/assets/mobileView3-fallback.png diff --git a/frontend/src/assets/mobileView4-160w.webp b/frontend/src/assets/mobileView4-160w.webp new file mode 100644 index 00000000..35c7d306 Binary files /dev/null and b/frontend/src/assets/mobileView4-160w.webp differ diff --git a/frontend/src/assets/mobileView4-240w.webp b/frontend/src/assets/mobileView4-240w.webp new file mode 100644 index 00000000..851b10fb Binary files /dev/null and b/frontend/src/assets/mobileView4-240w.webp differ diff --git a/frontend/src/assets/mobileView4-320w.webp b/frontend/src/assets/mobileView4-320w.webp new file mode 100644 index 00000000..b4d0f251 Binary files /dev/null and b/frontend/src/assets/mobileView4-320w.webp differ diff --git a/frontend/src/assets/mobileView4-480w.webp b/frontend/src/assets/mobileView4-480w.webp new file mode 100644 index 00000000..897921d3 Binary files /dev/null and b/frontend/src/assets/mobileView4-480w.webp differ diff --git a/frontend/src/assets/mobileView4.png b/frontend/src/assets/mobileView4-fallback.png similarity index 100% rename from frontend/src/assets/mobileView4.png rename to frontend/src/assets/mobileView4-fallback.png diff --git a/frontend/src/assets/navigationLogo.jpg b/frontend/src/assets/navigationLogo.jpg new file mode 100644 index 00000000..24743e99 Binary files /dev/null and b/frontend/src/assets/navigationLogo.jpg differ diff --git a/frontend/src/assets/navigationLogo.webp b/frontend/src/assets/navigationLogo.webp new file mode 100644 index 00000000..6c0d2787 Binary files /dev/null and b/frontend/src/assets/navigationLogo.webp differ diff --git a/frontend/src/components/common/Button/styles.ts b/frontend/src/components/common/Button/styles.ts index 4018ab41..737da9cc 100644 --- a/frontend/src/components/common/Button/styles.ts +++ b/frontend/src/components/common/Button/styles.ts @@ -4,10 +4,10 @@ import theme from '@/styles/theme'; const button = css` background: ${theme.colors.primary}; - width: 224px; + width: auto; height: 48px; border-radius: 12px; - font-size: 16px; + font-size: 14px; font-weight: 600; color: ${theme.colors.white}; margin: 24px; diff --git a/frontend/src/components/common/Checkbox/styles.ts b/frontend/src/components/common/Checkbox/styles.ts index 3de4c0d2..22e852f4 100644 --- a/frontend/src/components/common/Checkbox/styles.ts +++ b/frontend/src/components/common/Checkbox/styles.ts @@ -11,6 +11,7 @@ const input = css` const label = css` width: 28px; + min-width: 28px; height: 28px; border: 1px solid ${theme.colors.shadow30}; border-radius: 10px; diff --git a/frontend/src/components/common/Dimmer/index.tsx b/frontend/src/components/common/Dimmer/index.tsx index ef47027d..647ef760 100644 --- a/frontend/src/components/common/Dimmer/index.tsx +++ b/frontend/src/components/common/Dimmer/index.tsx @@ -4,11 +4,10 @@ import styles from './styles'; interface DimmerProps { children: React.ReactNode; - mode: 'full' | 'mobile'; isAbleClick?: boolean; } -const Dimmer: React.FC = ({ children, isAbleClick = true, mode = 'full' }) => { +const Dimmer: React.FC = ({ children, isAbleClick = true }) => { const { closeModal } = useModal(); const onClickDimmed = (e: React.MouseEvent) => { @@ -16,7 +15,7 @@ const Dimmer: React.FC = ({ children, isAbleClick = true, mode = 'f }; return ( -
+
{children}
); diff --git a/frontend/src/components/common/Dimmer/styles.ts b/frontend/src/components/common/Dimmer/styles.ts index 1bd2914c..e17d300a 100644 --- a/frontend/src/components/common/Dimmer/styles.ts +++ b/frontend/src/components/common/Dimmer/styles.ts @@ -2,9 +2,8 @@ import { css } from '@emotion/react'; import theme from '@/styles/theme'; -const dimmer = (mode: 'full' | 'mobile') => css` +const dimmer = css` background-color: ${theme.colors.shadow80}; - max-width: ${mode === 'full' ? '100vw' : '414px'}; width: 100vw; height: 100vh; display: flex; diff --git a/frontend/src/components/common/GitHubLoginButton/index.tsx b/frontend/src/components/common/GitHubLoginButton/index.tsx index e9d5aa08..dedbb62d 100644 --- a/frontend/src/components/common/GitHubLoginButton/index.tsx +++ b/frontend/src/components/common/GitHubLoginButton/index.tsx @@ -1,4 +1,4 @@ -import { GoMarkGithub } from 'react-icons/go'; +import { GoMarkGithub } from '@react-icons/all-files/go/GoMarkGithub'; import styles from './styles'; diff --git a/frontend/src/components/common/GitHubLoginButton/styles.ts b/frontend/src/components/common/GitHubLoginButton/styles.ts index 84339728..a6613032 100644 --- a/frontend/src/components/common/GitHubLoginButton/styles.ts +++ b/frontend/src/components/common/GitHubLoginButton/styles.ts @@ -4,15 +4,16 @@ const wrapper = css` display: flex; justify-content: center; align-items: center; - width: 260px; + width: auto; border-radius: 24px; color: white; background-color: #21262c; + padding: 0 16px; `; const text = css` - margin-left: 16px; - font-size: 24px; + margin-left: 12px; + font-size: 18px; `; const styles = { wrapper, text }; diff --git a/frontend/src/components/common/LazyImage/index.tsx b/frontend/src/components/common/LazyImage/index.tsx new file mode 100644 index 00000000..fc717178 --- /dev/null +++ b/frontend/src/components/common/LazyImage/index.tsx @@ -0,0 +1,34 @@ +import { useEffect, useRef, useState } from 'react'; + +interface LazyImageProps extends React.ImgHTMLAttributes { + imageUrl: string | undefined; +} + +const LazyImage: React.FC = ({ imageUrl, ...props }) => { + const lazyImageRef = useRef(null); + const observerRef = useRef(); + const [isLoaded, setIsLoaded] = useState(false); + + const intersectionCallBack = (entries: IntersectionObserverEntry[], io: IntersectionObserver) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + io.unobserve(entry.target); + setIsLoaded(true); + } + }); + }; + + useEffect(() => { + if (!observerRef.current) { + observerRef.current = new IntersectionObserver(intersectionCallBack); + } + + lazyImageRef.current && observerRef.current.observe(lazyImageRef.current); + + return () => observerRef.current && observerRef.current.disconnect(); + }, []); + + return ; +}; + +export default LazyImage; diff --git a/frontend/src/components/common/SlideMenu/index.tsx b/frontend/src/components/common/SlideMenu/index.tsx new file mode 100644 index 00000000..aa2c8a70 --- /dev/null +++ b/frontend/src/components/common/SlideMenu/index.tsx @@ -0,0 +1,12 @@ +import styles from './styles'; + +interface SlideMenuProps { + children: React.ReactNode; + isShowMenu: boolean | null; +} + +const SlideMenu: React.FC = ({ children, isShowMenu }) => { + return
{children}
; +}; + +export default SlideMenu; diff --git a/frontend/src/components/common/SlideMenu/styles.ts b/frontend/src/components/common/SlideMenu/styles.ts new file mode 100644 index 00000000..a16e5c34 --- /dev/null +++ b/frontend/src/components/common/SlideMenu/styles.ts @@ -0,0 +1,19 @@ +import { css } from '@emotion/react'; + +import animation from '@/styles/animation'; +import theme from '@/styles/theme'; + +const slideMenu = (isShowMenu: boolean | null) => css` + position: absolute; + min-height: 100vh; + background-color: ${theme.colors.white}; + z-index: 1000000; + width: 100%; + + animation: ${isShowMenu === true ? animation.navigatorOpen : animation.navigatorClose} 0.2s ease-out; + animation-fill-mode: forwards; +`; + +const styles = { slideMenu }; + +export default styles; diff --git a/frontend/src/components/common/Sticky/index.tsx b/frontend/src/components/common/Sticky/index.tsx new file mode 100644 index 00000000..0f5aef32 --- /dev/null +++ b/frontend/src/components/common/Sticky/index.tsx @@ -0,0 +1,33 @@ +import styles from './style'; +import { css } from '@emotion/react'; +import { useEffect, useRef, useState } from 'react'; + +import useScroll from '@/hooks/useScroll'; + +import theme from '@/styles/theme'; + +interface StickyProps { + children: React.ReactNode; + defaultPosition: number; +} + +const Sticky: React.FC = ({ children, defaultPosition }) => { + const [isActiveSticky, setIsActiveSticky] = useState(false); + + const progressBarRef = useRef(null); + const { scrollPosition } = useScroll(); + + useEffect(() => { + const isActive = progressBarRef.current?.offsetTop! > defaultPosition; + + setIsActiveSticky(isActive); + }, [scrollPosition]); + + return ( +
+ {children} +
+ ); +}; + +export default Sticky; diff --git a/frontend/src/components/common/Sticky/style.ts b/frontend/src/components/common/Sticky/style.ts new file mode 100644 index 00000000..720556cd --- /dev/null +++ b/frontend/src/components/common/Sticky/style.ts @@ -0,0 +1,24 @@ +import { css } from '@emotion/react'; + +import theme from '@/styles/theme'; + +const sticky = (isActiveSticky: boolean) => css` + position: sticky; + top: 0; + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; + padding-top: 16px; + background-color: ${theme.colors.white}; + z-index: 1; + + box-shadow: ${isActiveSticky ? `2px 2px 2px 0px ${theme.colors.shadow30}` : ''}; +`; + +const styles = { + sticky, +}; + +export default styles; diff --git a/frontend/src/components/common/ToastBar/index.tsx b/frontend/src/components/common/ToastBar/index.tsx index cf63c86e..695261c6 100644 --- a/frontend/src/components/common/ToastBar/index.tsx +++ b/frontend/src/components/common/ToastBar/index.tsx @@ -1,5 +1,5 @@ -import { AiOutlineCheckCircle } from 'react-icons/ai'; -import { BiError } from 'react-icons/bi'; +import { AiOutlineCheckCircle } from '@react-icons/all-files/ai/AiOutlineCheckCircle'; +import { BiError } from '@react-icons/all-files/bi/BiError'; import { useRecoilValue } from 'recoil'; import { toastState } from '@/recoil/toast'; diff --git a/frontend/src/components/host/ImageBox/index.tsx b/frontend/src/components/host/ImageBox/index.tsx index f4a22f85..3c28d751 100644 --- a/frontend/src/components/host/ImageBox/index.tsx +++ b/frontend/src/components/host/ImageBox/index.tsx @@ -1,4 +1,4 @@ -import { HiPlus } from 'react-icons/hi'; +import { HiPlus } from '@react-icons/all-files/hi/HiPlus'; import styles from './styles'; diff --git a/frontend/src/components/host/ImageBox/styles.ts b/frontend/src/components/host/ImageBox/styles.ts index aeb717c8..4bafeb96 100644 --- a/frontend/src/components/host/ImageBox/styles.ts +++ b/frontend/src/components/host/ImageBox/styles.ts @@ -30,6 +30,7 @@ const imageChangeBox = (imageUrl: string, borderStyle?: string) => css` `; const imageInput = css` + width: 100%; opacity: 0; z-index: -1; `; diff --git a/frontend/src/components/host/JobControl/styles.ts b/frontend/src/components/host/JobControl/styles.ts index 7e954b18..b7947cfb 100644 --- a/frontend/src/components/host/JobControl/styles.ts +++ b/frontend/src/components/host/JobControl/styles.ts @@ -11,29 +11,25 @@ const header = css` border-bottom: 1px solid ${theme.colors.gray300}; padding: 16px 32px; font-size: 16px; - - @media screen and (max-width: 720px) { - font-size: 14px; - } `; const createButton = css` margin: 0; margin-left: 12px; - font-size: 1.2em; + font-size: 1.1em; width: fit-content; height: fit-content; - padding: 12px; + padding: 8px 10px; background-color: ${theme.colors.green}; `; const jobNameInput = css` border: none; border-radius: 12px; - width: 55%; + width: 64%; height: 1.5em; padding: 1em; - font-size: 1.5em; + font-size: 1.2em; font-weight: 500; margin: 12px 0; background-color: ${theme.colors.white}; diff --git a/frontend/src/components/host/JobBox/index.tsx b/frontend/src/components/host/JobListCard/JobBox/index.tsx similarity index 100% rename from frontend/src/components/host/JobBox/index.tsx rename to frontend/src/components/host/JobListCard/JobBox/index.tsx diff --git a/frontend/src/components/host/JobBox/styles.ts b/frontend/src/components/host/JobListCard/JobBox/styles.ts similarity index 100% rename from frontend/src/components/host/JobBox/styles.ts rename to frontend/src/components/host/JobListCard/JobBox/styles.ts diff --git a/frontend/src/components/host/JobBox/useJobBox.tsx b/frontend/src/components/host/JobListCard/JobBox/useJobBox.tsx similarity index 100% rename from frontend/src/components/host/JobBox/useJobBox.tsx rename to frontend/src/components/host/JobListCard/JobBox/useJobBox.tsx diff --git a/frontend/src/components/host/JobListCard/index.tsx b/frontend/src/components/host/JobListCard/index.tsx index a14e29c6..6e175973 100644 --- a/frontend/src/components/host/JobListCard/index.tsx +++ b/frontend/src/components/host/JobListCard/index.tsx @@ -1,11 +1,12 @@ import useJobListCard from './useJobListCard'; import Button from '@/components/common/Button'; -import JobBox from '@/components/host/JobBox'; +import JobBox from '@/components/host/JobListCard/JobBox'; import { JobType } from '@/types'; import emptyFolder from '@/assets/emptyFolder.png'; +import emptyFolderWebp from '@/assets/emptyFolder.webp'; import styles from './styles'; @@ -19,7 +20,7 @@ const JobListCard: React.FC = ({ jobs }) => { return (
- ๊ณต๊ฐ„ ์—…๋ฌด ๋ชฉ๋ก +

์—…๋ฌด ๋ชฉ๋ก

@@ -27,8 +28,8 @@ const JobListCard: React.FC = ({ jobs }) => {
{jobs.length === 0 ? (
- -
์ƒ์„ฑ๋œ ์—…๋ฌด๊ฐ€ ์—†์–ด์š”.
+ +
์ƒ์„ฑ๋œ ์—…๋ฌด๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.
) : ( jobs.map(job => ) diff --git a/frontend/src/components/host/JobListCard/styles.ts b/frontend/src/components/host/JobListCard/styles.ts index 2a1d7465..ec75b1d3 100644 --- a/frontend/src/components/host/JobListCard/styles.ts +++ b/frontend/src/components/host/JobListCard/styles.ts @@ -4,19 +4,27 @@ import theme from '@/styles/theme'; const layout = css` min-width: 320px; - width: 100%; - height: 30.2rem; background-color: ${theme.colors.white}; box-shadow: 2px 2px 2px 2px ${theme.colors.shadow10}; border-radius: 8px; + + @media screen and (min-width: 1024px) { + height: 452px; + width: 100%; + } + + @media screen and (max-width: 1023px) { + height: 360px; + width: 90%; + } `; const title = css` display: flex; align-items: center; justify-content: space-between; - padding: 1rem 1.25rem; - font-size: 1.4rem; + padding: 0 24px; + font-size: 1.2rem; border-bottom: 1px solid ${theme.colors.gray300}; `; @@ -49,11 +57,8 @@ const jobList = css` const newJobButton = css` width: auto; height: 2rem; - padding: 6px 10px; - font-weight: 500; - font-size: 1rem; + font-size: 0.9rem; padding: 0 12px; - margin: 0; `; @@ -84,7 +89,9 @@ const empty = css` img { max-width: 100px; + width: 100px; margin-bottom: 12px; + aspect-ratio: auto 1 / 1; } `; const styles = { layout, title, jobListWrapper, jobList, newJobButton, updateButton, deleteButton, empty }; diff --git a/frontend/src/components/host/LandingView/FloatingActionButton/index.tsx b/frontend/src/components/host/LandingView/FloatingActionButton/index.tsx new file mode 100644 index 00000000..a2b068f3 --- /dev/null +++ b/frontend/src/components/host/LandingView/FloatingActionButton/index.tsx @@ -0,0 +1,22 @@ +import useFloatingActionButton from './useFloatingActionButton'; +import { RefObject } from 'react'; + +import styles from './styles'; + +interface FloatingActionButtonProps { + mainRef: RefObject; +} + +const FloatingActionButton: React.FC = ({ mainRef }) => { + const { eventNumber, onClick } = useFloatingActionButton(mainRef); + + return ( +
+
+ ์‹œ์ž‘ํ•˜๊ธฐ +
+
+ ); +}; + +export default FloatingActionButton; diff --git a/frontend/src/components/host/LandingView/FloatingActionButton/styles.ts b/frontend/src/components/host/LandingView/FloatingActionButton/styles.ts new file mode 100644 index 00000000..f713cde8 --- /dev/null +++ b/frontend/src/components/host/LandingView/FloatingActionButton/styles.ts @@ -0,0 +1,27 @@ +import { css } from '@emotion/react'; + +import theme from '@/styles/theme'; + +const floatingActionButton = (eventNumber: number) => css` + ${eventNumber === 1 && + ` + transform: translateX(calc(-50vw + 114px)) scale(1.4); + transition-duration: 1.5s; + `} + + right: 40px; + bottom: 40px; + position: fixed; + z-index: 200; + cursor: pointer; + color: white; + font-weight: 600; + background-color: ${theme.colors.primary}; + padding: 16px 32px; + border-radius: 24px; + box-shadow: 0px 0px 2px 3px ${theme.colors.shadow10}; +`; + +const styles = { floatingActionButton }; + +export default styles; diff --git a/frontend/src/components/host/LandingView/FloatingActionButton/useFloatingActionButton.ts b/frontend/src/components/host/LandingView/FloatingActionButton/useFloatingActionButton.ts new file mode 100644 index 00000000..92990d33 --- /dev/null +++ b/frontend/src/components/host/LandingView/FloatingActionButton/useFloatingActionButton.ts @@ -0,0 +1,26 @@ +import { RefObject, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; + +import useOnContainerScroll from '@/hooks/useOnContainerScroll'; + +const useFloatingActionButton = (mainRef: RefObject) => { + const navigate = useNavigate(); + const [eventNumber, setEventNumber] = useState(0); + + const { scrollInfo } = useOnContainerScroll(mainRef, () => { + if (scrollInfo.progress === 1) { + setEventNumber(1); + return; + } + + setEventNumber(0); + }); + + const onClick = () => { + navigate('/host'); + }; + + return { eventNumber, onClick }; +}; + +export default useFloatingActionButton; diff --git a/frontend/src/components/host/LandingView/HeroSection/index.tsx b/frontend/src/components/host/LandingView/HeroSection/index.tsx new file mode 100644 index 00000000..21418a52 --- /dev/null +++ b/frontend/src/components/host/LandingView/HeroSection/index.tsx @@ -0,0 +1,36 @@ +import useHeroSection from './useHeroSection'; +import { IoIosArrowDown } from '@react-icons/all-files/io/IoIosArrowDown'; + +import homeCover_360w from '@/assets/homeCover-360w.webp'; +import homeCover_480w from '@/assets/homeCover-480w.webp'; +import homeCover_fallback from '@/assets/homeCover-fallback.png'; + +import styles from './styles'; + +const HeroSection: React.FC = () => { + const { isFull, onMouseMove, canvasRef } = useHeroSection(); + + return ( + <> +
+
+
+ + + + + +
+
+ ์•„๋ž˜๋กœ ๋‚ด๋ ค์ฃผ์„ธ์š”. + +
+ +
+
+
+ + ); +}; + +export default HeroSection; diff --git a/frontend/src/components/host/LandingView/HeroSection/styles.ts b/frontend/src/components/host/LandingView/HeroSection/styles.ts new file mode 100644 index 00000000..87aca01c --- /dev/null +++ b/frontend/src/components/host/LandingView/HeroSection/styles.ts @@ -0,0 +1,79 @@ +import { css } from '@emotion/react'; + +import animation from '@/styles/animation'; +import theme from '@/styles/theme'; + +const layout = (isFull: boolean) => css` + width: 100vw; + height: 100vh; + overflow: hidden; + ${isFull ? `transform: translateY(68px);` : `position: fixed; top: 0; z-index: 100;`}; +`; + +const content = css` + height: 100vh; + position: relative; + top: 0; + width: 100%; + z-index: 1; + background-color: ${theme.colors.background}; +`; + +const homeCoverWrapper = css` + position: absolute; + display: flex; + justify-content: center; + align-items: center; + width: 100%; + height: 100%; +`; + +const homeCover = css` + margin: 1em 0; + animation: ${animation.wave} 2s alternate linear infinite; + + source, + img { + width: 360px; + aspect-ratio: auto 1 / 1; + @media screen and (min-width: 481px) { + width: 480px; + } + } +`; + +const arrowDownWrapper = css` + position: absolute; + bottom: 20px; + color: ${theme.colors.primary}; + width: 100%; + -webkit-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); + z-index: 3; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + animation: ${animation.moveDown} 2s 0s infinite; +`; + +const canvas = css` + height: 100%; + left: 50%; + position: relative; + top: 50%; + -webkit-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); + width: 100%; + z-index: 2; + opacity: 0.85; +`; + +const bottomWrapper = (isFull: boolean) => css` + margin-bottom: ${isFull ? '20vh' : '100vh'}; + background-color: ${theme.colors.background}; +`; + +const styles = { layout, content, homeCoverWrapper, homeCover, arrowDownWrapper, canvas, bottomWrapper }; + +export default styles; diff --git a/frontend/src/components/host/LandingView/HeroSection/useHeroSection.ts b/frontend/src/components/host/LandingView/HeroSection/useHeroSection.ts new file mode 100644 index 00000000..777ea71d --- /dev/null +++ b/frontend/src/components/host/LandingView/HeroSection/useHeroSection.ts @@ -0,0 +1,92 @@ +import { useEffect, useRef, useState } from 'react'; + +import useScroll from '@/hooks/useScroll'; + +import screenSize from '@/constants/screenSize'; + +const CIRCLE_SIZE = window.innerWidth > screenSize.MOBILE ? 80 : 60; + +const useHeroSection = () => { + const canvasRef = useRef(null); + + const positionRef = useRef({ x: window.innerWidth / 2, y: window.innerHeight / 2 }); + + const { scrollPosition } = useScroll(); + + const scrollPositionInt = scrollPosition + 3; + + const [isFull, setIsFull] = useState(false); + + const onMouseMove = (e: React.MouseEvent) => { + const canvas = canvasRef.current; + const ctx = canvas?.getContext('2d'); + if (!canvas || !ctx) return; + + const target = e.target as HTMLInputElement; + const localName = target.localName; + + if (localName !== 'canvas') return; + + positionRef.current = { + x: e.nativeEvent.offsetX, + y: e.nativeEvent.offsetY, + }; + + const scale = window.devicePixelRatio; + canvas.width = Math.floor(window.innerWidth * scale); + canvas.height = Math.floor(window.innerHeight * scale); + ctx.scale(scale, scale); + + const x = e.nativeEvent.offsetX; + const y = e.nativeEvent.offsetY; + + const radius = CIRCLE_SIZE * scrollPositionInt; + + ctx.beginPath(); + let circlePath = new Path2D(); + circlePath.arc(x, y, radius, 0, Math.PI * 2); + circlePath.rect(0, 0, window.innerWidth, window.innerHeight); + ctx.clip(circlePath, 'evenodd'); + ctx.fillStyle = '#000000'; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.closePath(); + }; + + useEffect(() => { + const canvas = canvasRef.current; + const ctx = canvas?.getContext('2d'); + if (!canvas || !ctx) return; + + const scale = window.devicePixelRatio; + + canvas.width = Math.floor(window.innerWidth * scale); + canvas.height = Math.floor(window.innerHeight * scale); + ctx.scale(scale, scale); + + const x = positionRef.current.x; + const y = positionRef.current.y; + + let radius = CIRCLE_SIZE * scrollPositionInt - scrollPositionInt * 2; + + if (radius < 0) radius = 0; + + ctx.beginPath(); + let circlePath = new Path2D(); + circlePath.arc(x, y, radius, 0, Math.PI * 2); + circlePath.rect(0, 0, window.innerWidth, window.innerHeight); + ctx.clip(circlePath, 'evenodd'); + ctx.fillStyle = '#000000'; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.closePath(); + + if (scrollPositionInt >= 12) { + setIsFull(true); + } else { + setIsFull(false); + } + }, [scrollPositionInt]); + + return { isFull, onMouseMove, canvasRef }; +}; + +export default useHeroSection; diff --git a/frontend/src/components/host/LandingView/HostPicture/index.tsx b/frontend/src/components/host/LandingView/HostPicture/index.tsx new file mode 100644 index 00000000..0b50a0ff --- /dev/null +++ b/frontend/src/components/host/LandingView/HostPicture/index.tsx @@ -0,0 +1,21 @@ +import { HostImageType } from '@/types'; + +import screenSize from '@/constants/screenSize'; + +interface HostPictureProps { + image: HostImageType; + className?: string; +} + +const HostPicture: React.FC = ({ image, className }) => { + return ( + + + + + + + ); +}; + +export default HostPicture; diff --git a/frontend/src/components/host/LandingView/HostViewSection1/index.tsx b/frontend/src/components/host/LandingView/HostViewSection1/index.tsx new file mode 100644 index 00000000..24ce5132 --- /dev/null +++ b/frontend/src/components/host/LandingView/HostViewSection1/index.tsx @@ -0,0 +1,31 @@ +import useHostViewSection1 from './useHostViewSection1'; + +import { ScreenModeType } from '@/types'; + +import styles from './styles'; + +interface HostViewSection1 { + screenMode: ScreenModeType; +} + +const HostViewSection1: React.FC = ({ screenMode }) => { + const { eventNumber, sectionRef } = useHostViewSection1(); + + return ( +
+
+

+ ์ง์ ‘ ๋‚ด ๊ณต๊ฐ„์„ ์ƒ์„ฑํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๊ณ ์‹ถ๋‹ค๋ฉด? +

+ {eventNumber === 1 && ( +

+ Gong + Check๊ณผ ํ•จ๊ป˜ ํ•˜์„ธ์š”. +

+ )} +
+
+ ); +}; + +export default HostViewSection1; diff --git a/frontend/src/components/host/LandingView/HostViewSection1/styles.ts b/frontend/src/components/host/LandingView/HostViewSection1/styles.ts new file mode 100644 index 00000000..b6e10c70 --- /dev/null +++ b/frontend/src/components/host/LandingView/HostViewSection1/styles.ts @@ -0,0 +1,62 @@ +import { css } from '@emotion/react'; + +import { ScreenModeType } from '@/types'; + +import animation from '@/styles/animation'; +import theme from '@/styles/theme'; + +const layout = (screenMode: ScreenModeType) => + css` + width: 100vw; + height: ${screenMode === 'DESKTOP' ? `100vh` : `50vh`}; + `; + +const content = css` + width: 100%; + height: 100%; + position: relative; + background-color: ${theme.colors.background}; +`; + +const title = (screenMode: ScreenModeType) => css` + top: 21.4vw; + left: 50%; + transform: translateX(-50%); + z-index: 11; + font-size: ${screenMode === 'DESKTOP' ? `2.7vw` : `5vw`}; + white-space: nowrap; + color: ${theme.colors.gray800}; + position: absolute; + b { + color: ${theme.colors.primary}; + } + + b + b { + color: ${theme.colors.green}; + } +`; + +const subTitle = (screenMode: ScreenModeType) => css` + position: absolute; + top: 44vw; + z-index: 11; + color: ${theme.colors.gray800}; + animation: ${animation.fadeIn} 1.5s; + white-space: nowrap; + + ${screenMode === 'DESKTOP' + ? `left: 33%; font-size: 2.7vw;` + : `left: 50%; transform: translateX(-50%); font-size: 5vw;`} + + b { + color: ${theme.colors.primary}; + } + + b + b { + color: ${theme.colors.green}; + } +`; + +const styles = { layout, content, title, subTitle }; + +export default styles; diff --git a/frontend/src/components/host/LandingView/HostViewSection1/useHostViewSection1.ts b/frontend/src/components/host/LandingView/HostViewSection1/useHostViewSection1.ts new file mode 100644 index 00000000..45c91f10 --- /dev/null +++ b/frontend/src/components/host/LandingView/HostViewSection1/useHostViewSection1.ts @@ -0,0 +1,23 @@ +import { useRef, useState } from 'react'; + +import useOnContainerScroll from '@/hooks/useOnContainerScroll'; + +const useHostViewSection1 = () => { + const [eventNumber, setEventNumber] = useState(0); + const sectionRef = useRef(null); + + const { dimension, scrollInfo } = useOnContainerScroll(sectionRef, () => { + const progress = Math.min(Math.max(0, scrollInfo.scrollY / dimension.windowHeight), 1) * 100; + if (progress === 0) return; + + if (progress >= 80) { + setEventNumber(1); + return; + } + setEventNumber(0); + }); + + return { eventNumber, sectionRef }; +}; + +export default useHostViewSection1; diff --git a/frontend/src/components/host/LandingView/HostViewSection2/index.tsx b/frontend/src/components/host/LandingView/HostViewSection2/index.tsx new file mode 100644 index 00000000..b410194d --- /dev/null +++ b/frontend/src/components/host/LandingView/HostViewSection2/index.tsx @@ -0,0 +1,87 @@ +import HostPicture from '../HostPicture'; +import useHostViewSection2 from './useHostViewSection2'; + +import { HostImageType, ScreenModeType } from '@/types'; + +import createSpace_280w from '@/assets/createSpace-280w.webp'; +import createSpace_360w from '@/assets/createSpace-360w.webp'; +import createSpace_540w from '@/assets/createSpace-540w.webp'; +import createSpace_800w from '@/assets/createSpace-800w.webp'; +import createSpace_fallback from '@/assets/createSpace-fallback.jpg'; +import dashboard_280w from '@/assets/dashboard-280w.webp'; +import dashboard_360w from '@/assets/dashboard-360w.webp'; +import dashboard_540w from '@/assets/dashboard-540w.webp'; +import dashboard_800w from '@/assets/dashboard-800w.webp'; +import dashboard_fallback from '@/assets/dashboard-fallback.jpg'; +import edit2_280w from '@/assets/edit2-280w.webp'; +import edit2_360w from '@/assets/edit2-360w.webp'; +import edit2_540w from '@/assets/edit2-540w.webp'; +import edit2_800w from '@/assets/edit2-800w.webp'; +import edit2_fallback from '@/assets/edit2-fallback.jpg'; +import edit_280w from '@/assets/edit-280w.webp'; +import edit_360w from '@/assets/edit-360w.webp'; +import edit_540w from '@/assets/edit-540w.webp'; +import edit_800w from '@/assets/edit-800w.webp'; +import edit_fallback from '@/assets/edit-fallback.jpg'; + +import theme from '@/styles/theme'; + +import styles from './styles'; + +const createSpace: HostImageType = { + '280w': createSpace_280w, + '360w': createSpace_360w, + '540w': createSpace_540w, + '800w': createSpace_800w, + fallback: createSpace_fallback, +}; + +const dashboard: HostImageType = { + '280w': dashboard_280w, + '360w': dashboard_360w, + '540w': dashboard_540w, + '800w': dashboard_800w, + fallback: dashboard_fallback, +}; + +const edit2: HostImageType = { + '280w': edit2_280w, + '360w': edit2_360w, + '540w': edit2_540w, + '800w': edit2_800w, + fallback: edit2_fallback, +}; + +const edit: HostImageType = { + '280w': edit_280w, + '360w': edit_360w, + '540w': edit_540w, + '800w': edit_800w, + fallback: edit_fallback, +}; + +interface HostViewSection2Props { + screenMode: ScreenModeType; +} + +const HostViewSection2: React.FC = ({ screenMode }) => { + const { eventNumber, sectionRef } = useHostViewSection2(); + + return ( +
+
+

๋‚ด ๊ณต๊ฐ„ ๊ด€๋ฆฌ

+ {eventNumber >= 1 && ( +
+ + + + +
+ )} +
+
+ ); +}; + +export default HostViewSection2; diff --git a/frontend/src/components/host/LandingView/HostViewSection2/styles.ts b/frontend/src/components/host/LandingView/HostViewSection2/styles.ts new file mode 100644 index 00000000..5756ac22 --- /dev/null +++ b/frontend/src/components/host/LandingView/HostViewSection2/styles.ts @@ -0,0 +1,64 @@ +import { css } from '@emotion/react'; + +import { ScreenModeType } from '@/types'; + +import animation from '@/styles/animation'; +import theme from '@/styles/theme'; + +const layout = css` + margin-top: 10vh; + width: 100vw; + height: 100vh; +`; + +const content = css` + width: 100%; + height: 100%; + background-color: ${theme.colors.background}; + position: relative; + display: flex; + justify-content: center; +`; + +const title = (screenMode: ScreenModeType) => css` + position: absolute; + top: 2vw; + left: 50%; + transform: translateX(-50%); + z-index: 11; + font-size: ${screenMode === 'DESKTOP' ? `2.7vw` : `5vw`}; + color: ${theme.colors.gray800}; + animation: ${animation.fadeIn} 1.5s; + animation-fill-mode: forwards; +`; + +const gridWrapper = css` + display: grid; + position: absolute; + top: 14vw; + animation: ${animation.moveUp} 1.5s; + animation-fill-mode: forwards; + z-index: 11; + gap: 10px 20px; + grid-template-columns: repeat(2, 1fr); + + @media (max-width: 768px) { + grid-template-columns: repeat(1, 1fr); + row-gap: 20px; + } + + & img { + height: 100%; + width: 100%; + } +`; + +const picture = (color: string) => css` + box-shadow: 3px 3px 3px 3px ${theme.colors.shadow20}; + border-radius: 4px; + border: 4px solid ${color}; +`; + +const styles = { layout, content, title, gridWrapper, picture }; + +export default styles; diff --git a/frontend/src/components/host/LandingView/HostViewSection2/useHostViewSection2.ts b/frontend/src/components/host/LandingView/HostViewSection2/useHostViewSection2.ts new file mode 100644 index 00000000..8d184f98 --- /dev/null +++ b/frontend/src/components/host/LandingView/HostViewSection2/useHostViewSection2.ts @@ -0,0 +1,23 @@ +import { useRef, useState } from 'react'; + +import useOnContainerScroll from '@/hooks/useOnContainerScroll'; + +const useHostViewSection2 = () => { + const [eventNumber, setEventNumber] = useState(0); + const sectionRef = useRef(null); + + const { dimension, scrollInfo } = useOnContainerScroll(sectionRef, () => { + const progress = Math.min(Math.max(0, scrollInfo.scrollY / dimension.windowHeight), 1) * 100; + if (progress === 0) return; + + if (progress >= 50) { + setEventNumber(1); + return; + } + setEventNumber(0); + }); + + return { eventNumber, sectionRef }; +}; + +export default useHostViewSection2; diff --git a/frontend/src/components/host/LandingView/HostViewSection3/index.tsx b/frontend/src/components/host/LandingView/HostViewSection3/index.tsx new file mode 100644 index 00000000..e759a2aa --- /dev/null +++ b/frontend/src/components/host/LandingView/HostViewSection3/index.tsx @@ -0,0 +1,21 @@ +import useHostViewSection3 from './useHostViewSection3'; + +import { ScreenModeType } from '@/types'; + +import styles from './styles'; + +interface HostViewSection3Props { + screenMode: ScreenModeType; +} + +const HostViewSection3: React.FC = ({ screenMode }) => { + const { eventNumber, sectionRef } = useHostViewSection3(); + + return ( +
+
{eventNumber === 1 &&

์ง€๊ธˆ ๋ฐ”๋กœ

}
+
+ ); +}; + +export default HostViewSection3; diff --git a/frontend/src/components/host/LandingView/HostViewSection3/styles.ts b/frontend/src/components/host/LandingView/HostViewSection3/styles.ts new file mode 100644 index 00000000..934453df --- /dev/null +++ b/frontend/src/components/host/LandingView/HostViewSection3/styles.ts @@ -0,0 +1,33 @@ +import { css } from '@emotion/react'; + +import { ScreenModeType } from '@/types'; + +import animation from '@/styles/animation'; +import theme from '@/styles/theme'; + +const layout = css` + width: 100vw; + height: 42vh; +`; + +const content = css` + width: 100%; + height: 100%; + background-color: ${theme.colors.background}; + display: flex; + align-items: center; + justify-content: center; +`; + +const title = (screenMode: ScreenModeType) => css` + z-index: 11; + font-size: ${screenMode === 'DESKTOP' ? `2.7vw` : `5vw`}; + color: ${theme.colors.gray800}; + animation: ${animation.moveDown} 1.5s; + animation-fill-mode: forwards; + padding-bottom: 8vh; +`; + +const styles = { layout, content, title }; + +export default styles; diff --git a/frontend/src/components/host/LandingView/HostViewSection3/useHostViewSection3.ts b/frontend/src/components/host/LandingView/HostViewSection3/useHostViewSection3.ts new file mode 100644 index 00000000..880075ff --- /dev/null +++ b/frontend/src/components/host/LandingView/HostViewSection3/useHostViewSection3.ts @@ -0,0 +1,24 @@ +import { useRef, useState } from 'react'; + +import useOnContainerScroll from '@/hooks/useOnContainerScroll'; + +const useHostViewSection3 = () => { + const [eventNumber, setEventNumber] = useState(0); + const sectionRef = useRef(null); + + const { dimension, scrollInfo } = useOnContainerScroll(sectionRef, () => { + const progress = Math.min(Math.max(0, scrollInfo.scrollY / dimension.windowHeight), 1) * 100; + if (progress === 0) return; + + if (progress >= 20) { + setEventNumber(1); + return; + } + + setEventNumber(0); + }); + + return { eventNumber, sectionRef }; +}; + +export default useHostViewSection3; diff --git a/frontend/src/components/host/LandingView/UserPicture/index.tsx b/frontend/src/components/host/LandingView/UserPicture/index.tsx new file mode 100644 index 00000000..ee3084cb --- /dev/null +++ b/frontend/src/components/host/LandingView/UserPicture/index.tsx @@ -0,0 +1,22 @@ +import { UserImageType } from '@/types'; + +import screenSize from '@/constants/screenSize'; + +interface UserPicture { + image: UserImageType; + className?: string; +} + +const UserPicture: React.FC = ({ image, className }) => { + return ( + + + + + + + + ); +}; + +export default UserPicture; diff --git a/frontend/src/components/host/LandingView/UserViewSection1/index.tsx b/frontend/src/components/host/LandingView/UserViewSection1/index.tsx new file mode 100644 index 00000000..1d55ab6a --- /dev/null +++ b/frontend/src/components/host/LandingView/UserViewSection1/index.tsx @@ -0,0 +1,69 @@ +import UserPicture from '../UserPicture'; +import useUserViewSection1 from './useUserViewSection1'; + +import { ScreenModeType, UserImageType } from '@/types'; + +import mobileView1_160w from '@/assets/mobileView1-160w.webp'; +import mobileView1_240w from '@/assets/mobileView1-240w.webp'; +import mobileView1_320w from '@/assets/mobileView1-320w.webp'; +import mobileView1_480w from '@/assets/mobileView1-480w.webp'; +import mobileView1_fallback from '@/assets/mobileView1-fallback.png'; +import mobileView2_160w from '@/assets/mobileView2-160w.webp'; +import mobileView2_240w from '@/assets/mobileView2-240w.webp'; +import mobileView2_320w from '@/assets/mobileView2-320w.webp'; +import mobileView2_480w from '@/assets/mobileView2-480w.webp'; +import mobileView2_fallback from '@/assets/mobileView2-fallback.png'; + +import styles from './styles'; + +const mobileView1: UserImageType = { + '160w': mobileView1_160w, + '240w': mobileView1_240w, + '320w': mobileView1_320w, + '480w': mobileView1_480w, + fallback: mobileView1_fallback, +}; + +const mobileView2: UserImageType = { + '160w': mobileView2_160w, + '240w': mobileView2_240w, + '320w': mobileView2_320w, + '480w': mobileView2_480w, + fallback: mobileView2_fallback, +}; + +interface UserViewSection1 { + screenMode: ScreenModeType; +} + +const UserViewSection1: React.FC = ({ screenMode }) => { + const { eventNumber, sectionRef } = useUserViewSection1(); + + return ( +
+
+

์‰ฝ๊ฒŒ ํ™•์ธํ•ด์š”.

+

ํ•จ๊ป˜ ์‚ฌ์šฉํ• 

+ + {eventNumber >= 1 && ( +
+ +

+ ๊ณต๊ฐ„๊ณผ +

+
+ )} + {eventNumber === 2 && ( +
+ +

+ ์—…๋ฌด๋ฅผ +

+
+ )} +
+
+ ); +}; + +export default UserViewSection1; diff --git a/frontend/src/components/host/LandingView/UserViewSection1/styles.ts b/frontend/src/components/host/LandingView/UserViewSection1/styles.ts new file mode 100644 index 00000000..8698cc94 --- /dev/null +++ b/frontend/src/components/host/LandingView/UserViewSection1/styles.ts @@ -0,0 +1,115 @@ +import { css } from '@emotion/react'; + +import { ScreenModeType } from '@/types'; + +import animation from '@/styles/animation'; +import theme from '@/styles/theme'; + +const layout = css` + width: 100vw; + height: 100vh; + z-index: 10; +`; + +const content = css` + width: 100%; + height: 100%; + background-color: ${theme.colors.background}; + position: relative; + z-index: 10; +`; + +const title = (screenMode: ScreenModeType) => css` + position: absolute; + color: ${theme.colors.primary}; + z-index: 11; + top: 13vw; + ${screenMode === 'DESKTOP' + ? `left: 12%; font-size: 4.1vw;` + : `left: 50%; transform: translateX(-50%); font-size: 6vw;`} +`; + +const subTitle = (screenMode: ScreenModeType) => css` + position: absolute; + top: 29vw; + z-index: 11; + color: ${theme.colors.gray800}; + ${screenMode === 'DESKTOP' + ? `left: 10%; font-size: 2.7vw;` + : `left: 30%; transform: translateX(-50%); font-size: 3.7vw;`} +`; + +const leftSectionWrapper = css` + animation: ${animation.moveRight} 1.5s; + animation-fill-mode: forwards; + z-index: 11; + position: absolute; + height: 100%; + width: 100%; +`; + +const leftSection = (screenMode: ScreenModeType) => css` + position: absolute; + ${screenMode === 'DESKTOP' ? `top: 5vw; right: 25%; width: 20%;` : `top: 28%; left: 20%;`} +`; + +const leftSectionTitle = (screenMode: ScreenModeType) => css` + position: absolute; + top: 29vw; + animation: ${animation.moveDown} 1.5s; + animation-fill-mode: forwards; + color: ${theme.colors.gray800}; + z-index: 11; + ${screenMode === 'DESKTOP' + ? `font-size: 2.7vw; left: 24.5%;` + : `font-size: 3.7vw; left: 42%; transform: translateX(-50%);`} + + b { + color: ${theme.colors.green}; + } +`; + +const rightSectionWrapper = css` + animation: ${animation.moveLeft} 1.5s; + animation-fill-mode: forwards; + z-index: 12; + position: absolute; + height: 100%; + width: 100%; +`; + +const rightSection = (screenMode: ScreenModeType) => css` + position: absolute; + ${screenMode === 'DESKTOP' ? `top: 12vw; right: 10%; width: 20%;` : `top: 32%; right: 20%;`} +`; + +const rightSectionTitle = (screenMode: ScreenModeType) => css` + position: absolute; + top: 29vw; + z-index: 11; + animation: ${animation.moveUp} 1.5s; + animation-fill-mode: forwards; + color: ${theme.colors.gray800}; + ${screenMode === 'DESKTOP' + ? `font-size: 2.7vw; left: 33%;` + : `font-size: 3.7vw; left: 54%; transform: translateX(-50%);`} + + b { + color: ${theme.colors.green}; + } +`; + +const styles = { + layout, + content, + title, + subTitle, + leftSectionWrapper, + leftSection, + leftSectionTitle, + rightSectionWrapper, + rightSection, + rightSectionTitle, +}; + +export default styles; diff --git a/frontend/src/components/host/LandingView/UserViewSection1/useUserViewSection1.ts b/frontend/src/components/host/LandingView/UserViewSection1/useUserViewSection1.ts new file mode 100644 index 00000000..e12be71d --- /dev/null +++ b/frontend/src/components/host/LandingView/UserViewSection1/useUserViewSection1.ts @@ -0,0 +1,27 @@ +import { useRef, useState } from 'react'; + +import useOnContainerScroll from '@/hooks/useOnContainerScroll'; + +const useUserViewSection1 = () => { + const [eventNumber, setEventNumber] = useState(0); + const sectionRef = useRef(null); + + const { dimension, scrollInfo } = useOnContainerScroll(sectionRef, () => { + const progress = Math.min(Math.max(0, scrollInfo.scrollY / dimension.windowHeight), 1) * 100; + if (progress === 0) return; + + if (progress >= 40 && progress < 80) { + setEventNumber(1); + return; + } + if (progress >= 80) { + setEventNumber(2); + return; + } + setEventNumber(0); + }); + + return { eventNumber, sectionRef }; +}; + +export default useUserViewSection1; diff --git a/frontend/src/components/host/LandingView/UserViewSection2/index.tsx b/frontend/src/components/host/LandingView/UserViewSection2/index.tsx new file mode 100644 index 00000000..5e35b03a --- /dev/null +++ b/frontend/src/components/host/LandingView/UserViewSection2/index.tsx @@ -0,0 +1,68 @@ +import UserPicture from '../UserPicture'; +import useUserViewSection2 from './useUserViewSection2'; + +import { ScreenModeType, UserImageType } from '@/types'; + +import mobileView3_160w from '@/assets/mobileView3-160w.webp'; +import mobileView3_240w from '@/assets/mobileView3-240w.webp'; +import mobileView3_320w from '@/assets/mobileView3-320w.webp'; +import mobileView3_480w from '@/assets/mobileView3-480w.webp'; +import mobileView3_fallback from '@/assets/mobileView3-fallback.png'; +import mobileView4_160w from '@/assets/mobileView4-160w.webp'; +import mobileView4_240w from '@/assets/mobileView4-240w.webp'; +import mobileView4_320w from '@/assets/mobileView4-320w.webp'; +import mobileView4_480w from '@/assets/mobileView4-480w.webp'; +import mobileView4_fallback from '@/assets/mobileView4-fallback.png'; + +import styles from './styles'; + +const mobileView3: UserImageType = { + '160w': mobileView3_160w, + '240w': mobileView3_240w, + '320w': mobileView3_320w, + '480w': mobileView3_480w, + fallback: mobileView3_fallback, +}; + +const mobileView4: UserImageType = { + '160w': mobileView4_160w, + '240w': mobileView4_240w, + '320w': mobileView4_320w, + '480w': mobileView4_480w, + fallback: mobileView4_fallback, +}; + +interface UserViewSection2Props { + screenMode: ScreenModeType; +} + +const UserViewSection2: React.FC = ({ screenMode }) => { + const { sectionRef, eventNumber } = useUserViewSection2(); + + return ( +
+
+

๊ฐ„๋‹จํ•˜๊ฒŒ ์ฒดํฌํ•ด์š”.

+ {eventNumber >= 1 && ( +
+ +

+ ์ฒดํฌ๋ฆฌ์ŠคํŠธ์™€ +

+
+ )} + {eventNumber === 2 && ( +
+ +

+ ์ƒ์„ธ์ •๋ณด ์ œ๊ณต +

+ {screenMode === 'DESKTOP' &&

How, Where

} +
+ )} +
+
+ ); +}; + +export default UserViewSection2; diff --git a/frontend/src/components/host/LandingView/UserViewSection2/styles.ts b/frontend/src/components/host/LandingView/UserViewSection2/styles.ts new file mode 100644 index 00000000..4ccdfe9f --- /dev/null +++ b/frontend/src/components/host/LandingView/UserViewSection2/styles.ts @@ -0,0 +1,109 @@ +import { css } from '@emotion/react'; + +import { ScreenModeType } from '@/types'; + +import animation from '@/styles/animation'; +import theme from '@/styles/theme'; + +const layout = css` + width: 100vw; + height: 100vh; +`; + +const content = css` + width: 100%; + height: 100%; + background-color: ${theme.colors.background}; + position: relative; +`; + +const title = (screenMode: ScreenModeType) => css` + position: absolute; + top: 12vw; + z-index: 11; + color: ${theme.colors.green}; + ${screenMode === 'DESKTOP' + ? `right: 10%; font-size: 4.1vw;` + : `left: 50%; transform: translateX(-50%); font-size: 6vw;`} +`; + +const leftSectionWrapper = css` + animation: ${animation.moveDown} 1.5s; + animation-fill-mode: forwards; + z-index: 11; + position: absolute; + height: 100%; + width: 100%; +`; + +const leftSection = (screenMode: ScreenModeType) => css` + position: absolute; + ${screenMode === 'DESKTOP' ? `top: 4vw; left: 28%; width: 20%;` : `top: 28%; left: 20%;`} +`; + +const leftSectionTitle = (screenMode: ScreenModeType) => css` + position: absolute; + top: 28.5vw; + z-index: 11; + color: ${theme.colors.gray800}; + animation: ${animation.moveRight} 1.5s; + animation-fill-mode: forwards; + ${screenMode === 'DESKTOP' ? `right: 26%; font-size: 2.7vw;` : `right: 38%; font-size: 3.7vw;`} + + b { + color: ${theme.colors.primary}; + } +`; + +const rightSectionWrapper = css` + position: absolute; + z-index: 12; + animation: ${animation.moveUp} 1.5s; + animation-fill-mode: forwards; + height: 100%; + width: 100%; +`; + +const rightSection = (screenMode: ScreenModeType) => css` + position: absolute; + ${screenMode === 'DESKTOP' ? `top: 7vw; left: 6%; width: 20%;` : `top: 32%; right: 20%;`} +`; + +const rightSectionTitle = (screenMode: ScreenModeType) => css` + position: absolute; + top: 28.5vw; + z-index: 11; + color: ${theme.colors.gray800}; + animation: ${animation.moveLeft} 1.5s; + animation-fill-mode: forwards; + ${screenMode === 'DESKTOP' ? `right: 9%; font-size: 2.7vw;` : `right: 13%; font-size: 3.7vw;`} + b { + color: ${theme.colors.primary}; + } +`; + +const rightSectionLittleTitle = css` + position: absolute; + top: 28.5vw; + right: 17%; + z-index: 11; + font-size: 1vw; + color: ${theme.colors.gray500}; + animation: ${animation.moveLeft} 1.5s; + animation-fill-mode: forwards; +`; + +const styles = { + layout, + content, + title, + leftSectionWrapper, + leftSection, + leftSectionTitle, + rightSectionWrapper, + rightSection, + rightSectionTitle, + rightSectionLittleTitle, +}; + +export default styles; diff --git a/frontend/src/components/host/LandingView/UserViewSection2/useUserViewSection2.ts b/frontend/src/components/host/LandingView/UserViewSection2/useUserViewSection2.ts new file mode 100644 index 00000000..caefcb55 --- /dev/null +++ b/frontend/src/components/host/LandingView/UserViewSection2/useUserViewSection2.ts @@ -0,0 +1,27 @@ +import { useRef, useState } from 'react'; + +import useOnContainerScroll from '@/hooks/useOnContainerScroll'; + +const useUserViewSection2 = () => { + const sectionRef = useRef(null); + const [eventNumber, setEventNumber] = useState(0); + + const { dimension, scrollInfo } = useOnContainerScroll(sectionRef, () => { + const progress = Math.min(Math.max(0, scrollInfo.scrollY / dimension.windowHeight), 1) * 100; + if (progress === 0) return; + + if (progress >= 60 && progress < 100) { + setEventNumber(1); + return; + } + if (progress >= 100) { + setEventNumber(2); + return; + } + setEventNumber(0); + }); + + return { sectionRef, eventNumber }; +}; + +export default useUserViewSection2; diff --git a/frontend/src/components/host/LandingView/UserViewSection3/index.tsx b/frontend/src/components/host/LandingView/UserViewSection3/index.tsx new file mode 100644 index 00000000..b25f44f6 --- /dev/null +++ b/frontend/src/components/host/LandingView/UserViewSection3/index.tsx @@ -0,0 +1,47 @@ +import UserPicture from '../UserPicture'; +import useUserViewSection3 from './useUserViewSection3'; + +import { ScreenModeType, UserImageType } from '@/types'; + +import mobileView3_160w from '@/assets/mobileView3-160w.webp'; +import mobileView3_240w from '@/assets/mobileView3-240w.webp'; +import mobileView3_320w from '@/assets/mobileView3-320w.webp'; +import mobileView3_480w from '@/assets/mobileView3-480w.webp'; +import mobileView3_fallback from '@/assets/mobileView3-fallback.png'; + +import styles from './styles'; + +const mobileView3: UserImageType = { + '160w': mobileView3_160w, + '240w': mobileView3_240w, + '320w': mobileView3_320w, + '480w': mobileView3_480w, + fallback: mobileView3_fallback, +}; + +interface UserViewSection3Props { + screenMode: ScreenModeType; +} + +const UserViewSection3: React.FC = ({ screenMode }) => { + const { sectionRef, eventNumber } = useUserViewSection3(); + + return ( +
+
+

ํ•จ๊ป˜ ์‚ฌ์šฉํ•ด์š”.

+ {eventNumber === 1 && ( +
+ + +

+ ์—ฌ๋Ÿฌ๋ช…์ด ๋™์‹œ์— ๊ฐ™์€ ์—…๋ฌด ์ฒดํฌ ๊ฐ€๋Šฅ +

+
+ )} +
+
+ ); +}; + +export default UserViewSection3; diff --git a/frontend/src/components/host/LandingView/UserViewSection3/styles.ts b/frontend/src/components/host/LandingView/UserViewSection3/styles.ts new file mode 100644 index 00000000..b5f6c09d --- /dev/null +++ b/frontend/src/components/host/LandingView/UserViewSection3/styles.ts @@ -0,0 +1,70 @@ +import { css } from '@emotion/react'; + +import { ScreenModeType } from '@/types'; + +import animation from '@/styles/animation'; +import theme from '@/styles/theme'; + +const layout = css` + width: 100vw; + height: 100vh; +`; + +const content = css` + width: 100%; + height: 100%; + background-color: ${theme.colors.background}; + position: relative; +`; + +const title = (screenMode: ScreenModeType) => css` + position: absolute; + top: 2.2vw; + z-index: 11; + color: ${theme.colors.primary}; + ${screenMode === 'DESKTOP' + ? `left: 38.5%; font-size: 4.1vw;` + : `left: 50%; transform: translateX(-50%); font-size: 6vw;`} +`; + +const subTitle = (screenMode: ScreenModeType) => css` + position: absolute; + z-index: 11; + color: ${theme.colors.gray800}; + animation: ${animation.fadeIn} 1.5s; + animation-fill-mode: forwards; + ${screenMode === 'DESKTOP' + ? `top: 10.5vw; left: 40.4%; font-size: 1.3vw;` + : `top: 16vw; left: 50%; transform: translateX(-50%); font-size: 3vw;`} + + b { + color: ${theme.colors.green}; + } +`; + +const leftSection = (screenMode: ScreenModeType) => css` + position: absolute; + z-index: 11; + animation: ${animation.moveRight} 1.5s; + animation-fill-mode: forwards; + ${screenMode === 'DESKTOP' ? `top: 15vw; left: 26%; width: 18%;` : `top: 20%; left: 20%;`} +`; + +const rightSection = (screenMode: ScreenModeType) => css` + position: absolute; + z-index: 11; + animation: ${animation.moveLeft} 1.5s; + animation-fill-mode: forwards; + ${screenMode === 'DESKTOP' ? `top: 15vw; right: 26%; width: 18%;` : `top: 28%; right: 20%;`} +`; + +const styles = { + layout, + content, + title, + subTitle, + leftSection, + rightSection, +}; + +export default styles; diff --git a/frontend/src/components/host/LandingView/UserViewSection3/useUserViewSection3.ts b/frontend/src/components/host/LandingView/UserViewSection3/useUserViewSection3.ts new file mode 100644 index 00000000..8045686c --- /dev/null +++ b/frontend/src/components/host/LandingView/UserViewSection3/useUserViewSection3.ts @@ -0,0 +1,23 @@ +import { useRef, useState } from 'react'; + +import useOnContainerScroll from '@/hooks/useOnContainerScroll'; + +const useUserViewSection3 = () => { + const [eventNumber, setEventNumber] = useState(0); + const sectionRef = useRef(null); + + const { dimension, scrollInfo } = useOnContainerScroll(sectionRef, () => { + const progress = Math.min(Math.max(0, scrollInfo.scrollY / dimension.windowHeight), 1) * 100; + if (progress === 0) return; + + if (progress >= 50) { + setEventNumber(1); + return; + } + setEventNumber(0); + }); + + return { sectionRef, eventNumber }; +}; + +export default useUserViewSection3; diff --git a/frontend/src/components/host/Navigator/LeftNavigator/index.tsx b/frontend/src/components/host/Navigator/LeftNavigator/index.tsx new file mode 100644 index 00000000..987461d2 --- /dev/null +++ b/frontend/src/components/host/Navigator/LeftNavigator/index.tsx @@ -0,0 +1,25 @@ +import Menu from '../Menu'; +import { useNavigate } from 'react-router-dom'; + +import logo from '@/assets/navigationLogo.png'; + +import styles from './styles'; + +const LeftNavigator: React.FC = () => { + const navigate = useNavigate(); + + const onClickLogo = () => { + if (location.pathname.split('/').length !== 4) navigate(-1); + }; + + return ( +
+
+ +
+ +
+ ); +}; + +export default LeftNavigator; diff --git a/frontend/src/components/host/Navigator/LeftNavigator/styles.ts b/frontend/src/components/host/Navigator/LeftNavigator/styles.ts new file mode 100644 index 00000000..39254066 --- /dev/null +++ b/frontend/src/components/host/Navigator/LeftNavigator/styles.ts @@ -0,0 +1,37 @@ +import { css } from '@emotion/react'; + +import theme from '@/styles/theme'; + +const layout = css` + font-size: 16px; + height: 100vh; + width: 14em; + position: fixed; + top: 0; + left: 0; + background-color: ${theme.colors.white}; + box-shadow: 6px 0 8px ${theme.colors.gray350}; + z-index: 1; +`; + +const logo = css` + display: flex; + justify-content: center; + align-items: center; + width: 100%; + height: 200px; + cursor: pointer; +`; + +const logoImage = css` + width: 160px; + height: 160px; +`; + +const styles = { + layout, + logo, + logoImage, +}; + +export default styles; diff --git a/frontend/src/components/host/Navigation/index.tsx b/frontend/src/components/host/Navigator/Menu/index.tsx similarity index 72% rename from frontend/src/components/host/Navigation/index.tsx rename to frontend/src/components/host/Navigator/Menu/index.tsx index 925accac..c1db7d87 100644 --- a/frontend/src/components/host/Navigation/index.tsx +++ b/frontend/src/components/host/Navigator/Menu/index.tsx @@ -1,20 +1,14 @@ -import useHostNavigation from './useHostNavigation'; -import { CgHomeAlt } from 'react-icons/cg'; -import { RiLockPasswordLine } from 'react-icons/ri'; - -import navigationLogo from '@/assets/navigationLogo.png'; +import useMenu from './useMenu'; +import { CgHomeAlt } from '@react-icons/all-files/cg/CgHomeAlt'; +import { RiLockPasswordLine } from '@react-icons/all-files/ri/RiLockPasswordLine'; import styles from './styles'; -const Navigation: React.FC = () => { - const { selectedSpaceId, spaceData, onClickPasswordUpdate, onClickSpace, onClickNewSpace } = useHostNavigation(); +const Menu = () => { + const { selectedSpaceId, spaceData, onClickPasswordUpdate, onClickSpace, onClickNewSpace } = useMenu(); return ( -
-
- -
- + <>
Menu
@@ -47,8 +41,8 @@ const Navigation: React.FC = () => {
-
+ ); }; -export default Navigation; +export default Menu; diff --git a/frontend/src/components/host/Navigation/styles.ts b/frontend/src/components/host/Navigator/Menu/styles.ts similarity index 69% rename from frontend/src/components/host/Navigation/styles.ts rename to frontend/src/components/host/Navigator/Menu/styles.ts index 0afe853a..ea9e3f0c 100644 --- a/frontend/src/components/host/Navigation/styles.ts +++ b/frontend/src/components/host/Navigator/Menu/styles.ts @@ -2,38 +2,6 @@ import { css } from '@emotion/react'; import theme from '@/styles/theme'; -const layout = css` - font-size: 16px; - position: fixed; - top: 0; - left: 0; - height: 100vh; - width: 14em; - background-color: ${theme.colors.white}; - box-shadow: 6px 0 8px ${theme.colors.gray350}; - z-index: 1; - - @media screen and (max-width: 1024px) { - font-size: 14px; - } - @media screen and (max-width: 720px) { - font-size: 12px; - } -`; - -const logo = css` - display: flex; - justify-content: center; - align-items: center; - width: 100%; - height: 200px; -`; - -const logoImage = css` - width: 160px; - height: 160px; -`; - const category = css` width: 100%; display: flex; @@ -42,7 +10,7 @@ const category = css` `; const categoryTitle = css` - font-size: 1em; + font-size: 0.9em; font-weight: 600; color: ${theme.colors.gray800}; margin: 8px 0; @@ -103,9 +71,6 @@ const addNewSpace = css` `; const styles = { - layout, - logo, - logoImage, category, categoryTitle, categoryList, diff --git a/frontend/src/components/host/Navigation/useHostNavigation.ts b/frontend/src/components/host/Navigator/Menu/useMenu.ts similarity index 88% rename from frontend/src/components/host/Navigation/useHostNavigation.ts rename to frontend/src/components/host/Navigator/Menu/useMenu.ts index a876eb4c..457970b6 100644 --- a/frontend/src/components/host/Navigation/useHostNavigation.ts +++ b/frontend/src/components/host/Navigator/Menu/useMenu.ts @@ -6,14 +6,16 @@ import apiSpace from '@/apis/space'; import { ID } from '@/types'; -const useHostNavigation = () => { +const useHostNavigator = () => { const navigate = useNavigate(); const { spaceId } = useParams() as { spaceId: ID }; const [selectedSpaceId, setSelectedSpaceId] = useState(spaceId); - const { data: spaceData } = useQuery(['spaces'], apiSpace.getSpaces); + const { data: spaceData } = useQuery(['spaces'], apiSpace.getSpaces, { + suspense: false, + }); const onClickPasswordUpdate = () => { navigate('/host/manage/passwordUpdate'); @@ -31,4 +33,4 @@ const useHostNavigation = () => { return { selectedSpaceId, spaceData, onClickPasswordUpdate, onClickSpace, onClickNewSpace }; }; -export default useHostNavigation; +export default useHostNavigator; diff --git a/frontend/src/components/host/Navigator/TopNavigator/index.tsx b/frontend/src/components/host/Navigator/TopNavigator/index.tsx new file mode 100644 index 00000000..e3d93d8c --- /dev/null +++ b/frontend/src/components/host/Navigator/TopNavigator/index.tsx @@ -0,0 +1,40 @@ +import Menu from '../Menu'; +import isNull from '@/utils/isNull'; +import { BiMenu } from '@react-icons/all-files/bi/BiMenu'; +import { BiX } from '@react-icons/all-files/bi/BiX'; +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; + +import SlideMenu from '@/components/common/SlideMenu'; + +import logo from '@/assets/logoTitle.png'; + +import styles from './styles'; + +const TopNavigator: React.FC = () => { + const navigate = useNavigate(); + + const [isShowMenu, setIsShowMenu] = useState(null); + + const onClickMenuIcon = () => setIsShowMenu(prev => !prev); + + const onClickLogo = () => { + if (location.pathname.split('/').length !== 4) navigate(-1); + }; + + return ( + <> +
+ {isShowMenu ? : } + +
+ {!isNull(isShowMenu) && ( + + + + )} + + ); +}; + +export default TopNavigator; diff --git a/frontend/src/components/host/Navigator/TopNavigator/styles.ts b/frontend/src/components/host/Navigator/TopNavigator/styles.ts new file mode 100644 index 00000000..ed9a1f4f --- /dev/null +++ b/frontend/src/components/host/Navigator/TopNavigator/styles.ts @@ -0,0 +1,42 @@ +import { css } from '@emotion/react'; + +import theme from '@/styles/theme'; + +const layout = css` + font-size: 16px; + position: fixed; + top: 0; + display: flex; + justify-content: center; + align-items: center; + transform: translate3d(0, 0, 0); + -webkit-transform: translateZ(0); + -webkit-overflow-scrolling: touch; + + background-color: ${theme.colors.white}; + z-index: 1; + + width: 100vw; + height: 64px; + box-shadow: 0 2px 2px 0px ${theme.colors.shadow30}; + + svg { + color: ${theme.colors.gray800}; + cursor: pointer; + position: absolute; + left: 12px; + } +`; + +const logo = css` + height: 40px; + transform: translateY(4px); + cursor: pointer; +`; + +const styles = { + layout, + logo, +}; + +export default styles; diff --git a/frontend/src/components/host/TaskBox/index.tsx b/frontend/src/components/host/SectionCard/TaskBox/index.tsx similarity index 88% rename from frontend/src/components/host/TaskBox/index.tsx rename to frontend/src/components/host/SectionCard/TaskBox/index.tsx index 26235ca5..777b665a 100644 --- a/frontend/src/components/host/TaskBox/index.tsx +++ b/frontend/src/components/host/SectionCard/TaskBox/index.tsx @@ -1,5 +1,6 @@ import useTaskBox from './useTaskBox'; -import { BiMinus, BiNews, BiXCircle } from 'react-icons/bi'; +import { BiMinus } from '@react-icons/all-files/bi/BiMinus'; +import { BiNews } from '@react-icons/all-files/bi/BiNews'; import { TaskType } from '@/types'; diff --git a/frontend/src/components/host/TaskBox/styles.ts b/frontend/src/components/host/SectionCard/TaskBox/styles.ts similarity index 100% rename from frontend/src/components/host/TaskBox/styles.ts rename to frontend/src/components/host/SectionCard/TaskBox/styles.ts diff --git a/frontend/src/components/host/TaskBox/useTaskBox.tsx b/frontend/src/components/host/SectionCard/TaskBox/useTaskBox.tsx similarity index 95% rename from frontend/src/components/host/TaskBox/useTaskBox.tsx rename to frontend/src/components/host/SectionCard/TaskBox/useTaskBox.tsx index af53b8e2..9f870ce8 100644 --- a/frontend/src/components/host/TaskBox/useTaskBox.tsx +++ b/frontend/src/components/host/SectionCard/TaskBox/useTaskBox.tsx @@ -1,4 +1,4 @@ -import SectionDetailModal from '../SectionDetailModal'; +import SectionDetailModal from '../../SectionDetailModal'; import useModal from '@/hooks/useModal'; import useSections from '@/hooks/useSections'; diff --git a/frontend/src/components/host/SectionCard/index.tsx b/frontend/src/components/host/SectionCard/index.tsx index 7cba22b1..ae2f1e50 100644 --- a/frontend/src/components/host/SectionCard/index.tsx +++ b/frontend/src/components/host/SectionCard/index.tsx @@ -1,6 +1,7 @@ -import TaskBox from '../TaskBox'; +import TaskBox from './TaskBox'; import useSectionCardDefault from './useSectionCardDefault'; -import { BiNews, BiX } from 'react-icons/bi'; +import { BiNews } from '@react-icons/all-files/bi/BiNews'; +import { BiX } from '@react-icons/all-files/bi/BiX'; import { SectionType } from '@/types'; diff --git a/frontend/src/components/host/SectionCard/styles.ts b/frontend/src/components/host/SectionCard/styles.ts index 4f978f21..594e387c 100644 --- a/frontend/src/components/host/SectionCard/styles.ts +++ b/frontend/src/components/host/SectionCard/styles.ts @@ -7,10 +7,10 @@ const container = css` display: flex; flex-direction: column; width: 100%; - max-width: 480px; - height: 360px; + min-width: 320px; + height: 356px; overflow-y: scroll; - padding: 32px; + padding: 40px 32px; background-color: ${theme.colors.white}; box-shadow: 2px 2px 2px 2px ${theme.colors.shadow20}; border-radius: 8px; @@ -42,7 +42,6 @@ const titleWrapper = css` display: flex; justify-content: space-between; align-items: center; - font-size: 20px; margin-bottom: 8px; padding-bottom: 16px; border-bottom: 2px solid ${theme.colors.shadow20}; @@ -79,11 +78,10 @@ const newTaskButton = css` `; const input = css` - font-size: 18px; - line-height: 38px; + font-size: 16px; border: 1px solid ${theme.colors.shadow30}; border-radius: 12px; - padding: 0 16px; + padding: 4px 16px; background-color: ${theme.colors.white}; width: 80%; diff --git a/frontend/src/components/host/SectionDetailModal/index.tsx b/frontend/src/components/host/SectionDetailModal/index.tsx index 9eeea7c2..57e42a00 100644 --- a/frontend/src/components/host/SectionDetailModal/index.tsx +++ b/frontend/src/components/host/SectionDetailModal/index.tsx @@ -1,5 +1,5 @@ import useSectionDetailModal from './useSectionDetailModal'; -import { BiX } from 'react-icons/bi'; +import { BiX } from '@react-icons/all-files/bi/BiX'; import Button from '@/components/common/Button'; import Dimmer from '@/components/common/Dimmer'; @@ -36,7 +36,7 @@ const SectionDetailModal: React.FC = props => { return ( - +

@@ -47,7 +47,7 @@ const SectionDetailModal: React.FC = props => { fileInput.current?.click()} /> @@ -55,14 +55,14 @@ const SectionDetailModal: React.FC = props => {