From 05fc98efe06cf19eb6be18834d8dbaa04148a091 Mon Sep 17 00:00:00 2001 From: 10tentacion Date: Thu, 1 May 2025 21:26:22 +0900 Subject: [PATCH 01/30] =?UTF-8?q?feat:=20=EC=9D=BC=EB=A0=89=ED=8A=B8?= =?UTF-8?q?=EB=A1=A0=20=EA=B0=9C=EB=B0=9C=20=EA=B5=AC=EC=A1=B0=20=EC=9E=A1?= =?UTF-8?q?=EA=B8=B0=20=EB=B0=8F=20=EC=84=A4=EC=A0=95=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EA=B5=AC=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + electron-builder.json | 22 + index.html | 2 +- package.json | 17 +- pnpm-lock.yaml | 1984 ++++++++++++++++- public/logo_icon.png | Bin 0 -> 75079 bytes src/{ => app}/App.tsx | 6 + src/{ => app}/index.css | 0 src/{ => app}/main.tsx | 0 src/{ => app}/mocks/browser.ts | 0 .../mocks/common/common.resolvers.ts | 0 .../mocks/common/common.responses.ts | 0 src/{ => app}/mocks/handlers.ts | 0 src/{ => app}/mocks/home/home.resolvers.ts | 0 src/{ => app}/mocks/home/home.responses.ts | 0 .../mocks/onboarding/onboarding.resolvers.ts | 0 .../mocks/onboarding/onboarding.responses.ts | 0 .../AllowedServiceGroupDetail.tsx | 0 .../AllowedServiceGroupDetailContent.tsx | 0 .../AllowedServiceGroupDetailHeader.tsx | 0 .../Tabs/AllowedServiceGroupDetailTabs.tsx | 0 .../AllowedServiceList/AllowedServiceList.tsx | 0 .../AllowedServicePage/AllowedServicePage.tsx | 0 .../RecommendService/RecommendService.tsx | 0 .../BoxAddCategory/BoxAddCategory.tsx | 0 .../HomePage/BoxCategory/BoxCategory.tsx | 0 .../BoxCategory/BoxTodoInput/BoxTodoInput.tsx | 0 .../StatusDefaultBoxCategory.tsx | 0 .../BoxCategory/hooks/useCreateTodo.ts | 0 .../HomePage/BoxTodayTodo/BoxTodayTodo.tsx | 0 .../ButtonHomeSmall/ButtonHomeSmall.tsx | 0 .../StatusAddBoxTodayTodo.tsx | 0 .../StatusDefaultBoxTodayTodo.tsx | 0 .../ButtonMoreFriends/ButtonMoreFriends.tsx | 0 .../ButtonUserProfile/ButtonUserProfile.tsx | 0 .../DatePicker/ButtonDate/ButtonDate.tsx | 0 .../pages/HomePage/DatePicker/DatePicker.tsx | 0 .../DatePicker/hooks/useDatePicker.ts | 0 src/{ => app}/pages/HomePage/HomePage.tsx | 0 .../ButtonAlert/ButtonAlert.tsx | 0 .../ModalContentsAlert/Complete/Complete.tsx | 0 .../DeleteAccount/DeleteAccount.tsx | 0 .../ModalContentsAlert/Logoout/Logout.tsx | 0 .../ModalContentsAlert/ModalContentsAlert.tsx | 0 .../ModalContentsAlert/types/index.ts | 0 .../StatusDefaultHome/StatusDefaultHome.tsx | 0 .../pages/HomePage/hooks/useCalendar.ts | 0 src/{ => app}/pages/LoginPage/LoginPage.tsx | 1 + .../LoginPage/hooks/useLottieAnimation.ts | 0 .../pages/NotFoundPage/NotFoundPage.tsx | 0 .../OnboardingPage/ButtonSkip/ButtonSkip.tsx | 0 .../pages/OnboardingPage/OnboardingPage.tsx | 0 .../OnboardingPage/StepField/StepField.tsx | 0 .../AllowedServices/AllowedServices.tsx | 0 .../ButtonService/ButtonService.tsx | 0 .../StepService/StepService.tsx | 0 .../OnboardingPage/StepService/Tabs/Tabs.tsx | 0 .../OnboardingPage/StepStart/StepStart.tsx | 0 .../pages/OnboardingPage/hooks/useFunnel.tsx | 0 .../pages/OnboardingPage/utils/serviceUrl.ts | 0 .../pages/RedirectPage/RedirectPage.tsx | 0 .../pages/TimerPage/Carousel/Carousel.tsx | 0 .../ContainerCarousel/ContainerCarousel.tsx | 0 .../hooks/useCarouselTimer.ts | 0 .../Checkbox/Checkbox.tsx | 0 .../PopoverAllowedService.tsx | 0 .../TimerPage/SidebarTimer/SideBarTimer.tsx | 0 .../TitleAllowedService.tsx | 0 .../ToolTipAllowedService.tsx | 0 .../Timer/ButtonTimerPlay/ButtonTimerPlay.tsx | 0 .../Timer/ProgressCircle/ProgressCircle.tsx | 0 src/{ => app}/pages/TimerPage/Timer/Timer.tsx | 0 src/{ => app}/pages/TimerPage/TimerPage.tsx | 0 .../pages/TimerPage/hooks/useTimerCount.ts | 0 .../pages/TimerPage/hooks/useToggleSidebar.ts | 0 .../pages/TimerPage/hooks/useUrlHandler.ts | 0 src/{ => app}/router/ProtectedRoute.tsx | 0 src/{ => app}/router/Router.tsx | 4 +- src/{ => app}/router/routesConfig.ts | 0 .../allowedService/allowedService.api.ts | 0 .../allowedService/allowedService.keys.ts | 0 .../allowedService.mutations.ts | 0 .../allowedService/allowedService.queries.ts | 0 src/{ => app}/shared/apisV2/auth/auth.api.ts | 0 .../shared/apisV2/auth/auth.queries.ts | 0 src/{ => app}/shared/apisV2/client.ts | 0 .../shared/apisV2/common/common.api.ts | 0 .../shared/apisV2/common/common.keys.ts | 0 .../shared/apisV2/common/common.mutations.ts | 0 .../shared/apisV2/common/common.queries.ts | 0 .../shared/apisV2/friends/friends.api.ts | 0 .../shared/apisV2/friends/friends.keys.ts | 0 .../apisV2/friends/friends.mutations.ts | 0 .../shared/apisV2/friends/friends.queries.ts | 0 src/{ => app}/shared/apisV2/home/home.api.ts | 0 src/{ => app}/shared/apisV2/home/home.keys.ts | 0 .../shared/apisV2/home/home.mutations.ts | 0 .../shared/apisV2/home/home.queries.ts | 0 .../apisV2/onboarding/onboarding.api.ts | 0 .../apisV2/onboarding/onboarding.mutations.ts | 0 src/{ => app}/shared/apisV2/queryClient.ts | 0 .../shared/apisV2/setting/setting.api.ts | 0 .../shared/apisV2/setting/setting.keys.ts | 0 .../apisV2/setting/setting.mutations.ts | 0 .../shared/apisV2/setting/setting.queries.ts | 0 .../shared/apisV2/timer/timer.api.ts | 0 .../shared/apisV2/timer/timer.keys.ts | 0 .../shared/apisV2/timer/timer.mutations.ts | 0 .../shared/apisV2/timer/timer.queries.ts | 0 .../shared/assets/images/example.jpg | 0 .../shared/assets/images/img_timer_bg.png | Bin .../shared/assets/images/login_background.png | Bin .../shared/assets/images/profile_image1.png | Bin .../shared/assets/images/profile_image2.png | Bin .../shared/assets/images/profile_image3.png | Bin .../shared/assets/images/profile_image4.png | Bin .../shared/assets/lotties/loading.json | 0 .../assets/lotties/morib_logo_motion.json | 0 src/{ => app}/shared/assets/svgs/404.svg | 0 src/{ => app}/shared/assets/svgs/add_btn.svg | 0 .../assets/svgs/arrow_circle_up_right.svg | 0 .../shared/assets/svgs/arrow_right.svg | 0 src/{ => app}/shared/assets/svgs/bell.svg | 0 src/{ => app}/shared/assets/svgs/btn_add.svg | 0 .../shared/assets/svgs/btn_arrow.svg | 0 .../shared/assets/svgs/btn_arrow_bgNone.svg | 0 src/{ => app}/shared/assets/svgs/btn_cal.svg | 0 .../shared/assets/svgs/btn_hamburger.svg | 0 src/{ => app}/shared/assets/svgs/btn_home.svg | 0 .../shared/assets/svgs/btn_inputClear.svg | 0 src/{ => app}/shared/assets/svgs/btn_list.svg | 0 .../assets/svgs/btn_moribset_active.svg | 0 .../assets/svgs/btn_moribset_default.svg | 0 .../shared/assets/svgs/btn_today.svg | 0 .../assets/svgs/button_inputSuccess.svg | 0 .../shared/assets/svgs/check_box_blank.svg | 0 .../shared/assets/svgs/check_box_fill.svg | 0 .../shared/assets/svgs/common/ic_logo.svg | 0 .../svgs/common/ic_meatball_default.svg | 0 .../shared/assets/svgs/connection_icon.svg | 0 .../shared/assets/svgs/default_profile.svg | 0 .../shared/assets/svgs/defaultpause.svg | 0 .../shared/assets/svgs/defaultplay.svg | 0 .../shared/assets/svgs/description.svg | 0 .../shared/assets/svgs/disabled_dropdown.svg | 0 src/{ => app}/shared/assets/svgs/dropIcon.svg | 0 src/{ => app}/shared/assets/svgs/elipse.svg | 0 src/{ => app}/shared/assets/svgs/error.svg | 0 .../shared/assets/svgs/error_input.svg | 0 .../shared/assets/svgs/friend_delBtn.svg | 0 .../shared/assets/svgs/friend_setting.svg | 0 .../shared/assets/svgs/google_login.svg | 0 .../shared/assets/svgs/gradient_circle.svg | 0 .../shared/assets/svgs/header_delBtn.svg | 0 .../shared/assets/svgs/home/ic_box.svg | 0 .../shared/assets/svgs/home/ic_plus.svg | 0 .../shared/assets/svgs/home_default_icon.svg | 0 .../shared/assets/svgs/hoverpause.svg | 0 .../shared/assets/svgs/hoverplay.svg | 0 .../shared/assets/svgs/ic_back_btn.svg | 0 .../shared/assets/svgs/ic_delete_alert.svg | 0 .../shared/assets/svgs/ic_description.svg | 0 .../shared/assets/svgs/ic_folder.svg | 0 src/{ => app}/shared/assets/svgs/ic_gear.svg | 0 src/{ => app}/shared/assets/svgs/ic_logo.svg | 0 src/{ => app}/shared/assets/svgs/ic_minus.svg | 0 .../shared/assets/svgs/ic_pencil.svg | 0 .../shared/assets/svgs/ic_service_design.svg | 0 .../assets/svgs/ic_service_design_sm.svg | 0 .../shared/assets/svgs/icon_clock.svg | 0 .../shared/assets/svgs/large_plus.svg | 0 .../shared/assets/svgs/logo_icon.svg | 0 src/{ => app}/shared/assets/svgs/mail.svg | 0 .../shared/assets/svgs/mingcute_time-fill.svg | 0 .../shared/assets/svgs/mingcute_time-line.svg | 0 .../shared/assets/svgs/minus_btn.svg | 0 .../shared/assets/svgs/more_friend.svg | 0 src/{ => app}/shared/assets/svgs/moribSet.svg | 0 .../assets/svgs/onboarding/ic_business.svg | 0 .../assets/svgs/onboarding/ic_design.svg | 0 .../assets/svgs/onboarding/ic_development.svg | 0 .../assets/svgs/onboarding/ic_marketing.svg | 0 .../assets/svgs/onboarding/ic_planning.svg | 0 .../assets/svgs/onboarding/ic_studying.svg | 0 .../shared/assets/svgs/onboarding_image.svg | 0 src/{ => app}/shared/assets/svgs/plus.svg | 0 src/{ => app}/shared/assets/svgs/react.svg | 0 .../assets/svgs/selected_number_icon.svg | 0 src/{ => app}/shared/assets/svgs/setting.svg | 0 .../assets/svgs/timer/ic_check_box_active.svg | 0 .../svgs/timer/ic_check_box_inactive.svg | 0 .../svgs/timer/ic_deactivated_clock.svg | 0 .../shared/assets/svgs/timer/ic_online.svg | 0 .../svgs/timer/ic_timer_inner_circle.svg | 0 .../assets/svgs/todo_meatball_press.svg | 0 .../shared/assets/svgs/todo_toggle.svg | 0 src/{ => app}/shared/assets/svgs/triangle.svg | 0 src/{ => app}/shared/assets/svgs/upIcon.svg | 0 .../shared/assets/svgs/user_circle.svg | 0 .../AutoFixedGrid/AutoFixedGrid.tsx | 0 .../shared/components/BoxTodo/BoxTodo.tsx | 0 .../ButtonArrowSVG/ButtonArrowSVG.tsx | 0 .../ButtonDropdownOptions.tsx | 0 .../ButtonHomeLarge/ButtonHomeLarge.tsx | 0 .../ButtonRadius5/ButtonRadius5.tsx | 0 .../ButtonRadius8/ButtonRadius8.tsx | 0 .../ButtonStatusToggle/ButtonStatusToggle.tsx | 0 .../ButtonTodayToggle/ButtonTodoToggle.tsx | 0 .../ButtonCalendarAddRoutine.tsx | 0 .../shared/components/Calendar/Calendar.tsx | 0 .../HeaderCalendar/HeaderCalendar.tsx | 4 +- .../shared/components/Calendar/calendar.css | 0 .../CircleColorIcon/CircleColorIcon.tsx | 0 .../components/ColorPallete/ColorPallete.tsx | 0 .../shared/components/Dropdown/Dropdown.tsx | 0 .../ErrorBoundary/ErrorBoundary.tsx | 3 +- .../FallbackApiError/FallbackApiError.tsx | 0 .../HeartBeatBoundary/HeartBeatBoundary.tsx | 0 .../LoadingOverlay/LoadingOverlay.tsx | 0 .../ButtonRequestAction.tsx | 0 .../FriendsListRequested.tsx | 0 .../FriendRequest/FriendsRequest.tsx | 0 .../FriendUserProfile/FriendUserProfile.tsx | 0 .../FriendsList/FriendsInfo/FriendInfo.tsx | 0 .../FriendsList/FriendsList.tsx | 0 .../ModalContentsFriends.tsx | 0 .../components/ModalWrapper/ModalWrapper.tsx | 0 .../components/ModalWrapper/styles/dialog.css | 0 .../shared/components/Portal/Portal.tsx | 0 .../shared/components/Spacer/Spacer.tsx | 0 .../shared/components/TextField/TextField.tsx | 0 src/{ => app}/shared/constants/btnText.ts | 0 .../shared/constants/colorPalette.ts | 0 src/{ => app}/shared/constants/emailRegex.ts | 0 src/{ => app}/shared/constants/error.ts | 0 src/{ => app}/shared/constants/fields.ts | 0 .../shared/constants/suggestedSites.ts | 0 .../shared/constants/timerPageText.ts | 0 src/{ => app}/shared/constants/weekDays.ts | 0 .../shared/hocs/withAuthProtection.tsx | 0 src/{ => app}/shared/hooks/useCarousel.ts | 0 src/{ => app}/shared/hooks/useClickOutside.ts | 0 src/{ => app}/shared/layout/Layout.tsx | 0 .../AccountContent/AccountContent.tsx | 0 .../ModalContentsSetting.tsx | 0 .../ModalContentsSetting/Tabs/Tabs.tsx | 0 .../WorkspaceSettingContent.tsx | 0 .../shared/layout/Sidebar/Sidebar.tsx | 0 src/{ => app}/shared/mocks/categoryData.ts | 0 src/{ => app}/shared/mocks/faviconData.ts | 0 src/{ => app}/shared/mocks/homeData.ts | 0 src/{ => app}/shared/mocks/urlData.ts | 0 src/{ => app}/shared/mocks/userData.ts | 0 src/{ => app}/shared/types/SSEEvent.ts | 0 src/{ => app}/shared/types/allowedService.ts | 0 src/{ => app}/shared/types/allowedSites.ts | 0 .../shared/types/api/allowedService.ts | 0 src/{ => app}/shared/types/api/auth.ts | 0 src/{ => app}/shared/types/api/common.ts | 0 src/{ => app}/shared/types/api/error.ts | 0 src/{ => app}/shared/types/api/friends.ts | 0 src/{ => app}/shared/types/api/home.ts | 0 src/{ => app}/shared/types/api/onboarding.ts | 0 src/{ => app}/shared/types/api/setting.ts | 0 src/{ => app}/shared/types/api/timer.ts | 0 src/{ => app}/shared/types/common/index.tsx | 0 src/{ => app}/shared/types/fileds.ts | 0 src/{ => app}/shared/types/friend.ts | 0 src/{ => app}/shared/types/global.ts | 0 src/{ => app}/shared/types/home/index.tsx | 0 src/{ => app}/shared/types/profile.ts | 0 src/{ => app}/shared/types/tasks.ts | 0 src/{ => app}/shared/types/todoData.ts | 0 src/{ => app}/shared/types/userData.ts | 0 src/{ => app}/shared/utils/auth.ts | 0 src/{ => app}/shared/utils/calendar/index.ts | 0 src/{ => app}/shared/utils/date/index.ts | 0 src/{ => app}/shared/utils/error.ts | 0 src/{ => app}/shared/utils/path.ts | 0 src/{ => app}/shared/utils/tasks.ts | 0 src/{ => app}/shared/utils/time/index.ts | 0 src/{ => app}/shared/utils/timer/index.ts | 0 src/{ => app}/shared/utils/url.ts | 0 src/{ => app}/shared/utils/url/index.ts | 0 src/{ => app}/shared/utils/validation.ts | 0 src/{ => app}/vite-env.d.ts | 0 src/electron/main.ts | 20 + src/electron/pathResolver.ts | 8 + src/electron/preload.cts | 8 + src/electron/tsconfig.json | 9 + src/electron/util.ts | 3 + tsconfig.app.json | 2 +- tsconfig.node.json | 2 +- vite.config.ts | 10 +- 294 files changed, 2089 insertions(+), 18 deletions(-) create mode 100644 electron-builder.json create mode 100644 public/logo_icon.png rename src/{ => app}/App.tsx (83%) rename src/{ => app}/index.css (100%) rename src/{ => app}/main.tsx (100%) rename src/{ => app}/mocks/browser.ts (100%) rename src/{ => app}/mocks/common/common.resolvers.ts (100%) rename src/{ => app}/mocks/common/common.responses.ts (100%) rename src/{ => app}/mocks/handlers.ts (100%) rename src/{ => app}/mocks/home/home.resolvers.ts (100%) rename src/{ => app}/mocks/home/home.responses.ts (100%) rename src/{ => app}/mocks/onboarding/onboarding.resolvers.ts (100%) rename src/{ => app}/mocks/onboarding/onboarding.responses.ts (100%) rename src/{ => app}/pages/AllowedServicePage/AllowedServiceGroupDetail/AllowedServiceGroupDetail.tsx (100%) rename src/{ => app}/pages/AllowedServicePage/AllowedServiceGroupDetail/Content/AllowedServiceGroupDetailContent.tsx (100%) rename src/{ => app}/pages/AllowedServicePage/AllowedServiceGroupDetail/Header/AllowedServiceGroupDetailHeader.tsx (100%) rename src/{ => app}/pages/AllowedServicePage/AllowedServiceGroupDetail/Tabs/AllowedServiceGroupDetailTabs.tsx (100%) rename src/{ => app}/pages/AllowedServicePage/AllowedServiceList/AllowedServiceList.tsx (100%) rename src/{ => app}/pages/AllowedServicePage/AllowedServicePage.tsx (100%) rename src/{ => app}/pages/AllowedServicePage/RecommendService/RecommendService.tsx (100%) rename src/{ => app}/pages/HomePage/BoxAddCategory/BoxAddCategory.tsx (100%) rename src/{ => app}/pages/HomePage/BoxCategory/BoxCategory.tsx (100%) rename src/{ => app}/pages/HomePage/BoxCategory/BoxTodoInput/BoxTodoInput.tsx (100%) rename src/{ => app}/pages/HomePage/BoxCategory/StatusDefaultBoxCategory/StatusDefaultBoxCategory.tsx (100%) rename src/{ => app}/pages/HomePage/BoxCategory/hooks/useCreateTodo.ts (100%) rename src/{ => app}/pages/HomePage/BoxTodayTodo/BoxTodayTodo.tsx (100%) rename src/{ => app}/pages/HomePage/BoxTodayTodo/StatusAddBoxTodayTodo/ButtonHomeSmall/ButtonHomeSmall.tsx (100%) rename src/{ => app}/pages/HomePage/BoxTodayTodo/StatusAddBoxTodayTodo/StatusAddBoxTodayTodo.tsx (100%) rename src/{ => app}/pages/HomePage/BoxTodayTodo/StatusDefaultBoxTodayTodo/StatusDefaultBoxTodayTodo.tsx (100%) rename src/{ => app}/pages/HomePage/ButtonMoreFriends/ButtonMoreFriends.tsx (100%) rename src/{ => app}/pages/HomePage/ButtonUserProfile/ButtonUserProfile.tsx (100%) rename src/{ => app}/pages/HomePage/DatePicker/ButtonDate/ButtonDate.tsx (100%) rename src/{ => app}/pages/HomePage/DatePicker/DatePicker.tsx (100%) rename src/{ => app}/pages/HomePage/DatePicker/hooks/useDatePicker.ts (100%) rename src/{ => app}/pages/HomePage/HomePage.tsx (100%) rename src/{ => app}/pages/HomePage/ModalContentsAlert/ButtonAlert/ButtonAlert.tsx (100%) rename src/{ => app}/pages/HomePage/ModalContentsAlert/Complete/Complete.tsx (100%) rename src/{ => app}/pages/HomePage/ModalContentsAlert/DeleteAccount/DeleteAccount.tsx (100%) rename src/{ => app}/pages/HomePage/ModalContentsAlert/Logoout/Logout.tsx (100%) rename src/{ => app}/pages/HomePage/ModalContentsAlert/ModalContentsAlert.tsx (100%) rename src/{ => app}/pages/HomePage/ModalContentsAlert/types/index.ts (100%) rename src/{ => app}/pages/HomePage/StatusDefaultHome/StatusDefaultHome.tsx (100%) rename src/{ => app}/pages/HomePage/hooks/useCalendar.ts (100%) rename src/{ => app}/pages/LoginPage/LoginPage.tsx (99%) rename src/{ => app}/pages/LoginPage/hooks/useLottieAnimation.ts (100%) rename src/{ => app}/pages/NotFoundPage/NotFoundPage.tsx (100%) rename src/{ => app}/pages/OnboardingPage/ButtonSkip/ButtonSkip.tsx (100%) rename src/{ => app}/pages/OnboardingPage/OnboardingPage.tsx (100%) rename src/{ => app}/pages/OnboardingPage/StepField/StepField.tsx (100%) rename src/{ => app}/pages/OnboardingPage/StepService/AllowedServices/AllowedServices.tsx (100%) rename src/{ => app}/pages/OnboardingPage/StepService/ButtonService/ButtonService.tsx (100%) rename src/{ => app}/pages/OnboardingPage/StepService/StepService.tsx (100%) rename src/{ => app}/pages/OnboardingPage/StepService/Tabs/Tabs.tsx (100%) rename src/{ => app}/pages/OnboardingPage/StepStart/StepStart.tsx (100%) rename src/{ => app}/pages/OnboardingPage/hooks/useFunnel.tsx (100%) rename src/{ => app}/pages/OnboardingPage/utils/serviceUrl.ts (100%) rename src/{ => app}/pages/RedirectPage/RedirectPage.tsx (100%) rename src/{ => app}/pages/TimerPage/Carousel/Carousel.tsx (100%) rename src/{ => app}/pages/TimerPage/Carousel/ContainerCarousel/ContainerCarousel.tsx (100%) rename src/{ => app}/pages/TimerPage/Carousel/ContainerCarousel/hooks/useCarouselTimer.ts (100%) rename src/{ => app}/pages/TimerPage/PopoverAllowedService/Checkbox/Checkbox.tsx (100%) rename src/{ => app}/pages/TimerPage/PopoverAllowedService/PopoverAllowedService.tsx (100%) rename src/{ => app}/pages/TimerPage/SidebarTimer/SideBarTimer.tsx (100%) rename src/{ => app}/pages/TimerPage/TItleAllowedService/TitleAllowedService.tsx (100%) rename src/{ => app}/pages/TimerPage/TItleAllowedService/TooltipAllowedService/ToolTipAllowedService.tsx (100%) rename src/{ => app}/pages/TimerPage/Timer/ButtonTimerPlay/ButtonTimerPlay.tsx (100%) rename src/{ => app}/pages/TimerPage/Timer/ProgressCircle/ProgressCircle.tsx (100%) rename src/{ => app}/pages/TimerPage/Timer/Timer.tsx (100%) rename src/{ => app}/pages/TimerPage/TimerPage.tsx (100%) rename src/{ => app}/pages/TimerPage/hooks/useTimerCount.ts (100%) rename src/{ => app}/pages/TimerPage/hooks/useToggleSidebar.ts (100%) rename src/{ => app}/pages/TimerPage/hooks/useUrlHandler.ts (100%) rename src/{ => app}/router/ProtectedRoute.tsx (100%) rename src/{ => app}/router/Router.tsx (95%) rename src/{ => app}/router/routesConfig.ts (100%) rename src/{ => app}/shared/apisV2/allowedService/allowedService.api.ts (100%) rename src/{ => app}/shared/apisV2/allowedService/allowedService.keys.ts (100%) rename src/{ => app}/shared/apisV2/allowedService/allowedService.mutations.ts (100%) rename src/{ => app}/shared/apisV2/allowedService/allowedService.queries.ts (100%) rename src/{ => app}/shared/apisV2/auth/auth.api.ts (100%) rename src/{ => app}/shared/apisV2/auth/auth.queries.ts (100%) rename src/{ => app}/shared/apisV2/client.ts (100%) rename src/{ => app}/shared/apisV2/common/common.api.ts (100%) rename src/{ => app}/shared/apisV2/common/common.keys.ts (100%) rename src/{ => app}/shared/apisV2/common/common.mutations.ts (100%) rename src/{ => app}/shared/apisV2/common/common.queries.ts (100%) rename src/{ => app}/shared/apisV2/friends/friends.api.ts (100%) rename src/{ => app}/shared/apisV2/friends/friends.keys.ts (100%) rename src/{ => app}/shared/apisV2/friends/friends.mutations.ts (100%) rename src/{ => app}/shared/apisV2/friends/friends.queries.ts (100%) rename src/{ => app}/shared/apisV2/home/home.api.ts (100%) rename src/{ => app}/shared/apisV2/home/home.keys.ts (100%) rename src/{ => app}/shared/apisV2/home/home.mutations.ts (100%) rename src/{ => app}/shared/apisV2/home/home.queries.ts (100%) rename src/{ => app}/shared/apisV2/onboarding/onboarding.api.ts (100%) rename src/{ => app}/shared/apisV2/onboarding/onboarding.mutations.ts (100%) rename src/{ => app}/shared/apisV2/queryClient.ts (100%) rename src/{ => app}/shared/apisV2/setting/setting.api.ts (100%) rename src/{ => app}/shared/apisV2/setting/setting.keys.ts (100%) rename src/{ => app}/shared/apisV2/setting/setting.mutations.ts (100%) rename src/{ => app}/shared/apisV2/setting/setting.queries.ts (100%) rename src/{ => app}/shared/apisV2/timer/timer.api.ts (100%) rename src/{ => app}/shared/apisV2/timer/timer.keys.ts (100%) rename src/{ => app}/shared/apisV2/timer/timer.mutations.ts (100%) rename src/{ => app}/shared/apisV2/timer/timer.queries.ts (100%) rename src/{ => app}/shared/assets/images/example.jpg (100%) rename src/{ => app}/shared/assets/images/img_timer_bg.png (100%) rename src/{ => app}/shared/assets/images/login_background.png (100%) rename src/{ => app}/shared/assets/images/profile_image1.png (100%) rename src/{ => app}/shared/assets/images/profile_image2.png (100%) rename src/{ => app}/shared/assets/images/profile_image3.png (100%) rename src/{ => app}/shared/assets/images/profile_image4.png (100%) rename src/{ => app}/shared/assets/lotties/loading.json (100%) rename src/{ => app}/shared/assets/lotties/morib_logo_motion.json (100%) rename src/{ => app}/shared/assets/svgs/404.svg (100%) rename src/{ => app}/shared/assets/svgs/add_btn.svg (100%) rename src/{ => app}/shared/assets/svgs/arrow_circle_up_right.svg (100%) rename src/{ => app}/shared/assets/svgs/arrow_right.svg (100%) rename src/{ => app}/shared/assets/svgs/bell.svg (100%) rename src/{ => app}/shared/assets/svgs/btn_add.svg (100%) rename src/{ => app}/shared/assets/svgs/btn_arrow.svg (100%) rename src/{ => app}/shared/assets/svgs/btn_arrow_bgNone.svg (100%) rename src/{ => app}/shared/assets/svgs/btn_cal.svg (100%) rename src/{ => app}/shared/assets/svgs/btn_hamburger.svg (100%) rename src/{ => app}/shared/assets/svgs/btn_home.svg (100%) rename src/{ => app}/shared/assets/svgs/btn_inputClear.svg (100%) rename src/{ => app}/shared/assets/svgs/btn_list.svg (100%) rename src/{ => app}/shared/assets/svgs/btn_moribset_active.svg (100%) rename src/{ => app}/shared/assets/svgs/btn_moribset_default.svg (100%) rename src/{ => app}/shared/assets/svgs/btn_today.svg (100%) rename src/{ => app}/shared/assets/svgs/button_inputSuccess.svg (100%) rename src/{ => app}/shared/assets/svgs/check_box_blank.svg (100%) rename src/{ => app}/shared/assets/svgs/check_box_fill.svg (100%) rename src/{ => app}/shared/assets/svgs/common/ic_logo.svg (100%) rename src/{ => app}/shared/assets/svgs/common/ic_meatball_default.svg (100%) rename src/{ => app}/shared/assets/svgs/connection_icon.svg (100%) rename src/{ => app}/shared/assets/svgs/default_profile.svg (100%) rename src/{ => app}/shared/assets/svgs/defaultpause.svg (100%) rename src/{ => app}/shared/assets/svgs/defaultplay.svg (100%) rename src/{ => app}/shared/assets/svgs/description.svg (100%) rename src/{ => app}/shared/assets/svgs/disabled_dropdown.svg (100%) rename src/{ => app}/shared/assets/svgs/dropIcon.svg (100%) rename src/{ => app}/shared/assets/svgs/elipse.svg (100%) rename src/{ => app}/shared/assets/svgs/error.svg (100%) rename src/{ => app}/shared/assets/svgs/error_input.svg (100%) rename src/{ => app}/shared/assets/svgs/friend_delBtn.svg (100%) rename src/{ => app}/shared/assets/svgs/friend_setting.svg (100%) rename src/{ => app}/shared/assets/svgs/google_login.svg (100%) rename src/{ => app}/shared/assets/svgs/gradient_circle.svg (100%) rename src/{ => app}/shared/assets/svgs/header_delBtn.svg (100%) rename src/{ => app}/shared/assets/svgs/home/ic_box.svg (100%) rename src/{ => app}/shared/assets/svgs/home/ic_plus.svg (100%) rename src/{ => app}/shared/assets/svgs/home_default_icon.svg (100%) rename src/{ => app}/shared/assets/svgs/hoverpause.svg (100%) rename src/{ => app}/shared/assets/svgs/hoverplay.svg (100%) rename src/{ => app}/shared/assets/svgs/ic_back_btn.svg (100%) rename src/{ => app}/shared/assets/svgs/ic_delete_alert.svg (100%) rename src/{ => app}/shared/assets/svgs/ic_description.svg (100%) rename src/{ => app}/shared/assets/svgs/ic_folder.svg (100%) rename src/{ => app}/shared/assets/svgs/ic_gear.svg (100%) rename src/{ => app}/shared/assets/svgs/ic_logo.svg (100%) rename src/{ => app}/shared/assets/svgs/ic_minus.svg (100%) rename src/{ => app}/shared/assets/svgs/ic_pencil.svg (100%) rename src/{ => app}/shared/assets/svgs/ic_service_design.svg (100%) rename src/{ => app}/shared/assets/svgs/ic_service_design_sm.svg (100%) rename src/{ => app}/shared/assets/svgs/icon_clock.svg (100%) rename src/{ => app}/shared/assets/svgs/large_plus.svg (100%) rename src/{ => app}/shared/assets/svgs/logo_icon.svg (100%) rename src/{ => app}/shared/assets/svgs/mail.svg (100%) rename src/{ => app}/shared/assets/svgs/mingcute_time-fill.svg (100%) rename src/{ => app}/shared/assets/svgs/mingcute_time-line.svg (100%) rename src/{ => app}/shared/assets/svgs/minus_btn.svg (100%) rename src/{ => app}/shared/assets/svgs/more_friend.svg (100%) rename src/{ => app}/shared/assets/svgs/moribSet.svg (100%) rename src/{ => app}/shared/assets/svgs/onboarding/ic_business.svg (100%) rename src/{ => app}/shared/assets/svgs/onboarding/ic_design.svg (100%) rename src/{ => app}/shared/assets/svgs/onboarding/ic_development.svg (100%) rename src/{ => app}/shared/assets/svgs/onboarding/ic_marketing.svg (100%) rename src/{ => app}/shared/assets/svgs/onboarding/ic_planning.svg (100%) rename src/{ => app}/shared/assets/svgs/onboarding/ic_studying.svg (100%) rename src/{ => app}/shared/assets/svgs/onboarding_image.svg (100%) rename src/{ => app}/shared/assets/svgs/plus.svg (100%) rename src/{ => app}/shared/assets/svgs/react.svg (100%) rename src/{ => app}/shared/assets/svgs/selected_number_icon.svg (100%) rename src/{ => app}/shared/assets/svgs/setting.svg (100%) rename src/{ => app}/shared/assets/svgs/timer/ic_check_box_active.svg (100%) rename src/{ => app}/shared/assets/svgs/timer/ic_check_box_inactive.svg (100%) rename src/{ => app}/shared/assets/svgs/timer/ic_deactivated_clock.svg (100%) rename src/{ => app}/shared/assets/svgs/timer/ic_online.svg (100%) rename src/{ => app}/shared/assets/svgs/timer/ic_timer_inner_circle.svg (100%) rename src/{ => app}/shared/assets/svgs/todo_meatball_press.svg (100%) rename src/{ => app}/shared/assets/svgs/todo_toggle.svg (100%) rename src/{ => app}/shared/assets/svgs/triangle.svg (100%) rename src/{ => app}/shared/assets/svgs/upIcon.svg (100%) rename src/{ => app}/shared/assets/svgs/user_circle.svg (100%) rename src/{ => app}/shared/components/AutoFixedGrid/AutoFixedGrid.tsx (100%) rename src/{ => app}/shared/components/BoxTodo/BoxTodo.tsx (100%) rename src/{ => app}/shared/components/ButtonArrowSVG/ButtonArrowSVG.tsx (100%) rename src/{ => app}/shared/components/ButtonDropdownOptions/ButtonDropdownOptions.tsx (100%) rename src/{ => app}/shared/components/ButtonHomeLarge/ButtonHomeLarge.tsx (100%) rename src/{ => app}/shared/components/ButtonRadius5/ButtonRadius5.tsx (100%) rename src/{ => app}/shared/components/ButtonRadius8/ButtonRadius8.tsx (100%) rename src/{ => app}/shared/components/ButtonStatusToggle/ButtonStatusToggle.tsx (100%) rename src/{ => app}/shared/components/ButtonTodayToggle/ButtonTodoToggle.tsx (100%) rename src/{ => app}/shared/components/Calendar/ButtonCalendarAddRoutine/ButtonCalendarAddRoutine.tsx (100%) rename src/{ => app}/shared/components/Calendar/Calendar.tsx (100%) rename src/{ => app}/shared/components/Calendar/HeaderCalendar/HeaderCalendar.tsx (89%) rename src/{ => app}/shared/components/Calendar/calendar.css (100%) rename src/{ => app}/shared/components/CircleColorIcon/CircleColorIcon.tsx (100%) rename src/{ => app}/shared/components/ColorPallete/ColorPallete.tsx (100%) rename src/{ => app}/shared/components/Dropdown/Dropdown.tsx (100%) rename src/{ => app}/shared/components/ErrorBoundary/ErrorBoundary.tsx (93%) rename src/{ => app}/shared/components/FallbackApiError/FallbackApiError.tsx (100%) rename src/{ => app}/shared/components/HeartBeatBoundary/HeartBeatBoundary.tsx (100%) rename src/{ => app}/shared/components/LoadingOverlay/LoadingOverlay.tsx (100%) rename src/{ => app}/shared/components/ModalContentsFriends/FriendRequest/ButtonRequestAction/ButtonRequestAction.tsx (100%) rename src/{ => app}/shared/components/ModalContentsFriends/FriendRequest/FriendsListRequested/FriendsListRequested.tsx (100%) rename src/{ => app}/shared/components/ModalContentsFriends/FriendRequest/FriendsRequest.tsx (100%) rename src/{ => app}/shared/components/ModalContentsFriends/FriendUserProfile/FriendUserProfile.tsx (100%) rename src/{ => app}/shared/components/ModalContentsFriends/FriendsList/FriendsInfo/FriendInfo.tsx (100%) rename src/{ => app}/shared/components/ModalContentsFriends/FriendsList/FriendsList.tsx (100%) rename src/{ => app}/shared/components/ModalContentsFriends/ModalContentsFriends.tsx (100%) rename src/{ => app}/shared/components/ModalWrapper/ModalWrapper.tsx (100%) rename src/{ => app}/shared/components/ModalWrapper/styles/dialog.css (100%) rename src/{ => app}/shared/components/Portal/Portal.tsx (100%) rename src/{ => app}/shared/components/Spacer/Spacer.tsx (100%) rename src/{ => app}/shared/components/TextField/TextField.tsx (100%) rename src/{ => app}/shared/constants/btnText.ts (100%) rename src/{ => app}/shared/constants/colorPalette.ts (100%) rename src/{ => app}/shared/constants/emailRegex.ts (100%) rename src/{ => app}/shared/constants/error.ts (100%) rename src/{ => app}/shared/constants/fields.ts (100%) rename src/{ => app}/shared/constants/suggestedSites.ts (100%) rename src/{ => app}/shared/constants/timerPageText.ts (100%) rename src/{ => app}/shared/constants/weekDays.ts (100%) rename src/{ => app}/shared/hocs/withAuthProtection.tsx (100%) rename src/{ => app}/shared/hooks/useCarousel.ts (100%) rename src/{ => app}/shared/hooks/useClickOutside.ts (100%) rename src/{ => app}/shared/layout/Layout.tsx (100%) rename src/{ => app}/shared/layout/Sidebar/ModalContentsSetting/AccountContent/AccountContent.tsx (100%) rename src/{ => app}/shared/layout/Sidebar/ModalContentsSetting/ModalContentsSetting.tsx (100%) rename src/{ => app}/shared/layout/Sidebar/ModalContentsSetting/Tabs/Tabs.tsx (100%) rename src/{ => app}/shared/layout/Sidebar/ModalContentsSetting/WorkspaceSettingContent/WorkspaceSettingContent.tsx (100%) rename src/{ => app}/shared/layout/Sidebar/Sidebar.tsx (100%) rename src/{ => app}/shared/mocks/categoryData.ts (100%) rename src/{ => app}/shared/mocks/faviconData.ts (100%) rename src/{ => app}/shared/mocks/homeData.ts (100%) rename src/{ => app}/shared/mocks/urlData.ts (100%) rename src/{ => app}/shared/mocks/userData.ts (100%) rename src/{ => app}/shared/types/SSEEvent.ts (100%) rename src/{ => app}/shared/types/allowedService.ts (100%) rename src/{ => app}/shared/types/allowedSites.ts (100%) rename src/{ => app}/shared/types/api/allowedService.ts (100%) rename src/{ => app}/shared/types/api/auth.ts (100%) rename src/{ => app}/shared/types/api/common.ts (100%) rename src/{ => app}/shared/types/api/error.ts (100%) rename src/{ => app}/shared/types/api/friends.ts (100%) rename src/{ => app}/shared/types/api/home.ts (100%) rename src/{ => app}/shared/types/api/onboarding.ts (100%) rename src/{ => app}/shared/types/api/setting.ts (100%) rename src/{ => app}/shared/types/api/timer.ts (100%) rename src/{ => app}/shared/types/common/index.tsx (100%) rename src/{ => app}/shared/types/fileds.ts (100%) rename src/{ => app}/shared/types/friend.ts (100%) rename src/{ => app}/shared/types/global.ts (100%) rename src/{ => app}/shared/types/home/index.tsx (100%) rename src/{ => app}/shared/types/profile.ts (100%) rename src/{ => app}/shared/types/tasks.ts (100%) rename src/{ => app}/shared/types/todoData.ts (100%) rename src/{ => app}/shared/types/userData.ts (100%) rename src/{ => app}/shared/utils/auth.ts (100%) rename src/{ => app}/shared/utils/calendar/index.ts (100%) rename src/{ => app}/shared/utils/date/index.ts (100%) rename src/{ => app}/shared/utils/error.ts (100%) rename src/{ => app}/shared/utils/path.ts (100%) rename src/{ => app}/shared/utils/tasks.ts (100%) rename src/{ => app}/shared/utils/time/index.ts (100%) rename src/{ => app}/shared/utils/timer/index.ts (100%) rename src/{ => app}/shared/utils/url.ts (100%) rename src/{ => app}/shared/utils/url/index.ts (100%) rename src/{ => app}/shared/utils/validation.ts (100%) rename src/{ => app}/vite-env.d.ts (100%) create mode 100644 src/electron/main.ts create mode 100644 src/electron/pathResolver.ts create mode 100644 src/electron/preload.cts create mode 100644 src/electron/tsconfig.json create mode 100644 src/electron/util.ts diff --git a/.gitignore b/.gitignore index c778df84..7245e2d4 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,8 @@ lerna-debug.log* node_modules dist dist-ssr +dist-electron +dist-react release *.local diff --git a/electron-builder.json b/electron-builder.json new file mode 100644 index 00000000..38612563 --- /dev/null +++ b/electron-builder.json @@ -0,0 +1,22 @@ +{ + "appId": "com.morib.client", + "productName": "Morib Client", + "icon": "public/logo_icon.png", + "files": [ + "dist-react", + "dist-electron" + ], + "extraResources": [ + "dist-electron/preload.cjs" + ], + "mac": { + "target":"dmg" + }, + "linux": { + "target": "AppImage", + "category": "Utility" + }, + "win": { + "target": ["portable","msi"] + } +} \ No newline at end of file diff --git a/index.html b/index.html index 729d447e..7460f899 100644 --- a/index.html +++ b/index.html @@ -34,6 +34,6 @@
- + diff --git a/package.json b/package.json index e44f92e5..ad533840 100644 --- a/package.json +++ b/package.json @@ -3,11 +3,18 @@ "private": true, "version": "0.0.0", "type": "module", + "main": "dist-electron/main.js", "scripts": { - "dev": "vite", + "dev": "pnpm dev:react & pnpm dev:electron", + "dev:react": "vite", + "dev:electron": "pnpm transpile:electron && cross-env NODE_ENV=development electron .", "build": "tsc -b && vite build", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", - "preview": "vite preview" + "preview": "vite preview", + "transpile:electron": "tsc --project src/electron/tsconfig.json", + "dist:mac": "pnpm transpile:electron && pnpm build && electron-builder --mac --arm64", + "dist:win": "pnpm transpile:electron && pnpm build && electron-builder --win --x64", + "dist:linux": "pnpm transpile:electron && pnpm build && electron-builder --linux --x64" }, "dependencies": { "@remix-run/router": "^1.17.0", @@ -39,6 +46,9 @@ "@typescript-eslint/parser": "^7.14.1", "@vitejs/plugin-react-swc": "^3.7.0", "autoprefixer": "^10.4.19", + "cross-env": "^7.0.3", + "electron": "^35.1.5", + "electron-builder": "^26.0.12", "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-jsx-a11y": "^6.9.0", @@ -59,5 +69,6 @@ "workerDirectory": [ "public" ] - } + }, + "packageManager": "pnpm@9.15.4+sha512.b2dc20e2fc72b3e18848459b37359a32064663e5627a51e4c74b2c29dd8e8e0491483c3abb40789cfd578bf362fb6ba8261b05f0387d76792ed6e23ea3b1b6a0" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b7ad5ced..f6278e4f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -90,6 +90,15 @@ importers: autoprefixer: specifier: ^10.4.19 version: 10.4.19(postcss@8.4.39) + cross-env: + specifier: ^7.0.3 + version: 7.0.3 + electron: + specifier: ^35.1.5 + version: 35.1.5 + electron-builder: + specifier: ^26.0.12 + version: 26.0.12(electron-builder-squirrel-windows@26.0.12) eslint: specifier: ^8.57.0 version: 8.57.0 @@ -138,6 +147,9 @@ importers: packages: + 7zip-bin@5.2.0: + resolution: {integrity: sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==} + '@alloc/quick-lru@5.2.0': resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} @@ -254,6 +266,57 @@ packages: '@bundled-es-modules/tough-cookie@0.1.6': resolution: {integrity: sha512-dvMHbL464C0zI+Yqxbz6kZ5TOEp7GLW+pry/RWndAR8MJQAXZ2rPmIs8tziTZjeIyhSNZgZbCePtfSbdWqStJw==} + '@develar/schema-utils@2.6.5': + resolution: {integrity: sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==} + engines: {node: '>= 8.9.0'} + + '@electron/asar@3.2.18': + resolution: {integrity: sha512-2XyvMe3N3Nrs8cV39IKELRHTYUWFKrmqqSY1U+GMlc0jvqjIVnoxhNd2H4JolWQncbJi1DCvb5TNxZuI2fEjWg==} + engines: {node: '>=10.12.0'} + hasBin: true + + '@electron/asar@3.4.1': + resolution: {integrity: sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA==} + engines: {node: '>=10.12.0'} + hasBin: true + + '@electron/fuses@1.8.0': + resolution: {integrity: sha512-zx0EIq78WlY/lBb1uXlziZmDZI4ubcCXIMJ4uGjXzZW0nS19TjSPeXPAjzzTmKQlJUZm0SbmZhPKP7tuQ1SsEw==} + hasBin: true + + '@electron/get@2.0.3': + resolution: {integrity: sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==} + engines: {node: '>=12'} + + '@electron/node-gyp@https://codeload.github.com/electron/node-gyp/tar.gz/06b29aafb7708acef8b3669835c8a7857ebc92d2': + resolution: {tarball: https://codeload.github.com/electron/node-gyp/tar.gz/06b29aafb7708acef8b3669835c8a7857ebc92d2} + version: 10.2.0-electron.1 + engines: {node: '>=12.13.0'} + hasBin: true + + '@electron/notarize@2.5.0': + resolution: {integrity: sha512-jNT8nwH1f9X5GEITXaQ8IF/KdskvIkOFfB2CvwumsveVidzpSc+mvhhTMdAGSYF3O+Nq49lJ7y+ssODRXu06+A==} + engines: {node: '>= 10.0.0'} + + '@electron/osx-sign@1.3.1': + resolution: {integrity: sha512-BAfviURMHpmb1Yb50YbCxnOY0wfwaLXH5KJ4+80zS0gUkzDX3ec23naTlEqKsN+PwYn+a1cCzM7BJ4Wcd3sGzw==} + engines: {node: '>=12.0.0'} + hasBin: true + + '@electron/rebuild@3.7.0': + resolution: {integrity: sha512-VW++CNSlZwMYP7MyXEbrKjpzEwhB5kDNbzGtiPEjwYysqyTCF+YbNJ210Dj3AjWsGSV4iEEwNkmJN9yGZmVvmw==} + engines: {node: '>=12.13.0'} + hasBin: true + + '@electron/universal@2.0.1': + resolution: {integrity: sha512-fKpv9kg4SPmt+hY7SVBnIYULE9QJl8L3sCfcBsnqbJwwBwAeTLokJ9TRt9y7bK0JAzIW2y78TVVjvnQEms/yyA==} + engines: {node: '>=16.4'} + + '@electron/windows-sign@1.2.1': + resolution: {integrity: sha512-YfASnrhJ+ve6Q43ZiDwmpBgYgi2u0bYjeAVi2tDfN7YWAKO8X9EEOuPGtqbJpPLM6TfAHimghICjWe2eaJ8BAg==} + engines: {node: '>=14.14'} + hasBin: true + '@esbuild/aix-ppc64@0.21.5': resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} engines: {node: '>=12'} @@ -431,6 +494,9 @@ packages: '@floating-ui/utils@0.2.4': resolution: {integrity: sha512-dWO2pw8hhi+WrXq1YJy2yCuWoL20PddgGaqTgVe4cOS9Q6qklXCiA1tJEqX6BEwRNSCP84/afac9hd4MS+zEUA==} + '@gar/promisify@1.1.3': + resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==} + '@humanwhocodes/config-array@0.11.14': resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} engines: {node: '>=10.10.0'} @@ -486,6 +552,14 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@malept/cross-spawn-promise@2.0.0': + resolution: {integrity: sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg==} + engines: {node: '>= 12.13.0'} + + '@malept/flatpak-bundler@0.4.0': + resolution: {integrity: sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==} + engines: {node: '>= 10.0.0'} + '@mswjs/interceptors@0.37.3': resolution: {integrity: sha512-USvgCL/uOGFtVa6SVyRrC8kIAedzRohxIXN5LISlg5C5vLZCn7dgMFVSNhSF9cuBEFrm/O2spDWEZeMnw4ZXYg==} engines: {node: '>=18'} @@ -502,6 +576,15 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@npmcli/fs@2.1.2': + resolution: {integrity: sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + '@npmcli/move-file@2.0.1': + resolution: {integrity: sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + deprecated: This functionality has been moved to @npmcli/fs + '@open-draft/deferred-promise@2.2.0': resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==} @@ -642,6 +725,10 @@ packages: peerDependencies: react: ^16.14.0 || 17.x || 18.x || 19.x + '@sindresorhus/is@4.6.0': + resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} + engines: {node: '>=10'} + '@socket.io/component-emitter@3.1.2': resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} @@ -788,6 +875,10 @@ packages: '@swc/types@0.1.9': resolution: {integrity: sha512-qKnCno++jzcJ4lM4NTfYifm1EFSCeIfKiAHAfkENZAV5Kl9PjJIyd2yeeVv6c/2CckuLyv2NmRC5pv6pm2WQBg==} + '@szmarczak/http-timer@4.0.6': + resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==} + engines: {node: '>=10'} + '@tanstack/eslint-plugin-query@5.49.1': resolution: {integrity: sha512-pTaEvG3HC1y7BOsFJtZmi8dhWwML97zEBq4qLM6BaUSzZ4WlFyhmtG2sBVO0H+lP1WgnLpQclRQ+nFikTqoUfQ==} peerDependencies: @@ -810,6 +901,10 @@ packages: peerDependencies: react: ^18.0.0 + '@tootallnate/once@2.0.0': + resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} + engines: {node: '>= 10'} + '@trivago/prettier-plugin-sort-imports@4.3.0': resolution: {integrity: sha512-r3n0onD3BTOVUNPhR4lhVK4/pABGpbA7bW3eumZnYdKaHkf1qEC+Mag6DPbGNuuh0eG8AaYj+YqmVHSiGslaTQ==} peerDependencies: @@ -819,21 +914,42 @@ packages: '@vue/compiler-sfc': optional: true + '@types/cacheable-request@6.0.3': + resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==} + '@types/cookie@0.6.0': resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + '@types/debug@4.1.12': + resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + '@types/estree@1.0.5': resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} '@types/event-source-polyfill@1.0.5': resolution: {integrity: sha512-iaiDuDI2aIFft7XkcwMzDWLqo7LVDixd2sR6B4wxJut9xcp/Ev9bO4EFg4rm6S9QxATLBj5OPxdeocgmhjwKaw==} + '@types/fs-extra@9.0.13': + resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==} + + '@types/http-cache-semantics@4.0.4': + resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} + + '@types/keyv@3.1.4': + resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} + '@types/lodash@4.17.7': resolution: {integrity: sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA==} + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + '@types/node@22.10.3': resolution: {integrity: sha512-DifAyw4BkrufCILvD3ucnuN8eydUfc/C1GlyrnI+LK6543w5/L3VeVgf05o3B4fqSXP1dKYLOZsKfutpxPzZrw==} + '@types/plist@3.0.5': + resolution: {integrity: sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==} + '@types/prop-types@15.7.12': resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==} @@ -849,12 +965,21 @@ packages: '@types/react@18.3.3': resolution: {integrity: sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==} + '@types/responselike@1.0.3': + resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==} + '@types/statuses@2.0.5': resolution: {integrity: sha512-jmIUGWrAiwu3dZpxntxieC+1n/5c3mjrImkmOSQ2NC5uP6cYO4aAZDdSmRcI5C1oiTmqlZGHC+/NmJrKogbP5A==} '@types/tough-cookie@4.0.5': resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} + '@types/verror@1.10.11': + resolution: {integrity: sha512-RlDm9K7+o5stv0Co8i8ZRGxDbrTxhJtgjqjFyVh/tXQyl/rYtTKlnTvZ88oSTeYREWurwx20Js4kTuKCsFkUtg==} + + '@types/yauzl@2.10.3': + resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} + '@typescript-eslint/eslint-plugin@7.14.1': resolution: {integrity: sha512-aAJd6bIf2vvQRjUG3ZkNXkmBpN+J7Wd0mfQiiVCJMu9Z5GcZZdcc0j8XwN/BM97Fl7e3SkTXODSk4VehUv7CGw==} engines: {node: ^18.18.0 || >=20.0.0} @@ -948,6 +1073,13 @@ packages: peerDependencies: vite: ^4 || ^5 + '@xmldom/xmldom@0.8.10': + resolution: {integrity: sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==} + engines: {node: '>=10.0.0'} + + abbrev@1.1.1: + resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -958,6 +1090,27 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + + agent-base@7.1.3: + resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} + engines: {node: '>= 14'} + + agentkeepalive@4.6.0: + resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} + engines: {node: '>= 8.0.0'} + + aggregate-error@3.1.0: + resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} + engines: {node: '>=8'} + + ajv-keywords@3.5.2: + resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} + peerDependencies: + ajv: ^6.9.1 + ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} @@ -992,6 +1145,16 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} + app-builder-bin@5.0.0-alpha.12: + resolution: {integrity: sha512-j87o0j6LqPL3QRr8yid6c+Tt5gC7xNfYo6uQIQkorAC6MpeayVMZrEDzKmJJ/Hlv7EnOQpaRm53k6ktDYZyB6w==} + + app-builder-lib@26.0.12: + resolution: {integrity: sha512-+/CEPH1fVKf6HowBUs6LcAIoRcjeqgvAeoSE+cl7Y7LndyQ9ViGPYibNk7wmhMHzNgHIuIbw4nWADPO+4mjgWw==} + engines: {node: '>=14.0.0'} + peerDependencies: + dmg-builder: 26.0.12 + electron-builder-squirrel-windows: 26.0.12 + arg@5.0.2: resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} @@ -1036,12 +1199,31 @@ packages: resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} engines: {node: '>= 0.4'} + assert-plus@1.0.0: + resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==} + engines: {node: '>=0.8'} + ast-types-flow@0.0.8: resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} + astral-regex@2.0.0: + resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} + engines: {node: '>=8'} + + async-exit-hook@2.0.1: + resolution: {integrity: sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==} + engines: {node: '>=0.12.0'} + + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + at-least-node@1.0.0: + resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} + engines: {node: '>= 4.0.0'} + autoprefixer@10.4.19: resolution: {integrity: sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==} engines: {node: ^10 || ^12 || >=14} @@ -1069,10 +1251,20 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + + boolean@3.2.0: + resolution: {integrity: sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. + brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} @@ -1088,6 +1280,34 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + buffer-crc32@0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + builder-util-runtime@9.3.1: + resolution: {integrity: sha512-2/egrNDDnRaxVwK3A+cJq6UOlqOdedGA7JPqCeJjN2Zjk1/QB/6QUi3b714ScIGS7HafFXTyzJEOr5b44I3kvQ==} + engines: {node: '>=12.0.0'} + + builder-util@26.0.11: + resolution: {integrity: sha512-xNjXfsldUEe153h1DraD0XvDOpqGR0L5eKFkdReB7eFW5HqysDZFfly4rckda6y9dF39N3pkPlOblcfHKGw+uA==} + + cacache@16.1.3: + resolution: {integrity: sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + cacheable-lookup@5.0.4: + resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==} + engines: {node: '>=10.6.0'} + + cacheable-request@7.0.4: + resolution: {integrity: sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==} + engines: {node: '>=8'} + call-bind@1.0.7: resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} engines: {node: '>= 0.4'} @@ -1119,6 +1339,33 @@ packages: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} + chownr@2.0.0: + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} + engines: {node: '>=10'} + + chromium-pickle-js@0.2.0: + resolution: {integrity: sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw==} + + ci-info@3.9.0: + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + engines: {node: '>=8'} + + clean-stack@2.2.0: + resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} + engines: {node: '>=6'} + + cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + + cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + + cli-truncate@2.1.0: + resolution: {integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==} + engines: {node: '>=8'} + cli-width@4.1.0: resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} engines: {node: '>= 12'} @@ -1127,6 +1374,13 @@ packages: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} + clone-response@1.0.3: + resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==} + + clone@1.0.4: + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} + clsx@2.1.1: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} @@ -1152,9 +1406,24 @@ packages: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} + commander@5.1.0: + resolution: {integrity: sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==} + engines: {node: '>= 6'} + + commander@9.5.0: + resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} + engines: {node: ^12.20.0 || >=14} + + compare-version@0.1.2: + resolution: {integrity: sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==} + engines: {node: '>=0.10.0'} + concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + config-file-ts@0.2.8-rc1: + resolution: {integrity: sha512-GtNECbVI82bT4RiDIzBSVuTKoSHufnU7Ce7/42bkWZJZFLjmDF2WBpVsvRkhKCfKBnTBb3qZrBwPpFBU/Myvhg==} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -1166,6 +1435,9 @@ packages: resolution: {integrity: sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==} deprecated: core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js. + core-util-is@1.0.2: + resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} + cosmiconfig@8.3.6: resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} engines: {node: '>=14'} @@ -1175,10 +1447,25 @@ packages: typescript: optional: true + crc@3.8.0: + resolution: {integrity: sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==} + + cross-dirname@0.1.0: + resolution: {integrity: sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==} + + cross-env@7.0.3: + resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} + engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} + hasBin: true + cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} @@ -1217,6 +1504,10 @@ packages: supports-color: optional: true + decompress-response@6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} + deep-equal@2.2.3: resolution: {integrity: sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==} engines: {node: '>= 0.4'} @@ -1224,6 +1515,13 @@ packages: deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + defaults@1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + + defer-to-connect@2.0.1: + resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} + engines: {node: '>=10'} + define-data-property@1.1.4: resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} engines: {node: '>= 0.4'} @@ -1236,9 +1534,19 @@ packages: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} + detect-libc@2.0.3: + resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} + engines: {node: '>=8'} + + detect-node@2.1.0: + resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} + didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + dir-compare@4.2.0: + resolution: {integrity: sha512-2xMCmOoMrdQIPHdsTawECdNPwlVFB9zGcz3kuhmBO6U3oU+UQjsue0i8ayLKpgBcm+hcXPMVSGUN9d+pvJ6+VQ==} + dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -1246,6 +1554,15 @@ packages: dlv@1.1.3: resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + dmg-builder@26.0.12: + resolution: {integrity: sha512-59CAAjAhTaIMCN8y9kD573vDkxbs1uhDcrFLHSgutYdPcGOU35Rf95725snvzEOy4BFB7+eLJ8djCNPmGwG67w==} + + dmg-license@1.0.11: + resolution: {integrity: sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==} + engines: {node: '>=8'} + os: [darwin] + hasBin: true + doctrine@2.1.0: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} @@ -1257,18 +1574,57 @@ packages: dot-case@3.0.4: resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + dotenv-expand@11.0.7: + resolution: {integrity: sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==} + engines: {node: '>=12'} + + dotenv@16.5.0: + resolution: {integrity: sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==} + engines: {node: '>=12'} + eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + ejs@3.1.10: + resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} + engines: {node: '>=0.10.0'} + hasBin: true + + electron-builder-squirrel-windows@26.0.12: + resolution: {integrity: sha512-kpwXM7c/ayRUbYVErQbsZ0nQZX4aLHQrPEG9C4h9vuJCXylwFH8a7Jgi2VpKIObzCXO7LKHiCw4KdioFLFOgqA==} + + electron-builder@26.0.12: + resolution: {integrity: sha512-cD1kz5g2sgPTMFHjLxfMjUK5JABq3//J4jPswi93tOPFz6btzXYtK5NrDt717NRbukCUDOrrvmYVOWERlqoiXA==} + engines: {node: '>=14.0.0'} + hasBin: true + + electron-publish@26.0.11: + resolution: {integrity: sha512-a8QRH0rAPIWH9WyyS5LbNvW9Ark6qe63/LqDB7vu2JXYpi0Gma5Q60Dh4tmTqhOBQt0xsrzD8qE7C+D7j+B24A==} + electron-to-chromium@1.4.815: resolution: {integrity: sha512-OvpTT2ItpOXJL7IGcYakRjHCt8L5GrrN/wHCQsRB4PQa1X9fe+X9oen245mIId7s14xvArCGSTIq644yPUKKLg==} + electron-winstaller@5.4.0: + resolution: {integrity: sha512-bO3y10YikuUwUuDUQRM4KfwNkKhnpVO7IPdbsrejwN9/AABJzzTQ4GeHwyzNSrVO+tEH3/Np255a3sVZpZDjvg==} + engines: {node: '>=8.0.0'} + + electron@35.1.5: + resolution: {integrity: sha512-LolvbKKQUSCGvEwbEQNt1cxD1t+YYClDNwBIjn4d28KM8FSqUn9zJuf6AbqNA7tVs9OFl/EQpmg/m4lZV1hH8g==} + engines: {node: '>= 12.20.55'} + hasBin: true + emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + encoding@0.1.13: + resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} + + end-of-stream@1.4.4: + resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + engine.io-client@6.5.4: resolution: {integrity: sha512-GeZeeRjpD2qf49cZQ0Wvh/8NJNfeXkXXcoGh+F77oEAgo9gUHwT1fCRxSNU+YEEaysOJTnsFHmM5oAcPy4ntvQ==} @@ -1280,6 +1636,13 @@ packages: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + err-code@2.0.3: + resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} + error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} @@ -1317,6 +1680,9 @@ packages: resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} engines: {node: '>= 0.4'} + es6-error@4.1.1: + resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==} + esbuild@0.21.5: resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} engines: {node: '>=12'} @@ -1416,6 +1782,18 @@ packages: event-source-polyfill@1.0.31: resolution: {integrity: sha512-4IJSItgS/41IxN5UVAVuAyczwZF7ZIEsM1XAoUzIHA6A+xzusEZUutdXz2Nr+MQPLxfTiCvqE79/C8HT8fKFvA==} + exponential-backoff@3.1.2: + resolution: {integrity: sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==} + + extract-zip@2.0.1: + resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} + engines: {node: '>= 10.17.0'} + hasBin: true + + extsprintf@1.4.1: + resolution: {integrity: sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==} + engines: {'0': node >=0.6.0} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -1435,10 +1813,16 @@ packages: fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + fd-slicer@1.1.0: + resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + file-entry-cache@6.0.1: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} + filelist@1.0.4: + resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -1477,6 +1861,30 @@ packages: fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + fs-extra@10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + + fs-extra@11.3.0: + resolution: {integrity: sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==} + engines: {node: '>=14.14'} + + fs-extra@7.0.1: + resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} + engines: {node: '>=6 <7 || >=8'} + + fs-extra@8.1.0: + resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} + engines: {node: '>=6 <7 || >=8'} + + fs-extra@9.1.0: + resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} + engines: {node: '>=10'} + + fs-minipass@2.1.0: + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} + engines: {node: '>= 8'} + fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} @@ -1507,6 +1915,10 @@ packages: resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} engines: {node: '>= 0.4'} + get-stream@5.2.0: + resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} + engines: {node: '>=8'} + get-symbol-description@1.0.2: resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} engines: {node: '>= 0.4'} @@ -1528,6 +1940,15 @@ packages: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Glob versions prior to v9 are no longer supported + glob@8.1.0: + resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} + engines: {node: '>=12'} + deprecated: Glob versions prior to v9 are no longer supported + + global-agent@3.0.0: + resolution: {integrity: sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==} + engines: {node: '>=10.0'} + globals@11.12.0: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} @@ -1547,6 +1968,13 @@ packages: gopd@1.0.1: resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + got@11.8.6: + resolution: {integrity: sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==} + engines: {node: '>=10.19.0'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} @@ -1590,6 +2018,48 @@ packages: hoist-non-react-statics@3.3.2: resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + hosted-git-info@4.1.0: + resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} + engines: {node: '>=10'} + + http-cache-semantics@4.1.1: + resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} + + http-proxy-agent@5.0.0: + resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} + engines: {node: '>= 6'} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + http2-wrapper@1.0.3: + resolution: {integrity: sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==} + engines: {node: '>=10.19.0'} + + https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + humanize-ms@1.2.1: + resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} + + iconv-corefoundation@1.1.7: + resolution: {integrity: sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==} + engines: {node: ^8.11.2 || >=10} + os: [darwin] + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + ignore@5.3.1: resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} engines: {node: '>= 4'} @@ -1602,6 +2072,13 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} + indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + + infer-owner@1.0.4: + resolution: {integrity: sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==} + inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. @@ -1613,6 +2090,10 @@ packages: resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} engines: {node: '>= 0.4'} + ip-address@9.0.5: + resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} + engines: {node: '>= 12'} + is-arguments@1.1.1: resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} engines: {node: '>= 0.4'} @@ -1643,6 +2124,10 @@ packages: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} + is-ci@3.0.1: + resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==} + hasBin: true + is-core-module@2.14.0: resolution: {integrity: sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==} engines: {node: '>= 0.4'} @@ -1674,6 +2159,13 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} + is-interactive@1.0.0: + resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} + engines: {node: '>=8'} + + is-lambda@1.0.1: + resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} + is-map@2.0.3: resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} engines: {node: '>= 0.4'} @@ -1721,6 +2213,10 @@ packages: resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} engines: {node: '>= 0.4'} + is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + is-weakmap@2.0.2: resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} engines: {node: '>= 0.4'} @@ -1735,6 +2231,14 @@ packages: isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + isbinaryfile@4.0.10: + resolution: {integrity: sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==} + engines: {node: '>= 8.0.0'} + + isbinaryfile@5.0.4: + resolution: {integrity: sha512-YKBKVkKhty7s8rxddb40oOkuP0NbaeXrQvLin6QMHL7Ypiy2RW9LwOVrVgZRyOrhQlayMd9t+D8yDy8MKFTSDQ==} + engines: {node: '>= 18.0.0'} + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -1745,6 +2249,11 @@ packages: resolution: {integrity: sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw==} engines: {node: '>=14'} + jake@10.9.2: + resolution: {integrity: sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==} + engines: {node: '>=10'} + hasBin: true + javascript-natural-sort@0.7.1: resolution: {integrity: sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==} @@ -1771,6 +2280,9 @@ packages: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true + jsbn@1.1.0: + resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} + jsesc@2.5.2: resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} engines: {node: '>=4'} @@ -1788,11 +2300,20 @@ packages: json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + json-stringify-safe@5.0.1: + resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} + json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} hasBin: true + jsonfile@4.0.0: + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + + jsonfile@6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + jsx-ast-utils@3.3.5: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} @@ -1807,6 +2328,9 @@ packages: resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} engines: {node: '>=0.10'} + lazy-val@1.0.5: + resolution: {integrity: sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==} + levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} @@ -1832,6 +2356,10 @@ packages: lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -1842,6 +2370,10 @@ packages: lower-case@2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + lowercase-keys@2.0.0: + resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} + engines: {node: '>=8'} + lru-cache@10.3.0: resolution: {integrity: sha512-CQl19J/g+Hbjbv4Y3mFNNXFEL/5t/KCg8POCuUqd4rMKjGG+j1ybER83hxV58zL+dFI1PTkt3GNFSHRt+d8qEQ==} engines: {node: 14 || >=16.14} @@ -1849,6 +2381,22 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + + lru-cache@7.18.3: + resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} + engines: {node: '>=12'} + + make-fetch-happen@10.2.1: + resolution: {integrity: sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + matcher@3.0.0: + resolution: {integrity: sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==} + engines: {node: '>=10'} + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -1865,17 +2413,86 @@ packages: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} + mime@2.6.0: + resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} + engines: {node: '>=4.0.0'} + hasBin: true + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + mimic-response@1.0.1: + resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} + engines: {node: '>=4'} + + mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + + minimatch@10.0.1: + resolution: {integrity: sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==} + engines: {node: 20 || >=22} + minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + minimatch@9.0.5: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass-collect@1.0.2: + resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==} + engines: {node: '>= 8'} + + minipass-fetch@2.1.2: + resolution: {integrity: sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + minipass-flush@1.0.5: + resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==} + engines: {node: '>= 8'} + + minipass-pipeline@1.2.4: + resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} + engines: {node: '>=8'} + + minipass-sized@1.0.3: + resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==} + engines: {node: '>=8'} + + minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + + minipass@5.0.0: + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} + minipass@7.1.2: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} + minizlib@2.1.2: + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} + + mkdirp@0.5.6: + resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} + hasBin: true + + mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} @@ -1904,12 +2521,31 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + negotiator@0.6.4: + resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==} + engines: {node: '>= 0.6'} + no-case@3.0.4: resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + node-abi@3.74.0: + resolution: {integrity: sha512-c5XK0MjkGBrQPGYG24GBADZud0NCbznxNx0ZkS+ebUTrmV1qTDxPxSL8zEAPURXSbLRWVexxmP4986BziahL5w==} + engines: {node: '>=10'} + + node-addon-api@1.7.2: + resolution: {integrity: sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==} + + node-api-version@0.2.1: + resolution: {integrity: sha512-2xP/IGGMmmSQpI1+O/k72jF/ykvZ89JeuKX3TLJAYPDVLUalrshrLHkeVcCCZqG/eEa635cr8IBYzgnDvM2O8Q==} + node-releases@2.0.14: resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} + nopt@6.0.0: + resolution: {integrity: sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + hasBin: true + normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -1918,6 +2554,10 @@ packages: resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} engines: {node: '>=0.10.0'} + normalize-url@6.1.0: + resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} + engines: {node: '>=10'} + object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -1961,13 +2601,25 @@ packages: once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} + ora@5.4.1: + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} + engines: {node: '>=10'} + outvariant@1.4.3: resolution: {integrity: sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==} + p-cancelable@2.1.1: + resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==} + engines: {node: '>=8'} + p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} @@ -1976,6 +2628,10 @@ packages: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} + p-map@4.0.0: + resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} + engines: {node: '>=10'} + package-json-from-dist@1.0.0: resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} @@ -2013,6 +2669,13 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} + pe-library@0.4.1: + resolution: {integrity: sha512-eRWB5LBz7PpDu4PUlwT0PhnQfTQJlDDdPa35urV4Osrm0t0AqQFGn+UIkU3klZvwJ8KPO3VbBFsXquA6p6kqZw==} + engines: {node: '>=12', npm: '>=6'} + + pend@1.2.0: + resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + picocolors@1.0.1: resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} @@ -2031,6 +2694,10 @@ packages: resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} engines: {node: '>= 6'} + plist@3.1.0: + resolution: {integrity: sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==} + engines: {node: '>=10.4.0'} + possible-typed-array-names@1.0.0: resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} engines: {node: '>= 0.4'} @@ -2076,6 +2743,11 @@ packages: resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} engines: {node: ^10 || ^12 || >=14} + postject@1.0.0-alpha.6: + resolution: {integrity: sha512-b9Eb8h2eVqNE8edvKdwqkrY6O7kAwmI8kcnBv1NScolYJbo59XUF0noFq+lxbC1yN20bmC0WBEbDC5H/7ASb0A==} + engines: {node: '>=14.0.0'} + hasBin: true + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -2141,6 +2813,26 @@ packages: engines: {node: '>=14'} hasBin: true + proc-log@2.0.1: + resolution: {integrity: sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + progress@2.0.3: + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} + + promise-inflight@1.0.1: + resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==} + peerDependencies: + bluebird: '*' + peerDependenciesMeta: + bluebird: + optional: true + + promise-retry@2.0.1: + resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} + engines: {node: '>=10'} + prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} @@ -2150,6 +2842,9 @@ packages: psl@1.15.0: resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==} + pump@3.0.2: + resolution: {integrity: sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -2160,6 +2855,10 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + quick-lru@5.1.1: + resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} + engines: {node: '>=10'} + react-datepicker@7.3.0: resolution: {integrity: sha512-EqRKLAtLZUTztiq6a+tjSjQX9ES0Xd229JPckAtyZZ4GoY3rtvNWAzkYZnQUf6zTWT50Ki0+t+W9VRQIkSJLfg==} peerDependencies: @@ -2203,9 +2902,17 @@ packages: resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} + read-binary-file-arch@1.0.6: + resolution: {integrity: sha512-BNg9EN3DD3GsDXX7Aa8O4p92sryjkmzYYgmgTAc6CA4uGLEDzFfxOxugu21akOxpcXHiEgsYkC6nPsQvLLLmEg==} + hasBin: true + read-cache@1.0.0: resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} @@ -2228,6 +2935,13 @@ packages: requires-port@1.0.0: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + resedit@1.7.2: + resolution: {integrity: sha512-vHjcY2MlAITJhC0eRD/Vv8Vlgmu9Sd3LX9zZvtGzU5ZImdTN3+d6e/4mnTyV8vEbyf1sgNIrWxhWlrys52OkEA==} + engines: {node: '>=12', npm: '>=6'} + + resolve-alpn@1.2.1: + resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -2240,15 +2954,35 @@ packages: resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} hasBin: true + responselike@2.0.1: + resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==} + + restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + + retry@0.12.0: + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} + reusify@1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + rimraf@2.6.3: + resolution: {integrity: sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true + roarr@2.15.4: + resolution: {integrity: sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==} + engines: {node: '>=8.0'} + rollup@4.18.0: resolution: {integrity: sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -2261,13 +2995,32 @@ packages: resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} engines: {node: '>=0.4'} + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + safe-regex-test@1.0.3: resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} engines: {node: '>= 0.4'} + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + sanitize-filename@1.6.3: + resolution: {integrity: sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==} + + sax@1.4.1: + resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} + scheduler@0.23.2: resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + semver-compare@1.0.0: + resolution: {integrity: sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==} + + semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true + semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -2277,6 +3030,10 @@ packages: engines: {node: '>=10'} hasBin: true + serialize-error@7.0.1: + resolution: {integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==} + engines: {node: '>=10'} + set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} @@ -2297,14 +3054,29 @@ packages: resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} engines: {node: '>= 0.4'} + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + simple-update-notifier@2.0.0: + resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==} + engines: {node: '>=10'} + slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} + slice-ansi@3.0.0: + resolution: {integrity: sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==} + engines: {node: '>=8'} + + smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + snake-case@3.0.4: resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} @@ -2316,14 +3088,40 @@ packages: resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==} engines: {node: '>=10.0.0'} + socks-proxy-agent@7.0.0: + resolution: {integrity: sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==} + engines: {node: '>= 10'} + + socks@2.8.4: + resolution: {integrity: sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + source-map-js@1.2.0: resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} engines: {node: '>=0.10.0'} + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + source-map@0.5.7: resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} engines: {node: '>=0.10.0'} + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + sprintf-js@1.1.3: + resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} + + ssri@9.0.1: + resolution: {integrity: sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + stat-mode@1.0.0: + resolution: {integrity: sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==} + engines: {node: '>= 6'} + statuses@2.0.1: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} @@ -2361,6 +3159,9 @@ packages: resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} engines: {node: '>= 0.4'} + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -2378,6 +3179,10 @@ packages: engines: {node: '>=16 || 14 >=14.17'} hasBin: true + sumchecker@3.0.1: + resolution: {integrity: sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==} + engines: {node: '>= 8.0'} + supports-color@5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} engines: {node: '>=4'} @@ -2405,6 +3210,17 @@ packages: engines: {node: '>=14.0.0'} hasBin: true + tar@6.2.1: + resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} + engines: {node: '>=10'} + + temp-file@3.4.0: + resolution: {integrity: sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==} + + temp@0.9.4: + resolution: {integrity: sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==} + engines: {node: '>=6.0.0'} + text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} @@ -2418,6 +3234,16 @@ packages: timezone@1.0.23: resolution: {integrity: sha512-yhQgk6qmSLB+TF8HGmApZAVI5bfzR1CoKUGr+WMZWmx75ED1uDewAZA8QMGCQ70TEv4GmM8pDB9jrHuxdaQ1PA==} + tiny-async-pool@1.3.0: + resolution: {integrity: sha512-01EAw5EDrcVrdgyCLgoSPvqznC0sVxDSVeiOz09FUpjh71G79VCqneOr+xvt7T1r76CF6ZZfPjHorN2+d+3mqA==} + + tmp-promise@3.0.3: + resolution: {integrity: sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==} + + tmp@0.2.3: + resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==} + engines: {node: '>=14.14'} + to-fast-properties@2.0.0: resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} engines: {node: '>=4'} @@ -2430,6 +3256,9 @@ packages: resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} engines: {node: '>=6'} + truncate-utf8-bytes@1.0.2: + resolution: {integrity: sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==} + ts-api-utils@1.3.0: resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} engines: {node: '>=16'} @@ -2446,6 +3275,10 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} + type-fest@0.13.1: + resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==} + engines: {node: '>=10'} + type-fest@0.20.2: resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} engines: {node: '>=10'} @@ -2485,10 +3318,26 @@ packages: undici-types@6.20.0: resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} + unique-filename@2.0.1: + resolution: {integrity: sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + unique-slug@3.0.0: + resolution: {integrity: sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + universalify@0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} + universalify@0.2.0: resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} engines: {node: '>= 4.0.0'} + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + update-browserslist-db@1.0.16: resolution: {integrity: sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==} hasBin: true @@ -2501,9 +3350,16 @@ packages: url-parse@1.5.10: resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + utf8-byte-length@1.0.5: + resolution: {integrity: sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==} + util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + verror@1.10.1: + resolution: {integrity: sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==} + engines: {node: '>=0.6.0'} + vite-plugin-svgr@4.2.0: resolution: {integrity: sha512-SC7+FfVtNQk7So0XMjrrtLAbEC8qjFPifyD7+fs/E6aaNdVde6umlVVh0QuwDLdOMu7vp5RiGFsB70nj5yo0XA==} peerDependencies: @@ -2537,6 +3393,9 @@ packages: terser: optional: true + wcwidth@1.0.1: + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + which-boxed-primitive@1.0.2: resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} @@ -2588,6 +3447,10 @@ packages: utf-8-validate: optional: true + xmlbuilder@15.1.1: + resolution: {integrity: sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==} + engines: {node: '>=8.0'} + xmlhttprequest-ssl@2.0.0: resolution: {integrity: sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==} engines: {node: '>=0.4.0'} @@ -2599,6 +3462,9 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yaml@2.4.5: resolution: {integrity: sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==} engines: {node: '>= 14'} @@ -2612,6 +3478,9 @@ packages: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} + yauzl@2.10.0: + resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -2622,6 +3491,8 @@ packages: snapshots: + 7zip-bin@5.2.0: {} + '@alloc/quick-lru@5.2.0': {} '@ampproject/remapping@2.3.0': @@ -2801,6 +3672,121 @@ snapshots: '@types/tough-cookie': 4.0.5 tough-cookie: 4.1.4 + '@develar/schema-utils@2.6.5': + dependencies: + ajv: 6.12.6 + ajv-keywords: 3.5.2(ajv@6.12.6) + + '@electron/asar@3.2.18': + dependencies: + commander: 5.1.0 + glob: 7.2.3 + minimatch: 3.1.2 + + '@electron/asar@3.4.1': + dependencies: + commander: 5.1.0 + glob: 7.2.3 + minimatch: 3.1.2 + + '@electron/fuses@1.8.0': + dependencies: + chalk: 4.1.2 + fs-extra: 9.1.0 + minimist: 1.2.8 + + '@electron/get@2.0.3': + dependencies: + debug: 4.3.5 + env-paths: 2.2.1 + fs-extra: 8.1.0 + got: 11.8.6 + progress: 2.0.3 + semver: 6.3.1 + sumchecker: 3.0.1 + optionalDependencies: + global-agent: 3.0.0 + transitivePeerDependencies: + - supports-color + + '@electron/node-gyp@https://codeload.github.com/electron/node-gyp/tar.gz/06b29aafb7708acef8b3669835c8a7857ebc92d2': + dependencies: + env-paths: 2.2.1 + exponential-backoff: 3.1.2 + glob: 8.1.0 + graceful-fs: 4.2.11 + make-fetch-happen: 10.2.1 + nopt: 6.0.0 + proc-log: 2.0.1 + semver: 7.6.2 + tar: 6.2.1 + which: 2.0.2 + transitivePeerDependencies: + - bluebird + - supports-color + + '@electron/notarize@2.5.0': + dependencies: + debug: 4.3.5 + fs-extra: 9.1.0 + promise-retry: 2.0.1 + transitivePeerDependencies: + - supports-color + + '@electron/osx-sign@1.3.1': + dependencies: + compare-version: 0.1.2 + debug: 4.3.5 + fs-extra: 10.1.0 + isbinaryfile: 4.0.10 + minimist: 1.2.8 + plist: 3.1.0 + transitivePeerDependencies: + - supports-color + + '@electron/rebuild@3.7.0': + dependencies: + '@electron/node-gyp': https://codeload.github.com/electron/node-gyp/tar.gz/06b29aafb7708acef8b3669835c8a7857ebc92d2 + '@malept/cross-spawn-promise': 2.0.0 + chalk: 4.1.2 + debug: 4.3.5 + detect-libc: 2.0.3 + fs-extra: 10.1.0 + got: 11.8.6 + node-abi: 3.74.0 + node-api-version: 0.2.1 + ora: 5.4.1 + read-binary-file-arch: 1.0.6 + semver: 7.6.2 + tar: 6.2.1 + yargs: 17.7.2 + transitivePeerDependencies: + - bluebird + - supports-color + + '@electron/universal@2.0.1': + dependencies: + '@electron/asar': 3.2.18 + '@malept/cross-spawn-promise': 2.0.0 + debug: 4.3.5 + dir-compare: 4.2.0 + fs-extra: 11.3.0 + minimatch: 9.0.5 + plist: 3.1.0 + transitivePeerDependencies: + - supports-color + + '@electron/windows-sign@1.2.1': + dependencies: + cross-dirname: 0.1.0 + debug: 4.3.5 + fs-extra: 11.3.0 + minimist: 1.2.8 + postject: 1.0.0-alpha.6 + transitivePeerDependencies: + - supports-color + optional: true + '@esbuild/aix-ppc64@0.21.5': optional: true @@ -2918,6 +3904,8 @@ snapshots: '@floating-ui/utils@0.2.4': {} + '@gar/promisify@1.1.3': {} + '@humanwhocodes/config-array@0.11.14': dependencies: '@humanwhocodes/object-schema': 2.0.3 @@ -2982,6 +3970,19 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.4.15 + '@malept/cross-spawn-promise@2.0.0': + dependencies: + cross-spawn: 7.0.6 + + '@malept/flatpak-bundler@0.4.0': + dependencies: + debug: 4.3.5 + fs-extra: 9.1.0 + lodash: 4.17.21 + tmp-promise: 3.0.3 + transitivePeerDependencies: + - supports-color + '@mswjs/interceptors@0.37.3': dependencies: '@open-draft/deferred-promise': 2.2.0 @@ -3003,6 +4004,16 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.17.1 + '@npmcli/fs@2.1.2': + dependencies: + '@gar/promisify': 1.1.3 + semver: 7.6.2 + + '@npmcli/move-file@2.0.1': + dependencies: + mkdirp: 1.0.4 + rimraf: 3.0.2 + '@open-draft/deferred-promise@2.2.0': {} '@open-draft/logger@0.3.0': @@ -3110,6 +4121,8 @@ snapshots: hoist-non-react-statics: 3.3.2 react: 18.3.1 + '@sindresorhus/is@4.6.0': {} + '@socket.io/component-emitter@3.1.2': {} '@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.24.7)': @@ -3234,6 +4247,10 @@ snapshots: dependencies: '@swc/counter': 0.1.3 + '@szmarczak/http-timer@4.0.6': + dependencies: + defer-to-connect: 2.0.1 + '@tanstack/eslint-plugin-query@5.49.1(eslint@8.57.0)(typescript@5.5.2)': dependencies: '@typescript-eslint/utils': 8.0.0-alpha.30(eslint@8.57.0)(typescript@5.5.2) @@ -3257,6 +4274,8 @@ snapshots: '@tanstack/query-core': 5.49.1 react: 18.3.1 + '@tootallnate/once@2.0.0': {} + '@trivago/prettier-plugin-sort-imports@4.3.0(prettier@3.3.2)': dependencies: '@babel/generator': 7.17.7 @@ -3269,18 +4288,47 @@ snapshots: transitivePeerDependencies: - supports-color + '@types/cacheable-request@6.0.3': + dependencies: + '@types/http-cache-semantics': 4.0.4 + '@types/keyv': 3.1.4 + '@types/node': 22.10.3 + '@types/responselike': 1.0.3 + '@types/cookie@0.6.0': {} + '@types/debug@4.1.12': + dependencies: + '@types/ms': 2.1.0 + '@types/estree@1.0.5': {} '@types/event-source-polyfill@1.0.5': {} + '@types/fs-extra@9.0.13': + dependencies: + '@types/node': 22.10.3 + + '@types/http-cache-semantics@4.0.4': {} + + '@types/keyv@3.1.4': + dependencies: + '@types/node': 22.10.3 + '@types/lodash@4.17.7': {} + '@types/ms@2.1.0': {} + '@types/node@22.10.3': dependencies: undici-types: 6.20.0 + '@types/plist@3.0.5': + dependencies: + '@types/node': 22.10.3 + xmlbuilder: 15.1.1 + optional: true + '@types/prop-types@15.7.12': {} '@types/react-datepicker@6.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': @@ -3305,10 +4353,22 @@ snapshots: '@types/prop-types': 15.7.12 csstype: 3.1.3 + '@types/responselike@1.0.3': + dependencies: + '@types/node': 22.10.3 + '@types/statuses@2.0.5': {} '@types/tough-cookie@4.0.5': {} + '@types/verror@1.10.11': + optional: true + + '@types/yauzl@2.10.3': + dependencies: + '@types/node': 22.10.3 + optional: true + '@typescript-eslint/eslint-plugin@7.14.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2)': dependencies: '@eslint-community/regexpp': 4.11.0 @@ -3437,12 +4497,37 @@ snapshots: transitivePeerDependencies: - '@swc/helpers' + '@xmldom/xmldom@0.8.10': {} + + abbrev@1.1.1: {} + acorn-jsx@5.3.2(acorn@8.12.0): dependencies: acorn: 8.12.0 acorn@8.12.0: {} + agent-base@6.0.2: + dependencies: + debug: 4.3.5 + transitivePeerDependencies: + - supports-color + + agent-base@7.1.3: {} + + agentkeepalive@4.6.0: + dependencies: + humanize-ms: 1.2.1 + + aggregate-error@3.1.0: + dependencies: + clean-stack: 2.2.0 + indent-string: 4.0.0 + + ajv-keywords@3.5.2(ajv@6.12.6): + dependencies: + ajv: 6.12.6 + ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 @@ -3475,6 +4560,49 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.1 + app-builder-bin@5.0.0-alpha.12: {} + + app-builder-lib@26.0.12(dmg-builder@26.0.12)(electron-builder-squirrel-windows@26.0.12): + dependencies: + '@develar/schema-utils': 2.6.5 + '@electron/asar': 3.2.18 + '@electron/fuses': 1.8.0 + '@electron/notarize': 2.5.0 + '@electron/osx-sign': 1.3.1 + '@electron/rebuild': 3.7.0 + '@electron/universal': 2.0.1 + '@malept/flatpak-bundler': 0.4.0 + '@types/fs-extra': 9.0.13 + async-exit-hook: 2.0.1 + builder-util: 26.0.11 + builder-util-runtime: 9.3.1 + chromium-pickle-js: 0.2.0 + config-file-ts: 0.2.8-rc1 + debug: 4.3.5 + dmg-builder: 26.0.12(electron-builder-squirrel-windows@26.0.12) + dotenv: 16.5.0 + dotenv-expand: 11.0.7 + ejs: 3.1.10 + electron-builder-squirrel-windows: 26.0.12(dmg-builder@26.0.12) + electron-publish: 26.0.11 + fs-extra: 10.1.0 + hosted-git-info: 4.1.0 + is-ci: 3.0.1 + isbinaryfile: 5.0.4 + js-yaml: 4.1.0 + json5: 2.2.3 + lazy-val: 1.0.5 + minimatch: 10.0.1 + plist: 3.1.0 + resedit: 1.7.2 + semver: 7.6.2 + tar: 6.2.1 + temp-file: 3.4.0 + tiny-async-pool: 1.3.0 + transitivePeerDependencies: + - bluebird + - supports-color + arg@5.0.2: {} argparse@2.0.1: {} @@ -3548,10 +4676,22 @@ snapshots: is-array-buffer: 3.0.4 is-shared-array-buffer: 1.0.3 + assert-plus@1.0.0: + optional: true + ast-types-flow@0.0.8: {} + astral-regex@2.0.0: + optional: true + + async-exit-hook@2.0.1: {} + + async@3.2.6: {} + asynckit@0.4.0: {} + at-least-node@1.0.0: {} + autoprefixer@10.4.19(postcss@8.4.39): dependencies: browserslist: 4.23.1 @@ -3587,8 +4727,19 @@ snapshots: balanced-match@1.0.2: {} + base64-js@1.5.1: {} + binary-extensions@2.3.0: {} + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + + boolean@3.2.0: + optional: true + brace-expansion@1.1.11: dependencies: balanced-match: 1.0.2 @@ -3609,6 +4760,79 @@ snapshots: node-releases: 2.0.14 update-browserslist-db: 1.0.16(browserslist@4.23.1) + buffer-crc32@0.2.13: {} + + buffer-from@1.1.2: {} + + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + builder-util-runtime@9.3.1: + dependencies: + debug: 4.3.5 + sax: 1.4.1 + transitivePeerDependencies: + - supports-color + + builder-util@26.0.11: + dependencies: + 7zip-bin: 5.2.0 + '@types/debug': 4.1.12 + app-builder-bin: 5.0.0-alpha.12 + builder-util-runtime: 9.3.1 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.3.5 + fs-extra: 10.1.0 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + is-ci: 3.0.1 + js-yaml: 4.1.0 + sanitize-filename: 1.6.3 + source-map-support: 0.5.21 + stat-mode: 1.0.0 + temp-file: 3.4.0 + tiny-async-pool: 1.3.0 + transitivePeerDependencies: + - supports-color + + cacache@16.1.3: + dependencies: + '@npmcli/fs': 2.1.2 + '@npmcli/move-file': 2.0.1 + chownr: 2.0.0 + fs-minipass: 2.1.0 + glob: 8.1.0 + infer-owner: 1.0.4 + lru-cache: 7.18.3 + minipass: 3.3.6 + minipass-collect: 1.0.2 + minipass-flush: 1.0.5 + minipass-pipeline: 1.2.4 + mkdirp: 1.0.4 + p-map: 4.0.0 + promise-inflight: 1.0.1 + rimraf: 3.0.2 + ssri: 9.0.1 + tar: 6.2.1 + unique-filename: 2.0.1 + transitivePeerDependencies: + - bluebird + + cacheable-lookup@5.0.4: {} + + cacheable-request@7.0.4: + dependencies: + clone-response: 1.0.3 + get-stream: 5.2.0 + http-cache-semantics: 4.1.1 + keyv: 4.5.4 + lowercase-keys: 2.0.0 + normalize-url: 6.1.0 + responselike: 2.0.1 + call-bind@1.0.7: dependencies: es-define-property: 1.0.0 @@ -3648,14 +4872,40 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - cli-width@4.1.0: {} + chownr@2.0.0: {} - cliui@8.0.1: - dependencies: - string-width: 4.2.3 + chromium-pickle-js@0.2.0: {} + + ci-info@3.9.0: {} + + clean-stack@2.2.0: {} + + cli-cursor@3.1.0: + dependencies: + restore-cursor: 3.1.0 + + cli-spinners@2.9.2: {} + + cli-truncate@2.1.0: + dependencies: + slice-ansi: 3.0.0 + string-width: 4.2.3 + optional: true + + cli-width@4.1.0: {} + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 + clone-response@1.0.3: + dependencies: + mimic-response: 1.0.1 + + clone@1.0.4: {} + clsx@2.1.1: {} color-convert@1.9.3: @@ -3676,14 +4926,29 @@ snapshots: commander@4.1.1: {} + commander@5.1.0: {} + + commander@9.5.0: + optional: true + + compare-version@0.1.2: {} + concat-map@0.0.1: {} + config-file-ts@0.2.8-rc1: + dependencies: + glob: 10.4.2 + typescript: 5.5.2 + convert-source-map@2.0.0: {} cookie@0.7.2: {} core-js@2.6.12: {} + core-util-is@1.0.2: + optional: true + cosmiconfig@8.3.6(typescript@5.5.2): dependencies: import-fresh: 3.3.0 @@ -3693,12 +4958,30 @@ snapshots: optionalDependencies: typescript: 5.5.2 + crc@3.8.0: + dependencies: + buffer: 5.7.1 + optional: true + + cross-dirname@0.1.0: + optional: true + + cross-env@7.0.3: + dependencies: + cross-spawn: 7.0.6 + cross-spawn@7.0.3: dependencies: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + cssesc@3.0.0: {} csstype@3.1.3: {} @@ -3731,6 +5014,10 @@ snapshots: dependencies: ms: 2.1.2 + decompress-response@6.0.0: + dependencies: + mimic-response: 3.1.0 + deep-equal@2.2.3: dependencies: array-buffer-byte-length: 1.0.1 @@ -3754,6 +5041,12 @@ snapshots: deep-is@0.1.4: {} + defaults@1.0.4: + dependencies: + clone: 1.0.4 + + defer-to-connect@2.0.1: {} + define-data-property@1.1.4: dependencies: es-define-property: 1.0.0 @@ -3768,14 +5061,51 @@ snapshots: delayed-stream@1.0.0: {} + detect-libc@2.0.3: {} + + detect-node@2.1.0: + optional: true + didyoumean@1.2.2: {} + dir-compare@4.2.0: + dependencies: + minimatch: 3.1.2 + p-limit: 3.1.0 + dir-glob@3.0.1: dependencies: path-type: 4.0.0 dlv@1.1.3: {} + dmg-builder@26.0.12(electron-builder-squirrel-windows@26.0.12): + dependencies: + app-builder-lib: 26.0.12(dmg-builder@26.0.12)(electron-builder-squirrel-windows@26.0.12) + builder-util: 26.0.11 + builder-util-runtime: 9.3.1 + fs-extra: 10.1.0 + iconv-lite: 0.6.3 + js-yaml: 4.1.0 + optionalDependencies: + dmg-license: 1.0.11 + transitivePeerDependencies: + - bluebird + - electron-builder-squirrel-windows + - supports-color + + dmg-license@1.0.11: + dependencies: + '@types/plist': 3.0.5 + '@types/verror': 1.10.11 + ajv: 6.12.6 + crc: 3.8.0 + iconv-corefoundation: 1.1.7 + plist: 3.1.0 + smart-buffer: 4.2.0 + verror: 1.10.1 + optional: true + doctrine@2.1.0: dependencies: esutils: 2.0.3 @@ -3789,14 +5119,93 @@ snapshots: no-case: 3.0.4 tslib: 2.6.3 + dotenv-expand@11.0.7: + dependencies: + dotenv: 16.5.0 + + dotenv@16.5.0: {} + eastasianwidth@0.2.0: {} + ejs@3.1.10: + dependencies: + jake: 10.9.2 + + electron-builder-squirrel-windows@26.0.12(dmg-builder@26.0.12): + dependencies: + app-builder-lib: 26.0.12(dmg-builder@26.0.12)(electron-builder-squirrel-windows@26.0.12) + builder-util: 26.0.11 + electron-winstaller: 5.4.0 + transitivePeerDependencies: + - bluebird + - dmg-builder + - supports-color + + electron-builder@26.0.12(electron-builder-squirrel-windows@26.0.12): + dependencies: + app-builder-lib: 26.0.12(dmg-builder@26.0.12)(electron-builder-squirrel-windows@26.0.12) + builder-util: 26.0.11 + builder-util-runtime: 9.3.1 + chalk: 4.1.2 + dmg-builder: 26.0.12(electron-builder-squirrel-windows@26.0.12) + fs-extra: 10.1.0 + is-ci: 3.0.1 + lazy-val: 1.0.5 + simple-update-notifier: 2.0.0 + yargs: 17.7.2 + transitivePeerDependencies: + - bluebird + - electron-builder-squirrel-windows + - supports-color + + electron-publish@26.0.11: + dependencies: + '@types/fs-extra': 9.0.13 + builder-util: 26.0.11 + builder-util-runtime: 9.3.1 + chalk: 4.1.2 + form-data: 4.0.0 + fs-extra: 10.1.0 + lazy-val: 1.0.5 + mime: 2.6.0 + transitivePeerDependencies: + - supports-color + electron-to-chromium@1.4.815: {} + electron-winstaller@5.4.0: + dependencies: + '@electron/asar': 3.4.1 + debug: 4.3.5 + fs-extra: 7.0.1 + lodash: 4.17.21 + temp: 0.9.4 + optionalDependencies: + '@electron/windows-sign': 1.2.1 + transitivePeerDependencies: + - supports-color + + electron@35.1.5: + dependencies: + '@electron/get': 2.0.3 + '@types/node': 22.10.3 + extract-zip: 2.0.1 + transitivePeerDependencies: + - supports-color + emoji-regex@8.0.0: {} emoji-regex@9.2.2: {} + encoding@0.1.13: + dependencies: + iconv-lite: 0.6.3 + optional: true + + end-of-stream@1.4.4: + dependencies: + once: 1.4.0 + engine.io-client@6.5.4: dependencies: '@socket.io/component-emitter': 3.1.2 @@ -3813,6 +5222,10 @@ snapshots: entities@4.5.0: {} + env-paths@2.2.1: {} + + err-code@2.0.3: {} + error-ex@1.3.2: dependencies: is-arrayish: 0.2.1 @@ -3921,6 +5334,9 @@ snapshots: is-date-object: 1.0.5 is-symbol: 1.0.4 + es6-error@4.1.1: + optional: true + esbuild@0.21.5: optionalDependencies: '@esbuild/aix-ppc64': 0.21.5 @@ -4088,6 +5504,21 @@ snapshots: event-source-polyfill@1.0.31: {} + exponential-backoff@3.1.2: {} + + extract-zip@2.0.1: + dependencies: + debug: 4.3.5 + get-stream: 5.2.0 + yauzl: 2.10.0 + optionalDependencies: + '@types/yauzl': 2.10.3 + transitivePeerDependencies: + - supports-color + + extsprintf@1.4.1: + optional: true + fast-deep-equal@3.1.3: {} fast-diff@1.3.0: {} @@ -4108,10 +5539,18 @@ snapshots: dependencies: reusify: 1.0.4 + fd-slicer@1.1.0: + dependencies: + pend: 1.2.0 + file-entry-cache@6.0.1: dependencies: flat-cache: 3.2.0 + filelist@1.0.4: + dependencies: + minimatch: 5.1.6 + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -4137,7 +5576,7 @@ snapshots: foreground-child@3.2.1: dependencies: - cross-spawn: 7.0.3 + cross-spawn: 7.0.6 signal-exit: 4.1.0 form-data@4.0.0: @@ -4148,6 +5587,41 @@ snapshots: fraction.js@4.3.7: {} + fs-extra@10.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + + fs-extra@11.3.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + + fs-extra@7.0.1: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + + fs-extra@8.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + + fs-extra@9.1.0: + dependencies: + at-least-node: 1.0.0 + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + + fs-minipass@2.1.0: + dependencies: + minipass: 3.3.6 + fs.realpath@1.0.0: {} fsevents@2.3.3: @@ -4176,6 +5650,10 @@ snapshots: has-symbols: 1.0.3 hasown: 2.0.2 + get-stream@5.2.0: + dependencies: + pump: 3.0.2 + get-symbol-description@1.0.2: dependencies: call-bind: 1.0.7 @@ -4208,6 +5686,24 @@ snapshots: once: 1.4.0 path-is-absolute: 1.0.1 + glob@8.1.0: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 5.1.6 + once: 1.4.0 + + global-agent@3.0.0: + dependencies: + boolean: 3.2.0 + es6-error: 4.1.1 + matcher: 3.0.0 + roarr: 2.15.4 + semver: 7.6.2 + serialize-error: 7.0.1 + optional: true + globals@11.12.0: {} globals@13.24.0: @@ -4232,6 +5728,22 @@ snapshots: dependencies: get-intrinsic: 1.2.4 + got@11.8.6: + dependencies: + '@sindresorhus/is': 4.6.0 + '@szmarczak/http-timer': 4.0.6 + '@types/cacheable-request': 6.0.3 + '@types/responselike': 1.0.3 + cacheable-lookup: 5.0.4 + cacheable-request: 7.0.4 + decompress-response: 6.0.0 + http2-wrapper: 1.0.3 + lowercase-keys: 2.0.0 + p-cancelable: 2.1.1 + responselike: 2.0.1 + + graceful-fs@4.2.11: {} + graphemer@1.4.0: {} graphql@16.10.0: {} @@ -4264,6 +5776,62 @@ snapshots: dependencies: react-is: 16.13.1 + hosted-git-info@4.1.0: + dependencies: + lru-cache: 6.0.0 + + http-cache-semantics@4.1.1: {} + + http-proxy-agent@5.0.0: + dependencies: + '@tootallnate/once': 2.0.0 + agent-base: 6.0.2 + debug: 4.3.5 + transitivePeerDependencies: + - supports-color + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.3 + debug: 4.3.5 + transitivePeerDependencies: + - supports-color + + http2-wrapper@1.0.3: + dependencies: + quick-lru: 5.1.1 + resolve-alpn: 1.2.1 + + https-proxy-agent@5.0.1: + dependencies: + agent-base: 6.0.2 + debug: 4.3.5 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.3 + debug: 4.3.5 + transitivePeerDependencies: + - supports-color + + humanize-ms@1.2.1: + dependencies: + ms: 2.1.2 + + iconv-corefoundation@1.1.7: + dependencies: + cli-truncate: 2.1.0 + node-addon-api: 1.7.2 + optional: true + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + ieee754@1.2.1: {} + ignore@5.3.1: {} import-fresh@3.3.0: @@ -4273,6 +5841,10 @@ snapshots: imurmurhash@0.1.4: {} + indent-string@4.0.0: {} + + infer-owner@1.0.4: {} + inflight@1.0.6: dependencies: once: 1.4.0 @@ -4286,6 +5858,11 @@ snapshots: hasown: 2.0.2 side-channel: 1.0.6 + ip-address@9.0.5: + dependencies: + jsbn: 1.1.0 + sprintf-js: 1.1.3 + is-arguments@1.1.1: dependencies: call-bind: 1.0.7 @@ -4317,6 +5894,10 @@ snapshots: is-callable@1.2.7: {} + is-ci@3.0.1: + dependencies: + ci-info: 3.9.0 + is-core-module@2.14.0: dependencies: hasown: 2.0.2 @@ -4345,6 +5926,10 @@ snapshots: dependencies: is-extglob: 2.1.1 + is-interactive@1.0.0: {} + + is-lambda@1.0.1: {} + is-map@2.0.3: {} is-negative-zero@2.0.3: {} @@ -4382,6 +5967,8 @@ snapshots: dependencies: which-typed-array: 1.1.15 + is-unicode-supported@0.1.0: {} + is-weakmap@2.0.2: {} is-weakref@1.0.2: @@ -4395,6 +5982,10 @@ snapshots: isarray@2.0.5: {} + isbinaryfile@4.0.10: {} + + isbinaryfile@5.0.4: {} + isexe@2.0.0: {} iterator.prototype@1.1.2: @@ -4411,6 +6002,13 @@ snapshots: optionalDependencies: '@pkgjs/parseargs': 0.11.0 + jake@10.9.2: + dependencies: + async: 3.2.6 + chalk: 4.1.2 + filelist: 1.0.4 + minimatch: 3.1.2 + javascript-natural-sort@0.7.1: {} jiti@1.21.6: {} @@ -4426,6 +6024,8 @@ snapshots: dependencies: argparse: 2.0.1 + jsbn@1.1.0: {} + jsesc@2.5.2: {} json-buffer@3.0.1: {} @@ -4436,8 +6036,21 @@ snapshots: json-stable-stringify-without-jsonify@1.0.1: {} + json-stringify-safe@5.0.1: + optional: true + json5@2.2.3: {} + jsonfile@4.0.0: + optionalDependencies: + graceful-fs: 4.2.11 + + jsonfile@6.1.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + jsx-ast-utils@3.3.5: dependencies: array-includes: 3.1.8 @@ -4455,6 +6068,8 @@ snapshots: dependencies: language-subtag-registry: 0.3.23 + lazy-val@1.0.5: {} + levn@0.4.1: dependencies: prelude-ls: 1.2.1 @@ -4474,6 +6089,11 @@ snapshots: lodash@4.17.21: {} + log-symbols@4.1.0: + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 @@ -4484,12 +6104,47 @@ snapshots: dependencies: tslib: 2.6.3 + lowercase-keys@2.0.0: {} + lru-cache@10.3.0: {} lru-cache@5.1.1: dependencies: yallist: 3.1.1 + lru-cache@6.0.0: + dependencies: + yallist: 4.0.0 + + lru-cache@7.18.3: {} + + make-fetch-happen@10.2.1: + dependencies: + agentkeepalive: 4.6.0 + cacache: 16.1.3 + http-cache-semantics: 4.1.1 + http-proxy-agent: 5.0.0 + https-proxy-agent: 5.0.1 + is-lambda: 1.0.1 + lru-cache: 7.18.3 + minipass: 3.3.6 + minipass-collect: 1.0.2 + minipass-fetch: 2.1.2 + minipass-flush: 1.0.5 + minipass-pipeline: 1.2.4 + negotiator: 0.6.4 + promise-retry: 2.0.1 + socks-proxy-agent: 7.0.0 + ssri: 9.0.1 + transitivePeerDependencies: + - bluebird + - supports-color + + matcher@3.0.0: + dependencies: + escape-string-regexp: 4.0.0 + optional: true + merge2@1.4.1: {} micromatch@4.0.7: @@ -4503,16 +6158,75 @@ snapshots: dependencies: mime-db: 1.52.0 + mime@2.6.0: {} + + mimic-fn@2.1.0: {} + + mimic-response@1.0.1: {} + + mimic-response@3.1.0: {} + + minimatch@10.0.1: + dependencies: + brace-expansion: 2.0.1 + minimatch@3.1.2: dependencies: brace-expansion: 1.1.11 + minimatch@5.1.6: + dependencies: + brace-expansion: 2.0.1 + minimatch@9.0.5: dependencies: brace-expansion: 2.0.1 + minimist@1.2.8: {} + + minipass-collect@1.0.2: + dependencies: + minipass: 3.3.6 + + minipass-fetch@2.1.2: + dependencies: + minipass: 3.3.6 + minipass-sized: 1.0.3 + minizlib: 2.1.2 + optionalDependencies: + encoding: 0.1.13 + + minipass-flush@1.0.5: + dependencies: + minipass: 3.3.6 + + minipass-pipeline@1.2.4: + dependencies: + minipass: 3.3.6 + + minipass-sized@1.0.3: + dependencies: + minipass: 3.3.6 + + minipass@3.3.6: + dependencies: + yallist: 4.0.0 + + minipass@5.0.0: {} + minipass@7.1.2: {} + minizlib@2.1.2: + dependencies: + minipass: 3.3.6 + yallist: 4.0.0 + + mkdirp@0.5.6: + dependencies: + minimist: 1.2.8 + + mkdirp@1.0.4: {} + ms@2.1.2: {} msw@2.7.0(@types/node@22.10.3)(typescript@5.5.2): @@ -4552,17 +6266,36 @@ snapshots: natural-compare@1.4.0: {} + negotiator@0.6.4: {} + no-case@3.0.4: dependencies: lower-case: 2.0.2 tslib: 2.6.3 + node-abi@3.74.0: + dependencies: + semver: 7.6.2 + + node-addon-api@1.7.2: + optional: true + + node-api-version@0.2.1: + dependencies: + semver: 7.6.2 + node-releases@2.0.14: {} + nopt@6.0.0: + dependencies: + abbrev: 1.1.1 + normalize-path@3.0.0: {} normalize-range@0.1.2: {} + normalize-url@6.1.0: {} + object-assign@4.1.1: {} object-hash@3.0.0: {} @@ -4612,6 +6345,10 @@ snapshots: dependencies: wrappy: 1.0.2 + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -4621,8 +6358,22 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 + ora@5.4.1: + dependencies: + bl: 4.1.0 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-spinners: 2.9.2 + is-interactive: 1.0.0 + is-unicode-supported: 0.1.0 + log-symbols: 4.1.0 + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + outvariant@1.4.3: {} + p-cancelable@2.1.1: {} + p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 @@ -4631,6 +6382,10 @@ snapshots: dependencies: p-limit: 3.1.0 + p-map@4.0.0: + dependencies: + aggregate-error: 3.1.0 + package-json-from-dist@1.0.0: {} parent-module@1.0.1: @@ -4661,6 +6416,10 @@ snapshots: path-type@4.0.0: {} + pe-library@0.4.1: {} + + pend@1.2.0: {} + picocolors@1.0.1: {} picocolors@1.1.1: {} @@ -4671,6 +6430,12 @@ snapshots: pirates@4.0.6: {} + plist@3.1.0: + dependencies: + '@xmldom/xmldom': 0.8.10 + base64-js: 1.5.1 + xmlbuilder: 15.1.1 + possible-typed-array-names@1.0.0: {} postcss-import@15.1.0(postcss@8.4.39): @@ -4710,6 +6475,11 @@ snapshots: picocolors: 1.0.1 source-map-js: 1.2.0 + postject@1.0.0-alpha.6: + dependencies: + commander: 9.5.0 + optional: true + prelude-ls@1.2.1: {} prettier-linter-helpers@1.0.0: @@ -4724,6 +6494,17 @@ snapshots: prettier@3.3.2: {} + proc-log@2.0.1: {} + + progress@2.0.3: {} + + promise-inflight@1.0.1: {} + + promise-retry@2.0.1: + dependencies: + err-code: 2.0.3 + retry: 0.12.0 + prop-types@15.8.1: dependencies: loose-envify: 1.4.0 @@ -4736,12 +6517,19 @@ snapshots: dependencies: punycode: 2.3.1 + pump@3.0.2: + dependencies: + end-of-stream: 1.4.4 + once: 1.4.0 + punycode@2.3.1: {} querystringify@2.2.0: {} queue-microtask@1.2.3: {} + quick-lru@5.1.1: {} + react-datepicker@7.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@floating-ui/react': 0.26.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -4787,10 +6575,22 @@ snapshots: dependencies: loose-envify: 1.4.0 + read-binary-file-arch@1.0.6: + dependencies: + debug: 4.3.5 + transitivePeerDependencies: + - supports-color + read-cache@1.0.0: dependencies: pify: 2.3.0 + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + readdirp@3.6.0: dependencies: picomatch: 2.3.1 @@ -4818,6 +6618,12 @@ snapshots: requires-port@1.0.0: {} + resedit@1.7.2: + dependencies: + pe-library: 0.4.1 + + resolve-alpn@1.2.1: {} + resolve-from@4.0.0: {} resolve@1.22.8: @@ -4832,12 +6638,37 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + responselike@2.0.1: + dependencies: + lowercase-keys: 2.0.0 + + restore-cursor@3.1.0: + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + + retry@0.12.0: {} + reusify@1.0.4: {} + rimraf@2.6.3: + dependencies: + glob: 7.2.3 + rimraf@3.0.2: dependencies: glob: 7.2.3 + roarr@2.15.4: + dependencies: + boolean: 3.2.0 + detect-node: 2.1.0 + globalthis: 1.0.4 + json-stringify-safe: 5.0.1 + semver-compare: 1.0.0 + sprintf-js: 1.1.3 + optional: true + rollup@4.18.0: dependencies: '@types/estree': 1.0.5 @@ -4871,20 +6702,40 @@ snapshots: has-symbols: 1.0.3 isarray: 2.0.5 + safe-buffer@5.2.1: {} + safe-regex-test@1.0.3: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 is-regex: 1.1.4 + safer-buffer@2.1.2: {} + + sanitize-filename@1.6.3: + dependencies: + truncate-utf8-bytes: 1.0.2 + + sax@1.4.1: {} + scheduler@0.23.2: dependencies: loose-envify: 1.4.0 + semver-compare@1.0.0: + optional: true + + semver@5.7.2: {} + semver@6.3.1: {} semver@7.6.2: {} + serialize-error@7.0.1: + dependencies: + type-fest: 0.13.1 + optional: true + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 @@ -4914,10 +6765,25 @@ snapshots: get-intrinsic: 1.2.4 object-inspect: 1.13.2 + signal-exit@3.0.7: {} + signal-exit@4.1.0: {} + simple-update-notifier@2.0.0: + dependencies: + semver: 7.6.2 + slash@3.0.0: {} + slice-ansi@3.0.0: + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + optional: true + + smart-buffer@4.2.0: {} + snake-case@3.0.4: dependencies: dot-case: 3.0.4 @@ -4941,10 +6807,38 @@ snapshots: transitivePeerDependencies: - supports-color + socks-proxy-agent@7.0.0: + dependencies: + agent-base: 6.0.2 + debug: 4.3.5 + socks: 2.8.4 + transitivePeerDependencies: + - supports-color + + socks@2.8.4: + dependencies: + ip-address: 9.0.5 + smart-buffer: 4.2.0 + source-map-js@1.2.0: {} + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + source-map@0.5.7: {} + source-map@0.6.1: {} + + sprintf-js@1.1.3: {} + + ssri@9.0.1: + dependencies: + minipass: 3.3.6 + + stat-mode@1.0.0: {} + statuses@2.0.1: {} stop-iteration-iterator@1.0.0: @@ -5004,6 +6898,10 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.0.0 + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -5024,6 +6922,12 @@ snapshots: pirates: 4.0.6 ts-interface-checker: 0.1.13 + sumchecker@3.0.1: + dependencies: + debug: 4.3.5 + transitivePeerDependencies: + - supports-color + supports-color@5.5.0: dependencies: has-flag: 3.0.0 @@ -5070,6 +6974,25 @@ snapshots: transitivePeerDependencies: - ts-node + tar@6.2.1: + dependencies: + chownr: 2.0.0 + fs-minipass: 2.1.0 + minipass: 5.0.0 + minizlib: 2.1.2 + mkdirp: 1.0.4 + yallist: 4.0.0 + + temp-file@3.4.0: + dependencies: + async-exit-hook: 2.0.1 + fs-extra: 10.1.0 + + temp@0.9.4: + dependencies: + mkdirp: 0.5.6 + rimraf: 2.6.3 + text-table@0.2.0: {} thenify-all@1.6.0: @@ -5082,6 +7005,16 @@ snapshots: timezone@1.0.23: {} + tiny-async-pool@1.3.0: + dependencies: + semver: 5.7.2 + + tmp-promise@3.0.3: + dependencies: + tmp: 0.2.3 + + tmp@0.2.3: {} + to-fast-properties@2.0.0: {} to-regex-range@5.0.1: @@ -5095,6 +7028,10 @@ snapshots: universalify: 0.2.0 url-parse: 1.5.10 + truncate-utf8-bytes@1.0.2: + dependencies: + utf8-byte-length: 1.0.5 + ts-api-utils@1.3.0(typescript@5.5.2): dependencies: typescript: 5.5.2 @@ -5107,6 +7044,9 @@ snapshots: dependencies: prelude-ls: 1.2.1 + type-fest@0.13.1: + optional: true + type-fest@0.20.2: {} type-fest@0.21.3: {} @@ -5156,8 +7096,20 @@ snapshots: undici-types@6.20.0: {} + unique-filename@2.0.1: + dependencies: + unique-slug: 3.0.0 + + unique-slug@3.0.0: + dependencies: + imurmurhash: 0.1.4 + + universalify@0.1.2: {} + universalify@0.2.0: {} + universalify@2.0.1: {} + update-browserslist-db@1.0.16(browserslist@4.23.1): dependencies: browserslist: 4.23.1 @@ -5173,8 +7125,17 @@ snapshots: querystringify: 2.2.0 requires-port: 1.0.0 + utf8-byte-length@1.0.5: {} + util-deprecate@1.0.2: {} + verror@1.10.1: + dependencies: + assert-plus: 1.0.0 + core-util-is: 1.0.2 + extsprintf: 1.4.1 + optional: true + vite-plugin-svgr@4.2.0(rollup@4.18.0)(typescript@5.5.2)(vite@5.3.2(@types/node@22.10.3)): dependencies: '@rollup/pluginutils': 5.1.0(rollup@4.18.0) @@ -5195,6 +7156,10 @@ snapshots: '@types/node': 22.10.3 fsevents: 2.3.3 + wcwidth@1.0.1: + dependencies: + defaults: 1.0.4 + which-boxed-primitive@1.0.2: dependencies: is-bigint: 1.0.4 @@ -5261,12 +7226,16 @@ snapshots: ws@8.17.1: {} + xmlbuilder@15.1.1: {} + xmlhttprequest-ssl@2.0.0: {} y18n@5.0.8: {} yallist@3.1.1: {} + yallist@4.0.0: {} + yaml@2.4.5: {} yargs-parser@21.1.1: {} @@ -5281,6 +7250,11 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 + yauzl@2.10.0: + dependencies: + buffer-crc32: 0.2.13 + fd-slicer: 1.1.0 + yocto-queue@0.1.0: {} yoctocolors-cjs@2.1.2: {} diff --git a/public/logo_icon.png b/public/logo_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..fc5de299a2f07cc668c2c2ac49c6011fc8ff966e GIT binary patch literal 75079 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 Cb)LHb literal 0 HcmV?d00001 diff --git a/src/App.tsx b/src/app/App.tsx similarity index 83% rename from src/App.tsx rename to src/app/App.tsx index 019d04f3..6870c48e 100644 --- a/src/App.tsx +++ b/src/app/App.tsx @@ -10,6 +10,12 @@ import { queryClient } from '@/shared/apisV2/queryClient'; import router from './router/Router'; const App = () => { + // @ts-expect-error test + if (window.electron) { + // @ts-expect-error test + window.electron.getStatisticData(); + } + return (
diff --git a/src/index.css b/src/app/index.css similarity index 100% rename from src/index.css rename to src/app/index.css diff --git a/src/main.tsx b/src/app/main.tsx similarity index 100% rename from src/main.tsx rename to src/app/main.tsx diff --git a/src/mocks/browser.ts b/src/app/mocks/browser.ts similarity index 100% rename from src/mocks/browser.ts rename to src/app/mocks/browser.ts diff --git a/src/mocks/common/common.resolvers.ts b/src/app/mocks/common/common.resolvers.ts similarity index 100% rename from src/mocks/common/common.resolvers.ts rename to src/app/mocks/common/common.resolvers.ts diff --git a/src/mocks/common/common.responses.ts b/src/app/mocks/common/common.responses.ts similarity index 100% rename from src/mocks/common/common.responses.ts rename to src/app/mocks/common/common.responses.ts diff --git a/src/mocks/handlers.ts b/src/app/mocks/handlers.ts similarity index 100% rename from src/mocks/handlers.ts rename to src/app/mocks/handlers.ts diff --git a/src/mocks/home/home.resolvers.ts b/src/app/mocks/home/home.resolvers.ts similarity index 100% rename from src/mocks/home/home.resolvers.ts rename to src/app/mocks/home/home.resolvers.ts diff --git a/src/mocks/home/home.responses.ts b/src/app/mocks/home/home.responses.ts similarity index 100% rename from src/mocks/home/home.responses.ts rename to src/app/mocks/home/home.responses.ts diff --git a/src/mocks/onboarding/onboarding.resolvers.ts b/src/app/mocks/onboarding/onboarding.resolvers.ts similarity index 100% rename from src/mocks/onboarding/onboarding.resolvers.ts rename to src/app/mocks/onboarding/onboarding.resolvers.ts diff --git a/src/mocks/onboarding/onboarding.responses.ts b/src/app/mocks/onboarding/onboarding.responses.ts similarity index 100% rename from src/mocks/onboarding/onboarding.responses.ts rename to src/app/mocks/onboarding/onboarding.responses.ts diff --git a/src/pages/AllowedServicePage/AllowedServiceGroupDetail/AllowedServiceGroupDetail.tsx b/src/app/pages/AllowedServicePage/AllowedServiceGroupDetail/AllowedServiceGroupDetail.tsx similarity index 100% rename from src/pages/AllowedServicePage/AllowedServiceGroupDetail/AllowedServiceGroupDetail.tsx rename to src/app/pages/AllowedServicePage/AllowedServiceGroupDetail/AllowedServiceGroupDetail.tsx diff --git a/src/pages/AllowedServicePage/AllowedServiceGroupDetail/Content/AllowedServiceGroupDetailContent.tsx b/src/app/pages/AllowedServicePage/AllowedServiceGroupDetail/Content/AllowedServiceGroupDetailContent.tsx similarity index 100% rename from src/pages/AllowedServicePage/AllowedServiceGroupDetail/Content/AllowedServiceGroupDetailContent.tsx rename to src/app/pages/AllowedServicePage/AllowedServiceGroupDetail/Content/AllowedServiceGroupDetailContent.tsx diff --git a/src/pages/AllowedServicePage/AllowedServiceGroupDetail/Header/AllowedServiceGroupDetailHeader.tsx b/src/app/pages/AllowedServicePage/AllowedServiceGroupDetail/Header/AllowedServiceGroupDetailHeader.tsx similarity index 100% rename from src/pages/AllowedServicePage/AllowedServiceGroupDetail/Header/AllowedServiceGroupDetailHeader.tsx rename to src/app/pages/AllowedServicePage/AllowedServiceGroupDetail/Header/AllowedServiceGroupDetailHeader.tsx diff --git a/src/pages/AllowedServicePage/AllowedServiceGroupDetail/Tabs/AllowedServiceGroupDetailTabs.tsx b/src/app/pages/AllowedServicePage/AllowedServiceGroupDetail/Tabs/AllowedServiceGroupDetailTabs.tsx similarity index 100% rename from src/pages/AllowedServicePage/AllowedServiceGroupDetail/Tabs/AllowedServiceGroupDetailTabs.tsx rename to src/app/pages/AllowedServicePage/AllowedServiceGroupDetail/Tabs/AllowedServiceGroupDetailTabs.tsx diff --git a/src/pages/AllowedServicePage/AllowedServiceList/AllowedServiceList.tsx b/src/app/pages/AllowedServicePage/AllowedServiceList/AllowedServiceList.tsx similarity index 100% rename from src/pages/AllowedServicePage/AllowedServiceList/AllowedServiceList.tsx rename to src/app/pages/AllowedServicePage/AllowedServiceList/AllowedServiceList.tsx diff --git a/src/pages/AllowedServicePage/AllowedServicePage.tsx b/src/app/pages/AllowedServicePage/AllowedServicePage.tsx similarity index 100% rename from src/pages/AllowedServicePage/AllowedServicePage.tsx rename to src/app/pages/AllowedServicePage/AllowedServicePage.tsx diff --git a/src/pages/AllowedServicePage/RecommendService/RecommendService.tsx b/src/app/pages/AllowedServicePage/RecommendService/RecommendService.tsx similarity index 100% rename from src/pages/AllowedServicePage/RecommendService/RecommendService.tsx rename to src/app/pages/AllowedServicePage/RecommendService/RecommendService.tsx diff --git a/src/pages/HomePage/BoxAddCategory/BoxAddCategory.tsx b/src/app/pages/HomePage/BoxAddCategory/BoxAddCategory.tsx similarity index 100% rename from src/pages/HomePage/BoxAddCategory/BoxAddCategory.tsx rename to src/app/pages/HomePage/BoxAddCategory/BoxAddCategory.tsx diff --git a/src/pages/HomePage/BoxCategory/BoxCategory.tsx b/src/app/pages/HomePage/BoxCategory/BoxCategory.tsx similarity index 100% rename from src/pages/HomePage/BoxCategory/BoxCategory.tsx rename to src/app/pages/HomePage/BoxCategory/BoxCategory.tsx diff --git a/src/pages/HomePage/BoxCategory/BoxTodoInput/BoxTodoInput.tsx b/src/app/pages/HomePage/BoxCategory/BoxTodoInput/BoxTodoInput.tsx similarity index 100% rename from src/pages/HomePage/BoxCategory/BoxTodoInput/BoxTodoInput.tsx rename to src/app/pages/HomePage/BoxCategory/BoxTodoInput/BoxTodoInput.tsx diff --git a/src/pages/HomePage/BoxCategory/StatusDefaultBoxCategory/StatusDefaultBoxCategory.tsx b/src/app/pages/HomePage/BoxCategory/StatusDefaultBoxCategory/StatusDefaultBoxCategory.tsx similarity index 100% rename from src/pages/HomePage/BoxCategory/StatusDefaultBoxCategory/StatusDefaultBoxCategory.tsx rename to src/app/pages/HomePage/BoxCategory/StatusDefaultBoxCategory/StatusDefaultBoxCategory.tsx diff --git a/src/pages/HomePage/BoxCategory/hooks/useCreateTodo.ts b/src/app/pages/HomePage/BoxCategory/hooks/useCreateTodo.ts similarity index 100% rename from src/pages/HomePage/BoxCategory/hooks/useCreateTodo.ts rename to src/app/pages/HomePage/BoxCategory/hooks/useCreateTodo.ts diff --git a/src/pages/HomePage/BoxTodayTodo/BoxTodayTodo.tsx b/src/app/pages/HomePage/BoxTodayTodo/BoxTodayTodo.tsx similarity index 100% rename from src/pages/HomePage/BoxTodayTodo/BoxTodayTodo.tsx rename to src/app/pages/HomePage/BoxTodayTodo/BoxTodayTodo.tsx diff --git a/src/pages/HomePage/BoxTodayTodo/StatusAddBoxTodayTodo/ButtonHomeSmall/ButtonHomeSmall.tsx b/src/app/pages/HomePage/BoxTodayTodo/StatusAddBoxTodayTodo/ButtonHomeSmall/ButtonHomeSmall.tsx similarity index 100% rename from src/pages/HomePage/BoxTodayTodo/StatusAddBoxTodayTodo/ButtonHomeSmall/ButtonHomeSmall.tsx rename to src/app/pages/HomePage/BoxTodayTodo/StatusAddBoxTodayTodo/ButtonHomeSmall/ButtonHomeSmall.tsx diff --git a/src/pages/HomePage/BoxTodayTodo/StatusAddBoxTodayTodo/StatusAddBoxTodayTodo.tsx b/src/app/pages/HomePage/BoxTodayTodo/StatusAddBoxTodayTodo/StatusAddBoxTodayTodo.tsx similarity index 100% rename from src/pages/HomePage/BoxTodayTodo/StatusAddBoxTodayTodo/StatusAddBoxTodayTodo.tsx rename to src/app/pages/HomePage/BoxTodayTodo/StatusAddBoxTodayTodo/StatusAddBoxTodayTodo.tsx diff --git a/src/pages/HomePage/BoxTodayTodo/StatusDefaultBoxTodayTodo/StatusDefaultBoxTodayTodo.tsx b/src/app/pages/HomePage/BoxTodayTodo/StatusDefaultBoxTodayTodo/StatusDefaultBoxTodayTodo.tsx similarity index 100% rename from src/pages/HomePage/BoxTodayTodo/StatusDefaultBoxTodayTodo/StatusDefaultBoxTodayTodo.tsx rename to src/app/pages/HomePage/BoxTodayTodo/StatusDefaultBoxTodayTodo/StatusDefaultBoxTodayTodo.tsx diff --git a/src/pages/HomePage/ButtonMoreFriends/ButtonMoreFriends.tsx b/src/app/pages/HomePage/ButtonMoreFriends/ButtonMoreFriends.tsx similarity index 100% rename from src/pages/HomePage/ButtonMoreFriends/ButtonMoreFriends.tsx rename to src/app/pages/HomePage/ButtonMoreFriends/ButtonMoreFriends.tsx diff --git a/src/pages/HomePage/ButtonUserProfile/ButtonUserProfile.tsx b/src/app/pages/HomePage/ButtonUserProfile/ButtonUserProfile.tsx similarity index 100% rename from src/pages/HomePage/ButtonUserProfile/ButtonUserProfile.tsx rename to src/app/pages/HomePage/ButtonUserProfile/ButtonUserProfile.tsx diff --git a/src/pages/HomePage/DatePicker/ButtonDate/ButtonDate.tsx b/src/app/pages/HomePage/DatePicker/ButtonDate/ButtonDate.tsx similarity index 100% rename from src/pages/HomePage/DatePicker/ButtonDate/ButtonDate.tsx rename to src/app/pages/HomePage/DatePicker/ButtonDate/ButtonDate.tsx diff --git a/src/pages/HomePage/DatePicker/DatePicker.tsx b/src/app/pages/HomePage/DatePicker/DatePicker.tsx similarity index 100% rename from src/pages/HomePage/DatePicker/DatePicker.tsx rename to src/app/pages/HomePage/DatePicker/DatePicker.tsx diff --git a/src/pages/HomePage/DatePicker/hooks/useDatePicker.ts b/src/app/pages/HomePage/DatePicker/hooks/useDatePicker.ts similarity index 100% rename from src/pages/HomePage/DatePicker/hooks/useDatePicker.ts rename to src/app/pages/HomePage/DatePicker/hooks/useDatePicker.ts diff --git a/src/pages/HomePage/HomePage.tsx b/src/app/pages/HomePage/HomePage.tsx similarity index 100% rename from src/pages/HomePage/HomePage.tsx rename to src/app/pages/HomePage/HomePage.tsx diff --git a/src/pages/HomePage/ModalContentsAlert/ButtonAlert/ButtonAlert.tsx b/src/app/pages/HomePage/ModalContentsAlert/ButtonAlert/ButtonAlert.tsx similarity index 100% rename from src/pages/HomePage/ModalContentsAlert/ButtonAlert/ButtonAlert.tsx rename to src/app/pages/HomePage/ModalContentsAlert/ButtonAlert/ButtonAlert.tsx diff --git a/src/pages/HomePage/ModalContentsAlert/Complete/Complete.tsx b/src/app/pages/HomePage/ModalContentsAlert/Complete/Complete.tsx similarity index 100% rename from src/pages/HomePage/ModalContentsAlert/Complete/Complete.tsx rename to src/app/pages/HomePage/ModalContentsAlert/Complete/Complete.tsx diff --git a/src/pages/HomePage/ModalContentsAlert/DeleteAccount/DeleteAccount.tsx b/src/app/pages/HomePage/ModalContentsAlert/DeleteAccount/DeleteAccount.tsx similarity index 100% rename from src/pages/HomePage/ModalContentsAlert/DeleteAccount/DeleteAccount.tsx rename to src/app/pages/HomePage/ModalContentsAlert/DeleteAccount/DeleteAccount.tsx diff --git a/src/pages/HomePage/ModalContentsAlert/Logoout/Logout.tsx b/src/app/pages/HomePage/ModalContentsAlert/Logoout/Logout.tsx similarity index 100% rename from src/pages/HomePage/ModalContentsAlert/Logoout/Logout.tsx rename to src/app/pages/HomePage/ModalContentsAlert/Logoout/Logout.tsx diff --git a/src/pages/HomePage/ModalContentsAlert/ModalContentsAlert.tsx b/src/app/pages/HomePage/ModalContentsAlert/ModalContentsAlert.tsx similarity index 100% rename from src/pages/HomePage/ModalContentsAlert/ModalContentsAlert.tsx rename to src/app/pages/HomePage/ModalContentsAlert/ModalContentsAlert.tsx diff --git a/src/pages/HomePage/ModalContentsAlert/types/index.ts b/src/app/pages/HomePage/ModalContentsAlert/types/index.ts similarity index 100% rename from src/pages/HomePage/ModalContentsAlert/types/index.ts rename to src/app/pages/HomePage/ModalContentsAlert/types/index.ts diff --git a/src/pages/HomePage/StatusDefaultHome/StatusDefaultHome.tsx b/src/app/pages/HomePage/StatusDefaultHome/StatusDefaultHome.tsx similarity index 100% rename from src/pages/HomePage/StatusDefaultHome/StatusDefaultHome.tsx rename to src/app/pages/HomePage/StatusDefaultHome/StatusDefaultHome.tsx diff --git a/src/pages/HomePage/hooks/useCalendar.ts b/src/app/pages/HomePage/hooks/useCalendar.ts similarity index 100% rename from src/pages/HomePage/hooks/useCalendar.ts rename to src/app/pages/HomePage/hooks/useCalendar.ts diff --git a/src/pages/LoginPage/LoginPage.tsx b/src/app/pages/LoginPage/LoginPage.tsx similarity index 99% rename from src/pages/LoginPage/LoginPage.tsx rename to src/app/pages/LoginPage/LoginPage.tsx index dbed0487..a8f5048e 100644 --- a/src/pages/LoginPage/LoginPage.tsx +++ b/src/app/pages/LoginPage/LoginPage.tsx @@ -58,6 +58,7 @@ const LoginPage = () => { onClick={handleClick} className={`ml-[12rem] transition-opacity duration-300 ${isAnimationComplete ? 'opacity-100' : 'opacity-0'}`} > + adasdas
diff --git a/src/pages/LoginPage/hooks/useLottieAnimation.ts b/src/app/pages/LoginPage/hooks/useLottieAnimation.ts similarity index 100% rename from src/pages/LoginPage/hooks/useLottieAnimation.ts rename to src/app/pages/LoginPage/hooks/useLottieAnimation.ts diff --git a/src/pages/NotFoundPage/NotFoundPage.tsx b/src/app/pages/NotFoundPage/NotFoundPage.tsx similarity index 100% rename from src/pages/NotFoundPage/NotFoundPage.tsx rename to src/app/pages/NotFoundPage/NotFoundPage.tsx diff --git a/src/pages/OnboardingPage/ButtonSkip/ButtonSkip.tsx b/src/app/pages/OnboardingPage/ButtonSkip/ButtonSkip.tsx similarity index 100% rename from src/pages/OnboardingPage/ButtonSkip/ButtonSkip.tsx rename to src/app/pages/OnboardingPage/ButtonSkip/ButtonSkip.tsx diff --git a/src/pages/OnboardingPage/OnboardingPage.tsx b/src/app/pages/OnboardingPage/OnboardingPage.tsx similarity index 100% rename from src/pages/OnboardingPage/OnboardingPage.tsx rename to src/app/pages/OnboardingPage/OnboardingPage.tsx diff --git a/src/pages/OnboardingPage/StepField/StepField.tsx b/src/app/pages/OnboardingPage/StepField/StepField.tsx similarity index 100% rename from src/pages/OnboardingPage/StepField/StepField.tsx rename to src/app/pages/OnboardingPage/StepField/StepField.tsx diff --git a/src/pages/OnboardingPage/StepService/AllowedServices/AllowedServices.tsx b/src/app/pages/OnboardingPage/StepService/AllowedServices/AllowedServices.tsx similarity index 100% rename from src/pages/OnboardingPage/StepService/AllowedServices/AllowedServices.tsx rename to src/app/pages/OnboardingPage/StepService/AllowedServices/AllowedServices.tsx diff --git a/src/pages/OnboardingPage/StepService/ButtonService/ButtonService.tsx b/src/app/pages/OnboardingPage/StepService/ButtonService/ButtonService.tsx similarity index 100% rename from src/pages/OnboardingPage/StepService/ButtonService/ButtonService.tsx rename to src/app/pages/OnboardingPage/StepService/ButtonService/ButtonService.tsx diff --git a/src/pages/OnboardingPage/StepService/StepService.tsx b/src/app/pages/OnboardingPage/StepService/StepService.tsx similarity index 100% rename from src/pages/OnboardingPage/StepService/StepService.tsx rename to src/app/pages/OnboardingPage/StepService/StepService.tsx diff --git a/src/pages/OnboardingPage/StepService/Tabs/Tabs.tsx b/src/app/pages/OnboardingPage/StepService/Tabs/Tabs.tsx similarity index 100% rename from src/pages/OnboardingPage/StepService/Tabs/Tabs.tsx rename to src/app/pages/OnboardingPage/StepService/Tabs/Tabs.tsx diff --git a/src/pages/OnboardingPage/StepStart/StepStart.tsx b/src/app/pages/OnboardingPage/StepStart/StepStart.tsx similarity index 100% rename from src/pages/OnboardingPage/StepStart/StepStart.tsx rename to src/app/pages/OnboardingPage/StepStart/StepStart.tsx diff --git a/src/pages/OnboardingPage/hooks/useFunnel.tsx b/src/app/pages/OnboardingPage/hooks/useFunnel.tsx similarity index 100% rename from src/pages/OnboardingPage/hooks/useFunnel.tsx rename to src/app/pages/OnboardingPage/hooks/useFunnel.tsx diff --git a/src/pages/OnboardingPage/utils/serviceUrl.ts b/src/app/pages/OnboardingPage/utils/serviceUrl.ts similarity index 100% rename from src/pages/OnboardingPage/utils/serviceUrl.ts rename to src/app/pages/OnboardingPage/utils/serviceUrl.ts diff --git a/src/pages/RedirectPage/RedirectPage.tsx b/src/app/pages/RedirectPage/RedirectPage.tsx similarity index 100% rename from src/pages/RedirectPage/RedirectPage.tsx rename to src/app/pages/RedirectPage/RedirectPage.tsx diff --git a/src/pages/TimerPage/Carousel/Carousel.tsx b/src/app/pages/TimerPage/Carousel/Carousel.tsx similarity index 100% rename from src/pages/TimerPage/Carousel/Carousel.tsx rename to src/app/pages/TimerPage/Carousel/Carousel.tsx diff --git a/src/pages/TimerPage/Carousel/ContainerCarousel/ContainerCarousel.tsx b/src/app/pages/TimerPage/Carousel/ContainerCarousel/ContainerCarousel.tsx similarity index 100% rename from src/pages/TimerPage/Carousel/ContainerCarousel/ContainerCarousel.tsx rename to src/app/pages/TimerPage/Carousel/ContainerCarousel/ContainerCarousel.tsx diff --git a/src/pages/TimerPage/Carousel/ContainerCarousel/hooks/useCarouselTimer.ts b/src/app/pages/TimerPage/Carousel/ContainerCarousel/hooks/useCarouselTimer.ts similarity index 100% rename from src/pages/TimerPage/Carousel/ContainerCarousel/hooks/useCarouselTimer.ts rename to src/app/pages/TimerPage/Carousel/ContainerCarousel/hooks/useCarouselTimer.ts diff --git a/src/pages/TimerPage/PopoverAllowedService/Checkbox/Checkbox.tsx b/src/app/pages/TimerPage/PopoverAllowedService/Checkbox/Checkbox.tsx similarity index 100% rename from src/pages/TimerPage/PopoverAllowedService/Checkbox/Checkbox.tsx rename to src/app/pages/TimerPage/PopoverAllowedService/Checkbox/Checkbox.tsx diff --git a/src/pages/TimerPage/PopoverAllowedService/PopoverAllowedService.tsx b/src/app/pages/TimerPage/PopoverAllowedService/PopoverAllowedService.tsx similarity index 100% rename from src/pages/TimerPage/PopoverAllowedService/PopoverAllowedService.tsx rename to src/app/pages/TimerPage/PopoverAllowedService/PopoverAllowedService.tsx diff --git a/src/pages/TimerPage/SidebarTimer/SideBarTimer.tsx b/src/app/pages/TimerPage/SidebarTimer/SideBarTimer.tsx similarity index 100% rename from src/pages/TimerPage/SidebarTimer/SideBarTimer.tsx rename to src/app/pages/TimerPage/SidebarTimer/SideBarTimer.tsx diff --git a/src/pages/TimerPage/TItleAllowedService/TitleAllowedService.tsx b/src/app/pages/TimerPage/TItleAllowedService/TitleAllowedService.tsx similarity index 100% rename from src/pages/TimerPage/TItleAllowedService/TitleAllowedService.tsx rename to src/app/pages/TimerPage/TItleAllowedService/TitleAllowedService.tsx diff --git a/src/pages/TimerPage/TItleAllowedService/TooltipAllowedService/ToolTipAllowedService.tsx b/src/app/pages/TimerPage/TItleAllowedService/TooltipAllowedService/ToolTipAllowedService.tsx similarity index 100% rename from src/pages/TimerPage/TItleAllowedService/TooltipAllowedService/ToolTipAllowedService.tsx rename to src/app/pages/TimerPage/TItleAllowedService/TooltipAllowedService/ToolTipAllowedService.tsx diff --git a/src/pages/TimerPage/Timer/ButtonTimerPlay/ButtonTimerPlay.tsx b/src/app/pages/TimerPage/Timer/ButtonTimerPlay/ButtonTimerPlay.tsx similarity index 100% rename from src/pages/TimerPage/Timer/ButtonTimerPlay/ButtonTimerPlay.tsx rename to src/app/pages/TimerPage/Timer/ButtonTimerPlay/ButtonTimerPlay.tsx diff --git a/src/pages/TimerPage/Timer/ProgressCircle/ProgressCircle.tsx b/src/app/pages/TimerPage/Timer/ProgressCircle/ProgressCircle.tsx similarity index 100% rename from src/pages/TimerPage/Timer/ProgressCircle/ProgressCircle.tsx rename to src/app/pages/TimerPage/Timer/ProgressCircle/ProgressCircle.tsx diff --git a/src/pages/TimerPage/Timer/Timer.tsx b/src/app/pages/TimerPage/Timer/Timer.tsx similarity index 100% rename from src/pages/TimerPage/Timer/Timer.tsx rename to src/app/pages/TimerPage/Timer/Timer.tsx diff --git a/src/pages/TimerPage/TimerPage.tsx b/src/app/pages/TimerPage/TimerPage.tsx similarity index 100% rename from src/pages/TimerPage/TimerPage.tsx rename to src/app/pages/TimerPage/TimerPage.tsx diff --git a/src/pages/TimerPage/hooks/useTimerCount.ts b/src/app/pages/TimerPage/hooks/useTimerCount.ts similarity index 100% rename from src/pages/TimerPage/hooks/useTimerCount.ts rename to src/app/pages/TimerPage/hooks/useTimerCount.ts diff --git a/src/pages/TimerPage/hooks/useToggleSidebar.ts b/src/app/pages/TimerPage/hooks/useToggleSidebar.ts similarity index 100% rename from src/pages/TimerPage/hooks/useToggleSidebar.ts rename to src/app/pages/TimerPage/hooks/useToggleSidebar.ts diff --git a/src/pages/TimerPage/hooks/useUrlHandler.ts b/src/app/pages/TimerPage/hooks/useUrlHandler.ts similarity index 100% rename from src/pages/TimerPage/hooks/useUrlHandler.ts rename to src/app/pages/TimerPage/hooks/useUrlHandler.ts diff --git a/src/router/ProtectedRoute.tsx b/src/app/router/ProtectedRoute.tsx similarity index 100% rename from src/router/ProtectedRoute.tsx rename to src/app/router/ProtectedRoute.tsx diff --git a/src/router/Router.tsx b/src/app/router/Router.tsx similarity index 95% rename from src/router/Router.tsx rename to src/app/router/Router.tsx index f0d6729f..412eb59f 100644 --- a/src/router/Router.tsx +++ b/src/app/router/Router.tsx @@ -1,7 +1,7 @@ import type { Router } from '@remix-run/router'; import { Suspense, lazy } from 'react'; -import { Outlet, createBrowserRouter } from 'react-router-dom'; +import { Outlet, createHashRouter } from 'react-router-dom'; import ErrorBoundary from '@/shared/components/ErrorBoundary/ErrorBoundary'; import HeartBeatBoundary from '@/shared/components/HeartBeatBoundary/HeartBeatBoundary'; @@ -20,7 +20,7 @@ const RedirectPage = lazy(() => import('@/pages/RedirectPage/RedirectPage')); const OnboardingPage = lazy(() => import('@/pages/OnboardingPage/OnboardingPage')); const TimerPage = lazy(() => import('@/pages/TimerPage/TimerPage')); -const router: Router = createBrowserRouter([ +const router: Router = createHashRouter([ { //public 라우트들 path: '/', diff --git a/src/router/routesConfig.ts b/src/app/router/routesConfig.ts similarity index 100% rename from src/router/routesConfig.ts rename to src/app/router/routesConfig.ts diff --git a/src/shared/apisV2/allowedService/allowedService.api.ts b/src/app/shared/apisV2/allowedService/allowedService.api.ts similarity index 100% rename from src/shared/apisV2/allowedService/allowedService.api.ts rename to src/app/shared/apisV2/allowedService/allowedService.api.ts diff --git a/src/shared/apisV2/allowedService/allowedService.keys.ts b/src/app/shared/apisV2/allowedService/allowedService.keys.ts similarity index 100% rename from src/shared/apisV2/allowedService/allowedService.keys.ts rename to src/app/shared/apisV2/allowedService/allowedService.keys.ts diff --git a/src/shared/apisV2/allowedService/allowedService.mutations.ts b/src/app/shared/apisV2/allowedService/allowedService.mutations.ts similarity index 100% rename from src/shared/apisV2/allowedService/allowedService.mutations.ts rename to src/app/shared/apisV2/allowedService/allowedService.mutations.ts diff --git a/src/shared/apisV2/allowedService/allowedService.queries.ts b/src/app/shared/apisV2/allowedService/allowedService.queries.ts similarity index 100% rename from src/shared/apisV2/allowedService/allowedService.queries.ts rename to src/app/shared/apisV2/allowedService/allowedService.queries.ts diff --git a/src/shared/apisV2/auth/auth.api.ts b/src/app/shared/apisV2/auth/auth.api.ts similarity index 100% rename from src/shared/apisV2/auth/auth.api.ts rename to src/app/shared/apisV2/auth/auth.api.ts diff --git a/src/shared/apisV2/auth/auth.queries.ts b/src/app/shared/apisV2/auth/auth.queries.ts similarity index 100% rename from src/shared/apisV2/auth/auth.queries.ts rename to src/app/shared/apisV2/auth/auth.queries.ts diff --git a/src/shared/apisV2/client.ts b/src/app/shared/apisV2/client.ts similarity index 100% rename from src/shared/apisV2/client.ts rename to src/app/shared/apisV2/client.ts diff --git a/src/shared/apisV2/common/common.api.ts b/src/app/shared/apisV2/common/common.api.ts similarity index 100% rename from src/shared/apisV2/common/common.api.ts rename to src/app/shared/apisV2/common/common.api.ts diff --git a/src/shared/apisV2/common/common.keys.ts b/src/app/shared/apisV2/common/common.keys.ts similarity index 100% rename from src/shared/apisV2/common/common.keys.ts rename to src/app/shared/apisV2/common/common.keys.ts diff --git a/src/shared/apisV2/common/common.mutations.ts b/src/app/shared/apisV2/common/common.mutations.ts similarity index 100% rename from src/shared/apisV2/common/common.mutations.ts rename to src/app/shared/apisV2/common/common.mutations.ts diff --git a/src/shared/apisV2/common/common.queries.ts b/src/app/shared/apisV2/common/common.queries.ts similarity index 100% rename from src/shared/apisV2/common/common.queries.ts rename to src/app/shared/apisV2/common/common.queries.ts diff --git a/src/shared/apisV2/friends/friends.api.ts b/src/app/shared/apisV2/friends/friends.api.ts similarity index 100% rename from src/shared/apisV2/friends/friends.api.ts rename to src/app/shared/apisV2/friends/friends.api.ts diff --git a/src/shared/apisV2/friends/friends.keys.ts b/src/app/shared/apisV2/friends/friends.keys.ts similarity index 100% rename from src/shared/apisV2/friends/friends.keys.ts rename to src/app/shared/apisV2/friends/friends.keys.ts diff --git a/src/shared/apisV2/friends/friends.mutations.ts b/src/app/shared/apisV2/friends/friends.mutations.ts similarity index 100% rename from src/shared/apisV2/friends/friends.mutations.ts rename to src/app/shared/apisV2/friends/friends.mutations.ts diff --git a/src/shared/apisV2/friends/friends.queries.ts b/src/app/shared/apisV2/friends/friends.queries.ts similarity index 100% rename from src/shared/apisV2/friends/friends.queries.ts rename to src/app/shared/apisV2/friends/friends.queries.ts diff --git a/src/shared/apisV2/home/home.api.ts b/src/app/shared/apisV2/home/home.api.ts similarity index 100% rename from src/shared/apisV2/home/home.api.ts rename to src/app/shared/apisV2/home/home.api.ts diff --git a/src/shared/apisV2/home/home.keys.ts b/src/app/shared/apisV2/home/home.keys.ts similarity index 100% rename from src/shared/apisV2/home/home.keys.ts rename to src/app/shared/apisV2/home/home.keys.ts diff --git a/src/shared/apisV2/home/home.mutations.ts b/src/app/shared/apisV2/home/home.mutations.ts similarity index 100% rename from src/shared/apisV2/home/home.mutations.ts rename to src/app/shared/apisV2/home/home.mutations.ts diff --git a/src/shared/apisV2/home/home.queries.ts b/src/app/shared/apisV2/home/home.queries.ts similarity index 100% rename from src/shared/apisV2/home/home.queries.ts rename to src/app/shared/apisV2/home/home.queries.ts diff --git a/src/shared/apisV2/onboarding/onboarding.api.ts b/src/app/shared/apisV2/onboarding/onboarding.api.ts similarity index 100% rename from src/shared/apisV2/onboarding/onboarding.api.ts rename to src/app/shared/apisV2/onboarding/onboarding.api.ts diff --git a/src/shared/apisV2/onboarding/onboarding.mutations.ts b/src/app/shared/apisV2/onboarding/onboarding.mutations.ts similarity index 100% rename from src/shared/apisV2/onboarding/onboarding.mutations.ts rename to src/app/shared/apisV2/onboarding/onboarding.mutations.ts diff --git a/src/shared/apisV2/queryClient.ts b/src/app/shared/apisV2/queryClient.ts similarity index 100% rename from src/shared/apisV2/queryClient.ts rename to src/app/shared/apisV2/queryClient.ts diff --git a/src/shared/apisV2/setting/setting.api.ts b/src/app/shared/apisV2/setting/setting.api.ts similarity index 100% rename from src/shared/apisV2/setting/setting.api.ts rename to src/app/shared/apisV2/setting/setting.api.ts diff --git a/src/shared/apisV2/setting/setting.keys.ts b/src/app/shared/apisV2/setting/setting.keys.ts similarity index 100% rename from src/shared/apisV2/setting/setting.keys.ts rename to src/app/shared/apisV2/setting/setting.keys.ts diff --git a/src/shared/apisV2/setting/setting.mutations.ts b/src/app/shared/apisV2/setting/setting.mutations.ts similarity index 100% rename from src/shared/apisV2/setting/setting.mutations.ts rename to src/app/shared/apisV2/setting/setting.mutations.ts diff --git a/src/shared/apisV2/setting/setting.queries.ts b/src/app/shared/apisV2/setting/setting.queries.ts similarity index 100% rename from src/shared/apisV2/setting/setting.queries.ts rename to src/app/shared/apisV2/setting/setting.queries.ts diff --git a/src/shared/apisV2/timer/timer.api.ts b/src/app/shared/apisV2/timer/timer.api.ts similarity index 100% rename from src/shared/apisV2/timer/timer.api.ts rename to src/app/shared/apisV2/timer/timer.api.ts diff --git a/src/shared/apisV2/timer/timer.keys.ts b/src/app/shared/apisV2/timer/timer.keys.ts similarity index 100% rename from src/shared/apisV2/timer/timer.keys.ts rename to src/app/shared/apisV2/timer/timer.keys.ts diff --git a/src/shared/apisV2/timer/timer.mutations.ts b/src/app/shared/apisV2/timer/timer.mutations.ts similarity index 100% rename from src/shared/apisV2/timer/timer.mutations.ts rename to src/app/shared/apisV2/timer/timer.mutations.ts diff --git a/src/shared/apisV2/timer/timer.queries.ts b/src/app/shared/apisV2/timer/timer.queries.ts similarity index 100% rename from src/shared/apisV2/timer/timer.queries.ts rename to src/app/shared/apisV2/timer/timer.queries.ts diff --git a/src/shared/assets/images/example.jpg b/src/app/shared/assets/images/example.jpg similarity index 100% rename from src/shared/assets/images/example.jpg rename to src/app/shared/assets/images/example.jpg diff --git a/src/shared/assets/images/img_timer_bg.png b/src/app/shared/assets/images/img_timer_bg.png similarity index 100% rename from src/shared/assets/images/img_timer_bg.png rename to src/app/shared/assets/images/img_timer_bg.png diff --git a/src/shared/assets/images/login_background.png b/src/app/shared/assets/images/login_background.png similarity index 100% rename from src/shared/assets/images/login_background.png rename to src/app/shared/assets/images/login_background.png diff --git a/src/shared/assets/images/profile_image1.png b/src/app/shared/assets/images/profile_image1.png similarity index 100% rename from src/shared/assets/images/profile_image1.png rename to src/app/shared/assets/images/profile_image1.png diff --git a/src/shared/assets/images/profile_image2.png b/src/app/shared/assets/images/profile_image2.png similarity index 100% rename from src/shared/assets/images/profile_image2.png rename to src/app/shared/assets/images/profile_image2.png diff --git a/src/shared/assets/images/profile_image3.png b/src/app/shared/assets/images/profile_image3.png similarity index 100% rename from src/shared/assets/images/profile_image3.png rename to src/app/shared/assets/images/profile_image3.png diff --git a/src/shared/assets/images/profile_image4.png b/src/app/shared/assets/images/profile_image4.png similarity index 100% rename from src/shared/assets/images/profile_image4.png rename to src/app/shared/assets/images/profile_image4.png diff --git a/src/shared/assets/lotties/loading.json b/src/app/shared/assets/lotties/loading.json similarity index 100% rename from src/shared/assets/lotties/loading.json rename to src/app/shared/assets/lotties/loading.json diff --git a/src/shared/assets/lotties/morib_logo_motion.json b/src/app/shared/assets/lotties/morib_logo_motion.json similarity index 100% rename from src/shared/assets/lotties/morib_logo_motion.json rename to src/app/shared/assets/lotties/morib_logo_motion.json diff --git a/src/shared/assets/svgs/404.svg b/src/app/shared/assets/svgs/404.svg similarity index 100% rename from src/shared/assets/svgs/404.svg rename to src/app/shared/assets/svgs/404.svg diff --git a/src/shared/assets/svgs/add_btn.svg b/src/app/shared/assets/svgs/add_btn.svg similarity index 100% rename from src/shared/assets/svgs/add_btn.svg rename to src/app/shared/assets/svgs/add_btn.svg diff --git a/src/shared/assets/svgs/arrow_circle_up_right.svg b/src/app/shared/assets/svgs/arrow_circle_up_right.svg similarity index 100% rename from src/shared/assets/svgs/arrow_circle_up_right.svg rename to src/app/shared/assets/svgs/arrow_circle_up_right.svg diff --git a/src/shared/assets/svgs/arrow_right.svg b/src/app/shared/assets/svgs/arrow_right.svg similarity index 100% rename from src/shared/assets/svgs/arrow_right.svg rename to src/app/shared/assets/svgs/arrow_right.svg diff --git a/src/shared/assets/svgs/bell.svg b/src/app/shared/assets/svgs/bell.svg similarity index 100% rename from src/shared/assets/svgs/bell.svg rename to src/app/shared/assets/svgs/bell.svg diff --git a/src/shared/assets/svgs/btn_add.svg b/src/app/shared/assets/svgs/btn_add.svg similarity index 100% rename from src/shared/assets/svgs/btn_add.svg rename to src/app/shared/assets/svgs/btn_add.svg diff --git a/src/shared/assets/svgs/btn_arrow.svg b/src/app/shared/assets/svgs/btn_arrow.svg similarity index 100% rename from src/shared/assets/svgs/btn_arrow.svg rename to src/app/shared/assets/svgs/btn_arrow.svg diff --git a/src/shared/assets/svgs/btn_arrow_bgNone.svg b/src/app/shared/assets/svgs/btn_arrow_bgNone.svg similarity index 100% rename from src/shared/assets/svgs/btn_arrow_bgNone.svg rename to src/app/shared/assets/svgs/btn_arrow_bgNone.svg diff --git a/src/shared/assets/svgs/btn_cal.svg b/src/app/shared/assets/svgs/btn_cal.svg similarity index 100% rename from src/shared/assets/svgs/btn_cal.svg rename to src/app/shared/assets/svgs/btn_cal.svg diff --git a/src/shared/assets/svgs/btn_hamburger.svg b/src/app/shared/assets/svgs/btn_hamburger.svg similarity index 100% rename from src/shared/assets/svgs/btn_hamburger.svg rename to src/app/shared/assets/svgs/btn_hamburger.svg diff --git a/src/shared/assets/svgs/btn_home.svg b/src/app/shared/assets/svgs/btn_home.svg similarity index 100% rename from src/shared/assets/svgs/btn_home.svg rename to src/app/shared/assets/svgs/btn_home.svg diff --git a/src/shared/assets/svgs/btn_inputClear.svg b/src/app/shared/assets/svgs/btn_inputClear.svg similarity index 100% rename from src/shared/assets/svgs/btn_inputClear.svg rename to src/app/shared/assets/svgs/btn_inputClear.svg diff --git a/src/shared/assets/svgs/btn_list.svg b/src/app/shared/assets/svgs/btn_list.svg similarity index 100% rename from src/shared/assets/svgs/btn_list.svg rename to src/app/shared/assets/svgs/btn_list.svg diff --git a/src/shared/assets/svgs/btn_moribset_active.svg b/src/app/shared/assets/svgs/btn_moribset_active.svg similarity index 100% rename from src/shared/assets/svgs/btn_moribset_active.svg rename to src/app/shared/assets/svgs/btn_moribset_active.svg diff --git a/src/shared/assets/svgs/btn_moribset_default.svg b/src/app/shared/assets/svgs/btn_moribset_default.svg similarity index 100% rename from src/shared/assets/svgs/btn_moribset_default.svg rename to src/app/shared/assets/svgs/btn_moribset_default.svg diff --git a/src/shared/assets/svgs/btn_today.svg b/src/app/shared/assets/svgs/btn_today.svg similarity index 100% rename from src/shared/assets/svgs/btn_today.svg rename to src/app/shared/assets/svgs/btn_today.svg diff --git a/src/shared/assets/svgs/button_inputSuccess.svg b/src/app/shared/assets/svgs/button_inputSuccess.svg similarity index 100% rename from src/shared/assets/svgs/button_inputSuccess.svg rename to src/app/shared/assets/svgs/button_inputSuccess.svg diff --git a/src/shared/assets/svgs/check_box_blank.svg b/src/app/shared/assets/svgs/check_box_blank.svg similarity index 100% rename from src/shared/assets/svgs/check_box_blank.svg rename to src/app/shared/assets/svgs/check_box_blank.svg diff --git a/src/shared/assets/svgs/check_box_fill.svg b/src/app/shared/assets/svgs/check_box_fill.svg similarity index 100% rename from src/shared/assets/svgs/check_box_fill.svg rename to src/app/shared/assets/svgs/check_box_fill.svg diff --git a/src/shared/assets/svgs/common/ic_logo.svg b/src/app/shared/assets/svgs/common/ic_logo.svg similarity index 100% rename from src/shared/assets/svgs/common/ic_logo.svg rename to src/app/shared/assets/svgs/common/ic_logo.svg diff --git a/src/shared/assets/svgs/common/ic_meatball_default.svg b/src/app/shared/assets/svgs/common/ic_meatball_default.svg similarity index 100% rename from src/shared/assets/svgs/common/ic_meatball_default.svg rename to src/app/shared/assets/svgs/common/ic_meatball_default.svg diff --git a/src/shared/assets/svgs/connection_icon.svg b/src/app/shared/assets/svgs/connection_icon.svg similarity index 100% rename from src/shared/assets/svgs/connection_icon.svg rename to src/app/shared/assets/svgs/connection_icon.svg diff --git a/src/shared/assets/svgs/default_profile.svg b/src/app/shared/assets/svgs/default_profile.svg similarity index 100% rename from src/shared/assets/svgs/default_profile.svg rename to src/app/shared/assets/svgs/default_profile.svg diff --git a/src/shared/assets/svgs/defaultpause.svg b/src/app/shared/assets/svgs/defaultpause.svg similarity index 100% rename from src/shared/assets/svgs/defaultpause.svg rename to src/app/shared/assets/svgs/defaultpause.svg diff --git a/src/shared/assets/svgs/defaultplay.svg b/src/app/shared/assets/svgs/defaultplay.svg similarity index 100% rename from src/shared/assets/svgs/defaultplay.svg rename to src/app/shared/assets/svgs/defaultplay.svg diff --git a/src/shared/assets/svgs/description.svg b/src/app/shared/assets/svgs/description.svg similarity index 100% rename from src/shared/assets/svgs/description.svg rename to src/app/shared/assets/svgs/description.svg diff --git a/src/shared/assets/svgs/disabled_dropdown.svg b/src/app/shared/assets/svgs/disabled_dropdown.svg similarity index 100% rename from src/shared/assets/svgs/disabled_dropdown.svg rename to src/app/shared/assets/svgs/disabled_dropdown.svg diff --git a/src/shared/assets/svgs/dropIcon.svg b/src/app/shared/assets/svgs/dropIcon.svg similarity index 100% rename from src/shared/assets/svgs/dropIcon.svg rename to src/app/shared/assets/svgs/dropIcon.svg diff --git a/src/shared/assets/svgs/elipse.svg b/src/app/shared/assets/svgs/elipse.svg similarity index 100% rename from src/shared/assets/svgs/elipse.svg rename to src/app/shared/assets/svgs/elipse.svg diff --git a/src/shared/assets/svgs/error.svg b/src/app/shared/assets/svgs/error.svg similarity index 100% rename from src/shared/assets/svgs/error.svg rename to src/app/shared/assets/svgs/error.svg diff --git a/src/shared/assets/svgs/error_input.svg b/src/app/shared/assets/svgs/error_input.svg similarity index 100% rename from src/shared/assets/svgs/error_input.svg rename to src/app/shared/assets/svgs/error_input.svg diff --git a/src/shared/assets/svgs/friend_delBtn.svg b/src/app/shared/assets/svgs/friend_delBtn.svg similarity index 100% rename from src/shared/assets/svgs/friend_delBtn.svg rename to src/app/shared/assets/svgs/friend_delBtn.svg diff --git a/src/shared/assets/svgs/friend_setting.svg b/src/app/shared/assets/svgs/friend_setting.svg similarity index 100% rename from src/shared/assets/svgs/friend_setting.svg rename to src/app/shared/assets/svgs/friend_setting.svg diff --git a/src/shared/assets/svgs/google_login.svg b/src/app/shared/assets/svgs/google_login.svg similarity index 100% rename from src/shared/assets/svgs/google_login.svg rename to src/app/shared/assets/svgs/google_login.svg diff --git a/src/shared/assets/svgs/gradient_circle.svg b/src/app/shared/assets/svgs/gradient_circle.svg similarity index 100% rename from src/shared/assets/svgs/gradient_circle.svg rename to src/app/shared/assets/svgs/gradient_circle.svg diff --git a/src/shared/assets/svgs/header_delBtn.svg b/src/app/shared/assets/svgs/header_delBtn.svg similarity index 100% rename from src/shared/assets/svgs/header_delBtn.svg rename to src/app/shared/assets/svgs/header_delBtn.svg diff --git a/src/shared/assets/svgs/home/ic_box.svg b/src/app/shared/assets/svgs/home/ic_box.svg similarity index 100% rename from src/shared/assets/svgs/home/ic_box.svg rename to src/app/shared/assets/svgs/home/ic_box.svg diff --git a/src/shared/assets/svgs/home/ic_plus.svg b/src/app/shared/assets/svgs/home/ic_plus.svg similarity index 100% rename from src/shared/assets/svgs/home/ic_plus.svg rename to src/app/shared/assets/svgs/home/ic_plus.svg diff --git a/src/shared/assets/svgs/home_default_icon.svg b/src/app/shared/assets/svgs/home_default_icon.svg similarity index 100% rename from src/shared/assets/svgs/home_default_icon.svg rename to src/app/shared/assets/svgs/home_default_icon.svg diff --git a/src/shared/assets/svgs/hoverpause.svg b/src/app/shared/assets/svgs/hoverpause.svg similarity index 100% rename from src/shared/assets/svgs/hoverpause.svg rename to src/app/shared/assets/svgs/hoverpause.svg diff --git a/src/shared/assets/svgs/hoverplay.svg b/src/app/shared/assets/svgs/hoverplay.svg similarity index 100% rename from src/shared/assets/svgs/hoverplay.svg rename to src/app/shared/assets/svgs/hoverplay.svg diff --git a/src/shared/assets/svgs/ic_back_btn.svg b/src/app/shared/assets/svgs/ic_back_btn.svg similarity index 100% rename from src/shared/assets/svgs/ic_back_btn.svg rename to src/app/shared/assets/svgs/ic_back_btn.svg diff --git a/src/shared/assets/svgs/ic_delete_alert.svg b/src/app/shared/assets/svgs/ic_delete_alert.svg similarity index 100% rename from src/shared/assets/svgs/ic_delete_alert.svg rename to src/app/shared/assets/svgs/ic_delete_alert.svg diff --git a/src/shared/assets/svgs/ic_description.svg b/src/app/shared/assets/svgs/ic_description.svg similarity index 100% rename from src/shared/assets/svgs/ic_description.svg rename to src/app/shared/assets/svgs/ic_description.svg diff --git a/src/shared/assets/svgs/ic_folder.svg b/src/app/shared/assets/svgs/ic_folder.svg similarity index 100% rename from src/shared/assets/svgs/ic_folder.svg rename to src/app/shared/assets/svgs/ic_folder.svg diff --git a/src/shared/assets/svgs/ic_gear.svg b/src/app/shared/assets/svgs/ic_gear.svg similarity index 100% rename from src/shared/assets/svgs/ic_gear.svg rename to src/app/shared/assets/svgs/ic_gear.svg diff --git a/src/shared/assets/svgs/ic_logo.svg b/src/app/shared/assets/svgs/ic_logo.svg similarity index 100% rename from src/shared/assets/svgs/ic_logo.svg rename to src/app/shared/assets/svgs/ic_logo.svg diff --git a/src/shared/assets/svgs/ic_minus.svg b/src/app/shared/assets/svgs/ic_minus.svg similarity index 100% rename from src/shared/assets/svgs/ic_minus.svg rename to src/app/shared/assets/svgs/ic_minus.svg diff --git a/src/shared/assets/svgs/ic_pencil.svg b/src/app/shared/assets/svgs/ic_pencil.svg similarity index 100% rename from src/shared/assets/svgs/ic_pencil.svg rename to src/app/shared/assets/svgs/ic_pencil.svg diff --git a/src/shared/assets/svgs/ic_service_design.svg b/src/app/shared/assets/svgs/ic_service_design.svg similarity index 100% rename from src/shared/assets/svgs/ic_service_design.svg rename to src/app/shared/assets/svgs/ic_service_design.svg diff --git a/src/shared/assets/svgs/ic_service_design_sm.svg b/src/app/shared/assets/svgs/ic_service_design_sm.svg similarity index 100% rename from src/shared/assets/svgs/ic_service_design_sm.svg rename to src/app/shared/assets/svgs/ic_service_design_sm.svg diff --git a/src/shared/assets/svgs/icon_clock.svg b/src/app/shared/assets/svgs/icon_clock.svg similarity index 100% rename from src/shared/assets/svgs/icon_clock.svg rename to src/app/shared/assets/svgs/icon_clock.svg diff --git a/src/shared/assets/svgs/large_plus.svg b/src/app/shared/assets/svgs/large_plus.svg similarity index 100% rename from src/shared/assets/svgs/large_plus.svg rename to src/app/shared/assets/svgs/large_plus.svg diff --git a/src/shared/assets/svgs/logo_icon.svg b/src/app/shared/assets/svgs/logo_icon.svg similarity index 100% rename from src/shared/assets/svgs/logo_icon.svg rename to src/app/shared/assets/svgs/logo_icon.svg diff --git a/src/shared/assets/svgs/mail.svg b/src/app/shared/assets/svgs/mail.svg similarity index 100% rename from src/shared/assets/svgs/mail.svg rename to src/app/shared/assets/svgs/mail.svg diff --git a/src/shared/assets/svgs/mingcute_time-fill.svg b/src/app/shared/assets/svgs/mingcute_time-fill.svg similarity index 100% rename from src/shared/assets/svgs/mingcute_time-fill.svg rename to src/app/shared/assets/svgs/mingcute_time-fill.svg diff --git a/src/shared/assets/svgs/mingcute_time-line.svg b/src/app/shared/assets/svgs/mingcute_time-line.svg similarity index 100% rename from src/shared/assets/svgs/mingcute_time-line.svg rename to src/app/shared/assets/svgs/mingcute_time-line.svg diff --git a/src/shared/assets/svgs/minus_btn.svg b/src/app/shared/assets/svgs/minus_btn.svg similarity index 100% rename from src/shared/assets/svgs/minus_btn.svg rename to src/app/shared/assets/svgs/minus_btn.svg diff --git a/src/shared/assets/svgs/more_friend.svg b/src/app/shared/assets/svgs/more_friend.svg similarity index 100% rename from src/shared/assets/svgs/more_friend.svg rename to src/app/shared/assets/svgs/more_friend.svg diff --git a/src/shared/assets/svgs/moribSet.svg b/src/app/shared/assets/svgs/moribSet.svg similarity index 100% rename from src/shared/assets/svgs/moribSet.svg rename to src/app/shared/assets/svgs/moribSet.svg diff --git a/src/shared/assets/svgs/onboarding/ic_business.svg b/src/app/shared/assets/svgs/onboarding/ic_business.svg similarity index 100% rename from src/shared/assets/svgs/onboarding/ic_business.svg rename to src/app/shared/assets/svgs/onboarding/ic_business.svg diff --git a/src/shared/assets/svgs/onboarding/ic_design.svg b/src/app/shared/assets/svgs/onboarding/ic_design.svg similarity index 100% rename from src/shared/assets/svgs/onboarding/ic_design.svg rename to src/app/shared/assets/svgs/onboarding/ic_design.svg diff --git a/src/shared/assets/svgs/onboarding/ic_development.svg b/src/app/shared/assets/svgs/onboarding/ic_development.svg similarity index 100% rename from src/shared/assets/svgs/onboarding/ic_development.svg rename to src/app/shared/assets/svgs/onboarding/ic_development.svg diff --git a/src/shared/assets/svgs/onboarding/ic_marketing.svg b/src/app/shared/assets/svgs/onboarding/ic_marketing.svg similarity index 100% rename from src/shared/assets/svgs/onboarding/ic_marketing.svg rename to src/app/shared/assets/svgs/onboarding/ic_marketing.svg diff --git a/src/shared/assets/svgs/onboarding/ic_planning.svg b/src/app/shared/assets/svgs/onboarding/ic_planning.svg similarity index 100% rename from src/shared/assets/svgs/onboarding/ic_planning.svg rename to src/app/shared/assets/svgs/onboarding/ic_planning.svg diff --git a/src/shared/assets/svgs/onboarding/ic_studying.svg b/src/app/shared/assets/svgs/onboarding/ic_studying.svg similarity index 100% rename from src/shared/assets/svgs/onboarding/ic_studying.svg rename to src/app/shared/assets/svgs/onboarding/ic_studying.svg diff --git a/src/shared/assets/svgs/onboarding_image.svg b/src/app/shared/assets/svgs/onboarding_image.svg similarity index 100% rename from src/shared/assets/svgs/onboarding_image.svg rename to src/app/shared/assets/svgs/onboarding_image.svg diff --git a/src/shared/assets/svgs/plus.svg b/src/app/shared/assets/svgs/plus.svg similarity index 100% rename from src/shared/assets/svgs/plus.svg rename to src/app/shared/assets/svgs/plus.svg diff --git a/src/shared/assets/svgs/react.svg b/src/app/shared/assets/svgs/react.svg similarity index 100% rename from src/shared/assets/svgs/react.svg rename to src/app/shared/assets/svgs/react.svg diff --git a/src/shared/assets/svgs/selected_number_icon.svg b/src/app/shared/assets/svgs/selected_number_icon.svg similarity index 100% rename from src/shared/assets/svgs/selected_number_icon.svg rename to src/app/shared/assets/svgs/selected_number_icon.svg diff --git a/src/shared/assets/svgs/setting.svg b/src/app/shared/assets/svgs/setting.svg similarity index 100% rename from src/shared/assets/svgs/setting.svg rename to src/app/shared/assets/svgs/setting.svg diff --git a/src/shared/assets/svgs/timer/ic_check_box_active.svg b/src/app/shared/assets/svgs/timer/ic_check_box_active.svg similarity index 100% rename from src/shared/assets/svgs/timer/ic_check_box_active.svg rename to src/app/shared/assets/svgs/timer/ic_check_box_active.svg diff --git a/src/shared/assets/svgs/timer/ic_check_box_inactive.svg b/src/app/shared/assets/svgs/timer/ic_check_box_inactive.svg similarity index 100% rename from src/shared/assets/svgs/timer/ic_check_box_inactive.svg rename to src/app/shared/assets/svgs/timer/ic_check_box_inactive.svg diff --git a/src/shared/assets/svgs/timer/ic_deactivated_clock.svg b/src/app/shared/assets/svgs/timer/ic_deactivated_clock.svg similarity index 100% rename from src/shared/assets/svgs/timer/ic_deactivated_clock.svg rename to src/app/shared/assets/svgs/timer/ic_deactivated_clock.svg diff --git a/src/shared/assets/svgs/timer/ic_online.svg b/src/app/shared/assets/svgs/timer/ic_online.svg similarity index 100% rename from src/shared/assets/svgs/timer/ic_online.svg rename to src/app/shared/assets/svgs/timer/ic_online.svg diff --git a/src/shared/assets/svgs/timer/ic_timer_inner_circle.svg b/src/app/shared/assets/svgs/timer/ic_timer_inner_circle.svg similarity index 100% rename from src/shared/assets/svgs/timer/ic_timer_inner_circle.svg rename to src/app/shared/assets/svgs/timer/ic_timer_inner_circle.svg diff --git a/src/shared/assets/svgs/todo_meatball_press.svg b/src/app/shared/assets/svgs/todo_meatball_press.svg similarity index 100% rename from src/shared/assets/svgs/todo_meatball_press.svg rename to src/app/shared/assets/svgs/todo_meatball_press.svg diff --git a/src/shared/assets/svgs/todo_toggle.svg b/src/app/shared/assets/svgs/todo_toggle.svg similarity index 100% rename from src/shared/assets/svgs/todo_toggle.svg rename to src/app/shared/assets/svgs/todo_toggle.svg diff --git a/src/shared/assets/svgs/triangle.svg b/src/app/shared/assets/svgs/triangle.svg similarity index 100% rename from src/shared/assets/svgs/triangle.svg rename to src/app/shared/assets/svgs/triangle.svg diff --git a/src/shared/assets/svgs/upIcon.svg b/src/app/shared/assets/svgs/upIcon.svg similarity index 100% rename from src/shared/assets/svgs/upIcon.svg rename to src/app/shared/assets/svgs/upIcon.svg diff --git a/src/shared/assets/svgs/user_circle.svg b/src/app/shared/assets/svgs/user_circle.svg similarity index 100% rename from src/shared/assets/svgs/user_circle.svg rename to src/app/shared/assets/svgs/user_circle.svg diff --git a/src/shared/components/AutoFixedGrid/AutoFixedGrid.tsx b/src/app/shared/components/AutoFixedGrid/AutoFixedGrid.tsx similarity index 100% rename from src/shared/components/AutoFixedGrid/AutoFixedGrid.tsx rename to src/app/shared/components/AutoFixedGrid/AutoFixedGrid.tsx diff --git a/src/shared/components/BoxTodo/BoxTodo.tsx b/src/app/shared/components/BoxTodo/BoxTodo.tsx similarity index 100% rename from src/shared/components/BoxTodo/BoxTodo.tsx rename to src/app/shared/components/BoxTodo/BoxTodo.tsx diff --git a/src/shared/components/ButtonArrowSVG/ButtonArrowSVG.tsx b/src/app/shared/components/ButtonArrowSVG/ButtonArrowSVG.tsx similarity index 100% rename from src/shared/components/ButtonArrowSVG/ButtonArrowSVG.tsx rename to src/app/shared/components/ButtonArrowSVG/ButtonArrowSVG.tsx diff --git a/src/shared/components/ButtonDropdownOptions/ButtonDropdownOptions.tsx b/src/app/shared/components/ButtonDropdownOptions/ButtonDropdownOptions.tsx similarity index 100% rename from src/shared/components/ButtonDropdownOptions/ButtonDropdownOptions.tsx rename to src/app/shared/components/ButtonDropdownOptions/ButtonDropdownOptions.tsx diff --git a/src/shared/components/ButtonHomeLarge/ButtonHomeLarge.tsx b/src/app/shared/components/ButtonHomeLarge/ButtonHomeLarge.tsx similarity index 100% rename from src/shared/components/ButtonHomeLarge/ButtonHomeLarge.tsx rename to src/app/shared/components/ButtonHomeLarge/ButtonHomeLarge.tsx diff --git a/src/shared/components/ButtonRadius5/ButtonRadius5.tsx b/src/app/shared/components/ButtonRadius5/ButtonRadius5.tsx similarity index 100% rename from src/shared/components/ButtonRadius5/ButtonRadius5.tsx rename to src/app/shared/components/ButtonRadius5/ButtonRadius5.tsx diff --git a/src/shared/components/ButtonRadius8/ButtonRadius8.tsx b/src/app/shared/components/ButtonRadius8/ButtonRadius8.tsx similarity index 100% rename from src/shared/components/ButtonRadius8/ButtonRadius8.tsx rename to src/app/shared/components/ButtonRadius8/ButtonRadius8.tsx diff --git a/src/shared/components/ButtonStatusToggle/ButtonStatusToggle.tsx b/src/app/shared/components/ButtonStatusToggle/ButtonStatusToggle.tsx similarity index 100% rename from src/shared/components/ButtonStatusToggle/ButtonStatusToggle.tsx rename to src/app/shared/components/ButtonStatusToggle/ButtonStatusToggle.tsx diff --git a/src/shared/components/ButtonTodayToggle/ButtonTodoToggle.tsx b/src/app/shared/components/ButtonTodayToggle/ButtonTodoToggle.tsx similarity index 100% rename from src/shared/components/ButtonTodayToggle/ButtonTodoToggle.tsx rename to src/app/shared/components/ButtonTodayToggle/ButtonTodoToggle.tsx diff --git a/src/shared/components/Calendar/ButtonCalendarAddRoutine/ButtonCalendarAddRoutine.tsx b/src/app/shared/components/Calendar/ButtonCalendarAddRoutine/ButtonCalendarAddRoutine.tsx similarity index 100% rename from src/shared/components/Calendar/ButtonCalendarAddRoutine/ButtonCalendarAddRoutine.tsx rename to src/app/shared/components/Calendar/ButtonCalendarAddRoutine/ButtonCalendarAddRoutine.tsx diff --git a/src/shared/components/Calendar/Calendar.tsx b/src/app/shared/components/Calendar/Calendar.tsx similarity index 100% rename from src/shared/components/Calendar/Calendar.tsx rename to src/app/shared/components/Calendar/Calendar.tsx diff --git a/src/shared/components/Calendar/HeaderCalendar/HeaderCalendar.tsx b/src/app/shared/components/Calendar/HeaderCalendar/HeaderCalendar.tsx similarity index 89% rename from src/shared/components/Calendar/HeaderCalendar/HeaderCalendar.tsx rename to src/app/shared/components/Calendar/HeaderCalendar/HeaderCalendar.tsx index 698adf7d..8d0bcd80 100644 --- a/src/shared/components/Calendar/HeaderCalendar/HeaderCalendar.tsx +++ b/src/app/shared/components/Calendar/HeaderCalendar/HeaderCalendar.tsx @@ -27,7 +27,7 @@ const HeaderCalendar: React.FC = ({ return (
-

+

{year}년 {month}월

-
+
{WEEK_DAYS.map((day) => ( {day} ))} diff --git a/src/shared/components/Calendar/calendar.css b/src/app/shared/components/Calendar/calendar.css similarity index 100% rename from src/shared/components/Calendar/calendar.css rename to src/app/shared/components/Calendar/calendar.css diff --git a/src/shared/components/CircleColorIcon/CircleColorIcon.tsx b/src/app/shared/components/CircleColorIcon/CircleColorIcon.tsx similarity index 100% rename from src/shared/components/CircleColorIcon/CircleColorIcon.tsx rename to src/app/shared/components/CircleColorIcon/CircleColorIcon.tsx diff --git a/src/shared/components/ColorPallete/ColorPallete.tsx b/src/app/shared/components/ColorPallete/ColorPallete.tsx similarity index 100% rename from src/shared/components/ColorPallete/ColorPallete.tsx rename to src/app/shared/components/ColorPallete/ColorPallete.tsx diff --git a/src/shared/components/Dropdown/Dropdown.tsx b/src/app/shared/components/Dropdown/Dropdown.tsx similarity index 100% rename from src/shared/components/Dropdown/Dropdown.tsx rename to src/app/shared/components/Dropdown/Dropdown.tsx diff --git a/src/shared/components/ErrorBoundary/ErrorBoundary.tsx b/src/app/shared/components/ErrorBoundary/ErrorBoundary.tsx similarity index 93% rename from src/shared/components/ErrorBoundary/ErrorBoundary.tsx rename to src/app/shared/components/ErrorBoundary/ErrorBoundary.tsx index 84ad7c26..58f08185 100644 --- a/src/shared/components/ErrorBoundary/ErrorBoundary.tsx +++ b/src/app/shared/components/ErrorBoundary/ErrorBoundary.tsx @@ -4,8 +4,7 @@ import { Component, ErrorInfo, ReactNode } from 'react'; import { AxiosError, isAxiosError } from 'axios'; -import { getErrorCategory, shouldShowFallbackUI } from '@/shared/utils/error'; -import { getErrorMessage } from '@/shared/utils/error'; +import { getErrorCategory, getErrorMessage, shouldShowFallbackUI } from '@/shared/utils/error'; import FallbackApiError from '../FallbackApiError/FallbackApiError'; diff --git a/src/shared/components/FallbackApiError/FallbackApiError.tsx b/src/app/shared/components/FallbackApiError/FallbackApiError.tsx similarity index 100% rename from src/shared/components/FallbackApiError/FallbackApiError.tsx rename to src/app/shared/components/FallbackApiError/FallbackApiError.tsx diff --git a/src/shared/components/HeartBeatBoundary/HeartBeatBoundary.tsx b/src/app/shared/components/HeartBeatBoundary/HeartBeatBoundary.tsx similarity index 100% rename from src/shared/components/HeartBeatBoundary/HeartBeatBoundary.tsx rename to src/app/shared/components/HeartBeatBoundary/HeartBeatBoundary.tsx diff --git a/src/shared/components/LoadingOverlay/LoadingOverlay.tsx b/src/app/shared/components/LoadingOverlay/LoadingOverlay.tsx similarity index 100% rename from src/shared/components/LoadingOverlay/LoadingOverlay.tsx rename to src/app/shared/components/LoadingOverlay/LoadingOverlay.tsx diff --git a/src/shared/components/ModalContentsFriends/FriendRequest/ButtonRequestAction/ButtonRequestAction.tsx b/src/app/shared/components/ModalContentsFriends/FriendRequest/ButtonRequestAction/ButtonRequestAction.tsx similarity index 100% rename from src/shared/components/ModalContentsFriends/FriendRequest/ButtonRequestAction/ButtonRequestAction.tsx rename to src/app/shared/components/ModalContentsFriends/FriendRequest/ButtonRequestAction/ButtonRequestAction.tsx diff --git a/src/shared/components/ModalContentsFriends/FriendRequest/FriendsListRequested/FriendsListRequested.tsx b/src/app/shared/components/ModalContentsFriends/FriendRequest/FriendsListRequested/FriendsListRequested.tsx similarity index 100% rename from src/shared/components/ModalContentsFriends/FriendRequest/FriendsListRequested/FriendsListRequested.tsx rename to src/app/shared/components/ModalContentsFriends/FriendRequest/FriendsListRequested/FriendsListRequested.tsx diff --git a/src/shared/components/ModalContentsFriends/FriendRequest/FriendsRequest.tsx b/src/app/shared/components/ModalContentsFriends/FriendRequest/FriendsRequest.tsx similarity index 100% rename from src/shared/components/ModalContentsFriends/FriendRequest/FriendsRequest.tsx rename to src/app/shared/components/ModalContentsFriends/FriendRequest/FriendsRequest.tsx diff --git a/src/shared/components/ModalContentsFriends/FriendUserProfile/FriendUserProfile.tsx b/src/app/shared/components/ModalContentsFriends/FriendUserProfile/FriendUserProfile.tsx similarity index 100% rename from src/shared/components/ModalContentsFriends/FriendUserProfile/FriendUserProfile.tsx rename to src/app/shared/components/ModalContentsFriends/FriendUserProfile/FriendUserProfile.tsx diff --git a/src/shared/components/ModalContentsFriends/FriendsList/FriendsInfo/FriendInfo.tsx b/src/app/shared/components/ModalContentsFriends/FriendsList/FriendsInfo/FriendInfo.tsx similarity index 100% rename from src/shared/components/ModalContentsFriends/FriendsList/FriendsInfo/FriendInfo.tsx rename to src/app/shared/components/ModalContentsFriends/FriendsList/FriendsInfo/FriendInfo.tsx diff --git a/src/shared/components/ModalContentsFriends/FriendsList/FriendsList.tsx b/src/app/shared/components/ModalContentsFriends/FriendsList/FriendsList.tsx similarity index 100% rename from src/shared/components/ModalContentsFriends/FriendsList/FriendsList.tsx rename to src/app/shared/components/ModalContentsFriends/FriendsList/FriendsList.tsx diff --git a/src/shared/components/ModalContentsFriends/ModalContentsFriends.tsx b/src/app/shared/components/ModalContentsFriends/ModalContentsFriends.tsx similarity index 100% rename from src/shared/components/ModalContentsFriends/ModalContentsFriends.tsx rename to src/app/shared/components/ModalContentsFriends/ModalContentsFriends.tsx diff --git a/src/shared/components/ModalWrapper/ModalWrapper.tsx b/src/app/shared/components/ModalWrapper/ModalWrapper.tsx similarity index 100% rename from src/shared/components/ModalWrapper/ModalWrapper.tsx rename to src/app/shared/components/ModalWrapper/ModalWrapper.tsx diff --git a/src/shared/components/ModalWrapper/styles/dialog.css b/src/app/shared/components/ModalWrapper/styles/dialog.css similarity index 100% rename from src/shared/components/ModalWrapper/styles/dialog.css rename to src/app/shared/components/ModalWrapper/styles/dialog.css diff --git a/src/shared/components/Portal/Portal.tsx b/src/app/shared/components/Portal/Portal.tsx similarity index 100% rename from src/shared/components/Portal/Portal.tsx rename to src/app/shared/components/Portal/Portal.tsx diff --git a/src/shared/components/Spacer/Spacer.tsx b/src/app/shared/components/Spacer/Spacer.tsx similarity index 100% rename from src/shared/components/Spacer/Spacer.tsx rename to src/app/shared/components/Spacer/Spacer.tsx diff --git a/src/shared/components/TextField/TextField.tsx b/src/app/shared/components/TextField/TextField.tsx similarity index 100% rename from src/shared/components/TextField/TextField.tsx rename to src/app/shared/components/TextField/TextField.tsx diff --git a/src/shared/constants/btnText.ts b/src/app/shared/constants/btnText.ts similarity index 100% rename from src/shared/constants/btnText.ts rename to src/app/shared/constants/btnText.ts diff --git a/src/shared/constants/colorPalette.ts b/src/app/shared/constants/colorPalette.ts similarity index 100% rename from src/shared/constants/colorPalette.ts rename to src/app/shared/constants/colorPalette.ts diff --git a/src/shared/constants/emailRegex.ts b/src/app/shared/constants/emailRegex.ts similarity index 100% rename from src/shared/constants/emailRegex.ts rename to src/app/shared/constants/emailRegex.ts diff --git a/src/shared/constants/error.ts b/src/app/shared/constants/error.ts similarity index 100% rename from src/shared/constants/error.ts rename to src/app/shared/constants/error.ts diff --git a/src/shared/constants/fields.ts b/src/app/shared/constants/fields.ts similarity index 100% rename from src/shared/constants/fields.ts rename to src/app/shared/constants/fields.ts diff --git a/src/shared/constants/suggestedSites.ts b/src/app/shared/constants/suggestedSites.ts similarity index 100% rename from src/shared/constants/suggestedSites.ts rename to src/app/shared/constants/suggestedSites.ts diff --git a/src/shared/constants/timerPageText.ts b/src/app/shared/constants/timerPageText.ts similarity index 100% rename from src/shared/constants/timerPageText.ts rename to src/app/shared/constants/timerPageText.ts diff --git a/src/shared/constants/weekDays.ts b/src/app/shared/constants/weekDays.ts similarity index 100% rename from src/shared/constants/weekDays.ts rename to src/app/shared/constants/weekDays.ts diff --git a/src/shared/hocs/withAuthProtection.tsx b/src/app/shared/hocs/withAuthProtection.tsx similarity index 100% rename from src/shared/hocs/withAuthProtection.tsx rename to src/app/shared/hocs/withAuthProtection.tsx diff --git a/src/shared/hooks/useCarousel.ts b/src/app/shared/hooks/useCarousel.ts similarity index 100% rename from src/shared/hooks/useCarousel.ts rename to src/app/shared/hooks/useCarousel.ts diff --git a/src/shared/hooks/useClickOutside.ts b/src/app/shared/hooks/useClickOutside.ts similarity index 100% rename from src/shared/hooks/useClickOutside.ts rename to src/app/shared/hooks/useClickOutside.ts diff --git a/src/shared/layout/Layout.tsx b/src/app/shared/layout/Layout.tsx similarity index 100% rename from src/shared/layout/Layout.tsx rename to src/app/shared/layout/Layout.tsx diff --git a/src/shared/layout/Sidebar/ModalContentsSetting/AccountContent/AccountContent.tsx b/src/app/shared/layout/Sidebar/ModalContentsSetting/AccountContent/AccountContent.tsx similarity index 100% rename from src/shared/layout/Sidebar/ModalContentsSetting/AccountContent/AccountContent.tsx rename to src/app/shared/layout/Sidebar/ModalContentsSetting/AccountContent/AccountContent.tsx diff --git a/src/shared/layout/Sidebar/ModalContentsSetting/ModalContentsSetting.tsx b/src/app/shared/layout/Sidebar/ModalContentsSetting/ModalContentsSetting.tsx similarity index 100% rename from src/shared/layout/Sidebar/ModalContentsSetting/ModalContentsSetting.tsx rename to src/app/shared/layout/Sidebar/ModalContentsSetting/ModalContentsSetting.tsx diff --git a/src/shared/layout/Sidebar/ModalContentsSetting/Tabs/Tabs.tsx b/src/app/shared/layout/Sidebar/ModalContentsSetting/Tabs/Tabs.tsx similarity index 100% rename from src/shared/layout/Sidebar/ModalContentsSetting/Tabs/Tabs.tsx rename to src/app/shared/layout/Sidebar/ModalContentsSetting/Tabs/Tabs.tsx diff --git a/src/shared/layout/Sidebar/ModalContentsSetting/WorkspaceSettingContent/WorkspaceSettingContent.tsx b/src/app/shared/layout/Sidebar/ModalContentsSetting/WorkspaceSettingContent/WorkspaceSettingContent.tsx similarity index 100% rename from src/shared/layout/Sidebar/ModalContentsSetting/WorkspaceSettingContent/WorkspaceSettingContent.tsx rename to src/app/shared/layout/Sidebar/ModalContentsSetting/WorkspaceSettingContent/WorkspaceSettingContent.tsx diff --git a/src/shared/layout/Sidebar/Sidebar.tsx b/src/app/shared/layout/Sidebar/Sidebar.tsx similarity index 100% rename from src/shared/layout/Sidebar/Sidebar.tsx rename to src/app/shared/layout/Sidebar/Sidebar.tsx diff --git a/src/shared/mocks/categoryData.ts b/src/app/shared/mocks/categoryData.ts similarity index 100% rename from src/shared/mocks/categoryData.ts rename to src/app/shared/mocks/categoryData.ts diff --git a/src/shared/mocks/faviconData.ts b/src/app/shared/mocks/faviconData.ts similarity index 100% rename from src/shared/mocks/faviconData.ts rename to src/app/shared/mocks/faviconData.ts diff --git a/src/shared/mocks/homeData.ts b/src/app/shared/mocks/homeData.ts similarity index 100% rename from src/shared/mocks/homeData.ts rename to src/app/shared/mocks/homeData.ts diff --git a/src/shared/mocks/urlData.ts b/src/app/shared/mocks/urlData.ts similarity index 100% rename from src/shared/mocks/urlData.ts rename to src/app/shared/mocks/urlData.ts diff --git a/src/shared/mocks/userData.ts b/src/app/shared/mocks/userData.ts similarity index 100% rename from src/shared/mocks/userData.ts rename to src/app/shared/mocks/userData.ts diff --git a/src/shared/types/SSEEvent.ts b/src/app/shared/types/SSEEvent.ts similarity index 100% rename from src/shared/types/SSEEvent.ts rename to src/app/shared/types/SSEEvent.ts diff --git a/src/shared/types/allowedService.ts b/src/app/shared/types/allowedService.ts similarity index 100% rename from src/shared/types/allowedService.ts rename to src/app/shared/types/allowedService.ts diff --git a/src/shared/types/allowedSites.ts b/src/app/shared/types/allowedSites.ts similarity index 100% rename from src/shared/types/allowedSites.ts rename to src/app/shared/types/allowedSites.ts diff --git a/src/shared/types/api/allowedService.ts b/src/app/shared/types/api/allowedService.ts similarity index 100% rename from src/shared/types/api/allowedService.ts rename to src/app/shared/types/api/allowedService.ts diff --git a/src/shared/types/api/auth.ts b/src/app/shared/types/api/auth.ts similarity index 100% rename from src/shared/types/api/auth.ts rename to src/app/shared/types/api/auth.ts diff --git a/src/shared/types/api/common.ts b/src/app/shared/types/api/common.ts similarity index 100% rename from src/shared/types/api/common.ts rename to src/app/shared/types/api/common.ts diff --git a/src/shared/types/api/error.ts b/src/app/shared/types/api/error.ts similarity index 100% rename from src/shared/types/api/error.ts rename to src/app/shared/types/api/error.ts diff --git a/src/shared/types/api/friends.ts b/src/app/shared/types/api/friends.ts similarity index 100% rename from src/shared/types/api/friends.ts rename to src/app/shared/types/api/friends.ts diff --git a/src/shared/types/api/home.ts b/src/app/shared/types/api/home.ts similarity index 100% rename from src/shared/types/api/home.ts rename to src/app/shared/types/api/home.ts diff --git a/src/shared/types/api/onboarding.ts b/src/app/shared/types/api/onboarding.ts similarity index 100% rename from src/shared/types/api/onboarding.ts rename to src/app/shared/types/api/onboarding.ts diff --git a/src/shared/types/api/setting.ts b/src/app/shared/types/api/setting.ts similarity index 100% rename from src/shared/types/api/setting.ts rename to src/app/shared/types/api/setting.ts diff --git a/src/shared/types/api/timer.ts b/src/app/shared/types/api/timer.ts similarity index 100% rename from src/shared/types/api/timer.ts rename to src/app/shared/types/api/timer.ts diff --git a/src/shared/types/common/index.tsx b/src/app/shared/types/common/index.tsx similarity index 100% rename from src/shared/types/common/index.tsx rename to src/app/shared/types/common/index.tsx diff --git a/src/shared/types/fileds.ts b/src/app/shared/types/fileds.ts similarity index 100% rename from src/shared/types/fileds.ts rename to src/app/shared/types/fileds.ts diff --git a/src/shared/types/friend.ts b/src/app/shared/types/friend.ts similarity index 100% rename from src/shared/types/friend.ts rename to src/app/shared/types/friend.ts diff --git a/src/shared/types/global.ts b/src/app/shared/types/global.ts similarity index 100% rename from src/shared/types/global.ts rename to src/app/shared/types/global.ts diff --git a/src/shared/types/home/index.tsx b/src/app/shared/types/home/index.tsx similarity index 100% rename from src/shared/types/home/index.tsx rename to src/app/shared/types/home/index.tsx diff --git a/src/shared/types/profile.ts b/src/app/shared/types/profile.ts similarity index 100% rename from src/shared/types/profile.ts rename to src/app/shared/types/profile.ts diff --git a/src/shared/types/tasks.ts b/src/app/shared/types/tasks.ts similarity index 100% rename from src/shared/types/tasks.ts rename to src/app/shared/types/tasks.ts diff --git a/src/shared/types/todoData.ts b/src/app/shared/types/todoData.ts similarity index 100% rename from src/shared/types/todoData.ts rename to src/app/shared/types/todoData.ts diff --git a/src/shared/types/userData.ts b/src/app/shared/types/userData.ts similarity index 100% rename from src/shared/types/userData.ts rename to src/app/shared/types/userData.ts diff --git a/src/shared/utils/auth.ts b/src/app/shared/utils/auth.ts similarity index 100% rename from src/shared/utils/auth.ts rename to src/app/shared/utils/auth.ts diff --git a/src/shared/utils/calendar/index.ts b/src/app/shared/utils/calendar/index.ts similarity index 100% rename from src/shared/utils/calendar/index.ts rename to src/app/shared/utils/calendar/index.ts diff --git a/src/shared/utils/date/index.ts b/src/app/shared/utils/date/index.ts similarity index 100% rename from src/shared/utils/date/index.ts rename to src/app/shared/utils/date/index.ts diff --git a/src/shared/utils/error.ts b/src/app/shared/utils/error.ts similarity index 100% rename from src/shared/utils/error.ts rename to src/app/shared/utils/error.ts diff --git a/src/shared/utils/path.ts b/src/app/shared/utils/path.ts similarity index 100% rename from src/shared/utils/path.ts rename to src/app/shared/utils/path.ts diff --git a/src/shared/utils/tasks.ts b/src/app/shared/utils/tasks.ts similarity index 100% rename from src/shared/utils/tasks.ts rename to src/app/shared/utils/tasks.ts diff --git a/src/shared/utils/time/index.ts b/src/app/shared/utils/time/index.ts similarity index 100% rename from src/shared/utils/time/index.ts rename to src/app/shared/utils/time/index.ts diff --git a/src/shared/utils/timer/index.ts b/src/app/shared/utils/timer/index.ts similarity index 100% rename from src/shared/utils/timer/index.ts rename to src/app/shared/utils/timer/index.ts diff --git a/src/shared/utils/url.ts b/src/app/shared/utils/url.ts similarity index 100% rename from src/shared/utils/url.ts rename to src/app/shared/utils/url.ts diff --git a/src/shared/utils/url/index.ts b/src/app/shared/utils/url/index.ts similarity index 100% rename from src/shared/utils/url/index.ts rename to src/app/shared/utils/url/index.ts diff --git a/src/shared/utils/validation.ts b/src/app/shared/utils/validation.ts similarity index 100% rename from src/shared/utils/validation.ts rename to src/app/shared/utils/validation.ts diff --git a/src/vite-env.d.ts b/src/app/vite-env.d.ts similarity index 100% rename from src/vite-env.d.ts rename to src/app/vite-env.d.ts diff --git a/src/electron/main.ts b/src/electron/main.ts new file mode 100644 index 00000000..b4cced79 --- /dev/null +++ b/src/electron/main.ts @@ -0,0 +1,20 @@ +import { BrowserWindow, app } from 'electron'; +import path from 'path'; + +import { getPreloadPath } from './pathResolver.js'; +import { isDev } from './util.js'; + +app.whenReady().then(() => { + const mainWindow = new BrowserWindow({ + webPreferences: { + preload: getPreloadPath(), + }, + width: 1280, + height: 920, + }); + if (isDev()) { + mainWindow.loadURL('http://localhost:5123'); + } else { + mainWindow.loadFile(path.join(app.getAppPath(), 'dist-react/index.html')); + } +}); diff --git a/src/electron/pathResolver.ts b/src/electron/pathResolver.ts new file mode 100644 index 00000000..c8c89651 --- /dev/null +++ b/src/electron/pathResolver.ts @@ -0,0 +1,8 @@ +import { app } from 'electron'; +import path from 'path'; + +import { isDev } from './util.js'; + +export function getPreloadPath() { + return path.join(app.getAppPath(), isDev() ? '.' : '..', '/dist-electron/preload.cjs'); +} diff --git a/src/electron/preload.cts b/src/electron/preload.cts new file mode 100644 index 00000000..37812d98 --- /dev/null +++ b/src/electron/preload.cts @@ -0,0 +1,8 @@ +const electron = require('electron'); + +electron.contextBridge.exposeInMainWorld('electron', { + subscribeStatus: (callback: (statistic: any) => void) => callback({}), + getStatisticData: () => { + console.log('static'); + }, +}); diff --git a/src/electron/tsconfig.json b/src/electron/tsconfig.json new file mode 100644 index 00000000..88b155c5 --- /dev/null +++ b/src/electron/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "strict": false, + "target": "ESNext", + "module": "NodeNext", + "outDir": "../../dist-electron", + "skipLibCheck": true, + } +} \ No newline at end of file diff --git a/src/electron/util.ts b/src/electron/util.ts new file mode 100644 index 00000000..9db04899 --- /dev/null +++ b/src/electron/util.ts @@ -0,0 +1,3 @@ +export function isDev(): boolean { + return process.env.NODE_ENV === 'development'; +} diff --git a/tsconfig.app.json b/tsconfig.app.json index 497f2255..7efe41b9 100644 --- a/tsconfig.app.json +++ b/tsconfig.app.json @@ -26,7 +26,7 @@ /* 절대경로 */ "baseUrl": ".", "paths": { - "@/*": ["src/*"] + "@/*": ["src/app/*"] }, /*svg 모듈 */ diff --git a/tsconfig.node.json b/tsconfig.node.json index 6dc4f9d6..9375319c 100644 --- a/tsconfig.node.json +++ b/tsconfig.node.json @@ -12,7 +12,7 @@ /* 절대경로 */ "baseUrl": ".", "paths": { - "@/*": ["src/*"] + "@/*": ["src/app/*"] }, /*svg 모듈 */ diff --git a/vite.config.ts b/vite.config.ts index 3008826a..7ca7a57c 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -6,6 +6,14 @@ import svgr from 'vite-plugin-svgr'; export default defineConfig({ plugins: [react(), svgr()], resolve: { - alias: [{ find: '@', replacement: '/src' }], + alias: [{ find: '@', replacement: '/src/app' }], + }, + base: './', + build: { + outDir: 'dist-react', + }, + server: { + port: 5123, + strictPort: true, }, }); From d8b78ceea290e8f31a2b68d7506a09ae7e33ed8c Mon Sep 17 00:00:00 2001 From: 10tentacion Date: Sat, 3 May 2025 17:12:47 +0900 Subject: [PATCH 02/30] =?UTF-8?q?feat:=20=EC=B5=9C=EC=8B=A0=20develop=20?= =?UTF-8?q?=EC=82=AC=ED=95=AD=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/App.tsx | 6 - .../AllowedServiceGroupDetailContent.tsx | 11 +- .../AllowedServiceList/AllowedServiceList.tsx | 3 +- .../AllowedServicePage/AllowedServicePage.tsx | 43 ++- .../HomePage/BoxCategory/BoxCategory.tsx | 260 +++++++++-------- .../StatusDefaultBoxCategory.tsx | 8 +- .../StatusAddBoxTodayTodo.tsx | 45 ++- .../StatusDefaultBoxTodayTodo.tsx | 10 +- .../ButtonMoreFriends/ButtonMoreFriends.tsx | 4 +- .../ButtonUserProfile/ButtonUserProfile.tsx | 21 +- .../pages/HomePage/DatePicker/DatePicker.tsx | 31 ++- src/app/pages/HomePage/HomePage.tsx | 163 ++++++++++- .../ButtonAlert/ButtonAlert.tsx | 13 +- .../RegisterAllowedService.tsx | 21 ++ .../TimerRestriction/TimerRestriction.tsx | 16 ++ .../TooltipFriendInfo/TooltipFriendInfo.tsx | 61 ++++ src/app/pages/LoginPage/LoginPage.tsx | 7 - .../OnboardingPage/ButtonSkip/ButtonSkip.tsx | 10 + .../pages/OnboardingPage/OnboardingPage.tsx | 4 +- src/app/pages/RedirectPage/RedirectPage.tsx | 20 +- .../AllowedServices/AllowedServicesItem.tsx | 32 +++ .../AllowedServicesPopover.tsx | 150 ++++++++++ .../AllowedServicesTitle.tsx} | 24 +- .../AllowedServicesTooltip.tsx} | 0 .../Checkbox => AllowedServices}/Checkbox.tsx | 8 +- src/app/pages/TimerPage/Carousel/Carousel.tsx | 14 +- .../TimerPage/Carousel/CarouselFriend.tsx | 74 +++++ .../ContainerCarousel/ContainerCarousel.tsx | 54 ---- .../hooks/useCarouselTimer.ts | 28 -- .../pages/TimerPage/MainTimer/MainTimer.tsx | 53 ++++ .../ButtonTimerPlay/ButtonTimerPlay.tsx | 0 .../ProgressCircle/ProgressCircle.tsx | 0 .../MainTimer/TimerDisplay/TimerDisplay.tsx | 41 +++ .../MainTimer/TimerHeader/TimerHeader.tsx | 34 +++ .../NavigationButtons/NavigationButtons.tsx | 27 ++ .../PopoverAllowedService.tsx | 143 ---------- .../TimerPage/SideMenuTimer/SideMenuTimer.tsx | 163 +++++++++++ .../TimerPage/SidebarTimer/SideBarTimer.tsx | 170 ----------- src/app/pages/TimerPage/Timer/Timer.tsx | 108 ------- src/app/pages/TimerPage/TimerPage.tsx | 263 +++++------------- .../pages/TimerPage/contexts/TimerContext.tsx | 113 ++++++++ .../TimerPage/hooks/useAllowedServices.ts | 52 ++++ .../pages/TimerPage/hooks/useTimerActions.ts | 174 ++++++++++++ .../pages/TimerPage/hooks/useTimerCount.ts | 40 ++- .../pages/TimerPage/hooks/useTimerState.ts | 139 +++++++++ .../pages/TimerPage/hooks/useToggleSidebar.ts | 14 - src/app/pages/TimerPage/hooks/useUIState.ts | 47 ++++ .../pages/TimerPage/hooks/useUrlHandler.ts | 93 ++++--- src/app/pages/TimerPage/utils/timeFormat.ts | 52 ++++ src/app/router/Router.tsx | 4 +- .../allowedService/allowedService.api.ts | 9 +- .../allowedService/allowedService.keys.ts | 9 +- .../allowedService/allowedService.queries.ts | 12 +- src/app/shared/apisV2/client.ts | 1 + .../apisV2/onboarding/onboarding.mutations.ts | 2 - src/app/shared/apisV2/timer/timer.api.ts | 49 +++- src/app/shared/apisV2/timer/timer.keys.ts | 12 +- .../shared/apisV2/timer/timer.mutations.ts | 28 +- src/app/shared/apisV2/timer/timer.queries.ts | 62 ++++- src/app/shared/assets/svgs/btn_cal_black.svg | 9 + src/app/shared/assets/svgs/ic_line.svg | 3 + .../assets/svgs/popover_add_category.svg | 5 + .../shared/assets/svgs/popover_add_todo.svg | 5 + .../shared/assets/svgs/tooltip_triangle.svg | 3 + src/app/shared/components/BoxTodo/BoxTodo.tsx | 31 ++- .../HeaderCalendar/HeaderCalendar.tsx | 4 +- .../shared/components/Dropdown/Dropdown.tsx | 59 ++-- .../ErrorBoundary/ErrorBoundary.tsx | 3 +- .../components/FaviconImage/FaviconImage.tsx | 19 ++ .../FriendsList/FriendsInfo/FriendInfo.tsx | 2 +- .../NotificationPanel/NotificationPanel.tsx | 20 ++ src/app/shared/hooks/useClickOutside.ts | 8 +- src/app/shared/layout/Sidebar/Sidebar.tsx | 2 +- src/app/shared/types/api/allowedService.ts | 4 + src/app/shared/types/api/timer.ts | 33 ++- src/app/shared/utils/auth.ts | 9 + 76 files changed, 2197 insertions(+), 1077 deletions(-) create mode 100644 src/app/pages/HomePage/ModalContentsAlert/RegisterAllowedService/RegisterAllowedService.tsx create mode 100644 src/app/pages/HomePage/ModalContentsAlert/TimerRestriction/TimerRestriction.tsx create mode 100644 src/app/pages/HomePage/TooltipFriendInfo/TooltipFriendInfo.tsx create mode 100644 src/app/pages/TimerPage/AllowedServices/AllowedServicesItem.tsx create mode 100644 src/app/pages/TimerPage/AllowedServices/AllowedServicesPopover.tsx rename src/app/pages/TimerPage/{TItleAllowedService/TitleAllowedService.tsx => AllowedServices/AllowedServicesTitle.tsx} (63%) rename src/app/pages/TimerPage/{TItleAllowedService/TooltipAllowedService/ToolTipAllowedService.tsx => AllowedServices/AllowedServicesTooltip.tsx} (100%) rename src/app/pages/TimerPage/{PopoverAllowedService/Checkbox => AllowedServices}/Checkbox.tsx (79%) create mode 100644 src/app/pages/TimerPage/Carousel/CarouselFriend.tsx delete mode 100644 src/app/pages/TimerPage/Carousel/ContainerCarousel/ContainerCarousel.tsx delete mode 100644 src/app/pages/TimerPage/Carousel/ContainerCarousel/hooks/useCarouselTimer.ts create mode 100644 src/app/pages/TimerPage/MainTimer/MainTimer.tsx rename src/app/pages/TimerPage/{Timer => MainTimer/TimerDisplay}/ButtonTimerPlay/ButtonTimerPlay.tsx (100%) rename src/app/pages/TimerPage/{Timer => MainTimer/TimerDisplay}/ProgressCircle/ProgressCircle.tsx (100%) create mode 100644 src/app/pages/TimerPage/MainTimer/TimerDisplay/TimerDisplay.tsx create mode 100644 src/app/pages/TimerPage/MainTimer/TimerHeader/TimerHeader.tsx create mode 100644 src/app/pages/TimerPage/NavigationButtons/NavigationButtons.tsx delete mode 100644 src/app/pages/TimerPage/PopoverAllowedService/PopoverAllowedService.tsx create mode 100644 src/app/pages/TimerPage/SideMenuTimer/SideMenuTimer.tsx delete mode 100644 src/app/pages/TimerPage/SidebarTimer/SideBarTimer.tsx delete mode 100644 src/app/pages/TimerPage/Timer/Timer.tsx create mode 100644 src/app/pages/TimerPage/contexts/TimerContext.tsx create mode 100644 src/app/pages/TimerPage/hooks/useAllowedServices.ts create mode 100644 src/app/pages/TimerPage/hooks/useTimerActions.ts create mode 100644 src/app/pages/TimerPage/hooks/useTimerState.ts delete mode 100644 src/app/pages/TimerPage/hooks/useToggleSidebar.ts create mode 100644 src/app/pages/TimerPage/hooks/useUIState.ts create mode 100644 src/app/pages/TimerPage/utils/timeFormat.ts create mode 100644 src/app/shared/assets/svgs/btn_cal_black.svg create mode 100644 src/app/shared/assets/svgs/ic_line.svg create mode 100644 src/app/shared/assets/svgs/popover_add_category.svg create mode 100644 src/app/shared/assets/svgs/popover_add_todo.svg create mode 100644 src/app/shared/assets/svgs/tooltip_triangle.svg create mode 100644 src/app/shared/components/FaviconImage/FaviconImage.tsx create mode 100644 src/app/shared/components/NotificationPanel/NotificationPanel.tsx diff --git a/src/app/App.tsx b/src/app/App.tsx index 6870c48e..019d04f3 100644 --- a/src/app/App.tsx +++ b/src/app/App.tsx @@ -10,12 +10,6 @@ import { queryClient } from '@/shared/apisV2/queryClient'; import router from './router/Router'; const App = () => { - // @ts-expect-error test - if (window.electron) { - // @ts-expect-error test - window.electron.getStatisticData(); - } - return (
diff --git a/src/app/pages/AllowedServicePage/AllowedServiceGroupDetail/Content/AllowedServiceGroupDetailContent.tsx b/src/app/pages/AllowedServicePage/AllowedServiceGroupDetail/Content/AllowedServiceGroupDetailContent.tsx index bbc7d3a1..b5987014 100644 --- a/src/app/pages/AllowedServicePage/AllowedServiceGroupDetail/Content/AllowedServiceGroupDetailContent.tsx +++ b/src/app/pages/AllowedServicePage/AllowedServiceGroupDetail/Content/AllowedServiceGroupDetailContent.tsx @@ -1,15 +1,12 @@ import { ReactNode } from 'react'; +import FaviconImage from '@/shared/components/FaviconImage/FaviconImage'; import Spacer from '@/shared/components/Spacer/Spacer'; -import { getMainDomain } from '@/shared/utils/url'; - import { AllowedServiceGroupDetailSiteType } from '@/shared/types/allowedService'; import MinusBtn from '@/shared/assets/svgs/minus_btn.svg?react'; -import { getServiceFavicon } from '@/pages/OnboardingPage/utils/serviceUrl'; - export interface AllowedServiceGroupDetailContentProps { children: ReactNode; } @@ -65,7 +62,11 @@ export const AllowedServiceGroupDetailContentTableRow = ({ return (
- favicon +

{allowedSiteData.siteName}

diff --git a/src/app/pages/AllowedServicePage/AllowedServiceList/AllowedServiceList.tsx b/src/app/pages/AllowedServicePage/AllowedServiceList/AllowedServiceList.tsx index 6aa008c8..25017fee 100644 --- a/src/app/pages/AllowedServicePage/AllowedServiceList/AllowedServiceList.tsx +++ b/src/app/pages/AllowedServicePage/AllowedServiceList/AllowedServiceList.tsx @@ -1,6 +1,7 @@ import { ButtonHTMLAttributes, MouseEvent, ReactNode } from 'react'; import Dropdown from '@/shared/components/Dropdown/Dropdown'; +import FaviconImage from '@/shared/components/FaviconImage/FaviconImage'; import Spacer from '@/shared/components/Spacer/Spacer'; import { AllowedServiceGroupType, ColorPaletteType } from '@/shared/types/allowedService'; @@ -122,7 +123,7 @@ const AllowedServiceListItem = ({
{allowedServiceGroupData?.favicons.map((siteUrl) => ( - favicon + ))} {allowedServiceGroupData?.extraCnt > 0 && (
diff --git a/src/app/pages/AllowedServicePage/AllowedServicePage.tsx b/src/app/pages/AllowedServicePage/AllowedServicePage.tsx index ba79297b..ea5beba0 100644 --- a/src/app/pages/AllowedServicePage/AllowedServicePage.tsx +++ b/src/app/pages/AllowedServicePage/AllowedServicePage.tsx @@ -58,6 +58,7 @@ const AllowedServicePage = () => { const handleChangeUrlInput = (e: ChangeEvent) => { setUrlInput(e.target.value); + resetAllowedService(); }; const handleEnableAddingAllowedServiceGroup = () => { @@ -73,13 +74,21 @@ const AllowedServicePage = () => { allowedGroupId: activeGroupId!, connectType: currentTap, }); - const { data: recommendedSites } = useGetRecommendedSites(); + const { data: recommendedSites } = useGetRecommendedSites({ + allowedGroupId: activeGroupId!, + }); const { mutate: patchChangeAllowedServiceGroupName } = usePatchChangeAllowedServiceGroupName(); const { mutate: patchChangeAllowedServiceGroupColor } = usePatchChangeAllowedServiceGroupColor(); const { mutate: postAddAllowedServiceGroup } = usePostAddAllowedServiceGroup(); const { mutate: deleteAllowedServiceGroup } = useDeleteAllowedServiceGroup(); - const { mutate: postAddAllowedService, reset: resetAllowedService, isError, error } = usePostAddAllowedService(); + const { + mutate: postAddAllowedService, + reset: resetAllowedService, + isPending, + isError, + error, + } = usePostAddAllowedService(); const { mutate: deleteAllowedService } = useDeleteAllowedService(); const resetUrlInput = () => { @@ -170,7 +179,7 @@ const AllowedServicePage = () => { }; const handleAddAllowedService = (urlInput: string, activeGroupId: number | null) => { - if (activeGroupId) { + if (activeGroupId && !isPending) { postAddAllowedService( { siteUrl: urlInput, @@ -185,10 +194,24 @@ const AllowedServicePage = () => { } }; - const handleDeleteAllowedService = (id: number) => { - deleteAllowedService({ - allowedSiteId: String(id), - }); + const handleDeleteAllowedService = (id: number, deleteUrl: string) => { + deleteAllowedService( + { + allowedSiteId: String(id), + }, + { + onSuccess: () => { + if ( + isError && + error?.response?.data.message && + error.response.data.message.includes('존재하는') && + deleteUrl === urlInput + ) { + resetAllowedService(); + } + }, + }, + ); }; const handleKeyDownUrlInput = (e: KeyboardEvent) => { @@ -293,7 +316,7 @@ const AllowedServicePage = () => { > handleAddAllowedService(urlInput, activeGroupId)} > 사이트 등록하기 @@ -305,7 +328,9 @@ const AllowedServicePage = () => { allowedServiceGroupDetail.data.allowedSites.map((allowedSiteData, index) => ( handleDeleteAllowedService(allowedSiteData.id)} + onDeleteAllowedSite={() => + handleDeleteAllowedService(allowedSiteData.id, allowedSiteData.siteUrl) + } {...allowedSiteData} /> ))} diff --git a/src/app/pages/HomePage/BoxCategory/BoxCategory.tsx b/src/app/pages/HomePage/BoxCategory/BoxCategory.tsx index 3cdf1bbe..8330f4a5 100644 --- a/src/app/pages/HomePage/BoxCategory/BoxCategory.tsx +++ b/src/app/pages/HomePage/BoxCategory/BoxCategory.tsx @@ -1,6 +1,6 @@ import dayjs, { Dayjs } from 'dayjs'; -import { type KeyboardEvent, Suspense, lazy, useRef, useState } from 'react'; +import { type KeyboardEvent, type MouseEvent, Suspense, lazy, useRef, useState } from 'react'; import BoxTodo from '@/shared/components/BoxTodo/BoxTodo'; import ButtonTodoToggle from '@/shared/components/ButtonTodayToggle/ButtonTodoToggle'; @@ -13,6 +13,7 @@ import type { TaskListType, TaskType } from '@/shared/types/tasks'; import MeatballDefaultIcon from '@/shared/assets/svgs/common/ic_meatball_default.svg?react'; import PlusIcon from '@/shared/assets/svgs/home/ic_plus.svg?react'; +import PopoverAddTodoIcon from '@/shared/assets/svgs/popover_add_todo.svg?react'; import { usePostToggleTaskStatus } from '@/shared/apisV2/common/common.mutations'; import { usePatchTask, usePostCreateTask } from '@/shared/apisV2/home/home.mutations'; @@ -70,13 +71,11 @@ const BoxCategory = ({ const [calendarStartDate, setCalendarStartDate] = useState(selectedDate); const [calendarEndDate, setCalendarEndDate] = useState(null); + const [calendarPosition, setCalendarPosition] = useState({ top: 0, left: 0 }); + const boxCategoryRef = useRef(null); const { mutate: patchTask } = usePatchTask(); - const getTargetTaskById = (taskId: number) => { - return ongoingTodos.find((task) => task.id === taskId) || completedTodos.find((task) => task.id === taskId); - }; - const { isPeriodOn, selectedEndDate, @@ -87,7 +86,14 @@ const BoxCategory = ({ handlePeriodEnd, } = useCalendar(); - const handleOpenTaskCalendar = (taskId: number) => { + const CALENDAR_ESTIMATED_HEIGHT = 389; + const CALENDAR_TOP_OFFSET = 4; + + const getTargetTaskById = (taskId: number) => { + return ongoingTodos.find((task) => task.id === taskId) || completedTodos.find((task) => task.id === taskId); + }; + + const handleOpenTaskCalendar = (taskId: number, e: MouseEvent) => { const targetTask = getTargetTaskById(taskId); if (targetTask) { @@ -100,6 +106,19 @@ const BoxCategory = ({ handlePeriodEnd(); } } + const buttonRect = e.currentTarget.getBoundingClientRect(); + const containerRect = boxCategoryRef.current?.getBoundingClientRect(); + + let top = buttonRect.top; + const left = buttonRect.right; + + if (containerRect) { + const maxTop = containerRect.bottom - CALENDAR_ESTIMATED_HEIGHT; + const minTop = containerRect.top; + top = Math.max(minTop, Math.min(buttonRect.top, maxTop)) + CALENDAR_TOP_OFFSET; + } + + setCalendarPosition({ top, left }); setSelectedTaskId(taskId); setIsCalendarOpen(true); @@ -213,128 +232,133 @@ const BoxCategory = ({ as="article" className="relative flex w-[31.6rem] flex-shrink-0 flex-col rounded-[16px] bg-gray-bg-03 p-[1.8rem]" > -
- {isCategoryEditing ? ( - setEditedCategoryName(e.target.value)} - onBlur={handleFinishEditing} - onKeyDown={handleKeyDown} - /> - ) : ( -

- {title} -

- )} -
- - - - - - - - onDeleteCategory(id)} /> - - +
+
+ {isCategoryEditing ? ( + setEditedCategoryName(e.target.value)} + onBlur={handleFinishEditing} + onKeyDown={handleKeyDown} + /> + ) : ( +

+ {title} +

+ )} +
+ {ongoingTodos.length === 0 && !isAdding && ( + + )} + + + + + + + + onDeleteCategory(id)} /> + + +
-
- {ongoingTodos.length === 0 && completedTodos.length === 0 && isAdding === false ? ( - - ) : ( - - - - {isAdding && !isCalendarOpen && ( - { - handleEditComplete(); - handleCreatePost(); - }} - name={name} - onInputChange={handleInputChange} - selectedStartDate={selectedDate} - selectedEndDate={selectedEndDate} - /> - )} - - {ongoingTodos.map(({ id, name, startDate, endDate, elapsedTime }) => { - const todo = { id, name, startDate, endDate, elapsedTime }; - const selectedNumber = getSelectedNumber(id); - return ( - - toggleTodoStatus( - { taskId: id }, - { - onSuccess: () => { - setCompletedTodoToggle(true); - }, - }, - ) - } - updateTodayTodos={() => updateTodayTodos(todo)} - clickable={addingTodayTodoStatus} - addingComplete={addingComplete} - handleCalendarToggle={() => handleOpenTaskCalendar(id)} - onPatchTask={handlePatchTask} - /> - ); - })} - - - {completedTodos.length !== 0 && ( - - {completedTodos.map(({ id, name, startDate, endDate, elapsedTime }) => ( - { - toggleTodoStatus({ taskId: id }); + {ongoingTodos.length === 0 && completedTodos.length === 0 && isAdding === false ? ( + + ) : ( + + + + {isAdding && !isCalendarOpen && ( + { + handleEditComplete(); + handleCreatePost(); }} - clickable={addingTodayTodoStatus} - addingComplete={addingComplete} - handleCalendarToggle={() => handleOpenTaskCalendar(id)} + name={name} + onInputChange={handleInputChange} + selectedStartDate={selectedDate} + selectedEndDate={selectedEndDate} /> - ))} + )} + + {ongoingTodos.map(({ id, name, startDate, endDate, elapsedTime }) => { + const todo = { id, name, startDate, endDate, elapsedTime }; + const selectedNumber = getSelectedNumber(id); + return ( + + toggleTodoStatus( + { taskId: id }, + { + onSuccess: () => { + setCompletedTodoToggle(true); + }, + }, + ) + } + updateTodayTodos={() => updateTodayTodos(todo)} + clickable={addingTodayTodoStatus} + addingComplete={addingComplete} + handleCalendarToggle={(e: MouseEvent) => handleOpenTaskCalendar(id, e)} + onPatchTask={handlePatchTask} + activeCalendarTask={isCalendarOpen && selectedTaskId === id} + /> + ); + })} - )} + + {completedTodos.length !== 0 && ( + + {completedTodos.map(({ id, name, startDate, endDate, elapsedTime }) => ( + { + toggleTodoStatus({ taskId: id }); + }} + clickable={addingTodayTodoStatus} + addingComplete={addingComplete} + handleCalendarToggle={(e: MouseEvent) => handleOpenTaskCalendar(id, e)} + /> + ))} + + )} + - - )} + )} +
{isCalendarOpen && ( Loading...
}>
{ if (node) { diff --git a/src/app/pages/HomePage/BoxCategory/StatusDefaultBoxCategory/StatusDefaultBoxCategory.tsx b/src/app/pages/HomePage/BoxCategory/StatusDefaultBoxCategory/StatusDefaultBoxCategory.tsx index f27ff881..3acd3a6d 100644 --- a/src/app/pages/HomePage/BoxCategory/StatusDefaultBoxCategory/StatusDefaultBoxCategory.tsx +++ b/src/app/pages/HomePage/BoxCategory/StatusDefaultBoxCategory/StatusDefaultBoxCategory.tsx @@ -1,11 +1,9 @@ const StatusDefaultBoxCategory = () => { return ( -
+
{/* 명확한 값이 없어서 임의로 mb-[20%로] 지정 */} -

- 할 일을 추가하려면 -
+ 아이콘을 선택해주세요. -

+

할 일을 추가하려면

+

+ 아이콘을 선택해주세요.

); }; diff --git a/src/app/pages/HomePage/BoxTodayTodo/StatusAddBoxTodayTodo/StatusAddBoxTodayTodo.tsx b/src/app/pages/HomePage/BoxTodayTodo/StatusAddBoxTodayTodo/StatusAddBoxTodayTodo.tsx index efc1fe4a..a0d6849f 100644 --- a/src/app/pages/HomePage/BoxTodayTodo/StatusAddBoxTodayTodo/StatusAddBoxTodayTodo.tsx +++ b/src/app/pages/HomePage/BoxTodayTodo/StatusAddBoxTodayTodo/StatusAddBoxTodayTodo.tsx @@ -1,11 +1,20 @@ +import { useRef } from 'react'; +import { useNavigate } from 'react-router-dom'; + import BoxTodo from '@/shared/components/BoxTodo/BoxTodo'; import ButtonRadius5 from '@/shared/components/ButtonRadius5/ButtonRadius5'; +import ModalWrapper, { ModalWrapperRef } from '@/shared/components/ModalWrapper/ModalWrapper'; import Spacer from '@/shared/components/Spacer/Spacer'; import type { TaskType } from '@/shared/types/tasks'; import { LARGE_BTN_TEXT, SMALL_BTN_TEXT } from '@/shared/constants/btnText'; +import { ROUTES_CONFIG } from '@/router/routesConfig'; + +import RegisterAllowedService from '@/pages/HomePage/ModalContentsAlert/RegisterAllowedService/RegisterAllowedService'; +import { useGetPopoverAllowedServiceList } from '@/shared/apisV2/timer/timer.queries'; + interface StatusAddBoxTodayTodoProps { selectedTodayTodos: Omit[]; onDisableAddStatus: () => void; @@ -27,6 +36,12 @@ const StatusAddBoxTodayTodo = ({ addingComplete, onCreateTodayTodos, }: StatusAddBoxTodayTodoProps) => { + const { data: allowedServiceList } = useGetPopoverAllowedServiceList(); + const registerServiceModalRef = useRef(null); + const navigate = useNavigate(); + + const allowedServicePath = ROUTES_CONFIG.allowedService.path; + const hasTodayTodos = !(selectedTodayTodos.length === 0); const clickable = addingComplete ? '' : 'pointer-events-none cursor-default '; const handleMouseEnter = () => { @@ -40,7 +55,11 @@ const StatusAddBoxTodayTodo = ({ }; const handleStartTimer = () => { - onCreateTodayTodos(); + if (allowedServiceList && allowedServiceList.data.length === 0) { + registerServiceModalRef.current?.open(); + } else { + onCreateTodayTodos(); + } }; return ( @@ -69,11 +88,10 @@ const StatusAddBoxTodayTodo = ({ })} ) : ( -

- 할 일 카드를 선택하여 -
- 오늘 할 일을 추가해 보세요. -

+
+

할 일 카드를 선택하여

+

오늘 할 일을 추가해 보세요.

+
)} @@ -103,6 +121,21 @@ const StatusAddBoxTodayTodo = ({
+ + + {() => ( + { + registerServiceModalRef.current?.close(); + onCreateTodayTodos(); + }} + onConfirm={() => { + registerServiceModalRef.current?.close(); + navigate(allowedServicePath); + }} + /> + )} + ); }; diff --git a/src/app/pages/HomePage/BoxTodayTodo/StatusDefaultBoxTodayTodo/StatusDefaultBoxTodayTodo.tsx b/src/app/pages/HomePage/BoxTodayTodo/StatusDefaultBoxTodayTodo/StatusDefaultBoxTodayTodo.tsx index 246ddd9d..fffefa47 100644 --- a/src/app/pages/HomePage/BoxTodayTodo/StatusDefaultBoxTodayTodo/StatusDefaultBoxTodayTodo.tsx +++ b/src/app/pages/HomePage/BoxTodayTodo/StatusDefaultBoxTodayTodo/StatusDefaultBoxTodayTodo.tsx @@ -11,11 +11,11 @@ const StatusDefaultBoxTodayTodo = ({ hasTodos, onEnableAddStatus }: StatusDefaul {/* NOTE: 중앙에 정렬된 레이아웃이 아니어서 임의 값로 살짝 위로 올림, 추후 정확하게 수정 */}

아직 오늘 할 일이 없어요

-

- 할 일을 추가하려면 -
- 아래 버튼을 선택해주세요. -

+
+

할 일을 추가하려면

+

+ 아이콘을 선택해주세요.

+
+
오늘 할 일 추가 diff --git a/src/app/pages/HomePage/ButtonMoreFriends/ButtonMoreFriends.tsx b/src/app/pages/HomePage/ButtonMoreFriends/ButtonMoreFriends.tsx index 67723290..92112ebb 100644 --- a/src/app/pages/HomePage/ButtonMoreFriends/ButtonMoreFriends.tsx +++ b/src/app/pages/HomePage/ButtonMoreFriends/ButtonMoreFriends.tsx @@ -9,8 +9,8 @@ interface ButtonMoreFriendsProps extends ButtonHTMLAttributes const ButtonMoreFriends = ({ friendsCount, ...props }: ButtonMoreFriendsProps) => { return ( diff --git a/src/app/pages/HomePage/ButtonUserProfile/ButtonUserProfile.tsx b/src/app/pages/HomePage/ButtonUserProfile/ButtonUserProfile.tsx index 433242eb..f8c4b609 100644 --- a/src/app/pages/HomePage/ButtonUserProfile/ButtonUserProfile.tsx +++ b/src/app/pages/HomePage/ButtonUserProfile/ButtonUserProfile.tsx @@ -6,17 +6,20 @@ import GradientCircleIcon from '@/shared/assets/svgs/gradient_circle.svg?react'; interface ButtonUserProfile extends ButtonHTMLAttributes { isMyProfile?: boolean; - userName?: string; isConnecting?: boolean; isSelectedUser?: boolean; + isOnline?: boolean; + imageUrl?: string; } const ButtonUserProfile = ({ isMyProfile = false, - userName = '나', - isConnecting = false, isSelectedUser = false, + isOnline = false, + imageUrl, }: ButtonUserProfile) => { + const profileImage = imageUrl || defaultPorfileIcon; + return (
- {(isMyProfile || isSelectedUser) && ( -

{userName}

- )} + {isMyProfile &&

} ); }; diff --git a/src/app/pages/HomePage/DatePicker/DatePicker.tsx b/src/app/pages/HomePage/DatePicker/DatePicker.tsx index dfea0b20..196534ef 100644 --- a/src/app/pages/HomePage/DatePicker/DatePicker.tsx +++ b/src/app/pages/HomePage/DatePicker/DatePicker.tsx @@ -29,8 +29,10 @@ const DatePicker = ({ todayDate, selectedDate, onSelectedDateChange }: DatePicke const homeDropdownData = getHomeDropdownData(todayDate); - const handleClickTodayBtn = () => { - handleToday(); + const selectedItemRefCallback = (node: HTMLLIElement | null) => { + if (node) { + node.scrollIntoView(); + } }; return ( @@ -49,16 +51,19 @@ const DatePicker = ({ todayDate, selectedDate, onSelectedDateChange }: DatePicke boxShadow="shadow-[0_4px_4.8px_0_rgba(0,0,0,0.25)]" className="top-[4.4rem]" > - {homeDropdownData.map((item) => ( -
  • - handleYearMonthClick(item)}> - {item.format('YYYY년 MM월')} - -
  • - ))} + {homeDropdownData.map((item) => { + const label = item.format('YYYY년 MM월'); + const isCurrent = label === selectedDate.format('YYYY년 MM월'); + return ( +
  • + handleYearMonthClick(item)}>{label} +
  • + ); + })} @@ -82,7 +87,7 @@ const DatePicker = ({ todayDate, selectedDate, onSelectedDateChange }: DatePicke
    - diff --git a/src/app/pages/HomePage/HomePage.tsx b/src/app/pages/HomePage/HomePage.tsx index 8e569744..ecd55add 100644 --- a/src/app/pages/HomePage/HomePage.tsx +++ b/src/app/pages/HomePage/HomePage.tsx @@ -8,6 +8,7 @@ import { useNavigate } from 'react-router-dom'; import AutoFixedGrid from '@/shared/components/AutoFixedGrid/AutoFixedGrid'; import ModalContentsFriends from '@/shared/components/ModalContentsFriends/ModalContentsFriends'; import ModalWrapper, { ModalWrapperRef } from '@/shared/components/ModalWrapper/ModalWrapper'; +import NotificationPanel from '@/shared/components/NotificationPanel/NotificationPanel'; import Spacer from '@/shared/components/Spacer/Spacer'; import useClickOutside from '@/shared/hooks/useClickOutside'; @@ -20,9 +21,12 @@ import { TaskType } from '@/shared/types/tasks'; import BellIcon from '@/shared/assets/svgs/bell.svg?react'; import FriendSettingIcon from '@/shared/assets/svgs/friend_setting.svg?react'; import LargePlusIcon from '@/shared/assets/svgs/large_plus.svg?react'; +import PopoverAddCategoryIcon from '@/shared/assets/svgs/popover_add_category.svg?react'; import { ROUTES_CONFIG } from '@/router/routesConfig'; +import TooltipFriendInfo from '@/pages/HomePage/TooltipFriendInfo/TooltipFriendInfo'; +import { useGetFriendList } from '@/shared/apisV2/friends/friends.queries'; import { useAddCategory, useDeleteCategory, @@ -30,6 +34,8 @@ import { usePostAddTodayTodos, } from '@/shared/apisV2/home/home.mutations'; import { useGetCategoryTask, useGetWorkTime } from '@/shared/apisV2/home/home.queries'; +import { useGetProfile } from '@/shared/apisV2/setting/setting.queries'; +import { useGetTimerFriends } from '@/shared/apisV2/timer/timer.queries'; import BoxAddCategory from './BoxAddCategory/BoxAddCategory'; import BoxCategory from './BoxCategory/BoxCategory'; @@ -37,6 +43,7 @@ import BoxTodayTodo from './BoxTodayTodo/BoxTodayTodo'; import ButtonMoreFriends from './ButtonMoreFriends/ButtonMoreFriends'; import ButtonUserProfile from './ButtonUserProfile/ButtonUserProfile'; import DatePicker from './DatePicker/DatePicker'; +import TimerRestriction from './ModalContentsAlert/TimerRestriction/TimerRestriction'; import StatusDefaultHome from './StatusDefaultHome/StatusDefaultHome'; dayjs.extend(utc); @@ -49,14 +56,29 @@ const HomePage = () => { const boxAddCategoryRef = useRef(null); const friendsModalRef = useRef(null); + const notificationPanelRef = useRef(null); + const bellIconRef = useRef(null); + const timerRestrictionModalRef = useRef(null); + + const [isNotificationVisible, setIsNotificationVisible] = useState(false); + + const MAX_VISIBLE_FRIENDS = 5; const [initialAdding, setInitialAdding] = useState(true); const [selectedDate, setSelectedDate] = useState(todayDate); const { startDate, endDate } = getThisWeekRange(selectedDate); const { data: categoriesData } = useGetCategoryTask({ startDate, endDate }); + const { data: userProfile } = useGetProfile(); + const { data: friendListData } = useGetFriendList(); + const { data: timerFriendsData } = useGetTimerFriends(); const categories = categoriesData?.data || []; + const friendList = friendListData?.data || []; + const timerFriends = timerFriendsData?.data || []; + + // 현재 접속 중인 친구만 필터링 + const onlineFriends = friendList.filter((friend) => friend.isOnline); const dailyCategoryTask = getDailyCategoryTask(selectedDate, categories); @@ -137,7 +159,24 @@ const HomePage = () => { friendsModalRef.current?.open(); }; + const toggleNotification = () => { + setIsNotificationVisible((prev) => !prev); + }; + useClickOutside(boxAddCategoryRef, handleOutsideClickWhileAddingCategory); + useClickOutside( + notificationPanelRef, + (event) => { + if (!isNotificationVisible) return; + + if (bellIconRef.current && event && bellIconRef.current.contains(event.target as Node)) { + return; + } + + setIsNotificationVisible(false); + }, + isNotificationVisible, + ); const deleteTodayTodos = (todo: Omit) => { setTodayTodos((prev) => prev.filter((prevTodo) => prevTodo.id !== todo.id)); @@ -150,9 +189,14 @@ const HomePage = () => { const enableAddingTodayTodo = () => { setAddingTodayTodoStatus(true); + setSelectedDate(todayDate); }; const handleSelectedDateChange = (date: Dayjs) => { + if (addingTodayTodoStatus && !todayDate.isSame(date, 'day')) { + timerRestrictionModalRef.current?.open(); + return; + } setSelectedDate(date); }; @@ -214,27 +258,99 @@ const HomePage = () => {
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - +
    • + {onlineFriends.slice(0, MAX_VISIBLE_FRIENDS).map((friend) => { + const onlineFriend = timerFriends.find((of) => of.id === friend.id); + + return ( +
    • +
      + +
      +
      + {onlineFriend ? ( + + ) : ( + + )} +
      +
    • + ); + })} + {onlineFriends.length < MAX_VISIBLE_FRIENDS && + friendList + .filter((friend) => !friend.isOnline) + .slice(0, MAX_VISIBLE_FRIENDS - onlineFriends.length) + .map((friend) => { + const offlineFriend = timerFriends.find((of) => of.id === friend.id); + + return ( +
    • +
      + +
      +
      + {offlineFriend ? ( + + ) : ( + + )} +
      +
    • + ); + })}
    - + + {friendList.length > MAX_VISIBLE_FRIENDS && ( + + )}
    -
    +
    -
    @@ -283,7 +399,12 @@ const HomePage = () => { )} {dailyCategoryTask.length <= 2 && ( -
    +
    + {!isAddingCategory && ( + + )} @@ -333,6 +454,18 @@ const HomePage = () => { {({ isModalOpen }) => } + + + {() => ( + { + timerRestrictionModalRef.current?.close(); + }} + /> + )} + + + {isNotificationVisible && } ); }; diff --git a/src/app/pages/HomePage/ModalContentsAlert/ButtonAlert/ButtonAlert.tsx b/src/app/pages/HomePage/ModalContentsAlert/ButtonAlert/ButtonAlert.tsx index aa54e587..ec88f90a 100644 --- a/src/app/pages/HomePage/ModalContentsAlert/ButtonAlert/ButtonAlert.tsx +++ b/src/app/pages/HomePage/ModalContentsAlert/ButtonAlert/ButtonAlert.tsx @@ -1,19 +1,22 @@ import { ButtonHTMLAttributes, ReactNode } from 'react'; interface ButtonAlertProps extends ButtonHTMLAttributes { - variant?: 'primary' | 'danger'; + variant?: 'primary' | 'danger' | 'mint'; children: ReactNode; className?: string; } const ButtonAlert = ({ children, variant = 'primary', className = '', ...props }: ButtonAlertProps) => { - const primaryStyle = 'bg-gray-bg-06 hover:bg-gray-bg-04 active:bg-gray-bg-05'; - const dangerStyle = 'bg-error-01 hover:bg-error-03 active:bg-error-03 active:text-gray-04'; - const buttonStyle = variant === 'primary' ? primaryStyle : dangerStyle; + const primaryStyle = 'bg-gray-bg-06 hover:bg-gray-bg-04 text-white active:bg-gray-bg-05'; + const dangerStyle = 'bg-error-01 hover:bg-error-03 active:bg-error-03 text-white active:text-gray-04'; + const mintStyle = 'bg-mint-02 hover:bg-mint-01 active:bg-mint-03 text-black active:text-gray-01'; + + const buttonStyle = variant === 'primary' ? primaryStyle : variant === 'danger' ? dangerStyle : mintStyle; + return ( diff --git a/src/app/pages/HomePage/ModalContentsAlert/RegisterAllowedService/RegisterAllowedService.tsx b/src/app/pages/HomePage/ModalContentsAlert/RegisterAllowedService/RegisterAllowedService.tsx new file mode 100644 index 00000000..e3892987 --- /dev/null +++ b/src/app/pages/HomePage/ModalContentsAlert/RegisterAllowedService/RegisterAllowedService.tsx @@ -0,0 +1,21 @@ +import ButtonAlert from '../ButtonAlert/ButtonAlert'; +import { AlertModalProps } from '../types/index'; + +const RegisterAllowedService = ({ onCloseModal, onConfirm }: AlertModalProps) => ( +
    +
    +

    허용 서비스 세트를 먼저 등록해주세요.

    +

    허용 서비스를 등록하러 갈까요?

    +
    +
    + + 등록하기 + + + 나중에 하기 + +
    +
    +); + +export default RegisterAllowedService; diff --git a/src/app/pages/HomePage/ModalContentsAlert/TimerRestriction/TimerRestriction.tsx b/src/app/pages/HomePage/ModalContentsAlert/TimerRestriction/TimerRestriction.tsx new file mode 100644 index 00000000..400d828c --- /dev/null +++ b/src/app/pages/HomePage/ModalContentsAlert/TimerRestriction/TimerRestriction.tsx @@ -0,0 +1,16 @@ +import ButtonAlert from '../ButtonAlert/ButtonAlert'; +import { AlertModalProps } from '../types/index'; + +const TimerRestriction = ({ onConfirm }: AlertModalProps) => ( +
    +
    +

    오늘 날짜에 해당하는 할 일만

    +

    추가할 수 있어요.

    +
    + + 확인 + +
    +); + +export default TimerRestriction; diff --git a/src/app/pages/HomePage/TooltipFriendInfo/TooltipFriendInfo.tsx b/src/app/pages/HomePage/TooltipFriendInfo/TooltipFriendInfo.tsx new file mode 100644 index 00000000..0213a272 --- /dev/null +++ b/src/app/pages/HomePage/TooltipFriendInfo/TooltipFriendInfo.tsx @@ -0,0 +1,61 @@ +import { formatSeconds } from '@/shared/utils/time'; + +import ConnectionIcon from '@/shared/assets/svgs/connection_icon.svg?react'; +import LineIcon from '@/shared/assets/svgs/ic_line.svg?react'; +import TooltipTriangleIcon from '@/shared/assets/svgs/tooltip_triangle.svg?react'; + +import { useTimerCount } from '@/pages/TimerPage/hooks/useTimerCount'; + +interface TooltipFriendInfoProps { + id: number; + image: string; + name: string; + time: number; + categoryName: string; + isPlaying: boolean; + isOnline: boolean; +} + +const TooltipFriendInfo = ({ image, name, time, categoryName, isPlaying, isOnline }: TooltipFriendInfoProps) => { + const { timer } = useTimerCount({ + isPlaying, + previousTime: time, + shouldRun: isPlaying, + }); + + const formattedTime = formatSeconds(timer); + + return ( +
    + +
    +
    +
    + {name + {isOnline && ( + + )} +
    +
    +

    {name}

    +
    + {categoryName && ( + <> +

    {categoryName}

    + + + )} +

    {formattedTime}

    +
    +
    +
    +
    +
    + ); +}; + +export default TooltipFriendInfo; diff --git a/src/app/pages/LoginPage/LoginPage.tsx b/src/app/pages/LoginPage/LoginPage.tsx index a8f5048e..4a9adb17 100644 --- a/src/app/pages/LoginPage/LoginPage.tsx +++ b/src/app/pages/LoginPage/LoginPage.tsx @@ -1,14 +1,8 @@ -import { useEffect } from 'react'; import Lottie from 'react-lottie'; -import { useNavigate } from 'react-router-dom'; - -import { getAccessToken } from '@/shared/utils/auth'; import LottieData from '@/shared/assets/lotties/morib_logo_motion.json'; import GoogleLoginIcon from '@/shared/assets/svgs/google_login.svg?react'; -import { ROUTES_CONFIG } from '@/router/routesConfig'; - import { useLottieAnimation } from '@/pages/LoginPage/hooks/useLottieAnimation'; const defaultOptions = { @@ -58,7 +52,6 @@ const LoginPage = () => { onClick={handleClick} className={`ml-[12rem] transition-opacity duration-300 ${isAnimationComplete ? 'opacity-100' : 'opacity-0'}`} > - adasdas
    diff --git a/src/app/pages/OnboardingPage/ButtonSkip/ButtonSkip.tsx b/src/app/pages/OnboardingPage/ButtonSkip/ButtonSkip.tsx index 9b1bdc94..4805f45c 100644 --- a/src/app/pages/OnboardingPage/ButtonSkip/ButtonSkip.tsx +++ b/src/app/pages/OnboardingPage/ButtonSkip/ButtonSkip.tsx @@ -1,8 +1,18 @@ import { useNavigate } from 'react-router-dom'; +import type { PostInterestAreaReq } from '@/shared/types/api/onboarding'; + import { ROUTES_CONFIG } from '@/router/routesConfig'; +import { usePostInterestArea } from '@/shared/apisV2/onboarding/onboarding.mutations'; + const ButtonSkip = () => { + const { mutate: postInterestArea } = usePostInterestArea(); + postInterestArea({} as PostInterestAreaReq, { + onSuccess: () => { + navigate('/home'); + }, + }); const navigate = useNavigate(); const handleNavigateToHome = () => { navigate(ROUTES_CONFIG.home.path); diff --git a/src/app/pages/OnboardingPage/OnboardingPage.tsx b/src/app/pages/OnboardingPage/OnboardingPage.tsx index a42f67ad..950734b9 100644 --- a/src/app/pages/OnboardingPage/OnboardingPage.tsx +++ b/src/app/pages/OnboardingPage/OnboardingPage.tsx @@ -21,9 +21,9 @@ const OnboardingPage = () => { }; useEffect(() => { - const isSignUp = localStorage.getItem('isSignUp'); + const 온보딩완료여부 = localStorage.getItem('isOnboardingComplete'); - if (isSignUp !== 'true') { + if (온보딩완료여부 !== 'true') { navigate(ROUTES_CONFIG.home.path, { replace: true }); } }); diff --git a/src/app/pages/RedirectPage/RedirectPage.tsx b/src/app/pages/RedirectPage/RedirectPage.tsx index 8fc73c8c..036b4a86 100644 --- a/src/app/pages/RedirectPage/RedirectPage.tsx +++ b/src/app/pages/RedirectPage/RedirectPage.tsx @@ -1,7 +1,7 @@ import { useEffect } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; -import { setAccessToken } from '@/shared/utils/auth'; +import { setAccessToken, setRefreshToken } from '@/shared/utils/auth'; import { ROUTES_CONFIG } from '@/router/routesConfig'; @@ -10,18 +10,26 @@ const RedirectPage = () => { const navigate = useNavigate(); useEffect(() => { - const params = new URLSearchParams(search); + // @ts-expect-error + const params = window.electron + ? new URLSearchParams(window.location.hash.substring(1)) + : new URLSearchParams(search); const accessToken = params.get('accessToken'); - const isSignUp = params.get('isSignUp'); + const refreshToken = params.get('refreshToken'); + const 온보딩완료여부 = params.get('isOnboardingComplete'); - if (accessToken) { + if (accessToken && refreshToken) { setAccessToken(accessToken); - if (isSignUp === 'true') { - localStorage.setItem('isSignUp', isSignUp); + setRefreshToken(refreshToken); + + if (온보딩완료여부 === 'true') { + localStorage.setItem('isOnboardingComplete', 온보딩완료여부); navigate(`${ROUTES_CONFIG.onboarding.path}?step=start`, { replace: true }); } else { navigate(`${ROUTES_CONFIG.home.path}`, { replace: true }); } + } else { + navigate(`${ROUTES_CONFIG.login.path}`, { replace: true }); } }, [navigate, search]); diff --git a/src/app/pages/TimerPage/AllowedServices/AllowedServicesItem.tsx b/src/app/pages/TimerPage/AllowedServices/AllowedServicesItem.tsx new file mode 100644 index 00000000..c4c67b18 --- /dev/null +++ b/src/app/pages/TimerPage/AllowedServices/AllowedServicesItem.tsx @@ -0,0 +1,32 @@ +import { COLOR_PALETTE_MAP } from '@/shared/constants/colorPalette'; + +interface AllowedServicesItemProps { + id: number; + name: string; + colorCode: string; + isActive: boolean; + onSelect: (id: number) => void; +} + +const AllowedServicesItem = ({ id, name, colorCode, isActive, onSelect }: AllowedServicesItemProps) => { + const colorClass = + colorCode in COLOR_PALETTE_MAP ? COLOR_PALETTE_MAP[colorCode as keyof typeof COLOR_PALETTE_MAP] : 'bg-gray-bg-07'; + + return ( +
  • onSelect(id)} + tabIndex={0} + > +
    + {name} +
    + + +
  • + ); +}; + +export default AllowedServicesItem; diff --git a/src/app/pages/TimerPage/AllowedServices/AllowedServicesPopover.tsx b/src/app/pages/TimerPage/AllowedServices/AllowedServicesPopover.tsx new file mode 100644 index 00000000..e8b7619c --- /dev/null +++ b/src/app/pages/TimerPage/AllowedServices/AllowedServicesPopover.tsx @@ -0,0 +1,150 @@ +import { useCallback, useMemo, useState } from 'react'; + +import { COLOR_PALETTE_MAP } from '@/shared/constants/colorPalette'; + +import { getServiceFavicon } from '@/pages/OnboardingPage/utils/serviceUrl'; +import { usePostApplyAllowedServiceGroup } from '@/shared/apisV2/timer/timer.mutations'; +import { useGetPopoverAllowedServiceList } from '@/shared/apisV2/timer/timer.queries'; + +import { useTimerContext } from '../contexts/TimerContext'; +import Checkbox from './Checkbox'; + +interface AllowedServicesPopoverProps { + onCancel: () => void; +} + +/** + * 허용 서비스 설정 팝오버 컴포넌트 + * @param onCancel 취소 시 실행할 함수 + */ +const AllowedServicesPopover = ({ onCancel }: AllowedServicesPopoverProps) => { + const { actions } = useTimerContext(); + + const { data: allowedServiceList } = useGetPopoverAllowedServiceList(); + + // 서비스 그룹 데이터 메모이제이션 + const serviceGroups = useMemo(() => allowedServiceList?.data ?? [], [allowedServiceList?.data]); + + // 초기 선택된 그룹 ID와 선택된 그룹 목록 계산 + const initialData = useMemo(() => { + const selectedIds = serviceGroups.filter((group) => group.selected).map((group) => group.id); + const firstId = serviceGroups[0]?.id ?? null; + return { selectedIds, firstId }; + }, [serviceGroups]); + + // 선택된 서비스 그룹 ID 관리 + const [selectedServiceGroupId, setSelectedServiceGroupId] = useState(initialData.firstId); + + // 체크된 서비스 그룹 ID 목록 관리 + const [checkedServiceGroupIds, setCheckedServiceGroupIds] = useState(initialData.selectedIds); + + // 현재 선택된 그룹의 허용 사이트 목록 (파생 데이터) + const activeAllowedSites = useMemo(() => { + return serviceGroups.find((group) => group.id === selectedServiceGroupId)?.allowedSites ?? []; + }, [serviceGroups, selectedServiceGroupId]); + + // 체크박스 상태 확인 (파생 함수) + const isCheckedServiceGroupId = useMemo(() => { + return (id: number) => checkedServiceGroupIds.includes(id); + }, [checkedServiceGroupIds]); + + // API mutation 설정 + const { mutate: applyAllowedServiceGroup } = usePostApplyAllowedServiceGroup({ + allowedGroupIdList: checkedServiceGroupIds, + }); + + // 체크박스 토글 핸들러 + const toggleCheckedServiceGroupId = useCallback((id: number) => { + setCheckedServiceGroupIds((prev) => + prev.includes(id) ? prev.filter((checkedId) => checkedId !== id) : [...prev, id], + ); + }, []); + + // 적용 버튼 핸들러 + const handleApplyAllowedServiceGroup = useCallback(() => { + applyAllowedServiceGroup(undefined, { + onSuccess: actions.hideAllowedServices, + }); + }, [actions.hideAllowedServices, applyAllowedServiceGroup]); + + // 서비스 그룹 선택 핸들러 + const handleSelectServiceGroup = useCallback((id: number) => { + setSelectedServiceGroupId(id); + }, []); + + if (!allowedServiceList) { + return null; + } + + return ( +
    + {/* 왼쪽 서비스 그룹 목록 */} +
    +

    허용서비스 세트

    +
      + {serviceGroups.map((service) => ( +
    • handleSelectServiceGroup(service.id)} + className={`flex h-[3.4rem] w-full cursor-pointer items-center rounded-[6px] px-[0.5rem] hover:bg-gray-bg-04 ${ + service.id === selectedServiceGroupId ? 'bg-gray-bg-05' : '' + }`} + > +
      +
      e.stopPropagation()}> + toggleCheckedServiceGroupId(service.id)} + checked={isCheckedServiceGroupId(service.id)} + /> +
      + +
      + {service.name} +
      + + {/* 색상 표시 원 */} +
      +
      +
    • + ))} +
    +
    + + +
    +
    + + {/* 오른쪽 허용 사이트 목록 */} +
    +

    허용할 사이트

    +
      + {activeAllowedSites.map((siteInfo) => ( +
    • + {`${siteInfo.siteName} + {siteInfo.siteName} +
    • + ))} +
    +
    +
    + ); +}; + +export default AllowedServicesPopover; diff --git a/src/app/pages/TimerPage/TItleAllowedService/TitleAllowedService.tsx b/src/app/pages/TimerPage/AllowedServices/AllowedServicesTitle.tsx similarity index 63% rename from src/app/pages/TimerPage/TItleAllowedService/TitleAllowedService.tsx rename to src/app/pages/TimerPage/AllowedServices/AllowedServicesTitle.tsx index 4c214019..5ed00edc 100644 --- a/src/app/pages/TimerPage/TItleAllowedService/TitleAllowedService.tsx +++ b/src/app/pages/TimerPage/AllowedServices/AllowedServicesTitle.tsx @@ -1,23 +1,34 @@ import MoribSetBtnActiveIcon from '@/shared/assets/svgs/btn_moribset_active.svg?react'; import MoribSetBtnDefaultIcon from '@/shared/assets/svgs/btn_moribset_default.svg?react'; -import ToolTipAllowedService from './TooltipAllowedService/ToolTipAllowedService'; +import AllowedServicesTooltip from './AllowedServicesTooltip'; -interface TitleAllowedServiceProps { +interface AllowedServicesTitleProps { onClick: () => void; registeredNames: string[]; isAllowedServiceVisible: boolean; } -const TitleAllowedService = ({ onClick, registeredNames, isAllowedServiceVisible }: TitleAllowedServiceProps) => { +/** + * 허용 서비스 타이틀 컴포넌트 + * @param onClick 클릭 시 실행할 함수 + * @param registeredNames 등록된 서비스 이름 목록 + * @param isAllowedServiceVisible 허용 서비스 팝업이 표시 중인지 여부 + */ +const AllowedServicesTitle = ({ onClick, registeredNames, isAllowedServiceVisible }: AllowedServicesTitleProps) => { const joinedNames = registeredNames.join(', '); + const hasServices = registeredNames.length > 0; return (
    - {registeredNames.length > 0 ? ( + {hasServices ? ( + // 서비스가 등록된 경우 <>

    @@ -27,6 +38,7 @@ const TitleAllowedService = ({ onClick, registeredNames, isAllowedServiceVisible

    ) : ( + // 서비스가 등록되지 않은 경우
    @@ -34,7 +46,7 @@ const TitleAllowedService = ({ onClick, registeredNames, isAllowedServiceVisible
    {!isAllowedServiceVisible && (
    - +
    )}
    @@ -43,4 +55,4 @@ const TitleAllowedService = ({ onClick, registeredNames, isAllowedServiceVisible ); }; -export default TitleAllowedService; +export default AllowedServicesTitle; diff --git a/src/app/pages/TimerPage/TItleAllowedService/TooltipAllowedService/ToolTipAllowedService.tsx b/src/app/pages/TimerPage/AllowedServices/AllowedServicesTooltip.tsx similarity index 100% rename from src/app/pages/TimerPage/TItleAllowedService/TooltipAllowedService/ToolTipAllowedService.tsx rename to src/app/pages/TimerPage/AllowedServices/AllowedServicesTooltip.tsx diff --git a/src/app/pages/TimerPage/PopoverAllowedService/Checkbox/Checkbox.tsx b/src/app/pages/TimerPage/AllowedServices/Checkbox.tsx similarity index 79% rename from src/app/pages/TimerPage/PopoverAllowedService/Checkbox/Checkbox.tsx rename to src/app/pages/TimerPage/AllowedServices/Checkbox.tsx index 1c27fb42..8d2764ca 100644 --- a/src/app/pages/TimerPage/PopoverAllowedService/Checkbox/Checkbox.tsx +++ b/src/app/pages/TimerPage/AllowedServices/Checkbox.tsx @@ -1,3 +1,5 @@ +import React from 'react'; + import ActiveCheckboxIcon from '@/shared/assets/svgs/timer/ic_check_box_active.svg?react'; import InactiveCheckboxIcon from '@/shared/assets/svgs/timer/ic_check_box_inactive.svg?react'; @@ -6,8 +8,12 @@ interface CheckboxProps { checked?: boolean; } +/** + * 체크박스 컴포넌트 + * @param onClick 클릭 시 실행할 함수 + * @param checked 체크 여부 + */ const Checkbox = ({ onClick, checked = false }: CheckboxProps) => { - // TODO: 접근성 고려하여 나머지 input 들도 tabIndex가 필요한지 검토하기 return (
    {checked ? : } diff --git a/src/app/pages/TimerPage/Carousel/Carousel.tsx b/src/app/pages/TimerPage/Carousel/Carousel.tsx index 2ed91e78..f4cb653f 100644 --- a/src/app/pages/TimerPage/Carousel/Carousel.tsx +++ b/src/app/pages/TimerPage/Carousel/Carousel.tsx @@ -11,8 +11,11 @@ import { Direction } from '@/shared/types/global'; import { useGetTimerFriends } from '@/shared/apisV2/timer/timer.queries'; -import ContainerCarousel from './ContainerCarousel/ContainerCarousel'; +import CarouselFriend from './CarouselFriend'; +/** + * 타이머 페이지 하단 친구 캐러셀 컴포넌트 + */ const Carousel = () => { const friendsModalRef = useRef(null); const carouselRef = useRef(null); @@ -29,20 +32,21 @@ const Carousel = () => {
    - {friendsList?.data.length === 0 ? ( + {!friendsList?.data || friendsList.data.length === 0 ? (

    함께 몰입할 친구를 추가해보아요!

    친구 추가하기
    ) : ( <> - {friendsList?.data.map((friend) => ( - ( + diff --git a/src/app/pages/TimerPage/Carousel/CarouselFriend.tsx b/src/app/pages/TimerPage/Carousel/CarouselFriend.tsx new file mode 100644 index 00000000..aea25cac --- /dev/null +++ b/src/app/pages/TimerPage/Carousel/CarouselFriend.tsx @@ -0,0 +1,74 @@ +import React, { memo } from 'react'; + +import { formatSeconds } from '@/shared/utils/time'; + +import DefaultProfileIcon from '@/shared/assets/svgs/default_profile.svg'; +import ActivatedClockIcon from '@/shared/assets/svgs/icon_clock.svg?react'; +import DeactivatedClockIcon from '@/shared/assets/svgs/timer/ic_deactivated_clock.svg?react'; +import OnlineIcon from '@/shared/assets/svgs/timer/ic_online.svg?react'; + +import { useTimerCount } from '../hooks/useTimerCount'; + +interface CarouselFriendProps { + id: number; + image: string; + name: string; + time: number; + categoryName: string; + isPlaying: boolean; + isOnline: boolean; +} + +/** + * 캐러셀에 표시되는 친구 아이템 컴포넌트 + * 타이머 시간 계산은 useTimerCount 훅을 사용하여 관심사 분리 + */ +const CarouselFriend = memo(function CarouselFriend({ + image, + name, + time, + categoryName, + isPlaying, + isOnline, +}: CarouselFriendProps) { + const { timer } = useTimerCount({ + isPlaying, + previousTime: time, + shouldRun: isPlaying, + }); + + const formattedTime = formatSeconds(timer); + const ClockIcon = isPlaying ? ActivatedClockIcon : DeactivatedClockIcon; + + return ( +
    + {/* 프로필 이미지 영역 */} + + {`${name}의 { + e.currentTarget.onerror = null; + e.currentTarget.src = DefaultProfileIcon; + }} + className="h-[7.4rem] w-[7.4rem] rounded-full" + /> + {isOnline && ( + + )} + + + {/* 타이머 표시 영역 */} +
    + + {formattedTime} +
    + + {/* 이름 및 카테고리 표시 */} + {name} + {categoryName} +
    + ); +}); + +export default CarouselFriend; diff --git a/src/app/pages/TimerPage/Carousel/ContainerCarousel/ContainerCarousel.tsx b/src/app/pages/TimerPage/Carousel/ContainerCarousel/ContainerCarousel.tsx deleted file mode 100644 index 8410c792..00000000 --- a/src/app/pages/TimerPage/Carousel/ContainerCarousel/ContainerCarousel.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { formatSeconds } from '@/shared/utils/time'; - -import DefaultProfileIcon from '@/shared/assets/svgs/default_profile.svg'; -import ActivatedClockIcon from '@/shared/assets/svgs/icon_clock.svg?react'; -import DeactivatedClockIcon from '@/shared/assets/svgs/timer/ic_deactivated_clock.svg?react'; -import OnlineIcon from '@/shared/assets/svgs/timer/ic_online.svg?react'; - -import useCarouselTimer from './hooks/useCarouselTimer'; - -interface ContainerCarouselProps { - image: string; - name: string; - time: number; - categoryname: string; - isPlaying: boolean; - isOnline: boolean; -} - -const ContainerCarousel = ({ image, name, time, categoryname, isPlaying, isOnline }: ContainerCarouselProps) => { - const timer = useCarouselTimer({ isPlaying, previousTime: time }); - const formattedTime = formatSeconds(timer); - - const ClockIcon = isPlaying ? ActivatedClockIcon : DeactivatedClockIcon; - - return ( - <> -
    - - 유저 프로필 { - e.currentTarget.onerror = null; - e.currentTarget.src = DefaultProfileIcon; - }} - className="h-[7.4rem] w-[7.4rem] rounded-full" - /> - {isOnline && ( - - )} - - -
    - - {formattedTime} -
    - {name} - {categoryname} -
    - - ); -}; - -export default ContainerCarousel; diff --git a/src/app/pages/TimerPage/Carousel/ContainerCarousel/hooks/useCarouselTimer.ts b/src/app/pages/TimerPage/Carousel/ContainerCarousel/hooks/useCarouselTimer.ts deleted file mode 100644 index f7b0c2e5..00000000 --- a/src/app/pages/TimerPage/Carousel/ContainerCarousel/hooks/useCarouselTimer.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { useEffect, useState } from 'react'; - -interface UseTimerCountProps { - isPlaying: boolean; - previousTime: number; -} - -const useCarouselTimer = ({ isPlaying, previousTime }: UseTimerCountProps) => { - const [timer, setTimer] = useState(previousTime); - - useEffect(() => { - let timerIntervalId: ReturnType; - - if (isPlaying) { - timerIntervalId = setInterval(() => { - setTimer((prevTimer) => prevTimer + 1); - }, 1000); - } - - return () => { - if (timerIntervalId) clearInterval(timerIntervalId); - }; - }, [isPlaying]); - - return timer; -}; - -export default useCarouselTimer; diff --git a/src/app/pages/TimerPage/MainTimer/MainTimer.tsx b/src/app/pages/TimerPage/MainTimer/MainTimer.tsx new file mode 100644 index 00000000..042f29ce --- /dev/null +++ b/src/app/pages/TimerPage/MainTimer/MainTimer.tsx @@ -0,0 +1,53 @@ +import React, { useCallback } from 'react'; + +import { getFormattedTimeInfo } from '@/pages/TimerPage/utils/timeFormat'; + +import { useTimerContext } from '../contexts/TimerContext'; +import TimerDisplay from './TimerDisplay/TimerDisplay'; +import TimerHeader from './TimerHeader/TimerHeader'; + +/** + * 메인 타이머 컴포넌트 + * + * 타이머 헤더와 타이머 디스플레이를 포함하며, 타이머 상태와 재생/정지 기능을 관리. + */ +const MainTimer = () => { + // 타이머 컨텍스트에서 필요한 상태와 액션만 가져오기 + const { timer, elapsedTime, totalElapsedTimeOfToday, isPlaying, selectedTask, actions } = useTimerContext(); + + // 작업이 선택되어 있는지 여부 + const hasSelectedTask = selectedTask.id !== null; + + // 재생/정지 토글 핸들러 - 작업이 선택되지 않은 경우 처리 방지 + const handlePlayPauseToggle = useCallback(() => { + if (!hasSelectedTask) return; + + // 현재 상태의 반대로 토글 + actions.togglePlay(!isPlaying); + }, [isPlaying, hasSelectedTask, actions]); + + // 표시할 시간 정보 계산 - 유틸 함수로 분리하여 관심사 분리 + const timeInfo = getFormattedTimeInfo(timer, elapsedTime, totalElapsedTimeOfToday); + + return ( +
    + {/* 타이머 헤더 - 선택된 작업 정보 표시 */} + + + {/* 타이머 디스플레이 - 시간 표시 및 제어 버튼 */} + +
    + ); +}; + +export default MainTimer; diff --git a/src/app/pages/TimerPage/Timer/ButtonTimerPlay/ButtonTimerPlay.tsx b/src/app/pages/TimerPage/MainTimer/TimerDisplay/ButtonTimerPlay/ButtonTimerPlay.tsx similarity index 100% rename from src/app/pages/TimerPage/Timer/ButtonTimerPlay/ButtonTimerPlay.tsx rename to src/app/pages/TimerPage/MainTimer/TimerDisplay/ButtonTimerPlay/ButtonTimerPlay.tsx diff --git a/src/app/pages/TimerPage/Timer/ProgressCircle/ProgressCircle.tsx b/src/app/pages/TimerPage/MainTimer/TimerDisplay/ProgressCircle/ProgressCircle.tsx similarity index 100% rename from src/app/pages/TimerPage/Timer/ProgressCircle/ProgressCircle.tsx rename to src/app/pages/TimerPage/MainTimer/TimerDisplay/ProgressCircle/ProgressCircle.tsx diff --git a/src/app/pages/TimerPage/MainTimer/TimerDisplay/TimerDisplay.tsx b/src/app/pages/TimerPage/MainTimer/TimerDisplay/TimerDisplay.tsx new file mode 100644 index 00000000..7909fe97 --- /dev/null +++ b/src/app/pages/TimerPage/MainTimer/TimerDisplay/TimerDisplay.tsx @@ -0,0 +1,41 @@ +import React from 'react'; + +import InnerCircleIcon from '@/shared/assets/svgs/timer/ic_timer_inner_circle.svg?react'; + +import ButtonTimerPlay from './ButtonTimerPlay/ButtonTimerPlay'; +import ProgressCircle from './ProgressCircle/ProgressCircle'; + +/** + * 타이머 디스플레이 컴포넌트 props 타입 정의 + */ +interface TimerDisplayProps { + statusText: string; + formattedTimeText: string; + timer: number; + isPlaying: boolean; + onToggle: () => void; +} + +/** + * 타이머 디스플레이 컴포넌트 + * + * 타이머의 시간 표시, 상태 텍스트 및 재생/정지 버튼을 포함하는 UI + */ +const TimerDisplay = ({ statusText, formattedTimeText, timer, isPlaying, onToggle }: TimerDisplayProps) => { + return ( +
    + + +
    +
    + {statusText} + {formattedTimeText} +
    + + +
    +
    + ); +}; + +export default TimerDisplay; diff --git a/src/app/pages/TimerPage/MainTimer/TimerHeader/TimerHeader.tsx b/src/app/pages/TimerPage/MainTimer/TimerHeader/TimerHeader.tsx new file mode 100644 index 00000000..271e5b4b --- /dev/null +++ b/src/app/pages/TimerPage/MainTimer/TimerHeader/TimerHeader.tsx @@ -0,0 +1,34 @@ +import React from 'react'; + +/** + * 타이머 헤더 컴포넌트 props 타입 정의 + */ +interface TimerHeaderProps { + selectedTaskName: string; + selectedTaskCategoryName: string; + hasSelectedTask: boolean; +} + +/** + * 타이머 헤더 컴포넌트 + * + * 선택된 할일의 이름과 카테고리를 표시하거나, 할일이 선택되지 않은 경우 안내 메시지를 표시. + */ +const TimerHeader = ({ selectedTaskName, selectedTaskCategoryName, hasSelectedTask }: TimerHeaderProps) => { + if (!hasSelectedTask) { + return ( +
    +

    할일을 선택해주세요

    +
    + ); + } + + return ( +
    +

    {selectedTaskName}

    +

    {selectedTaskCategoryName}

    +
    + ); +}; + +export default TimerHeader; diff --git a/src/app/pages/TimerPage/NavigationButtons/NavigationButtons.tsx b/src/app/pages/TimerPage/NavigationButtons/NavigationButtons.tsx new file mode 100644 index 00000000..3750d488 --- /dev/null +++ b/src/app/pages/TimerPage/NavigationButtons/NavigationButtons.tsx @@ -0,0 +1,27 @@ +import React from 'react'; + +import HamburgerIcon from '@/shared/assets/svgs/btn_hamburger.svg?react'; +import HomeIcon from '@/shared/assets/svgs/btn_home.svg?react'; + +interface NavigationButtonsProps { + onHomeClick: () => void; + onSidebarToggle: () => void; +} + +/** + * 네비게이션 버튼 컴포넌트 + */ +const NavigationButtons = ({ onHomeClick, onSidebarToggle }: NavigationButtonsProps) => { + return ( +
    + + +
    + ); +}; + +export default NavigationButtons; diff --git a/src/app/pages/TimerPage/PopoverAllowedService/PopoverAllowedService.tsx b/src/app/pages/TimerPage/PopoverAllowedService/PopoverAllowedService.tsx deleted file mode 100644 index 93404899..00000000 --- a/src/app/pages/TimerPage/PopoverAllowedService/PopoverAllowedService.tsx +++ /dev/null @@ -1,143 +0,0 @@ -import { useEffect, useState } from 'react'; - -import type { PopoverAllowedSitesType } from '@/shared/types/allowedService'; - -import { COLOR_PALETTE_MAP } from '@/shared/constants/colorPalette'; - -import { getServiceFavicon } from '@/pages/OnboardingPage/utils/serviceUrl'; -import { usePostApplyAllowedServiceGroup } from '@/shared/apisV2/timer/timer.mutations'; -import { useGetPopoverAllowedServiceList } from '@/shared/apisV2/timer/timer.queries'; - -import Checkbox from './Checkbox/Checkbox'; - -const PopoverAllowedService = ({ onCancel }: { onCancel: () => void }) => { - const [selectedServiceGroupId, setSelectedServiceGroupId] = useState(null); - const [checkedServiceGroupIds, setCheckedServiceGroupIds] = useState([]); - const [activeAllowedSites, setActiveAllowedSites] = useState([]); - - const { data: allowedServiceList } = useGetPopoverAllowedServiceList(); - const { mutate: applyAllowedServiceGroup } = usePostApplyAllowedServiceGroup({ - allowedGroupIdList: checkedServiceGroupIds, - }); - - const isCheckedServiceGroupId = (id: number) => checkedServiceGroupIds.includes(id); - - const handleAllowedServiceGroupClick = (id: number) => { - setSelectedServiceGroupId(id); - changeActiveAllowedSites(id); - }; - - const changeActiveAllowedSites = (selectedServiceGroupId: number) => { - const activeAllowedSites = allowedServiceList?.data?.find( - (group) => group.id === selectedServiceGroupId, - )?.allowedSites; - - if (activeAllowedSites) { - setActiveAllowedSites(activeAllowedSites); - } - }; - - const toggleCheckedServiceGroupIds = (id: number) => { - if (isCheckedServiceGroupId(id)) { - const newActiveServiceGroupIds = checkedServiceGroupIds.filter((checkedId) => checkedId !== id); - setCheckedServiceGroupIds(newActiveServiceGroupIds); - } else { - const newActiveServiceGroupIds = [...checkedServiceGroupIds, id]; - setCheckedServiceGroupIds(newActiveServiceGroupIds); - } - }; - - const handleApplyAllowedServiceGroup = () => { - applyAllowedServiceGroup(undefined, { - onSuccess: () => { - onCancel(); - }, - }); - }; - - useEffect(() => { - if (allowedServiceList) { - const registeredNames = [] as string[]; - - allowedServiceList.data.forEach((group) => { - if (group.selected) { - if (!isCheckedServiceGroupId(group.id)) { - setCheckedServiceGroupIds((prev) => [...prev, group.id]); - } - - registeredNames.push(...group.allowedSites.map((site) => site.siteName)); - } - }); - } - }, [allowedServiceList]); - - return ( -
    -
    -

    허용서비스 세트

    -
      - {allowedServiceList?.data?.map((service) => ( -
    • handleAllowedServiceGroupClick(service.id)} - tabIndex={0} - > -
      { - e.stopPropagation(); - }} - className="h-[2.8rem] w-[2.8rem] p-[0.7rem]" - > - { - toggleCheckedServiceGroupIds(service.id); - }} - checked={isCheckedServiceGroupId(service.id)} - /> -
      -
      - {service.name} -
      - - -
    • - ))} -
    -
    - - -
    -
    - -
    -

    허용할 사이트

    -
      - {activeAllowedSites.map((siteInfo) => ( -
    • - {`${siteInfo.siteName} - {siteInfo.siteName} -
    • - ))} -
    -
    -
    - ); -}; - -export default PopoverAllowedService; diff --git a/src/app/pages/TimerPage/SideMenuTimer/SideMenuTimer.tsx b/src/app/pages/TimerPage/SideMenuTimer/SideMenuTimer.tsx new file mode 100644 index 00000000..2e8561eb --- /dev/null +++ b/src/app/pages/TimerPage/SideMenuTimer/SideMenuTimer.tsx @@ -0,0 +1,163 @@ +import { useCallback, useRef, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; + +import { useQueryClient } from '@tanstack/react-query'; + +import BoxTodo from '@/shared/components/BoxTodo/BoxTodo'; +import ButtonRadius5 from '@/shared/components/ButtonRadius5/ButtonRadius5'; +import ButtonTodoToggle from '@/shared/components/ButtonTodayToggle/ButtonTodoToggle'; +import Spacer from '@/shared/components/Spacer/Spacer'; + +import { TimerTodoType } from '@/shared/types/tasks'; + +import BtnListIcon from '@/shared/assets/svgs/btn_list.svg?react'; + +import { getTimerIncreasedTime } from '@/pages/TimerPage/utils/timeFormat'; +import { usePostToggleTaskStatus } from '@/shared/apisV2/common/common.mutations'; +import { timerKeys } from '@/shared/apisV2/timer/timer.keys'; + +import { useTimerContext } from '../contexts/TimerContext'; + +interface SideMenuTimerProps { + completedTodos: TimerTodoType[]; + ongoingTodos: TimerTodoType[]; +} + +/** + * 타이머 사이드바 컴포넌트 + */ +const SideMenuTimer = ({ ongoingTodos = [], completedTodos = [] }: SideMenuTimerProps) => { + const { todayFormattedDate, timer, isPlaying, selectedTask, isSidebarOpen, actions } = useTimerContext(); + + const [completedTodoToggle, setCompletedTodoToggle] = useState(false); + const sidebarRef = useRef(null); + const navigate = useNavigate(); + const queryClient = useQueryClient(); + + const { mutate: toggleTaskStatus } = usePostToggleTaskStatus(); + + // 완료된 할일 토글 핸들러 + const handleCompletedTodoToggle = useCallback(() => { + setCompletedTodoToggle((prev) => !prev); + }, []); + + // 할일 클릭 핸들러 - 최적화됨 + const handleTodoClick = useCallback( + (todo: TimerTodoType) => { + if (selectedTask.id === todo.id) return; + + // 선택 처리 - 최적화된 selectTask 함수 사용 + actions.selectTask(todo.id, todo.elapsedTime, todo.name, todo.categoryName); + }, + [actions, selectedTask.id], + ); + + // 홈으로 이동 핸들러 + const handleNavigateHome = useCallback(async () => { + try { + // 타이머가 실행 중인 경우 먼저 정지 + if (isPlaying && selectedTask.id !== null && actions.stopCurrentTimer) { + await actions.stopCurrentTimer(selectedTask.id); + } + navigate('/home'); + } catch (error) { + console.error('홈으로 이동 중 오류 발생:', error); + } + }, [isPlaying, selectedTask.id, actions, navigate]); + + // 홈페이지 프리로드 + const handleMouseEnter = useCallback(() => { + import('@/pages/HomePage/HomePage').catch((error) => { + console.error('홈페이지를 받아오는데 오류가 발생했습니다.', error); + }); + }, []); + + // 할일 완료 상태 토글 핸들러 - 최적화됨 + const handleToggleTodoComplete = useCallback( + (taskId: number, isOngoing: boolean) => { + toggleTaskStatus( + { taskId }, + { + onSuccess: () => { + // 진행 중인 할일을 완료 처리했으면 완료 목록 토글 활성화 + if (isOngoing) { + setCompletedTodoToggle(true); + } + + queryClient.invalidateQueries({ + queryKey: timerKeys.todos({ targetDate: todayFormattedDate }), + }); + }, + }, + ); + }, + [toggleTaskStatus, todayFormattedDate, queryClient], + ); + + // 할일 항목 렌더링 함수 - 최적화됨 + const renderTodoItem = useCallback( + (todo: TimerTodoType, isOngoing: boolean) => ( + handleTodoClick(todo)} + onToggleComplete={() => handleToggleTodoComplete(todo.id, isOngoing)} + timerIncreasedTime={getTimerIncreasedTime(todo.id, todo.elapsedTime, selectedTask.id, timer)} + undeletable={true} + /> + ), + [handleTodoClick, handleToggleTodoComplete, selectedTask.id, timer], + ); + + return ( +
    + {/* 사이드바 헤더 */} +
    +

    오늘 할 일

    + +
    + + {/* 할일 목록 */} + + {!ongoingTodos.length && !completedTodos.length ? ( +
    +

    오늘 할 일이 없습니다.

    +
    + ) : ( + <> + {/* 진행 중인 할일 목록 */} + {ongoingTodos.map((todo) => renderTodoItem(todo, true))} + + {/* 완료된 할일 토글 섹션 */} + {completedTodos.length > 0 && ( + + {completedTodos.map((todo) => renderTodoItem(todo, false))} + + )} + + )} +
    + + {/* 하단 버튼 영역 */} +
    + + 홈으로 나가기 + +
    +
    + ); +}; + +export default SideMenuTimer; diff --git a/src/app/pages/TimerPage/SidebarTimer/SideBarTimer.tsx b/src/app/pages/TimerPage/SidebarTimer/SideBarTimer.tsx deleted file mode 100644 index c68b97ea..00000000 --- a/src/app/pages/TimerPage/SidebarTimer/SideBarTimer.tsx +++ /dev/null @@ -1,170 +0,0 @@ -import { useRef, useState } from 'react'; -import { useNavigate } from 'react-router-dom'; - -import { useQueryClient } from '@tanstack/react-query'; - -import BoxTodo from '@/shared/components/BoxTodo/BoxTodo'; -import ButtonRadius5 from '@/shared/components/ButtonRadius5/ButtonRadius5'; -import ButtonTodoToggle from '@/shared/components/ButtonTodayToggle/ButtonTodoToggle'; -import Spacer from '@/shared/components/Spacer/Spacer'; - -import { TimerTodoType } from '@/shared/types/tasks'; - -import BtnListIcon from '@/shared/assets/svgs/btn_list.svg?react'; - -import { usePostToggleTaskStatus } from '@/shared/apisV2/common/common.mutations'; -import { usePostUpdateTimerInfo } from '@/shared/apisV2/timer/timer.mutations'; - -interface CategoryBoxProps { - completedTodos: TimerTodoType[]; - ongoingTodos: TimerTodoType[]; - toggleSidebar: () => void; - selectedTodoName: string; - onTodoSelection: (id: number, time: number, name: string, categoryName: string) => void; - selectedTodo: number | null; - onPlayToggle: (isPlaying: boolean) => void; - isPlaying: boolean; - elapsedTime: number; - formattedTodayDate: string; - resetTimerIncreasedTime: () => void; - timerIncreasedTime: number; - isSideOpen: boolean; - resetAccumulatedIncreasedTime: () => void; -} - -const SideBarTimer = ({ - ongoingTodos = [], - completedTodos = [], - toggleSidebar, - onTodoSelection, - selectedTodo, - onPlayToggle, - isPlaying, - formattedTodayDate, - resetTimerIncreasedTime, - timerIncreasedTime, - isSideOpen, - resetAccumulatedIncreasedTime, -}: CategoryBoxProps) => { - const [completedTodoToggle, setCompletedTodoToggle] = useState(false); - - const handleCompletedTodoToggle = () => { - setCompletedTodoToggle((prev) => !prev); - }; - - const sidebarRef = useRef(null); - const navigate = useNavigate(); - const queryClient = useQueryClient(); - - const { mutate, isError, error } = usePostToggleTaskStatus(); - const { mutate: updateTimerInfo } = usePostUpdateTimerInfo(); - - const handleTodoClick = (id: number, time: number, name: string, categoryName: string) => { - if (isPlaying) { - if (selectedTodo !== null) { - updateTimerInfo( - { - taskId: selectedTodo, - elapsedTime: timerIncreasedTime, - targetDate: formattedTodayDate, - timerStatus: 'PAUSED', - }, - { - onSuccess: () => { - onPlayToggle(false); - queryClient.invalidateQueries({ queryKey: ['todo', formattedTodayDate] }); - resetTimerIncreasedTime(); - resetAccumulatedIncreasedTime(); - }, - }, - ); - } - } - resetTimerIncreasedTime(); - onTodoSelection(id, time, name, categoryName); - }; - - const handleNavigateHome = () => { - if (isPlaying && selectedTodo !== null) { - updateTimerInfo( - { - taskId: selectedTodo, - elapsedTime: timerIncreasedTime, - targetDate: formattedTodayDate, - timerStatus: 'PAUSED', - }, - { - onSuccess: () => { - onPlayToggle(false); - navigate('/home'); - }, - }, - ); - } else { - navigate('/home'); - } - }; - - const handleMouseEnter = () => { - import('@/pages/HomePage/HomePage').catch((error) => { - console.error('홈페이지를 받아오는데 오류가 발생했습니다.', error); - }); - }; - - if (isError) { - console.error(error); - } - - return ( -
    -
    -

    오늘 할 일

    - -
    - - {ongoingTodos.map((todo) => ( - handleTodoClick(todo.id, todo.elapsedTime, todo.name, todo.categoryName)} - onToggleComplete={() => - mutate( - { taskId: todo.id }, - { - onSuccess: () => { - setCompletedTodoToggle(true); - }, - }, - ) - } - timerIncreasedTime={todo.id === selectedTodo ? timerIncreasedTime : 0} - /> - ))} - - {completedTodos.map((todo) => ( - mutate({ taskId: todo.id })} - timerIncreasedTime={todo.id === selectedTodo ? timerIncreasedTime : 0} - /> - ))} - - -
    - - 홈으로 나가기 - -
    -
    - ); -}; - -export default SideBarTimer; diff --git a/src/app/pages/TimerPage/Timer/Timer.tsx b/src/app/pages/TimerPage/Timer/Timer.tsx deleted file mode 100644 index 10455877..00000000 --- a/src/app/pages/TimerPage/Timer/Timer.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import { useQueryClient } from '@tanstack/react-query'; - -import { formatSeconds } from '@/shared/utils/time'; - -import InnerCircleIcon from '@/shared/assets/svgs/timer/ic_timer_inner_circle.svg?react'; - -import { timerKeys } from '@/shared/apisV2/timer/timer.keys'; -import { usePostUpdateTimerInfo } from '@/shared/apisV2/timer/timer.mutations'; - -import ButtonTimerPlay from './ButtonTimerPlay/ButtonTimerPlay'; -import ProgressCircle from './ProgressCircle/ProgressCircle'; - -interface TaskTotalTimeProps { - selectedCategoryName: string; - selectedTodo: number | null; - onPlayToggle: (isPlaying: boolean) => void; - isPlaying: boolean; - formattedTodayDate: string; - timerTime: number; - timerIncreasedTime: number; - resetTimerIncreasedTime: () => void; - accumulatedTime: number; - resetAccumulatedIncreasedTime: () => void; - updateElapsedTime: (newTime: number) => void; -} - -const Timer = ({ - selectedCategoryName, - selectedTodo, - onPlayToggle, - isPlaying, - formattedTodayDate, - timerTime, - timerIncreasedTime, - resetTimerIncreasedTime, - accumulatedTime, - resetAccumulatedIncreasedTime, - updateElapsedTime, -}: TaskTotalTimeProps) => { - const queryClient = useQueryClient(); - - const { mutate: updateTimerInfo, isError, error } = usePostUpdateTimerInfo(); - - const handlePlayPauseToggle = () => { - if (selectedTodo !== null) { - if (isPlaying && selectedCategoryName.length > 0) { - updateElapsedTime(timerTime); - - updateTimerInfo( - { - taskId: selectedTodo, - elapsedTime: accumulatedTime, - targetDate: formattedTodayDate, - timerStatus: 'PAUSED', - }, - { - onSuccess: () => { - onPlayToggle(false); - resetTimerIncreasedTime(); - resetAccumulatedIncreasedTime(); - queryClient.invalidateQueries({ queryKey: timerKeys.timer }); - }, - }, - ); - } else { - updateTimerInfo( - { - taskId: selectedTodo, - elapsedTime: accumulatedTime, - targetDate: formattedTodayDate, - timerStatus: 'PAUSED', - }, - { - onSuccess: () => { - onPlayToggle(true); - }, - }, - ); - } - } - }; - - if (isError) { - console.error(error); - } - - const hours = Math.floor(accumulatedTime / 3600); - const minutes = Math.floor((accumulatedTime % 3600) / 60); - - return ( -
    - - -
    -
    - - {hours === 0 ? `오늘 ${minutes}분 몰입 중` : `오늘 ${hours}시간 ${minutes}분 몰입 중`} - - {formatSeconds(timerTime)}; -
    - - -
    -
    - ); -}; - -export default Timer; diff --git a/src/app/pages/TimerPage/TimerPage.tsx b/src/app/pages/TimerPage/TimerPage.tsx index 08329db5..27d7a8be 100644 --- a/src/app/pages/TimerPage/TimerPage.tsx +++ b/src/app/pages/TimerPage/TimerPage.tsx @@ -2,238 +2,109 @@ import dayjs from 'dayjs'; import timezone from 'dayjs/plugin/timezone'; import utc from 'dayjs/plugin/utc'; -import { useEffect, useMemo, useRef, useState } from 'react'; +import React, { useCallback, useMemo } from 'react'; import { useNavigate } from 'react-router-dom'; import { splitTasksByCompletion } from '@/shared/utils/timer'; -import { getBaseUrl } from '@/shared/utils/url'; - -import { TimerTodoType } from '@/shared/types/tasks'; - -import { DATE_FORMAT, DEFAULT_URL, TIMEZONE } from '@/shared/constants/timerPageText'; - -import HamburgerIcon from '@/shared/assets/svgs/btn_hamburger.svg?react'; -import HomeIcon from '@/shared/assets/svgs/btn_home.svg?react'; import { ROUTES_CONFIG } from '@/router/routesConfig'; -import { usePostUpdateTimerInfo } from '@/shared/apisV2/timer/timer.mutations'; -import { useGetPopoverAllowedServiceList, useGetTimerTodos } from '@/shared/apisV2/timer/timer.queries'; +import { useGetTimerTodos } from '@/shared/apisV2/timer/timer.queries'; +import AllowedServicesPopover from './AllowedServices/AllowedServicesPopover'; +import AllowedServicesTitle from './AllowedServices/AllowedServicesTitle'; import Carousel from './Carousel/Carousel'; -import PopoverAllowedService from './PopoverAllowedService/PopoverAllowedService'; -import SideBarTimer from './SidebarTimer/SideBarTimer'; -import TitleAllowedService from './TItleAllowedService/TitleAllowedService'; -import Timer from './Timer/Timer'; -import { useTimerCount } from './hooks/useTimerCount'; -import { useToggleSidebar } from './hooks/useToggleSidebar'; -import { useUrlHandler } from './hooks/useUrlHandler'; +import MainTimer from './MainTimer/MainTimer'; +import NavigationButtons from './NavigationButtons/NavigationButtons'; +import SideMenuTimer from './SideMenuTimer/SideMenuTimer'; +import { TimerProvider, useTimerContext } from './contexts/TimerContext'; +// 날짜 설정 플러그인 초기화 dayjs.extend(utc); dayjs.extend(timezone); -const TimerPage = () => { - const todayDate = dayjs().tz(TIMEZONE); - const formattedTodayDate = todayDate.format(DATE_FORMAT); - +/** + * 타이머 페이지 컨텐츠 컴포넌트 + * TimerProvider 내부에서 렌더링되어 Context에 접근 + */ +const TimerPageContent = () => { const navigate = useNavigate(); - const isUpdatingRef = useRef(null); - - const { data: todosData } = useGetTimerTodos({ targetDate: formattedTodayDate }); - - const { task: todos = [], totalTimeOfToday = 0 } = todosData?.data || {}; - const { ongoingTodos, completedTodos } = splitTasksByCompletion(todos); - const [selectedTodoId, setSelectedTodoId] = useState(null); - const [selectedTodoData, setSelectedTodoData] = useState(undefined); - const [isInitialRender, setIsInitialRender] = useState(true); - - const [registeredNames, setRegisteredNames] = useState([]); - const [allowedSitesUrl, setAllowedSitesUrl] = useState([]); - const [elapsedTime, setElapsedTime] = useState(0); - const [isPlaying, setIsPlaying] = useState(false); - const [isAllowedServiceVisible, setIsAllowedServiceVisible] = useState(false); - - const { data: allowedServiceList } = useGetPopoverAllowedServiceList(); - const { isSidebarOpen, handleSidebarToggle } = useToggleSidebar(); - const { mutate: updateTimerInfo } = usePostUpdateTimerInfo(); - - const handleUpdateTimerInfo = () => { - updateTimerInfo({ - taskId: selectedTodoId!, - elapsedTime: accumulatedTime, - targetDate: formattedTodayDate, - timerStatus: isPlaying ? 'RUNNING' : 'PAUSED', - }); - }; - - const { - timer: timerTime, - increasedTime: timerIncreasedTime, - resetIncreasedTime: resetTimerIncreasedTime, - } = useTimerCount({ isPlaying, previousTime: elapsedTime }); - const { timer: accumulatedTime, resetIncreasedTime: resetAccumulatedIncreasedTime } = useTimerCount({ - isPlaying, - previousTime: totalTimeOfToday, - }); - - useEffect(() => { - const currentUpdateRef = Math.floor(accumulatedTime / 40); // JavaScript의 타이머는 완벽하게 정확하지 않아서 40.001초나 39.999초와 같은 값이 될 수도 있으므로 Math.floor를 사용하여 소수점 이하를 버림 - - if (selectedTodoData && accumulatedTime % 40 === 0 && currentUpdateRef !== isUpdatingRef.current) { - handleUpdateTimerInfo(); - isUpdatingRef.current = currentUpdateRef; - } - }, [accumulatedTime]); - - const urls = useMemo(() => allowedSitesUrl.map((url) => url.trim()) || [], [allowedSitesUrl]); - - const baseUrls = useMemo(() => { - const mappedUrls = urls.map(getBaseUrl); - return [...mappedUrls, DEFAULT_URL]; - }, [urls]); - - useUrlHandler({ - isPlaying, - selectedTodo: selectedTodoId, - baseUrls, - stopTimer: updateTimerInfo, - formattedTodayDate, - timerIncreasedTime, - setIsPlaying, - getBaseUrl, - }); - - const handleTodoSelection = (id: number) => { - setSelectedTodoId(id); - }; - - const handlePlayToggle = (isPlaying: boolean) => { - setIsPlaying(isPlaying); - }; - - const handleMoribSetTitleClick = () => { - setIsAllowedServiceVisible(true); - }; - - const handleCancelClick = () => { - setIsAllowedServiceVisible(false); - }; - - const handleRegister = (selectedNames: string[]) => { - setRegisteredNames(selectedNames); - }; - - const updateElapsedTime = (newTime: number) => { - setElapsedTime(newTime); - }; - - useEffect(() => { - if (todosData && todosData.data.task.length > 0 && isInitialRender) { - const selectedId = todosData.data.task[0].id; - setSelectedTodoId(selectedId); - setIsInitialRender(false); - } + const { todayFormattedDate, isSidebarOpen, isAllowedServiceVisible, allowedServices, actions } = useTimerContext(); + + // 할일 데이터 조회 + const { data: todosData } = useGetTimerTodos({ targetDate: todayFormattedDate }); + + // 할일 목록 파싱 - useMemo로 불필요한 재계산 방지 + const { ongoingTodos, completedTodos } = useMemo(() => { + // data?.task가 없으면 빈 배열 반환 + const todos = todosData?.data?.task ?? []; + return splitTasksByCompletion(todos); }, [todosData]); - useEffect(() => { - if (selectedTodoId) { - setSelectedTodoData(todosData?.data.task.find((todo: TimerTodoType) => todo.id === selectedTodoId)); + const { isPlaying, selectedTask } = useTimerContext(); + + // 홈 네비게이션 핸들러 + const navigateToHome = useCallback(async () => { + try { + // 타이머가 실행 중인 경우 먼저 정지 + if (isPlaying && selectedTask.id !== null && actions.stopCurrentTimer) { + await actions.stopCurrentTimer(selectedTask.id); + } + navigate('/home'); + } catch (error) { + console.error('홈으로 이동 중 오류 발생:', error); } - }, [selectedTodoId]); - - useEffect(() => { - setElapsedTime(selectedTodoData?.elapsedTime || 0); - }, [selectedTodoData?.elapsedTime]); - - useEffect(() => { - if (allowedServiceList) { - const allowedSitesUrl = [] as string[]; - const groupNames = [] as string[]; - - allowedServiceList.data.forEach((group) => { - if (group.selected) { - groupNames.push(group.name); - if (group.allowedSites) { - group.allowedSites.forEach((site) => { - allowedSitesUrl.push(site.siteUrl); - }); - } - } - }); - const uniqueAllowedSites = Array.from(new Set(allowedSitesUrl)); - - handleRegister(groupNames); - setAllowedSitesUrl(uniqueAllowedSites); - } - }, [allowedServiceList]); + }, [isPlaying, selectedTask.id, actions, navigate]); return (
    - + {/* 허용 서비스 팝오버 */} {isAllowedServiceVisible && (
    - +
    )} -
    - - -
    + {/* 네비게이션 버튼 */} + + {/* 타이머 메인 영역 */}
    -
    -

    {selectedTodoData?.name || ''}

    -

    {selectedTodoData?.categoryName || ''}

    -
    - - - +
    + + +
    - + {/* 사이드 메뉴 */} +
    ); }; +/** + * 타이머 페이지 루트 컴포넌트 + * TimerProvider를 사용하여 컨텍스트 제공 + */ +const TimerPage = () => { + return ( + + + + ); +}; + export default TimerPage; diff --git a/src/app/pages/TimerPage/contexts/TimerContext.tsx b/src/app/pages/TimerPage/contexts/TimerContext.tsx new file mode 100644 index 00000000..ece3f26d --- /dev/null +++ b/src/app/pages/TimerPage/contexts/TimerContext.tsx @@ -0,0 +1,113 @@ +import dayjs from 'dayjs'; + +import React, { ReactNode, createContext, useContext } from 'react'; + +import { DATE_FORMAT, TIMEZONE } from '@/shared/constants/timerPageText'; + +import { useAllowedServices } from '../hooks/useAllowedServices'; +import { useTimerActions } from '../hooks/useTimerActions'; +import { useTimerState } from '../hooks/useTimerState'; +import { useUIState } from '../hooks/useUIState'; + +/** + * 타이머 컨텍스트 타입 정의 + */ +interface TimerContextType { + todayFormattedDate: string; + timer: number; + elapsedTime: number; + totalElapsedTimeOfToday: number; + isPlaying: boolean; + selectedTask: { + id: number | null; + name: string; + categoryName: string; + }; + isSidebarOpen: boolean; + isAllowedServiceVisible: boolean; + allowedServices: { + registeredNames: string[]; + allowedSiteUrls: string[]; + baseUrls: string[]; + }; + actions: { + togglePlay: (isPlaying: boolean) => void; + updateElapsedTime: (newTime: number) => void; + selectTask: (id: number, time: number, name: string, categoryName: string) => void; + toggleSidebar: () => void; + showAllowedServices: () => void; + hideAllowedServices: () => void; + stopCurrentTimer?: (taskId: number) => Promise; + }; +} + +/** + * 타이머 컨텍스트 생성 + */ +const TimerContext = createContext(undefined); + +/** + * 타이머 컨텍스트 사용을 위한 커스텀 훅 + */ +export const useTimerContext = () => { + const context = useContext(TimerContext); + if (!context) { + throw new Error('useTimerContext must be used within a TimerProvider'); + } + return context; +}; + +interface TimerProviderProps { + children: ReactNode; +} + +/** + * 타이머 컨텍스트 프로바이더 + */ +export const TimerProvider: React.FC = ({ children }) => { + // 날짜 포맷 설정 + const todayDate = dayjs().tz(TIMEZONE); + const todayFormattedDate = todayDate.format(DATE_FORMAT); + + // 타이머 상태 관리 훅 + const timerState = useTimerState(todayFormattedDate); + const { timer, elapsedTime, totalElapsedTimeOfToday, selectedTask, isPlaying, updateElapsedTime } = timerState; + + // UI 상태 관리 + const uiState = useUIState(); + const { isSidebarOpen, isAllowedServiceVisible, actions: uiActions } = uiState; + + // 허용 서비스 관리 + const allowedServices = useAllowedServices(); + + // 액션 관리 + const timerActions = useTimerActions(todayFormattedDate, { + ...timerState, + selectedTask, + isPlaying, + }); + + // Context 값 구성 + const contextValue: TimerContextType = { + todayFormattedDate, + timer, + elapsedTime, + totalElapsedTimeOfToday, + isPlaying, + selectedTask, + isSidebarOpen, + isAllowedServiceVisible, + allowedServices, + actions: { + togglePlay: timerActions.togglePlay, + updateElapsedTime, + selectTask: timerActions.selectTask, + toggleSidebar: uiActions.toggleSidebar, + showAllowedServices: uiActions.showAllowedServices, + hideAllowedServices: uiActions.hideAllowedServices, + stopCurrentTimer: timerActions.stopCurrentTimer, + }, + }; + + return {children}; +}; diff --git a/src/app/pages/TimerPage/hooks/useAllowedServices.ts b/src/app/pages/TimerPage/hooks/useAllowedServices.ts new file mode 100644 index 00000000..7a293dd6 --- /dev/null +++ b/src/app/pages/TimerPage/hooks/useAllowedServices.ts @@ -0,0 +1,52 @@ +import { useMemo } from 'react'; + +import { getBaseUrl } from '@/shared/utils/url'; + +import { DEFAULT_URL } from '@/shared/constants/timerPageText'; + +import { useGetPopoverAllowedServiceList } from '@/shared/apisV2/timer/timer.queries'; + +/** + * 허용된 서비스를 관리하는 커스텀 훅 + * React Query를 통해 받아온 데이터를 가공하여 필요한 형태로 반환 + * @returns 등록된 서비스 이름 목록, 허용된 사이트 URL 목록, 기본 URL을 포함한 URL 목록 + */ +export const useAllowedServices = () => { + // 허용된 서비스 목록 가져오기 + const { data: allowedServiceList } = useGetPopoverAllowedServiceList(); + + // 등록된 서비스 및 URL 정보 가공 + const { registeredNames, allowedSiteUrls } = useMemo(() => { + if (!allowedServiceList?.data) { + return { registeredNames: [], allowedSiteUrls: [] }; + } + + // 선택된 서비스 그룹만 필터링 + const selectedGroups = allowedServiceList.data.filter((group) => group.selected); + + // 그룹 이름만 추출 + const names = selectedGroups.map((group) => group.name); + + // 모든 허용된 사이트 URL 수집 및 중복 제거 + const urls = Array.from( + new Set(selectedGroups.flatMap((group) => group.allowedSites?.map((site) => site.siteUrl) || [])), + ); + + return { + registeredNames: names, + allowedSiteUrls: urls, + }; + }, [allowedServiceList?.data]); + + // 정리된 URL에서 기본 URL 추출 + const baseUrls = useMemo(() => { + const mappedUrls = allowedSiteUrls.map((url) => getBaseUrl(url.trim())); + return [...mappedUrls, DEFAULT_URL]; + }, [allowedSiteUrls]); + + return { + registeredNames, + allowedSiteUrls, + baseUrls, + }; +}; diff --git a/src/app/pages/TimerPage/hooks/useTimerActions.ts b/src/app/pages/TimerPage/hooks/useTimerActions.ts new file mode 100644 index 00000000..0ec2fafa --- /dev/null +++ b/src/app/pages/TimerPage/hooks/useTimerActions.ts @@ -0,0 +1,174 @@ +import { useCallback } from 'react'; + +import { useQueryClient } from '@tanstack/react-query'; + +import { timerKeys } from '@/shared/apisV2/timer/timer.keys'; +import { usePostSelectTimerTask, usePostTimerPause, usePostTimerRun } from '@/shared/apisV2/timer/timer.mutations'; + +/** + * 타이머 관련 액션들을 관리하는 훅 + * + * 타이머 시작/정지, 작업 선택 등의 액션을 제공. + * + * @param todayFormattedDate 포맷된 오늘 날짜 + * @param timerState 타이머 상태 객체 + * @returns 타이머 액션 함수들 + */ +export function useTimerActions( + todayFormattedDate: string, + timerState: { + selectedTask: { id: number | null }; + isPlaying: boolean; + setIsPlaying: (isPlaying: boolean) => void; + updateSelectedTaskState: (id: number, time: number, name: string, categoryName: string) => void; + }, +) { + const queryClient = useQueryClient(); + const { selectedTask, isPlaying, setIsPlaying, updateSelectedTaskState } = timerState; + + // API Mutations + const { mutateAsync: runTimer } = usePostTimerRun(); + const { mutateAsync: pauseTimer } = usePostTimerPause(); + const { mutateAsync: selectTimerTask } = usePostSelectTimerTask(); + + /** + * 특정 타이머 쿼리만 무효화 - 성능 최적화 + */ + const invalidateSelectedTimerQuery = useCallback(async () => { + await queryClient.invalidateQueries({ + queryKey: timerKeys.selectedTimerTask({ targetDate: todayFormattedDate }), + }); + }, [queryClient, todayFormattedDate]); + + /** + * 타이머 정지/실행 관련 쿼리 무효화 + */ + const invalidateTimerStatusQueries = useCallback(async () => { + const queries = [ + timerKeys.selectedTimerTask({ targetDate: todayFormattedDate }), + timerKeys.timerHeartBeat({ targetDate: todayFormattedDate }), + ]; + + await Promise.all(queries.map((queryKey) => queryClient.invalidateQueries({ queryKey }))); + }, [queryClient, todayFormattedDate]); + + /** + * 모든 타이머 관련 쿼리 무효화 - 필요한 경우에만 사용 + */ + const invalidateAllTimerQueries = useCallback(async () => { + const queries = [ + timerKeys.selectedTimerTask({ targetDate: todayFormattedDate }), + timerKeys.timerHeartBeat({ targetDate: todayFormattedDate }), + timerKeys.timer, + timerKeys.todos({ targetDate: todayFormattedDate }), + ]; + + await Promise.all(queries.map((queryKey) => queryClient.invalidateQueries({ queryKey }))); + }, [queryClient, todayFormattedDate]); + + /** + * 현재 실행 중인 타이머 정지 + * Promise를 반환하여 순서 보장 + */ + const stopCurrentTimer = useCallback( + async (taskId: number) => { + if (!taskId) return false; + + try { + await pauseTimer({ + taskId, + targetDate: todayFormattedDate, + }); + + setIsPlaying(false); + // 타이머 정지 시 상태 관련 쿼리만 무효화 + await invalidateTimerStatusQueries(); + return true; + } catch (error) { + console.error('타이머 정지 중 오류 발생:', error); + return false; + } + }, + [pauseTimer, todayFormattedDate, setIsPlaying, invalidateTimerStatusQueries], + ); + + /** + * 타이머 토글 함수 + */ + const togglePlay = useCallback( + async (shouldPlay: boolean) => { + if (!selectedTask.id) return; + + try { + if (shouldPlay) { + await runTimer({ + taskId: selectedTask.id, + targetDate: todayFormattedDate, + }); + setIsPlaying(true); + } else { + await pauseTimer({ + taskId: selectedTask.id, + targetDate: todayFormattedDate, + }); + setIsPlaying(false); + } + + // 타이머 상태 변경 시 필요한 쿼리만 무효화 + await invalidateTimerStatusQueries(); + } catch (error) { + console.error('타이머 토글 중 오류 발생:', error); + } + }, + [selectedTask.id, runTimer, pauseTimer, todayFormattedDate, setIsPlaying, invalidateTimerStatusQueries], + ); + + /** + * 타이머 작업 선택 함수 + * 현재 실행 중인 경우에만 pause하고, 순서를 보장. + * API 요청 최적화: 꼭 필요한 쿼리만 무효화 + */ + const selectTask = useCallback( + async (id: number, time: number, name: string, categoryName: string) => { + // 이미 선택된 작업을 다시 선택한 경우 무시 + if (selectedTask.id === id) return; + + try { + // 1. 현재 타이머가 실행 중인 경우에만 정지 + if (isPlaying && selectedTask.id !== null) { + await stopCurrentTimer(selectedTask.id); + } + + // 2. 새 작업 선택 API 호출 + await selectTimerTask({ + taskId: id, + targetDate: todayFormattedDate, + }); + + // 3. 로컬 상태 업데이트 (간단하게 시간만 갱신, 나머지는 서버 응답에 따라 업데이트) + updateSelectedTaskState(id, time, name, categoryName); + + // 4. 꼭 필요한 쿼리만 무효화 - 선택된 타이머 정보만 갱신 + await invalidateSelectedTimerQuery(); + } catch (error) { + console.error('작업 선택 중 오류 발생:', error); + } + }, + [ + selectedTask.id, + isPlaying, + selectTimerTask, + todayFormattedDate, + stopCurrentTimer, + updateSelectedTaskState, + invalidateSelectedTimerQuery, + ], + ); + + return { + togglePlay, + selectTask, + stopCurrentTimer, + invalidateAllTimerQueries, // 전체 무효화가 필요한 경우를 위해 노출 + }; +} diff --git a/src/app/pages/TimerPage/hooks/useTimerCount.ts b/src/app/pages/TimerPage/hooks/useTimerCount.ts index 6be7e216..7e8a0132 100644 --- a/src/app/pages/TimerPage/hooks/useTimerCount.ts +++ b/src/app/pages/TimerPage/hooks/useTimerCount.ts @@ -3,48 +3,80 @@ import { useEffect, useRef, useState } from 'react'; interface UseTimerCountProps { isPlaying: boolean; previousTime: number; + shouldRun?: boolean; } + interface UseTimerCountReturn { timer: number; increasedTime: number; resetIncreasedTime: () => void; } -export const useTimerCount = ({ isPlaying, previousTime }: UseTimerCountProps): UseTimerCountReturn => { +/** + * 타이머 카운트를 관리하는 커스텀 훅 + * + * 타이머의 초기 시간(previousTime)을 받아 시간이 증가하는 기능을 제공. + * isPlaying 상태에 따라 자동으로 타이머를 시작하거나 멈춤 + * + * @example + * // 사용 예시 + * const { timer, increasedTime, resetIncreasedTime } = useTimerCount({ + * isPlaying: true, + * previousTime: 60, // 1분의 초기 시간 + * }); + * + * @param isPlaying 타이머가 실행 중인지 여부 + * @param previousTime 이전 저장된 시간 (초 단위) + * @param shouldRun 타이머가 실행되어야 하는지 여부 (기본값: isPlaying) + * @returns 타이머 정보 객체 (현재 시간, 증가한 시간, 증가 시간 초기화 함수) + */ +export const useTimerCount = ({ + isPlaying, + previousTime, + shouldRun = isPlaying, +}: UseTimerCountProps): UseTimerCountReturn => { const [increasedTime, setIncreasedTime] = useState(0); const timerIntervalId = useRef | null>(null); + // 이전 시간이 변경되면 증가 시간 초기화 useEffect(() => { setIncreasedTime(0); }, [previousTime]); + // 타이머 실행/정지 처리 useEffect(() => { - if (isPlaying) { + const shouldStartTimer = isPlaying && shouldRun; + + if (shouldStartTimer) { + // 이미 실행 중인 타이머가 없으면 새로 시작 if (timerIntervalId.current === null) { timerIntervalId.current = setInterval(() => { setIncreasedTime((prevTime) => prevTime + 1); }, 1000); } } else { + // 실행 중인 타이머가 있으면 정지 if (timerIntervalId.current !== null) { clearInterval(timerIntervalId.current); timerIntervalId.current = null; - setIncreasedTime(0); } } + // 컴포넌트 언마운트 시 타이머 정리 return () => { if (timerIntervalId.current !== null) { clearInterval(timerIntervalId.current); timerIntervalId.current = null; } }; - }, [isPlaying]); + }, [isPlaying, shouldRun]); + // 증가 시간 초기화 함수 const resetIncreasedTime = () => { setIncreasedTime(0); }; + // 총 타이머 시간 = 이전 시간 + 증가 시간 const timer = previousTime + increasedTime; return { timer, increasedTime, resetIncreasedTime }; diff --git a/src/app/pages/TimerPage/hooks/useTimerState.ts b/src/app/pages/TimerPage/hooks/useTimerState.ts new file mode 100644 index 00000000..6ee4bbce --- /dev/null +++ b/src/app/pages/TimerPage/hooks/useTimerState.ts @@ -0,0 +1,139 @@ +import { useCallback, useMemo } from 'react'; + +import { useQueryClient } from '@tanstack/react-query'; + +import { timerKeys } from '@/shared/apisV2/timer/timer.keys'; +import { usePostTimerPause, usePostTimerRun } from '@/shared/apisV2/timer/timer.mutations'; +import { useGetSelectedTimerTask, useGetTimerHeartBeat } from '@/shared/apisV2/timer/timer.queries'; + +import { useAllowedServices } from './useAllowedServices'; +import { useTimerCount } from './useTimerCount'; +import { useUrlHandler } from './useUrlHandler'; + +/** + * 타이머 상태 관리 훅 + * + * @param todayFormattedDate 포맷된 오늘 날짜 + * @returns 타이머 상태와 관련 함수들 + */ +export function useTimerState(todayFormattedDate: string) { + const queryClient = useQueryClient(); + + const { mutate: pauseTimer } = usePostTimerPause(); + const { mutate: runTimer } = usePostTimerRun(); + + // 서버에서 선택된 타이머 작업 조회 + const { data: selectedTimerTaskData } = useGetSelectedTimerTask({ + targetDate: todayFormattedDate, + }); + + // 타이머 상태 추출 + const timerStatus = selectedTimerTaskData?.data?.timerStatus ?? 'PAUSED'; + const serverElapsedTime = selectedTimerTaskData?.data?.elapsedTime ?? 0; + const totalElapsedTimeOfToday = selectedTimerTaskData?.data?.totalElapsedTimeOfToday ?? 0; + + // 선택된 작업 정보 파싱 + const selectedTaskInfo = useMemo(() => { + const taskData = selectedTimerTaskData?.data; + + return { + id: taskData?.selectedTaskId ?? null, + name: taskData?.taskName ?? '', + categoryName: taskData?.runningCategoryName ?? '', + elapsedTime: taskData?.elapsedTime ?? 0, + status: taskData?.timerStatus ?? 'PAUSED', + }; + }, [selectedTimerTaskData]); + + // 타이머가 실행 중인지 여부 + const isPlaying = timerStatus === 'RUNNING'; + + // isPlaying 상태 변경 콜백 + const setIsPlaying = useCallback( + (playing: boolean) => { + if (!selectedTaskInfo.id) return; + + // 현재 상태와 다를 때만 API 호출 + if (playing !== isPlaying) { + const payload = { + taskId: selectedTaskInfo.id, + targetDate: todayFormattedDate, + }; + + if (playing) { + runTimer(payload, { + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: timerKeys.selectedTimerTask({ targetDate: todayFormattedDate }), + }); + }, + }); + } else { + pauseTimer(payload, { + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: timerKeys.selectedTimerTask({ targetDate: todayFormattedDate }), + }); + }, + }); + } + } + }, + [isPlaying, pauseTimer, queryClient, runTimer, selectedTaskInfo.id, todayFormattedDate], + ); + + // 허용된 서비스 목록 관리 + const { baseUrls } = useAllowedServices(); + + // URL 변경 감지 및 처리 + useUrlHandler({ + isPlaying, + selectedTaskId: selectedTaskInfo.id, + baseUrls, + todayFormattedDate, + setIsPlaying, + }); + + // 타이머가 실행 중일 때만 heartBeat API 호출 + const { data: heartBeatData } = useGetTimerHeartBeat( + { + targetDate: todayFormattedDate, + }, + isPlaying && selectedTaskInfo.id !== null, + ); + + // 최신 경과 시간 (heartBeat 또는 선택된 작업 데이터에서) + const currentElapsedTime = heartBeatData?.data?.elapsedTime ?? serverElapsedTime; + + // 타이머 카운트 훅 + const { timer } = useTimerCount({ + isPlaying, + previousTime: currentElapsedTime, + shouldRun: isPlaying, + }); + + /** + * 선택된 작업 상태 업데이트 함수 + * 서버에 상태 업데이트 후 쿼리를 무효화하여 최신 데이터로 UI를 갱신. + */ + const updateSelectedTaskState = useCallback(() => { + // 필요한 경우 쿼리 무효화 + queryClient.invalidateQueries({ queryKey: timerKeys.selectedTimerTask({ targetDate: todayFormattedDate }) }); + queryClient.invalidateQueries({ queryKey: timerKeys.timerHeartBeat({ targetDate: todayFormattedDate }) }); + }, [queryClient, todayFormattedDate]); + + return { + timer, + isPlaying, + setIsPlaying, + elapsedTime: currentElapsedTime, + totalElapsedTimeOfToday, + updateElapsedTime: updateSelectedTaskState, // 기존 API 호환성 유지 + selectedTask: { + id: selectedTaskInfo.id, + name: selectedTaskInfo.name, + categoryName: selectedTaskInfo.categoryName, + }, + updateSelectedTaskState, + }; +} diff --git a/src/app/pages/TimerPage/hooks/useToggleSidebar.ts b/src/app/pages/TimerPage/hooks/useToggleSidebar.ts deleted file mode 100644 index 97728454..00000000 --- a/src/app/pages/TimerPage/hooks/useToggleSidebar.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { useState } from 'react'; - -export const useToggleSidebar = () => { - const [isSidebarOpen, setIsSidebarOpen] = useState(false); - - const handleSidebarToggle = () => { - setIsSidebarOpen((prev) => !prev); - }; - - return { - isSidebarOpen, - handleSidebarToggle, - }; -}; diff --git a/src/app/pages/TimerPage/hooks/useUIState.ts b/src/app/pages/TimerPage/hooks/useUIState.ts new file mode 100644 index 00000000..26f6eccf --- /dev/null +++ b/src/app/pages/TimerPage/hooks/useUIState.ts @@ -0,0 +1,47 @@ +import { useState } from 'react'; + +/** + * 타이머 UI 상태를 관리하는 훅 + * + * 사이드바 토글, 모립셋 가시성과 같은 UI 관련 상태를 관리. + * + * @returns UI 상태와 액션 함수들 + */ +export function useUIState() { + // 사이드바 관련 상태 + const [isSidebarOpen, setIsSidebarOpen] = useState(false); + + // 모립셋 허용 서비스 상태 + const [isAllowedServiceVisible, setIsAllowedServiceVisible] = useState(false); + + /** + * 사이드바 토글 함수 + */ + const toggleSidebar = () => { + setIsSidebarOpen((prev) => !prev); + }; + + /** + * 모립셋 UI 핸들러 + */ + const showAllowedServices = () => { + setIsAllowedServiceVisible(true); + }; + + const hideAllowedServices = () => { + setIsAllowedServiceVisible(false); + }; + + return { + // 상태 + isSidebarOpen, + isAllowedServiceVisible, + + // 액션 + actions: { + toggleSidebar, + showAllowedServices, + hideAllowedServices, + }, + }; +} diff --git a/src/app/pages/TimerPage/hooks/useUrlHandler.ts b/src/app/pages/TimerPage/hooks/useUrlHandler.ts index f576aa09..1d58b42d 100644 --- a/src/app/pages/TimerPage/hooks/useUrlHandler.ts +++ b/src/app/pages/TimerPage/hooks/useUrlHandler.ts @@ -1,58 +1,79 @@ -import { useEffect } from 'react'; +import { useCallback, useEffect } from 'react'; -import { PostUpdateTimerInfoReq } from '@/shared/types/api/timer'; +import { useQueryClient } from '@tanstack/react-query'; + +import { getBaseUrl } from '@/shared/utils/url'; + +import { timerKeys } from '@/shared/apisV2/timer/timer.keys'; +import { usePostTimerPause } from '@/shared/apisV2/timer/timer.mutations'; interface UseUrlHandlerProps { isPlaying: boolean; - selectedTodo: number | null; + selectedTaskId: number | null; baseUrls: string[]; - stopTimer: (params: PostUpdateTimerInfoReq, options: { onSuccess: () => void }) => void; - timerIncreasedTime: number; + todayFormattedDate: string; setIsPlaying: (isPlaying: boolean) => void; - getBaseUrl: (url: string) => string; - formattedTodayDate: string; } -export const useUrlHandler = ({ +/** + * URL 변경을 감지하여 허용되지 않은 사이트로 이동 시 타이머를 정지하는 훅 + * + * @param props 타이머 상태와 관련 함수 + */ +export function useUrlHandler({ isPlaying, - selectedTodo, + selectedTaskId, baseUrls, - stopTimer, - timerIncreasedTime, + todayFormattedDate, setIsPlaying, - getBaseUrl, - formattedTodayDate, -}: UseUrlHandlerProps) => { +}: UseUrlHandlerProps) { + const queryClient = useQueryClient(); + const { mutate: pauseTimer } = usePostTimerPause(); + + // 타이머 정지 함수 + const stopTimer = useCallback(() => { + if (!selectedTaskId) return; + + pauseTimer( + { + taskId: selectedTaskId, + targetDate: todayFormattedDate, + }, + { + onSuccess: () => { + setIsPlaying(false); + queryClient.invalidateQueries({ queryKey: timerKeys.timer }); + }, + }, + ); + }, [pauseTimer, todayFormattedDate, setIsPlaying, queryClient, selectedTaskId]); + + // URL 변경 이벤트 처리 useEffect(() => { - const handleMessage = (event: any) => { - if (event.detail.action === 'urlUpdated') { - const updatedUrl = event.detail.url.trim() + '/'; + // URL 변경 메시지 핸들러 + const handleMessage = (event: Event) => { + const customEvent = event as CustomEvent<{ + action: string; + url: string; + }>; + + if (customEvent.detail?.action === 'urlUpdated') { + const updatedUrl = customEvent.detail.url.trim() + '/'; const updatedBaseUrl = getBaseUrl(updatedUrl); - setTimeout(() => { - if (isPlaying && selectedTodo !== null && !baseUrls.includes(updatedBaseUrl)) { - stopTimer( - { - taskId: selectedTodo, - elapsedTime: timerIncreasedTime, - targetDate: formattedTodayDate, - timerStatus: 'PAUSED', - }, - { - onSuccess: () => { - setIsPlaying(false); - }, - }, - ); - } - }, 0); + // 허용되지 않은 사이트로 이동했고 타이머가 실행 중이면 정지 + if (isPlaying && selectedTaskId !== null && !baseUrls.includes(updatedBaseUrl)) { + stopTimer(); + } } }; + // 이벤트 리스너 등록 document.addEventListener('FROM_EXTENSION', handleMessage); + // 정리 함수 return () => { document.removeEventListener('FROM_EXTENSION', handleMessage); }; - }, [timerIncreasedTime, isPlaying, selectedTodo, stopTimer, baseUrls, getBaseUrl, formattedTodayDate, setIsPlaying]); -}; + }, [isPlaying, selectedTaskId, baseUrls, stopTimer]); +} diff --git a/src/app/pages/TimerPage/utils/timeFormat.ts b/src/app/pages/TimerPage/utils/timeFormat.ts new file mode 100644 index 00000000..4a99637c --- /dev/null +++ b/src/app/pages/TimerPage/utils/timeFormat.ts @@ -0,0 +1,52 @@ +import { formatSeconds } from '@/shared/utils/time'; + +/** + * 타이머 시간 정보를 포맷팅하는 유틸리티 함수 + * 표시할 시간 값을 계산하고 포맷팅. + * + * @param timer 현재 타이머 시간 (초 단위) + * @param elapsedTime 서버에서 받아온 총 경과 시간 (초 단위) + * @param totalElapsedTimeOfToday 서버에서 받아온 오늘의 총 작업 시간 (초 단위) + * @returns 포맷팅된 시간 정보 객체 + */ +export const getFormattedTimeInfo = (timer: number, elapsedTime: number, totalElapsedTimeOfToday: number) => { + const formattedTimeText = formatSeconds(timer); + + const totalTimeToday = totalElapsedTimeOfToday + (timer > elapsedTime ? timer - elapsedTime : 0); + + const hours = Math.floor(totalTimeToday / 3600); + const minutes = Math.floor((totalTimeToday % 3600) / 60); + + const statusText = hours === 0 ? `오늘 ${minutes}분 몰입 중` : `오늘 ${hours}시간 ${minutes}분 몰입 중`; + + return { + formattedTimeText, + statusText, + hours, + minutes, + seconds: timer % 60, + totalElapsedTimeToday: totalTimeToday, + }; +}; + +/** + * 타이머가 증가한 시간을 계산. + * 선택된 작업에 따라 타이머 증가 시간을 동기화. + * + * @param todoId 할일 ID + * @param todoElapsedTime 할일의 서버 저장 경과 시간 + * @param selectedTaskId 현재 선택된 작업 ID + * @param currentTimer 현재 타이머 시간 + * @returns 증가한 시간 (초 단위) + */ +export const getTimerIncreasedTime = ( + todoId: number, + todoElapsedTime: number, + selectedTaskId: number | null, + currentTimer: number, +) => { + if (todoId === selectedTaskId) { + return currentTimer - todoElapsedTime; + } + return 0; +}; diff --git a/src/app/router/Router.tsx b/src/app/router/Router.tsx index 412eb59f..f0d6729f 100644 --- a/src/app/router/Router.tsx +++ b/src/app/router/Router.tsx @@ -1,7 +1,7 @@ import type { Router } from '@remix-run/router'; import { Suspense, lazy } from 'react'; -import { Outlet, createHashRouter } from 'react-router-dom'; +import { Outlet, createBrowserRouter } from 'react-router-dom'; import ErrorBoundary from '@/shared/components/ErrorBoundary/ErrorBoundary'; import HeartBeatBoundary from '@/shared/components/HeartBeatBoundary/HeartBeatBoundary'; @@ -20,7 +20,7 @@ const RedirectPage = lazy(() => import('@/pages/RedirectPage/RedirectPage')); const OnboardingPage = lazy(() => import('@/pages/OnboardingPage/OnboardingPage')); const TimerPage = lazy(() => import('@/pages/TimerPage/TimerPage')); -const router: Router = createHashRouter([ +const router: Router = createBrowserRouter([ { //public 라우트들 path: '/', diff --git a/src/app/shared/apisV2/allowedService/allowedService.api.ts b/src/app/shared/apisV2/allowedService/allowedService.api.ts index acdeb33d..0a7f7c22 100644 --- a/src/app/shared/apisV2/allowedService/allowedService.api.ts +++ b/src/app/shared/apisV2/allowedService/allowedService.api.ts @@ -5,6 +5,7 @@ import { GetAllowedServiceGroupDetailRes, GetAllowedServiceListReq, GetAllowedServiceListRes, + GetRecommendedSitesReq, GetRecommendedSitesRes, PatchChangeAllowedServiceGroupColorReq, PatchChangeAllowedServiceGroupNameReq, @@ -21,7 +22,7 @@ const ALLOWED_SERVICE_ENDPOINT = { PATCH_CHANGE_ALLOWED_SERVICE_GROUP_NAME: 'api/v2/allowedGroup/:allowedGroupId/name', PATCH_CHANGE_ALLOWED_SERVICE_GROUP_COLOR: 'api/v2/allowedGroup/:allowedGroupId/colorCode', DELETE_ALLOWED_SERVICE_GROUP: 'api/v2/allowedGroup/:allowedGroupId', - GET_RECOMMENDED_STIES: 'api/v2/recommendSite', + GET_RECOMMENDED_SITES: 'api/v2/recommendSite', POST_ADD_ALLOWED_SERVICE: 'api/v2/allowedSite/:allowedGroupId', DELETE_ALLOWED_SERVICE: 'api/v2/allowedSite/:allowedSiteId', }; @@ -81,8 +82,10 @@ export const deleteAllowedServiceGroup = async ({ allowedGroupId }: DeleteAllowe return data; }; -export const getRecommendedSites = async (): Promise => { - const { data } = await authClient.get(ALLOWED_SERVICE_ENDPOINT.GET_RECOMMENDED_STIES); +export const getRecommendedSites = async ({ + allowedGroupId, +}: GetRecommendedSitesReq): Promise => { + const { data } = await authClient.get(ALLOWED_SERVICE_ENDPOINT.GET_RECOMMENDED_SITES, { params: { allowedGroupId } }); return data; }; diff --git a/src/app/shared/apisV2/allowedService/allowedService.keys.ts b/src/app/shared/apisV2/allowedService/allowedService.keys.ts index 485d7233..a55cc8cb 100644 --- a/src/app/shared/apisV2/allowedService/allowedService.keys.ts +++ b/src/app/shared/apisV2/allowedService/allowedService.keys.ts @@ -1,4 +1,8 @@ -import { GetAllowedServiceGroupDetailReq, GetAllowedServiceListReq } from '@/shared/types/api/allowedService'; +import { + GetAllowedServiceGroupDetailReq, + GetAllowedServiceListReq, + GetRecommendedSitesReq, +} from '@/shared/types/api/allowedService'; export const allowedServiceKeys = { allowedService: ['allowedService'] as const, @@ -6,5 +10,6 @@ export const allowedServiceKeys = { [...allowedServiceKeys.allowedService, 'list', connectType] as const, allowedServiceGroupDetail: ({ allowedGroupId, connectType }: GetAllowedServiceGroupDetailReq) => [...allowedServiceKeys.allowedService, 'group', allowedGroupId, connectType] as const, - recommendedSites: () => [...allowedServiceKeys.allowedService, 'recommendedSites'] as const, + recommendedSites: ({ allowedGroupId }: GetRecommendedSitesReq) => + [...allowedServiceKeys.allowedService, 'recommendedSites', allowedGroupId] as const, }; diff --git a/src/app/shared/apisV2/allowedService/allowedService.queries.ts b/src/app/shared/apisV2/allowedService/allowedService.queries.ts index b07805b4..0c455cef 100644 --- a/src/app/shared/apisV2/allowedService/allowedService.queries.ts +++ b/src/app/shared/apisV2/allowedService/allowedService.queries.ts @@ -1,6 +1,10 @@ import { useQuery } from '@tanstack/react-query'; -import { GetAllowedServiceGroupDetailReq, GetAllowedServiceListReq } from '@/shared/types/api/allowedService'; +import { + GetAllowedServiceGroupDetailReq, + GetAllowedServiceListReq, + GetRecommendedSitesReq, +} from '@/shared/types/api/allowedService'; import { getAllowedServiceGroupDetail, getAllowedServiceList, getRecommendedSites } from './allowedService.api'; import { allowedServiceKeys } from './allowedService.keys'; @@ -20,9 +24,9 @@ export const useGetAllowedServiceGroupDetail = ({ allowedGroupId, connectType }: }); }; -export const useGetRecommendedSites = () => { +export const useGetRecommendedSites = ({ allowedGroupId }: GetRecommendedSitesReq) => { return useQuery({ - queryKey: allowedServiceKeys.recommendedSites(), - queryFn: getRecommendedSites, + queryKey: allowedServiceKeys.recommendedSites({ allowedGroupId }), + queryFn: () => getRecommendedSites({ allowedGroupId }), }); }; diff --git a/src/app/shared/apisV2/client.ts b/src/app/shared/apisV2/client.ts index 3d3fe537..17907303 100644 --- a/src/app/shared/apisV2/client.ts +++ b/src/app/shared/apisV2/client.ts @@ -40,6 +40,7 @@ const addAuthInterceptor = (axiosClient: AxiosInstance) => { prevRequest.sent = true; // 401 에러가 떴을 때 토큰 재발급 try { + // NOTE: 추후에 로컬스토리지에 저장된 refreshToken을 사용하게끔 고치는 것이 필요할수있음 const { data } = await postReissueToken(); setAccessToken(data.accessToken); return axiosClient(prevRequest); diff --git a/src/app/shared/apisV2/onboarding/onboarding.mutations.ts b/src/app/shared/apisV2/onboarding/onboarding.mutations.ts index 9a8d1d86..4847636d 100644 --- a/src/app/shared/apisV2/onboarding/onboarding.mutations.ts +++ b/src/app/shared/apisV2/onboarding/onboarding.mutations.ts @@ -1,7 +1,5 @@ import { useMutation } from '@tanstack/react-query'; -import { AxiosError } from 'axios'; - import { ApiErrorResponseType } from '@/shared/types/api/error'; import { PostInterestAreaReq, PostInterestAreaRes } from '@/shared/types/api/onboarding'; diff --git a/src/app/shared/apisV2/timer/timer.api.ts b/src/app/shared/apisV2/timer/timer.api.ts index a2ff3b2b..db2ed7d1 100644 --- a/src/app/shared/apisV2/timer/timer.api.ts +++ b/src/app/shared/apisV2/timer/timer.api.ts @@ -1,11 +1,15 @@ import type { GetPopoverAllowedServiceListRes, + GetSelectedTimerTaskReq, + GetSelectedTimerTaskRes, GetTimerFriendsRes, + GetTimerHeartBeatReq, GetTimerTodosReq, GetTimerTodosRes, - GetUpdateTimerInfoReq, PostApplyAllowedServiceGroupReq, - PostUpdateTimerInfoReq, + PostSelectTimerTaskReq, + PostTimerPauseReq, + PostTimerRunReq, } from '@/shared/types/api/timer'; import { authClient } from '../client'; @@ -15,8 +19,11 @@ const TIMER_ENDPOINT = { GET_TIMER_FRIENDS: 'api/v2/timer/friends', GET_POPOVER_ALLOWED_SERVICE_LIST: 'api/v2/timer/allowedGroups', POST_APPLY_ALLOWED_SERVICE_GROUP: 'api/v2/timer/allowedGroups', - POST_TIMER_INFO_UPDATE: 'api/v2/timer/sync', - GET_TIMER_INFO_UPDATE: 'api/v2/timer/ping', + POST_TIMER_RUN: 'api/v2/timer/run', + POST_TIMER_PAUSE: 'api/v2/timer/pause', + POST_SELECT_TIMER_TASK: 'api/v2/timer/select', + GET_TIMER_TASK_LIST: 'api/v2/timer/selected', + GET_TIMER_HEART_BEAT: 'api/v2/timer/heart-beat', }; export const getTimerTodos = async ({ targetDate }: GetTimerTodosReq): Promise => { @@ -39,19 +46,31 @@ export const postApplyAllowedServiceGroup = async ({ allowedGroupIdList }: PostA return data; }; -export const postUpdateTimerInfo = async ({ taskId, elapsedTime, targetDate, timerStatus }: PostUpdateTimerInfoReq) => { - const { data } = await authClient.post(TIMER_ENDPOINT.POST_TIMER_INFO_UPDATE, { - taskId, - elapsedTime, - targetDate, - timerStatus, - }); +export const postTimerRun = async ({ taskId, targetDate }: PostTimerRunReq) => { + const { data } = await authClient.post(TIMER_ENDPOINT.POST_TIMER_RUN, { taskId, targetDate }); return data; }; -export const getUpdateTimerInfo = async ({ taskId, elapsedTime, targetDate, timerStatus }: GetUpdateTimerInfoReq) => { - const { data } = await authClient.get(TIMER_ENDPOINT.GET_TIMER_INFO_UPDATE, { - params: { taskId, elapsedTime, targetDate, timerStatus }, - }); +export const postTimerPause = async ({ taskId, targetDate }: PostTimerPauseReq) => { + const { data } = await authClient.post(TIMER_ENDPOINT.POST_TIMER_PAUSE, { taskId, targetDate }); + return data; +}; + +export const postSelectTimerTask = async ({ taskId, targetDate }: PostSelectTimerTaskReq) => { + const { data } = await authClient.post(TIMER_ENDPOINT.POST_SELECT_TIMER_TASK, { taskId, targetDate }); + return data; +}; + +export const getSelectedTimerTask = async ({ + targetDate, +}: GetSelectedTimerTaskReq): Promise => { + const { data } = await authClient.get(TIMER_ENDPOINT.GET_TIMER_TASK_LIST, { params: { targetDate } }); + return data; +}; + +export const getTimerHeartBeat = async ({ + targetDate, +}: GetTimerHeartBeatReq): Promise<{ data: { elapsedTime: number } }> => { + const { data } = await authClient.get(TIMER_ENDPOINT.GET_TIMER_HEART_BEAT, { params: { targetDate } }); return data; }; diff --git a/src/app/shared/apisV2/timer/timer.keys.ts b/src/app/shared/apisV2/timer/timer.keys.ts index 30ee812a..f9cd660f 100644 --- a/src/app/shared/apisV2/timer/timer.keys.ts +++ b/src/app/shared/apisV2/timer/timer.keys.ts @@ -1,16 +1,10 @@ -import { GetTimerTodosReq, GetUpdateTimerInfoReq } from '@/shared/types/api/timer'; +import { GetSelectedTimerTaskReq, GetTimerHeartBeatReq, GetTimerTodosReq } from '@/shared/types/api/timer'; export const timerKeys = { timer: ['timer'] as const, todos: ({ targetDate }: GetTimerTodosReq) => [...timerKeys.timer, targetDate], friends: () => [...timerKeys.timer, 'friends'], popover: () => [...timerKeys.timer, 'popover'], - updateTimerInfo: ({ taskId, elapsedTime, targetDate, timerStatus }: GetUpdateTimerInfoReq) => [ - ...timerKeys.timer, - 'updateTimerInfo', - taskId, - elapsedTime, - targetDate, - timerStatus, - ], + selectedTimerTask: ({ targetDate }: GetSelectedTimerTaskReq) => [...timerKeys.timer, 'selectedTimerTask', targetDate], + timerHeartBeat: ({ targetDate }: GetTimerHeartBeatReq) => [...timerKeys.timer, 'heartBeat', targetDate], }; diff --git a/src/app/shared/apisV2/timer/timer.mutations.ts b/src/app/shared/apisV2/timer/timer.mutations.ts index 2a8caa8f..56f41313 100644 --- a/src/app/shared/apisV2/timer/timer.mutations.ts +++ b/src/app/shared/apisV2/timer/timer.mutations.ts @@ -2,7 +2,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { PostApplyAllowedServiceGroupReq } from '@/shared/types/api/timer'; -import { postApplyAllowedServiceGroup, postUpdateTimerInfo } from './timer.api'; +import { postApplyAllowedServiceGroup, postSelectTimerTask, postTimerPause, postTimerRun } from './timer.api'; import { timerKeys } from './timer.keys'; export const usePostApplyAllowedServiceGroup = ({ allowedGroupIdList }: PostApplyAllowedServiceGroupReq) => { @@ -16,11 +16,33 @@ export const usePostApplyAllowedServiceGroup = ({ allowedGroupIdList }: PostAppl }); }; -export const usePostUpdateTimerInfo = () => { +export const usePostTimerRun = () => { const queryClient = useQueryClient(); return useMutation({ - mutationFn: postUpdateTimerInfo, + mutationFn: postTimerRun, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: timerKeys.timer }); + }, + }); +}; + +export const usePostTimerPause = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: postTimerPause, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: timerKeys.timer }); + }, + }); +}; + +export const usePostSelectTimerTask = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: postSelectTimerTask, onSuccess: () => { queryClient.invalidateQueries({ queryKey: timerKeys.timer }); }, diff --git a/src/app/shared/apisV2/timer/timer.queries.ts b/src/app/shared/apisV2/timer/timer.queries.ts index 2bc22a4a..5ff1fe2a 100644 --- a/src/app/shared/apisV2/timer/timer.queries.ts +++ b/src/app/shared/apisV2/timer/timer.queries.ts @@ -1,14 +1,33 @@ -import { useQuery } from '@tanstack/react-query'; +import { UseQueryOptions, useQuery } from '@tanstack/react-query'; -import { GetTimerTodosReq, GetUpdateTimerInfoReq } from '@/shared/types/api/timer'; +import { + GetSelectedTimerTaskReq, + GetSelectedTimerTaskRes, + GetTimerHeartBeatReq, + GetTimerTodosReq, + GetTimerTodosRes, +} from '@/shared/types/api/timer'; -import { getPopoverAllowedServiceList, getTimerFriends, getTimerTodos, getUpdateTimerInfo } from './timer.api'; +import { + getPopoverAllowedServiceList, + getSelectedTimerTask, + getTimerFriends, + getTimerHeartBeat, + getTimerTodos, +} from './timer.api'; import { timerKeys } from './timer.keys'; -export const useGetTimerTodos = ({ targetDate }: GetTimerTodosReq) => { - return useQuery({ +export const useGetTimerTodos = ( + { targetDate }: GetTimerTodosReq, + options?: Omit, 'queryKey' | 'queryFn'>, +) => { + return useQuery({ queryKey: timerKeys.todos({ targetDate }), queryFn: () => getTimerTodos({ targetDate }), + staleTime: 10000, // 10초 동안 캐시 데이터 사용 + gcTime: 5 * 60 * 1000, // 5분 동안 캐시 유지 + refetchOnWindowFocus: false, // 윈도우 포커스 시 자동 갱신 방지 + ...options, }); }; @@ -17,6 +36,7 @@ export const useGetTimerFriends = () => { queryKey: timerKeys.friends(), queryFn: getTimerFriends, refetchInterval: 58000, + staleTime: 30000, // 30초 동안 캐시 데이터 사용 }); }; @@ -27,12 +47,32 @@ export const useGetPopoverAllowedServiceList = () => { }); }; -export const useGetUpdateTimerInfoPing = ({ taskId, elapsedTime, targetDate, timerStatus }: GetUpdateTimerInfoReq) => { - return useQuery({ - queryKey: timerKeys.updateTimerInfo({ taskId, elapsedTime, targetDate, timerStatus }), - queryFn: () => getUpdateTimerInfo({ taskId, elapsedTime, targetDate, timerStatus }), - refetchInterval: 40000, +export const useGetSelectedTimerTask = ( + { targetDate }: GetSelectedTimerTaskReq, + options?: Omit, 'queryKey' | 'queryFn'>, +) => { + return useQuery({ + queryKey: timerKeys.selectedTimerTask({ targetDate }), + queryFn: () => getSelectedTimerTask({ targetDate }), + staleTime: 5000, // 5초 동안 캐시 데이터 사용 + ...options, + }); +}; + +export const useGetTimerHeartBeat = ( + { targetDate }: GetTimerHeartBeatReq, + isPlaying: boolean = true, + options?: Omit< + UseQueryOptions<{ data: { elapsedTime: number } }, Error, { data: { elapsedTime: number } }>, + 'queryKey' | 'queryFn' + >, +) => { + return useQuery<{ data: { elapsedTime: number } }, Error>({ + queryKey: timerKeys.timerHeartBeat({ targetDate }), + queryFn: () => getTimerHeartBeat({ targetDate }), + refetchInterval: 10000, refetchIntervalInBackground: true, - enabled: !!taskId, + enabled: isPlaying, + ...options, }); }; diff --git a/src/app/shared/assets/svgs/btn_cal_black.svg b/src/app/shared/assets/svgs/btn_cal_black.svg new file mode 100644 index 00000000..d4189304 --- /dev/null +++ b/src/app/shared/assets/svgs/btn_cal_black.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/app/shared/assets/svgs/ic_line.svg b/src/app/shared/assets/svgs/ic_line.svg new file mode 100644 index 00000000..29e3f027 --- /dev/null +++ b/src/app/shared/assets/svgs/ic_line.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/app/shared/assets/svgs/popover_add_category.svg b/src/app/shared/assets/svgs/popover_add_category.svg new file mode 100644 index 00000000..f010f365 --- /dev/null +++ b/src/app/shared/assets/svgs/popover_add_category.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/app/shared/assets/svgs/popover_add_todo.svg b/src/app/shared/assets/svgs/popover_add_todo.svg new file mode 100644 index 00000000..1824fab3 --- /dev/null +++ b/src/app/shared/assets/svgs/popover_add_todo.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/app/shared/assets/svgs/tooltip_triangle.svg b/src/app/shared/assets/svgs/tooltip_triangle.svg new file mode 100644 index 00000000..13c5af36 --- /dev/null +++ b/src/app/shared/assets/svgs/tooltip_triangle.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/app/shared/components/BoxTodo/BoxTodo.tsx b/src/app/shared/components/BoxTodo/BoxTodo.tsx index 68896617..627c9195 100644 --- a/src/app/shared/components/BoxTodo/BoxTodo.tsx +++ b/src/app/shared/components/BoxTodo/BoxTodo.tsx @@ -7,6 +7,7 @@ import { formatSeconds } from '@/shared/utils/time'; import type { TaskType } from '@/shared/types/tasks'; import ButtonCalendarIcon from '@/shared/assets/svgs/btn_cal.svg?react'; +import ButtonCalendartBlackIcon from '@/shared/assets/svgs/btn_cal_black.svg?react'; import CheckBoxBlankIcon from '@/shared/assets/svgs/check_box_blank.svg?react'; import CheckBoxFillIcon from '@/shared/assets/svgs/check_box_fill.svg?react'; import MeatballIcon from '@/shared/assets/svgs/common/ic_meatball_default.svg?react'; @@ -35,6 +36,8 @@ interface BoxTodoProps { timerIncreasedTime?: number; handleCalendarToggle?: (e: MouseEvent) => void; onPatchTask?: (taskId: number, name: string, startDate: string, endDate: string | null) => void; + activeCalendarTask?: boolean; + undeletable?: boolean; } const BoxTodo = ({ @@ -54,17 +57,21 @@ const BoxTodo = ({ timerIncreasedTime, handleCalendarToggle, onPatchTask, + activeCalendarTask, + undeletable = false, }: BoxTodoProps) => { const { mutate: deleteTask } = useDeleteTask(); - const formattedTime = formatSeconds(timerIncreasedTime ? elapsedTime + timerIncreasedTime : elapsedTime); + // timerIncreasedTime이 제공되면 그 값을 사용하고, 아니면 기본 elapsedTime 사용 + const displayTime = timerIncreasedTime !== undefined ? elapsedTime + timerIncreasedTime : elapsedTime; + const formattedTime = formatSeconds(displayTime); const formattedstartDate = startDate.replace(/-/g, '.'); const formattedendDate = endDate ? endDate.replace(/-/g, '.') : ''; const nameStyle = isComplete ? 'line-through' : ''; const CheckBoxIcon = isComplete ? : ; - const TimeIcon = elapsedTime ? : ; - const timeTextClass = elapsedTime ? 'text-mint-01' : 'text-gray-04'; + const TimeIcon = displayTime ? : ; + const timeTextClass = displayTime ? 'text-mint-01' : 'text-gray-04'; const selectedStyle = isSelected && !addingComplete @@ -121,7 +128,7 @@ const BoxTodo = ({ return (
    @@ -141,14 +148,14 @@ const BoxTodo = ({ /> ) : (

    {name}

    )}
    - {!addingComplete && !isSelected && !clickable && ( + {!isComplete && !isSelected && !clickable && !undeletable && ( @@ -166,15 +173,17 @@ const BoxTodo = ({ )}
    -
    +
    -
    +
    {TimeIcon}

    {formattedTime}

    diff --git a/src/app/shared/components/Calendar/HeaderCalendar/HeaderCalendar.tsx b/src/app/shared/components/Calendar/HeaderCalendar/HeaderCalendar.tsx index 8d0bcd80..698adf7d 100644 --- a/src/app/shared/components/Calendar/HeaderCalendar/HeaderCalendar.tsx +++ b/src/app/shared/components/Calendar/HeaderCalendar/HeaderCalendar.tsx @@ -27,7 +27,7 @@ const HeaderCalendar: React.FC = ({ return (
    -

    +

    {year}년 {month}월

    -
    +
    {WEEK_DAYS.map((day) => ( {day} ))} diff --git a/src/app/shared/components/Dropdown/Dropdown.tsx b/src/app/shared/components/Dropdown/Dropdown.tsx index 322c4567..882e012f 100644 --- a/src/app/shared/components/Dropdown/Dropdown.tsx +++ b/src/app/shared/components/Dropdown/Dropdown.tsx @@ -1,4 +1,4 @@ -import { ButtonHTMLAttributes, ReactNode, createContext, useContext, useRef, useState } from 'react'; +import { ButtonHTMLAttributes, ReactNode, createContext, forwardRef, useContext, useRef, useState } from 'react'; import useClickOutside from '../../hooks/useClickOutside'; @@ -7,14 +7,13 @@ interface DropdownContextProps { handleToggleOpen: () => void; handleToggleClose: () => void; } - const DropdownContext = createContext(null); // useDropdownContext: Select 컴포넌트 외부에서 서브 컴포넌트들이 사용됐을 때 에러 처리 const useDropdownContext = () => { const context = useContext(DropdownContext); if (!context) { - throw new Error('Select 컴포넌트는 Select 내에서 사용되어야 합니다.'); + throw new Error('Select 컴포넌트는 Select 내에서 사용되어야 합니다.'); } return context; }; @@ -22,14 +21,24 @@ const useDropdownContext = () => { // Dropdown root 컴포넌트 interface DropdownRootProps { children: ReactNode; + onOpenChange?: (open: boolean) => void; } -const DropdownRoot = ({ children }: DropdownRootProps) => { +const DropdownRoot = ({ children, onOpenChange }: DropdownRootProps) => { const [open, setOpen] = useState(false); + const ref = useRef(null); + + const handleToggleOpen = () => { + setOpen((prev) => { + const next = !prev; + onOpenChange?.(next); + return next; + }); + }; + const handleClose = () => { + setOpen(false); + onOpenChange?.(false); + }; - const handleToggleOpen = () => setOpen((prev) => !prev); - const handleClose = () => setOpen(false); - - const ref = useRef(null); useClickOutside(ref, handleClose); const contextValue: DropdownContextProps = { @@ -77,35 +86,36 @@ interface DropdownContentProps { className?: string; } -const DropdownContent = ({ children, maxHeight, boxShadow, className }: DropdownContentProps) => { - const { open, handleToggleClose } = useDropdownContext(); +const DropdownContent = forwardRef( + ({ children, maxHeight, boxShadow, className }, ref) => { + const { open, handleToggleClose } = useDropdownContext(); + const shadowStyle = boxShadow ?? 'shadow-[0_3px_30px_0_rgba(0,0,0,0.4)]'; - const shadowStyle = boxShadow ? boxShadow : 'shadow-[0_3px_30px_0_rgba(0,0,0,0.4)]'; - - return ( -
      - {open && children} -
    - ); -}; + return ( +
      + {open && children} +
    + ); + }, +); +DropdownContent.displayName = 'DropdownContent'; // Dropdown의 메뉴 리스트 아이템 컴포넌트 interface DropdownItemProps extends ButtonHTMLAttributes { label: string; textColor?: 'default' | 'red'; } - const DropdownItem = ({ label, textColor = 'default', ...props }: DropdownItemProps) => { const textStyle = textColor === 'red' ? 'text-error-01' : 'text-white'; - return (
  • @@ -118,5 +128,4 @@ const Dropdown = Object.assign(DropdownRoot, { Content: DropdownContent, Item: DropdownItem, }); - export default Dropdown; diff --git a/src/app/shared/components/ErrorBoundary/ErrorBoundary.tsx b/src/app/shared/components/ErrorBoundary/ErrorBoundary.tsx index 58f08185..84ad7c26 100644 --- a/src/app/shared/components/ErrorBoundary/ErrorBoundary.tsx +++ b/src/app/shared/components/ErrorBoundary/ErrorBoundary.tsx @@ -4,7 +4,8 @@ import { Component, ErrorInfo, ReactNode } from 'react'; import { AxiosError, isAxiosError } from 'axios'; -import { getErrorCategory, getErrorMessage, shouldShowFallbackUI } from '@/shared/utils/error'; +import { getErrorCategory, shouldShowFallbackUI } from '@/shared/utils/error'; +import { getErrorMessage } from '@/shared/utils/error'; import FallbackApiError from '../FallbackApiError/FallbackApiError'; diff --git a/src/app/shared/components/FaviconImage/FaviconImage.tsx b/src/app/shared/components/FaviconImage/FaviconImage.tsx new file mode 100644 index 00000000..ce48cfba --- /dev/null +++ b/src/app/shared/components/FaviconImage/FaviconImage.tsx @@ -0,0 +1,19 @@ +import { ImgHTMLAttributes } from 'react'; + +import LogoPath from '@/shared/assets/svgs/logo_icon.svg'; + +export const FaviconImage = ({ src, className, ...rest }: ImgHTMLAttributes) => { + return ( + { + e.currentTarget.src = LogoPath; + e.currentTarget.alt = '모립 로고 아이콘'; + }} + /> + ); +}; + +export default FaviconImage; diff --git a/src/app/shared/components/ModalContentsFriends/FriendsList/FriendsInfo/FriendInfo.tsx b/src/app/shared/components/ModalContentsFriends/FriendsList/FriendsInfo/FriendInfo.tsx index 893a415d..995ab612 100644 --- a/src/app/shared/components/ModalContentsFriends/FriendsList/FriendsInfo/FriendInfo.tsx +++ b/src/app/shared/components/ModalContentsFriends/FriendsList/FriendsInfo/FriendInfo.tsx @@ -25,7 +25,7 @@ const FriendInfo = ({ friendsData }: FriendsInfoProp) => {
    -

    {friend.name}

    +

    {friend.name}

    {friend.email}

    diff --git a/src/app/shared/components/NotificationPanel/NotificationPanel.tsx b/src/app/shared/components/NotificationPanel/NotificationPanel.tsx new file mode 100644 index 00000000..5531ca65 --- /dev/null +++ b/src/app/shared/components/NotificationPanel/NotificationPanel.tsx @@ -0,0 +1,20 @@ +import { forwardRef } from 'react'; + +const NotificationPanel = forwardRef((_, ref) => { + return ( +
    +

    알림

    + +
    +

    아직 받은 알림이 없어요.

    +
    +
    + ); +}); + +NotificationPanel.displayName = 'NotificationPanel'; + +export default NotificationPanel; diff --git a/src/app/shared/hooks/useClickOutside.ts b/src/app/shared/hooks/useClickOutside.ts index 468f0448..1d6243fe 100644 --- a/src/app/shared/hooks/useClickOutside.ts +++ b/src/app/shared/hooks/useClickOutside.ts @@ -1,11 +1,15 @@ import { RefObject, useEffect } from 'react'; -const useClickOutside = (ref: RefObject, callback: () => void, enable: boolean = true) => { +const useClickOutside = ( + ref: RefObject, + callback: (event?: MouseEvent) => void, + enable: boolean = true, +) => { useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (!enable) return; if (ref.current && !ref.current.contains(event.target as Node)) { - callback(); + callback(event); } }; diff --git a/src/app/shared/layout/Sidebar/Sidebar.tsx b/src/app/shared/layout/Sidebar/Sidebar.tsx index ce4ee4df..32ceb5b7 100644 --- a/src/app/shared/layout/Sidebar/Sidebar.tsx +++ b/src/app/shared/layout/Sidebar/Sidebar.tsx @@ -44,7 +44,7 @@ const Sidebar = () => { /> -
    +
    {isEditing ? ( @@ -148,7 +157,9 @@ const BoxTodo = ({ /> ) : (

    {name} @@ -175,7 +186,9 @@ const BoxTodo = ({

  • -

    +

    {friend.isOnline ? '온라인' : '오프라인'}

    -

    +

    {formatSecondsForFriendsList(friend.elapsedTime)}

    -
    +
    +
    deleteFriend({ friendId: friend.id })} label="친구삭제" textColor="red" /> diff --git a/src/app/shared/components/NotificationPanel/NotificationPanel.tsx b/src/app/shared/components/NotificationPanel/NotificationPanel.tsx index 5531ca65..8539d498 100644 --- a/src/app/shared/components/NotificationPanel/NotificationPanel.tsx +++ b/src/app/shared/components/NotificationPanel/NotificationPanel.tsx @@ -6,7 +6,7 @@ const NotificationPanel = forwardRef((_, ref) => { ref={ref} className="absolute right-[3.2rem] top-[11.5rem] h-[38.2rem] w-[36.9rem] rounded-[14px] bg-gray-bg-03 p-[2.8rem] drop-shadow-calendarDrop" > -

    알림

    +

    알림

    아직 받은 알림이 없어요.

    diff --git a/src/app/shared/constants/suggestedSites.ts b/src/app/shared/constants/suggestedSites.ts deleted file mode 100644 index 6aa42c7a..00000000 --- a/src/app/shared/constants/suggestedSites.ts +++ /dev/null @@ -1,244 +0,0 @@ -import { FieldType } from '@/shared/types/fileds'; - -import { AllowedSiteType } from '../types/allowedSites'; - -// LINK: https://www.notion.so/11a97d212fa28029992bdc1bef53c766?pvs=4 - -export const SUGGESTED_STIES: Record = { - 비즈니스: [ - { - favicon: 'https://www.gstatic.com/trends/favicon.ico', - siteName: 'Google Trends', - pageName: 'Google Trends', - siteUrl: 'https://trends.google.com/trends/?geo=US', - }, - { - favicon: 'https://trello.com/favicon.ico', - siteName: 'Trello', - pageName: 'Trello', - siteUrl: 'https://trello.com/en', - }, - { - favicon: `https://a.sfdcstatic.com/shared/images/c360-nav/salesforce-no-type-logo.svg`, - siteName: 'Salesforce', - pageName: 'Salesforce Korea', - siteUrl: 'https://www.salesforce.com/kr/?ir=1', - }, - { - favicon: 'https://slack.com/favicon.ico', - siteName: 'Slack', - pageName: 'Slack - 협업툴', - siteUrl: 'https://slack.com/intl/ko-kr/', - }, - { - favicon: 'https://cfl.dropboxstatic.com/static/metaserver/static/images/favicon.ico', - siteName: 'Dropbox', - pageName: 'Dropbox for Business', - siteUrl: 'https://www.dropbox.com/business', - }, - ], - 디자인: [ - { - favicon: 'https://static.figma.com/uploads/b6df2735e4cb368306acf5480b50f96e69f96099', - siteName: '피그마', - pageName: 'Figma - 디자인 협업툴', - siteUrl: 'https://www.figma.com/', - }, - { - favicon: 'https://unsplash.com/favicon.ico', - siteName: '언스플레쉬', - pageName: 'Unsplash', - siteUrl: 'https://unsplash.com/ko', - }, - { - favicon: 'https://www.google.com/s2/favicons?domain=freepik.com&sz=128', - siteName: '프리픽', - pageName: 'Freepik', - siteUrl: 'https://www.freepik.com/', - }, - { - favicon: 'https://kr.pinterest.com/favicon.ico', - siteName: '핀터레스트', - pageName: 'Pinterest', - siteUrl: 'https://kr.pinterest.com/', - }, - { - favicon: 'https://a5.behance.net/37b8f834c44e77b0120ebd66481759ccebaf636d/img/site/favicon.png?cb=264615658', - siteName: '비핸스', - pageName: 'Behance', - siteUrl: 'https://www.behance.net/', - }, - { - favicon: 'https://cdn-bastani.stunning.kr/static/feature/notefolioFavicon/favicon.ico', - siteName: '노트폴리오', - pageName: 'Notefolio', - siteUrl: 'https://notefolio.net/', - }, - { - favicon: `https://cdn.dribbble.com/assets/favicon-192x192-d70ad402693bdd1a8460da7f9f3c590e817da7369c5287789ac968cf6947d214.png`, - siteName: '드리블', - pageName: 'Dribbble', - siteUrl: 'https://dribbble.com/', - }, - ], - 마케팅: [ - { - favicon: 'https://newneek.co/icons/android-icon-192x192.png', - siteName: '뉴닉', - pageName: 'Newneek', - siteUrl: 'https://newneek.co/', - }, - { - favicon: 'https://www.careet.net/content/images/favicon.ico', - siteName: '캐릿', - pageName: 'Careet', - siteUrl: 'https://www.careet.net/', - }, - { - favicon: 'https://tsn.dmcmedia.co.kr/dmcreportCDN/DMCReportFront/images/favicon.png', - siteName: 'DMC리포트', - pageName: 'DMC Report', - siteUrl: 'https://www.dmcreport.co.kr/', - }, - { - favicon: 'https://www.mezzomedia.co.kr/images/favicon.ico', - siteName: '메조 미디어', - pageName: 'Mezzo Media', - siteUrl: 'https://www.mezzomedia.co.kr/', - }, - { - favicon: 'https://www.nasmedia.co.kr/wp-content/themes/nasmedia/images/favicon/favicon-180.png', - siteName: '나스 미디어', - pageName: 'Nas Media', - siteUrl: 'https://www.nasmedia.co.kr/', - }, - ], - 기획: [ - { - favicon: 'https://disquiet.io/favicons/favicon-96.png', - siteName: '디스콰이엇', - pageName: 'Disquiet', - siteUrl: 'https://disquiet.io/', - }, - { - favicon: 'https://brunch.co.kr/favicon.ico', - siteName: '브런치', - pageName: 'Brunch', - siteUrl: 'https://brunch.co.kr/', - }, - { - favicon: 'https://www.notion.so/images/logo-ios.png', - siteName: '노션', - pageName: 'Notion', - siteUrl: 'https://www.notion.so/', - }, - { - favicon: 'https://ph-static.imgix.net/ph-favicon-brand-500.ico?auto=format', - siteName: 'Product Hunt', - pageName: 'Product Hunt', - siteUrl: 'https://www.producthunt.com/', - }, - { - favicon: 'https://static.figma.com/uploads/b6df2735e4cb368306acf5480b50f96e69f96099', - siteName: '피그마', - pageName: 'Figma - 디자인 협업툴', - siteUrl: 'https://www.figma.com/', - }, - ], - 개발: [ - { - favicon: 'https://chatgpt.com/favicon.ico', - siteName: '챗지피티', - pageName: 'ChatGPT', - siteUrl: 'https://chatgpt.com/', - }, - { - favicon: 'https://www.acmicpc.net/favicon.ico', - siteName: '백준', - pageName: 'Baekjoon Online Judge', - siteUrl: 'https://www.acmicpc.net/', - }, - { - favicon: 'https://github.com/favicon.ico', - siteName: 'GitHub', - pageName: 'GitHub', - siteUrl: 'https://github.com/', - }, - { - favicon: 'https://slack.com/favicon.ico', - siteName: 'Slack', - pageName: 'Slack', - siteUrl: 'https://slack.com/intl/ko-kr/', - }, - { - favicon: 'https://stackoverflow.com/favicon.ico', - siteName: 'Stack Overflow', - pageName: 'Stack Overflow', - siteUrl: 'https://stackoverflow.com/', - }, - ], - 공부: [ - { - favicon: 'https://www.notion.so/images/logo-ios.png', - siteName: '노션', - pageName: 'Notion', - siteUrl: 'https://www.notion.so/', - }, - { - favicon: 'https://www.copykiller.com/common/img/logo/favicon_ck_platform_32x32.png', // 더 개선 필요 - siteName: '카피킬러라이트', - pageName: 'CopyKiller Lite', - siteUrl: 'https://www.copykiller.com/', - }, - { - favicon: 'https://www.riss.kr/commons/images/favicon.ico', - siteName: 'RISS', - pageName: 'RISS', - siteUrl: 'https://www.riss.kr/', - }, - { - favicon: 'https://drive.google.com/favicon.ico', - siteName: 'Google Drive', - pageName: 'Google Drive', - siteUrl: 'https://drive.google.com/', - }, - { - favicon: 'https://meet.google.com/favicon.ico', - siteName: 'Google Workspace', - pageName: 'Google Workspace', - siteUrl: 'https://meet.google.com/', - }, - ], - 기타: [ - { - favicon: 'https://papago.naver.com/favicon.ico', - siteName: '네이버 파파고', - pageName: 'Naver Papago', - siteUrl: 'https://papago.naver.com/', - }, - { - favicon: 'https://chatgpt.com/favicon.ico', - siteName: '챗지피티', - pageName: 'ChatGPT', - siteUrl: 'https://chatgpt.com/', - }, - { - favicon: 'https://www.ilovepdf.com/favicon.ico', - siteName: 'iLovePDF', - pageName: 'iLovePDF', - siteUrl: 'https://www.ilovepdf.com/ko', - }, - { - favicon: 'https://www.hancomdocs.com/favicon.ico', - siteName: 'Hancomdocs', - pageName: 'Hancomdocs', - siteUrl: 'https://www.hancomdocs.com/home', - }, - { - favicon: 'https://ssl.gstatic.com/ui/v1/icons/mail/rfr/gmail.ico', - siteName: 'Gmail', - pageName: 'Gmail', - siteUrl: 'https://mail.google.com/mail/', - }, - ], -}; diff --git a/src/app/shared/layout/Sidebar/ModalContentsSetting/AccountContent/AccountContent.tsx b/src/app/shared/layout/Sidebar/ModalContentsSetting/AccountContent/AccountContent.tsx index b4b9996a..970db44b 100644 --- a/src/app/shared/layout/Sidebar/ModalContentsSetting/AccountContent/AccountContent.tsx +++ b/src/app/shared/layout/Sidebar/ModalContentsSetting/AccountContent/AccountContent.tsx @@ -79,7 +79,7 @@ const AccountContent = ({ ...props }: AccountContentProps) => {

    데스크톱 푸시 알림

    -

    데스크톱 앱에서 작성의 푸시 알림을 즉시 받으세요.

    +

    데스크톱 앱에서 모립의 푸시 알림을 즉시 받으세요.

    @@ -100,7 +100,9 @@ const AccountContent = ({ ...props }: AccountContentProps) => {

    내 계정 삭제

    -

    본 기기를 포함한 모든 기기에서 로그아웃합니다.

    +

    + 계정을 영구적으로 삭제하고 모든 워크스페이스에서 액세스 권한을 제거합니다. +

    + + + + + + + { + handleOpenConfirmDeleteModal(); + }} + /> + +
    + + {() => ( + { + allowToMergeParentDomain.mutate({ + allowedGroupId: activeGroupId, + siteUrl: allowedSiteData.siteUrl, + }); + handleCloseDomainAllowModal(); + }} + onCancel={handleCloseDomainAllowModal} + /> + )} + + + {() => ( + { + handleCloseConfirmDeleteModal(); + onDeleteAllowedSite(); + }} + pageName={allowedSiteData.pageName} + /> + )} +
    ); }; diff --git a/src/app/pages/AllowedServicePage/AllowedServiceList/AllowedServiceList.tsx b/src/app/pages/AllowedServicePage/AllowedServiceList/AllowedServiceList.tsx index 25017fee..11a09dda 100644 --- a/src/app/pages/AllowedServicePage/AllowedServiceList/AllowedServiceList.tsx +++ b/src/app/pages/AllowedServicePage/AllowedServiceList/AllowedServiceList.tsx @@ -63,29 +63,26 @@ const AllowedServiceListContent = ({ children }: { children: ReactNode }) => { }; interface AllowedServiceListItemProps extends AllowedServiceGroupType { - index: number; activeGroupId: number | null; activeGroupTitleInput: string; onSelectActiveGroup: (activeGroupId: number | null) => void; - onDeleteAllowedServiceGroup: (groupId: number, isActive: boolean, currentIndex: number) => void; + onDeleteAllowedServiceGroup: (groupId: number) => void; isEditingTitle: boolean; } const AllowedServiceListItem = ({ - index, activeGroupId, activeGroupTitleInput, onSelectActiveGroup, onDeleteAllowedServiceGroup, isEditingTitle, - ...allowedServiceGroupData }: AllowedServiceListItemProps) => { const isActive = activeGroupId === allowedServiceGroupData.id; const handleDeleteAllowedServiceGroup = (e: MouseEvent) => { e.stopPropagation(); - onDeleteAllowedServiceGroup(allowedServiceGroupData.id, isActive, index); + onDeleteAllowedServiceGroup(allowedServiceGroupData.id); }; const handleSelectActiveGroupId = () => { diff --git a/src/app/pages/AllowedServicePage/AllowedServicePage.tsx b/src/app/pages/AllowedServicePage/AllowedServicePage.tsx index ea5beba0..352aaf8d 100644 --- a/src/app/pages/AllowedServicePage/AllowedServicePage.tsx +++ b/src/app/pages/AllowedServicePage/AllowedServicePage.tsx @@ -5,17 +5,20 @@ import { useQueryClient } from '@tanstack/react-query'; import AutoFixedGrid from '@/shared/components/AutoFixedGrid/AutoFixedGrid'; import ModalContentsFriends from '@/shared/components/ModalContentsFriends/ModalContentsFriends'; import ModalWrapper, { ModalWrapperRef } from '@/shared/components/ModalWrapper/ModalWrapper'; +import NotificationPanel from '@/shared/components/NotificationPanel/NotificationPanel'; import Spacer from '@/shared/components/Spacer/Spacer'; import TextField from '@/shared/components/TextField/TextField'; +import useClickOutside from '@/shared/hooks/useClickOutside'; + import { isUrlValid } from '@/shared/utils/validation'; import { ColorPaletteType } from '@/shared/types/allowedService'; -import { GetAllowedServiceListRes } from '@/shared/types/api/allowedService'; import BellIcon from '@/shared/assets/svgs/bell.svg?react'; import FriendSettingIcon from '@/shared/assets/svgs/friend_setting.svg?react'; +import ModalContentsAlert from '@/pages/AllowedServicePage/ModalContentsAlert/ModalContentsAlert'; import { allowedServiceKeys } from '@/shared/apisV2/allowedService/allowedService.keys'; import { useDeleteAllowedService, @@ -43,10 +46,16 @@ const AllowedServicePage = () => { const [isEditingTitle, setIsEditingTitle] = useState(false); const [urlInput, setUrlInput] = useState(''); const [selectedColor, setSelectedColor] = useState('#868C93'); + const [isNotificationVisible, setIsNotificationVisible] = useState(false); const queryClient = useQueryClient(); const friendsModalRef = useRef(null); + const requireTitleModalRef = useRef(null); + + const bellIconRef = useRef(null); + const notificationPanelRef = useRef(null); + const handleChangeTitleInput = (e: ChangeEvent) => { setTitleInput(e.target.value); @@ -150,35 +159,24 @@ const AllowedServicePage = () => { } }; - const handleDeleteAllowedServiceGroup = (groupId: number, isActive: boolean, currentIndex: number) => { + const handleDeleteAllowedServiceGroup = (groupId: number) => { deleteAllowedServiceGroup( { allowedGroupId: groupId }, { onSuccess: () => { - queryClient.setQueryData( - allowedServiceKeys.allowedServiceList({ connectType: currentTap }), - (oldData: GetAllowedServiceListRes) => { - if (!oldData) return oldData; - return { - ...oldData, - data: oldData.data.filter((group) => group.id !== groupId), - }; - }, - ); - - if (isActive) { - if (allowedServiceList && allowedServiceList.data.length > 1) { - setActiveGroupId(allowedServiceList.data[currentIndex + 1].id); - } else { - handleEnableAddingAllowedServiceGroup(); - } - } + queryClient.invalidateQueries({ + queryKey: allowedServiceKeys.allowedServiceList({ connectType: currentTap }), + }); }, }, ); }; const handleAddAllowedService = (urlInput: string, activeGroupId: number | null) => { + if (!activeGroupId) { + handleOpenRequireTitleModal(); + return; + } if (activeGroupId && !isPending) { postAddAllowedService( { @@ -216,14 +214,41 @@ const AllowedServicePage = () => { const handleKeyDownUrlInput = (e: KeyboardEvent) => { if (e.key === 'Enter' && !e.nativeEvent.isComposing) { + e.preventDefault(); handleAddAllowedService(urlInput, activeGroupId); } }; + const toggleNotification = () => { + setIsNotificationVisible((prev) => !prev); + }; + + useClickOutside( + notificationPanelRef, + (event) => { + if (!isNotificationVisible) return; + + if (bellIconRef.current && event && bellIconRef.current.contains(event.target as Node)) { + return; + } + + setIsNotificationVisible(false); + }, + isNotificationVisible, + ); + // NOTE: 첫 렌더링 시 api를 통해 받은 첫번째 allowed service group id를 activeGroupId로 설정 + // 리스트 삭제 후, 현재 active 그룹이 리스트에 없는 경우 첫 번째 그룹으로 설정 useEffect(() => { - if (activeGroupId === null && allowedServiceList && allowedServiceList?.data.length > 0) { - setActiveGroupId(allowedServiceList.data[0].id); + if (allowedServiceList && allowedServiceList.data.length > 0) { + const activeGroupExists = activeGroupId && allowedServiceList.data.some((group) => group.id === activeGroupId); + + if (!activeGroupId || !activeGroupExists) { + setActiveGroupId(allowedServiceList.data[0].id); + } + } else if (allowedServiceList && allowedServiceList.data.length === 0 && activeGroupId !== null) { + // 리스트가 비어있고 선택된 그룹이 있으면 입력 모드로 전환 + handleEnableAddingAllowedServiceGroup(); } }, [allowedServiceList]); @@ -239,13 +264,21 @@ const AllowedServicePage = () => { friendsModalRef.current?.open(); }; + const handleOpenRequireTitleModal = () => { + requireTitleModalRef.current?.open(); + }; + + const handleCloseRequireTitleModal = () => { + requireTitleModalRef.current?.close(); + }; + return (
    -
    @@ -261,10 +294,9 @@ const AllowedServicePage = () => { {activeGroupId === null && ( )} - {allowedServiceList?.data.map((allowedServiceGroupData, index) => ( + {allowedServiceList?.data.map((allowedServiceGroupData) => ( { setCurrentTap('WEB')} isActive={currentTap === 'WEB'}> 웹사이트 - - 앱 - @@ -322,15 +351,16 @@ const AllowedServicePage = () => { 사이트 등록하기 - {allowedServiceGroupDetail && + activeGroupId && allowedServiceGroupDetail.data.allowedSites.map((allowedSiteData, index) => ( - handleDeleteAllowedService(allowedSiteData.id, allowedSiteData.siteUrl) - } + activeGroupId={activeGroupId} + onDeleteAllowedSite={() => { + handleDeleteAllowedService(allowedSiteData.id, allowedSiteData.siteUrl); + }} {...allowedSiteData} /> ))} @@ -352,6 +382,13 @@ const AllowedServicePage = () => { {({ isModalOpen }) => } + + + {() => } + + + {isNotificationVisible && } +
    ); }; diff --git a/src/app/pages/AllowedServicePage/ModalContentsAlert/ModalContentsAlert.tsx b/src/app/pages/AllowedServicePage/ModalContentsAlert/ModalContentsAlert.tsx new file mode 100644 index 00000000..0b1a1a3c --- /dev/null +++ b/src/app/pages/AllowedServicePage/ModalContentsAlert/ModalContentsAlert.tsx @@ -0,0 +1,99 @@ +import { forwardRef } from 'react'; + +import ButtonRadius5 from '@/shared/components/ButtonRadius5/ButtonRadius5'; + +interface RequireTitleProps { + onClick: () => void; +} + +const RequireTitle = forwardRef(({ onClick }, ref) => { + return ( +
    +

    + 허용서비스 리스트의 이름을 +
    + 먼저 입력해주세요. +

    + + 확인 + +
    + ); +}); + +interface ConfirmDeleteProps { + onClick: () => void; + pageName: string; +} + +const ConfirmDelete = forwardRef(({ onClick, pageName }, ref) => { + return ( +
    +

    + ' + + {pageName} + + ' 허용 사이트가 +
    + 삭제되었습니다. +

    + + 확인 + +
    + ); +}); + +interface DomainAllowConfirmProps { + onConfirm: () => void; + onCancel: () => void; + siteName?: string; +} + +const DomainAllowConfirm = forwardRef( + ({ onConfirm, onCancel, siteName }, ref) => { + return ( +
    +

    + ' + + {siteName} + + '의
    + 상위 도메인을 허용할까요? +

    +

    해당 사이트 이름을 가진 링크들이 하나로 통합돼요.

    +
    + + 허용 + + + 취소하기 + +
    +
    + ); + }, +); + +RequireTitle.displayName = 'RequireTitle'; +ConfirmDelete.displayName = 'ConfirmDelete'; +DomainAllowConfirm.displayName = 'DomainAllowConfirm'; + +const ModalContentsAlert = { + RequireTitle, + DomainAllowConfirm, + ConfirmDelete, +}; + +export default ModalContentsAlert; diff --git a/src/app/pages/AllowedServicePage/RecommendService/RecommendService.tsx b/src/app/pages/AllowedServicePage/RecommendService/RecommendService.tsx index d567af2d..86996815 100644 --- a/src/app/pages/AllowedServicePage/RecommendService/RecommendService.tsx +++ b/src/app/pages/AllowedServicePage/RecommendService/RecommendService.tsx @@ -8,8 +8,6 @@ import useCarousel from '@/shared/hooks/useCarousel'; import { RecommendSiteType } from '@/shared/types/allowedService'; import { Direction } from '@/shared/types/global'; -import { getServiceFavicon } from '@/pages/OnboardingPage/utils/serviceUrl'; - interface RecommendServiceItemProps extends ButtonHTMLAttributes { recommendSite: RecommendSiteType; } @@ -20,8 +18,8 @@ const RecommendServiceItem = ({ recommendSite, ...props }: RecommendServiceItemP className="flex w-[23.9rem] flex-shrink-0 items-center gap-[1.5rem] rounded-[8px] bg-gray-bg-01 p-[2rem] hover:bg-gray-bg-04 active:bg-gray-bg-02" > favicon

    {recommendSite.siteName}

    From 5c8ee4038a15fc9fa476c4d6152eed55c804758a Mon Sep 17 00:00:00 2001 From: 10tentacion Date: Sat, 10 May 2025 15:05:41 +0900 Subject: [PATCH 19/30] =?UTF-8?q?feat:=20=ED=83=80=EC=9D=B4=EB=A8=B8=20?= =?UTF-8?q?=EC=BA=90=EB=9F=AC=EC=85=80=20QA=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/pages/TimerPage/Carousel/CarouselFriend.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/app/pages/TimerPage/Carousel/CarouselFriend.tsx b/src/app/pages/TimerPage/Carousel/CarouselFriend.tsx index aea25cac..e0b6fbf9 100644 --- a/src/app/pages/TimerPage/Carousel/CarouselFriend.tsx +++ b/src/app/pages/TimerPage/Carousel/CarouselFriend.tsx @@ -43,7 +43,7 @@ const CarouselFriend = memo(function CarouselFriend({ return (
    {/* 프로필 이미지 영역 */} - + {`${name}의 - {formattedTime} + + {formattedTime} +
    {/* 이름 및 카테고리 표시 */} From ee732162907fd9d26a8d6f091f777dc2ff081256 Mon Sep 17 00:00:00 2001 From: 10tentacion Date: Sat, 10 May 2025 15:10:48 +0900 Subject: [PATCH 20/30] =?UTF-8?q?feat:=20#321=20=ED=83=80=EC=9D=B4?= =?UTF-8?q?=EB=A8=B8=20=EB=82=98=EB=A8=B8=EC=A7=80=20QA=20=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pages/TimerPage/MainTimer/MainTimer.tsx | 29 ++++++++++++++++--- .../ButtonTimerPlay/ButtonTimerPlay.tsx | 7 +++-- .../MainTimer/TimerDisplay/TimerDisplay.tsx | 12 ++++++-- .../MainTimer/TimerHeader/TimerHeader.tsx | 8 ++++- .../TimerPage/SideMenuTimer/SideMenuTimer.tsx | 18 ++++++++++-- 5 files changed, 63 insertions(+), 11 deletions(-) diff --git a/src/app/pages/TimerPage/MainTimer/MainTimer.tsx b/src/app/pages/TimerPage/MainTimer/MainTimer.tsx index 042f29ce..a7970b1e 100644 --- a/src/app/pages/TimerPage/MainTimer/MainTimer.tsx +++ b/src/app/pages/TimerPage/MainTimer/MainTimer.tsx @@ -1,6 +1,9 @@ -import React, { useCallback } from 'react'; +import React, { useCallback, useMemo } from 'react'; + +import { splitTasksByCompletion } from '@/shared/utils/timer'; import { getFormattedTimeInfo } from '@/pages/TimerPage/utils/timeFormat'; +import { useGetTimerTodos } from '@/shared/apisV2/timer/timer.queries'; import { useTimerContext } from '../contexts/TimerContext'; import TimerDisplay from './TimerDisplay/TimerDisplay'; @@ -13,18 +16,34 @@ import TimerHeader from './TimerHeader/TimerHeader'; */ const MainTimer = () => { // 타이머 컨텍스트에서 필요한 상태와 액션만 가져오기 - const { timer, elapsedTime, totalElapsedTimeOfToday, isPlaying, selectedTask, actions } = useTimerContext(); + const { todayFormattedDate, timer, elapsedTime, totalElapsedTimeOfToday, isPlaying, selectedTask, actions } = + useTimerContext(); + + // 할일 데이터 조회 + const { data: todosData } = useGetTimerTodos({ targetDate: todayFormattedDate }); + + // 완료된 할 일 목록 가져오기 + const { completedTodos } = useMemo(() => { + const todos = todosData?.data?.task ?? []; + return splitTasksByCompletion(todos); + }, [todosData]); // 작업이 선택되어 있는지 여부 const hasSelectedTask = selectedTask.id !== null; + // 선택된 작업이 완료되었는지 확인 + const isSelectedTaskCompleted = useCallback(() => { + if (!selectedTask.id) return false; + return completedTodos.some((todo) => todo.id === selectedTask.id); + }, [completedTodos, selectedTask.id]); + // 재생/정지 토글 핸들러 - 작업이 선택되지 않은 경우 처리 방지 const handlePlayPauseToggle = useCallback(() => { - if (!hasSelectedTask) return; + if (!hasSelectedTask || isSelectedTaskCompleted()) return; // 현재 상태의 반대로 토글 actions.togglePlay(!isPlaying); - }, [isPlaying, hasSelectedTask, actions]); + }, [isPlaying, hasSelectedTask, isSelectedTaskCompleted, actions]); // 표시할 시간 정보 계산 - 유틸 함수로 분리하여 관심사 분리 const timeInfo = getFormattedTimeInfo(timer, elapsedTime, totalElapsedTimeOfToday); @@ -36,6 +55,7 @@ const MainTimer = () => { selectedTaskName={selectedTask.name} selectedTaskCategoryName={selectedTask.categoryName} hasSelectedTask={hasSelectedTask} + isCompleted={isSelectedTaskCompleted()} /> {/* 타이머 디스플레이 - 시간 표시 및 제어 버튼 */} @@ -45,6 +65,7 @@ const MainTimer = () => { timer={timer} isPlaying={isPlaying} onToggle={handlePlayPauseToggle} + disabled={!hasSelectedTask || isSelectedTaskCompleted()} />
    ); diff --git a/src/app/pages/TimerPage/MainTimer/TimerDisplay/ButtonTimerPlay/ButtonTimerPlay.tsx b/src/app/pages/TimerPage/MainTimer/TimerDisplay/ButtonTimerPlay/ButtonTimerPlay.tsx index d7241329..94fc857f 100644 --- a/src/app/pages/TimerPage/MainTimer/TimerDisplay/ButtonTimerPlay/ButtonTimerPlay.tsx +++ b/src/app/pages/TimerPage/MainTimer/TimerDisplay/ButtonTimerPlay/ButtonTimerPlay.tsx @@ -1,3 +1,5 @@ +import { MouseEvent } from 'react'; + import PauseIcon from '@/shared/assets/svgs/defaultpause.svg?react'; import PlayIcon from '@/shared/assets/svgs/defaultplay.svg?react'; import HoverPauseIcon from '@/shared/assets/svgs/hoverpause.svg?react'; @@ -6,14 +8,15 @@ import HoverPlayIcon from '@/shared/assets/svgs/hoverplay.svg?react'; interface ButtonTimerPlayProps { onClick: () => void; isPlaying: boolean; + disabled?: boolean; } -const ButtonTimerPlay = ({ onClick, isPlaying }: ButtonTimerPlayProps) => { +const ButtonTimerPlay = ({ onClick, isPlaying, disabled = false }: ButtonTimerPlayProps) => { const IconComponent = isPlaying ? PauseIcon : PlayIcon; const HoverIconComponent = isPlaying ? HoverPauseIcon : HoverPlayIcon; return ( - diff --git a/src/app/pages/TimerPage/MainTimer/TimerDisplay/TimerDisplay.tsx b/src/app/pages/TimerPage/MainTimer/TimerDisplay/TimerDisplay.tsx index 7909fe97..754a9453 100644 --- a/src/app/pages/TimerPage/MainTimer/TimerDisplay/TimerDisplay.tsx +++ b/src/app/pages/TimerPage/MainTimer/TimerDisplay/TimerDisplay.tsx @@ -14,6 +14,7 @@ interface TimerDisplayProps { timer: number; isPlaying: boolean; onToggle: () => void; + disabled?: boolean; } /** @@ -21,7 +22,14 @@ interface TimerDisplayProps { * * 타이머의 시간 표시, 상태 텍스트 및 재생/정지 버튼을 포함하는 UI */ -const TimerDisplay = ({ statusText, formattedTimeText, timer, isPlaying, onToggle }: TimerDisplayProps) => { +const TimerDisplay = ({ + statusText, + formattedTimeText, + timer, + isPlaying, + onToggle, + disabled = false, +}: TimerDisplayProps) => { return (
    @@ -32,7 +40,7 @@ const TimerDisplay = ({ statusText, formattedTimeText, timer, isPlaying, onToggl {formattedTimeText}
    - +
    ); diff --git a/src/app/pages/TimerPage/MainTimer/TimerHeader/TimerHeader.tsx b/src/app/pages/TimerPage/MainTimer/TimerHeader/TimerHeader.tsx index 271e5b4b..0004b209 100644 --- a/src/app/pages/TimerPage/MainTimer/TimerHeader/TimerHeader.tsx +++ b/src/app/pages/TimerPage/MainTimer/TimerHeader/TimerHeader.tsx @@ -7,6 +7,7 @@ interface TimerHeaderProps { selectedTaskName: string; selectedTaskCategoryName: string; hasSelectedTask: boolean; + isCompleted?: boolean; } /** @@ -14,7 +15,12 @@ interface TimerHeaderProps { * * 선택된 할일의 이름과 카테고리를 표시하거나, 할일이 선택되지 않은 경우 안내 메시지를 표시. */ -const TimerHeader = ({ selectedTaskName, selectedTaskCategoryName, hasSelectedTask }: TimerHeaderProps) => { +const TimerHeader = ({ + selectedTaskName, + selectedTaskCategoryName, + hasSelectedTask, + isCompleted = false, +}: TimerHeaderProps) => { if (!hasSelectedTask) { return (
    diff --git a/src/app/pages/TimerPage/SideMenuTimer/SideMenuTimer.tsx b/src/app/pages/TimerPage/SideMenuTimer/SideMenuTimer.tsx index 2e8561eb..4c24df10 100644 --- a/src/app/pages/TimerPage/SideMenuTimer/SideMenuTimer.tsx +++ b/src/app/pages/TimerPage/SideMenuTimer/SideMenuTimer.tsx @@ -84,6 +84,16 @@ const SideMenuTimer = ({ ongoingTodos = [], completedTodos = [] }: SideMenuTimer setCompletedTodoToggle(true); } + // 선택된 할 일의 상태가 변경되면 타이머 정지 및 남은 할 일 중 첫번 째 할 일 선택 + if (selectedTask.id === taskId && actions.stopCurrentTimer) { + actions.stopCurrentTimer(selectedTask.id); + const remainingTodos = ongoingTodos.filter((todo) => todo.id !== taskId); + if (remainingTodos.length > 0) { + const next = remainingTodos[0]; + actions.selectTask(next.id, next.elapsedTime, next.name, next.categoryName); + } + } + queryClient.invalidateQueries({ queryKey: timerKeys.todos({ targetDate: todayFormattedDate }), }); @@ -91,7 +101,7 @@ const SideMenuTimer = ({ ongoingTodos = [], completedTodos = [] }: SideMenuTimer }, ); }, - [toggleTaskStatus, todayFormattedDate, queryClient], + [toggleTaskStatus, todayFormattedDate, queryClient, selectedTask.id, actions, ongoingTodos], ); // 할일 항목 렌더링 함수 - 최적화됨 @@ -102,9 +112,13 @@ const SideMenuTimer = ({ ongoingTodos = [], completedTodos = [] }: SideMenuTimer {...todo} isSelected={todo.id === selectedTask.id} onClick={() => handleTodoClick(todo)} - onToggleComplete={() => handleToggleTodoComplete(todo.id, isOngoing)} + onToggleComplete={(e) => { + e.stopPropagation(); + handleToggleTodoComplete(todo.id, isOngoing); + }} timerIncreasedTime={getTimerIncreasedTime(todo.id, todo.elapsedTime, selectedTask.id, timer)} undeletable={true} + disableHoverCalendar={true} /> ), [handleTodoClick, handleToggleTodoComplete, selectedTask.id, timer], From 815398a738d1e53bda5489584731683ffab5c94e Mon Sep 17 00:00:00 2001 From: 10tentacion Date: Sat, 10 May 2025 15:11:46 +0900 Subject: [PATCH 21/30] =?UTF-8?q?feat:=20#321=20=ED=99=88=20QA=20=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pages/HomePage/BoxCategory/BoxCategory.tsx | 9 ++++++--- .../HomePage/BoxTodayTodo/BoxTodayTodo.tsx | 1 + .../StatusAddBoxTodayTodo.tsx | 3 +++ .../StatusDefaultBoxTodayTodo.tsx | 4 ++-- src/app/pages/HomePage/HomePage.tsx | 18 ++++++++++++++---- 5 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/app/pages/HomePage/BoxCategory/BoxCategory.tsx b/src/app/pages/HomePage/BoxCategory/BoxCategory.tsx index 8330f4a5..ac7795e2 100644 --- a/src/app/pages/HomePage/BoxCategory/BoxCategory.tsx +++ b/src/app/pages/HomePage/BoxCategory/BoxCategory.tsx @@ -244,7 +244,10 @@ const BoxCategory = ({ onKeyDown={handleKeyDown} /> ) : ( -

    +

    {title}

    )} @@ -257,12 +260,12 @@ const BoxCategory = ({ - + diff --git a/src/app/pages/HomePage/BoxTodayTodo/BoxTodayTodo.tsx b/src/app/pages/HomePage/BoxTodayTodo/BoxTodayTodo.tsx index e2017c2d..e0af9917 100644 --- a/src/app/pages/HomePage/BoxTodayTodo/BoxTodayTodo.tsx +++ b/src/app/pages/HomePage/BoxTodayTodo/BoxTodayTodo.tsx @@ -55,6 +55,7 @@ const BoxTodayTodo = ({ cancelComplete={cancelComplete} addingComplete={addingComplete} onCreateTodayTodos={onCreateTodayTodos} + addingTodayTodoStatus={addingTodayTodoStatus} /> ) : ( diff --git a/src/app/pages/HomePage/BoxTodayTodo/StatusAddBoxTodayTodo/StatusAddBoxTodayTodo.tsx b/src/app/pages/HomePage/BoxTodayTodo/StatusAddBoxTodayTodo/StatusAddBoxTodayTodo.tsx index a0d6849f..c704f646 100644 --- a/src/app/pages/HomePage/BoxTodayTodo/StatusAddBoxTodayTodo/StatusAddBoxTodayTodo.tsx +++ b/src/app/pages/HomePage/BoxTodayTodo/StatusAddBoxTodayTodo/StatusAddBoxTodayTodo.tsx @@ -24,6 +24,7 @@ interface StatusAddBoxTodayTodoProps { cancelComplete: () => void; addingComplete: boolean; onCreateTodayTodos: () => void; + addingTodayTodoStatus: boolean; } const StatusAddBoxTodayTodo = ({ @@ -35,6 +36,7 @@ const StatusAddBoxTodayTodo = ({ cancelComplete, addingComplete, onCreateTodayTodos, + addingTodayTodoStatus, }: StatusAddBoxTodayTodoProps) => { const { data: allowedServiceList } = useGetPopoverAllowedServiceList(); const registerServiceModalRef = useRef(null); @@ -82,6 +84,7 @@ const StatusAddBoxTodayTodo = ({ selectedNumber={selectedNumber} updateTodayTodos={deleteTodayTodos} addingComplete={addingComplete} + addingTodayTodoStatus={addingTodayTodoStatus} /> ); diff --git a/src/app/pages/HomePage/BoxTodayTodo/StatusDefaultBoxTodayTodo/StatusDefaultBoxTodayTodo.tsx b/src/app/pages/HomePage/BoxTodayTodo/StatusDefaultBoxTodayTodo/StatusDefaultBoxTodayTodo.tsx index fffefa47..90db4157 100644 --- a/src/app/pages/HomePage/BoxTodayTodo/StatusDefaultBoxTodayTodo/StatusDefaultBoxTodayTodo.tsx +++ b/src/app/pages/HomePage/BoxTodayTodo/StatusDefaultBoxTodayTodo/StatusDefaultBoxTodayTodo.tsx @@ -12,8 +12,8 @@ const StatusDefaultBoxTodayTodo = ({ hasTodos, onEnableAddStatus }: StatusDefaul

    아직 오늘 할 일이 없어요

    -

    할 일을 추가하려면

    -

    + 아이콘을 선택해주세요.

    +

    할 일을 추가해

    +

    타이머를 시작해 보세요.

    diff --git a/src/app/pages/HomePage/HomePage.tsx b/src/app/pages/HomePage/HomePage.tsx index ecd55add..9181d23c 100644 --- a/src/app/pages/HomePage/HomePage.tsx +++ b/src/app/pages/HomePage/HomePage.tsx @@ -405,8 +405,13 @@ const HomePage = () => { )} -
    )} @@ -425,8 +430,13 @@ const HomePage = () => { {dailyCategoryTask.length > 2 && (
    -
    )} From d9309d0742068a8dc9e61982fdeaab8296a084dc Mon Sep 17 00:00:00 2001 From: 10tentacion Date: Sat, 10 May 2025 15:24:43 +0900 Subject: [PATCH 22/30] =?UTF-8?q?chore:=20=EA=B0=9C=EB=B0=9C=EC=84=9C?= =?UTF-8?q?=EB=B2=84=20=ED=8F=AC=ED=8A=B8=20=EB=B2=88=ED=98=B8=205173?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/electron/main.ts | 4 ++-- src/electron/pathResolver.ts | 2 +- vite.config.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/electron/main.ts b/src/electron/main.ts index f3e2392a..fbbe5f10 100644 --- a/src/electron/main.ts +++ b/src/electron/main.ts @@ -99,7 +99,7 @@ function setupAuthHandlers() { } else { // 로그인 페이지로 이동 if (isDev()) { - mainWindow.loadURL('http://localhost:5123/'); + mainWindow.loadURL('http://localhost:5173/'); } else { mainWindow.loadFile(path.join(app.getAppPath(), 'dist-react/index.html')); } @@ -192,7 +192,7 @@ function createWindow() { }); if (isDev()) { - mainWindow.loadURL('http://localhost:5123/'); + mainWindow.loadURL('http://localhost:5173/'); } else { mainWindow.loadFile(path.join(app.getAppPath(), 'dist-react/index.html')); } diff --git a/src/electron/pathResolver.ts b/src/electron/pathResolver.ts index b6c586e7..c2b00bc6 100644 --- a/src/electron/pathResolver.ts +++ b/src/electron/pathResolver.ts @@ -4,7 +4,7 @@ import { pathToFileURL } from 'url'; import { isDev } from './util.js'; -export const LOCAL_HOST = 'http://localhost:5123'; +export const LOCAL_HOST = 'http://localhost:5173'; export const INDEX_PATH = path.join(app.getAppPath(), 'dist-react', 'index.html'); export function getPreloadPath() { diff --git a/vite.config.ts b/vite.config.ts index 7ca7a57c..380bf862 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -13,7 +13,7 @@ export default defineConfig({ outDir: 'dist-react', }, server: { - port: 5123, + port: 5173, strictPort: true, }, }); From c79aef194d4f374e7a67e2a6e54151fc6916aeda Mon Sep 17 00:00:00 2001 From: 10tentacion Date: Sat, 10 May 2025 15:25:05 +0900 Subject: [PATCH 23/30] =?UTF-8?q?feat:=20=ED=86=A0=ED=81=B0=20=EC=9E=AC?= =?UTF-8?q?=EB=B0=9C=EA=B8=89=20=EB=A1=9C=EC=A7=81=20=EB=B0=94=EB=80=90=20?= =?UTF-8?q?=ED=98=95=EC=8B=9D=EC=97=90=20=EB=A7=9E=EC=B6=94=EC=96=B4=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/shared/apisV2/auth/auth.api.ts | 18 ++++++++++++------ src/app/shared/apisV2/client.ts | 5 ++++- src/app/shared/types/api/auth.ts | 1 + 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/app/shared/apisV2/auth/auth.api.ts b/src/app/shared/apisV2/auth/auth.api.ts index 20a6c2f0..e50d9545 100644 --- a/src/app/shared/apisV2/auth/auth.api.ts +++ b/src/app/shared/apisV2/auth/auth.api.ts @@ -1,24 +1,30 @@ import axios from 'axios'; -import { getAccessToken } from '@/shared/utils/auth'; +import { getAccessToken, getRefreshToken } from '@/shared/utils/auth'; import { reissueRes } from '@/shared/types/api/auth'; import { authClient } from '@/shared/apisV2/client'; const AUTH_ENDPOINT = { - POST_REISSUE_TOKEN: 'api/v2/users/reissue', + POST_REISSUE_TOKEN: 'api/v2/users/reissue/tmp', // NOTE: 임시 토큰 해결방식 POST_LOGOUT: 'api/v2/users/logout', }; export const postReissueToken = async (): Promise => { const accessToken = getAccessToken(); - const { data } = await axios.post(AUTH_ENDPOINT.POST_REISSUE_TOKEN, { - headers: { - Authorization: `Bearer ${accessToken}`, + const { data } = await axios.post( + AUTH_ENDPOINT.POST_REISSUE_TOKEN, + { + refreshToken: getRefreshToken(), }, - }); + { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }, + ); return data; }; diff --git a/src/app/shared/apisV2/client.ts b/src/app/shared/apisV2/client.ts index 17907303..3dd2d432 100644 --- a/src/app/shared/apisV2/client.ts +++ b/src/app/shared/apisV2/client.ts @@ -1,6 +1,6 @@ import axios, { AxiosInstance, AxiosRequestConfig } from 'axios'; -import { getAccessToken, reloginWithoutLogout, setAccessToken } from '@/shared/utils/auth'; +import { getAccessToken, reloginWithoutLogout, setAccessToken, setRefreshToken } from '@/shared/utils/auth'; import { postReissueToken } from '@/shared/apisV2/auth/auth.api'; @@ -42,7 +42,10 @@ const addAuthInterceptor = (axiosClient: AxiosInstance) => { try { // NOTE: 추후에 로컬스토리지에 저장된 refreshToken을 사용하게끔 고치는 것이 필요할수있음 const { data } = await postReissueToken(); + setAccessToken(data.accessToken); + setRefreshToken(data.refreshToken); + return axiosClient(prevRequest); } catch (reissueError) { reloginWithoutLogout(); diff --git a/src/app/shared/types/api/auth.ts b/src/app/shared/types/api/auth.ts index 5c848197..b82e73b6 100644 --- a/src/app/shared/types/api/auth.ts +++ b/src/app/shared/types/api/auth.ts @@ -3,5 +3,6 @@ export interface reissueRes { message: string; data: { accessToken: string; + refreshToken: string; }; } From 5eeeb0320b57fbece681b39af01148132d44ca82 Mon Sep 17 00:00:00 2001 From: 10tentacion Date: Sat, 10 May 2025 19:39:23 +0900 Subject: [PATCH 24/30] =?UTF-8?q?feat:=20=ED=99=94=EB=A9=B4=20=ED=95=B4?= =?UTF-8?q?=EC=83=81=EB=8F=84=20=EC=A1=B0=EC=A0=95=20=EB=B0=8F=20=EA=B0=9C?= =?UTF-8?q?=EB=B0=9C=EC=9E=90=20=EB=8F=84=EA=B5=AC=20=EC=97=AC=EB=8A=94=20?= =?UTF-8?q?=EC=83=81=ED=99=A9=20=EB=B0=A9=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/electron/main.ts | 71 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/src/electron/main.ts b/src/electron/main.ts index fbbe5f10..5d65db60 100644 --- a/src/electron/main.ts +++ b/src/electron/main.ts @@ -1,4 +1,4 @@ -import { BrowserWindow, app, ipcMain, shell } from 'electron'; +import { BrowserWindow, app, ipcMain, screen, shell } from 'electron'; import path from 'path'; import { startBrowserMonitoring, stopBrowserMonitoring } from './browserMonitor.js'; @@ -158,10 +158,34 @@ function setupBrowserMonitorHandlers() { }); } +// 화면 해상도 확인 및 줌 레벨 설정 함수 +function adjustZoomLevelIfNeeded(window: BrowserWindow | null) { + if (!window) return; + + // 맥북 14인치 해상도 기준 (3024x1964) + const MAC14_WIDTH = 3024; + const MAC14_HEIGHT = 1964; + + const display = screen.getPrimaryDisplay(); + + const pixelWidth = display.workAreaSize.width * display.scaleFactor; + const pixelHeight = display.workAreaSize.height * display.scaleFactor; + + // 해상도가 맥북 14인치보다 작은 경우 + if (pixelWidth < MAC14_WIDTH || pixelHeight < MAC14_HEIGHT) { + // 콘텐츠가 로드된 후 줌 레벨 설정 + window.webContents.once('did-finish-load', () => { + // 화면 비율 80%로 설정 (줌 레벨 -1.0은 약 80%에 해당) + window.webContents.setZoomLevel(-1.0); + }); + } +} + function createWindow() { mainWindow = new BrowserWindow({ webPreferences: { preload: getPreloadPath(), + devTools: false, // 개발자 도구 비활성화 }, width: 1440, height: 920, @@ -174,6 +198,28 @@ function createWindow() { mainWindow?.show(); }); + // 화면 해상도에 따라 줌 레벨 조정 + adjustZoomLevelIfNeeded(mainWindow); + + // cmd + option + i 단축키 차단 + mainWindow.webContents.on('before-input-event', (event, input) => { + // 개발자 도구를 열 수 있는 모든 단축키 차단 + if ( + // cmd + option + i (macOS) + (input.key === 'i' && input.meta && input.alt) || + // F12 + input.key === 'F12' || + // cmd + shift + i (macOS), ctrl + shift + i (Windows/Linux) + (input.key === 'i' && input.shift && (input.meta || input.control)) || + // cmd + shift + c (macOS), ctrl + shift + c (Windows/Linux) + (input.key === 'c' && input.shift && (input.meta || input.control)) || + // cmd + shift + j (macOS), ctrl + shift + j (Windows/Linux) + (input.key === 'j' && input.shift && (input.meta || input.control)) + ) { + event.preventDefault(); + } + }); + mainWindow.webContents.setWindowOpenHandler((details) => { shell.openExternal(details.url); // Open URL in user's browser. return { action: 'deny' }; // Prevent the app from opening the URL. @@ -206,6 +252,7 @@ function createAuthenticatedWindow( authWindow = new BrowserWindow({ webPreferences: { preload: getPreloadPath(), + devTools: false, // 개발자 도구 비활성화 }, width: 1440, height: 920, @@ -218,6 +265,28 @@ function createAuthenticatedWindow( authWindow?.show(); }); + // 화면 해상도에 따라 줌 레벨 조정 + adjustZoomLevelIfNeeded(authWindow); + + // cmd + option + i 단축키 차단 + authWindow.webContents.on('before-input-event', (event, input) => { + // 개발자 도구를 열 수 있는 모든 단축키 차단 + if ( + // cmd + option + i (macOS) + (input.key === 'i' && input.meta && input.alt) || + // F12 + input.key === 'F12' || + // cmd + shift + i (macOS), ctrl + shift + i (Windows/Linux) + (input.key === 'i' && input.shift && (input.meta || input.control)) || + // cmd + shift + c (macOS), ctrl + shift + c (Windows/Linux) + (input.key === 'c' && input.shift && (input.meta || input.control)) || + // cmd + shift + j (macOS), ctrl + shift + j (Windows/Linux) + (input.key === 'j' && input.shift && (input.meta || input.control)) + ) { + event.preventDefault(); + } + }); + // 닫기 버튼 클릭 시 앱을 종료하지 않고 숨김 처리 authWindow.on('close', (event) => { // 앱이 실제로 종료되려는 경우는 처리하지 않음 From 01c8534029b885ff10dcad4d18767f271ec372d7 Mon Sep 17 00:00:00 2001 From: 10tentacion Date: Sat, 10 May 2025 20:19:06 +0900 Subject: [PATCH 25/30] =?UTF-8?q?feat:=20truncate=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 제대로 안되어서 확인 필요함 --- src/app/pages/AllowedServicePage/AllowedServicePage.tsx | 8 +++----- .../RecommendService/RecommendService.tsx | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/app/pages/AllowedServicePage/AllowedServicePage.tsx b/src/app/pages/AllowedServicePage/AllowedServicePage.tsx index 352aaf8d..6633a14f 100644 --- a/src/app/pages/AllowedServicePage/AllowedServicePage.tsx +++ b/src/app/pages/AllowedServicePage/AllowedServicePage.tsx @@ -52,11 +52,10 @@ const AllowedServicePage = () => { const friendsModalRef = useRef(null); const requireTitleModalRef = useRef(null); - + const bellIconRef = useRef(null); const notificationPanelRef = useRef(null); - const handleChangeTitleInput = (e: ChangeEvent) => { setTitleInput(e.target.value); }; @@ -385,10 +384,9 @@ const AllowedServicePage = () => { {() => } - - - {isNotificationVisible && } + + {isNotificationVisible && } ); }; diff --git a/src/app/pages/AllowedServicePage/RecommendService/RecommendService.tsx b/src/app/pages/AllowedServicePage/RecommendService/RecommendService.tsx index 86996815..8f9f5a65 100644 --- a/src/app/pages/AllowedServicePage/RecommendService/RecommendService.tsx +++ b/src/app/pages/AllowedServicePage/RecommendService/RecommendService.tsx @@ -22,7 +22,7 @@ const RecommendServiceItem = ({ recommendSite, ...props }: RecommendServiceItemP alt={`${recommendSite.siteName} 아이콘`} className="h-[4.2rem] w-[4.2rem] rounded-full" /> -

    {recommendSite.siteName}

    +

    {recommendSite.siteName}

    ); }; From 238cb3b896ea96cc0d55d10ebfe2dc3e36663fe1 Mon Sep 17 00:00:00 2001 From: 10tentacion Date: Sat, 10 May 2025 20:38:27 +0900 Subject: [PATCH 26/30] =?UTF-8?q?feat:=20=EC=9E=90=EB=8F=99=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=A4=8C=20=EB=90=98=EB=8A=94=20=ED=98=84=EC=83=81?= =?UTF-8?q?=20=EB=B0=A9=EC=A7=80,=20=EC=A0=84=EC=B2=B4=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=EC=97=90=EC=84=9C=20x=20=EB=88=8C=EB=A0=80=EC=9D=84?= =?UTF-8?q?=20=EB=95=8C=20=EA=B2=80=EC=A0=95=20=ED=99=94=EB=A9=B4=20?= =?UTF-8?q?=EB=B0=A9=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/logo_icon.png | Bin 75079 -> 83043 bytes public/logo_icon.svg | 8 +++---- src/electron/main.ts | 52 ++++++++++++++++++++++++++++++++++++++----- 3 files changed, 50 insertions(+), 10 deletions(-) diff --git a/public/logo_icon.png b/public/logo_icon.png index fc5de299a2f07cc668c2c2ac49c6011fc8ff966e..339d13d0c697f517b1a8fb15f62b4537740f73a6 100644 GIT binary patch literal 83043 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 literal 75079 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 Cb)LHb diff --git a/public/logo_icon.svg b/public/logo_icon.svg index a2db73a7..8d53e970 100644 --- a/public/logo_icon.svg +++ b/public/logo_icon.svg @@ -1,8 +1,8 @@ - - - + + + - + diff --git a/src/electron/main.ts b/src/electron/main.ts index 5d65db60..9ae45849 100644 --- a/src/electron/main.ts +++ b/src/electron/main.ts @@ -201,7 +201,15 @@ function createWindow() { // 화면 해상도에 따라 줌 레벨 조정 adjustZoomLevelIfNeeded(mainWindow); - // cmd + option + i 단축키 차단 + // 줌 관련 설정 + mainWindow.webContents.on('zoom-changed', (event, zoomDirection) => { + // cmd+ 또는 cmd- 키를 사용한 줌만 허용 + if (zoomDirection !== 'in' && zoomDirection !== 'out') { + event.preventDefault(); + } + }); + + // 마우스 휠로 인한 줌 변경 방지 (pinch-to-zoom) mainWindow.webContents.on('before-input-event', (event, input) => { // 개발자 도구를 열 수 있는 모든 단축키 차단 if ( @@ -214,7 +222,9 @@ function createWindow() { // cmd + shift + c (macOS), ctrl + shift + c (Windows/Linux) (input.key === 'c' && input.shift && (input.meta || input.control)) || // cmd + shift + j (macOS), ctrl + shift + j (Windows/Linux) - (input.key === 'j' && input.shift && (input.meta || input.control)) + (input.key === 'j' && input.shift && (input.meta || input.control)) || + // 줌 관련 키 중 cmd+ 및 cmd- 외의 다른 키 차단 (ctrl+0 등) + (input.key === '0' && (input.meta || input.control)) ) { event.preventDefault(); } @@ -230,7 +240,17 @@ function createWindow() { // 앱이 실제로 종료되려는 경우는 처리하지 않음 if (!isAppQuitting && authWindow !== null && !authWindow.isDestroyed() && mainWindow && !mainWindow.isDestroyed()) { event.preventDefault(); - mainWindow.hide(); // 최소화 대신 숨김 처리 + + // 전체화면 상태인지 확인 + if (mainWindow.isFullScreen()) { + // 전체화면 상태면 먼저 전체화면 해제 후 숨김 + mainWindow.setFullScreen(false); + // 전체화면 해제 애니메이션 완료 후 숨김 처리 + } else { + // 전체화면이 아니면 바로 숨김 + mainWindow.hide(); + } + return false; } @@ -268,7 +288,15 @@ function createAuthenticatedWindow( // 화면 해상도에 따라 줌 레벨 조정 adjustZoomLevelIfNeeded(authWindow); - // cmd + option + i 단축키 차단 + // 줌 관련 설정 + authWindow.webContents.on('zoom-changed', (event, zoomDirection) => { + // cmd+ 또는 cmd- 키를 사용한 줌만 허용 + if (zoomDirection !== 'in' && zoomDirection !== 'out') { + event.preventDefault(); + } + }); + + // 마우스 휠로 인한 줌 변경 방지 및 개발자 도구 단축키 차단 authWindow.webContents.on('before-input-event', (event, input) => { // 개발자 도구를 열 수 있는 모든 단축키 차단 if ( @@ -281,7 +309,9 @@ function createAuthenticatedWindow( // cmd + shift + c (macOS), ctrl + shift + c (Windows/Linux) (input.key === 'c' && input.shift && (input.meta || input.control)) || // cmd + shift + j (macOS), ctrl + shift + j (Windows/Linux) - (input.key === 'j' && input.shift && (input.meta || input.control)) + (input.key === 'j' && input.shift && (input.meta || input.control)) || + // 줌 관련 키 중 cmd+ 및 cmd- 외의 다른 키 차단 (ctrl+0 등) + (input.key === '0' && (input.meta || input.control)) ) { event.preventDefault(); } @@ -292,7 +322,17 @@ function createAuthenticatedWindow( // 앱이 실제로 종료되려는 경우는 처리하지 않음 if (!isAppQuitting && authWindow && !authWindow.isDestroyed()) { event.preventDefault(); - authWindow.hide(); // 최소화 대신 숨김 처리 + + // 전체화면 상태인지 확인 + if (authWindow.isFullScreen()) { + // 전체화면 상태면 먼저 전체화면 해제 후 숨김 + authWindow.setFullScreen(false); + // 전체화면 해제 애니메이션 완료 후 숨김 처리 + } else { + // 전체화면이 아니면 바로 숨김 + authWindow.hide(); + } + return false; } From 83b9de1301436490e766228c826029f38bc71062 Mon Sep 17 00:00:00 2001 From: 10tentacion Date: Sun, 11 May 2025 06:29:01 +0900 Subject: [PATCH 27/30] =?UTF-8?q?feat:=20=ED=83=80=EC=9D=B4=EB=A8=B8?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EC=84=A0=ED=83=9D=EB=90=9C=20=ED=95=A0=20?= =?UTF-8?q?=EC=9D=BC=EC=9D=B4=20=EC=97=86=EC=9D=84=20=EA=B2=BD=EC=9A=B0=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/pages/HomePage/HomePage.tsx | 22 +++++++++++++- .../ModalContentsTimerError.tsx | 29 +++++++++++++++++++ src/app/pages/TimerPage/TimerPage.tsx | 21 ++++++++++++-- 3 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 src/app/pages/HomePage/ModalContentsTimerError/ModalContentsTimerError.tsx diff --git a/src/app/pages/HomePage/HomePage.tsx b/src/app/pages/HomePage/HomePage.tsx index 9181d23c..5fe3f6a1 100644 --- a/src/app/pages/HomePage/HomePage.tsx +++ b/src/app/pages/HomePage/HomePage.tsx @@ -3,7 +3,7 @@ import timezone from 'dayjs/plugin/timezone'; import utc from 'dayjs/plugin/utc'; import { useEffect, useRef, useState } from 'react'; -import { useNavigate } from 'react-router-dom'; +import { useLocation, useNavigate } from 'react-router-dom'; import AutoFixedGrid from '@/shared/components/AutoFixedGrid/AutoFixedGrid'; import ModalContentsFriends from '@/shared/components/ModalContentsFriends/ModalContentsFriends'; @@ -44,12 +44,17 @@ import ButtonMoreFriends from './ButtonMoreFriends/ButtonMoreFriends'; import ButtonUserProfile from './ButtonUserProfile/ButtonUserProfile'; import DatePicker from './DatePicker/DatePicker'; import TimerRestriction from './ModalContentsAlert/TimerRestriction/TimerRestriction'; +import ModalContentsTimerError from './ModalContentsTimerError/ModalContentsTimerError'; import StatusDefaultHome from './StatusDefaultHome/StatusDefaultHome'; dayjs.extend(utc); dayjs.extend(timezone); const HomePage = () => { + const { search } = useLocation(); + const params = new URLSearchParams(search); + const isTimerError = params.get('error') === 'true'; + const todayDate = dayjs().tz('Asia/Seoul'); const formattedTodayDate = todayDate.format('YYYY-MM-DD'); const categoryRef = useRef(null); @@ -59,6 +64,7 @@ const HomePage = () => { const notificationPanelRef = useRef(null); const bellIconRef = useRef(null); const timerRestrictionModalRef = useRef(null); + const timerErrorModalRef = useRef(null); const [isNotificationVisible, setIsNotificationVisible] = useState(false); @@ -155,6 +161,10 @@ const HomePage = () => { ); }; + const handleCloseTimerErrorModal = () => { + timerErrorModalRef.current?.close(); + }; + const handleOpenFriendsModal = () => { friendsModalRef.current?.open(); }; @@ -250,6 +260,12 @@ const HomePage = () => { handleCategoryScroll(); }, [isAddingCategory, dailyCategoryTask.length]); + useEffect(() => { + if (isTimerError) { + timerErrorModalRef.current?.open(); + } + }, [isTimerError]); + return ( { )} + + {() => } + + {isNotificationVisible && } ); diff --git a/src/app/pages/HomePage/ModalContentsTimerError/ModalContentsTimerError.tsx b/src/app/pages/HomePage/ModalContentsTimerError/ModalContentsTimerError.tsx new file mode 100644 index 00000000..bfb2df0f --- /dev/null +++ b/src/app/pages/HomePage/ModalContentsTimerError/ModalContentsTimerError.tsx @@ -0,0 +1,29 @@ +import { forwardRef } from 'react'; + +import { ButtonRadius5 } from '@/shared/components/ButtonRadius5/ButtonRadius5'; + +interface ModalContentsTimerErrorProps { + onClick: () => void; +} + +const ModalContentsTimerError = forwardRef(({ onClick }, ref) => { + return ( +
    +

    + 자정이 지나 할 일이 초기화되었어요. +
    + 다시 할 일을 선택하고 몰입해 볼까요? +

    + + 확인 + +
    + ); +}); + +ModalContentsTimerError.displayName = 'ModalContentsTimerError'; + +export default ModalContentsTimerError; diff --git a/src/app/pages/TimerPage/TimerPage.tsx b/src/app/pages/TimerPage/TimerPage.tsx index b2381c76..50572f59 100644 --- a/src/app/pages/TimerPage/TimerPage.tsx +++ b/src/app/pages/TimerPage/TimerPage.tsx @@ -2,12 +2,12 @@ import dayjs from 'dayjs'; import timezone from 'dayjs/plugin/timezone'; import utc from 'dayjs/plugin/utc'; -import { useCallback, useMemo } from 'react'; +import { useCallback, useEffect, useMemo } from 'react'; import { useNavigate } from 'react-router-dom'; import { splitTasksByCompletion } from '@/shared/utils/timer'; -import { useGetTimerTodos } from '@/shared/apisV2/timer/timer.queries'; +import { useGetSelectedTimerTask, useGetTimerTodos } from '@/shared/apisV2/timer/timer.queries'; import AllowedServicesPopover from './AllowedServices/AllowedServicesPopover'; import AllowedServicesTitle from './AllowedServices/AllowedServicesTitle'; @@ -48,6 +48,10 @@ const TimerPageContent = () => { return splitTasksByCompletion(todos); }, [todosData]); + const { data: selectedTimerTaskData, isError: isSelectedTimerTaskError } = useGetSelectedTimerTask({ + targetDate: todayFormattedDate, + }); + // 허용되지 않은 URL 감지 시 처리 const handleRegisterAllowedService = useCallback( (url: string) => { @@ -79,6 +83,19 @@ const TimerPageContent = () => { } }, [isPlaying, selectedTask.id, browserMonitor.isActive, actions, navigate]); + useEffect(() => { + if (isSelectedTimerTaskError || selectedTimerTaskData?.data.selectedTaskId === null) { + // 시스템 알림 띄우기 함수 + const showSystemNotification = (title: string, message: string) => { + if ('Notification' in window) { + new window.Notification(title, { body: message }); + } + }; + showSystemNotification('타이머에서 선택된 할일이 초기화 되었어요.', '다시 할 일을 선택하고 몰입해 볼까요?'); + navigate('/home?error=true'); + } + }, [isSelectedTimerTaskError, navigate, selectedTimerTaskData?.data.selectedTaskId]); + return (
    From 9af821ba8fd6c6440048a9a284af3069db51890a Mon Sep 17 00:00:00 2001 From: 10tentacion Date: Sun, 11 May 2025 06:40:09 +0900 Subject: [PATCH 28/30] =?UTF-8?q?feat:=20=ED=83=80=EC=9D=B4=EB=A8=B8?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EC=84=A0=ED=83=9D=EB=90=9C=20=ED=95=A0=20?= =?UTF-8?q?=EC=9D=BC=EC=9D=B4=20=EC=97=86=EC=9D=84=20=EA=B2=BD=EC=9A=B0=20?= =?UTF-8?q?=EC=8B=9C=EC=8A=A4=ED=85=9C=20=EC=95=8C=EB=A6=BC=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/pages/TimerPage/TimerPage.tsx | 15 ++++++++------- src/app/vite-env.d.ts | 4 ++++ src/electron/main.ts | 16 +++++++++++++++- src/electron/preload.cts | 8 ++++++++ 4 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/app/pages/TimerPage/TimerPage.tsx b/src/app/pages/TimerPage/TimerPage.tsx index 50572f59..b0e8b07b 100644 --- a/src/app/pages/TimerPage/TimerPage.tsx +++ b/src/app/pages/TimerPage/TimerPage.tsx @@ -85,13 +85,14 @@ const TimerPageContent = () => { useEffect(() => { if (isSelectedTimerTaskError || selectedTimerTaskData?.data.selectedTaskId === null) { - // 시스템 알림 띄우기 함수 - const showSystemNotification = (title: string, message: string) => { - if ('Notification' in window) { - new window.Notification(title, { body: message }); - } - }; - showSystemNotification('타이머에서 선택된 할일이 초기화 되었어요.', '다시 할 일을 선택하고 몰입해 볼까요?'); + // Electron 시스템 알림 사용 + if (window.electron && window.electron.notification) { + window.electron.notification.showSystemNotification( + '타이머에서 선택된 할일이 초기화 되었어요.', + '다시 타이머를 실행해주세요.', + ); + } + navigate('/home?error=true'); } }, [isSelectedTimerTaskError, navigate, selectedTimerTaskData?.data.selectedTaskId]); diff --git a/src/app/vite-env.d.ts b/src/app/vite-env.d.ts index 2c7e6145..ff71c620 100644 --- a/src/app/vite-env.d.ts +++ b/src/app/vite-env.d.ts @@ -24,5 +24,9 @@ interface Window { auth?: { relogin: () => void; }; + + notification?: { + showSystemNotification: (title: string, body: string) => void; + }; }; } diff --git a/src/electron/main.ts b/src/electron/main.ts index 9ae45849..8289bd79 100644 --- a/src/electron/main.ts +++ b/src/electron/main.ts @@ -1,4 +1,4 @@ -import { BrowserWindow, app, ipcMain, screen, shell } from 'electron'; +import { BrowserWindow, Notification, app, ipcMain, screen, shell } from 'electron'; import path from 'path'; import { startBrowserMonitoring, stopBrowserMonitoring } from './browserMonitor.js'; @@ -366,3 +366,17 @@ ipcMain.on('shell:open', () => { const pagePath = path.join('file://', pageDirectory, 'index.html'); shell.openExternal(pagePath); }); + +// 시스템 알림 표시 IPC 핸들러 +ipcMain.on('notification:show', (_, { title, body }) => { + const iconPath = path.join(app.getAppPath(), 'dist-electron/morib_logo.png'); + + const notification = new Notification({ + title, + body, + icon: iconPath, + silent: false, + }); + + notification.show(); +}); diff --git a/src/electron/preload.cts b/src/electron/preload.cts index f9020ea9..54810a9a 100644 --- a/src/electron/preload.cts +++ b/src/electron/preload.cts @@ -43,4 +43,12 @@ contextBridge.exposeInMainWorld('electron', { ipcRenderer.send('auth:relogin'); }, }, + + // 시스템 알림 관련 API 추가 + notification: { + // 시스템 알림 표시 함수 + showSystemNotification: (title: string, body: string) => { + ipcRenderer.send('notification:show', { title, body }); + }, + }, }); From 31bf1b01bd78252cec5495a6174035d04c417090 Mon Sep 17 00:00:00 2001 From: 10tentacion Date: Mon, 12 May 2025 00:10:55 +0900 Subject: [PATCH 29/30] =?UTF-8?q?feat:=20throwOnError=EB=A5=BC=20=ED=86=B5?= =?UTF-8?q?=ED=95=B4=EC=84=9C=20=EC=97=90=EB=9F=AC=20=EA=B0=9D=EC=B2=B4?= =?UTF-8?q?=EB=A5=BC=20=EA=B0=90=EC=A7=80=ED=95=98=EA=B2=8C=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/shared/apisV2/timer/timer.queries.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/shared/apisV2/timer/timer.queries.ts b/src/app/shared/apisV2/timer/timer.queries.ts index 5ff1fe2a..4a812d3a 100644 --- a/src/app/shared/apisV2/timer/timer.queries.ts +++ b/src/app/shared/apisV2/timer/timer.queries.ts @@ -54,6 +54,7 @@ export const useGetSelectedTimerTask = ( return useQuery({ queryKey: timerKeys.selectedTimerTask({ targetDate }), queryFn: () => getSelectedTimerTask({ targetDate }), + throwOnError: false, staleTime: 5000, // 5초 동안 캐시 데이터 사용 ...options, }); From 908537366fd8706ce47abe6bd6c84ac0cc009ef6 Mon Sep 17 00:00:00 2001 From: 10tentacion Date: Wed, 9 Jul 2025 18:01:51 +0900 Subject: [PATCH 30/30] =?UTF-8?q?feat:=20packages.json=EC=97=90=EC=84=9C?= =?UTF-8?q?=20=EC=BD=94=EC=96=B4=ED=8C=A9=20=EC=82=AC=EC=9A=A9=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=AA=85=EC=8B=9C=EB=90=9C=20=ED=95=84=EB=93=9C=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20pnpm=20=EB=B2=84=EC=A0=84=20=EB=AA=85?= =?UTF-8?q?=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 5e72f3ce..8f6360b3 100644 --- a/package.json +++ b/package.json @@ -81,5 +81,7 @@ "public" ] }, - "packageManager": "pnpm@9.15.4+sha512.b2dc20e2fc72b3e18848459b37359a32064663e5627a51e4c74b2c29dd8e8e0491483c3abb40789cfd578bf362fb6ba8261b05f0387d76792ed6e23ea3b1b6a0" + "engines": { + "pnpm": "9" + } }