Skip to content

Commit

Permalink
Merge pull request #100 from EveryUniv/fix/95(백수연)
Browse files Browse the repository at this point in the history
fix: ID, 비밀번호 찾기 이슈 및 Suspense 추가
  • Loading branch information
gaeunnlee authored Apr 29, 2024
2 parents 2695758 + b2f528f commit c1af6d7
Show file tree
Hide file tree
Showing 69 changed files with 1,427 additions and 1,223 deletions.
29 changes: 23 additions & 6 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,45 @@
import ErrorBoundary from '@components/common/errorBoundary';
import Error from '@components/errorFallback';
import ModalProvider from '@components/ui/modal/modal-provider';
import { Spinner } from '@components/ui/spinner/indext';
import { Toaster } from '@components/ui/toast/toaster';
import { useResetError } from '@hooks/useResetErrorBoundary';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import React from 'react';
import React, { Suspense, lazy } from 'react';
import { BrowserRouter } from 'react-router-dom';

import Router from './Router';
import Router from '@/Router';
const DefaultLayout = lazy(() => import('@components/layouts/DefaultLayout'));


const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: 0,
refetchOnWindowFocus: false,
throwOnError: true,
throwOnError: true,
},
},
});

export default function App() {
const App = () => {
const { handleErrorReset } = useResetError();
return (
<QueryClientProvider client={queryClient}>
<ModalProvider>
<Toaster />
<BrowserRouter>
<Router />
<ErrorBoundary onReset={handleErrorReset} Fallback={Error}>
<Suspense fallback={<Spinner />}>
<DefaultLayout>
<Router />
</DefaultLayout>
</Suspense>
</ErrorBoundary>
</BrowserRouter>
</ModalProvider>
</QueryClientProvider>
);
}
};

export default App;
83 changes: 47 additions & 36 deletions src/Router.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import DefaultLayout from '@components/layouts/DefaultLayout';
import MainSkeleton from '@components/ui/skeleton/main';
import { ROUTES } from '@constants/route';
import NotFound from '@pages/404';
import BusinessBoard from '@pages/business';
Expand Down Expand Up @@ -29,44 +29,55 @@ import SignupVerify from '@pages/signup';
import SignupInfo from '@pages/signup/info';
import SignupSuccess from '@pages/signup/success';
import SignupTerms from '@pages/signup/terms';
import React from 'react';
import React, { Suspense } from 'react';
import { Routes, Route } from 'react-router-dom';


