Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Quiz Module:Modify Directory Structure and Optimize requests #143

Merged
merged 8 commits into from
Feb 24, 2025
Merged
Show file tree
Hide file tree
Changes from 3 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
54 changes: 0 additions & 54 deletions src/app/quiz/Search.js

This file was deleted.

96 changes: 1 addition & 95 deletions src/app/quiz/[id]/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,98 +16,4 @@

'use client';

import { useSession } from 'next-auth/react';
import Image from 'next/image';
import { useRouter } from 'next/navigation';
import QuizBannerPic from 'public/images/quiz-banner.png';
import { useState } from 'react';
import useSWR from 'swr';

import { Button } from '@/components/Button';
import { ArrowUturnLeftIcon } from '@/components/icon/solid';
import { HistoryIcon } from '@/components/Icons';
import { OViewer } from '@/components/MarkDown';
import { fetcher } from '@/utils/request';

import QuizLimiterWidget from '#/domain/quiz/widgets/quiz-limiter';
import { useMediaUrl } from '#/state/application/hooks';

import { CourseCard } from '../../learn/[type]/CourseCard';
import RankList from './RankList';
import { RankListModal } from './RankListModal';
import { Record } from './Record';

export default function Quiz({params}) {
const mediaUrl = useMediaUrl();
const [openChallenge, setOpenChallenge] = useState(false);
const [openRankList, setOpenRankList] = useState(false);
const [checkLimit, setCheckLimit] = useState(false);
const { data } = useSWR(`/ts/v1/quiz/${params.id}/index`, fetcher);
const { data: coursesList } = useSWR(`v1/learn/course/opencourse?skip=0&take=2&order=default&quiz_bind_id=${params.id}`, fetcher);
const { status } = useSession();
const router = useRouter();

return (
<QuizLimiterWidget
id={params.id}
type={data?.limit?.limit_type}
check={checkLimit}
quiz
onReset={() => setCheckLimit(false)}
>
<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})` }}>
<div className="md:absolute flex items-center mt-[15px] md:mt-6 mx-4 md:mx-14">
<span
onClick={() => window.history.back()}
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">
<ArrowUturnLeftIcon className="h-4 w-4 mr-2" />Return
</span>
<span onClick={() => {
if (status !== 'authenticated') {
router.push(`/signin?from=/quiz/${params.id}`);
} else {
setOpenChallenge(true);
}
}} 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">
<HistoryIcon className="mr-2" />Challenge Record
</span>
</div>
<div className="flex items-center justify-center md:pt-9 md:pb-4">
{data?.quiz_user?.user_avatar && mediaUrl && (
<Image
className="h-7 w-7 rounded object-cover mr-2"
height={24}
width={24}
alt={'user_avatar'}
src={mediaUrl + data?.quiz_user?.user_avatar}
/>
)}
<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>
</div>
<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>
</div>
<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]">
<h5 className="text-lg mb-4 md:mb-3">Quiz Describe</h5>
<OViewer value={data?.describe} />
<Button
onClick={() => setCheckLimit(true)}
className="mt-4 md:mt-6 mb-9 md:mb-10 !font-bold px-[64px] !text-base max-md:w-full">
Challenge now
</Button>
<RankList rank={data?.my_rank} list={data?.rank}/>
<p className="text-sm text-center mt-6 cursor-pointer" onClick={()=>{setOpenRankList(true);}}><strong>{data?.user_num}</strong> builders have participated</p>
</div>
{
coursesList?.count > 0 && (
<div className="max-w-[800px] max-md:mt-9 mx-6 md:mx-auto relative md:top-[-105px] max-md:pb-14">
<h3 className="text-[18px] max-md:leading-[24px] md:text-lg mb-6">Related courses</h3>
<div className="grid gap-y-6 md:gap-4 md:grid-cols-2">
{coursesList?.list?.map(i => <CourseCard data={i} key={`open-courses-${i.base.course_series_id}`} />)}
</div>
</div>)
}
<RankListModal quizId={params.id} shown={openRankList} onClose={() => setOpenRankList(false)} rank={data?.my_rank}/>
<Record quizId={params.id} shown={openChallenge} onClose={() => setOpenChallenge(false)} />
</QuizLimiterWidget>
);
}
export { default } from '#/domain/quiz/views/quiz-detail';
2 changes: 1 addition & 1 deletion src/app/quiz/[id]/questions/[version]/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

import QuizQuestionListView from '#/domain/quiz/views/quiz-question-list';

export default function QuizDetails({ params }) {
export default function QuizVersion({ params }) {
return (
<QuizQuestionListView id={params.id} version={params.version} />
);
Expand Down
2 changes: 1 addition & 1 deletion src/app/quiz/[id]/questions/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

import QuizQuestionListView from '#/domain/quiz/views/quiz-question-list';

export default function QuizDetails({ params }) {
export default function QuizQuestions({ params }) {
return (
<QuizQuestionListView id={params.id} />
);
Expand Down
149 changes: 1 addition & 148 deletions src/app/quiz/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,151 +16,4 @@

'use client';

import Aos from 'aos';
import Image from 'next/image';
import QuizBannerPic from 'public/images/quiz-banner.svg';
import QuizS1 from 'public/images/quiz-s-1.svg';
import QuizS2 from 'public/images/quiz-s-2.svg';
import QuizS3 from 'public/images/quiz-s-3.svg';
import { useEffect,useState } from 'react';

import { ArrowRightIcon } from '@/components/Icons';

import { StartOnOpenBuild } from '../learn/StartOnOpenBuild';
import { QuizList } from './List';

const Steps = [
{
name: 'Learn Courses',
icon: QuizS1,
},
{
name: 'Complete Quizzes',
icon: QuizS2,
},
{
name: 'Earn Prizes',
icon: QuizS3,
},
];

export default function Quiz() {
const [activeStep, setActiveStep] = useState(0);

useEffect(() => {
Aos.init({
delay: 100, // values from 0 to 3000, with step 50ms
duration: 800, // values from 0 to 3000, with step 50ms
});
}, []);
return (
<div>
<div className="md:flex bg-white md:justify-center pb-14">
<div className="md:w-[1200px] md:flex md:justify-between md:pr-[120px] py-6" data-aos="fade-left">
<div className="md:pt-[58px] md:max-w-[420px]" data-aos="fade-right">
<h1 className="text-[32px] md:text-[48px] leading-[42px] md:leading-[64px] max-md:text-center mb-3 md:mb-4">Learn & Earn</h1>
<p className="max-md:px-6 mx-auto max-md:mb-6 max-md:text-center text-[20px] md:text-[22px] leading-[26px] md:leading-8 font-normal">Discover cutting-edge protocols and ecosystems, <strong>earn prizes</strong> and <strong>thrive</strong> in the crypto space!</p>
</div>
<Image className="max-md:w-[360px] max-md:mx-auto" src={QuizBannerPic} alt="" />
</div>
</div>
<div data-aos="fade-bottom" className="w-[1200px] h-[110px] mx-auto shadow-[0_4px_24px_rgba(0,0,0,0.08)] bg-white !translate-y-[-50%] rounded-xl hidden md:flex justify-center items-center gap-[96px]">
{Steps.map((i, k) => (
<div key={`quiz-step-${k}`} className="flex gap-[96px] items-center">
<div className="flex">
<Image src={i.icon} alt="" />
<div className="ml-4">
<p>Step {k + 1}</p>
<h3>{i.name}</h3>
</div>
</div>
{k < Steps.length - 1 && <ArrowRightIcon className="w-6 h-6" />}
</div>
))}
</div>
{/* mobile steps */}
<div className="md:hidden h-[110px] mx-6 py-6 px-4 shadow-[0_4px_24px_rgba(0,0,0,0.08)] bg-white !translate-y-[-50%] rounded-xl">
{Steps.map((i, k) => {
const isNotLast = k < Steps.length - 1;
const isFirst = k === 0;
return (
<div key={`quiz-step-${k}`} className={`${activeStep === k ? 'flex' : 'hidden'} gap-[20px] items-center`}>
<ArrowRightIcon
className={`w-5 h-5 rotate-180 flex-shrink-0 ${isFirst ? 'opacity-[.2]':''}`}
onClick={() => {
if(k > 0){
setActiveStep(k-1);
}
}}
/>
<div className="flex flex-1">
<Image src={i.icon} alt="" className="h-[60px]"/>
<div className="ml-4">
<p>Step {k + 1}</p>
<h3>{i.name}</h3>
</div>
</div>
<ArrowRightIcon
className={`w-5 h-5 flex-shrink-0 ${isNotLast ? '' : 'opacity-[.2]'}`}
onClick={()=>{
if(isNotLast){
setActiveStep(k+1);
}
}}
/>
</div>
);
})}
</div>
<div className="md:w-[1200px] mx-6 md:mx-auto mb-14 md:mb-[110px]">
<QuizList />
<div className="md:flex mt-14">
<h3 className="md:flex-1 text-[24px] md:text-[32px] max-md:leading-10 max-md:pb-4">Quiz FAQ</h3>
<div className="md:w-[640px]">
<div className="collapse rounded-none collapse-plus border-t border-gray-1100">
<input type="radio" name="my-accordion-3" defaultChecked />
<div className="collapse-title font-bold h-[80px] flex items-center text-xl">
Who is able to participate in Quiz?
</div>
<div className="collapse-content pl-0 opacity-80">
<p>Anyone can participate in Quiz, and to claim the prize, users must connect their Github account first.</p>
</div>
</div>
<div className="collapse rounded-none collapse-plus border-t border-gray-1100">
<input type="radio" name="my-accordion-3" />
<div className="collapse-title font-bold h-[80px] flex items-center text-xl">
How do I participate in Quiz?
</div>
<div className="collapse-content pl-0 opacity-80">
<p>1. Enroll in a relative course on the certain Quiz page</p>
<p>2. Learn and complete the Quiz</p>
<p>3. Submit your address for the prize</p>
<p>4. Receive your prize if qualified</p>
</div>
</div>
<div className="collapse rounded-none collapse-plus border-t border-gray-1100">
<input type="radio" name="my-accordion-3" />
<div className="collapse-title font-bold h-[80px] flex items-center text-xl">
What are the prizes for Quiz?
</div>
<div className="collapse-content pl-0 opacity-80">
<p>The prizes can be tokens, points, NFTs, souvenirs, etc, and its value is estimated.</p>
</div>
</div>
<div className="collapse rounded-none collapse-plus border-t border-b border-gray-1100">
<input type="radio" name="my-accordion-3" />
<div className="collapse-title font-bold h-[80px] flex items-center text-xl">
Can I take the Quiz if it has ended?
</div>
<div className="collapse-content pl-0 opacity-80">
<p>You can always take the Quiz whenever you want. However, the prize is available only when the quiz status is Ongoing. </p>
</div>
</div>

</div>
</div>
</div>
<StartOnOpenBuild />
</div>
);
}
export { default } from '#/domain/quiz/views/quiz';
35 changes: 35 additions & 0 deletions src/domain/learn/widgets/card-progress/CardProgress.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* Copyright 2024 OpenBuild
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { ArrowRightIcon } from '@/components/Icons';
import { ProgressBar } from '@/components/ProgressBar';

export default function CardProgress({ value }) {
return (
<div className="flex items-center justify-between p-6">
<div>
<h6 className="mb-3 text-sm font-bold">
Progress
<span className="ml-2 font-normal">{value}%</span>
</h6>
<div className="min-w-[160px]">
<ProgressBar progress={value} />
</div>
</div>
<ArrowRightIcon className="h-3 w-[18px]" />
</div>
);
}
17 changes: 17 additions & 0 deletions src/domain/learn/widgets/card-progress/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* Copyright 2024 OpenBuild
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

export { default } from './CardProgress';
Loading
Loading