Skip to content

Commit d3cdcab

Browse files
committed
refactor(app): move quiz detail page into domain/quiz/views
1 parent e73acfe commit d3cdcab

File tree

14 files changed

+131
-125
lines changed

14 files changed

+131
-125
lines changed

src/app/quiz/[id]/page.js

+3-90
Original file line numberDiff line numberDiff line change
@@ -16,95 +16,8 @@
1616

1717
'use client';
1818

19-
import { useSession } from 'next-auth/react';
20-
import Image from 'next/image';
21-
import { useRouter } from 'next/navigation';
22-
import QuizBannerPic from 'public/images/quiz-banner.png';
23-
import { useState } from 'react';
24-
import useSWR from 'swr';
19+
import { QuizDetailViewWidget } from '#/domain/quiz';
2520

26-
import { Button } from '@/components/Button';
27-
import { ArrowUturnLeftIcon } from '@/components/icon/solid';
28-
import { HistoryIcon } from '@/components/Icons';
29-
import { OViewer } from '@/components/MarkDown';
30-
import { fetcher } from '@/utils/request';
31-
32-
import { CourseListViewWidget } from '#/domain/course';
33-
import { QuizLimiterWidget, RankListViewWidget } from '#/domain/quiz';
34-
import { useMediaUrl } from '#/state/application/hooks';
35-
36-
import { RankListModal } from './RankListModal';
37-
import { Record } from './Record';
38-
39-
export default function Quiz({params}) {
40-
const mediaUrl = useMediaUrl();
41-
const [openChallenge, setOpenChallenge] = useState(false);
42-
const [openRankList, setOpenRankList] = useState(false);
43-
const [checkLimit, setCheckLimit] = useState(false);
44-
const { data } = useSWR(`/ts/v1/quiz/${params.id}/index`, fetcher);
45-
const { data: coursesList } = useSWR(`v1/learn/course/opencourse?skip=0&take=2&order=default&quiz_bind_id=${params.id}`, fetcher);
46-
const { status } = useSession();
47-
const router = useRouter();
48-
49-
return (
50-
<QuizLimiterWidget
51-
id={params.id}
52-
type={data?.limit?.limit_type}
53-
check={checkLimit}
54-
quiz
55-
onReset={() => setCheckLimit(false)}
56-
>
57-
<div className="max-md:flex max-md:flex-col max-md:gap-y-4 h-[250px] md:h-[360px] bg-cover bg-center bg-no-repeat relative" style={{ backgroundImage: `url(${QuizBannerPic.src})` }}>
58-
<div className="md:absolute flex items-center mt-[15px] md:mt-6 mx-4 md:mx-14">
59-
<span
60-
onClick={() => window.history.back()}
61-
className="transition-all hidden md:flex items-center cursor-pointer text-sm opacity-80 rounded py-2 px-3 border border-gray-1100 text-black mr-2 hover:border-gray">
62-
<ArrowUturnLeftIcon className="h-4 w-4 mr-2" />Return
63-
</span>
64-
<span onClick={() => {
65-
if (status !== 'authenticated') {
66-
router.push(`/signin?from=/quiz/${params.id}`);
67-
} else {
68-
setOpenChallenge(true);
69-
}
70-
}} className="cursor-pointer transition-all flex text-sm items-center opacity-80 rounded py-2 px-3 border border-gray-1100 text-black hover:border-gray">
71-
<HistoryIcon className="mr-2" />Challenge Record
72-
</span>
73-
</div>
74-
<div className="flex items-center justify-center md:pt-9 md:pb-4">
75-
{data?.quiz_user?.user_avatar && mediaUrl && (
76-
<Image
77-
className="h-7 w-7 rounded object-cover mr-2"
78-
height={24}
79-
width={24}
80-
alt={'user_avatar'}
81-
src={mediaUrl + data?.quiz_user?.user_avatar}
82-
/>
83-
)}
84-
<p className="opacity-90 max-md:text-[18px]">by <a href={`/u/${data?.quiz_user?.user_handle}`}><strong>{data?.quiz_user?.user_nick_name}</strong></a></p>
85-
</div>
86-
<h1 className="text-[28px] md:text-[42px] leading-[32px] md:leading-[52px] text-center max-md:px-6 md:max-w-[692px] mx-auto">{data?.title}</h1>
87-
</div>
88-
<div className="max-w-[800px] mx-auto bg-white rounded-xl p-6 md:px-9 md:pt-10 md:pb-6 relative z-[2] md:top-[-155px]">
89-
<h5 className="text-lg mb-4 md:mb-3">Quiz Describe</h5>
90-
<OViewer value={data?.describe} />
91-
<Button
92-
onClick={() => setCheckLimit(true)}
93-
className="mt-4 md:mt-6 mb-9 md:mb-10 !font-bold px-[64px] !text-base max-md:w-full">
94-
Challenge now
95-
</Button>
96-
<RankListViewWidget rank={data?.my_rank} list={data?.rank}/>
97-
<p className="text-sm text-center mt-6 cursor-pointer" onClick={()=>{setOpenRankList(true);}}><strong>{data?.user_num}</strong> builders have participated</p>
98-
</div>
99-
{
100-
coursesList?.count > 0 && (
101-
<div className="max-w-[800px] max-md:mt-9 mx-6 md:mx-auto relative md:top-[-105px] max-md:pb-14">
102-
<h3 className="text-[18px] max-md:leading-[24px] md:text-lg mb-6">Related courses</h3>
103-
<CourseListViewWidget className="gap-y-6 md:gap-4 md:grid-cols-2" data={coursesList?.list} />
104-
</div>)
105-
}
106-
<RankListModal quizId={params.id} shown={openRankList} onClose={() => setOpenRankList(false)} rank={data?.my_rank}/>
107-
<Record quizId={params.id} shown={openChallenge} onClose={() => setOpenChallenge(false)} />
108-
</QuizLimiterWidget>
109-
);
21+
export default function QuizDetailPage({ params }) {
22+
return <QuizDetailViewWidget quizId={params.id} />;
11023
}

src/domain/course/views/course-detail/ChapterSection.js

-2
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@
1414
* limitations under the License.
1515
*/
1616

17-
'use client';
18-
1917
import BigNumber from 'bignumber.js';
2018
import { useRouter } from 'next/navigation';
2119
import { useMemo } from 'react';

src/domain/quiz/index.js

+1-4
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
export { fetchRankList } from './repository';
18-
1917
export { default as QuizLimiterWidget } from './widgets/quiz-limiter';
2018

2119
export { default as QuizListViewWidget } from './views/quiz-list';
22-
export { default as RankListViewWidget } from './views/rank-list';
23-
export { default as RecordListViewWidget } from './views/record-list';
20+
export { default as QuizDetailViewWidget } from './views/quiz-detail';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/**
2+
* Copyright 2024 OpenBuild
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { useSession } from 'next-auth/react';
18+
import Image from 'next/image';
19+
import { useRouter } from 'next/navigation';
20+
import QuizBannerPic from 'public/images/quiz-banner.png';
21+
import { useState } from 'react';
22+
import useSWR from 'swr';
23+
24+
import { Button } from '@/components/Button';
25+
import { ArrowUturnLeftIcon } from '@/components/icon/solid';
26+
import { HistoryIcon } from '@/components/Icons';
27+
import { OViewer } from '@/components/MarkDown';
28+
import { fetcher } from '@/utils/request';
29+
30+
import { useMediaUrl } from '#/state/application/hooks';
31+
32+
import { CourseListViewWidget } from '../../../course';
33+
import QuizLimiterWidget from '../../widgets/quiz-limiter';
34+
import RankList from './RankList';
35+
import RankListModal from './RankListModal';
36+
import RecordListModal from './RecordListModal';
37+
38+
function QuizDetailView({ quizId }) {
39+
const mediaUrl = useMediaUrl();
40+
const [openChallenge, setOpenChallenge] = useState(false);
41+
const [openRankList, setOpenRankList] = useState(false);
42+
const [checkLimit, setCheckLimit] = useState(false);
43+
const { data } = useSWR(`/ts/v1/quiz/${quizId}/index`, fetcher);
44+
const { data: coursesList } = useSWR(`v1/learn/course/opencourse?skip=0&take=2&order=default&quiz_bind_id=${quizId}`, fetcher);
45+
const { status } = useSession();
46+
const router = useRouter();
47+
48+
return (
49+
<QuizLimiterWidget
50+
id={quizId}
51+
type={data?.limit?.limit_type}
52+
check={checkLimit}
53+
quiz
54+
onReset={() => setCheckLimit(false)}
55+
>
56+
<div className="max-md:flex max-md:flex-col max-md:gap-y-4 h-[250px] md:h-[360px] bg-cover bg-center bg-no-repeat relative" style={{ backgroundImage: `url(${QuizBannerPic.src})` }}>
57+
<div className="md:absolute flex items-center mt-[15px] md:mt-6 mx-4 md:mx-14">
58+
<span
59+
onClick={() => window.history.back()}
60+
className="transition-all hidden md:flex items-center cursor-pointer text-sm opacity-80 rounded py-2 px-3 border border-gray-1100 text-black mr-2 hover:border-gray">
61+
<ArrowUturnLeftIcon className="h-4 w-4 mr-2" />Return
62+
</span>
63+
<span onClick={() => {
64+
if (status !== 'authenticated') {
65+
router.push(`/signin?from=/quiz/${quizId}`);
66+
} else {
67+
setOpenChallenge(true);
68+
}
69+
}} className="cursor-pointer transition-all flex text-sm items-center opacity-80 rounded py-2 px-3 border border-gray-1100 text-black hover:border-gray">
70+
<HistoryIcon className="mr-2" />Challenge Record
71+
</span>
72+
</div>
73+
<div className="flex items-center justify-center md:pt-9 md:pb-4">
74+
{data?.quiz_user?.user_avatar && mediaUrl && (
75+
<Image
76+
className="h-7 w-7 rounded object-cover mr-2"
77+
height={24}
78+
width={24}
79+
alt={'user_avatar'}
80+
src={mediaUrl + data?.quiz_user?.user_avatar}
81+
/>
82+
)}
83+
<p className="opacity-90 max-md:text-[18px]">by <a href={`/u/${data?.quiz_user?.user_handle}`}><strong>{data?.quiz_user?.user_nick_name}</strong></a></p>
84+
</div>
85+
<h1 className="text-[28px] md:text-[42px] leading-[32px] md:leading-[52px] text-center max-md:px-6 md:max-w-[692px] mx-auto">{data?.title}</h1>
86+
</div>
87+
<div className="max-w-[800px] mx-auto bg-white rounded-xl p-6 md:px-9 md:pt-10 md:pb-6 relative z-[2] md:top-[-155px]">
88+
<h5 className="text-lg mb-4 md:mb-3">Quiz Describe</h5>
89+
<OViewer value={data?.describe} />
90+
<Button
91+
onClick={() => setCheckLimit(true)}
92+
className="mt-4 md:mt-6 mb-9 md:mb-10 !font-bold px-[64px] !text-base max-md:w-full">
93+
Challenge now
94+
</Button>
95+
<RankList rank={data?.my_rank} list={data?.rank}/>
96+
<p className="text-sm text-center mt-6 cursor-pointer" onClick={()=>{setOpenRankList(true);}}><strong>{data?.user_num}</strong> builders have participated</p>
97+
</div>
98+
{
99+
coursesList?.count > 0 && (
100+
<div className="max-w-[800px] max-md:mt-9 mx-6 md:mx-auto relative md:top-[-105px] max-md:pb-14">
101+
<h3 className="text-[18px] max-md:leading-[24px] md:text-lg mb-6">Related courses</h3>
102+
<CourseListViewWidget className="gap-y-6 md:gap-4 md:grid-cols-2" data={coursesList?.list} />
103+
</div>)
104+
}
105+
<RankListModal quizId={quizId} shown={openRankList} onClose={() => setOpenRankList(false)} rank={data?.my_rank}/>
106+
<RecordListModal quizId={quizId} shown={openChallenge} onClose={() => setOpenChallenge(false)} />
107+
</QuizLimiterWidget>
108+
);
109+
}
110+
111+
export default QuizDetailView;

src/domain/quiz/views/rank-list/RankList.js src/domain/quiz/views/quiz-detail/RankList.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import Rank1Icon from './rank-1.svg';
2222
import Rank2Icon from './rank-2.svg';
2323
import Rank3Icon from './rank-3.svg';
2424

25-
export default function RankListView({ rank, list }) {
25+
export default function RankList({ rank, list }) {
2626
const mediaUrl = useMediaUrl();
2727

2828
return (

src/app/quiz/[id]/RankListModal.js src/domain/quiz/views/quiz-detail/RankListModal.js

+6-3
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@ import Loader from '@/components/Loader';
2121
import { Modal } from '@/components/Modal';
2222
import useMounted from '@/hooks/useMounted';
2323

24-
import { fetchRankList, RankListViewWidget } from '#/domain/quiz';
24+
import { fetchRankList } from '../../repository';
25+
import RankList from './RankList';
2526

26-
export function RankListModal({ quizId, shown, onClose, rank }) {
27+
function RankListModal({ quizId, shown, onClose, rank }) {
2728
const [data, setData] = useState(null);
2829
const [loading, setLoading] = useState(true);
2930

@@ -46,8 +47,10 @@ export function RankListModal({ quizId, shown, onClose, rank }) {
4647
/>
4748
<div className="md:h-[600px] h-[400px] flex flex-col overflow-y-auto rounded-inherit overflow-hidden">
4849
{loading && <Loader classname="mr-2" />}
49-
<RankListViewWidget rank={rank} list={data} />
50+
<RankList rank={rank} list={data} />
5051
</div>
5152
</Modal>
5253
);
5354
}
55+
56+
export default RankListModal;

src/domain/quiz/views/record-list/RecordList.js src/domain/quiz/views/quiz-detail/RecordList.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import { formatTime, fromUtcOffset, formatTimeMeridiem } from '@/utils/date';
2121

2222
import { useMediaUrl } from '#/state/application/hooks';
2323

24-
function RecordListView({ data = [] }) {
24+
function RecordList({ data = [] }) {
2525
const mediaUrl = useMediaUrl();
2626

2727
return (
@@ -76,4 +76,4 @@ function RecordListView({ data = [] }) {
7676
);
7777
}
7878

79-
export default RecordListView;
79+
export default RecordList;

src/app/quiz/[id]/Record.js src/domain/quiz/views/quiz-detail/RecordListModal.js

+5-4
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,12 @@ import useSWR from 'swr';
1818

1919
import { ModalCloseIcon } from '@/components/Icons';
2020
import { Modal } from '@/components/Modal';
21-
// import clsx from 'clsx'
2221
import { NoData } from '@/components/NoData';
2322
import { fetcher } from '@/utils/request';
2423

25-
import { RecordListViewWidget } from '#/domain/quiz';
24+
import RecordList from './RecordList';
2625

27-
export function Record({quizId, shown, onClose}) {
26+
function RecordListModal({quizId, shown, onClose}) {
2827
const { data } = useSWR(shown ? `/ts/v1/quiz/${quizId}/answer` : null, fetcher);
2928

3029
return (
@@ -35,10 +34,12 @@ export function Record({quizId, shown, onClose}) {
3534
<h3 className="text-center py-4 border-b border-gray-600">
3635
Challenge Record
3736
</h3>
38-
<RecordListViewWidget data={data} />
37+
<RecordList data={data} />
3938
{(!data|| data?.length === 0) && <div className="pb-12"><NoData /></div>}
4039
</div>
4140
</div>
4241
</Modal>
4342
);
4443
}
44+
45+
export default RecordListModal;

src/domain/quiz/views/rank-list/index.js src/domain/quiz/views/quiz-detail/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@
1414
* limitations under the License.
1515
*/
1616

17-
export { default } from './RankList';
17+
export { default } from './QuizDetail';

src/domain/quiz/views/record-list/index.js

-17
This file was deleted.

src/entry/pages/quiz-list/QuizListPage.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ import QuizBannerPic from 'public/images/quiz-banner.svg';
2020
import { useState } from 'react';
2121

2222
import { ArrowRightIcon } from '@/components/Icons';
23+
import useMounted from '@/hooks/useMounted';
2324

2425
import { QuizListViewWidget } from '#/domain/quiz';
25-
import useMounted from '#/shared/hooks/useMounted';
2626

2727
import StartOnOpenBuild from '../../components/StartOnOpenBuild';
2828
import QuizS1 from './quiz-s-1.svg';

0 commit comments

Comments
 (0)