diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..0506d83 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +vite.config.ts +jest.config.ts \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json index ff33cbd..41f68a8 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -33,6 +33,9 @@ "typescript": { "alwaysTryTypes": true } + }, + "react": { + "version": "detect" } }, "rules": { diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 390dc1c..30222da 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -19,7 +19,7 @@ jobs: uses: actions/checkout@master - name: Cache node modules - uses: actions/cache@v1 + uses: actions/cache@v4 with: path: node_modules key: ${{ runner.OS }}-build-${{ hashFiles('**/package-lock.json') }} @@ -41,11 +41,14 @@ jobs: - name: Install Dependencies run: pnpm install --no-frozen-lockfile --force + - name: Lint Code + run: pnpm lint + - name: Build run: CI='' pnpm run build - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v1 + uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} diff --git a/index.html b/index.html index bc79258..f74e58e 100644 --- a/index.html +++ b/index.html @@ -5,36 +5,6 @@ - GDSC_KNU_OFFICIAL - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/public/ApplyInquiry.png b/public/ApplyInquiry.png new file mode 100644 index 0000000..3d62dab Binary files /dev/null and b/public/ApplyInquiry.png differ diff --git a/public/CommingSoon.png b/public/CommingSoon.png new file mode 100644 index 0000000..6828854 Binary files /dev/null and b/public/CommingSoon.png differ diff --git a/src/apis/hooks/apply/ApplyInquiryAPI.ts b/src/apis/hooks/apply/ApplyInquiryAPI.ts index c681e78..44d8e3b 100644 --- a/src/apis/hooks/apply/ApplyInquiryAPI.ts +++ b/src/apis/hooks/apply/ApplyInquiryAPI.ts @@ -14,6 +14,12 @@ export const ApplyInquiryAPI = async (name: string, studentNumber: string) => { if (typedError.code === 409) { alert('최종지원된 서류는 확인하실 수 없습니다.'); throw new Error('최종지원된 서류는 확인하실 수 없습니다.'); + } else if (typedError.code === 403) { + alert('본인이 아닌 지원자의 서류는 확인하실 수 없습니다.'); + throw new Error('본인이 아닌 지원자의 서류는 확인하실 수 없습니다.'); + } else { + alert('서버 오류가 발생했습니다. 잠시 후 다시 시도해주세요.'); + throw new Error('서버 오류가 발생했습니다. 잠시 후 다시 시도해주세요.'); } } throw new Error('아직 서류를 제출하지 않으셨습니다.'); diff --git a/src/components/feature/auth/AuthModal.tsx b/src/components/feature/auth/AuthModal.tsx index 8e4f4b3..37ba981 100644 --- a/src/components/feature/auth/AuthModal.tsx +++ b/src/components/feature/auth/AuthModal.tsx @@ -3,7 +3,6 @@ import Text from '@gdsc/components/common/typography/Text'; import { AuthBox } from '@gdsc/styles/AuthModalStyle'; import { displayCenter } from '@gdsc/styles/LayoutStyle'; -// eslint-disable-next-line import/no-unresolved import logo from '/GDSC.svg'; import styled from '@emotion/styled'; diff --git a/src/hooks/useScrollTracker.ts b/src/hooks/useScrollTracker.ts new file mode 100644 index 0000000..144fd0c --- /dev/null +++ b/src/hooks/useScrollTracker.ts @@ -0,0 +1,27 @@ +import { useEffect } from 'react'; + +import { handleScroll } from '@gdsc/utils/anlytics'; + +// 유틸리티 함수 경로 + +const useScrollTracker = () => { + useEffect(() => { + const onScroll = () => { + const scrollPosition = window.scrollY; + const scrollHeight = + document.documentElement.scrollHeight - window.innerHeight; + const scrolledPercentage = (scrollPosition / scrollHeight) * 100; + + if (scrolledPercentage >= 50) { + handleScroll(); + } + }; + + window.addEventListener('scroll', onScroll); + return () => { + window.removeEventListener('scroll', onScroll); + }; + }, []); +}; + +export default useScrollTracker; diff --git a/src/pages/CommingSoonPage.tsx b/src/pages/CommingSoonPage.tsx index 6bffaa5..03692e2 100644 --- a/src/pages/CommingSoonPage.tsx +++ b/src/pages/CommingSoonPage.tsx @@ -33,42 +33,44 @@ const CommingSoonPage = () => { const isMobile = useMediaQuery({ query: '(max-width: 500px)' }); return ( - - home - - Comming Soon - - - 죄송합니다. -
현재 개발 중인 페이지입니다. -
- - - 빠른 시일내에
- 사용자 분들께 더 좋은 서비스를 제공할 수 있도록 노력하겠습니다. -
- 감사합니다. + <> + + home + + Comming Soon -
- {isMobile && ( - - - 홈으로 - - - )} -
+ + 죄송합니다. +
현재 개발 중인 페이지입니다. +
+ + + 빠른 시일내에
+ 사용자 분들께 더 좋은 서비스를 제공할 수 있도록 노력하겠습니다. +
+ 감사합니다. +
+
+ {isMobile && ( + + + 홈으로 + + + )} + + ); }; diff --git a/src/pages/apply/ApplyExPage.tsx b/src/pages/apply/ApplyExPage.tsx index 087dd8e..d1c3e7d 100644 --- a/src/pages/apply/ApplyExPage.tsx +++ b/src/pages/apply/ApplyExPage.tsx @@ -1,13 +1,10 @@ -import { useParams } from 'react-router-dom'; - import ApplyEx from '@gdsc/pages/apply/components/ApplyEx'; import Star from '@gdsc/pages/main/components/Star'; -import { SEO } from '@gdsc/utils/Seo'; - import { DisplayLayout } from '@gdsc/styles/LayoutStyle'; import styled from '@emotion/styled'; +import { ApplyExMetaData } from '@gdsc/router/components/MetaData'; const ApplyLayout = styled(DisplayLayout)` height: 100%; @@ -18,16 +15,9 @@ const ApplyLayout = styled(DisplayLayout)` `; const ApplyExPage = () => { - const { tech } = useParams(); - return ( <> - + {[...Array(25)].map((_, index) => ( diff --git a/src/pages/apply/ApplyFormPage.tsx b/src/pages/apply/ApplyFormPage.tsx index 13c2910..b20a725 100644 --- a/src/pages/apply/ApplyFormPage.tsx +++ b/src/pages/apply/ApplyFormPage.tsx @@ -1,23 +1,14 @@ -import { useParams } from 'react-router-dom'; - import ApplyForm from '@gdsc/pages/apply/components/ApplyForm'; import Star from '@gdsc/pages/main/components/Star'; -import { SEO } from '@gdsc/utils/Seo'; - import { DisplayLayout } from '@gdsc/styles/LayoutStyle'; -const ApplyFormPage = () => { - const { tech } = useParams(); +import { ApplyFormMetaData } from '@gdsc/router/components/MetaData'; +const ApplyFormPage = () => { return ( <> - + {[...Array(25)].map((_, index) => ( diff --git a/src/pages/apply/ApplyPage.tsx b/src/pages/apply/ApplyPage.tsx index 11ec7c6..fe814df 100644 --- a/src/pages/apply/ApplyPage.tsx +++ b/src/pages/apply/ApplyPage.tsx @@ -6,11 +6,10 @@ import ApplyNav from '@gdsc/pages/apply/components/ApplyNav'; import ApplyNavEnd from '@gdsc/pages/apply/components/ApplyNavEnd'; import Star from '@gdsc/pages/main/components/Star'; -import { SEO } from '@gdsc/utils/Seo'; - import { DisplayLayout } from '@gdsc/styles/LayoutStyle'; import styled from '@emotion/styled'; +import { ApplyMetaData } from '@gdsc/router/components/MetaData'; const ApplyLayout = styled(DisplayLayout)` height: 100%; @@ -31,12 +30,7 @@ const ApplyPage = () => { return ( <> - + {showForm ? : } {[...Array(25)].map((_, index) => ( diff --git a/src/pages/apply/components/ApplyForm.tsx b/src/pages/apply/components/ApplyForm.tsx index 0043c91..8fce064 100644 --- a/src/pages/apply/components/ApplyForm.tsx +++ b/src/pages/apply/components/ApplyForm.tsx @@ -32,6 +32,7 @@ import { } from '@gdsc/pages/apply/components/ApplyFormDocs'; import { ApplyFormSchema } from '@gdsc/utils/ApplyFormScehma.util'; +import { handleFormSubmit } from '@gdsc/utils/anlytics'; import { useApplyFormMutation } from '@gdsc/apis/hooks/apply/ApplyFormQuery'; @@ -141,10 +142,12 @@ const ApplyForm = () => { })) || [], }; if (submitType === 'submit') { + handleFormSubmit('Final Submit'); submitApplication(finalFormData); // console.log(finalFormData); } else if (submitType === 'save') { // console.log(saveFormData); + handleFormSubmit('Save Submit'); submitApplication(saveFormData); } } diff --git a/src/pages/apply/components/ApplySaveForm.tsx b/src/pages/apply/components/ApplySaveForm.tsx index 8d51b60..9fca843 100644 --- a/src/pages/apply/components/ApplySaveForm.tsx +++ b/src/pages/apply/components/ApplySaveForm.tsx @@ -31,6 +31,7 @@ import { } from '@gdsc/pages/apply/components/ApplyFormDocs'; import { ApplyFormSchema } from '@gdsc/utils/ApplyFormScehma.util'; +import { handleFormSubmit } from '@gdsc/utils/anlytics'; import { useApplySaveMutation } from '@gdsc/apis/hooks/apply/ApplySaveQuery'; @@ -45,8 +46,8 @@ import { import { ApplyFormInterface, ApplyFormQuestionInterface, + ApplyFormAPIInterface, } from '@gdsc/types/ApplyInterface'; -import { ApplyFormAPIInterface } from '@gdsc/types/ApplyInterface'; import { ErrorMessage } from '@hookform/error-message'; import { zodResolver } from '@hookform/resolvers/zod'; @@ -128,11 +129,13 @@ const ApplySaveForm = ({ SaveData }: ApplySaveFormProps) => { })) || [], }; if (submitType === 'submit') { + handleFormSubmit('Sub-Final Submit'); saveApplication(finalFormData); // console.log(finalFormData); } else if (submitType === 'save') { // submitApplication(saveFormData); // console.log(saveFormData); + handleFormSubmit('Sub-Save Submit'); saveApplication(saveFormData); } } diff --git a/src/pages/community/CommunityPage.tsx b/src/pages/community/CommunityPage.tsx index d109800..de20869 100644 --- a/src/pages/community/CommunityPage.tsx +++ b/src/pages/community/CommunityPage.tsx @@ -1,5 +1,13 @@ +import CommingSoonPage from '../CommingSoonPage'; +import { CommunityMetaData } from '@gdsc/router/components/MetaData'; + const CommunityPage = () => { - return <>커뮤니티 페이지; + return ( + <> + + + + ); }; export default CommunityPage; diff --git a/src/pages/introduce/IntroducePage.tsx b/src/pages/introduce/IntroducePage.tsx index 50869e1..b5bf8d0 100644 --- a/src/pages/introduce/IntroducePage.tsx +++ b/src/pages/introduce/IntroducePage.tsx @@ -8,23 +8,21 @@ import FieldTable from '@gdsc/pages/introduce/components/FieldTable'; import IntroduceEvent from '@gdsc/pages/introduce/components/IntroduceEvent'; import LineEvent from '@gdsc/pages/introduce/components/LineEvent'; -import { SEO } from '@gdsc/utils/Seo'; +import useScrollTracker from '@gdsc/hooks/useScrollTracker'; import { DisplayLayout } from '@gdsc/styles/LayoutStyle'; +import { IntroduceMetaData } from '@gdsc/router/components/MetaData'; import { ScrollTrigger } from 'gsap/ScrollTrigger'; gsap.registerPlugin(ScrollTrigger); const IntroducePage = () => { + useScrollTracker(); + return ( <> - + diff --git a/src/pages/main/MainPage.tsx b/src/pages/main/MainPage.tsx index a128206..b48ff60 100644 --- a/src/pages/main/MainPage.tsx +++ b/src/pages/main/MainPage.tsx @@ -1,5 +1,4 @@ import { lazy } from 'react'; -import { Helmet } from 'react-helmet-async'; import { useMediaQuery } from 'react-responsive'; import Content from '@gdsc/pages/main/components/Content'; @@ -8,6 +7,7 @@ import MainRound from '@gdsc/pages/main/components/MainRound'; import { DisplayLayout } from '@gdsc/styles/LayoutStyle'; import styled from '@emotion/styled'; +import { MainMetaData } from '@gdsc/router/components/MetaData'; const MainFooterMobile = lazy( () => import('@gdsc/components/feature/footer/MainFooterMobile') @@ -29,20 +29,7 @@ const MainPage = () => { return ( <> - - GDSC KNU - 메인 페이지 - - - - - - + diff --git a/src/pages/main/components/RotateCarousel.style.ts b/src/pages/main/components/RotateCarousel.style.ts index 25f248a..3e3684d 100644 --- a/src/pages/main/components/RotateCarousel.style.ts +++ b/src/pages/main/components/RotateCarousel.style.ts @@ -118,4 +118,4 @@ export const EarthVideo = styled.video` left: 50%; transform: translate(-50%, -50%); clip-path: view-box ellipse(44% 44%); -`; \ No newline at end of file +`; diff --git a/src/pages/mypage/MyPage.tsx b/src/pages/mypage/MyPage.tsx index 441a388..fc9be73 100644 --- a/src/pages/mypage/MyPage.tsx +++ b/src/pages/mypage/MyPage.tsx @@ -15,6 +15,7 @@ import { usePutMyData } from '@gdsc/apis/hooks/mypage/usePutMyData'; import { displayCenter } from '@gdsc/styles/LayoutStyle'; import styled from '@emotion/styled'; +import { MypageMetaData } from '@gdsc/router/components/MetaData'; import { putUserDataInterface } from '@gdsc/types/UserInterface'; const MyPageWrapper = styled.div<{ color: string }>` @@ -199,6 +200,7 @@ const MyPage = () => { return ( <> + diff --git a/src/pages/signin/SigninPage.tsx b/src/pages/signin/SigninPage.tsx index 0c13866..51e9de2 100644 --- a/src/pages/signin/SigninPage.tsx +++ b/src/pages/signin/SigninPage.tsx @@ -2,7 +2,6 @@ import { useEffect } from 'react'; import SigninModal from '@gdsc/pages/signin/components/SigninModal'; -import { SEO } from '@gdsc/utils/Seo'; import { detectUserAgent } from '@gdsc/utils/detectUserAgent'; import { openLinkInKakaoExternal, @@ -11,6 +10,8 @@ import { import { AuthWrapper } from '@gdsc/styles/AuthModalStyle'; +import { SigninMetaData } from '@gdsc/router/components/MetaData'; + const SigninPage = () => { useEffect(function RedirectByUserAgent() { const signInUrl = 'https://gdsc-knu.com/signin'; @@ -23,12 +24,7 @@ const SigninPage = () => { return ( <> - + diff --git a/src/pages/signup/SignupPendingPage.tsx b/src/pages/signup/SignupPendingPage.tsx index 9bd184c..502cb60 100644 --- a/src/pages/signup/SignupPendingPage.tsx +++ b/src/pages/signup/SignupPendingPage.tsx @@ -6,7 +6,6 @@ import homeIcon from '@gdsc/assets/HomeIcon.svg'; import { AuthWrapper, AuthBox } from '@gdsc/styles/AuthModalStyle'; import { displayCenter } from '@gdsc/styles/LayoutStyle'; -// eslint-disable-next-line import/no-unresolved import logo from '/GDSC.svg'; import styled from '@emotion/styled'; diff --git a/src/pages/team/index.tsx b/src/pages/team/index.tsx index 8b21b76..a52476b 100644 --- a/src/pages/team/index.tsx +++ b/src/pages/team/index.tsx @@ -1,3 +1,13 @@ -export const TeamPage = () => { - return <>123; +import CommingSoonPage from '../CommingSoonPage'; +import { TeamMetaData } from '@gdsc/router/components/MetaData'; + +const TeamPage = () => { + return ( + <> + + + + ); }; + +export default TeamPage; diff --git a/src/pages/tech_blog/TechBlogPage.tsx b/src/pages/tech_blog/TechBlogPage.tsx index 0852176..517d6fe 100644 --- a/src/pages/tech_blog/TechBlogPage.tsx +++ b/src/pages/tech_blog/TechBlogPage.tsx @@ -1,5 +1,13 @@ +import CommingSoonPage from '../CommingSoonPage'; +import { TeamBlogMetaData } from '@gdsc/router/components/MetaData'; + const TechBlogPage = () => { - return <>테크블로그 페이지; + return ( + <> + + + + ); }; export default TechBlogPage; diff --git a/src/router/Router.tsx b/src/router/Router.tsx index ee4047d..4acbad3 100644 --- a/src/router/Router.tsx +++ b/src/router/Router.tsx @@ -1,18 +1,22 @@ import { lazy } from 'react'; -import { createBrowserRouter } from 'react-router-dom'; +import { + createBrowserRouter, + IndexRouteObject, + NonIndexRouteObject, + Outlet, +} from 'react-router-dom'; import { AsyncBoundary } from '@gdsc/components/common/AsyncBoundary'; import { LoadingView } from '@gdsc/components/common/View/LoadingView'; -// import { TeamPage } from '@gdsc/pages/team'; -// import TechBlogPage from '@gdsc/pages/tech_blog/TechBlogPage'; -import ErrorPage from '@gdsc/pages/ErrorPage'; - -// import CommunityPage from '@gdsc/pages/community/CommunityPage'; import { TeamUpdateProvider } from '@gdsc/provider/TeamUpdate'; +import RouteChangeTracker from '@gdsc/router/components/RouteChangeTracker'; import StatusRoute from '@gdsc/router/components/StatusRoute'; -const CommingSoonPage = lazy(() => import('@gdsc/pages/CommingSoonPage')); +const TechBlogPage = lazy(() => import('@gdsc/pages/tech_blog/TechBlogPage')); +const ErrorPage = lazy(() => import('@gdsc/pages/ErrorPage')); +const CommunityPage = lazy(() => import('@gdsc/pages/community/CommunityPage')); +const TeamPage = lazy(() => import('@gdsc/pages/team')); const RootPage = lazy(() => import('@gdsc/pages/RootPage')); const MainPage = lazy(() => import('@gdsc/pages/main/MainPage')); const SigninPage = lazy(() => import('@gdsc/pages/signin/SigninPage')); @@ -37,32 +41,24 @@ const AdminTeamArrangePage = lazy( () => import('@gdsc/pages/admin/AdminTeamArrangePage') ); -export const Router = createBrowserRouter([ +type AppRouteObject = (IndexRouteObject | NonIndexRouteObject) & { + children?: AppRouteObject[]; +}; + +const routesConfig: AppRouteObject[] = [ { path: '/', - element: ( - }> - - - ), + element: , id: 'root', errorElement: , children: [ { index: true, - element: ( - }> - - - ), + element: , }, { path: 'signin', - element: ( - }> - - - ), + element: , }, { path: 'apply', @@ -70,40 +66,25 @@ export const Router = createBrowserRouter([ children: [ { path: '', - element: ( - }> - - - ), + element: , }, { path: ':tech', + element: , children: [ { path: '', - element: ( - }> - - - ), + element: , }, { path: 'form', - element: ( - }> - - - ), + element: , }, ], }, { path: 'inquiry', - element: ( - }> - - - ), + element: , }, ], }, @@ -113,11 +94,7 @@ export const Router = createBrowserRouter([ children: [ { path: '', - element: ( - }> - - - ), + element: , }, ], }, @@ -127,21 +104,13 @@ export const Router = createBrowserRouter([ children: [ { path: '', - element: ( - }> - - - ), + element: , }, ], }, { path: 'introduce', - element: ( - }> - - - ), + element: , }, { path: 'community', @@ -149,11 +118,7 @@ export const Router = createBrowserRouter([ children: [ { path: '', - element: ( - }> - - - ), + element: , }, ], }, @@ -163,11 +128,7 @@ export const Router = createBrowserRouter([ children: [ { path: '', - element: ( - }> - - - ), + element: , }, ], }, @@ -175,11 +136,7 @@ export const Router = createBrowserRouter([ }, { path: '/admin', - element: ( - }> - - - ), + element: , id: 'adminRoot', errorElement: , children: [ @@ -189,11 +146,7 @@ export const Router = createBrowserRouter([ children: [ { path: '', - element: ( - }> - - - ), + element: , }, ], }, @@ -204,11 +157,9 @@ export const Router = createBrowserRouter([ { path: '', element: ( - }> - - - - + + + ), }, ], @@ -219,11 +170,7 @@ export const Router = createBrowserRouter([ children: [ { path: '', - element: ( - }> - - - ), + element: , }, ], }, @@ -231,18 +178,47 @@ export const Router = createBrowserRouter([ }, { path: '/oauth/:provider/redirect', - element: ( - }> - - - ), + element: , }, { path: 'signup', - element: ( - }> - - - ), + element: , }, -]); +]; + +const createRoutesWithAsyncBoundary = ( + routes: AppRouteObject[] +): AppRouteObject[] => { + return routes.map((route) => { + const { element, children, ...rest } = route; + + if ('index' in route) { + return { + ...rest, + element: ( + }> + + {element} + + ), + } as AppRouteObject; + } else { + return { + ...rest, + element: ( + }> + + {element} + + ), + children: children + ? createRoutesWithAsyncBoundary(children) + : undefined, + } as AppRouteObject; + } + }); +}; + +export const Router = createBrowserRouter( + createRoutesWithAsyncBoundary(routesConfig) +); diff --git a/src/router/components/MetaData.tsx b/src/router/components/MetaData.tsx new file mode 100644 index 0000000..6e4a382 --- /dev/null +++ b/src/router/components/MetaData.tsx @@ -0,0 +1,128 @@ +import { useParams } from 'react-router-dom'; + +import { SEO } from '@gdsc/router/components/Seo'; + +export const MainMetaData = () => { + return ( + + ); +}; + +export const SigninMetaData = () => { + return ( + + ); +}; + +export const MypageMetaData = () => { + return ( + + ); +}; + +export const IntroduceMetaData = () => { + return ( + + ); +}; + +export const ApplyMetaData = () => { + return ( + + ); +}; + +export const ApplyFormMetaData = () => { + const { tech } = useParams(); + + return ( + + ); +}; + +export const ApplyExMetaData = () => { + const { tech } = useParams(); + + return ( + + ); +}; + +export const ApplyInquiryMetaData = () => { + return ( + + ); +}; + +export const TeamBlogMetaData = () => { + return ( + + ); +}; + +export const TeamMetaData = () => { + return ( + + ); +}; + +export const CommunityMetaData = () => { + return ( + + ); +}; diff --git a/src/utils/RouteChangeTracker.tsx b/src/router/components/RouteChangeTracker.ts similarity index 66% rename from src/utils/RouteChangeTracker.tsx rename to src/router/components/RouteChangeTracker.ts index 88bc32a..d2d1796 100644 --- a/src/utils/RouteChangeTracker.tsx +++ b/src/router/components/RouteChangeTracker.ts @@ -2,22 +2,25 @@ import { useEffect, useState } from 'react'; import ReactGA from 'react-ga4'; import { useLocation } from 'react-router-dom'; +import { trackPageView } from '@gdsc/utils/anlytics'; + const RouteChangeTracker = () => { const location = useLocation(); - const [initialized, setInitialized] = useState(false); + const [initialized, setInitialized] = useState(false); useEffect(() => { - if (!window.location.href.includes('localhost')) { - ReactGA.initialize(import.meta.env.VITE_APP_GA_TRACKING_ID); + if (import.meta.env.VITE_ENV === 'production') { + ReactGA.initialize(import.meta.env.VITE_APP_GA_TRACKING_ID as string); } setInitialized(true); }, []); + useEffect(() => { if (initialized) { - ReactGA.set({ page: location.pathname }); - ReactGA.send('pageview'); + trackPageView(location.pathname); } }, [initialized, location]); + return null; }; diff --git a/src/utils/Seo.tsx b/src/router/components/Seo.tsx similarity index 73% rename from src/utils/Seo.tsx rename to src/router/components/Seo.tsx index f3c7529..55de5f7 100644 --- a/src/utils/Seo.tsx +++ b/src/router/components/Seo.tsx @@ -16,7 +16,12 @@ export const SEO = ({ title, description, url, image }: SEOProps) => { + + + + + diff --git a/src/utils/anlytics.ts b/src/utils/anlytics.ts new file mode 100644 index 0000000..d43701c --- /dev/null +++ b/src/utils/anlytics.ts @@ -0,0 +1,35 @@ +import ReactGA from 'react-ga4'; + +export const handleClickBtn = (label: string) => { + ReactGA.event({ + category: 'Button', + action: 'Click', + label: label, + value: 1, + }); +}; + +export const handleFormSubmit = (label: string) => { + ReactGA.event({ + category: 'Form', + action: 'Submit', + label: label, + value: 1, + }); +}; + +export const handleScroll = () => { + ReactGA.event({ + category: 'Scroll', + action: 'User Scrolled', + label: 'Scrolled Down 50%', + value: 50, + }); +}; + +export const trackPageView = (path: string) => { + if (import.meta.env.VITE_ENV === 'production') { + ReactGA.set({ page: path }); + ReactGA.send('pageview'); + } +}; diff --git a/vite.config.ts b/vite.config.ts index 953b98b..104c96b 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -13,7 +13,6 @@ export default defineConfig({ '/', '/signin', '/introduce', - '/signup', '/apply', '/apply/inquiry', '/apply/frontend', @@ -26,6 +25,10 @@ export default defineConfig({ '/apply/android/form', '/apply/designer', '/apply/designer/form', + '/mypage', + '/team', + '/community', + '/techblog', ], renderer: new PuppeteerRenderer({ maxConcurrentRoutes: 1,