From 2910b20974b479c5cee70fa4bb79a057f2db3358 Mon Sep 17 00:00:00 2001 From: Jay Sheen Date: Thu, 18 Sep 2025 10:54:29 +0900 Subject: [PATCH] =?UTF-8?q?=EA=B3=B5=EC=A7=80=EC=82=AC=ED=95=AD=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EA=B0=9C=EC=84=A0=20-=20?= =?UTF-8?q?=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=20=ED=95=84=ED=84=B0=20?= =?UTF-8?q?=EB=B0=8F=20=EB=8B=A4=EA=B5=AD=EC=96=B4=20=EC=A7=80=EC=9B=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 공지사항 리스트/상세 페이지에 다국어(i18n) 적용 - 카테고리 선택 필터 기능 추가 (전체/공지/이벤트/서비스/중요) - 검색창 Enter 키 지원 및 한글 입력 이슈 해결 - 검색/필터 실행 시 input 포커스 자동 해제 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- src/app/providers/app-provider.tsx | 1 + src/entities/support/ui/notice-item.tsx | 6 ++-- src/locales/en.json | 15 ++++++++- src/locales/i18n.ts | 4 +-- src/locales/ko.json | 15 ++++++++- src/pages/support/notice/detail-page.tsx | 9 ++++-- src/pages/support/notice/list-page.tsx | 39 ++++++++++++++++++------ 7 files changed, 71 insertions(+), 18 deletions(-) diff --git a/src/app/providers/app-provider.tsx b/src/app/providers/app-provider.tsx index f20b0a6..051a8b7 100644 --- a/src/app/providers/app-provider.tsx +++ b/src/app/providers/app-provider.tsx @@ -5,6 +5,7 @@ import { CookiesProvider } from 'react-cookie'; import { QueryClientProvider } from '@tanstack/react-query'; import { GlobalErrorBoundary } from '@/widgets/error-boundaries'; import { getGlobalQueryClient } from '@/shared/configs/query'; +import '@/locales/i18n'; interface AppProviderProps { children: React.ReactNode; diff --git a/src/entities/support/ui/notice-item.tsx b/src/entities/support/ui/notice-item.tsx index 49389ff..4b248a2 100644 --- a/src/entities/support/ui/notice-item.tsx +++ b/src/entities/support/ui/notice-item.tsx @@ -2,15 +2,17 @@ import { PATHS } from '@/shared/constants/paths'; import { useNavigate } from '@/shared/lib/hooks/use-navigate'; import { NoticeItemProps } from '../model/types'; import moment from 'moment'; +import { useTranslation } from 'react-i18next'; export const SupportNoticeItem = ({ id, title, category, regDate, - isNew + isNew // eslint-disable-line @typescript-eslint/no-unused-vars }: NoticeItemProps) => { const { navigate } = useNavigate(); + const { t } = useTranslation(); const onClickToDetail = () => { navigate(PATHS.support.notice.detail, { @@ -30,7 +32,7 @@ export const SupportNoticeItem = ({
{ title }
- { category }{ moment(regDate).format('YYYY.MM.DD HH:mm:ss') } + { t(`support.notice.categories.${category}`) }{ moment(regDate).format('YYYY.MM.DD HH:mm:ss') }
diff --git a/src/locales/en.json b/src/locales/en.json index 8fbf129..d08f49f 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -74,5 +74,18 @@ "status": "Status", "completed": "Completed", "failed": "Failed" - } + }, + "support": { + "notice": { + "title": "Notice", + "searchPlaceholder": "Enter search keyword", + "categories": { + "ALL": "All", + "NOTICE": "Notice", + "EVENT": "Event", + "SERVICE": "Service", + "IMPORTANT": "Important" + } + } + } } \ No newline at end of file diff --git a/src/locales/i18n.ts b/src/locales/i18n.ts index 140863f..390c757 100644 --- a/src/locales/i18n.ts +++ b/src/locales/i18n.ts @@ -13,8 +13,8 @@ i18n en: {translation: en}, ko: {translation: ko}, }, - lng: 'en', - fallbackLng: 'en', + lng: 'ko', + fallbackLng: 'ko', debug: true, interpolation: { escapeValue: false, diff --git a/src/locales/ko.json b/src/locales/ko.json index 3df79b4..123bd43 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -74,5 +74,18 @@ "status": "상태", "completed": "완료", "failed": "실패" - } + }, + "support": { + "notice": { + "title": "공지사항", + "searchPlaceholder": "검색어를 입력하세요", + "categories": { + "ALL": "전체", + "NOTICE": "공지", + "EVENT": "이벤트", + "SERVICE": "서비스", + "IMPORTANT": "중요" + } + } + } } \ No newline at end of file diff --git a/src/pages/support/notice/detail-page.tsx b/src/pages/support/notice/detail-page.tsx index 5df5c9d..096d0ae 100644 --- a/src/pages/support/notice/detail-page.tsx +++ b/src/pages/support/notice/detail-page.tsx @@ -1,11 +1,12 @@ import { useEffect, useState } from 'react'; import { useLocation } from 'react-router'; +import { useTranslation } from 'react-i18next'; import { PATHS } from '@/shared/constants/paths'; import { useNoticeDetailMutation } from '@/entities/support/api/use-notice-detail-mutation'; import { useNavigate } from '@/shared/lib/hooks/use-navigate'; import { HeaderType } from '@/entities/common/model/types'; import { NoticeItem } from '@/entities/support/model/types'; -import { +import { useSetHeaderTitle, useSetHeaderType, useSetFooterMode, @@ -16,12 +17,13 @@ import moment from 'moment'; export const NoticeDetailPage = () => { const { navigate } = useNavigate(); const location = useLocation(); + const { t } = useTranslation(); const [result, setResult] = useState({}); let from = location?.state.from; - useSetHeaderTitle('공지사항'); + useSetHeaderTitle(t('support.notice.title')); useSetHeaderType(HeaderType.RightClose); useSetFooterMode(false); useSetOnBack(() => { @@ -51,6 +53,7 @@ export const NoticeDetailPage = () => { useEffect(() => { callDetail(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return ( @@ -61,7 +64,7 @@ export const NoticeDetailPage = () => {
{ result.title }
-
{ moment(result.regDate).format('YYYY.MM.DD') } | { result.category }
+
{ moment(result.regDate).format('YYYY.MM.DD') } | { t(`support.notice.categories.${result.category}`) }
{ result.content }
diff --git a/src/pages/support/notice/list-page.tsx b/src/pages/support/notice/list-page.tsx index d411c2c..8faba6e 100644 --- a/src/pages/support/notice/list-page.tsx +++ b/src/pages/support/notice/list-page.tsx @@ -1,4 +1,5 @@ import { ChangeEvent, useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { PATHS } from '@/shared/constants/paths'; import { useNavigate } from '@/shared/lib/hooks/use-navigate'; import { useNoticeListMutation } from '@/entities/support/api/use-notice-list-mutation'; @@ -6,7 +7,7 @@ import { DEFAULT_PAGE_PARAM } from '@/entities/common/model/constant'; import { NoticeItem } from '@/entities/support/model/types'; import { SupportNoticeItem } from '@/entities/support/ui/notice-item'; import { HeaderType } from '@/entities/common/model/types'; -import { +import { useSetHeaderTitle, useSetHeaderType, useSetFooterMode, @@ -15,12 +16,14 @@ import { export const NoticeListPage = () => { const { navigate } = useNavigate(); + const { t } = useTranslation(); const [pageParam, setPageParam] = useState(DEFAULT_PAGE_PARAM); const [searchKeyword, setSearchKeyword] = useState(''); + const [selectedCategory, setSelectedCategory] = useState('ALL'); const [resultList, setResultList] = useState>([]); - useSetHeaderTitle('공지사항'); + useSetHeaderTitle(t('support.notice.title')); useSetHeaderType(HeaderType.LeftArrow); useSetFooterMode(true); useSetOnBack(() => { @@ -30,11 +33,11 @@ export const NoticeListPage = () => { const { mutateAsync: noticeList } = useNoticeListMutation(); const callList = () => { let listParams = { - category: 'ALL', + category: selectedCategory, searchKeyword: searchKeyword, ...{page: pageParam} }; - + noticeList(listParams).then((rs) => { console.log(rs) setResultList(rs.content); @@ -42,6 +45,10 @@ export const NoticeListPage = () => { }; const onClickToAction = () => { + // Remove focus from active element + if (document.activeElement instanceof HTMLElement) { + document.activeElement.blur(); + } callList(); }; @@ -64,7 +71,8 @@ export const NoticeListPage = () => { useEffect(() => { callList(); - }, []); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [selectedCategory]); return ( <> @@ -79,16 +87,29 @@ export const NoticeListPage = () => { aria-hidden="true" onClick={ () => onClickToAction() } > - ) => setSearchKeyword(e.target.value) } + onKeyDown={ (e: React.KeyboardEvent) => { + if (e.key === 'Enter' && !e.nativeEvent.isComposing) { + onClickToAction(); + } + }} />
- ) => setSelectedCategory(e.target.value)} + > + + + + +