feat: question page confetti

pull/4442/head
Kamran Ahmed 1 year ago
parent 4cb905b69a
commit 2a7fd53c8b
  1. 2
      package.json
  2. 40
      pnpm-lock.yaml
  3. 83
      src/components/Questions/QuestionsList.tsx
  4. 37
      src/components/Questions/QuestionsProgress.tsx
  5. 70
      src/pages/questions/[questionGroupId].astro

@ -32,10 +32,12 @@
"astro-compress": "^2.0.8", "astro-compress": "^2.0.8",
"jose": "^4.14.4", "jose": "^4.14.4",
"js-cookie": "^3.0.5", "js-cookie": "^3.0.5",
"lucide-react": "^0.274.0",
"nanostores": "^0.9.2", "nanostores": "^0.9.2",
"node-html-parser": "^6.1.5", "node-html-parser": "^6.1.5",
"npm-check-updates": "^16.10.12", "npm-check-updates": "^16.10.12",
"react": "^18.0.0", "react": "^18.0.0",
"react-confetti": "^6.1.0",
"react-dom": "^18.0.0", "react-dom": "^18.0.0",
"rehype-external-links": "^2.1.0", "rehype-external-links": "^2.1.0",
"roadmap-renderer": "^1.0.6", "roadmap-renderer": "^1.0.6",

@ -38,6 +38,9 @@ dependencies:
js-cookie: js-cookie:
specifier: ^3.0.5 specifier: ^3.0.5
version: 3.0.5 version: 3.0.5
lucide-react:
specifier: ^0.274.0
version: 0.274.0(react@18.0.0)
nanostores: nanostores:
specifier: ^0.9.2 specifier: ^0.9.2
version: 0.9.2 version: 0.9.2
@ -50,6 +53,9 @@ dependencies:
react: react:
specifier: ^18.0.0 specifier: ^18.0.0
version: 18.0.0 version: 18.0.0
react-confetti:
specifier: ^6.1.0
version: 6.1.0(react@18.0.0)
react-dom: react-dom:
specifier: ^18.0.0 specifier: ^18.0.0
version: 18.0.0(react@18.0.0) version: 18.0.0(react@18.0.0)
@ -1519,7 +1525,7 @@ packages:
dependencies: dependencies:
browserslist: 4.21.10 browserslist: 4.21.10
caniuse-lite: 1.0.30001525 caniuse-lite: 1.0.30001525
fraction.js: 4.3.4 fraction.js: 4.3.6
normalize-range: 0.1.2 normalize-range: 0.1.2
picocolors: 1.0.0 picocolors: 1.0.0
postcss: 8.4.29 postcss: 8.4.29
@ -1611,7 +1617,7 @@ packages:
hasBin: true hasBin: true
dependencies: dependencies:
caniuse-lite: 1.0.30001525 caniuse-lite: 1.0.30001525
electron-to-chromium: 1.4.507 electron-to-chromium: 1.4.508
node-releases: 2.0.13 node-releases: 2.0.13
update-browserslist-db: 1.0.11(browserslist@4.21.10) update-browserslist-db: 1.0.11(browserslist@4.21.10)
dev: false dev: false
@ -2124,8 +2130,8 @@ packages:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
dev: false dev: false
/electron-to-chromium@1.4.507: /electron-to-chromium@1.4.508:
resolution: {integrity: sha512-brvPFnO1lu3UYBpBht2qWw9qqhdG4htTjT90/9oOJmxQ77VvTxL9+ghErFqQzgj7n8268ONAmlebqjBR/S+qgA==} resolution: {integrity: sha512-FFa8QKjQK/A5QuFr2167myhMesGrhlOBD+3cYNxO9/S4XzHEXesyTD/1/xF644gC8buFPz3ca6G1LOQD0tZrrg==}
dev: false dev: false
/email-addresses@5.0.0: /email-addresses@5.0.0:
@ -2426,8 +2432,8 @@ packages:
engines: {node: '>=10'} engines: {node: '>=10'}
dev: false dev: false
/fraction.js@4.3.4: /fraction.js@4.3.6:
resolution: {integrity: sha512-pwiTgt0Q7t+GHZA4yaLjObx4vXmmdcS0iSJ19o8d/goUGgItX9UZWKWNnLHehxviD8wU2IWRsnR8cD5+yOJP2Q==} resolution: {integrity: sha512-n2aZ9tNfYDwaHhvFTkhFErqOMIb8uyzSQ+vGJBjZyanAKZVbGUQ1sngfk9FdkBw7G26O7AgNjLcecLffD1c7eg==}
dev: false dev: false
/fs-constants@1.0.0: /fs-constants@1.0.0:
@ -3239,6 +3245,14 @@ packages:
engines: {node: '>=12'} engines: {node: '>=12'}
dev: false dev: false
/lucide-react@0.274.0(react@18.0.0):
resolution: {integrity: sha512-qiWcojRXEwDiSimMX1+arnxha+ROJzZjJaVvCC0rsG6a9pUPjZePXSq7em4ZKMp0NDm1hyzPNkM7UaWC3LU2AA==}
peerDependencies:
react: ^16.5.1 || ^17.0.0 || ^18.0.0
dependencies:
react: 18.0.0
dev: false
/magic-string@0.30.3: /magic-string@0.30.3:
resolution: {integrity: sha512-B7xGbll2fG/VjP+SWg4sX3JynwIU0mjoTc6MPpKNuIvftk6u6vqhDnk1R80b8C2GBR6ywqy+1DcKBrevBg+bmw==} resolution: {integrity: sha512-B7xGbll2fG/VjP+SWg4sX3JynwIU0mjoTc6MPpKNuIvftk6u6vqhDnk1R80b8C2GBR6ywqy+1DcKBrevBg+bmw==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -4607,6 +4621,16 @@ packages:
strip-json-comments: 2.0.1 strip-json-comments: 2.0.1
dev: false dev: false
/react-confetti@6.1.0(react@18.0.0):
resolution: {integrity: sha512-7Ypx4vz0+g8ECVxr88W9zhcQpbeujJAVqL14ZnXJ3I23mOI9/oBVTQ3dkJhUmB0D6XOtCZEM6N0Gm9PMngkORw==}
engines: {node: '>=10.18'}
peerDependencies:
react: ^16.3.0 || ^17.0.1 || ^18.0.0
dependencies:
react: 18.0.0
tween-functions: 1.2.0
dev: false
/react-dom@18.0.0(react@18.0.0): /react-dom@18.0.0(react@18.0.0):
resolution: {integrity: sha512-XqX7uzmFo0pUceWFCt7Gff6IyIMzFUn7QMZrbrQfGxtaxXZIcGQzoNpRLE3fQLnS4XzLLPMZX2T9TRcSrasicw==} resolution: {integrity: sha512-XqX7uzmFo0pUceWFCt7Gff6IyIMzFUn7QMZrbrQfGxtaxXZIcGQzoNpRLE3fQLnS4XzLLPMZX2T9TRcSrasicw==}
peerDependencies: peerDependencies:
@ -5443,6 +5467,10 @@ packages:
safe-buffer: 5.2.1 safe-buffer: 5.2.1
dev: false dev: false
/tween-functions@1.2.0:
resolution: {integrity: sha512-PZBtLYcCLtEcjL14Fzb1gSxPBeL7nWvGhO5ZFPGqziCcr8uvHp0NDmdjBchp6KHL+tExcg0m3NISmKxhU394dA==}
dev: false
/type-fest@0.13.1: /type-fest@0.13.1:
resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==} resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==}
engines: {node: '>=10'} engines: {node: '>=10'}