export default function Router() {
return (
<DefaultLayout>
<Routes>
<Route path={ROUTES.MAIN} element={<Main />} />
<Route path={ROUTES.LOGIN} element={<Login />} />
<Route path={ROUTES.RESET.INDEX} element={<ResetIdPw />} />
<Route path={ROUTES.RESET.ID} element={<ResetId />} />
<Route path={ROUTES.RESET.PW_VERIFY} element={<VerifyPw />} />
<Route path={ROUTES.RESET.PW} element={<ResetPw />} />
<Route path={ROUTES.MYPAGE.INDEX} element={<MyPage />} />
<Route path={ROUTES.MYPAGE.PASSWORD} element={<MyPagePassword />} />
<Route path={ROUTES.MYPAGE.EDIT} element={<MyPageEdit />} />
<Route path={ROUTES.MYPAGE.UPDATE} element={<MyPageUpdate />} />
<Route index path={ROUTES.SIGNUP.ROOT} element={<SignupVerify />} />
<Route path={ROUTES.SIGNUP.TERMS} element={<SignupTerms />} />
<Route path={ROUTES.SIGNUP.INFO} element={<SignupInfo />} />
<Route path={ROUTES.SIGNUP.SUCCESS} element={<SignupSuccess />} />
<Route path={ROUTES.COUNCIL.GREETING} element={<Greeting />} />
<Route path={ROUTES.COUNCIL.ORGANIZATION} element={<Organization />} />
<Route path={ROUTES.COUNCIL.LOCATION} element={<Location />} />
<Route path={ROUTES.COUNCIL.RECRUITMENT} element={<Recruitment />} />
<Route path={ROUTES.PETITION.ROOT} element={<PetitionBoard />} />
<Route path={ROUTES.PETITION.ID(':id')} element={<PetitionDetail />} />
<Route path={ROUTES.NOTICE.ROOT} element={<NoticeBoard />} />
<Route path={ROUTES.NOTICE.ID(':id')} element={<NoticeDetail />} />
<Route path={ROUTES.PETITION.POST} element={<PetitionForm />} />
<Route path={ROUTES.NOT_FOUND} element={<NotFound />} />
<Route path={ROUTES.NOTICE.POST} element={<NoticePost />} />
<Route path={ROUTES.CONFERENCE.ROOT} element={<ConferenceBoard />} />
<Route path={ROUTES.RULE.ROOT} element={<RuleBoard />} />
<Route path={ROUTES.BUSINESS.CATEGORY(':category')} element={<BusinessBoard />} />
<Route path={ROUTES.BUSINESS.DETAIL(':id', ':category')} element={<BusinessDetail />} />
</Routes>
</DefaultLayout>
<Routes>
<Route
path={ROUTES.MAIN}
element={
<Suspense fallback={<MainSkeleton />}>
<Main />
</Suspense>
}
/>
<Route path={ROUTES.LOGIN} element={<Login />} />
{/* 아이디 및 비밀번호 재설정 */}
<Route path={ROUTES.RESET.INDEX} element={<ResetIdPw />} />
<Route path={ROUTES.RESET.ID} element={<ResetId />} />
<Route path={ROUTES.RESET.PW_VERIFY} element={<VerifyPw />} />
<Route path={ROUTES.RESET.PW} element={<ResetPw />} />
{/* 마이페이지 설정 */}
<Route path={ROUTES.MYPAGE.INDEX} element={<MyPage />} />
<Route path={ROUTES.MYPAGE.PASSWORD} element={<MyPagePassword />} />
<Route path={ROUTES.MYPAGE.EDIT} element={<MyPageEdit />} />
<Route path={ROUTES.MYPAGE.UPDATE} element={<MyPageUpdate />} />
{/* 회원가입 */}
<Route index path={ROUTES.SIGNUP.ROOT} element={<SignupVerify />} />
<Route path={ROUTES.SIGNUP.TERMS} element={<SignupTerms />} />
<Route path={ROUTES.SIGNUP.INFO} element={<SignupInfo />} />
<Route path={ROUTES.SIGNUP.SUCCESS} element={<SignupSuccess />} />
{/* 총학생회 */}
<Route path={ROUTES.COUNCIL.GREETING} element={<Greeting />} />
<Route path={ROUTES.COUNCIL.ORGANIZATION} element={<Organization />} />
<Route path={ROUTES.COUNCIL.LOCATION} element={<Location />} />
<Route path={ROUTES.COUNCIL.RECRUITMENT} element={<Recruitment />} />
<Route path={ROUTES.CONFERENCE.ROOT} element={<ConferenceBoard />} />
<Route path={ROUTES.RULE.ROOT} element={<RuleBoard />} />
{/* 청원 */}
<Route path={ROUTES.PETITION.ROOT} element={<PetitionBoard />} />
<Route path={ROUTES.PETITION.ID(':id')} element={<PetitionDetail />} />
<Route path={ROUTES.PETITION.POST} element={<PetitionForm />} />
{/* 총학공지 */}
<Route path={ROUTES.NOTICE.ROOT} element={<NoticeBoard />} />
<Route path={ROUTES.NOTICE.ID(':id')} element={<NoticeDetail />} />
<Route path={ROUTES.NOTICE.POST} element={<NoticePost />} />
<Route path={ROUTES.NOT_FOUND} element={<NotFound />} />
{/* 제휴 */}
<Route path={ROUTES.BUSINESS.CATEGORY(':category')} element={<BusinessBoard />} />
<Route path={ROUTES.BUSINESS.DETAIL(':id', ':category')} element={<BusinessDetail />} />
</Routes>
);
}
48 changes: 48 additions & 0 deletions src/components/business/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import Board from '@components/ui/board';
import IntersectionBox from '@components/ui/box/intersectionBox';
import ItemList from '@components/ui/item-list';
import { Spinner } from '@components/ui/spinner/indext';
import { ROUTES } from '@constants/route';
import { CoalitionContentResponse, useGetCoalitionList } from '@hooks/api/coalition/useGetCoalitionList';
import { useInfiniteScroll } from '@hooks/useInfiniteScroll';
import React, { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';

import { CoalitionType } from '@/types/coalition';

export default function BusinessList({categoryType}: {categoryType: string}) {
const navigate = useNavigate();

const {
data: coalition,
refetch,
fetchNextPage,
isFetchingNextPage,
} = useGetCoalitionList(categoryType as CoalitionType);
const intersectionRef = useInfiniteScroll(fetchNextPage);

useEffect(() => {
refetch();
}, [categoryType, refetch]);


const goToBusinessDetail = (item: CoalitionContentResponse) => {
navigate(ROUTES.BUSINESS.DETAIL(categoryType.toLowerCase() as string, item.id.toString()), {
state: item,
});
};

return (
<Board>
{coalition?.pages.map((page) =>
page.content.map((item) => (
<Board.Cell key={item.id} onClick={() => goToBusinessDetail(item)}>
<ItemList content={item} />
</Board.Cell>
)),
)}
<IntersectionBox ref={intersectionRef} />
{isFetchingNextPage && <Spinner />}
</Board>
);
}
2 changes: 1 addition & 1 deletion src/components/common/carousel/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export default function Carousel({ data, className }: CarouselProps) {
</div>
<div className='absolute bottom-0 h-[30px] text-white flex gap-2'>
{data.map((_, index) => (
<GoDotFill key={index} className={index === currentIndex ? '' : 'text-slate-300'} />
<GoDotFill key={index} className={index === currentIndex ? 'text-black' : 'text-slate-300'} />
))}
</div>
{data.map((item, index) => (
Expand Down
30 changes: 18 additions & 12 deletions src/components/common/gnb/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,45 @@ import React from 'react';
import { Link, useNavigate } from 'react-router-dom';

interface Props extends React.ComponentProps<'header'> {
left?: JSX.Element | null;
center?: JSX.Element | null;
children: React.ReactNode;
}

export default function Gnb({ left, center, ...props }: Props) {
const Gnb = ({ children, ...props }: Props) => {
return (
<header
className={`w-[390px] flex mx-auto px-[22px] py-1.5 h-[50px] items-center ${
typeof props.className !== 'undefined' ? props.className : ''
}`}
{...props}
>
<div className='w-full flex items-center'>
{left && left}
{center && center}
</div>
<div className='w-full flex items-center'>{children}</div>
</header>
);
}
};

Gnb.Logo = function Logo() {
const GnbLogo = () => {
return (
<Link to={ROUTES.MAIN}>
<img src={logo} alt='단국대학교 로고' />
</Link>
);
};

Gnb.GoBack = function GoBack() {
const GnbGoBack = ({ url }: { url?: string }) => {
const navigate = useNavigate();
return <IconButton id='arrow_back' width={18} height={22} color='white' onClick={() => navigate(-1)} />;
return (
<IconButton
id='arrow_back'
width={18}
height={22}
color='white'
onClick={() => (url ? navigate(url) : navigate(-1))}
/>
);
};

Gnb.Title = function Title({ children }: { children: string }) {
const GnbTitle = ({ children }: { children: string }) => {
return <h1 className='font-semibold mx-auto text-white text-xs'>{children}</h1>;
};

export { Gnb, GnbLogo, GnbGoBack, GnbTitle };
33 changes: 14 additions & 19 deletions src/components/common/gnh/index.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
import Selector, { TOption } from '@components/ui/selector';
import React from 'react';

interface GnhProps {
headingText: string;
subHeadingText?: string;
subHeadingStyle: string;
headingStyle: string;
dropDown?: TOption[];
}
const GnhTitle = ({ children, className }: React.ComponentProps<'h1'>) => {
return (
<h1 className={`${className ?? ''} text-2xl font-extrabold text-white`}>{children}</h1>
);
};

const GnhSubtitle = ({ children, className }: React.ComponentProps<'h2'>) => {
return (
<h2 className={`${className ?? ''} text-white`}>{children}</h2>
);
};


export { GnhTitle, GnhSubtitle };

const Gnh = ({ headingText, subHeadingText, headingStyle, subHeadingStyle, dropDown }: GnhProps) => (
<React.Fragment>
{headingText && <h1 className={`${headingStyle} text-2xl font-extrabold text-white`}>{headingText}</h1>}
{dropDown !== undefined && dropDown.length > 0 && subHeadingText ? (
<Selector list={dropDown} subHeadingText={subHeadingText} />
) : (
<h2 className={`${subHeadingStyle} text-white`}>{subHeadingText}</h2>
)}
</React.Fragment>
);
export default Gnh;
33 changes: 33 additions & 0 deletions src/components/conference/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import Board from '@components/ui/board';
import IntersectionBox from '@components/ui/box/intersectionBox';
import { Spinner } from '@components/ui/spinner/indext';
import { Date } from '@components/ui/text/board';
import { useGetConference } from '@hooks/api/conference/useGetConference';
import { ConferenceContentResponse } from '@hooks/api/conference/useGetConference';
import { useInfiniteScroll } from '@hooks/useInfiniteScroll';
import React from 'react';

export default function ConferenceList() {
const { data: conference, fetchNextPage, isFetchingNextPage } = useGetConference();
const intersectionRef = useInfiniteScroll(fetchNextPage);

const openFile = (item: ConferenceContentResponse) => {
window.open(item.files[0].url);
};
return (
<Board>
{conference?.pages.map((page) =>
page.content.map((item) => (
<Board.Cell key={item.id} onClick={() => openFile(item)}>
<div className='flex gap-2 p-3'>
<p className='grow text-center truncate'>{item.title}</p>
<Date date={item.createdAt} />
</div>
</Board.Cell>
)),
)}
<IntersectionBox ref={intersectionRef} />
{isFetchingNextPage && <Spinner />}
</Board>
);
}
23 changes: 23 additions & 0 deletions src/components/layouts/ContentSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import Nav from '@components/common/nav';
import { AnimatePresence } from 'framer-motion';
import React from 'react';

interface ContentSectionProps extends React.ComponentProps<'div'> {
showNav?: boolean;
}

const ContentSection = ({ children, className, showNav, ...props }: ContentSectionProps) => {
return (
<div className={`rounded-t-3xl pt-4 flex flex-col bg-white ${className ?? ''}
${showNav ? 'mb-20' : ''}`} {...props}>
{children}
{showNav && (
<AnimatePresence>
<Nav />
</AnimatePresence>
)}
</div>
);
};

export default ContentSection;
Loading

0 comments on commit c1af6d7

Please sign in to comment.