Compare commits
66 commits
initapiser
...
main
Author | SHA1 | Date | |
---|---|---|---|
28b3f8a0b0 | |||
6367c690a4 | |||
2f6981f4f6 | |||
089d1c8f14 | |||
|
1e767da34c | ||
|
6839343269 | ||
d0fe776b68 | |||
6761a2b0d0 | |||
5500da1f6d | |||
|
10da00708a | ||
92f0d25cd0 | |||
|
bcb098218b | ||
b7b1fca1bc | |||
|
62b44acbdf | ||
5adfd1ffc1 | |||
|
10b3e4e71c | ||
6741f0de18 | |||
|
e8b8e5b800 | ||
e9ee6c0b69 | |||
|
0066c9f343 | ||
3d1ba5cfce | |||
6fa4bf8bdd | |||
28eeb31054 | |||
7dd3d263e9 | |||
ba0b31d0d2 | |||
|
f042f07cf9 | ||
|
64e1aa295a | ||
|
9e07238ddc | ||
|
a3dc222184 | ||
|
b4fed6e3c9 | ||
af2c5afa95 | |||
0d4d1f0944 | |||
c71f5cd9a6 | |||
328b304b6c | |||
dcd44670df | |||
1c6c5327c3 | |||
|
48efcce544 | ||
|
6557cfba89 | ||
2bf3ae95a2 | |||
1c4aa2f744 | |||
a433af3f81 | |||
|
04e0168ea0 | ||
|
2743ce75d5 | ||
a072d2f04c | |||
|
ee5ccde234 | ||
47e47d0cb4 | |||
43db39291e | |||
|
3b362da028 | ||
|
2851e60c78 | ||
|
1fbc541a5d | ||
b17f45c459 | |||
fffda90529 | |||
|
effc06eb0f | ||
7d76207057 | |||
3282da41ed | |||
|
42baa432ad | ||
|
58aa8a09c6 | ||
fb25c8477c | |||
|
2530acdcf1 | ||
4a98209021 | |||
|
fda1fae418 | ||
|
4895d3b889 | ||
|
ff81a070cf | ||
819760817e | |||
8281d66939 | |||
c76d1b3635 |
|
@ -1,3 +1,7 @@
|
||||||
{
|
{
|
||||||
"extends": "next/core-web-vitals"
|
"extends": "next/core-web-vitals",
|
||||||
|
"rules": {
|
||||||
|
"react/no-unescaped-entities": "off",
|
||||||
|
"react/jsx-key": "off"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
1
.gitignore
vendored
|
@ -34,3 +34,4 @@ yarn-error.log*
|
||||||
# typescript
|
# typescript
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
next-env.d.ts
|
next-env.d.ts
|
||||||
|
app/test/*
|
||||||
|
|
1
.nvmrc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
v20.12.2
|
40
README.md
|
@ -1,36 +1,10 @@
|
||||||
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
# ***FlashLearn***
|
||||||
|
> The React based app with flashcards for you to learn. Made for the INFOMATRIX 2024 Hackathon.
|
||||||
|
|
||||||
## Getting Started
|
**How to use:**
|
||||||
|
1. **Click the "Get Started" Button.**
|
||||||
|
2. **Select a subject.**
|
||||||
|
3. ***Learn!***
|
||||||
|
|
||||||
First, run the development server:
|
*Made with the MIT Licence*
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run dev
|
|
||||||
# or
|
|
||||||
yarn dev
|
|
||||||
# or
|
|
||||||
pnpm dev
|
|
||||||
# or
|
|
||||||
bun dev
|
|
||||||
```
|
|
||||||
|
|
||||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
|
||||||
|
|
||||||
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
|
||||||
|
|
||||||
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
|
|
||||||
|
|
||||||
## Learn More
|
|
||||||
|
|
||||||
To learn more about Next.js, take a look at the following resources:
|
|
||||||
|
|
||||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
|
||||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
|
||||||
|
|
||||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
|
|
||||||
|
|
||||||
## Deploy on Vercel
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
`json-server db.json`
|
# Start the server:
|
||||||
|
`json-server db.json -p 4000`
|
||||||
|
# Question endpoint:
|
||||||
`http://localhost:3000/questions`
|
`http://localhost:3000/questions`
|
||||||
|
# Example:
|
||||||
`http://localhost:3000/questions/1`
|
`http://localhost:3000/questions/1`
|
730
api/db.json
|
@ -1,75 +1,705 @@
|
||||||
{
|
{
|
||||||
"questions": [
|
"questions": [
|
||||||
{
|
{
|
||||||
"id": "1",
|
"id": 1,
|
||||||
"category": "C++",
|
"category": "cpp",
|
||||||
"type": "quiz",
|
"type": "none",
|
||||||
"question": "What is the output of the following code: std::cout << sizeof(int);",
|
"text": "What is the output of the expression 3 + 2 * 5 in C++?",
|
||||||
"answer": "The output depends on the system, typically 4 on most systems."
|
"answer": "13"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "2",
|
"id": 2,
|
||||||
"category": "geography",
|
"category": "cpp",
|
||||||
"type": "quiz",
|
"type": "none",
|
||||||
"question": "What is the capital of France?",
|
"text": "What is the default access specifier for class members in C++?",
|
||||||
"answer": "Paris"
|
"answer": "private"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "3",
|
"id": 3,
|
||||||
"category": "cybersecurity",
|
"category": "cpp",
|
||||||
"type": "true or false",
|
"type": "none",
|
||||||
"question": "A firewall can protect a network from all types of cyber attacks.",
|
"text": "What is the name of the process that automatically converts a primitive type to its corresponding wrapper class object in C++?",
|
||||||
"answer": "false"
|
"answer": "Boxing"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "4",
|
"id": 4,
|
||||||
"category": "C++",
|
"category": "cpp",
|
||||||
"type": "true or false",
|
"type": "none",
|
||||||
"question": "C++ supports multiple inheritance.",
|
"text": "Which C++ feature allows the creation of a function or an operator with more than one form?",
|
||||||
"answer": "true"
|
"answer": "Polymorphism"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "5",
|
"id": 5,
|
||||||
"category": "geography",
|
"category": "cpp",
|
||||||
"type": "quiz",
|
"type": "none",
|
||||||
"question": "Which is the largest continent by land area?",
|
"text": "What is the main purpose of a constructor in C++?",
|
||||||
"answer": "Asia"
|
"answer": "To initialize an object"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "6",
|
"id": 6,
|
||||||
"category": "cybersecurity",
|
"category": "cpp",
|
||||||
"type": "quiz",
|
"type": "none",
|
||||||
"question": "What does the acronym 'DDoS' stand for?",
|
"text": "What does STL stand for in C++?",
|
||||||
|
"answer": "Standard Template Library"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 7,
|
||||||
|
"category": "cpp",
|
||||||
|
"type": "none",
|
||||||
|
"text": "Which operator is used to access the members of a structure through a pointer in C++?",
|
||||||
|
"answer": "->"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 8,
|
||||||
|
"category": "cpp",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is the keyword used to define a constant in C++?",
|
||||||
|
"answer": "const"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 9,
|
||||||
|
"category": "cpp",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is the primary purpose of the 'new' operator in C++?",
|
||||||
|
"answer": "To allocate memory dynamically"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 10,
|
||||||
|
"category": "cpp",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What does RTTI stand for in C++?",
|
||||||
|
"answer": "Run-Time Type Information"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 11,
|
||||||
|
"category": "cpp",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is a pure virtual function in C++?",
|
||||||
|
"answer": "A function with no definition that must be overridden in derived classes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 12,
|
||||||
|
"category": "cpp",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is the name of the feature that allows the same function name to be used for different types in C++?",
|
||||||
|
"answer": "Function Overloading"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 13,
|
||||||
|
"category": "cpp",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is a 'namespace' in C++?",
|
||||||
|
"answer": "A declarative region that provides a scope to the identifiers inside it"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 14,
|
||||||
|
"category": "cpp",
|
||||||
|
"type": "none",
|
||||||
|
"text": "Which C++ keyword is used to prevent a variable from being modified?",
|
||||||
|
"answer": "const"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 15,
|
||||||
|
"category": "cpp",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is the function of the 'friend' keyword in C++?",
|
||||||
|
"answer": "To allow a function or another class access to the private and protected members of a class"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 16,
|
||||||
|
"category": "cpp",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is 'inheritance' in C++?",
|
||||||
|
"answer": "The process by which one class acquires the properties and functionalities of another class"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 17,
|
||||||
|
"category": "cpp",
|
||||||
|
"type": "none",
|
||||||
|
"text": "Which operator is used for scope resolution in C++?",
|
||||||
|
"answer": "::"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 18,
|
||||||
|
"category": "cpp",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is a 'destructor' in C++?",
|
||||||
|
"answer": "A special member function that is executed when an object of its class is destroyed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 19,
|
||||||
|
"category": "cpp",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is the primary use of the 'this' pointer in C++?",
|
||||||
|
"answer": "To refer to the calling object itself"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 20,
|
||||||
|
"category": "cpp",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is the return type of the main function in C++?",
|
||||||
|
"answer": "int"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 21,
|
||||||
|
"category": "cybersec",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What does 'phishing' mean in cybersecurity?",
|
||||||
|
"answer": "A technique used to trick users into providing sensitive information by pretending to be a trustworthy entity"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 22,
|
||||||
|
"category": "cybersec",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is the purpose of a firewall in network security?",
|
||||||
|
"answer": "To monitor and control incoming and outgoing network traffic based on predetermined security rules"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 23,
|
||||||
|
"category": "cybersec",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What does 'DDoS' stand for?",
|
||||||
"answer": "Distributed Denial of Service"
|
"answer": "Distributed Denial of Service"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "7",
|
"id": 24,
|
||||||
"category": "C++",
|
"category": "cybersec",
|
||||||
"type": "quiz",
|
"type": "none",
|
||||||
"question": "Which C++ standard introduced the concept of 'auto' keyword?",
|
"text": "What is a 'zero-day' exploit?",
|
||||||
"answer": "C++11"
|
"answer": "A cyber attack that occurs on the same day a vulnerability is discovered and before a fix is implemented"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "8",
|
"id": 25,
|
||||||
|
"category": "cybersec",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is 'encryption' in the context of cybersecurity?",
|
||||||
|
"answer": "The process of converting information or data into a code to prevent unauthorized access"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 26,
|
||||||
|
"category": "cybersec",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What does 'VPN' stand for in cybersecurity?",
|
||||||
|
"answer": "Virtual Private Network"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 27,
|
||||||
|
"category": "cybersec",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is a 'trojan horse' in cybersecurity?",
|
||||||
|
"answer": "A type of malware that disguises itself as legitimate software"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 28,
|
||||||
|
"category": "cybersec",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is the primary goal of social engineering attacks?",
|
||||||
|
"answer": "To manipulate individuals into divulging confidential information"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 29,
|
||||||
|
"category": "cybersec",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is 'ransomware'?",
|
||||||
|
"answer": "A type of malware that encrypts a user's data and demands payment to restore access"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 30,
|
||||||
|
"category": "cybersec",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What does 'SQL injection' exploit?",
|
||||||
|
"answer": "Vulnerabilities in an application's software by inserting malicious SQL code"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 31,
|
||||||
|
"category": "cybersec",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is 'two-factor authentication'?",
|
||||||
|
"answer": "A security process that requires two different forms of identification to access an account"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 32,
|
||||||
|
"category": "cybersec",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is 'malware'?",
|
||||||
|
"answer": "Software designed to disrupt, damage, or gain unauthorized access to computer systems"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 33,
|
||||||
|
"category": "cybersec",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What does 'TLS' stand for in cybersecurity?",
|
||||||
|
"answer": "Transport Layer Security"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 34,
|
||||||
|
"category": "cybersec",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is 'brute force attack' in cybersecurity?",
|
||||||
|
"answer": "A method to find passwords or encryption keys by trying every possible combination"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 35,
|
||||||
|
"category": "cybersec",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is the primary function of an Intrusion Detection System (IDS)?",
|
||||||
|
"answer": "To monitor network traffic for suspicious activity and potential threats"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 36,
|
||||||
|
"category": "cybersec",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is 'spoofing' in cybersecurity?",
|
||||||
|
"answer": "The act of disguising a communication from an unknown source as being from a known, trusted source"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 37,
|
||||||
|
"category": "cybersec",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is 'rootkit'?",
|
||||||
|
"answer": "A type of malicious software designed to hide the existence of certain processes or programs from normal methods of detection"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 38,
|
||||||
|
"category": "cybersec",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What does 'CVE' stand for in cybersecurity?",
|
||||||
|
"answer": "Common Vulnerabilities and Exposures"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 39,
|
||||||
|
"category": "cybersec",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is 'pharming' in cybersecurity?",
|
||||||
|
"answer": "A cyber attack intended to redirect a website's traffic to another, fake site"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 40,
|
||||||
|
"category": "cybersec",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What does 'APT' stand for in cybersecurity?",
|
||||||
|
"answer": "Advanced Persistent Threat"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 41,
|
||||||
|
"category": "cybersec",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is 'cross-site scripting' (XSS)?",
|
||||||
|
"answer": "A vulnerability that allows attackers to inject malicious scripts into webpages viewed by users"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 42,
|
||||||
|
"category": "cybersec",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What does 'BYOD' stand for in cybersecurity?",
|
||||||
|
"answer": "Bring Your Own Device"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 43,
|
||||||
|
"category": "cybersec",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is 'penetration testing'?",
|
||||||
|
"answer": "A method of evaluating the security of a computer system by simulating an attack"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 44,
|
||||||
|
"category": "cybersec",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What does 'CIA' stand for in cybersecurity?",
|
||||||
|
"answer": "Confidentiality, Integrity, and Availability"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 45,
|
||||||
|
"category": "cybersec",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is 'smishing'?",
|
||||||
|
"answer": "A type of phishing attack that uses SMS messages to lure victims"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 46,
|
||||||
|
"category": "cybersec",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is a 'honeypot' in cybersecurity?",
|
||||||
|
"answer": "A security system set up to attract and detect hackers"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 47,
|
||||||
|
"category": "cybersec",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is the purpose of 'endpoint protection'?",
|
||||||
|
"answer": "To secure endpoints or entry points of end-user devices such as desktops, laptops, and mobile devices"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 48,
|
||||||
|
"category": "cybersec",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What does 'SIEM' stand for in cybersecurity?",
|
||||||
|
"answer": "Security Information and Event Management"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 49,
|
||||||
|
"category": "cybersec",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is a 'man-in-the-middle' attack?",
|
||||||
|
"answer": "A cyber attack where the attacker secretly intercepts and relays messages between two parties"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 50,
|
||||||
|
"category": "cybersec",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is 'data exfiltration'?",
|
||||||
|
"answer": "The unauthorized transfer of data from a computer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 51,
|
||||||
"category": "geography",
|
"category": "geography",
|
||||||
"type": "true or false",
|
"type": "none",
|
||||||
"question": "The Amazon River is the longest river in the world.",
|
"text": "What is the longest river in the world?",
|
||||||
"answer": "false"
|
"answer": "The Nile River"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "9",
|
"id": 52,
|
||||||
"category": "cybersecurity",
|
"category": "geography",
|
||||||
"type": "quiz",
|
"type": "none",
|
||||||
"question": "What is the main purpose of encryption?",
|
"text": "What is the capital city of Australia?",
|
||||||
"answer": "To protect data confidentiality by converting it into a code."
|
"answer": "Canberra"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "10",
|
"id": 53,
|
||||||
"category": "C++",
|
"category": "geography",
|
||||||
"type": "quiz",
|
"type": "none",
|
||||||
"question": "What is the function of the 'virtual' keyword in C++?",
|
"text": "Which desert is the largest in the world?",
|
||||||
"answer": "It allows a member function to be overridden in a derived class."
|
"answer": "The Sahara Desert"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 54,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is the smallest country in the world by land area?",
|
||||||
|
"answer": "Vatican City"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 55,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What mountain range forms the natural border between France and Spain?",
|
||||||
|
"answer": "The Pyrenees"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 56,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "Which country has the most natural lakes?",
|
||||||
|
"answer": "Canada"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 57,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is the highest mountain in Africa?",
|
||||||
|
"answer": "Mount Kilimanjaro"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 58,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is the capital of Japan?",
|
||||||
|
"answer": "Tokyo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 59,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What river runs through the Grand Canyon?",
|
||||||
|
"answer": "The Colorado River"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 60,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "Which country is known as the Land of the Rising Sun?",
|
||||||
|
"answer": "Japan"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 61,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is the largest island in the world?",
|
||||||
|
"answer": "Greenland"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 62,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "Which European country shares borders with the most neighbors?",
|
||||||
|
"answer": "Germany"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 63,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is the deepest ocean trench in the world?",
|
||||||
|
"answer": "The Mariana Trench"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 64,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "Which U.S. state is the largest by area?",
|
||||||
|
"answer": "Alaska"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 65,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is the capital city of Canada?",
|
||||||
|
"answer": "Ottawa"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 66,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "Which river is the longest in Europe?",
|
||||||
|
"answer": "The Volga River"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 67,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "Which continent has the most countries?",
|
||||||
|
"answer": "Africa"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 68,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is the capital of Brazil?",
|
||||||
|
"answer": "Brasília"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 69,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is the official language of Egypt?",
|
||||||
|
"answer": "Arabic"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 70,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "Which ocean is the largest by surface area?",
|
||||||
|
"answer": "The Pacific Ocean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 71,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is the name of the sea bordered by Europe to the north and Africa to the south?",
|
||||||
|
"answer": "The Mediterranean Sea"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 72,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "Which country has the most islands?",
|
||||||
|
"answer": "Sweden"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 73,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is the largest country in South America?",
|
||||||
|
"answer": "Brazil"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 74,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is the longest mountain range in the world?",
|
||||||
|
"answer": "The Andes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 75,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is the capital city of Russia?",
|
||||||
|
"answer": "Moscow"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 76,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is the largest desert in Asia?",
|
||||||
|
"answer": "The Gobi Desert"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 77,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is the capital of Kenya?",
|
||||||
|
"answer": "Nairobi"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 78,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "Which country is the smallest in Europe by land area?",
|
||||||
|
"answer": "Vatican City"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 79,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "Which river forms part of the border between Mexico and the United States?",
|
||||||
|
"answer": "The Rio Grande"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 80,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is the capital city of South Korea?",
|
||||||
|
"answer": "Seoul"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 81,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is the largest country in Africa by land area?",
|
||||||
|
"answer": "Algeria"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 82,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "Which country is known as the Land of the Maple Leaf?",
|
||||||
|
"answer": "Canada"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 83,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "Which river is the longest in Asia?",
|
||||||
|
"answer": "The Yangtze River"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 84,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is the capital city of India?",
|
||||||
|
"answer": "New Delhi"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 85,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "Which ocean lies on the east coast of the United States?",
|
||||||
|
"answer": "The Atlantic Ocean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 86,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is the capital city of Italy?",
|
||||||
|
"answer": "Rome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 87,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "Which river is the main river that flows through Paris?",
|
||||||
|
"answer": "The Seine"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 88,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is the smallest ocean in the world?",
|
||||||
|
"answer": "The Arctic Ocean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 89,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is the capital city of Mexico?",
|
||||||
|
"answer": "Mexico City"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 90,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "Which country is known for its unique wildlife and has the Great Barrier Reef?",
|
||||||
|
"answer": "Australia"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 91,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is the capital of Argentina?",
|
||||||
|
"answer": "Buenos Aires"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 92,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "Which country has the largest population in the world?",
|
||||||
|
"answer": "China"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 93,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is the capital city of the United Kingdom?",
|
||||||
|
"answer": "London"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 94,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "Which continent is known as the birthplace of humanity?",
|
||||||
|
"answer": "Africa"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 95,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is the capital city of Thailand?",
|
||||||
|
"answer": "Bangkok"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 96,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is the capital of Egypt?",
|
||||||
|
"answer": "Cairo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 97,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "Which country is home to the Amazon Rainforest?",
|
||||||
|
"answer": "Brazil"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 98,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is the capital city of Spain?",
|
||||||
|
"answer": "Madrid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 99,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "What is the currency of Japan?",
|
||||||
|
"answer": "Yen"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 100,
|
||||||
|
"category": "geography",
|
||||||
|
"type": "none",
|
||||||
|
"text": "Which country is known as the Land of Fire and Ice?",
|
||||||
|
"answer": "Iceland"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"sessions": []
|
||||||
}
|
}
|
||||||
|
|
110
app/about-us/page.tsx
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
import { ClipboardDocumentListIcon, DocumentTextIcon } from '@heroicons/react/24/outline';
|
||||||
|
import Logo from '../components/logo';
|
||||||
|
import TopicCard from '../components/topic_card';
|
||||||
|
import LinkButton from '../components/link-button';
|
||||||
|
{/*Index of people and their respective image and title*/}
|
||||||
|
const people = [
|
||||||
|
{
|
||||||
|
name: 'Andrei Banu',
|
||||||
|
role: 'UI/UX designer',
|
||||||
|
imageUrl:
|
||||||
|
'arch.png',
|
||||||
|
email: 'mailto:child1.andy@gmail.com'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Ioan Cristian Chelaru',
|
||||||
|
role: 'UI/UX designer',
|
||||||
|
imageUrl:
|
||||||
|
'blahaj.avif',
|
||||||
|
email: 'mailto:iccjoc@proton.me'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Alexandru Gabriel Stan',
|
||||||
|
role: 'Backend & UI developer',
|
||||||
|
imageUrl:
|
||||||
|
'alex.png',
|
||||||
|
email: 'mailto:alex.stan.2010@proton.me'
|
||||||
|
},
|
||||||
|
// More people...
|
||||||
|
]
|
||||||
|
|
||||||
|
export default function Example() {
|
||||||
|
return (
|
||||||
|
<div className="sm:py-32">
|
||||||
|
{/*Background visuals*/}
|
||||||
|
<div
|
||||||
|
className="absolute inset-x-0 -top-40 -z-10 transform-gpu overflow-hidden blur-3xl sm:-top-80"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="relative left-[calc(50%-11rem)] aspect-[1155/678] w-[36.125rem] -translate-x-1/2 rotate-[30deg] bg-gradient-to-tr from-[#ff80b5] to-[#9089fc] opacity-30 sm:left-[calc(50%-30rem)] sm:w-[72.1875rem]"
|
||||||
|
style={{
|
||||||
|
clipPath:
|
||||||
|
'polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className="relative left-[calc(50%+3rem)] aspect-[1155/678] w-[36.125rem] -translate-x-1/2 bg-gradient-to-tr from-[#ff80b5] to-[#9089fc] opacity-30 sm:left-[calc(50%+36rem)] sm:w-[72.1875rem]"
|
||||||
|
style={{
|
||||||
|
clipPath:
|
||||||
|
'polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Logo/>
|
||||||
|
<div style={{paddingBottom: '100px'}}></div>
|
||||||
|
<div className="mx-auto grid max-w-7xl gap-x-8 gap-y-20 px-6 lg:px-8 xl:grid-cols-3"> {/*Team presentation text*/}
|
||||||
|
<div className="max-w-2xl">
|
||||||
|
<h2 className="text-3xl font-bold tracking-tight text-gray-200 sm:text-4xl">Meet our developers</h2>
|
||||||
|
<p className="mt-6 text-lg leading-8 text-gray-200">
|
||||||
|
We are a group of passionate middle schoolers that got together to hopefully make a difference in the world
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{/*Display people*/}
|
||||||
|
<ul role="list" className="grid gap-x-8 gap-y-12 sm:grid-cols-2 sm:gap-y-16 xl:col-span-2">
|
||||||
|
{people.map((person) => (
|
||||||
|
<li key={person.name}>
|
||||||
|
<div className="flex items-center gap-x-6">
|
||||||
|
<img className="h-16 w-16 rounded-full" src={person.imageUrl} alt="" />
|
||||||
|
<div>
|
||||||
|
<h3 className="text-base font-semibold leading-7 tracking-tight text-gray-200">{person.name}</h3>
|
||||||
|
<p className="text-sm font-semibold leading-6 text-indigo-200">{person.role}</p>
|
||||||
|
<LinkButton text="Send mail" href={person.email}/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<br></br>
|
||||||
|
{/*Licence and source code buttons*/}
|
||||||
|
<div
|
||||||
|
style={{display:"flex", alignItems: 'center', alignSelf:'center', justifyContent: 'center', marginLeft: "40px", marginRight: "40px"}}
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
href="https://git.gra.phite.ro/BlahajTeam/next-app"
|
||||||
|
className="rounded-md text-sm font-semibold text-gray-200 shadow-sm hover:text-gray-400 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white"
|
||||||
|
style={{minWidth: '190px', maxWidth: '190px', paddingLeft: '50px', paddingRight: '50px', paddingTop: '20px', paddingBottom: '20px', alignSelf: 'center'}}
|
||||||
|
>
|
||||||
|
<DocumentTextIcon></DocumentTextIcon>
|
||||||
|
Source Code
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="https://git.gra.phite.ro/BlahajTeam/next-app/src/branch/main/LICENSE"
|
||||||
|
className="rounded-md text-sm font-semibold text-gray-200 shadow-sm hover:text-gray-400 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white"
|
||||||
|
style={{minWidth: '190px', maxWidth: '190px', paddingLeft: '50px', paddingRight: '50px', paddingTop: '20px', paddingBottom: '20px', alignSelf: 'center'}}
|
||||||
|
>
|
||||||
|
<ClipboardDocumentListIcon></ClipboardDocumentListIcon>
|
||||||
|
Our Licence
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="absolute inset-x-0 top-[calc(100%-13rem)] -z-10 transform-gpu overflow-hidden blur-3xl sm:top-[calc(100%-30rem)]"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
import Image from "next/image";
|
|
||||||
|
|
||||||
export default function Home() {
|
|
||||||
return (
|
|
||||||
<main className="grid min-h-full place-items-center bg-white px-6 py-24 sm:py-32 lg:px-8">
|
|
||||||
<div className="text-center">
|
|
||||||
<p className="text-base font-semibold text-indigo-600">404</p>
|
|
||||||
<h1 className="mt-4 text-3xl font-bold tracking-tight text-gray-900 sm:text-5xl">Page not found</h1>
|
|
||||||
<p className="mt-6 text-base leading-7 text-gray-600">Sorry, we couldn’t find the page you’re looking for.</p>
|
|
||||||
<div className="mt-10 flex items-center justify-center gap-x-6">
|
|
||||||
<a
|
|
||||||
href="#"
|
|
||||||
className="rounded-md bg-indigo-600 px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
|
|
||||||
>
|
|
||||||
Go back home
|
|
||||||
</a>
|
|
||||||
<a href="#" className="text-sm font-semibold text-gray-900">
|
|
||||||
Contact support <span aria-hidden="true">→</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
);
|
|
||||||
}
|
|
53
app/components/form.tsx
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
'use client';
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
export enum FieldType {
|
||||||
|
number = 'number',
|
||||||
|
text = 'text'
|
||||||
|
}
|
||||||
|
|
||||||
|
export type FormFields = {
|
||||||
|
type: FieldType,
|
||||||
|
name: string,
|
||||||
|
label: string,
|
||||||
|
required: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const Form = <T,>({ fields, onSubmit }) => {
|
||||||
|
const [formData, setFormData] = useState({});
|
||||||
|
|
||||||
|
const handleChange = (event) => {
|
||||||
|
const { name, value } = event.target;
|
||||||
|
setFormData( (prevData) => ({
|
||||||
|
...prevData,
|
||||||
|
[name]: value
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSubmit = (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
onSubmit( formData );
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
{fields.map( (field) => {
|
||||||
|
return (
|
||||||
|
<div key={field.name}>
|
||||||
|
<label>{field.label}</label>
|
||||||
|
<input
|
||||||
|
type={field.type}
|
||||||
|
name={field.name}
|
||||||
|
value={formData[field.name] || ''}
|
||||||
|
onChange={handleChange}
|
||||||
|
required={field.required}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)})}
|
||||||
|
<button type="submit">Submit</button>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Form;
|
||||||
|
|
26
app/components/link-button.tsx
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
'use client';
|
||||||
|
import { motion } from "framer-motion"
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
const LinkButton = ({text, href, onPress}: {text: string, href: string, onPress?: () => any}) => {
|
||||||
|
const [buttonHovered, setButtonHovered] = useState( false );
|
||||||
|
const [buttonPressed, setButtonPressed] = useState( false );
|
||||||
|
return (
|
||||||
|
<div className="mt-10 flex items-center justify-center gap-x-6 lg:justify-start">
|
||||||
|
<motion.a
|
||||||
|
href={href}
|
||||||
|
className="rounded-md bg-white px-3.5 py-2.5 text-sm font-semibold text-gray-900 shadow-sm hover:bg-gray-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white"
|
||||||
|
style={{ marginBottom: '40px' }}
|
||||||
|
animate={{opacity: buttonHovered ? 0.8 : 1}}
|
||||||
|
onMouseEnter={()=>setButtonHovered(true)}
|
||||||
|
onMouseLeave={()=>setButtonHovered(false)}
|
||||||
|
onClick={()=>{setButtonPressed(true); onPress && onPress();}}
|
||||||
|
>
|
||||||
|
{text}
|
||||||
|
</motion.a>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LinkButton;
|
45
app/components/logo.tsx
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
'use client';
|
||||||
|
import { Transition } from "@headlessui/react";
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
import { useState } from "react";
|
||||||
|
import LinkButton from "./link-button";
|
||||||
|
|
||||||
|
const Logo = () => {
|
||||||
|
|
||||||
|
const [logoHovered, setLogoHovered] = useState( false );
|
||||||
|
const [aboutButtonHovered, setAboutButtonHovered] = useState( false );
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ display:"flex", alignItems: 'center', justifyContent: 'center', marginLeft: "40px", marginRight: "40px"}}>
|
||||||
|
{/* <Transition
|
||||||
|
appear={false}
|
||||||
|
show={true}
|
||||||
|
enter="transition-opacity duration-75"
|
||||||
|
enterFrom={logoHovered ? "opacity-100" : "opacity-70"}
|
||||||
|
enterTo={logoHovered ? "opacity-70" : "opacity-100"}
|
||||||
|
leave="transition-opacity duration-75"
|
||||||
|
leaveFrom={logoHovered ? "opacity-70" : "opacity-100"}
|
||||||
|
leaveTo={logoHovered ? "opacity-100" : "opacity-70"}> */}
|
||||||
|
<motion.a href="/" style={{ marginBottom: '40px', marginLeft: '40px'}} animate={{opacity: logoHovered ? 0.7 : 1}}
|
||||||
|
onMouseEnter={()=>setLogoHovered(true)}
|
||||||
|
onMouseLeave={()=>setLogoHovered(false)}>
|
||||||
|
<img src="/logo-no-background.png" height="auto" width="400px"/>
|
||||||
|
</motion.a>
|
||||||
|
{/* </Transition> */}
|
||||||
|
<div style={{ marginBottom: '40px', marginRight: '50px' }}></div>
|
||||||
|
{/* <motion.a
|
||||||
|
href="/about-us"
|
||||||
|
className="rounded-md bg-white text-sm font-semibold text-gray-900 shadow-sm hover:bg-gray-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white"
|
||||||
|
style={{ marginBottom: '40px', marginRight: '40px', padding: '10px' }}
|
||||||
|
animate={{opacity: aboutButtonHovered ? 0.8 : 1}}
|
||||||
|
onMouseEnter={()=>setAboutButtonHovered(true)}
|
||||||
|
onMouseLeave={()=>setAboutButtonHovered(false)}
|
||||||
|
>
|
||||||
|
About us
|
||||||
|
</motion.a> */}
|
||||||
|
<LinkButton text="About us" href="/about-us"/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Logo;
|
120
app/components/question_component.tsx
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
import { ReactComponentElement, ReactNode, useEffect } from "react";
|
||||||
|
import { AnswerType, Question } from "../lib/question";
|
||||||
|
import { SessionState } from "../lib/session";
|
||||||
|
import { CheckCircleIcon, XCircleIcon } from "@heroicons/react/24/outline";
|
||||||
|
import { Button, Transition } from "@headlessui/react";
|
||||||
|
import wait from "../lib/delay";
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
|
||||||
|
const QuestionView = ({ question, setState, state }: { question: Question, setState: any, state: SessionState }) => {
|
||||||
|
|
||||||
|
const handleOpenClick = () => {
|
||||||
|
setState( (prevState: SessionState) => ({
|
||||||
|
...prevState,
|
||||||
|
opened: {
|
||||||
|
...prevState.opened,
|
||||||
|
[question.id]: !prevState.opened[ question.id ]
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect( () => {
|
||||||
|
wait( 1000 );
|
||||||
|
}, [] );
|
||||||
|
|
||||||
|
const handleAnswerClick = (right: boolean) => {
|
||||||
|
setState( (prevState: SessionState) => ({
|
||||||
|
...prevState,
|
||||||
|
answered: prevState.answered + 1,
|
||||||
|
wrong: prevState.wrong + (right ? 0 : 1),
|
||||||
|
right: prevState.right + (right ? 1 : 0),
|
||||||
|
answer: {
|
||||||
|
...prevState.answer,
|
||||||
|
[question.id]: right ? AnswerType.Right : AnswerType.Wrong
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
question.answered = right ? AnswerType.Right : AnswerType.Wrong;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mx-auto max-w-2xl py-32 sm:py-48 lg:py-56">
|
||||||
|
<div className="text-center">
|
||||||
|
{/* {question && question.text} */}
|
||||||
|
<Transition
|
||||||
|
appear={true}
|
||||||
|
show={question ? true : false /* a bit hacky */}
|
||||||
|
enter="transition-opacity duration-300"
|
||||||
|
enterFrom="opacity-0"
|
||||||
|
enterTo="opacity-90"
|
||||||
|
leave="transition-opacity duration-150"
|
||||||
|
leaveFrom="opacity-90"
|
||||||
|
leaveTo="opacity-0">
|
||||||
|
<h1 className="text-4xl font-bold tracking-tight text-gray-50">
|
||||||
|
{question.text}
|
||||||
|
</h1>
|
||||||
|
</Transition>
|
||||||
|
{/* <Transition
|
||||||
|
show={(question && state.opened[ question.id ]) ? true : false}
|
||||||
|
enter="transition-opacity duration-300"
|
||||||
|
enterFrom="opacity-0"
|
||||||
|
enterTo="opacity-90"
|
||||||
|
leave="transition-opacity duration-150"
|
||||||
|
leaveFrom="opacity-90"
|
||||||
|
leaveTo="opacity-0"> */}
|
||||||
|
{/* <motion.div animate={{visibility: ((question && state.opened[ question.id ]) ? "visible" : "collapse") }}> */}
|
||||||
|
<motion.div animate={(question && state.opened[ question.id ]) ? {visibility: "visible", opacity: 1} : {visibility: "collapse", opacity: 0}}>
|
||||||
|
<div><p className="mt-6 text-lg leading-8 text-gray-200">
|
||||||
|
{question.answer}
|
||||||
|
</p>
|
||||||
|
{/* <div className="mt-10 flex items-center justify-center gap-x-6">
|
||||||
|
<button
|
||||||
|
onClick={() => { handleAnswerClick( true )}}
|
||||||
|
className="rounded-md bg-indigo-600 px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
|
||||||
|
>
|
||||||
|
I knew that!
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => {handleAnswerClick( false )}}
|
||||||
|
className="text-sm font-semibold leading-6 text-gray-300"
|
||||||
|
>
|
||||||
|
I didn't know that!
|
||||||
|
</button>
|
||||||
|
</div> */}
|
||||||
|
<div className="mt-10 flex items-center justify-center gap-x-6">
|
||||||
|
<button
|
||||||
|
onClick={() => { handleAnswerClick( true )}}
|
||||||
|
className="rounded-md bg-green-600 px-3.5 py-2.5 text-s font-semibold text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
|
||||||
|
style={{minWidth:'40%', minHeight:'40%', maxWidth:'40%', maxHeight:'40%'}}
|
||||||
|
>
|
||||||
|
<CheckCircleIcon></CheckCircleIcon>
|
||||||
|
I knew that!
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => {handleAnswerClick( false )}}
|
||||||
|
className="rounded-md bg-red-600 px-3.5 py-2.5 text-s font-semibold text-white shadow-sm hover:bg-red-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
|
||||||
|
style={{minWidth:'40%', minHeight:'40%', maxWidth:'40%', maxHeight:'40%'}}
|
||||||
|
>
|
||||||
|
<XCircleIcon></XCircleIcon>
|
||||||
|
I didn't know!
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
<Transition
|
||||||
|
show={(question && (state.opened[ question.id ])) ? false : true}
|
||||||
|
enter="transition-opacity duration-300"
|
||||||
|
enterFrom="opacity-0"
|
||||||
|
enterTo="opacity-90"
|
||||||
|
leave="transition-opacity duration-150"
|
||||||
|
leaveFrom="opacity-90"
|
||||||
|
leaveTo="opacity-0">
|
||||||
|
<Button className="leading-6 text-black back rounded-md bg-white text-sm font-semibold text-gray-900" onClick={handleOpenClick} style = {{padding: '5px'}}>
|
||||||
|
Show answer
|
||||||
|
</Button>
|
||||||
|
</Transition>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default QuestionView;
|
113
app/components/results.tsx
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
import { CheckIcon, XMarkIcon, QuestionMarkCircleIcon, FingerPrintIcon, LockClosedIcon } from '@heroicons/react/24/outline'
|
||||||
|
import Logo from './logo';
|
||||||
|
import { SessionState } from '../lib/session';
|
||||||
|
import LinkButton from './link-button';
|
||||||
|
|
||||||
|
/*
|
||||||
|
const features = [
|
||||||
|
{
|
||||||
|
name: 'Correct Questions',
|
||||||
|
description:
|
||||||
|
'You got [insert number here] questions right! Good Job!',
|
||||||
|
icon: CheckIcon,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Incorrect Questions',
|
||||||
|
description:
|
||||||
|
'You got [insert number here] questions wrong! You can do better!',
|
||||||
|
icon: XMarkIcon,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Total Questions',
|
||||||
|
description:
|
||||||
|
'There were [insert number here] questions in total.',
|
||||||
|
icon: QuestionMarkCircleIcon,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
*/
|
||||||
|
|
||||||
|
const Result = ({name, text1, number, text2, Icon}: {name: string, text1: string, number: number, text2: string, Icon: any}) => {
|
||||||
|
return (
|
||||||
|
<div className="relative pl-16">
|
||||||
|
<dt className="text-base font-semibold leading-7 text-gray-100">
|
||||||
|
<div className="absolute left-0 top-0 flex h-10 w-10 items-center justify-center rounded-lg bg-indigo-600">
|
||||||
|
<Icon className="h-6 w-6 text-white" aria-hidden="true"/>
|
||||||
|
</div>
|
||||||
|
{name}
|
||||||
|
</dt>
|
||||||
|
<dd className="mt-2 text-base leading-7 text-gray-300">{`${text1} ${number} ${text2}`}</dd>
|
||||||
|
</div>
|
||||||
|
// {/* <p className="relative pl-16">{`${text1} ${number} ${text2}`}</p> */}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const ResultScreen = ({state}: {state: SessionState}) => {
|
||||||
|
return (
|
||||||
|
<div className="py-24 sm:py-32">
|
||||||
|
<div
|
||||||
|
className="absolute inset-x-0 -top-40 -z-10 transform-gpu overflow-hidden blur-3xl sm:-top-80"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="relative left-[calc(50%-11rem)] aspect-[1155/678] w-[36.125rem] -translate-x-1/2 rotate-[30deg] bg-gradient-to-tr from-[#ff80b5] to-[#9089fc] opacity-30 sm:left-[calc(50%-30rem)] sm:w-[72.1875rem]"
|
||||||
|
style={{
|
||||||
|
clipPath:
|
||||||
|
'polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div style={{paddingBottom: '128px'}}></div>
|
||||||
|
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||||
|
<div className="mx-auto max-w-2xl lg:text-center">
|
||||||
|
<h2 className="text-base font-semibold leading-7 text-indigo-300">You did it!</h2>
|
||||||
|
<p className="mt-2 text-3xl font-bold tracking-tight text-gray-100 sm:text-4xl">
|
||||||
|
Here are the results:
|
||||||
|
</p>
|
||||||
|
<p className="mt-6 text-lg leading-8 text-gray-600">
|
||||||
|
On this page you can see what you did at the quiz. We'll give your wrong questions again, another time!
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="mx-auto mt-16 max-w-2xl sm:mt-20 lg:mt-24 lg:max-w-4xl">
|
||||||
|
<dl className="grid max-w-xl grid-cols-1 gap-x-8 gap-y-10 lg:max-w-none lg:grid-cols-2 lg:gap-y-16">
|
||||||
|
{/* {features.map((feature) => (
|
||||||
|
<div key={feature.name} className="relative pl-16">
|
||||||
|
<dt className="text-base font-semibold leading-7 text-gray-100">
|
||||||
|
<div className="absolute left-0 top-0 flex h-10 w-10 items-center justify-center rounded-lg bg-indigo-600">
|
||||||
|
<feature.icon className="h-6 w-6 text-white" aria-hidden="true" />
|
||||||
|
</div>
|
||||||
|
{feature.name}
|
||||||
|
</dt>
|
||||||
|
<dd className="mt-2 text-base leading-7 text-gray-300">{feature.description}</dd>
|
||||||
|
</div>
|
||||||
|
))} */}
|
||||||
|
<Result name="Correct Answers" key="correct" text1="Congrats for your" number={state.right} text2="correctly answered questions! Great job!" Icon={CheckIcon}/>
|
||||||
|
<Result name="Wrong Answers" key="wrong" text1="You had" number={state.wrong} text2="wrong answers... It's never too late to learn!" Icon={XMarkIcon}/>
|
||||||
|
<Result name="Total Answers" key="total" text1="The" number={state.answered} text2="questions you answered surely helped you learn more." Icon={QuestionMarkCircleIcon}/>
|
||||||
|
</dl>
|
||||||
|
<br></br>
|
||||||
|
{/* <a
|
||||||
|
href="/sessionconfig"
|
||||||
|
className="rounded-md bg-indigo-600 px-3.5 py-2.5 text-sm font-semibold text-gray-100 shadow-sm hover:bg-indigo-900 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white"
|
||||||
|
style={{ marginTop: '100px' }}
|
||||||
|
>
|
||||||
|
Go Back
|
||||||
|
</a> */}
|
||||||
|
<LinkButton text="Learn about another topic" href="/sessionconfig"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="absolute inset-x-0 top-[calc(100%-13rem)] -z-10 transform-gpu overflow-hidden blur-3xl sm:top-[calc(100%-30rem)]"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="relative left-[calc(50%+3rem)] aspect-[1155/678] w-[36.125rem] -translate-x-1/2 bg-gradient-to-tr from-[#ff80b5] to-[#9089fc] opacity-30 sm:left-[calc(50%+36rem)] sm:w-[72.1875rem]"
|
||||||
|
style={{
|
||||||
|
clipPath:
|
||||||
|
'polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default ResultScreen;
|
48
app/components/topic_card.tsx
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
'use client';
|
||||||
|
import { motion } from 'framer-motion';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import LinkButton from './link-button';
|
||||||
|
const TopicCard = ({ title, children, link }: { title: string, children: any, link: string }) => {
|
||||||
|
const [clicked, setClicked] = useState( false );
|
||||||
|
const [hovered, setHovered] = useState( false );
|
||||||
|
return (
|
||||||
|
<motion.div className="mx-auto mt-16 max-w-2xl rounded-3xl sm:mt-20 lg:mx-0 lg:flex lg:max-w-none relative isolate overflow-hidden bg-gray-900 px-6 pt-16 shadow-2xl sm:rounded-3xl sm:px-16 md:pt-24 lg:flex lg:gap-x-20 lg:px-24 lg:pt-0" style={{marginTop: '20px', paddingTop: '0px', paddingBottom: '24px'}}
|
||||||
|
animate={{x: hovered? 10 : 0, opacity: clicked ? 0.5 : 1 }} onMouseEnter={()=>setHovered(true)} onMouseLeave={()=>setHovered(false)}>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 1024 1024"
|
||||||
|
className="absolute left-1/2 top-1/2 -z-10 h-[64rem] w-[64rem] -translate-y-1/2 [mask-image:radial-gradient(closest-side,white,transparent)] sm:left-full sm:-ml-80 lg:left-1/2 lg:ml-0 lg:-translate-x-1/2 lg:translate-y-0"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<circle cx={512} cy={512} r={512} fill="url(#759c1415-0410-454c-8f7c-9a820de03641)" fillOpacity="0.7" />
|
||||||
|
<defs>
|
||||||
|
<radialGradient id="759c1415-0410-454c-8f7c-9a820de03641">
|
||||||
|
<stop stopColor="#7775D6" />
|
||||||
|
<stop offset={1} stopColor="#E935C1" />
|
||||||
|
</radialGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
<div className="p-8 sm:p-10 lg:flex-auto ">
|
||||||
|
<h3 className="text-2xl font-bold tracking-tight text-gray-200">{title}</h3>
|
||||||
|
<p className="mt-6 text-base leading-7 text-gray-200">
|
||||||
|
{children}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="-mt-2 p-2 lg:mt-0 lg:max-w-md lg:flex-shrink-0 lg:flex lg:flex-col lg:justify-center"
|
||||||
|
style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}
|
||||||
|
>
|
||||||
|
{/* <a
|
||||||
|
href={link}
|
||||||
|
className="rounded-md text-sm font-semibold border text-gray-200 shadow-sm hover:text-gray-400 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white"
|
||||||
|
style={{ paddingLeft: '50px', paddingRight: '50px', paddingTop: '20px', paddingBottom: '20px'}}
|
||||||
|
>
|
||||||
|
|
||||||
|
Begin
|
||||||
|
</a> */}
|
||||||
|
<LinkButton text="Begin" href={link} onPress={()=>setClicked(true)}/>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TopicCard;
|
BIN
app/favicon.ico
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 258 KiB |
|
@ -5,8 +5,8 @@ import "./globals.css";
|
||||||
const inter = Inter({ subsets: ["latin"] });
|
const inter = Inter({ subsets: ["latin"] });
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Create Next App",
|
title: "FlashLearn",
|
||||||
description: "Generated by create next app",
|
description: "A react based, flashcard app.",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function RootLayout({
|
export default function RootLayout({
|
||||||
|
|
124
app/learn/[category]/page.tsx
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
'use client';
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import { Dialog, DialogPanel } from '@headlessui/react'
|
||||||
|
import { Bars3Icon, XMarkIcon } from '@heroicons/react/24/outline'
|
||||||
|
import Logo from '../../components/logo';
|
||||||
|
import { CheckIcon } from '@heroicons/react/20/solid';
|
||||||
|
import { SessionState } from '@/app/lib/session';
|
||||||
|
import { AnswerType, Question, getQuestions } from '@/app/lib/question';
|
||||||
|
import QuestionView from '@/app/components/question_component';
|
||||||
|
import ResultScreen from '@/app/components/results';
|
||||||
|
|
||||||
|
const shuffle = (array: any[]) => {
|
||||||
|
let i = array.length - 1;
|
||||||
|
|
||||||
|
while ( i >= 0 ) {
|
||||||
|
const random_i = Math.floor( Math.random() * i );
|
||||||
|
|
||||||
|
[array[ i ], array[ random_i ]] = [array[ random_i ], array[ i ]];
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Page = ({ params }: { params: { category: string }}) => {
|
||||||
|
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
|
||||||
|
const [state, setState]: [SessionState, any] = useState<SessionState>( {
|
||||||
|
answered: 0,
|
||||||
|
wrong: 0,
|
||||||
|
right: 0,
|
||||||
|
category: '',
|
||||||
|
opened: [],
|
||||||
|
answer: []
|
||||||
|
} );
|
||||||
|
|
||||||
|
// const category = params.category;
|
||||||
|
|
||||||
|
// const [all_questions, set_all_questions] = useState<Question[]>([]);
|
||||||
|
const [questions, setQuestions] = useState<Question[]>([]);
|
||||||
|
|
||||||
|
useEffect( () => {
|
||||||
|
// console.log( params.category );
|
||||||
|
const fetch_all_questions = async () => {
|
||||||
|
let result = await getQuestions();
|
||||||
|
console.log( `all_questions: ${ JSON.stringify( result ) }` );
|
||||||
|
// set_all_questions( result );
|
||||||
|
result = result.filter( q => q.category === params.category)
|
||||||
|
.map(it=>({...it, answered: AnswerType.Unset}));
|
||||||
|
//result = result.filter
|
||||||
|
shuffle( result );
|
||||||
|
setQuestions( result );
|
||||||
|
console.log( `q12 uestions: ${ JSON.stringify( questions ) }`, result );
|
||||||
|
}
|
||||||
|
fetch_all_questions();
|
||||||
|
|
||||||
|
var ans: AnswerType[];
|
||||||
|
questions?.forEach(q => {
|
||||||
|
ans[ q.id ] = AnswerType.Unset;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
setState( (prevState: SessionState) => ({
|
||||||
|
...prevState,
|
||||||
|
answer: ans
|
||||||
|
}));
|
||||||
|
*/
|
||||||
|
}, [ params.category ]);
|
||||||
|
|
||||||
|
console.log( "questions16", questions );
|
||||||
|
|
||||||
|
|
||||||
|
console.log( "state15", state );
|
||||||
|
|
||||||
|
// alternate version (show all of them at the same time):
|
||||||
|
// {/* {questions.map( (q) => (q.answered == AnswerType.Unset) ? <QuestionView key = {q.id} question={q} state={state} setState={setState}/> : <></>)} */}
|
||||||
|
|
||||||
|
const index = state.answered;
|
||||||
|
console.log( "index18", index )
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
|
||||||
|
<div className="relative isolate px-6 pt-14 lg:px-8">
|
||||||
|
<div style={{paddingTop: '72px'}}></div>
|
||||||
|
<Logo/>
|
||||||
|
<div
|
||||||
|
className="absolute inset-x-0 -top-40 -z-10 transform-gpu overflow-hidden blur-3xl sm:-top-80"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="relative left-[calc(50%-11rem)] aspect-[1155/678] w-[36.125rem] -translate-x-1/2 rotate-[30deg] bg-gradient-to-tr from-[#ff80b5] to-[#9089fc] opacity-30 sm:left-[calc(50%-30rem)] sm:w-[72.1875rem]"
|
||||||
|
style={{
|
||||||
|
clipPath:
|
||||||
|
'polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className="relative left-[calc(50%+3rem)] aspect-[1155/678] w-[36.125rem] -translate-x-1/2 bg-gradient-to-tr from-[#ff80b5] to-[#9089fc] opacity-30 sm:left-[calc(50%+36rem)] sm:w-[72.1875rem]"
|
||||||
|
style={{
|
||||||
|
clipPath:
|
||||||
|
'polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
{(()=>{
|
||||||
|
if ( !questions[ 0 ] )
|
||||||
|
return <></>;
|
||||||
|
return (index < 10 && questions[ index ]) ? <QuestionView question={questions[ index ]} state = {state} setState={setState}/> : <ResultScreen state={state}/>;
|
||||||
|
})()}
|
||||||
|
|
||||||
|
{/* {questions[ 0 ] ? ((index < 10 && questions[ index ]) ? <QuestionView question={questions[ index ]} state = {state} setState={setState}/> : <ResultScreen state={state}/>) : null} */}
|
||||||
|
|
||||||
|
<div
|
||||||
|
className="absolute inset-x-0 top-[calc(100%-13rem)] -z-10 transform-gpu overflow-hidden blur-3xl sm:top-[calc(100%-30rem)]"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Page;
|
87
app/learn/page.tsx
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
import { CheckIcon } from '@heroicons/react/20/solid'
|
||||||
|
import TopicCard from '../components/topic_card'
|
||||||
|
import Logo from '../components/logo';
|
||||||
|
import { Transition } from '@headlessui/react';
|
||||||
|
|
||||||
|
const includedFeatures = [
|
||||||
|
'Private forum access',
|
||||||
|
'Member resources',
|
||||||
|
'Entry to annual conference',
|
||||||
|
'Official member t-shirt',
|
||||||
|
]
|
||||||
|
|
||||||
|
export default function Example() {
|
||||||
|
|
||||||
|
//List of topics
|
||||||
|
const cards = [
|
||||||
|
["Learn cybersecurity", "Learning cybersecurity is like becoming a guardian of the digital realm.\nIt's about mastering the art of protecting information, systems, and networks from cyber threats.\nFrom understanding encryption algorithms to detecting malware, every lesson equips you with tools to fortify against cyber-attacks.", "/learn/cybersec" ],
|
||||||
|
["Learn geography", "Geography is the study of our world's intricate tapestry — from the towering peaks of the Himalayas to the vast expanse of the Sahara Desert. It's about understanding the relationships between people, places, and the environment.", "/learn/geography"],
|
||||||
|
["Learn CPP", "As a powerful, versatile language, CPP teaches you the fundamentals of object-oriented programming while offering fine-grained control over system resources. You'll explore concepts like classes, inheritance, and polymorphism, alongside memory management and pointers.", "/learn/cpp"]
|
||||||
|
];
|
||||||
|
return (
|
||||||
|
|
||||||
|
<div className="py-24 sm:py-32" style = {{ alignItems: 'center', justifyContent: 'center' }}>
|
||||||
|
{/*Background*/}
|
||||||
|
<div
|
||||||
|
className="absolute inset-x-0 -top-40 -z-10 transform-gpu overflow-hidden blur-3xl sm:-top-80"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="relative left-[calc(50%-11rem)] aspect-[1155/678] w-[36.125rem] -translate-x-1/2 rotate-[30deg] bg-gradient-to-tr from-[#ff80b5] to-[#9089fc] opacity-30 sm:left-[calc(50%-30rem)] sm:w-[72.1875rem]"
|
||||||
|
style={{
|
||||||
|
clipPath:
|
||||||
|
'polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className="relative left-[calc(50%+3rem)] aspect-[1155/678] w-[36.125rem] -translate-x-1/2 bg-gradient-to-tr from-[#ff80b5] to-[#9089fc] opacity-30 sm:left-[calc(50%+36rem)] sm:w-[72.1875rem]"
|
||||||
|
style={{
|
||||||
|
clipPath:
|
||||||
|
'polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/*The logo*/}
|
||||||
|
<Logo/>
|
||||||
|
<div style={{paddingTop: '128px'}}></div>
|
||||||
|
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||||
|
<Transition
|
||||||
|
appear={true}
|
||||||
|
show={true}
|
||||||
|
enter="transition-opacity duration-300"
|
||||||
|
enterFrom="opacity-0"
|
||||||
|
enterTo="opacity-80">
|
||||||
|
<div>
|
||||||
|
{ cards.map((elem) => {
|
||||||
|
return (
|
||||||
|
<TopicCard title={elem[0]} link={elem[2]}>{elem[1]}</TopicCard>
|
||||||
|
)})}
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
|
{/*
|
||||||
|
<TopicCard title = "Learn cybersecurity">
|
||||||
|
Learning cybersecurity is like becoming a guardian of the digital realm.
|
||||||
|
It's about mastering the art of protecting information, systems, and networks from cyber threats.
|
||||||
|
From understanding encryption algorithms to detecting malware, every lesson equips you with tools to fortify against cyber-attacks.
|
||||||
|
</TopicCard>
|
||||||
|
<TopicCard title = "Learn geography">
|
||||||
|
Learning cybersecurity is like becoming a guardian of the digital realm.
|
||||||
|
It's about mastering the art of protecting information, systems, and networks from cyber threats.
|
||||||
|
From understanding encryption algorithms to detecting malware, every lesson equips you with tools to fortify against cyber-attacks.
|
||||||
|
</TopicCard>
|
||||||
|
<TopicCard title = "Learn C++">
|
||||||
|
Learning cybersecurity is like becoming a guardian of the digital realm.
|
||||||
|
It's about mastering the art of protecting information, systems, and networks from cyber threats.
|
||||||
|
From understanding encryption algorithms to detecting malware, every lesson equips you with tools to fortify against cyber-attacks.
|
||||||
|
</TopicCard>
|
||||||
|
*/}
|
||||||
|
</div>
|
||||||
|
{/*Background*/}
|
||||||
|
<div
|
||||||
|
className="absolute inset-x-0 top-[calc(100%-13rem)] -z-10 transform-gpu overflow-hidden blur-3xl sm:top-[calc(100%-30rem)]"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
33
app/lib/api.ts
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
// the base URL of the api; all requests start with it
|
||||||
|
const base_url: string = "https://api.phite.ro/";
|
||||||
|
|
||||||
|
/*
|
||||||
|
export const makeGetRequest = async<T> (path: string): Promise<T> => {
|
||||||
|
return await (await fetch( base_url + path )).json();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// generic function that makes a web request
|
||||||
|
const makeRequest = async (path: string, method: string, body?: string): Promise<Response> => {
|
||||||
|
console.log( `making ${method} request: ${base_url + path}, ${body}` );
|
||||||
|
return fetch( base_url + path, { method: method, body: body ? body : null } );
|
||||||
|
}
|
||||||
|
|
||||||
|
// the following functions are wrappers around `makeRequest`,
|
||||||
|
// for every type of request that's needed.
|
||||||
|
|
||||||
|
export const makeGetRequest = async<T> (path: string): Promise<T> => {
|
||||||
|
return (await makeRequest( path, "GET" )).json();
|
||||||
|
}
|
||||||
|
|
||||||
|
export const makePostRequest = async<T> (path: string, body: T): Promise<T> => {
|
||||||
|
return (await makeRequest( path, "POST", JSON.stringify( body ) )).json();
|
||||||
|
}
|
||||||
|
|
||||||
|
export const makePutRequest = async<T> (path: string, body: T): Promise<T> => {
|
||||||
|
return (await makeRequest( path, "PUT", JSON.stringify( body ) )).json();
|
||||||
|
}
|
||||||
|
|
||||||
|
export const makeDeleteRequest = async<T> (path: string): Promise<T> => {
|
||||||
|
return (await makeRequest( path, "DELETE" )).json();
|
||||||
|
}
|
5
app/lib/delay.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
const wait = (time: number): Promise<void> => {
|
||||||
|
return new Promise( res => setTimeout( res, time ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
export default wait;
|
44
app/lib/question.ts
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import { makeDeleteRequest, makeGetRequest, makePostRequest } from "./api";
|
||||||
|
|
||||||
|
export const enum QuizQuestion {
|
||||||
|
UL,
|
||||||
|
UR,
|
||||||
|
DL,
|
||||||
|
DR
|
||||||
|
};
|
||||||
|
|
||||||
|
export const enum AnswerType {
|
||||||
|
Unset,
|
||||||
|
Right,
|
||||||
|
Wrong
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Question = {
|
||||||
|
id: number;
|
||||||
|
category: string | null;
|
||||||
|
type?: string; // TODO: make a TS type for every question type
|
||||||
|
text: string;
|
||||||
|
answer: string | boolean | QuizQuestion;
|
||||||
|
answered?: AnswerType;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getQuestion = async (id: number): Promise<Question> => {
|
||||||
|
return makeGetRequest( `questions/${id}/` );
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getQuestions = async (): Promise<Question[]> => {
|
||||||
|
return makeGetRequest( `questions/` );
|
||||||
|
}
|
||||||
|
|
||||||
|
export const addQuestion = async (question: Question): Promise<Question> => {
|
||||||
|
console.log( `adding question ${JSON.stringify( question)}` );
|
||||||
|
return makePostRequest( `questions`, question );
|
||||||
|
}
|
||||||
|
|
||||||
|
export const editQuestion = async (question: Question): Promise<Question> => {
|
||||||
|
return makePostRequest( `questions/${question.id}`, question );
|
||||||
|
}
|
||||||
|
|
||||||
|
export const deleteQuestion = async (id: number): Promise<Question> => {
|
||||||
|
return makeDeleteRequest( `questions/${id}` );
|
||||||
|
}
|
27
app/lib/session.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import { makeGetRequest, makePostRequest } from "./api";
|
||||||
|
import { AnswerType } from "./question";
|
||||||
|
|
||||||
|
export type Session = {
|
||||||
|
id: number;
|
||||||
|
right: number;
|
||||||
|
wrong: number;
|
||||||
|
total: number;
|
||||||
|
// user: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SessionState = {
|
||||||
|
answered: number;
|
||||||
|
wrong: number;
|
||||||
|
right: number;
|
||||||
|
category: string;
|
||||||
|
opened: boolean[];
|
||||||
|
answer: AnswerType[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getSession = async (id: number): Promise<Session> => {
|
||||||
|
return makeGetRequest( `sessions/${id}/` );
|
||||||
|
}
|
||||||
|
|
||||||
|
export const postSession = async (session: Session): Promise<Session> => {
|
||||||
|
return makePostRequest( `sessions`, session );
|
||||||
|
}
|
213
app/page.tsx
|
@ -1,113 +1,102 @@
|
||||||
import Image from "next/image";
|
'use client';
|
||||||
|
import { Fragment, useState } from 'react'
|
||||||
export default function Home() {
|
import { Menu, MenuButton, MenuItem, MenuItems, Transition } from '@headlessui/react'
|
||||||
return (
|
import { ChevronDownIcon } from '@heroicons/react/20/solid'
|
||||||
<main className="flex min-h-screen flex-col items-center justify-between p-24">
|
import Logo from './components/logo';
|
||||||
<div className="z-10 w-full max-w-5xl items-center justify-between font-mono text-sm lg:flex">
|
import { motion } from 'framer-motion';
|
||||||
<p className="fixed left-0 top-0 flex w-full justify-center border-b border-gray-300 bg-gradient-to-b from-zinc-200 pb-6 pt-8 backdrop-blur-2xl dark:border-neutral-800 dark:bg-zinc-800/30 dark:from-inherit lg:static lg:w-auto lg:rounded-xl lg:border lg:bg-gray-200 lg:p-4 lg:dark:bg-zinc-800/30">
|
import LinkButton from './components/link-button';
|
||||||
Get started by editing
|
function classNames(...classes) {
|
||||||
<code className="font-mono font-bold">app/page.tsx</code>
|
return classes.filter(Boolean).join(' ')
|
||||||
</p>
|
}
|
||||||
<div className="fixed bottom-0 left-0 flex h-48 w-full items-end justify-center bg-gradient-to-t from-white via-white dark:from-black dark:via-black lg:static lg:size-auto lg:bg-none">
|
export default function Example() {
|
||||||
<a
|
const [buttonPressed, setButtonPressed] = useState( false );
|
||||||
className="pointer-events-none flex place-items-center gap-2 p-8 lg:pointer-events-auto lg:p-0"
|
return (
|
||||||
href="https://vercel.com?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
<div>
|
||||||
target="_blank"
|
{/*Background visual elements*/}
|
||||||
rel="noopener noreferrer"
|
<div
|
||||||
>
|
className="absolute inset-x-0 -top-40 -z-10 transform-gpu overflow-hidden blur-3xl sm:-top-80"
|
||||||
By{" "}
|
aria-hidden="true"
|
||||||
<Image
|
>
|
||||||
src="/vercel.svg"
|
<div
|
||||||
alt="Vercel Logo"
|
className="relative left-[calc(50%-11rem)] aspect-[1155/678] w-[36.125rem] -translate-x-1/2 rotate-[30deg] bg-gradient-to-tr from-[#ff80b5] to-[#9089fc] opacity-30 sm:left-[calc(50%-30rem)] sm:w-[72.1875rem]"
|
||||||
className="dark:invert"
|
style={{
|
||||||
width={100}
|
clipPath:
|
||||||
height={24}
|
'polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)',
|
||||||
priority
|
}}
|
||||||
/>
|
/>
|
||||||
</a>
|
<div
|
||||||
</div>
|
className="relative left-[calc(50%+3rem)] aspect-[1155/678] w-[36.125rem] -translate-x-1/2 bg-gradient-to-tr from-[#ff80b5] to-[#9089fc] opacity-30 sm:left-[calc(50%+36rem)] sm:w-[72.1875rem]"
|
||||||
</div>
|
style={{
|
||||||
|
clipPath:
|
||||||
<div className="relative z-[-1] flex place-items-center before:absolute before:h-[300px] before:w-full before:-translate-x-1/2 before:rounded-full before:bg-gradient-radial before:from-white before:to-transparent before:blur-2xl before:content-[''] after:absolute after:-z-20 after:h-[180px] after:w-full after:translate-x-1/3 after:bg-gradient-conic after:from-sky-200 after:via-blue-200 after:blur-2xl after:content-[''] before:dark:bg-gradient-to-br before:dark:from-transparent before:dark:to-blue-700 before:dark:opacity-10 after:dark:from-sky-900 after:dark:via-[#0141ff] after:dark:opacity-40 sm:before:w-[480px] sm:after:w-[240px] before:lg:h-[360px]">
|
'polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)',
|
||||||
<Image
|
}}
|
||||||
className="relative dark:drop-shadow-[0_0_0.3rem_#ffffff70] dark:invert"
|
/>
|
||||||
src="/next.svg"
|
</div>
|
||||||
alt="Next.js Logo"
|
<div style={{paddingTop: '128px'}}></div>
|
||||||
width={180}
|
<Logo/>
|
||||||
height={37}
|
<Transition
|
||||||
priority
|
appear={true}
|
||||||
/>
|
show={!buttonPressed}
|
||||||
</div>
|
enter="transition-opacity duration-500"
|
||||||
|
enterFrom="opacity-0"
|
||||||
<div className="mb-32 grid text-center lg:mb-0 lg:w-full lg:max-w-5xl lg:grid-cols-4 lg:text-left">
|
enterTo="opacity-80"
|
||||||
<a
|
leave="transition-opacity duration-150"
|
||||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
leaveFrom="opacity-80"
|
||||||
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
|
leaveTo="opacity-0">
|
||||||
target="_blank"
|
<div className="mx-auto max-w-7xl py-24 sm:px-6 sm:py-32 lg:px-8">
|
||||||
rel="noopener noreferrer"
|
<div className="relative isolate overflow-hidden bg-gray-900 px-6 pt-16 shadow-2xl sm:rounded-3xl sm:px-16 md:pt-24 lg:flex lg:gap-x-20 lg:px-24 lg:pt-0">
|
||||||
>
|
<svg
|
||||||
<h2 className="mb-3 text-2xl font-semibold">
|
viewBox="0 0 1024 1024"
|
||||||
Docs{" "}
|
className="absolute left-1/2 top-1/2 -z-10 h-[64rem] w-[64rem] -translate-y-1/2 [mask-image:radial-gradient(closest-side,white,transparent)] sm:left-full sm:-ml-80 lg:left-1/2 lg:ml-0 lg:-translate-x-1/2 lg:translate-y-0"
|
||||||
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
|
aria-hidden="true"
|
||||||
->
|
>
|
||||||
</span>
|
<circle cx={512} cy={512} r={512} fill="url(#759c1415-0410-454c-8f7c-9a820de03641)" fillOpacity="0.7" />
|
||||||
</h2>
|
<defs>
|
||||||
<p className="m-0 max-w-[30ch] text-sm opacity-50">
|
<radialGradient id="759c1415-0410-454c-8f7c-9a820de03641">
|
||||||
Find in-depth information about Next.js features and API.
|
<stop stopColor="#7775D6" />
|
||||||
</p>
|
<stop offset={1} stopColor="#E935C1" />
|
||||||
</a>
|
</radialGradient>
|
||||||
|
</defs>
|
||||||
<a
|
</svg>
|
||||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
<div className="mx-auto max-w-md text-center lg:mx-0 lg:flex-auto lg:py-32 lg:text-left">
|
||||||
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
|
<h2 className="text-3xl font-bold tracking-tight text-white sm:text-4xl">
|
||||||
target="_blank"
|
Start using flash cards
|
||||||
rel="noopener noreferrer"
|
<br />
|
||||||
>
|
Begin with our app today.
|
||||||
<h2 className="mb-3 text-2xl font-semibold">
|
</h2>
|
||||||
Learn{" "}
|
<p className="mt-6 text-lg leading-8 text-gray-300">
|
||||||
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
|
Get questions from a multitude of different subjects that include cybersecurity, geography and C++
|
||||||
->
|
</p>
|
||||||
</span>
|
<div className="mt-10 flex items-center justify-center gap-x-6 lg:justify-start">
|
||||||
</h2>
|
{/* <motion.a
|
||||||
<p className="m-0 max-w-[30ch] text-sm opacity-50">
|
href="sessionconfig"
|
||||||
Learn about Next.js in an interactive course with quizzes!
|
className="rounded-md bg-white px-3.5 py-2.5 text-sm font-semibold text-gray-900 shadow-sm hover:bg-gray-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white"
|
||||||
</p>
|
style={{ marginBottom: '40px' }}
|
||||||
</a>
|
animate={{opacity: buttonHovered ? 0.8 : 1}}
|
||||||
|
onMouseEnter={()=>setButtonHovered(true)}
|
||||||
<a
|
onMouseLeave={()=>setButtonHovered(false)}
|
||||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
onClick={()=>setButtonPressed(true)}
|
||||||
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
|
>
|
||||||
target="_blank"
|
Get started
|
||||||
rel="noopener noreferrer"
|
</motion.a> */}
|
||||||
>
|
<LinkButton text="Get started" href="/sessionconfig" onPress={()=>setButtonPressed(true)}/>
|
||||||
<h2 className="mb-3 text-2xl font-semibold">
|
</div>
|
||||||
Templates{" "}
|
</div>
|
||||||
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
|
</div>
|
||||||
->
|
</div>
|
||||||
</span>
|
</Transition>
|
||||||
</h2>
|
<div
|
||||||
<p className="m-0 max-w-[30ch] text-sm opacity-50">
|
className="absolute inset-x-0 top-[calc(100%-13rem)] -z-10 transform-gpu overflow-hidden blur-3xl sm:top-[calc(100%-30rem)]"
|
||||||
Explore starter templates for Next.js.
|
aria-hidden="true"
|
||||||
</p>
|
>
|
||||||
</a>
|
<div
|
||||||
|
className="relative left-[calc(50%+3rem)] aspect-[1155/678] w-[36.125rem] -translate-x-1/2 bg-gradient-to-tr from-[#ff80b5] to-[#9089fc] opacity-30 sm:left-[calc(50%+36rem)] sm:w-[72.1875rem]"
|
||||||
<a
|
style={{
|
||||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
clipPath:
|
||||||
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
|
'polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)',
|
||||||
target="_blank"
|
}}
|
||||||
rel="noopener noreferrer"
|
/>
|
||||||
>
|
</div>
|
||||||
<h2 className="mb-3 text-2xl font-semibold">
|
</div>
|
||||||
Deploy{" "}
|
)
|
||||||
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
|
|
||||||
->
|
|
||||||
</span>
|
|
||||||
</h2>
|
|
||||||
<p className="m-0 max-w-[30ch] text-balance text-sm opacity-50">
|
|
||||||
Instantly deploy your Next.js site to a shareable URL with Vercel.
|
|
||||||
</p>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
87
app/sessionconfig/page.tsx
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
import { CheckIcon } from '@heroicons/react/20/solid'
|
||||||
|
import TopicCard from '../components/topic_card'
|
||||||
|
import Logo from '../components/logo';
|
||||||
|
import { Transition } from '@headlessui/react';
|
||||||
|
|
||||||
|
const includedFeatures = [
|
||||||
|
'Private forum access',
|
||||||
|
'Member resources',
|
||||||
|
'Entry to annual conference',
|
||||||
|
'Official member t-shirt',
|
||||||
|
]
|
||||||
|
|
||||||
|
export default function Example() {
|
||||||
|
|
||||||
|
//List of topics
|
||||||
|
const cards = [
|
||||||
|
["Learn cybersecurity", "Learning cybersecurity is like becoming a guardian of the digital realm.\nIt's about mastering the art of protecting information, systems, and networks from cyber threats.\nFrom understanding encryption algorithms to detecting malware, every lesson equips you with tools to fortify against cyber-attacks.", "/learn/cybersec" ],
|
||||||
|
["Learn geography", "Geography is the study of our world's intricate tapestry — from the towering peaks of the Himalayas to the vast expanse of the Sahara Desert. It's about understanding the relationships between people, places and the environment.", "/learn/geography"],
|
||||||
|
["Learn CPP", "As a powerful, versatile language, C++ teaches you the fundamentals of object-oriented programming while offering fine-grained control over system resources. You'll explore concepts like classes, inheritance, and polymorphism, alongside memory management and pointers.", "/learn/cpp"]
|
||||||
|
];
|
||||||
|
return (
|
||||||
|
|
||||||
|
<div className="py-24 sm:py-32" style = {{ alignItems: 'center', justifyContent: 'center' }}>
|
||||||
|
{/*Background*/}
|
||||||
|
<div
|
||||||
|
className="absolute inset-x-0 -top-40 -z-10 transform-gpu overflow-hidden blur-3xl sm:-top-80"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="relative left-[calc(50%-11rem)] aspect-[1155/678] w-[36.125rem] -translate-x-1/2 rotate-[30deg] bg-gradient-to-tr from-[#ff80b5] to-[#9089fc] opacity-30 sm:left-[calc(50%-30rem)] sm:w-[72.1875rem]"
|
||||||
|
style={{
|
||||||
|
clipPath:
|
||||||
|
'polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className="relative left-[calc(50%+3rem)] aspect-[1155/678] w-[36.125rem] -translate-x-1/2 bg-gradient-to-tr from-[#ff80b5] to-[#9089fc] opacity-30 sm:left-[calc(50%+36rem)] sm:w-[72.1875rem]"
|
||||||
|
style={{
|
||||||
|
clipPath:
|
||||||
|
'polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/*The logo*/}
|
||||||
|
<Logo/>
|
||||||
|
<div style={{paddingTop: '128px'}}></div>
|
||||||
|
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||||
|
<Transition
|
||||||
|
appear={true}
|
||||||
|
show={true}
|
||||||
|
enter="transition-opacity duration-300"
|
||||||
|
enterFrom="opacity-0"
|
||||||
|
enterTo="opacity-80">
|
||||||
|
<div>
|
||||||
|
{ cards.map((elem) => {
|
||||||
|
return (
|
||||||
|
<TopicCard title={elem[0]} link={elem[2]}>{elem[1]}</TopicCard>
|
||||||
|
)})}
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
|
{/*
|
||||||
|
<TopicCard title = "Learn cybersecurity">
|
||||||
|
Learning cybersecurity is like becoming a guardian of the digital realm.
|
||||||
|
It's about mastering the art of protecting information, systems, and networks from cyber threats.
|
||||||
|
From understanding encryption algorithms to detecting malware, every lesson equips you with tools to fortify against cyber-attacks.
|
||||||
|
</TopicCard>
|
||||||
|
<TopicCard title = "Learn geography">
|
||||||
|
Learning cybersecurity is like becoming a guardian of the digital realm.
|
||||||
|
It's about mastering the art of protecting information, systems, and networks from cyber threats.
|
||||||
|
From understanding encryption algorithms to detecting malware, every lesson equips you with tools to fortify against cyber-attacks.
|
||||||
|
</TopicCard>
|
||||||
|
<TopicCard title = "Learn C++">
|
||||||
|
Learning cybersecurity is like becoming a guardian of the digital realm.
|
||||||
|
It's about mastering the art of protecting information, systems, and networks from cyber threats.
|
||||||
|
From understanding encryption algorithms to detecting malware, every lesson equips you with tools to fortify against cyber-attacks.
|
||||||
|
</TopicCard>
|
||||||
|
*/}
|
||||||
|
</div>
|
||||||
|
{/*Background*/}
|
||||||
|
<div
|
||||||
|
className="absolute inset-x-0 top-[calc(100%-13rem)] -z-10 transform-gpu overflow-hidden blur-3xl sm:top-[calc(100%-30rem)]"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
40
package-lock.json
generated
|
@ -10,9 +10,11 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@headlessui/react": "^2.0.4",
|
"@headlessui/react": "^2.0.4",
|
||||||
"@heroicons/react": "^2.1.3",
|
"@heroicons/react": "^2.1.3",
|
||||||
|
"framer-motion": "^11.2.10",
|
||||||
"next": "14.2.3",
|
"next": "14.2.3",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
"react-dom": "^18"
|
"react-dom": "^18",
|
||||||
|
"react-use-wizard": "^2.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
|
@ -2244,6 +2246,30 @@
|
||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/framer-motion": {
|
||||||
|
"version": "11.2.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.2.10.tgz",
|
||||||
|
"integrity": "sha512-/gr3PLZUVFCc86a9MqCUboVrALscrdluzTb3yew+2/qKBU8CX6nzs918/SRBRCqaPbx0TZP10CB6yFgK2C5cYQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@emotion/is-prop-valid": "*",
|
||||||
|
"react": "^18.0.0",
|
||||||
|
"react-dom": "^18.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@emotion/is-prop-valid": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fs.realpath": {
|
"node_modules/fs.realpath": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||||
|
@ -3911,6 +3937,18 @@
|
||||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/react-use-wizard": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-use-wizard/-/react-use-wizard-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-z06pQNZ18FvoK+ZDW50hOf0uOySo0Hhd7H4y0MLyk/tBCwpUKGBeULosORBVhipUaWihNLpLJxwH5f+85Qt5XA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.8.0",
|
||||||
|
"react-dom": ">=16.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/read-cache": {
|
"node_modules/read-cache": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||||
|
|
|
@ -11,9 +11,11 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@headlessui/react": "^2.0.4",
|
"@headlessui/react": "^2.0.4",
|
||||||
"@heroicons/react": "^2.1.3",
|
"@heroicons/react": "^2.1.3",
|
||||||
|
"framer-motion": "^11.2.10",
|
||||||
"next": "14.2.3",
|
"next": "14.2.3",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
"react-dom": "^18"
|
"react-dom": "^18",
|
||||||
|
"react-use-wizard": "^2.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
|
|
BIN
public/alex.png
Normal file
After Width: | Height: | Size: 5.9 KiB |
BIN
public/andrei.jpg
Normal file
After Width: | Height: | Size: 1 MiB |
BIN
public/arch.png
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
public/blahaj.avif
Normal file
BIN
public/favicon.ico
Normal file
After Width: | Height: | Size: 258 KiB |
BIN
public/ioan.jpg
Normal file
After Width: | Height: | Size: 474 KiB |
BIN
public/logo-no-background-favicon.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
public/logo-no-background.png
Normal file
After Width: | Height: | Size: 25 KiB |
|
@ -12,6 +12,7 @@
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"jsx": "preserve",
|
"jsx": "preserve",
|
||||||
"incremental": true,
|
"incremental": true,
|
||||||
|
"noImplicitAny": false,
|
||||||
"plugins": [
|
"plugins": [
|
||||||
{
|
{
|
||||||
"name": "next"
|
"name": "next"
|
||||||
|
@ -21,6 +22,6 @@
|
||||||
"@/*": ["./*"]
|
"@/*": ["./*"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "app/components/Dropdown.js"],
|
||||||
"exclude": ["node_modules"]
|
"exclude": ["node_modules"]
|
||||||
}
|
}
|
||||||
|
|