ghost613 commited on
Commit
9515c98
·
verified ·
1 Parent(s): e40da14

Upload 20 files

Browse files
Dockerfile ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use Node.js image
2
+ FROM node:18-alpine
3
+
4
+ # Create app directory
5
+ WORKDIR /app
6
+
7
+ # Install app dependencies
8
+ COPY package*.json ./
9
+ RUN npm install
10
+
11
+ # Bundle app source
12
+ COPY . .
13
+
14
+ # Build the app
15
+ RUN npm run build
16
+
17
+ # Expose port
18
+ EXPOSE 3000
19
+
20
+ # Start the app
21
+ CMD ["npm", "start"]
README.md CHANGED
@@ -1,12 +1,45 @@
1
  ---
2
- title: math-quiz
3
- emoji: 🐳
4
- colorFrom: green
5
  colorTo: red
6
- sdk: static
7
  pinned: false
8
- tags:
9
- - deepsite
10
  ---
 
11
 
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: Math Quiz App
3
+ emoji: 🧮
4
+ colorFrom: blue
5
  colorTo: red
6
+ sdk: docker
7
  pinned: false
8
+ app_port: 3000
 
9
  ---
10
+ This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
11
 
12
+ ## Getting Started
13
+
14
+ First, run the development server:
15
+
16
+ ```bash
17
+ npm run dev
18
+ # or
19
+ yarn dev
20
+ # or
21
+ pnpm dev
22
+ # or
23
+ bun dev
24
+ ```
25
+
26
+ Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
27
+
28
+ You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
29
+
30
+ This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
31
+
32
+ ## Learn More
33
+
34
+ To learn more about Next.js, take a look at the following resources:
35
+
36
+ - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
37
+ - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
38
+
39
+ You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
40
+
41
+ ## Deploy on Vercel
42
+
43
+ The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
44
+
45
+ Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
app/favicon.ico ADDED
app/globals.css ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
app/layout.tsx ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import type { Metadata } from "next";
2
+ import "./globals.css";
3
+
4
+ export const metadata: Metadata = {
5
+ title: "Math Quiz App",
6
+ description: "A simple math quiz application",
7
+ };
8
+
9
+ export default function RootLayout({
10
+ children,
11
+ }: Readonly<{
12
+ children: React.ReactNode;
13
+ }>) {
14
+ return (
15
+ <html lang="en">
16
+ <body>{children}</body>
17
+ </html>
18
+ );
19
+ }
app/page.tsx ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import MathQuizApp from "@/components/MathQuizApp";
2
+
3
+ export default function Home() {
4
+ return (
5
+ <main className="min-h-screen bg-gray-100">
6
+ <MathQuizApp />
7
+ </main>
8
+ );
9
+ }
components/MathQuizApp.tsx ADDED
@@ -0,0 +1,291 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use client';
2
+
3
+ import { useState } from 'react';
4
+ import { Check, X, Calculator } from 'lucide-react';
5
+
6
+ const operations = ['+', '-', '*', '/'] as const;
7
+ type Operation = typeof operations[number];
8
+
9
+ const difficulties = ['easy', 'medium', 'hard'] as const;
10
+ type Difficulty = typeof difficulties[number];
11
+
12
+ interface Question {
13
+ num1: number;
14
+ num2: number;
15
+ operation: Operation;
16
+ answer: number;
17
+ }
18
+
19
+ const generateQuestion = (difficulty: Difficulty): Question => {
20
+ const operation = operations[Math.floor(Math.random() * operations.length)];
21
+ let num1 = 0;
22
+ let num2 = 0;
23
+
24
+ switch (difficulty) {
25
+ case 'easy':
26
+ num1 = Math.floor(Math.random() * 10) + 1;
27
+ num2 = Math.floor(Math.random() * 10) + 1;
28
+ break;
29
+ case 'medium':
30
+ num1 = Math.floor(Math.random() * 50) + 1;
31
+ num2 = Math.floor(Math.random() * 50) + 1;
32
+ break;
33
+ case 'hard':
34
+ num1 = Math.floor(Math.random() * 100) + 1;
35
+ num2 = Math.floor(Math.random() * 100) + 1;
36
+ break;
37
+ }
38
+
39
+ // For division, ensure it results in a whole number or simpler decimal
40
+ if (operation === '/') {
41
+ // Make sure num2 is not zero to avoid division by zero
42
+ num2 = num2 === 0 ? 1 : num2;
43
+ // For easier difficulty levels, try to make divisions that result in whole numbers
44
+ if (difficulty === 'easy' || difficulty === 'medium') {
45
+ num1 = num2 * Math.floor(Math.random() * 10) + 1;
46
+ }
47
+ }
48
+
49
+ const answer = eval(`${num1} ${operation} ${num2}`);
50
+ // Round the answer to 2 decimal places if it's a float
51
+ const roundedAnswer = Number.isInteger(answer) ? answer : parseFloat(answer.toFixed(2));
52
+
53
+ return {
54
+ num1,
55
+ num2,
56
+ operation,
57
+ answer: roundedAnswer,
58
+ };
59
+ };
60
+
61
+ interface CalculatorComponentProps {
62
+ onClose: () => void;
63
+ }
64
+
65
+ const CalculatorComponent: React.FC<CalculatorComponentProps> = ({ onClose }) => {
66
+ const [display, setDisplay] = useState('0');
67
+ const [firstOperand, setFirstOperand] = useState<number | null>(null);
68
+ const [operator, setOperator] = useState<string | null>(null);
69
+ const [waitingForSecondOperand, setWaitingForSecondOperand] = useState(false);
70
+
71
+ const inputDigit = (digit: string) => {
72
+ if (waitingForSecondOperand) {
73
+ setDisplay(digit);
74
+ setWaitingForSecondOperand(false);
75
+ } else {
76
+ setDisplay(display === '0' ? digit : display + digit);
77
+ }
78
+ };
79
+
80
+ const inputDecimal = () => {
81
+ if (waitingForSecondOperand) {
82
+ setDisplay('0.');
83
+ setWaitingForSecondOperand(false);
84
+ return;
85
+ }
86
+ if (!display.includes('.')) {
87
+ setDisplay(display + '.');
88
+ }
89
+ };
90
+
91
+ const clear = () => {
92
+ setDisplay('0');
93
+ setFirstOperand(null);
94
+ setOperator(null);
95
+ setWaitingForSecondOperand(false);
96
+ };
97
+
98
+ const calculate = (firstOp: number, secondOp: number, op: string): number | string => {
99
+ switch (op) {
100
+ case '+': return firstOp + secondOp;
101
+ case '-': return firstOp - secondOp;
102
+ case '*': return firstOp * secondOp;
103
+ case '/': return secondOp === 0 ? 'Error' : firstOp / secondOp;
104
+ default: return secondOp;
105
+ }
106
+ };
107
+
108
+ const performOperation = (nextOperator: string) => {
109
+ const inputValue = parseFloat(display);
110
+
111
+ if (nextOperator === '=') {
112
+ if (operator && firstOperand !== null) {
113
+ const result = calculate(firstOperand, inputValue, operator);
114
+ setDisplay(String(result));
115
+ setFirstOperand(null);
116
+ setOperator(null);
117
+ setWaitingForSecondOperand(false);
118
+ }
119
+ } else {
120
+ if (firstOperand === null) {
121
+ setFirstOperand(inputValue);
122
+ } else if (operator) {
123
+ const result = calculate(firstOperand, inputValue, operator);
124
+ setDisplay(String(result));
125
+ setFirstOperand(typeof result === 'number' ? result : null);
126
+ }
127
+
128
+ setWaitingForSecondOperand(true);
129
+ setOperator(nextOperator);
130
+ }
131
+ };
132
+
133
+ return (
134
+ <div className="bg-gray-100 p-4 rounded-lg shadow-lg w-full max-w-md">
135
+ <div className="flex justify-between items-center mb-4">
136
+ <h2 className="text-xl font-bold">Calculator</h2>
137
+ <button onClick={onClose} className="text-gray-600 hover:text-gray-800">
138
+ <X className="w-6 h-6" />
139
+ </button>
140
+ </div>
141
+ <div className="bg-gray-200 p-4 rounded mb-4 text-right text-2xl font-bold">
142
+ {display}
143
+ </div>
144
+ <div className="grid grid-cols-4 gap-2">
145
+ <button onClick={clear} className="p-3 bg-gray-300 hover:bg-gray-400 rounded">C</button>
146
+ <button onClick={() => performOperation('/')} className="p-3 bg-gray-300 hover:bg-gray-400 rounded">/</button>
147
+ <button onClick={() => performOperation('*')} className="p-3 bg-gray-300 hover:bg-gray-400 rounded">*</button>
148
+ <button onClick={() => performOperation('-')} className="p-3 bg-gray-300 hover:bg-gray-400 rounded">-</button>
149
+
150
+ <button onClick={() => inputDigit('7')} className="p-3 bg-white hover:bg-gray-100 rounded">7</button>
151
+ <button onClick={() => inputDigit('8')} className="p-3 bg-white hover:bg-gray-100 rounded">8</button>
152
+ <button onClick={() => inputDigit('9')} className="p-3 bg-white hover:bg-gray-100 rounded">9</button>
153
+ <button onClick={() => performOperation('+')} className="p-3 bg-gray-300 hover:bg-gray-400 rounded">+</button>
154
+
155
+ <button onClick={() => inputDigit('4')} className="p-3 bg-white hover:bg-gray-100 rounded">4</button>
156
+ <button onClick={() => inputDigit('5')} className="p-3 bg-white hover:bg-gray-100 rounded">5</button>
157
+ <button onClick={() => inputDigit('6')} className="p-3 bg-white hover:bg-gray-100 rounded">6</button>
158
+ <button onClick={() => performOperation('=')} className="p-3 bg-blue-500 hover:bg-blue-600 text-white rounded row-span-2">=</button>
159
+
160
+ <button onClick={() => inputDigit('1')} className="p-3 bg-white hover:bg-gray-100 rounded">1</button>
161
+ <button onClick={() => inputDigit('2')} className="p-3 bg-white hover:bg-gray-100 rounded">2</button>
162
+ <button onClick={() => inputDigit('3')} className="p-3 bg-white hover:bg-gray-100 rounded">3</button>
163
+
164
+ <button onClick={() => inputDigit('0')} className="p-3 bg-white hover:bg-gray-100 rounded col-span-2">0</button>
165
+ <button onClick={inputDecimal} className="p-3 bg-white hover:bg-gray-100 rounded">.</button>
166
+ </div>
167
+ </div>
168
+ );
169
+ };
170
+
171
+ const MathQuizApp: React.FC = () => {
172
+ const [question, setQuestion] = useState<Question>(generateQuestion('easy'));
173
+ const [userAnswer, setUserAnswer] = useState('');
174
+ const [result, setResult] = useState('');
175
+ const [score, setScore] = useState(0);
176
+ const [wrongAnswers, setWrongAnswers] = useState(0);
177
+ const [difficulty, setDifficulty] = useState<Difficulty>('easy');
178
+ const [showCalculator, setShowCalculator] = useState(false);
179
+
180
+ const handleSubmit = (e: React.FormEvent) => {
181
+ e.preventDefault();
182
+
183
+ // Parse the user answer as a float and check against the question answer
184
+ const parsedAnswer = parseFloat(userAnswer);
185
+
186
+ // Check if the answer matches (with some tolerance for floating point)
187
+ const isCorrect = Math.abs(parsedAnswer - question.answer) < 0.001;
188
+
189
+ if (isCorrect) {
190
+ setResult('Correct!');
191
+ setScore(score + 1);
192
+ } else {
193
+ setResult(`Incorrect. The correct answer is ${question.answer}.`);
194
+ setWrongAnswers(wrongAnswers + 1);
195
+ }
196
+
197
+ setUserAnswer('');
198
+ setQuestion(generateQuestion(difficulty));
199
+ };
200
+
201
+ const handleDifficultyChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
202
+ const newDifficulty = e.target.value as Difficulty;
203
+ setDifficulty(newDifficulty);
204
+ setQuestion(generateQuestion(newDifficulty));
205
+ setScore(0);
206
+ setWrongAnswers(0);
207
+ setResult('');
208
+ };
209
+
210
+ return (
211
+ <div className="bg-gray-50 min-h-screen flex items-center justify-center p-4">
212
+ <div className="max-w-4xl w-full mx-auto bg-white rounded-lg shadow-md p-8">
213
+ <div className="flex justify-between items-center mb-6">
214
+ <h1 className="text-4xl font-bold">Math Quiz App</h1>
215
+ <button
216
+ onClick={() => setShowCalculator(!showCalculator)}
217
+ className="flex items-center gap-2 bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg"
218
+ >
219
+ <Calculator className="w-5 h-5" />
220
+ {showCalculator ? 'Hide Calculator' : 'Show Calculator'}
221
+ </button>
222
+ </div>
223
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-8">
224
+ <div className="space-y-6">
225
+ <form onSubmit={handleSubmit} className="space-y-6">
226
+ <div className="flex flex-col items-center gap-6">
227
+ <div className="flex gap-8 items-center justify-center">
228
+ <div className="text-xl font-bold">Correct: {score}</div>
229
+ <div className="text-xl font-bold text-red-500">Wrong: {wrongAnswers}</div>
230
+ </div>
231
+ <select
232
+ value={difficulty}
233
+ onChange={handleDifficultyChange}
234
+ className="bg-gray-50 border border-gray-300 text-gray-900 text-lg rounded-lg focus:ring-blue-500 focus:border-blue-500 p-2.5 w-48 text-center"
235
+ >
236
+ {difficulties.map((diff) => (
237
+ <option key={diff} value={diff}>
238
+ {diff.charAt(0).toUpperCase() + diff.slice(1)}
239
+ </option>
240
+ ))}
241
+ </select>
242
+ </div>
243
+ <div className="text-center">
244
+ <p className="text-3xl font-bold mb-6">
245
+ What is {question.num1} {question.operation} {question.num2}?
246
+ </p>
247
+ <input
248
+ type="number"
249
+ value={userAnswer}
250
+ onChange={(e) => setUserAnswer(e.target.value)}
251
+ className="bg-gray-50 border border-gray-300 text-gray-900 text-xl rounded-lg focus:ring-blue-500 focus:border-blue-500 p-4 w-full text-center"
252
+ step="any"
253
+ />
254
+ </div>
255
+ <div className="text-center">
256
+ <button
257
+ type="submit"
258
+ className="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-lg px-8 py-3 text-center"
259
+ >
260
+ Submit
261
+ </button>
262
+ </div>
263
+ {result && (
264
+ <p
265
+ className={`text-xl font-bold text-center ${
266
+ result === 'Correct!' ? 'text-green-500' : 'text-red-500'
267
+ }`}
268
+ >
269
+ {result}{' '}
270
+ {result === 'Correct!' ? (
271
+ <Check className="inline-block w-6 h-6" />
272
+ ) : (
273
+ <X className="inline-block w-6 h-6" />
274
+ )}
275
+ </p>
276
+ )}
277
+ </form>
278
+ </div>
279
+
280
+ {showCalculator && (
281
+ <div className="flex justify-center">
282
+ <CalculatorComponent onClose={() => setShowCalculator(false)} />
283
+ </div>
284
+ )}
285
+ </div>
286
+ </div>
287
+ </div>
288
+ );
289
+ };
290
+
291
+ export default MathQuizApp;
eslint.config.mjs ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { dirname } from "path";
2
+ import { fileURLToPath } from "url";
3
+ import { FlatCompat } from "@eslint/eslintrc";
4
+
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = dirname(__filename);
7
+
8
+ const compat = new FlatCompat({
9
+ baseDirectory: __dirname,
10
+ });
11
+
12
+ const eslintConfig = [
13
+ ...compat.extends("next/core-web-vitals", "next/typescript"),
14
+ ];
15
+
16
+ export default eslintConfig;
next-env.d.ts ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ /// <reference types="next" />
2
+ /// <reference types="next/image-types/global" />
3
+
4
+ // NOTE: This file should not be edited
5
+ // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
next.config.ts ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ import type { NextConfig } from "next";
2
+
3
+ const nextConfig: NextConfig = {
4
+ /* config options here */
5
+ };
6
+
7
+ export default nextConfig;
package-lock.json ADDED
The diff for this file is too large to render. See raw diff
 
