Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/service/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"dependencies": {
"@next/third-parties": "^15.2.4",
"@repo/pointer-design-system": "workspace:*",
"@repo/pointer-editor": "workspace:*",
"@tanstack/react-query": "^5.66.0",
"@tanstack/react-query-devtools": "^5.66.0",
"dayjs": "^1.11.13",
Expand Down
3 changes: 3 additions & 0 deletions apps/service/public/svg/ic-question-white.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion apps/service/src/apis/authMiddleware.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client';
import { Middleware } from 'openapi-fetch';

import { getAccessToken, setAccessToken, setName, setRefreshToken } from '@utils';
import { getAccessToken, setAccessToken, setGrade, setName, setRefreshToken } from '@utils';
import { postRefreshToken } from '@/apis/controller/auth';

const UNPROTECTED_ROUTES = ['/api/student/auth/social/login', '/api/common/auth/refresh'];
Expand Down Expand Up @@ -32,6 +32,7 @@ const reissueToken = async () => {
}
if (result.data?.name) {
setName(result.data.name);
setGrade(result.data.grade);
}
return accessToken;
};
Expand Down
9 changes: 8 additions & 1 deletion apps/service/src/apis/controller/home/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,12 @@ import useGetWeeklyProgress from './useGetWeeklyProgress';
import useGetNoticeUnreadCount from './useGetNoticeUnreadCount';
import useGetNotice from './useGetNotice';
import putRead from './putRead';
import useGetWeeklyPublish from './useGetWeeklyPublish';

export { useGetWeeklyProgress, useGetNoticeUnreadCount, useGetNotice, putRead };
export {
useGetWeeklyPublish,
useGetWeeklyProgress,
useGetNoticeUnreadCount,
useGetNotice,
putRead,
};
7 changes: 7 additions & 0 deletions apps/service/src/apis/controller/home/useGetWeeklyPublish.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { TanstackQueryClient } from '@apis';

const useGetWeeklyPublish = () => {
return TanstackQueryClient.useQuery('get', '/api/student/study/publish/weekly');
};

export default useGetWeeklyPublish;
14 changes: 9 additions & 5 deletions apps/service/src/app/(home)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@
import { useRouter } from 'next/navigation';

