Skip to content

Commit a020252

Browse files
committed
style: style progress components
1 parent 81d968a commit a020252

File tree

6 files changed

+134
-22
lines changed

6 files changed

+134
-22
lines changed

src/components/Header.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ import { Input } from './ui/input';
1313
import { User, Edit2, Save, X } from 'lucide-react';
1414
import { Tooltip, TooltipTrigger, TooltipContent } from './ui/tooltip';
1515
import { validateEmail } from '../../utils/validateEmail';
16-
import QuestionCounter from './ui/QuestionCounter';
16+
import QuestionCounter from './ui/questionCounter/QuestionCounter';
17+
import SmallCircularQuestionCounter from './ui/questionCounter/smallCircularQuestionCounter';
18+
import LargeCircularQuestionCounter from './ui/questionCounter/LargeCircularQuestionCounter';
1719

1820
const Header: React.FC = () => {
1921
// const { questions, setQuestions } = useQuestions();
@@ -50,9 +52,9 @@ const Header: React.FC = () => {
5052
{data.username ? (
5153
<span className='mr-2'>Logged as: {data.username}</span>
5254
) : (
53-
<span>Not logged</span>
55+
<span className='mr-2'>Not logged</span>
5456
)}
55-
<User size={24} />
57+
<SmallCircularQuestionCounter />
5658
</div>
5759
</DialogTrigger>
5860
<DialogContent headerTitle="User's Data" className='sm:max-w-md p-0'>
@@ -142,6 +144,7 @@ const Header: React.FC = () => {
142144
</div>
143145
<div className='text-sm text-gray-800'>
144146
<QuestionCounter />
147+
<LargeCircularQuestionCounter />
145148
</div>
146149
</div>
147150
</div>

src/components/ui/QuestionCounter.tsx

Lines changed: 0 additions & 17 deletions
This file was deleted.
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import React from 'react';
2+
import { useAnsweredCount } from '../../../hooks/useAnsweredCount';
3+
4+
interface LargeCircularQuestionCounterProps {
5+
size?: number; // default 100px
6+
strokeWidth?: number; // default 6px
7+
}
8+
9+
const LargeCircularQuestionCounter: React.FC<
10+
LargeCircularQuestionCounterProps
11+
> = ({ size = 100, strokeWidth = 6 }) => {
12+
const { answered, total } = useAnsweredCount();
13+
const progress = total > 0 ? Math.round((answered / total) * 100) : 0;
14+
15+
const radius = (size - strokeWidth) / 2;
16+
const circumference = 2 * Math.PI * radius;
17+
const dashOffset = circumference * (1 - progress / 100);
18+
19+
return (
20+
<div className='relative w-fit'>
21+
<svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}>
22+
{/* Background circle */}
23+
<circle
24+
className='text-gray-200'
25+
stroke='currentColor'
26+
strokeWidth={strokeWidth}
27+
fill='transparent'
28+
r={radius}
29+
cx={size / 2}
30+
cy={size / 2}
31+
/>
32+
{/* Progress circle */}
33+
<circle
34+
className='text-brand-pink'
35+
stroke='currentColor'
36+
strokeWidth={strokeWidth}
37+
fill='transparent'
38+
r={radius}
39+
cx={size / 2}
40+
cy={size / 2}
41+
strokeDasharray={circumference}
42+
strokeDashoffset={dashOffset}
43+
transform={`rotate(-90 ${size / 2} ${size / 2})`} // ✅ Start from the top
44+
style={{ transition: 'stroke-dashoffset 0.35s ease-out' }}
45+
/>
46+
</svg>
47+
{/* Percentage Text in Center */}
48+
<span className='absolute inset-0 flex items-center justify-center text-lg font-semibold'>
49+
{progress}%
50+
</span>
51+
</div>
52+
);
53+
};
54+
55+
export default LargeCircularQuestionCounter;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import React from 'react';
2+
import { useAnsweredCount } from '../../../hooks/useAnsweredCount';
3+
4+
const QuestionCounter: React.FC = () => {
5+
const { answered, total } = useAnsweredCount(); // Extract values
6+
7+
return (
8+
<div className='text-sm text-gray-700'>
9+
{answered} / {total} questions answered
10+
</div>
11+
);
12+
};
13+
14+
export default QuestionCounter;
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import React from 'react';
2+
import { useAnsweredCount } from '../../../hooks/useAnsweredCount';
3+
4+
interface SmallCircularQuestionCounterProps {
5+
size?: number; // default 24px
6+
strokeWidth?: number; // default 2px
7+
}
8+
9+
const SmallCircularQuestionCounter: React.FC<
10+
SmallCircularQuestionCounterProps
11+
> = ({ size = 24, strokeWidth = 4 }) => {
12+
const { answered, total } = useAnsweredCount();
13+
const progress = total > 0 ? Math.round((answered / total) * 100) : 0;
14+
15+
const radius = (size - strokeWidth) / 2;
16+
const circumference = 2 * Math.PI * radius;
17+
const dashOffset = circumference * (1 - progress / 100);
18+
19+
return (
20+
<svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}>
21+
{/* Background circle */}
22+
<circle
23+
className='text-gray-200'
24+
stroke='currentColor'
25+
strokeWidth={strokeWidth}
26+
fill='transparent'
27+
r={radius}
28+
cx={size / 2}
29+
cy={size / 2}
30+
/>
31+
{/* Progress circle */}
32+
<circle
33+
className='text-green-500'
34+
stroke='currentColor'
35+
strokeWidth={strokeWidth}
36+
fill='transparent'
37+
r={radius}
38+
cx={size / 2}
39+
cy={size / 2}
40+
strokeDasharray={circumference}
41+
strokeDashoffset={dashOffset}
42+
transform={`rotate(-90 ${size / 2} ${size / 2})`} // ✅ Start from the top
43+
style={{ transition: 'stroke-dashoffset 0.35s ease-out' }}
44+
/>
45+
</svg>
46+
);
47+
};
48+
49+
export default SmallCircularQuestionCounter;

src/hooks/useAnsweredCount.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
1+
import { useQuestions } from './useQuestions';
12
import { useStatements } from './useStatements';
23

3-
export function useAnsweredCount(): number {
4+
export function useAnsweredCount(): { answered: number; total: number } {
5+
const { questions } = useQuestions();
46
const { data } = useStatements();
5-
return data.statements.filter((s) => s.presetId).length;
7+
8+
// Count how many preset questions have been answered (i.e., they have a matching statement)
9+
const answeredCount = questions.filter((q) =>
10+
data.statements.some((s) => s.presetId === q.id)
11+
).length;
12+
13+
return { answered: answeredCount, total: questions.length };
614
}

0 commit comments

Comments
 (0)