@ -0,0 +1,83 @@
import { QuestionsProgress } from './QuestionsProgress';
import { CheckCircle, SkipForward, Sparkles } from 'lucide-react';
import { useRef, useState } from 'react';
import ReactConfetti from 'react-confetti';
export function QuestionsList() {
const [confettiPos, setConfettiPos] = useState<
undefined | { x: number; y: number; w: number; h: number }
>(undefined);
const alreadyKnowRef = useRef<HTMLButtonElement>(null);
return (
<div className="mb-40 gap-3 text-center">
<QuestionsProgress />
{confettiPos && (
<ReactConfetti
numberOfPieces={20}
recycle={false}
onConfettiComplete={() => {
setConfettiPos(undefined);
}}
initialVelocityX={2}
initialVelocityY={8}
tweenDuration={25}
confettiSource={{
x: confettiPos.x,
y: confettiPos.y,
w: confettiPos.w,
h: confettiPos.h,
}}
/>
)}
<div className="relative mb-4 h-[400px] w-full overflow-hidden rounded-lg border border-gray-300 bg-white">
<div className="flex h-full w-full items-center justify-center">
<p className="animate-pulse text-2xl text-black duration-100">
Please wait ..
</p>
</div>
</div>
<div className="flex flex-col gap-3 sm:flex-row">
<button
ref={alreadyKnowRef}
onClick={(e) => {
const alreadyKnowRect =
alreadyKnowRef.current?.getBoundingClientRect();
const buttonX = alreadyKnowRect?.x || 0;
const buttonY = alreadyKnowRect?.y || 0;
// set confetti position, keeping in mind the scroll values
setConfettiPos({
x: buttonX,
y: buttonY + window.scrollY,
w: alreadyKnowRect?.width || 0,
h: alreadyKnowRect?.height || 0,
});
}}
className="flex flex-1 items-center rounded-xl border border-gray-300 bg-white py-3 px-4 text-black transition-colors hover:border-black hover:bg-black hover:text-white"
>
<CheckCircle className="mr-1 h-4 text-current" />
Already Know that
</button>
<button
data-next-question="dont-know"
className="flex flex-1 items-center rounded-xl border border-gray-300 bg-white py-3 px-4 text-black transition-colors hover:border-black hover:bg-black hover:text-white"
>
<Sparkles className="mr-1 h-4 text-current" />
Didn't Know that
</button>
<button
data-next-question="skip"
className="flex flex-1 items-center rounded-xl border border-red-600 p-3 text-red-600 hover:bg-red-600 hover:text-white"
>
<SkipForward className="mr-1 h-4" />
Skip Question
</button>
</div>
</div>
);
}

