@moontra/moonui-pro 2.20.2 → 2.20.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +8 -3
- package/plugin/index.d.ts +86 -0
- package/plugin/index.js +308 -0
- package/scripts/postinstall.js +176 -23
- package/src/components/advanced-chart/index.tsx +0 -1246
- package/src/components/advanced-forms/index.tsx +0 -585
- package/src/components/animated-button/index.tsx +0 -385
- package/src/components/calendar/event-dialog.tsx +0 -377
- package/src/components/calendar/index.tsx +0 -1220
- package/src/components/calendar-pro/index.tsx +0 -1697
- package/src/components/color-picker/index.tsx +0 -432
- package/src/components/credit-card-input/index.tsx +0 -406
- package/src/components/dashboard/dashboard-grid.tsx +0 -480
- package/src/components/dashboard/demo.tsx +0 -425
- package/src/components/dashboard/index.tsx +0 -1046
- package/src/components/dashboard/time-range-picker.tsx +0 -336
- package/src/components/dashboard/types.ts +0 -225
- package/src/components/dashboard/widgets/activity-feed.tsx +0 -349
- package/src/components/dashboard/widgets/chart-widget.tsx +0 -418
- package/src/components/dashboard/widgets/comparison-widget.tsx +0 -177
- package/src/components/dashboard/widgets/index.ts +0 -5
- package/src/components/dashboard/widgets/metric-card.tsx +0 -363
- package/src/components/dashboard/widgets/progress-widget.tsx +0 -113
- package/src/components/data-table/data-table-bulk-actions.tsx +0 -204
- package/src/components/data-table/data-table-column-toggle.tsx +0 -169
- package/src/components/data-table/data-table-export.ts +0 -156
- package/src/components/data-table/data-table-filter-drawer.tsx +0 -448
- package/src/components/data-table/index.tsx +0 -845
- package/src/components/draggable-list/index.tsx +0 -100
- package/src/components/error-boundary/index.tsx +0 -232
- package/src/components/file-upload/index.tsx +0 -1660
- package/src/components/floating-action-button/index.tsx +0 -206
- package/src/components/form-wizard/form-wizard-context.tsx +0 -335
- package/src/components/form-wizard/form-wizard-navigation.tsx +0 -118
- package/src/components/form-wizard/form-wizard-progress.tsx +0 -329
- package/src/components/form-wizard/form-wizard-step.tsx +0 -111
- package/src/components/form-wizard/index.tsx +0 -102
- package/src/components/form-wizard/types.ts +0 -77
- package/src/components/gesture-drawer/index.tsx +0 -551
- package/src/components/github-stars/github-api.ts +0 -426
- package/src/components/github-stars/hooks.ts +0 -517
- package/src/components/github-stars/index.tsx +0 -375
- package/src/components/github-stars/types.ts +0 -148
- package/src/components/github-stars/variants.tsx +0 -515
- package/src/components/health-check/index.tsx +0 -439
- package/src/components/hover-card-3d/index.tsx +0 -529
- package/src/components/index.ts +0 -130
- package/src/components/internal/index.ts +0 -78
- package/src/components/kanban/add-card-modal.tsx +0 -502
- package/src/components/kanban/card-detail-modal.tsx +0 -761
- package/src/components/kanban/index.ts +0 -13
- package/src/components/kanban/kanban.tsx +0 -1689
- package/src/components/kanban/types.ts +0 -168
- package/src/components/lazy-component/index.tsx +0 -823
- package/src/components/license-error/index.tsx +0 -31
- package/src/components/magnetic-button/index.tsx +0 -216
- package/src/components/memory-efficient-data/index.tsx +0 -1018
- package/src/components/moonui-quiz-form/index.tsx +0 -817
- package/src/components/navbar/index.tsx +0 -781
- package/src/components/optimized-image/index.tsx +0 -425
- package/src/components/performance-debugger/index.tsx +0 -613
- package/src/components/performance-monitor/index.tsx +0 -808
- package/src/components/phone-number-input/index.tsx +0 -343
- package/src/components/phone-number-input/phone-number-input-simple.tsx +0 -167
- package/src/components/pinch-zoom/index.tsx +0 -566
- package/src/components/quiz-form/index.tsx +0 -479
- package/src/components/rich-text-editor/index.tsx +0 -2322
- package/src/components/rich-text-editor/slash-commands-extension.ts +0 -230
- package/src/components/rich-text-editor/slash-commands.css +0 -35
- package/src/components/rich-text-editor/table-styles.css +0 -65
- package/src/components/sidebar/index.tsx +0 -884
- package/src/components/spotlight-card/index.tsx +0 -191
- package/src/components/swipeable-card/index.tsx +0 -100
- package/src/components/timeline/index.tsx +0 -1183
- package/src/components/ui/accordion.tsx +0 -581
- package/src/components/ui/alert-dialog.tsx +0 -141
- package/src/components/ui/alert.tsx +0 -141
- package/src/components/ui/aspect-ratio.tsx +0 -245
- package/src/components/ui/avatar.tsx +0 -155
- package/src/components/ui/badge.tsx +0 -230
- package/src/components/ui/breadcrumb.tsx +0 -216
- package/src/components/ui/button.tsx +0 -228
- package/src/components/ui/calendar.tsx +0 -387
- package/src/components/ui/card.tsx +0 -216
- package/src/components/ui/checkbox.tsx +0 -259
- package/src/components/ui/collapsible.tsx +0 -631
- package/src/components/ui/color-picker.tsx +0 -97
- package/src/components/ui/command.tsx +0 -948
- package/src/components/ui/dialog.tsx +0 -752
- package/src/components/ui/dropdown-menu.tsx +0 -706
- package/src/components/ui/gesture-drawer.tsx +0 -11
- package/src/components/ui/hover-card.tsx +0 -29
- package/src/components/ui/index.ts +0 -222
- package/src/components/ui/input.tsx +0 -224
- package/src/components/ui/label.tsx +0 -29
- package/src/components/ui/lightbox.tsx +0 -606
- package/src/components/ui/magnetic-button.tsx +0 -129
- package/src/components/ui/media-gallery.tsx +0 -611
- package/src/components/ui/navigation-menu.tsx +0 -130
- package/src/components/ui/pagination.tsx +0 -125
- package/src/components/ui/popover.tsx +0 -185
- package/src/components/ui/progress.tsx +0 -30
- package/src/components/ui/radio-group.tsx +0 -257
- package/src/components/ui/scroll-area.tsx +0 -47
- package/src/components/ui/select.tsx +0 -378
- package/src/components/ui/separator.tsx +0 -145
- package/src/components/ui/sheet.tsx +0 -139
- package/src/components/ui/skeleton.tsx +0 -20
- package/src/components/ui/slider.tsx +0 -354
- package/src/components/ui/spotlight-card.tsx +0 -119
- package/src/components/ui/switch.tsx +0 -86
- package/src/components/ui/table.tsx +0 -331
- package/src/components/ui/tabs-pro.tsx +0 -542
- package/src/components/ui/tabs.tsx +0 -54
- package/src/components/ui/textarea.tsx +0 -28
- package/src/components/ui/toast.tsx +0 -317
- package/src/components/ui/toggle.tsx +0 -119
- package/src/components/ui/tooltip.tsx +0 -151
- package/src/components/virtual-list/index.tsx +0 -668
- package/src/hooks/use-chart.ts +0 -205
- package/src/hooks/use-data-table.ts +0 -182
- package/src/hooks/use-docs-pro-access.ts +0 -13
- package/src/hooks/use-license-check.ts +0 -65
- package/src/hooks/use-subscription.ts +0 -19
- package/src/hooks/use-toast.ts +0 -15
- package/src/index.ts +0 -22
- package/src/lib/ai-providers.ts +0 -377
- package/src/lib/component-metadata.ts +0 -18
- package/src/lib/micro-interactions.ts +0 -255
- package/src/lib/paddle.ts +0 -17
- package/src/lib/utils.ts +0 -6
- package/src/patterns/login-form/index.tsx +0 -276
- package/src/patterns/login-form/types.ts +0 -67
- package/src/setupTests.ts +0 -41
- package/src/styles/advanced-chart.css +0 -239
- package/src/styles/calendar.css +0 -35
- package/src/styles/design-system.css +0 -363
- package/src/styles/index.css +0 -681
- package/src/styles/tailwind.css +0 -7
- package/src/styles/tokens.css +0 -455
- package/src/types/next-auth.d.ts +0 -21
- package/src/use-intersection-observer.tsx +0 -154
- package/src/use-local-storage.tsx +0 -71
- package/src/use-paddle.ts +0 -138
- package/src/use-performance-optimizer.ts +0 -389
- package/src/use-pro-access.ts +0 -141
- package/src/use-scroll-animation.ts +0 -219
- package/src/use-subscription.ts +0 -37
- package/src/use-toast.ts +0 -32
- package/src/utils/chart-helpers.ts +0 -357
- package/src/utils/cn.ts +0 -6
- package/src/utils/data-processing.ts +0 -151
- package/src/utils/license-validator.tsx +0 -183
|
@@ -1,817 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import React, { useState, useEffect, useCallback, useMemo } from 'react';
|
|
4
|
-
import { cva, type VariantProps } from 'class-variance-authority';
|
|
5
|
-
import { cn } from '../../lib/utils';
|
|
6
|
-
import { motion, AnimatePresence } from 'framer-motion';
|
|
7
|
-
import {
|
|
8
|
-
Clock,
|
|
9
|
-
CheckCircle2,
|
|
10
|
-
XCircle,
|
|
11
|
-
AlertCircle,
|
|
12
|
-
ChevronRight,
|
|
13
|
-
ChevronLeft,
|
|
14
|
-
Download,
|
|
15
|
-
Share2,
|
|
16
|
-
RotateCcw,
|
|
17
|
-
Eye,
|
|
18
|
-
EyeOff,
|
|
19
|
-
HelpCircle,
|
|
20
|
-
Star
|
|
21
|
-
} from 'lucide-react';
|
|
22
|
-
|
|
23
|
-
const quizFormVariants = cva(
|
|
24
|
-
'relative w-full rounded-lg border bg-card text-card-foreground shadow-sm',
|
|
25
|
-
{
|
|
26
|
-
variants: {
|
|
27
|
-
variant: {
|
|
28
|
-
default: 'border-border',
|
|
29
|
-
exam: 'border-primary/20 bg-primary/5',
|
|
30
|
-
personality: 'border-secondary/20 bg-secondary/5',
|
|
31
|
-
},
|
|
32
|
-
size: {
|
|
33
|
-
sm: 'p-4',
|
|
34
|
-
md: 'p-6',
|
|
35
|
-
lg: 'p-8',
|
|
36
|
-
},
|
|
37
|
-
},
|
|
38
|
-
defaultVariants: {
|
|
39
|
-
variant: 'default',
|
|
40
|
-
size: 'md',
|
|
41
|
-
},
|
|
42
|
-
}
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
// Soru tipleri
|
|
46
|
-
export type QuestionType =
|
|
47
|
-
| 'single-choice'
|
|
48
|
-
| 'multiple-choice'
|
|
49
|
-
| 'text'
|
|
50
|
-
| 'rating'
|
|
51
|
-
| 'true-false'
|
|
52
|
-
| 'matching';
|
|
53
|
-
|
|
54
|
-
// Soru interface'i
|
|
55
|
-
export interface Question {
|
|
56
|
-
id: string;
|
|
57
|
-
type: QuestionType;
|
|
58
|
-
question: string;
|
|
59
|
-
options?: string[];
|
|
60
|
-
correctAnswer?: string | string[] | Record<string, string>;
|
|
61
|
-
points?: number;
|
|
62
|
-
hint?: string;
|
|
63
|
-
explanation?: string;
|
|
64
|
-
required?: boolean;
|
|
65
|
-
// Matching için
|
|
66
|
-
leftOptions?: string[];
|
|
67
|
-
rightOptions?: string[];
|
|
68
|
-
// Rating için
|
|
69
|
-
maxRating?: number;
|
|
70
|
-
ratingLabels?: string[];
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Quiz ayarları
|
|
74
|
-
export interface QuizSettings {
|
|
75
|
-
shuffleQuestions?: boolean;
|
|
76
|
-
shuffleOptions?: boolean;
|
|
77
|
-
showInstantFeedback?: boolean;
|
|
78
|
-
showHints?: boolean;
|
|
79
|
-
allowReview?: boolean;
|
|
80
|
-
timeLimit?: number; // dakika cinsinden
|
|
81
|
-
passingScore?: number; // yüzde cinsinden
|
|
82
|
-
maxAttempts?: number;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Kullanıcı cevabı
|
|
86
|
-
export interface UserAnswer {
|
|
87
|
-
questionId: string;
|
|
88
|
-
answer: string | string[] | Record<string, string> | null;
|
|
89
|
-
timeSpent?: number;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Quiz sonucu
|
|
93
|
-
export interface QuizResult {
|
|
94
|
-
score: number;
|
|
95
|
-
totalScore: number;
|
|
96
|
-
percentage: number;
|
|
97
|
-
passed?: boolean;
|
|
98
|
-
answers: Array<{
|
|
99
|
-
questionId: string;
|
|
100
|
-
userAnswer: any;
|
|
101
|
-
correctAnswer: any;
|
|
102
|
-
isCorrect: boolean;
|
|
103
|
-
points: number;
|
|
104
|
-
}>;
|
|
105
|
-
timeSpent?: number;
|
|
106
|
-
completedAt: Date;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export interface MoonUIQuizFormProProps
|
|
110
|
-
extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onProgress'>,
|
|
111
|
-
VariantProps<typeof quizFormVariants> {
|
|
112
|
-
questions: Question[];
|
|
113
|
-
settings?: QuizSettings;
|
|
114
|
-
onComplete?: (result: QuizResult) => void;
|
|
115
|
-
onProgress?: (current: number, total: number) => void;
|
|
116
|
-
title?: string;
|
|
117
|
-
description?: string;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export const MoonUIQuizFormPro = React.forwardRef<
|
|
121
|
-
HTMLDivElement,
|
|
122
|
-
MoonUIQuizFormProProps
|
|
123
|
-
>(({
|
|
124
|
-
className,
|
|
125
|
-
variant,
|
|
126
|
-
size,
|
|
127
|
-
questions: initialQuestions,
|
|
128
|
-
settings = {},
|
|
129
|
-
onComplete,
|
|
130
|
-
onProgress,
|
|
131
|
-
title,
|
|
132
|
-
description,
|
|
133
|
-
...props
|
|
134
|
-
}, ref) => {
|
|
135
|
-
// State'ler
|
|
136
|
-
const [questions, setQuestions] = useState<Question[]>([]);
|
|
137
|
-
const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0);
|
|
138
|
-
const [userAnswers, setUserAnswers] = useState<Record<string, UserAnswer>>({});
|
|
139
|
-
const [showResults, setShowResults] = useState(false);
|
|
140
|
-
const [quizResult, setQuizResult] = useState<QuizResult | null>(null);
|
|
141
|
-
const [timeLeft, setTimeLeft] = useState<number | null>(
|
|
142
|
-
settings.timeLimit ? settings.timeLimit * 60 : null
|
|
143
|
-
);
|
|
144
|
-
const [questionStartTime, setQuestionStartTime] = useState<number>(Date.now());
|
|
145
|
-
const [showHint, setShowHint] = useState(false);
|
|
146
|
-
const [attempts, setAttempts] = useState(0);
|
|
147
|
-
|
|
148
|
-
// Soruları hazırla (shuffle vs.)
|
|
149
|
-
useEffect(() => {
|
|
150
|
-
let processedQuestions = [...initialQuestions];
|
|
151
|
-
|
|
152
|
-
// Soruları karıştır
|
|
153
|
-
if (settings.shuffleQuestions) {
|
|
154
|
-
processedQuestions = shuffleArray(processedQuestions);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// Seçenekleri karıştır
|
|
158
|
-
if (settings.shuffleOptions) {
|
|
159
|
-
processedQuestions = processedQuestions.map(q => {
|
|
160
|
-
if (q.options) {
|
|
161
|
-
return { ...q, options: shuffleArray([...q.options]) };
|
|
162
|
-
}
|
|
163
|
-
return q;
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
setQuestions(processedQuestions);
|
|
168
|
-
}, [initialQuestions, settings.shuffleQuestions, settings.shuffleOptions]);
|
|
169
|
-
|
|
170
|
-
// Timer
|
|
171
|
-
useEffect(() => {
|
|
172
|
-
if (timeLeft === null || timeLeft <= 0 || showResults) return;
|
|
173
|
-
|
|
174
|
-
const timer = setInterval(() => {
|
|
175
|
-
setTimeLeft(prev => {
|
|
176
|
-
if (prev === null || prev <= 1) {
|
|
177
|
-
handleComplete();
|
|
178
|
-
return 0;
|
|
179
|
-
}
|
|
180
|
-
return prev - 1;
|
|
181
|
-
});
|
|
182
|
-
}, 1000);
|
|
183
|
-
|
|
184
|
-
return () => clearInterval(timer);
|
|
185
|
-
}, [timeLeft, showResults]);
|
|
186
|
-
|
|
187
|
-
// Progress callback
|
|
188
|
-
useEffect(() => {
|
|
189
|
-
if (onProgress) {
|
|
190
|
-
onProgress(currentQuestionIndex + 1, questions.length);
|
|
191
|
-
}
|
|
192
|
-
}, [currentQuestionIndex, questions.length, onProgress]);
|
|
193
|
-
|
|
194
|
-
// Mevcut soru
|
|
195
|
-
const currentQuestion = questions[currentQuestionIndex];
|
|
196
|
-
|
|
197
|
-
// Array'i karıştır
|
|
198
|
-
const shuffleArray = <T,>(array: T[]): T[] => {
|
|
199
|
-
const newArray = [...array];
|
|
200
|
-
for (let i = newArray.length - 1; i > 0; i--) {
|
|
201
|
-
const j = Math.floor(Math.random() * (i + 1));
|
|
202
|
-
[newArray[i], newArray[j]] = [newArray[j], newArray[i]];
|
|
203
|
-
}
|
|
204
|
-
return newArray;
|
|
205
|
-
};
|
|
206
|
-
|
|
207
|
-
// Cevabı kaydet
|
|
208
|
-
const saveAnswer = (answer: any) => {
|
|
209
|
-
const timeSpent = Date.now() - questionStartTime;
|
|
210
|
-
setUserAnswers(prev => ({
|
|
211
|
-
...prev,
|
|
212
|
-
[currentQuestion.id]: {
|
|
213
|
-
questionId: currentQuestion.id,
|
|
214
|
-
answer,
|
|
215
|
-
timeSpent,
|
|
216
|
-
},
|
|
217
|
-
}));
|
|
218
|
-
};
|
|
219
|
-
|
|
220
|
-
// Sonraki soruya geç
|
|
221
|
-
const handleNext = () => {
|
|
222
|
-
if (currentQuestionIndex < questions.length - 1) {
|
|
223
|
-
setCurrentQuestionIndex(prev => prev + 1);
|
|
224
|
-
setQuestionStartTime(Date.now());
|
|
225
|
-
setShowHint(false);
|
|
226
|
-
} else {
|
|
227
|
-
handleComplete();
|
|
228
|
-
}
|
|
229
|
-
};
|
|
230
|
-
|
|
231
|
-
// Önceki soruya dön
|
|
232
|
-
const handlePrevious = () => {
|
|
233
|
-
if (currentQuestionIndex > 0) {
|
|
234
|
-
setCurrentQuestionIndex(prev => prev - 1);
|
|
235
|
-
setShowHint(false);
|
|
236
|
-
}
|
|
237
|
-
};
|
|
238
|
-
|
|
239
|
-
// Quiz'i tamamla
|
|
240
|
-
const handleComplete = () => {
|
|
241
|
-
const result = calculateResult();
|
|
242
|
-
setQuizResult(result);
|
|
243
|
-
setShowResults(true);
|
|
244
|
-
setAttempts(prev => prev + 1);
|
|
245
|
-
|
|
246
|
-
if (onComplete) {
|
|
247
|
-
onComplete(result);
|
|
248
|
-
}
|
|
249
|
-
};
|
|
250
|
-
|
|
251
|
-
// Sonucu hesapla
|
|
252
|
-
const calculateResult = (): QuizResult => {
|
|
253
|
-
let totalScore = 0;
|
|
254
|
-
let userScore = 0;
|
|
255
|
-
const answers: QuizResult['answers'] = [];
|
|
256
|
-
|
|
257
|
-
questions.forEach(question => {
|
|
258
|
-
const userAnswer = userAnswers[question.id];
|
|
259
|
-
const points = question.points || 1;
|
|
260
|
-
totalScore += points;
|
|
261
|
-
|
|
262
|
-
let isCorrect = false;
|
|
263
|
-
|
|
264
|
-
if (userAnswer?.answer && question.correctAnswer !== undefined) {
|
|
265
|
-
switch (question.type) {
|
|
266
|
-
case 'single-choice':
|
|
267
|
-
case 'true-false':
|
|
268
|
-
case 'text':
|
|
269
|
-
isCorrect = userAnswer.answer === question.correctAnswer;
|
|
270
|
-
break;
|
|
271
|
-
|
|
272
|
-
case 'multiple-choice':
|
|
273
|
-
if (Array.isArray(userAnswer.answer) && Array.isArray(question.correctAnswer)) {
|
|
274
|
-
isCorrect =
|
|
275
|
-
userAnswer.answer.length === question.correctAnswer.length &&
|
|
276
|
-
userAnswer.answer.every(a => (question.correctAnswer as string[]).includes(a));
|
|
277
|
-
}
|
|
278
|
-
break;
|
|
279
|
-
|
|
280
|
-
case 'matching':
|
|
281
|
-
if (typeof userAnswer.answer === 'object' && typeof question.correctAnswer === 'object') {
|
|
282
|
-
const correct = question.correctAnswer as Record<string, string>;
|
|
283
|
-
const user = userAnswer.answer as Record<string, string>;
|
|
284
|
-
isCorrect = Object.keys(correct).every(key => correct[key] === user[key]);
|
|
285
|
-
}
|
|
286
|
-
break;
|
|
287
|
-
|
|
288
|
-
case 'rating':
|
|
289
|
-
// Rating'de doğru/yanlış yok
|
|
290
|
-
isCorrect = true;
|
|
291
|
-
break;
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
if (isCorrect) {
|
|
296
|
-
userScore += points;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
answers.push({
|
|
300
|
-
questionId: question.id,
|
|
301
|
-
userAnswer: userAnswer?.answer || null,
|
|
302
|
-
correctAnswer: question.correctAnswer,
|
|
303
|
-
isCorrect,
|
|
304
|
-
points: isCorrect ? points : 0,
|
|
305
|
-
});
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
const percentage = totalScore > 0 ? (userScore / totalScore) * 100 : 0;
|
|
309
|
-
const totalTimeSpent = Object.values(userAnswers).reduce(
|
|
310
|
-
(sum, answer) => sum + (answer.timeSpent || 0),
|
|
311
|
-
0
|
|
312
|
-
);
|
|
313
|
-
|
|
314
|
-
return {
|
|
315
|
-
score: userScore,
|
|
316
|
-
totalScore,
|
|
317
|
-
percentage,
|
|
318
|
-
passed: settings.passingScore ? percentage >= settings.passingScore : undefined,
|
|
319
|
-
answers,
|
|
320
|
-
timeSpent: totalTimeSpent,
|
|
321
|
-
completedAt: new Date(),
|
|
322
|
-
};
|
|
323
|
-
};
|
|
324
|
-
|
|
325
|
-
// Quiz'i yeniden başlat
|
|
326
|
-
const handleRestart = () => {
|
|
327
|
-
setCurrentQuestionIndex(0);
|
|
328
|
-
setUserAnswers({});
|
|
329
|
-
setShowResults(false);
|
|
330
|
-
setQuizResult(null);
|
|
331
|
-
setTimeLeft(settings.timeLimit ? settings.timeLimit * 60 : null);
|
|
332
|
-
setQuestionStartTime(Date.now());
|
|
333
|
-
setShowHint(false);
|
|
334
|
-
|
|
335
|
-
// Soruları yeniden karıştır
|
|
336
|
-
if (settings.shuffleQuestions) {
|
|
337
|
-
setQuestions(shuffleArray([...initialQuestions]));
|
|
338
|
-
}
|
|
339
|
-
};
|
|
340
|
-
|
|
341
|
-
// Sonuçları indir
|
|
342
|
-
const handleDownloadResults = () => {
|
|
343
|
-
if (!quizResult) return;
|
|
344
|
-
|
|
345
|
-
const data = {
|
|
346
|
-
title,
|
|
347
|
-
completedAt: quizResult.completedAt,
|
|
348
|
-
score: quizResult.score,
|
|
349
|
-
totalScore: quizResult.totalScore,
|
|
350
|
-
percentage: quizResult.percentage,
|
|
351
|
-
passed: quizResult.passed,
|
|
352
|
-
timeSpent: quizResult.timeSpent,
|
|
353
|
-
answers: quizResult.answers.map(a => {
|
|
354
|
-
const question = questions.find(q => q.id === a.questionId);
|
|
355
|
-
return {
|
|
356
|
-
question: question?.question,
|
|
357
|
-
userAnswer: a.userAnswer,
|
|
358
|
-
correctAnswer: a.correctAnswer,
|
|
359
|
-
isCorrect: a.isCorrect,
|
|
360
|
-
points: a.points,
|
|
361
|
-
};
|
|
362
|
-
}),
|
|
363
|
-
};
|
|
364
|
-
|
|
365
|
-
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
|
|
366
|
-
const url = URL.createObjectURL(blob);
|
|
367
|
-
const link = document.createElement('a');
|
|
368
|
-
link.href = url;
|
|
369
|
-
link.download = `quiz-results-${Date.now()}.json`;
|
|
370
|
-
link.click();
|
|
371
|
-
URL.revokeObjectURL(url);
|
|
372
|
-
};
|
|
373
|
-
|
|
374
|
-
// Zamanı formatla
|
|
375
|
-
const formatTime = (seconds: number): string => {
|
|
376
|
-
const mins = Math.floor(seconds / 60);
|
|
377
|
-
const secs = seconds % 60;
|
|
378
|
-
return `${mins}:${secs.toString().padStart(2, '0')}`;
|
|
379
|
-
};
|
|
380
|
-
|
|
381
|
-
// Render question
|
|
382
|
-
const renderQuestion = () => {
|
|
383
|
-
if (!currentQuestion) return null;
|
|
384
|
-
|
|
385
|
-
const userAnswer = userAnswers[currentQuestion.id]?.answer;
|
|
386
|
-
|
|
387
|
-
switch (currentQuestion.type) {
|
|
388
|
-
case 'single-choice':
|
|
389
|
-
case 'true-false':
|
|
390
|
-
return (
|
|
391
|
-
<div className="space-y-3">
|
|
392
|
-
{currentQuestion.options?.map((option, index) => (
|
|
393
|
-
<motion.button
|
|
394
|
-
key={index}
|
|
395
|
-
initial={{ opacity: 0, x: -20 }}
|
|
396
|
-
animate={{ opacity: 1, x: 0 }}
|
|
397
|
-
transition={{ delay: index * 0.1 }}
|
|
398
|
-
onClick={() => saveAnswer(option)}
|
|
399
|
-
className={cn(
|
|
400
|
-
'w-full text-left p-4 rounded-lg border transition-colors',
|
|
401
|
-
userAnswer === option
|
|
402
|
-
? 'border-primary bg-primary/10'
|
|
403
|
-
: 'border-border hover:border-primary/50'
|
|
404
|
-
)}
|
|
405
|
-
>
|
|
406
|
-
{option}
|
|
407
|
-
</motion.button>
|
|
408
|
-
))}
|
|
409
|
-
</div>
|
|
410
|
-
);
|
|
411
|
-
|
|
412
|
-
case 'multiple-choice':
|
|
413
|
-
return (
|
|
414
|
-
<div className="space-y-3">
|
|
415
|
-
{currentQuestion.options?.map((option, index) => {
|
|
416
|
-
const selected = Array.isArray(userAnswer) && userAnswer.includes(option);
|
|
417
|
-
return (
|
|
418
|
-
<motion.button
|
|
419
|
-
key={index}
|
|
420
|
-
initial={{ opacity: 0, x: -20 }}
|
|
421
|
-
animate={{ opacity: 1, x: 0 }}
|
|
422
|
-
transition={{ delay: index * 0.1 }}
|
|
423
|
-
onClick={() => {
|
|
424
|
-
const current = (userAnswer || []) as string[];
|
|
425
|
-
const newAnswer = selected
|
|
426
|
-
? current.filter(a => a !== option)
|
|
427
|
-
: [...current, option];
|
|
428
|
-
saveAnswer(newAnswer);
|
|
429
|
-
}}
|
|
430
|
-
className={cn(
|
|
431
|
-
'w-full text-left p-4 rounded-lg border transition-colors',
|
|
432
|
-
selected
|
|
433
|
-
? 'border-primary bg-primary/10'
|
|
434
|
-
: 'border-border hover:border-primary/50'
|
|
435
|
-
)}
|
|
436
|
-
>
|
|
437
|
-
<div className="flex items-center gap-3">
|
|
438
|
-
<div className={cn(
|
|
439
|
-
'w-5 h-5 rounded border-2 transition-colors',
|
|
440
|
-
selected ? 'bg-primary border-primary' : 'border-border'
|
|
441
|
-
)}>
|
|
442
|
-
{selected && (
|
|
443
|
-
<CheckCircle2 className="w-full h-full text-primary-foreground" />
|
|
444
|
-
)}
|
|
445
|
-
</div>
|
|
446
|
-
{option}
|
|
447
|
-
</div>
|
|
448
|
-
</motion.button>
|
|
449
|
-
);
|
|
450
|
-
})}
|
|
451
|
-
</div>
|
|
452
|
-
);
|
|
453
|
-
|
|
454
|
-
case 'text':
|
|
455
|
-
return (
|
|
456
|
-
<motion.div
|
|
457
|
-
initial={{ opacity: 0, y: 20 }}
|
|
458
|
-
animate={{ opacity: 1, y: 0 }}
|
|
459
|
-
>
|
|
460
|
-
<textarea
|
|
461
|
-
className="w-full p-4 rounded-lg border border-input bg-background resize-none"
|
|
462
|
-
rows={4}
|
|
463
|
-
placeholder="Type your answer here..."
|
|
464
|
-
value={(userAnswer as string) || ''}
|
|
465
|
-
onChange={(e) => saveAnswer(e.target.value)}
|
|
466
|
-
/>
|
|
467
|
-
</motion.div>
|
|
468
|
-
);
|
|
469
|
-
|
|
470
|
-
case 'rating':
|
|
471
|
-
const maxRating = currentQuestion.maxRating || 5;
|
|
472
|
-
const currentRating = (typeof userAnswer === 'number' ? userAnswer : 0);
|
|
473
|
-
|
|
474
|
-
return (
|
|
475
|
-
<motion.div
|
|
476
|
-
initial={{ opacity: 0, y: 20 }}
|
|
477
|
-
animate={{ opacity: 1, y: 0 }}
|
|
478
|
-
className="space-y-4"
|
|
479
|
-
>
|
|
480
|
-
<div className="flex justify-center gap-2">
|
|
481
|
-
{Array.from({ length: maxRating }, (_, i) => i + 1).map((rating) => (
|
|
482
|
-
<button
|
|
483
|
-
key={rating}
|
|
484
|
-
onClick={() => saveAnswer(rating)}
|
|
485
|
-
className="transition-transform hover:scale-110"
|
|
486
|
-
>
|
|
487
|
-
<Star
|
|
488
|
-
className={cn(
|
|
489
|
-
'w-10 h-10 transition-colors',
|
|
490
|
-
rating <= currentRating
|
|
491
|
-
? 'fill-yellow-500 text-yellow-500'
|
|
492
|
-
: 'text-muted-foreground'
|
|
493
|
-
)}
|
|
494
|
-
/>
|
|
495
|
-
</button>
|
|
496
|
-
))}
|
|
497
|
-
</div>
|
|
498
|
-
{currentQuestion.ratingLabels && (
|
|
499
|
-
<div className="flex justify-between text-sm text-muted-foreground">
|
|
500
|
-
<span>{currentQuestion.ratingLabels[0]}</span>
|
|
501
|
-
<span>{currentQuestion.ratingLabels[1]}</span>
|
|
502
|
-
</div>
|
|
503
|
-
)}
|
|
504
|
-
</motion.div>
|
|
505
|
-
);
|
|
506
|
-
|
|
507
|
-
case 'matching':
|
|
508
|
-
const matches = (userAnswer || {}) as Record<string, string>;
|
|
509
|
-
|
|
510
|
-
return (
|
|
511
|
-
<div className="grid grid-cols-2 gap-6">
|
|
512
|
-
<div className="space-y-3">
|
|
513
|
-
<h4 className="font-medium mb-2">Match these items:</h4>
|
|
514
|
-
{currentQuestion.leftOptions?.map((left, index) => (
|
|
515
|
-
<motion.div
|
|
516
|
-
key={left}
|
|
517
|
-
initial={{ opacity: 0, x: -20 }}
|
|
518
|
-
animate={{ opacity: 1, x: 0 }}
|
|
519
|
-
transition={{ delay: index * 0.1 }}
|
|
520
|
-
className="p-3 rounded-lg border bg-muted/50"
|
|
521
|
-
>
|
|
522
|
-
{left}
|
|
523
|
-
</motion.div>
|
|
524
|
-
))}
|
|
525
|
-
</div>
|
|
526
|
-
|
|
527
|
-
<div className="space-y-3">
|
|
528
|
-
<h4 className="font-medium mb-2">With these:</h4>
|
|
529
|
-
{currentQuestion.leftOptions?.map((left) => (
|
|
530
|
-
<motion.div
|
|
531
|
-
key={left}
|
|
532
|
-
initial={{ opacity: 0, x: 20 }}
|
|
533
|
-
animate={{ opacity: 1, x: 0 }}
|
|
534
|
-
>
|
|
535
|
-
<select
|
|
536
|
-
className="w-full p-3 rounded-lg border bg-background"
|
|
537
|
-
value={matches[left] || ''}
|
|
538
|
-
onChange={(e) => saveAnswer({ ...matches, [left]: e.target.value })}
|
|
539
|
-
>
|
|
540
|
-
<option value="">Select...</option>
|
|
541
|
-
{currentQuestion.rightOptions?.map((right) => (
|
|
542
|
-
<option key={right} value={right}>
|
|
543
|
-
{right}
|
|
544
|
-
</option>
|
|
545
|
-
))}
|
|
546
|
-
</select>
|
|
547
|
-
</motion.div>
|
|
548
|
-
))}
|
|
549
|
-
</div>
|
|
550
|
-
</div>
|
|
551
|
-
);
|
|
552
|
-
|
|
553
|
-
default:
|
|
554
|
-
return null;
|
|
555
|
-
}
|
|
556
|
-
};
|
|
557
|
-
|
|
558
|
-
// Sonuçları göster
|
|
559
|
-
if (showResults && quizResult) {
|
|
560
|
-
return (
|
|
561
|
-
<div ref={ref} className={cn(quizFormVariants({ variant, size }), className)} {...props}>
|
|
562
|
-
<motion.div
|
|
563
|
-
initial={{ opacity: 0, scale: 0.95 }}
|
|
564
|
-
animate={{ opacity: 1, scale: 1 }}
|
|
565
|
-
className="space-y-6"
|
|
566
|
-
>
|
|
567
|
-
<div className="text-center">
|
|
568
|
-
<h2 className="text-2xl font-bold mb-2">Quiz Completed!</h2>
|
|
569
|
-
<div className="text-5xl font-bold mb-4">
|
|
570
|
-
{quizResult.percentage.toFixed(1)}%
|
|
571
|
-
</div>
|
|
572
|
-
<p className="text-muted-foreground">
|
|
573
|
-
You scored {quizResult.score} out of {quizResult.totalScore} points
|
|
574
|
-
</p>
|
|
575
|
-
{quizResult.passed !== undefined && (
|
|
576
|
-
<div className={cn(
|
|
577
|
-
'mt-4 inline-flex items-center gap-2 px-4 py-2 rounded-full',
|
|
578
|
-
quizResult.passed ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700'
|
|
579
|
-
)}>
|
|
580
|
-
{quizResult.passed ? (
|
|
581
|
-
<>
|
|
582
|
-
<CheckCircle2 className="w-5 h-5" />
|
|
583
|
-
Passed
|
|
584
|
-
</>
|
|
585
|
-
) : (
|
|
586
|
-
<>
|
|
587
|
-
<XCircle className="w-5 h-5" />
|
|
588
|
-
Failed
|
|
589
|
-
</>
|
|
590
|
-
)}
|
|
591
|
-
</div>
|
|
592
|
-
)}
|
|
593
|
-
</div>
|
|
594
|
-
|
|
595
|
-
{settings.allowReview && (
|
|
596
|
-
<div className="space-y-4">
|
|
597
|
-
<h3 className="font-semibold">Review Answers:</h3>
|
|
598
|
-
{quizResult.answers.map((answer, index) => {
|
|
599
|
-
const question = questions.find(q => q.id === answer.questionId);
|
|
600
|
-
if (!question) return null;
|
|
601
|
-
|
|
602
|
-
return (
|
|
603
|
-
<div key={answer.questionId} className="p-4 rounded-lg border bg-muted/50">
|
|
604
|
-
<div className="flex items-start gap-3">
|
|
605
|
-
{answer.isCorrect ? (
|
|
606
|
-
<CheckCircle2 className="w-5 h-5 text-green-600 mt-0.5" />
|
|
607
|
-
) : (
|
|
608
|
-
<XCircle className="w-5 h-5 text-red-600 mt-0.5" />
|
|
609
|
-
)}
|
|
610
|
-
<div className="flex-1">
|
|
611
|
-
<p className="font-medium mb-2">
|
|
612
|
-
{index + 1}. {question.question}
|
|
613
|
-
</p>
|
|
614
|
-
<p className="text-sm text-muted-foreground">
|
|
615
|
-
Your answer: {JSON.stringify(answer.userAnswer)}
|
|
616
|
-
</p>
|
|
617
|
-
{!answer.isCorrect && question.correctAnswer !== undefined && (
|
|
618
|
-
<p className="text-sm text-green-600 mt-1">
|
|
619
|
-
Correct answer: {JSON.stringify(question.correctAnswer)}
|
|
620
|
-
</p>
|
|
621
|
-
)}
|
|
622
|
-
{question.explanation && (
|
|
623
|
-
<p className="text-sm text-muted-foreground mt-2">
|
|
624
|
-
{question.explanation}
|
|
625
|
-
</p>
|
|
626
|
-
)}
|
|
627
|
-
</div>
|
|
628
|
-
</div>
|
|
629
|
-
</div>
|
|
630
|
-
);
|
|
631
|
-
})}
|
|
632
|
-
</div>
|
|
633
|
-
)}
|
|
634
|
-
|
|
635
|
-
<div className="flex gap-3 justify-center">
|
|
636
|
-
{(!settings.maxAttempts || attempts < settings.maxAttempts) && (
|
|
637
|
-
<button
|
|
638
|
-
onClick={handleRestart}
|
|
639
|
-
className="inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-primary text-primary-foreground hover:bg-primary/90"
|
|
640
|
-
>
|
|
641
|
-
<RotateCcw className="w-4 h-4" />
|
|
642
|
-
Try Again
|
|
643
|
-
</button>
|
|
644
|
-
)}
|
|
645
|
-
<button
|
|
646
|
-
onClick={handleDownloadResults}
|
|
647
|
-
className="inline-flex items-center gap-2 px-4 py-2 rounded-lg border hover:bg-muted"
|
|
648
|
-
>
|
|
649
|
-
<Download className="w-4 h-4" />
|
|
650
|
-
Download Results
|
|
651
|
-
</button>
|
|
652
|
-
</div>
|
|
653
|
-
</motion.div>
|
|
654
|
-
</div>
|
|
655
|
-
);
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
return (
|
|
659
|
-
<div ref={ref} className={cn(quizFormVariants({ variant, size }), className)} {...props}>
|
|
660
|
-
{/* Header */}
|
|
661
|
-
{(title || timeLeft !== null) && (
|
|
662
|
-
<div className="mb-6 flex items-center justify-between">
|
|
663
|
-
<div>
|
|
664
|
-
{title && <h2 className="text-2xl font-bold">{title}</h2>}
|
|
665
|
-
{description && <p className="text-muted-foreground mt-1">{description}</p>}
|
|
666
|
-
</div>
|
|
667
|
-
{timeLeft !== null && (
|
|
668
|
-
<div className={cn(
|
|
669
|
-
'flex items-center gap-2 px-3 py-1 rounded-full text-sm font-medium',
|
|
670
|
-
timeLeft < 60 ? 'bg-red-100 text-red-700' : 'bg-muted'
|
|
671
|
-
)}>
|
|
672
|
-
<Clock className="w-4 h-4" />
|
|
673
|
-
{formatTime(timeLeft)}
|
|
674
|
-
</div>
|
|
675
|
-
)}
|
|
676
|
-
</div>
|
|
677
|
-
)}
|
|
678
|
-
|
|
679
|
-
{/* Progress bar */}
|
|
680
|
-
<div className="mb-6">
|
|
681
|
-
<div className="flex items-center justify-between text-sm text-muted-foreground mb-2">
|
|
682
|
-
<span>Question {currentQuestionIndex + 1} of {questions.length}</span>
|
|
683
|
-
<span>{Math.round(((currentQuestionIndex + 1) / questions.length) * 100)}%</span>
|
|
684
|
-
</div>
|
|
685
|
-
<div className="h-2 bg-muted rounded-full overflow-hidden">
|
|
686
|
-
<motion.div
|
|
687
|
-
className="h-full bg-primary"
|
|
688
|
-
initial={{ width: 0 }}
|
|
689
|
-
animate={{ width: `${((currentQuestionIndex + 1) / questions.length) * 100}%` }}
|
|
690
|
-
transition={{ duration: 0.3 }}
|
|
691
|
-
/>
|
|
692
|
-
</div>
|
|
693
|
-
</div>
|
|
694
|
-
|
|
695
|
-
{/* Question */}
|
|
696
|
-
<AnimatePresence mode="wait">
|
|
697
|
-
{currentQuestion && (
|
|
698
|
-
<motion.div
|
|
699
|
-
key={currentQuestion.id}
|
|
700
|
-
initial={{ opacity: 0, x: 20 }}
|
|
701
|
-
animate={{ opacity: 1, x: 0 }}
|
|
702
|
-
exit={{ opacity: 0, x: -20 }}
|
|
703
|
-
className="mb-6"
|
|
704
|
-
>
|
|
705
|
-
<h3 className="text-lg font-semibold mb-4">
|
|
706
|
-
{currentQuestion.question}
|
|
707
|
-
{currentQuestion.required && (
|
|
708
|
-
<span className="text-red-500 ml-1">*</span>
|
|
709
|
-
)}
|
|
710
|
-
</h3>
|
|
711
|
-
|
|
712
|
-
{renderQuestion()}
|
|
713
|
-
|
|
714
|
-
{/* Hint */}
|
|
715
|
-
{settings.showHints && currentQuestion.hint && (
|
|
716
|
-
<div className="mt-4">
|
|
717
|
-
<button
|
|
718
|
-
onClick={() => setShowHint(!showHint)}
|
|
719
|
-
className="inline-flex items-center gap-2 text-sm text-muted-foreground hover:text-foreground"
|
|
720
|
-
>
|
|
721
|
-
<HelpCircle className="w-4 h-4" />
|
|
722
|
-
{showHint ? 'Hide' : 'Show'} Hint
|
|
723
|
-
</button>
|
|
724
|
-
{showHint && (
|
|
725
|
-
<motion.div
|
|
726
|
-
initial={{ opacity: 0, height: 0 }}
|
|
727
|
-
animate={{ opacity: 1, height: 'auto' }}
|
|
728
|
-
className="mt-2 p-3 rounded-lg bg-muted/50 text-sm"
|
|
729
|
-
>
|
|
730
|
-
{currentQuestion.hint}
|
|
731
|
-
</motion.div>
|
|
732
|
-
)}
|
|
733
|
-
</div>
|
|
734
|
-
)}
|
|
735
|
-
|
|
736
|
-
{/* Instant feedback */}
|
|
737
|
-
{settings.showInstantFeedback && userAnswers[currentQuestion.id] && currentQuestion.correctAnswer !== undefined && (
|
|
738
|
-
<motion.div
|
|
739
|
-
initial={{ opacity: 0, y: 10 }}
|
|
740
|
-
animate={{ opacity: 1, y: 0 }}
|
|
741
|
-
className="mt-4"
|
|
742
|
-
>
|
|
743
|
-
{(() => {
|
|
744
|
-
const userAnswer = userAnswers[currentQuestion.id].answer;
|
|
745
|
-
let isCorrect = false;
|
|
746
|
-
|
|
747
|
-
// Doğruluğu kontrol et
|
|
748
|
-
switch (currentQuestion.type) {
|
|
749
|
-
case 'single-choice':
|
|
750
|
-
case 'true-false':
|
|
751
|
-
isCorrect = userAnswer === currentQuestion.correctAnswer;
|
|
752
|
-
break;
|
|
753
|
-
// Diğer tipler için kontrol eklenebilir
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
return (
|
|
757
|
-
<div className={cn(
|
|
758
|
-
'p-3 rounded-lg flex items-start gap-2',
|
|
759
|
-
isCorrect ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700'
|
|
760
|
-
)}>
|
|
761
|
-
{isCorrect ? (
|
|
762
|
-
<CheckCircle2 className="w-5 h-5 mt-0.5" />
|
|
763
|
-
) : (
|
|
764
|
-
<XCircle className="w-5 h-5 mt-0.5" />
|
|
765
|
-
)}
|
|
766
|
-
<div>
|
|
767
|
-
<p className="font-medium">{isCorrect ? 'Correct!' : 'Incorrect'}</p>
|
|
768
|
-
{currentQuestion.explanation && (
|
|
769
|
-
<p className="text-sm mt-1">{currentQuestion.explanation}</p>
|
|
770
|
-
)}
|
|
771
|
-
</div>
|
|
772
|
-
</div>
|
|
773
|
-
);
|
|
774
|
-
})()}
|
|
775
|
-
</motion.div>
|
|
776
|
-
)}
|
|
777
|
-
</motion.div>
|
|
778
|
-
)}
|
|
779
|
-
</AnimatePresence>
|
|
780
|
-
|
|
781
|
-
{/* Navigation */}
|
|
782
|
-
<div className="flex items-center justify-between">
|
|
783
|
-
<button
|
|
784
|
-
onClick={handlePrevious}
|
|
785
|
-
disabled={currentQuestionIndex === 0}
|
|
786
|
-
className={cn(
|
|
787
|
-
'inline-flex items-center gap-2 px-4 py-2 rounded-lg transition-colors',
|
|
788
|
-
currentQuestionIndex === 0
|
|
789
|
-
? 'opacity-50 cursor-not-allowed bg-muted text-muted-foreground'
|
|
790
|
-
: 'bg-muted hover:bg-muted/80'
|
|
791
|
-
)}
|
|
792
|
-
>
|
|
793
|
-
<ChevronLeft className="w-4 h-4" />
|
|
794
|
-
Previous
|
|
795
|
-
</button>
|
|
796
|
-
|
|
797
|
-
<button
|
|
798
|
-
onClick={currentQuestionIndex === questions.length - 1 ? handleComplete : handleNext}
|
|
799
|
-
disabled={currentQuestion?.required && !userAnswers[currentQuestion.id]}
|
|
800
|
-
className={cn(
|
|
801
|
-
'inline-flex items-center gap-2 px-4 py-2 rounded-lg transition-colors',
|
|
802
|
-
currentQuestion?.required && !userAnswers[currentQuestion.id]
|
|
803
|
-
? 'opacity-50 cursor-not-allowed bg-muted text-muted-foreground'
|
|
804
|
-
: 'bg-primary text-primary-foreground hover:bg-primary/90'
|
|
805
|
-
)}
|
|
806
|
-
>
|
|
807
|
-
{currentQuestionIndex === questions.length - 1 ? 'Complete' : 'Next'}
|
|
808
|
-
<ChevronRight className="w-4 h-4" />
|
|
809
|
-
</button>
|
|
810
|
-
</div>
|
|
811
|
-
</div>
|
|
812
|
-
);
|
|
813
|
-
});
|
|
814
|
-
|
|
815
|
-
MoonUIQuizFormPro.displayName = 'MoonUIQuizFormPro';
|
|
816
|
-
|
|
817
|
-
export default MoonUIQuizFormPro;
|