package.json ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "math-quiz",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "dev": "next dev --turbopack",
7
+ "build": "next build",
8
+ "start": "next start",
9
+ "lint": "next lint"
10
+ },
11
+ "dependencies": {
12
+ "lucide-react": "^0.469.0",
13
+ "next": "15.1.3",
14
+ "react": "^19.0.0",
15
+ "react-dom": "^19.0.0"
16
+ },
17
+ "devDependencies": {
18
+ "@eslint/eslintrc": "^3",
19
+ "@types/node": "^20",
20
+ "@types/react": "^19",
21
+ "@types/react-dom": "^19",
22
+ "eslint": "^9",
23
+ "eslint-config-next": "15.1.3",
24
+ "postcss": "^8",
25
+ "tailwindcss": "^3.4.1",
26
+ "typescript": "^5"
27
+ }
28
+ }
postcss.config.mjs ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ /** @type {import('postcss-load-config').Config} */
2
+ const config = {
3
+ plugins: {
4
+ tailwindcss: {},
5
+ },
6
+ };
7
+
8
+ export default config;
public/file.svg ADDED
public/globe.svg ADDED
public/next.svg ADDED
public/vercel.svg ADDED
public/window.svg ADDED
tailwind.config.ts ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import type { Config } from "tailwindcss";
2
+
3
+ export default {
4
+ content: [
5
+ "./pages/**/*.{js,ts,jsx,tsx,mdx}",
6
+ "./components/**/*.{js,ts,jsx,tsx,mdx}",
7
+ "./app/**/*.{js,ts,jsx,tsx,mdx}",
8
+ ],
9
+ theme: {
10
+ extend: {
11
+ colors: {
12
+ background: "var(--background)",
13
+ foreground: "var(--foreground)",
14
+ },
15
+ },
16
+ },
17
+ plugins: [],
18
+ } satisfies Config;
tsconfig.json ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2017",
4
+ "lib": ["dom", "dom.iterable", "esnext"],
5
+ "allowJs": true,
6
+ "skipLibCheck": true,
7
+ "strict": true,
8
+ "noEmit": true,
9
+ "esModuleInterop": true,
10
+ "module": "esnext",
11
+ "moduleResolution": "bundler",
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "jsx": "preserve",
15
+ "incremental": true,
16
+ "plugins": [
17
+ {
18
+ "name": "next"
19
+ }
20
+ ],
21
+ "paths": {
22
+ "@/*": ["./*"]
23
+ }
24
+ },
25
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
26
+ "exclude": ["node_modules"]
27
+ }