@ -0,0 +1,37 @@
import {Check, CheckCircle, Lightbulb, PartyPopper, RotateCcw, Sparkles} from 'lucide-react';
export function QuestionsProgress() {
return (
<div className="mb-5 rounded-lg border border-gray-300 bg-white p-6">
<div className="mb-3 flex items-center text-gray-600">
<div className="relative w-full flex-1 rounded-xl bg-gray-200 p-1">
<div className="absolute bottom-0 left-0 top-0 w-[30%] rounded-xl bg-slate-800"></div>
</div>
<span className="ml-3 text-sm">5 / 100</span>
</div>
<div className="relative -left-1 flex flex-col gap-2 text-sm text-black sm:flex-row sm:gap-3">
<span className="flex items-center">
<CheckCircle className="mr-1 h-4" />
<span>Already knew</span>
<span className="ml-2 rounded-md bg-gray-200/80 px-1.5 font-medium text-black">
44 Questions
</span>
</span>
<span className="flex items-center">
<Sparkles className="mr-1 h-4" />
<span>Didn't Know</span>
<span className="ml-2 rounded-md bg-gray-200/80 px-1.5 font-medium text-black">
20 Questions
</span>
</span>
<button className="flex items-center text-red-600 hover:text-red-900">
<RotateCcw className="mr-1 h-4" />
Reset Progress
</button>
</div>
</div>
);
}

@ -4,6 +4,7 @@ import SimplePageHeader from '../../components/SimplePageHeader.astro';
import BaseLayout from '../../layouts/BaseLayout.astro'; import BaseLayout from '../../layouts/BaseLayout.astro';
import Footer from '../../components/Footer.astro'; import Footer from '../../components/Footer.astro';
import AstroIcon from "../../components/AstroIcon.astro"; import AstroIcon from "../../components/AstroIcon.astro";
import { QuestionsList } from '../../components/Questions/QuestionsList';
export async function getStaticPaths() { export async function getStaticPaths() {
return [ return [
@ -34,74 +35,7 @@ export async function getStaticPaths() {
</p> </p>
</div> </div>
<div class='gap-3 text-center mb-40'> <QuestionsList client:load />
<!-- Progress bar -->
<div class='mb-5 rounded-lg border border-gray-300 p-6 bg-white'>
<div class='mb-3 flex items-center text-gray-600'>
<div class='relative w-full flex-1 rounded-xl bg-gray-200 p-1'>
<div
class='absolute bottom-0 left-0 top-0 w-[30%] rounded-xl bg-slate-800'
>
</div>
</div>
<span class='ml-3 text-sm'>5 / 100</span>
</div>
<div
class='relative -left-1 flex flex-col gap-2 text-sm text-black sm:flex-row sm:gap-3'
>
<span class='flex items-center'>
<AstroIcon icon='check-current' class='h-3 text-gray-800 mr-1.5' />
<span>Already knew</span>
<span class='bg-gray-200/80 px-1.5 rounded-md ml-2 text-black font-medium'>44 Questions</span>
</span>
<span class='flex items-center'>
<AstroIcon icon='question' class='h-4 mr-1.5' />
<span>Didn't Know</span>
<span class='bg-gray-200/80 px-1.5 rounded-md ml-2 text-black font-medium'>20 Questions</span>
</span>
<button class='flex items-center text-red-600 hover:text-red-900'>
<AstroIcon icon='reset' class='h-3 sm:h-4' />
<span class='ml-0.5 sm:ml-1'>Reset Progress</span>
</button>
</div>
</div>
<div class='relative mb-4 h-[400px] w-full overflow-hidden rounded-lg bg-white border border-gray-300'>
<div
class='flex h-full w-full items-center justify-center'
>
<p class='animate-pulse text-2xl text-black duration-100'>
Please wait ..
</p>
</div>
</div>
<div class='flex flex-col gap-3 sm:flex-row'>
<button
class='flex flex-1 items-center rounded-xl bg-white border border-gray-300 py-3 px-4 text-black transition-colors hover:bg-black hover:border-black hover:text-white'
>
<AstroIcon icon='check-current' class='mr-2 h-4 text-current' />
Already Know that
</button>
<button
data-next-question='dont-know'
class='flex flex-1 items-center rounded-xl bg-white border border-gray-300 py-3 px-4 text-black transition-colors hover:bg-black hover:border-black hover:text-white'
>
<AstroIcon icon='bulb' class='mr-2 h-5 text-current' />
Didn't Know that
</button>
<button
data-next-question='skip'
class='flex flex-1 items-center rounded-xl border border-red-600 p-3 text-red-600 hover:bg-red-600 hover:text-white'
>
<AstroIcon icon='skip' class='mr-2 h-4' />
Skip Question
</button>
</div>
</div>
</div> </div>
</div> </div>

Loading…
Cancel
Save