import { Button } from '@components';
import { IcCalendar } from '@svg';
import { IcCalendar, IcQuestionWhite } from '@svg';
import { trackEvent } from '@utils';
import { HomeHeader, NoticeButton, ProblemSwiper, WeekProgress } from '@/components/home';
import { useGetWeeklyPublish } from '@/apis/controller/home';
const Page = () => {
const router = useRouter();
const { data, isLoading } = useGetWeeklyPublish();
const problemSets = data?.data ?? [];

const handleClickAllProblem = () => {
trackEvent('home_all_problem_button_click');
Expand All @@ -31,21 +34,22 @@ const Page = () => {
</div>
</main>
<div className='mt-[2.4rem]'>
{/* {isLoading ? (
{isLoading ? (
<div className='h-[456px] w-full' />
) : problemSets ? (
<ProblemSwiper problemSets={problemSets} />
) : (
<></>
)} */}
)}
</div>
<footer className='bg-background mt-[2.4rem] flex flex-col gap-[1rem] px-[2rem] pb-[3.3rem]'>

<footer className='bg-background flex flex-col gap-[1rem] px-[2rem] pb-[3.3rem]'>
<Button variant='light' onClick={handleClickAllProblem}>
<IcCalendar width={24} height={24} />
날짜별로 보기
</Button>
<Button variant='blue' onClick={handleClickQnA}>
<IcCalendar width={24} height={24} />
<IcQuestionWhite width={24} height={24} />
QnA 바로가기
</Button>
</footer>
Expand Down
3 changes: 2 additions & 1 deletion apps/service/src/app/onboarding/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { useEffect, useState } from 'react';

import { Button, Header } from '@components';
import { postUserInfo } from '@/apis/controller/auth';
import { setName } from '@utils';
import { setName, setGrade } from '@utils';
import UserInfoForm from '@/components/onboarding/UserInfoForm';

const Page = () => {
Expand All @@ -26,6 +26,7 @@ const Page = () => {
if (result.isSuccess) {
if (result.data) {
setName(result.data.name);
setGrade(result.data.grade);
}
router.push('/');
} else {
Expand Down
20 changes: 20 additions & 0 deletions apps/service/src/assets/svg/IcQuestionWhite.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { SVGProps } from 'react';
import { memo } from 'react';
interface SVGRProps {
title?: string;
titleId?: string;
}
const SvgIcQuestionWhite = ({ title, titleId, ...props }: SVGProps<SVGSVGElement> & SVGRProps) => (
<svg fill='none' xmlns='http://www.w3.org/2000/svg' aria-labelledby={titleId} {...props}>
{title ? <title id={titleId}>{title}</title> : null}
<path
d='M12 1C18.0751 1 23 5.92487 23 12C23 18.0751 18.0751 23 12 23C5.92487 23 1 18.0751 1 12C1 5.92487 5.92487 1 12 1Z'
stroke='white'
strokeWidth={2}
strokeLinecap='round'
strokeLinejoin='round'
/>
</svg>
);
const Memo = memo(SvgIcQuestionWhite);
export default Memo;
1 change: 1 addition & 0 deletions apps/service/src/assets/svg/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export { default as IcPrescription20 } from './IcPrescription20';
export { default as IcPrevBlack } from './IcPrevBlack';
export { default as IcPrev } from './IcPrev';
export { default as IcQuestion18 } from './IcQuestion18';
export { default as IcQuestionWhite } from './IcQuestionWhite';
export { default as IcRight } from './IcRight';
export { default as IcSearch } from './IcSearch';
export { default as IcSecret } from './IcSecret';
Expand Down
9 changes: 5 additions & 4 deletions apps/service/src/components/home/HomeHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
import Link from 'next/link';
import { IcSetting } from '@svg';
import { LogoHeader } from '@/assets/svg/logo';
import { getName, getGrade } from '@/utils/common/auth';

const HomeHeader = () => {
const name = getName();
const grade = getGrade();
return (
<header className='bg-background fixed inset-0 z-40 mx-auto flex h-[6rem] max-w-[768px] items-center justify-between px-[2rem]'>
<Link href='/'>
<LogoHeader width={106} height={24} title='로고' titleId='logo-icon' />
</Link>
<div className='flex items-center gap-[0.8rem]'>
{/* TODO: 학년 받아오기 */}
<div className='font-medium-12 text-main bg-sub2 flex h-[2.2rem] items-center justify-center rounded-[0.4rem] px-[0.8rem]'>
2학년
{grade}학년
</div>
<div className='font-medium-14 text-black'>
{/* TODO: 이름 받아오기 */}
<>
<span className='text-main mr-[0.4rem]'>홍길동</span>님
<span className='text-main mr-[0.4rem]'>{name}</span>님
</>
</div>
<Link href='/my-page'>
Expand Down
40 changes: 12 additions & 28 deletions apps/service/src/components/home/ProblemCard/ProblemCard.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
import Image from 'next/image';
import { useRouter } from 'next/navigation';

import { Button } from '@components';
import { trackEvent } from '@utils';
import { IcSolve } from '@svg';
import ProblemViewer, { Problem } from '@repo/pointer-editor/ProblemViewer';

interface Props {
publishId: number;
dateString: string;
title: string;
image: string;
solvedCount: number;
problem: Problem;
}

const ProblemCard = ({ publishId, dateString, title, image, solvedCount }: Props) => {
const ProblemCard = ({ publishId, dateString, title, problem }: Props) => {
const router = useRouter();

const handleClickProblem = () => {
Expand All @@ -25,36 +24,21 @@ const ProblemCard = ({ publishId, dateString, title, image, solvedCount }: Props

return (
<article
className={`bg-sub2 flex h-full w-full flex-col justify-between rounded-[16px] p-[2.4rem]`}>
className={`bg-sub2 flex w-full flex-col justify-between gap-[2.4rem] rounded-[16px] p-[2.4rem]`}>
<div className='flex flex-col items-start gap-[0.8rem]'>
<p className={`font-medium-16 text-black`}>{dateString} 문제</p>
<h3 className={`font-bold-20 text-main line-clamp-2 h-[6rem]`}>{title}</h3>
<p className={`font-medium-16 text-black`}>{dateString}</p>
<h3 className={`font-bold-20 text-main line-clamp-2`}>{title}</h3>
</div>
<div className='relative h-[15.7rem] overflow-hidden rounded-[1.6rem] bg-white p-[0.8rem]'>
<Image
src={image}
alt='문제 이미지'
className='w-full object-contain object-top'
width={264}
height={157}
priority
/>

<div className='relative overflow-hidden'>
<ProblemViewer problem={problem} loading={false} />
<div
className={`from-sub2 absolute bottom-0 left-0 h-[8.9rem] w-full bg-gradient-to-t to-transparent`}
/>
</div>
<div className='flex flex-col gap-[1.2rem]'>
<p className='font-medium-12 h-[1.8rem] text-center text-black'>
<span>
<strong className='text-main'>{solvedCount}명</strong>이 문제를 풀었어요!
</span>
</p>
<Button onClick={handleClickProblem}>
<IcSolve width={24} height={24} />
문제 풀러 가기
</Button>
</div>
<Button onClick={handleClickProblem}>
<IcSolve width={24} height={24} />
문제 풀러 가기
</Button>
</article>
);
};
Expand Down
16 changes: 7 additions & 9 deletions apps/service/src/components/home/ProblemSwiper/ProblemSwiper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,21 @@ import { ProblemCard, ProblemSecretCard, ProblemEmptyCard } from '@/components/h

import './ProblemSwiper.css';

type ProblemSetHomeFeedResponse = components['schemas']['ProblemSetHomeFeedResponse'];
type ProblemSetHomeFeedResponse = components['schemas']['PublishMetaResp'];

interface ProblemSwiperProps {
problemSets: ProblemSetHomeFeedResponse[];
}

const renderProblemCard = (problem: ProblemSetHomeFeedResponse, dateString: string) => {
// 문제가 없는 경우
if (problem.publishId === null || problem.publishId === undefined) {
if (problem === null) {
return <ProblemEmptyCard dateString={dateString} />;
}

// 미래 날짜의 비공개 문제인 경우
const today = dayjs();
const date = dayjs(problem.date);
const date = dayjs(problem.publishAt);
const isSecret = date.isAfter(today);

if (isSecret) {
Expand All @@ -32,11 +32,10 @@ const renderProblemCard = (problem: ProblemSetHomeFeedResponse, dateString: stri
// 공개된 일반 문제인 경우
return (
<ProblemCard
publishId={problem.publishId}
publishId={problem.id}
dateString={dateString}
title={problem.title ?? ''}
image={problem.problemHomeFeedResponse?.mainProblemImageUrl ?? ''}
solvedCount={problem.submitCount ?? 0}
title={problem.problemSet.firstProblem.title}
problem={problem.problemSet.firstProblem.problemContent}
/>
);
};
Expand All @@ -45,7 +44,6 @@ const ProblemSwiper = ({ problemSets }: ProblemSwiperProps) => {
const dayOfWeek = dayjs().day();

const initialSlide = dayOfWeek === 0 || dayOfWeek === 6 ? 4 : dayOfWeek - 1;

return (
<Swiper
slidesPerView={'auto'}
Expand All @@ -58,7 +56,7 @@ const ProblemSwiper = ({ problemSets }: ProblemSwiperProps) => {
modules={[Pagination]}
className='mySwiper'>
{problemSets.map((problem, index) => {
const date = dayjs(problem.date);
const date = dayjs(problem.publishAt);
const dateString = date.format('MM월 DD일');

return <SwiperSlide key={index}>{renderProblemCard(problem, dateString)}</SwiperSlide>;
Expand Down
13 changes: 12 additions & 1 deletion apps/service/src/utils/common/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,16 @@ const setRefreshToken = (refreshToken: string) =>
localStorage.setItem('refreshToken', refreshToken);
const getName = () => localStorage.getItem('name');
const setName = (name: string) => localStorage.setItem('name', name);
const getGrade = () => localStorage.getItem('grade');
const setGrade = (grade: number) => localStorage.setItem('grade', grade.toString());

export { getAccessToken, setAccessToken, getRefreshToken, setRefreshToken, getName, setName };
export {
getAccessToken,
setAccessToken,
getRefreshToken,
setRefreshToken,
getName,
setName,
setGrade,
getGrade,
};
62 changes: 48 additions & 14 deletions apps/service/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
"compilerOptions": {
"composite": true,
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
Expand All @@ -20,18 +24,48 @@
}
],
"paths": {
"@apis": ["./src/apis"],
"@svg": ["./src/assets/svg"],
"@image": ["./public/image"],
"@components": ["./src/components/common"],
"@contexts": ["./src/contexts"],
"@hooks": ["./src/hooks/common"],
"@types": ["./src/types"],
"@schema": ["./src/types/api/schema.d.ts"],
"@utils": ["./src/utils/common"],
"@/*": ["./src/*"]
"@apis": [
"./src/apis"
],
"@svg": [
"./src/assets/svg"
],
"@image": [
"./public/image"
],
"@components": [
"./src/components/common"
],
"@contexts": [
"./src/contexts"
],
"@hooks": [
"./src/hooks/common"
],
"@types": [
"./src/types"
],
"@schema": [
"./src/types/api/schema.d.ts"
],
"@utils": [
"./src/utils/common"
],
"@/*": [
"./src/*"
],
"@repo/pointer-editor/*": [
"../../packages/pointer-editor"
]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts"
],
"exclude": [
"node_modules"
]
}
Loading
Loading