Spaces:
Running
Running
Add 3 files
Browse files- README.md +5 -3
- index.html +1634 -19
- prompts.txt +3 -0
README.md
CHANGED
@@ -1,10 +1,12 @@
|
|
1 |
---
|
2 |
-
title:
|
3 |
-
emoji:
|
4 |
colorFrom: blue
|
5 |
colorTo: blue
|
6 |
sdk: static
|
7 |
pinned: false
|
|
|
|
|
8 |
---
|
9 |
|
10 |
-
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
1 |
---
|
2 |
+
title: wjec-revision-dashboard
|
3 |
+
emoji: π³
|
4 |
colorFrom: blue
|
5 |
colorTo: blue
|
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
|
index.html
CHANGED
@@ -1,19 +1,1634 @@
|
|
1 |
-
<!
|
2 |
-
<html>
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html lang="en" class="light">
|
3 |
+
<head>
|
4 |
+
<meta charset="UTF-8">
|
5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 |
+
<title>WJEC A-Level Revision Dashboard</title>
|
7 |
+
<script src="https://cdn.tailwindcss.com"></script>
|
8 |
+
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
9 |
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
10 |
+
<style>
|
11 |
+
.radar-chart-container {
|
12 |
+
position: relative;
|
13 |
+
height: 300px;
|
14 |
+
width: 100%;
|
15 |
+
}
|
16 |
+
|
17 |
+
.task-progress {
|
18 |
+
transition: width 0.5s ease-in-out;
|
19 |
+
}
|
20 |
+
|
21 |
+
.dark .dark\:bg-gray-800 {
|
22 |
+
background-color: #1f2937;
|
23 |
+
}
|
24 |
+
|
25 |
+
.dark .dark\:bg-gray-700 {
|
26 |
+
background-color: #374151;
|
27 |
+
}
|
28 |
+
|
29 |
+
.dark .dark\:text-white {
|
30 |
+
color: #f9fafb;
|
31 |
+
}
|
32 |
+
|
33 |
+
.dark .dark\:border-gray-600 {
|
34 |
+
border-color: #4b5563;
|
35 |
+
}
|
36 |
+
|
37 |
+
.profile-icon {
|
38 |
+
background: linear-gradient(135deg, #3b82f6, #8b5cf6);
|
39 |
+
}
|
40 |
+
|
41 |
+
.tab-active {
|
42 |
+
border-bottom: 3px solid #8b5cf6;
|
43 |
+
color: #8b5cf6;
|
44 |
+
font-weight: 600;
|
45 |
+
}
|
46 |
+
|
47 |
+
.dark .tab-active {
|
48 |
+
border-bottom: 3px solid #8b5cf6;
|
49 |
+
color: #8b5cf6;
|
50 |
+
}
|
51 |
+
|
52 |
+
.checkbox-container input:checked ~ .checkmark {
|
53 |
+
background-color: #8b5cf6;
|
54 |
+
border-color: #8b5cf6;
|
55 |
+
}
|
56 |
+
|
57 |
+
.checkbox-container .checkmark:after {
|
58 |
+
content: "";
|
59 |
+
position: absolute;
|
60 |
+
display: none;
|
61 |
+
left: 6px;
|
62 |
+
top: 2px;
|
63 |
+
width: 5px;
|
64 |
+
height: 10px;
|
65 |
+
border: solid white;
|
66 |
+
border-width: 0 2px 2px 0;
|
67 |
+
transform: rotate(45deg);
|
68 |
+
}
|
69 |
+
|
70 |
+
.checkbox-container input:checked ~ .checkmark:after {
|
71 |
+
display: block;
|
72 |
+
}
|
73 |
+
|
74 |
+
.activity-icon {
|
75 |
+
width: 36px;
|
76 |
+
height: 36px;
|
77 |
+
border-radius: 50%;
|
78 |
+
display: flex;
|
79 |
+
align-items: center;
|
80 |
+
justify-content: center;
|
81 |
+
}
|
82 |
+
|
83 |
+
.exam-pill {
|
84 |
+
transition: all 0.2s ease;
|
85 |
+
}
|
86 |
+
|
87 |
+
.exam-pill:hover {
|
88 |
+
transform: translateY(-2px);
|
89 |
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
90 |
+
}
|
91 |
+
|
92 |
+
.radar-tooltip {
|
93 |
+
position: absolute;
|
94 |
+
background: white;
|
95 |
+
border: 1px solid #ddd;
|
96 |
+
border-radius: 8px;
|
97 |
+
padding: 10px;
|
98 |
+
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
99 |
+
z-index: 100;
|
100 |
+
pointer-events: none;
|
101 |
+
display: none;
|
102 |
+
}
|
103 |
+
</style>
|
104 |
+
</head>
|
105 |
+
<body class="bg-gray-50 text-gray-800 min-h-screen transition-colors duration-300">
|
106 |
+
<div class="container mx-auto px-4 py-6 max-w-7xl">
|
107 |
+
<!-- Header -->
|
108 |
+
<header class="flex justify-between items-center mb-8">
|
109 |
+
<h1 class="text-3xl font-bold text-indigo-600">WJEC Revision Dashboard</h1>
|
110 |
+
<div class="flex items-center space-x-4">
|
111 |
+
<div class="relative">
|
112 |
+
<button id="theme-toggle" class="p-2 rounded-full bg-gray-200 dark:bg-gray-700">
|
113 |
+
<i class="fas fa-moon dark:hidden"></i>
|
114 |
+
<i class="fas fa-sun hidden dark:block text-yellow-300"></i>
|
115 |
+
</button>
|
116 |
+
</div>
|
117 |
+
<div class="profile-icon w-10 h-10 rounded-full flex items-center justify-center text-white font-bold cursor-pointer hover:shadow-lg transition-shadow">
|
118 |
+
M
|
119 |
+
</div>
|
120 |
+
</div>
|
121 |
+
</header>
|
122 |
+
|
123 |
+
<!-- Main Content -->
|
124 |
+
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
125 |
+
<!-- Left Column -->
|
126 |
+
<div class="lg:col-span-2 space-y-6">
|
127 |
+
<!-- Tabs Navigation -->
|
128 |
+
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-1">
|
129 |
+
<div class="flex border-b border-gray-200 dark:border-gray-700">
|
130 |
+
<button class="tab-button py-2 px-4 text-sm font-medium" data-tab="tasks">Tasks</button>
|
131 |
+
<button class="tab-button py-2 px-4 text-sm font-medium" data-tab="past-papers">Past Papers</button>
|
132 |
+
<button class="tab-button py-2 px-4 text-sm font-medium" data-tab="progress">Progress</button>
|
133 |
+
<button class="tab-button py-2 px-4 text-sm font-medium" data-tab="exams">Exams</button>
|
134 |
+
</div>
|
135 |
+
|
136 |
+
<!-- Tasks Tab -->
|
137 |
+
<div id="tasks-tab" class="tab-content p-4">
|
138 |
+
<div class="flex justify-between items-center mb-4">
|
139 |
+
<h2 class="text-xl font-semibold">My Revision Tasks</h2>
|
140 |
+
<button id="add-task-btn" class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-lg flex items-center">
|
141 |
+
<i class="fas fa-plus mr-2"></i> Add Task
|
142 |
+
</button>
|
143 |
+
</div>
|
144 |
+
|
145 |
+
<!-- Task Filters -->
|
146 |
+
<div class="mb-4 flex flex-wrap gap-2">
|
147 |
+
<button class="filter-btn px-3 py-1 rounded-full text-xs bg-indigo-100 text-indigo-800" data-filter="all">All</button>
|
148 |
+
<button class="filter-btn px-3 py-1 rounded-full text-xs bg-blue-100 text-blue-800" data-filter="Biology">Biology</button>
|
149 |
+
<button class="filter-btn px-3 py-1 rounded-full text-xs bg-green-100 text-green-800" data-filter="Chemistry">Chemistry</button>
|
150 |
+
<button class="filter-btn px-3 py-1 rounded-full text-xs bg-yellow-100 text-yellow-800" data-filter="Maths">Maths</button>
|
151 |
+
<button class="filter-btn px-3 py-1 rounded-full text-xs bg-purple-100 text-purple-800" data-filter="Flashcards">Flashcards</button>
|
152 |
+
<button class="filter-btn px-3 py-1 rounded-full text-xs bg-pink-100 text-pink-800" data-filter="Past Papers">Past Papers</button>
|
153 |
+
<button class="filter-btn px-3 py-1 rounded-full text-xs bg-gray-100 text-gray-800" data-filter="Revision">Revision</button>
|
154 |
+
</div>
|
155 |
+
|
156 |
+
<!-- Task List -->
|
157 |
+
<div id="task-list" class="space-y-3">
|
158 |
+
<!-- Tasks will be dynamically inserted here -->
|
159 |
+
</div>
|
160 |
+
</div>
|
161 |
+
|
162 |
+
<!-- Past Papers Tab -->
|
163 |
+
<div id="past-papers-tab" class="tab-content hidden p-4">
|
164 |
+
<div class="flex justify-between items-center mb-4">
|
165 |
+
<h2 class="text-xl font-semibold">Past Paper Library (2017-2023)</h2>
|
166 |
+
<div class="relative">
|
167 |
+
<input type="text" id="paper-search" placeholder="Search papers..." class="pl-8 pr-4 py-2 border rounded-lg w-64">
|
168 |
+
<i class="fas fa-search absolute left-3 top-3 text-gray-400"></i>
|
169 |
+
</div>
|
170 |
+
</div>
|
171 |
+
|
172 |
+
<!-- Paper Filters -->
|
173 |
+
<div class="mb-4 flex flex-wrap gap-2">
|
174 |
+
<select id="paper-subject" class="px-3 py-1 rounded-lg border text-sm">
|
175 |
+
<option value="all">All Subjects</option>
|
176 |
+
<option value="Biology">Biology</option>
|
177 |
+
<option value="Chemistry">Chemistry</option>
|
178 |
+
<option value="Maths">Maths</option>
|
179 |
+
</select>
|
180 |
+
<select id="paper-year" class="px-3 py-1 rounded-lg border text-sm">
|
181 |
+
<option value="all">All Years</option>
|
182 |
+
<option value="2023">2023</option>
|
183 |
+
<option value="2022">2022</option>
|
184 |
+
<option value="2021">2021</option>
|
185 |
+
<option value="2020">2020</option>
|
186 |
+
<option value="2019">2019</option>
|
187 |
+
<option value="2018">2018</option>
|
188 |
+
<option value="2017">2017</option>
|
189 |
+
</select>
|
190 |
+
<select id="paper-status" class="px-3 py-1 rounded-lg border text-sm">
|
191 |
+
<option value="all">All Statuses</option>
|
192 |
+
<option value="Not Started">Not Started</option>
|
193 |
+
<option value="In Progress">In Progress</option>
|
194 |
+
<option value="Completed">Completed</option>
|
195 |
+
</select>
|
196 |
+
</div>
|
197 |
+
|
198 |
+
<!-- Papers List -->
|
199 |
+
<div id="papers-list" class="space-y-3">
|
200 |
+
<!-- Papers will be dynamically inserted here -->
|
201 |
+
</div>
|
202 |
+
</div>
|
203 |
+
|
204 |
+
<!-- Progress Tab -->
|
205 |
+
<div id="progress-tab" class="tab-content hidden p-4">
|
206 |
+
<h2 class="text-xl font-semibold mb-4">Progress Overview</h2>
|
207 |
+
|
208 |
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
|
209 |
+
<div class="bg-white dark:bg-gray-700 p-4 rounded-lg shadow">
|
210 |
+
<h3 class="font-medium mb-2">Biology Progress</h3>
|
211 |
+
<div class="radar-chart-container">
|
212 |
+
<canvas id="biology-radar"></canvas>
|
213 |
+
</div>
|
214 |
+
</div>
|
215 |
+
<div class="bg-white dark:bg-gray-700 p-4 rounded-lg shadow">
|
216 |
+
<h3 class="font-medium mb-2">Chemistry Progress</h3>
|
217 |
+
<div class="radar-chart-container">
|
218 |
+
<canvas id="chemistry-radar"></canvas>
|
219 |
+
</div>
|
220 |
+
</div>
|
221 |
+
<div class="bg-white dark:bg-gray-700 p-4 rounded-lg shadow">
|
222 |
+
<h3 class="font-medium mb-2">Maths Progress</h3>
|
223 |
+
<div class="radar-chart-container">
|
224 |
+
<canvas id="maths-radar"></canvas>
|
225 |
+
</div>
|
226 |
+
</div>
|
227 |
+
<div class="bg-white dark:bg-gray-700 p-4 rounded-lg shadow">
|
228 |
+
<h3 class="font-medium mb-2">Overall Completion</h3>
|
229 |
+
<div class="h-64 flex items-center justify-center">
|
230 |
+
<canvas id="overall-chart" class="max-h-full"></canvas>
|
231 |
+
</div>
|
232 |
+
</div>
|
233 |
+
</div>
|
234 |
+
</div>
|
235 |
+
|
236 |
+
<!-- Exams Tab -->
|
237 |
+
<div id="exams-tab" class="tab-content hidden p-4">
|
238 |
+
<div class="flex justify-between items-center mb-4">
|
239 |
+
<h2 class="text-xl font-semibold">Exam Countdown</h2>
|
240 |
+
<button id="add-exam-btn" class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-lg flex items-center">
|
241 |
+
<i class="fas fa-plus mr-2"></i> Add Exam
|
242 |
+
</button>
|
243 |
+
</div>
|
244 |
+
|
245 |
+
<div id="exams-list" class="space-y-4">
|
246 |
+
<!-- Exams will be dynamically inserted here -->
|
247 |
+
</div>
|
248 |
+
</div>
|
249 |
+
</div>
|
250 |
+
</div>
|
251 |
+
|
252 |
+
<!-- Right Column -->
|
253 |
+
<div class="space-y-6">
|
254 |
+
<!-- Upcoming Deadlines -->
|
255 |
+
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-4">
|
256 |
+
<h2 class="text-xl font-semibold mb-4">Upcoming Deadlines</h2>
|
257 |
+
<div id="deadlines-list" class="space-y-3">
|
258 |
+
<div class="p-3 bg-red-50 dark:bg-gray-700 rounded-lg border-l-4 border-red-500">
|
259 |
+
<div class="flex justify-between items-start">
|
260 |
+
<div>
|
261 |
+
<h3 class="font-medium">Biology Mock Exam</h3>
|
262 |
+
<p class="text-sm text-gray-600 dark:text-gray-300">Unit 3.1 Biological Molecules</p>
|
263 |
+
</div>
|
264 |
+
<span class="text-xs bg-red-100 dark:bg-red-900 text-red-800 dark:text-red-200 px-2 py-1 rounded">Tomorrow</span>
|
265 |
+
</div>
|
266 |
+
</div>
|
267 |
+
<div class="p-3 bg-yellow-50 dark:bg-gray-700 rounded-lg border-l-4 border-yellow-500">
|
268 |
+
<div class="flex justify-between items-start">
|
269 |
+
<div>
|
270 |
+
<h3 class="font-medium">Chemistry Practical</h3>
|
271 |
+
<p class="text-sm text-gray-600 dark:text-gray-300">Required Practical 4</p>
|
272 |
+
</div>
|
273 |
+
<span class="text-xs bg-yellow-100 dark:bg-yellow-900 text-yellow-800 dark:text-yellow-200 px-2 py-1 rounded">3 days</span>
|
274 |
+
</div>
|
275 |
+
</div>
|
276 |
+
<div class="p-3 bg-blue-50 dark:bg-gray-700 rounded-lg border-l-4 border-blue-500">
|
277 |
+
<div class="flex justify-between items-start">
|
278 |
+
<div>
|
279 |
+
<h3 class="font-medium">Maths Assignment</h3>
|
280 |
+
<p class="text-sm text-gray-600 dark:text-gray-300">Calculus Problems</p>
|
281 |
+
</div>
|
282 |
+
<span class="text-xs bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200 px-2 py-1 rounded">1 week</span>
|
283 |
+
</div>
|
284 |
+
</div>
|
285 |
+
</div>
|
286 |
+
</div>
|
287 |
+
|
288 |
+
<!-- Quick Stats -->
|
289 |
+
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-4">
|
290 |
+
<h2 class="text-xl font-semibold mb-4">Quick Stats</h2>
|
291 |
+
<div class="grid grid-cols-2 gap-4">
|
292 |
+
<div class="bg-indigo-50 dark:bg-gray-700 p-3 rounded-lg">
|
293 |
+
<p class="text-sm text-indigo-600 dark:text-indigo-400">Tasks Completed</p>
|
294 |
+
<p class="text-2xl font-bold" id="completed-tasks">0</p>
|
295 |
+
</div>
|
296 |
+
<div class="bg-green-50 dark:bg-gray-700 p-3 rounded-lg">
|
297 |
+
<p class="text-sm text-green-600 dark:text-green-400">Papers Attempted</p>
|
298 |
+
<p class="text-2xl font-bold" id="attempted-papers">0</p>
|
299 |
+
</div>
|
300 |
+
<div class="bg-purple-50 dark:bg-gray-700 p-3 rounded-lg">
|
301 |
+
<p class="text-sm text-purple-600 dark:text-purple-400">Biology Progress</p>
|
302 |
+
<p class="text-2xl font-bold" id="biology-progress">0%</p>
|
303 |
+
</div>
|
304 |
+
<div class="bg-blue-50 dark:bg-gray-700 p-3 rounded-lg">
|
305 |
+
<p class="text-sm text-blue-600 dark:text-blue-400">Days to Exams</p>
|
306 |
+
<p class="text-2xl font-bold" id="days-to-exams">42</p>
|
307 |
+
</div>
|
308 |
+
</div>
|
309 |
+
</div>
|
310 |
+
|
311 |
+
<!-- Recent Activity -->
|
312 |
+
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-4">
|
313 |
+
<h2 class="text-xl font-semibold mb-4">Recent Activity</h2>
|
314 |
+
<div class="space-y-3">
|
315 |
+
<div class="flex items-start space-x-3">
|
316 |
+
<div class="activity-icon bg-green-100 dark:bg-green-900">
|
317 |
+
<i class="fas fa-check text-green-600 dark:text-green-400"></i>
|
318 |
+
</div>
|
319 |
+
<div>
|
320 |
+
<p class="text-sm">Completed <span class="font-medium">Chemistry Unit 2.3</span> flashcards</p>
|
321 |
+
<p class="text-xs text-gray-500 dark:text-gray-400">2 hours ago</p>
|
322 |
+
</div>
|
323 |
+
</div>
|
324 |
+
<div class="flex items-start space-x-3">
|
325 |
+
<div class="activity-icon bg-blue-100 dark:bg-blue-900">
|
326 |
+
<i class="fas fa-file-alt text-blue-600 dark:text-blue-400"></i>
|
327 |
+
</div>
|
328 |
+
<div>
|
329 |
+
<p class="text-sm">Started <span class="font-medium">Maths 2022 Paper 1</span></p>
|
330 |
+
<p class="text-xs text-gray-500 dark:text-gray-400">Yesterday</p>
|
331 |
+
</div>
|
332 |
+
</div>
|
333 |
+
<div class="flex items-start space-x-3">
|
334 |
+
<div class="activity-icon bg-purple-100 dark:bg-purple-900">
|
335 |
+
<i class="fas fa-tasks text-purple-600 dark:text-purple-400"></i>
|
336 |
+
</div>
|
337 |
+
<div>
|
338 |
+
<p class="text-sm">Added <span class="font-medium">Biology Unit 4.1</span> to tasks</p>
|
339 |
+
<p class="text-xs text-gray-500 dark:text-gray-400">2 days ago</p>
|
340 |
+
</div>
|
341 |
+
</div>
|
342 |
+
</div>
|
343 |
+
</div>
|
344 |
+
</div>
|
345 |
+
</div>
|
346 |
+
</div>
|
347 |
+
|
348 |
+
<!-- Add Task Modal -->
|
349 |
+
<div id="task-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
|
350 |
+
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-xl w-full max-w-md">
|
351 |
+
<div class="p-4 border-b border-gray-200 dark:border-gray-700 flex justify-between items-center">
|
352 |
+
<h3 class="text-lg font-semibold">Add New Task</h3>
|
353 |
+
<button id="close-modal" class="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200">
|
354 |
+
<i class="fas fa-times"></i>
|
355 |
+
</button>
|
356 |
+
</div>
|
357 |
+
<div class="p-4">
|
358 |
+
<form id="task-form">
|
359 |
+
<div class="mb-4">
|
360 |
+
<label for="task-title" class="block text-sm font-medium mb-1">Task Title</label>
|
361 |
+
<input type="text" id="task-title" class="w-full px-3 py-2 border rounded-lg dark:bg-gray-700 dark:border-gray-600" required>
|
362 |
+
</div>
|
363 |
+
<div class="mb-4">
|
364 |
+
<label for="task-subject" class="block text-sm font-medium mb-1">Subject</label>
|
365 |
+
<select id="task-subject" class="w-full px-3 py-2 border rounded-lg dark:bg-gray-700 dark:border-gray-600">
|
366 |
+
<option value="">Select Subject</option>
|
367 |
+
<option value="Biology">Biology</option>
|
368 |
+
<option value="Chemistry">Chemistry</option>
|
369 |
+
<option value="Maths">Maths</option>
|
370 |
+
</select>
|
371 |
+
</div>
|
372 |
+
<div class="mb-4">
|
373 |
+
<label for="task-topic" class="block text-sm font-medium mb-1">Topic (WJEC Specification)</label>
|
374 |
+
<select id="task-topic" class="w-full px-3 py-2 border rounded-lg dark:bg-gray-700 dark:border-gray-600">
|
375 |
+
<option value="">Select Topic</option>
|
376 |
+
<!-- Will be populated dynamically based on subject -->
|
377 |
+
</select>
|
378 |
+
</div>
|
379 |
+
<div class="mb-4">
|
380 |
+
<label class="block text-sm font-medium mb-1">Task Type</label>
|
381 |
+
<div class="flex flex-wrap gap-2">
|
382 |
+
<label class="inline-flex items-center">
|
383 |
+
<input type="checkbox" name="task-type" value="Flashcards" class="hidden">
|
384 |
+
<span class="px-3 py-1 rounded-full text-xs bg-purple-100 text-purple-800 cursor-pointer">Flashcards</span>
|
385 |
+
</label>
|
386 |
+
<label class="inline-flex items-center">
|
387 |
+
<input type="checkbox" name="task-type" value="Past Papers" class="hidden">
|
388 |
+
<span class="px-3 py-1 rounded-full text-xs bg-pink-100 text-pink-800 cursor-pointer">Past Papers</span>
|
389 |
+
</label>
|
390 |
+
<label class="inline-flex items-center">
|
391 |
+
<input type="checkbox" name="task-type" value="Revision" class="hidden">
|
392 |
+
<span class="px-3 py-1 rounded-full text-xs bg-gray-100 text-gray-800 cursor-pointer">Revision</span>
|
393 |
+
</label>
|
394 |
+
</div>
|
395 |
+
</div>
|
396 |
+
<div class="mb-4">
|
397 |
+
<label for="task-due" class="block text-sm font-medium mb-1">Due Date</label>
|
398 |
+
<input type="date" id="task-due" class="w-full px-3 py-2 border rounded-lg dark:bg-gray-700 dark:border-gray-600">
|
399 |
+
</div>
|
400 |
+
<div class="mb-4">
|
401 |
+
<label for="task-progress" class="block text-sm font-medium mb-1">Progress</label>
|
402 |
+
<input type="range" id="task-progress" min="0" max="100" value="0" class="w-full">
|
403 |
+
<div class="flex justify-between text-xs text-gray-500">
|
404 |
+
<span>0%</span>
|
405 |
+
<span>50%</span>
|
406 |
+
<span>100%</span>
|
407 |
+
</div>
|
408 |
+
</div>
|
409 |
+
<div class="flex justify-end space-x-2">
|
410 |
+
<button type="button" id="cancel-task" class="px-4 py-2 border rounded-lg">Cancel</button>
|
411 |
+
<button type="submit" class="px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700">Save Task</button>
|
412 |
+
</div>
|
413 |
+
</form>
|
414 |
+
</div>
|
415 |
+
</div>
|
416 |
+
</div>
|
417 |
+
|
418 |
+
<!-- Add Exam Modal -->
|
419 |
+
<div id="exam-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
|
420 |
+
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-xl w-full max-w-md">
|
421 |
+
<div class="p-4 border-b border-gray-200 dark:border-gray-700 flex justify-between items-center">
|
422 |
+
<h3 class="text-lg font-semibold">Add New Exam</h3>
|
423 |
+
<button id="close-exam-modal" class="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200">
|
424 |
+
<i class="fas fa-times"></i>
|
425 |
+
</button>
|
426 |
+
</div>
|
427 |
+
<div class="p-4">
|
428 |
+
<form id="exam-form">
|
429 |
+
<div class="mb-4">
|
430 |
+
<label for="exam-name" class="block text-sm font-medium mb-1">Exam Name</label>
|
431 |
+
<input type="text" id="exam-name" class="w-full px-3 py-2 border rounded-lg dark:bg-gray-700 dark:border-gray-600" required>
|
432 |
+
</div>
|
433 |
+
<div class="mb-4">
|
434 |
+
<label for="exam-subject" class="block text-sm font-medium mb-1">Subject</label>
|
435 |
+
<select id="exam-subject" class="w-full px-3 py-2 border rounded-lg dark:bg-gray-700 dark:border-gray-600">
|
436 |
+
<option value="">Select Subject</option>
|
437 |
+
<option value="Biology">Biology</option>
|
438 |
+
<option value="Chemistry">Chemistry</option>
|
439 |
+
<option value="Maths">Maths</option>
|
440 |
+
</select>
|
441 |
+
</div>
|
442 |
+
<div class="mb-4">
|
443 |
+
<label for="exam-unit" class="block text-sm font-medium mb-1">Unit</label>
|
444 |
+
<select id="exam-unit" class="w-full px-3 py-2 border rounded-lg dark:bg-gray-700 dark:border-gray-600">
|
445 |
+
<option value="">Select Unit</option>
|
446 |
+
<option value="Unit 1">Unit 1</option>
|
447 |
+
<option value="Unit 2">Unit 2</option>
|
448 |
+
<option value="Unit 3">Unit 3</option>
|
449 |
+
<option value="Unit 4">Unit 4</option>
|
450 |
+
</select>
|
451 |
+
</div>
|
452 |
+
<div class="mb-4">
|
453 |
+
<label for="exam-date" class="block text-sm font-medium mb-1">Exam Date</label>
|
454 |
+
<input type="date" id="exam-date" class="w-full px-3 py-2 border rounded-lg dark:bg-gray-700 dark:border-gray-600" required>
|
455 |
+
</div>
|
456 |
+
<div class="mb-4">
|
457 |
+
<label for="exam-time" class="block text-sm font-medium mb-1">Exam Time</label>
|
458 |
+
<input type="time" id="exam-time" class="w-full px-3 py-2 border rounded-lg dark:bg-gray-700 dark:border-gray-600">
|
459 |
+
</div>
|
460 |
+
<div class="flex justify-end space-x-2">
|
461 |
+
<button type="button" id="cancel-exam" class="px-4 py-2 border rounded-lg">Cancel</button>
|
462 |
+
<button type="submit" class="px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700">Save Exam</button>
|
463 |
+
</div>
|
464 |
+
</form>
|
465 |
+
</div>
|
466 |
+
</div>
|
467 |
+
</div>
|
468 |
+
|
469 |
+
<!-- Radar Chart Tooltip -->
|
470 |
+
<div id="radar-tooltip" class="radar-tooltip"></div>
|
471 |
+
|
472 |
+
<script>
|
473 |
+
// Theme Toggle
|
474 |
+
const themeToggle = document.getElementById('theme-toggle');
|
475 |
+
const html = document.documentElement;
|
476 |
+
|
477 |
+
themeToggle.addEventListener('click', () => {
|
478 |
+
html.classList.toggle('dark');
|
479 |
+
localStorage.setItem('theme', html.classList.contains('dark') ? 'dark' : 'light');
|
480 |
+
});
|
481 |
+
|
482 |
+
// Check for saved theme preference
|
483 |
+
if (localStorage.getItem('theme') === 'dark' || (!localStorage.getItem('theme') && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
484 |
+
html.classList.add('dark');
|
485 |
+
}
|
486 |
+
|
487 |
+
// Tab Navigation
|
488 |
+
const tabButtons = document.querySelectorAll('.tab-button');
|
489 |
+
const tabContents = document.querySelectorAll('.tab-content');
|
490 |
+
|
491 |
+
tabButtons.forEach(button => {
|
492 |
+
button.addEventListener('click', () => {
|
493 |
+
const tabName = button.getAttribute('data-tab');
|
494 |
+
|
495 |
+
// Update active tab button
|
496 |
+
tabButtons.forEach(btn => btn.classList.remove('tab-active'));
|
497 |
+
button.classList.add('tab-active');
|
498 |
+
|
499 |
+
// Show corresponding tab content
|
500 |
+
tabContents.forEach(content => content.classList.add('hidden'));
|
501 |
+
document.getElementById(`${tabName}-tab`).classList.remove('hidden');
|
502 |
+
|
503 |
+
// Update charts when progress tab is shown
|
504 |
+
if (tabName === 'progress') {
|
505 |
+
updateCharts();
|
506 |
+
}
|
507 |
+
});
|
508 |
+
});
|
509 |
+
|
510 |
+
// Set first tab as active by default
|
511 |
+
if (tabButtons.length > 0) {
|
512 |
+
tabButtons[0].classList.add('tab-active');
|
513 |
+
tabContents[0].classList.remove('hidden');
|
514 |
+
}
|
515 |
+
|
516 |
+
// Task Management
|
517 |
+
let tasks = JSON.parse(localStorage.getItem('tasks')) || [];
|
518 |
+
let papers = JSON.parse(localStorage.getItem('papers')) || [];
|
519 |
+
let exams = JSON.parse(localStorage.getItem('exams')) || [];
|
520 |
+
|
521 |
+
// WJEC specification data with units
|
522 |
+
const wjecSpecs = {
|
523 |
+
Biology: {
|
524 |
+
"Unit 3": ["3.1", "3.2", "3.3", "3.4", "3.5", "3.6", "3.7", "3.8"],
|
525 |
+
"Unit 4": ["4.1", "4.2", "4.3", "4.4", "4.5", "4.6"]
|
526 |
+
},
|
527 |
+
Chemistry: {
|
528 |
+
"Unit 1": ["1.1", "1.2", "1.3", "1.4", "1.5", "1.6", "1.7"],
|
529 |
+
"Unit 2": ["2.1", "2.2", "2.3", "2.4", "2.5", "2.6", "2.7", "2.8"],
|
530 |
+
"Unit 3": ["3.1", "3.2", "3.3", "3.4", "3.5", "3.6", "3.7", "3.8", "3.9"],
|
531 |
+
"Unit 4": ["4.1", "4.2", "4.3", "4.4", "4.5", "4.6", "4.7", "4.8"]
|
532 |
+
},
|
533 |
+
Maths: {
|
534 |
+
"Unit 1": ["1.1 Proof", "1.2 Algebra and Functions"],
|
535 |
+
"Unit 2": ["2.1 Coordinate Geometry", "2.2 Sequences and Series"],
|
536 |
+
"Unit 3": ["3.1 Trigonometry", "3.2 Exponentials and Logarithms"],
|
537 |
+
"Unit 4": ["4.1 Differentiation", "4.2 Integration"]
|
538 |
+
}
|
539 |
+
};
|
540 |
+
|
541 |
+
// Populate topic dropdown based on subject and unit
|
542 |
+
const subjectSelect = document.getElementById('task-subject');
|
543 |
+
const topicSelect = document.getElementById('task-topic');
|
544 |
+
|
545 |
+
subjectSelect.addEventListener('change', () => {
|
546 |
+
const subject = subjectSelect.value;
|
547 |
+
topicSelect.innerHTML = '<option value="">Select Topic</option>';
|
548 |
+
|
549 |
+
if (subject && wjecSpecs[subject]) {
|
550 |
+
for (const unit in wjecSpecs[subject]) {
|
551 |
+
const optgroup = document.createElement('optgroup');
|
552 |
+
optgroup.label = unit;
|
553 |
+
|
554 |
+
wjecSpecs[subject][unit].forEach(topic => {
|
555 |
+
const option = document.createElement('option');
|
556 |
+
option.value = `${unit} ${topic}`;
|
557 |
+
option.textContent = `${unit} ${topic}`;
|
558 |
+
optgroup.appendChild(option);
|
559 |
+
});
|
560 |
+
|
561 |
+
topicSelect.appendChild(optgroup);
|
562 |
+
}
|
563 |
+
}
|
564 |
+
});
|
565 |
+
|
566 |
+
// Task Modal
|
567 |
+
const addTaskBtn = document.getElementById('add-task-btn');
|
568 |
+
const taskModal = document.getElementById('task-modal');
|
569 |
+
const closeModal = document.getElementById('close-modal');
|
570 |
+
const cancelTask = document.getElementById('cancel-task');
|
571 |
+
const taskForm = document.getElementById('task-form');
|
572 |
+
|
573 |
+
addTaskBtn.addEventListener('click', () => {
|
574 |
+
taskModal.classList.remove('hidden');
|
575 |
+
});
|
576 |
+
|
577 |
+
closeModal.addEventListener('click', () => {
|
578 |
+
taskModal.classList.add('hidden');
|
579 |
+
});
|
580 |
+
|
581 |
+
cancelTask.addEventListener('click', () => {
|
582 |
+
taskModal.classList.add('hidden');
|
583 |
+
});
|
584 |
+
|
585 |
+
// Task Type Selection
|
586 |
+
const taskTypeLabels = document.querySelectorAll('label[class*="inline-flex"]');
|
587 |
+
|
588 |
+
taskTypeLabels.forEach(label => {
|
589 |
+
label.addEventListener('click', () => {
|
590 |
+
const checkbox = label.querySelector('input[type="checkbox"]');
|
591 |
+
checkbox.checked = !checkbox.checked;
|
592 |
+
|
593 |
+
if (checkbox.checked) {
|
594 |
+
label.querySelector('span').classList.add('bg-indigo-100', 'text-indigo-800');
|
595 |
+
label.querySelector('span').classList.remove('bg-purple-100', 'bg-pink-100', 'bg-gray-100');
|
596 |
+
} else {
|
597 |
+
const value = checkbox.value;
|
598 |
+
if (value === 'Flashcards') {
|
599 |
+
label.querySelector('span').classList.add('bg-purple-100', 'text-purple-800');
|
600 |
+
} else if (value === 'Past Papers') {
|
601 |
+
label.querySelector('span').classList.add('bg-pink-100', 'text-pink-800');
|
602 |
+
} else {
|
603 |
+
label.querySelector('span').classList.add('bg-gray-100', 'text-gray-800');
|
604 |
+
}
|
605 |
+
label.querySelector('span').classList.remove('bg-indigo-100', 'text-indigo-800');
|
606 |
+
}
|
607 |
+
});
|
608 |
+
});
|
609 |
+
|
610 |
+
// Save Task
|
611 |
+
taskForm.addEventListener('submit', (e) => {
|
612 |
+
e.preventDefault();
|
613 |
+
|
614 |
+
const title = document.getElementById('task-title').value;
|
615 |
+
const subject = document.getElementById('task-subject').value;
|
616 |
+
const topic = document.getElementById('task-topic').value;
|
617 |
+
const dueDate = document.getElementById('task-due').value;
|
618 |
+
const progress = document.getElementById('task-progress').value;
|
619 |
+
|
620 |
+
// Get selected task types
|
621 |
+
const taskTypes = [];
|
622 |
+
document.querySelectorAll('input[name="task-type"]:checked').forEach(checkbox => {
|
623 |
+
taskTypes.push(checkbox.value);
|
624 |
+
});
|
625 |
+
|
626 |
+
const newTask = {
|
627 |
+
id: Date.now(),
|
628 |
+
title,
|
629 |
+
subject,
|
630 |
+
topic,
|
631 |
+
types: taskTypes,
|
632 |
+
dueDate,
|
633 |
+
progress: parseInt(progress),
|
634 |
+
createdAt: new Date().toISOString()
|
635 |
+
};
|
636 |
+
|
637 |
+
tasks.push(newTask);
|
638 |
+
saveTasks();
|
639 |
+
renderTasks();
|
640 |
+
updateCharts();
|
641 |
+
updateStats();
|
642 |
+
|
643 |
+
// Reset form and close modal
|
644 |
+
taskForm.reset();
|
645 |
+
taskModal.classList.add('hidden');
|
646 |
+
|
647 |
+
// Reset topic dropdown
|
648 |
+
topicSelect.innerHTML = '<option value="">Select Topic</option>';
|
649 |
+
|
650 |
+
// Reset task type selections
|
651 |
+
document.querySelectorAll('input[name="task-type"]').forEach(checkbox => {
|
652 |
+
checkbox.checked = false;
|
653 |
+
});
|
654 |
+
taskTypeLabels.forEach(label => {
|
655 |
+
const value = label.querySelector('input').value;
|
656 |
+
const span = label.querySelector('span');
|
657 |
+
span.classList.remove('bg-indigo-100', 'text-indigo-800');
|
658 |
+
if (value === 'Flashcards') {
|
659 |
+
span.classList.add('bg-purple-100', 'text-purple-800');
|
660 |
+
} else if (value === 'Past Papers') {
|
661 |
+
span.classList.add('bg-pink-100', 'text-pink-800');
|
662 |
+
} else {
|
663 |
+
span.classList.add('bg-gray-100', 'text-gray-800');
|
664 |
+
}
|
665 |
+
});
|
666 |
+
});
|
667 |
+
|
668 |
+
// Filter Tasks
|
669 |
+
const filterButtons = document.querySelectorAll('.filter-btn');
|
670 |
+
|
671 |
+
filterButtons.forEach(button => {
|
672 |
+
button.addEventListener('click', () => {
|
673 |
+
const filter = button.getAttribute('data-filter');
|
674 |
+
renderTasks(filter);
|
675 |
+
});
|
676 |
+
});
|
677 |
+
|
678 |
+
// Render Tasks
|
679 |
+
function renderTasks(filter = 'all') {
|
680 |
+
const taskList = document.getElementById('task-list');
|
681 |
+
taskList.innerHTML = '';
|
682 |
+
|
683 |
+
let filteredTasks = tasks;
|
684 |
+
|
685 |
+
if (filter !== 'all') {
|
686 |
+
filteredTasks = tasks.filter(task => {
|
687 |
+
return task.subject === filter ||
|
688 |
+
(task.types && task.types.includes(filter));
|
689 |
+
});
|
690 |
+
}
|
691 |
+
|
692 |
+
if (filteredTasks.length === 0) {
|
693 |
+
taskList.innerHTML = '<p class="text-center py-4 text-gray-500">No tasks found. Add a new task to get started!</p>';
|
694 |
+
return;
|
695 |
+
}
|
696 |
+
|
697 |
+
filteredTasks.forEach(task => {
|
698 |
+
const taskElement = document.createElement('div');
|
699 |
+
taskElement.className = 'bg-white dark:bg-gray-700 rounded-lg shadow p-4 border-l-4 border-indigo-500';
|
700 |
+
taskElement.dataset.id = task.id;
|
701 |
+
|
702 |
+
// Determine badge color based on due date
|
703 |
+
let badgeColor = 'gray';
|
704 |
+
let badgeText = '';
|
705 |
+
|
706 |
+
if (task.dueDate) {
|
707 |
+
const today = new Date();
|
708 |
+
const dueDate = new Date(task.dueDate);
|
709 |
+
const diffTime = dueDate - today;
|
710 |
+
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
711 |
+
|
712 |
+
if (diffDays < 0) {
|
713 |
+
badgeColor = 'red';
|
714 |
+
badgeText = 'Overdue';
|
715 |
+
} else if (diffDays === 0) {
|
716 |
+
badgeColor = 'red';
|
717 |
+
badgeText = 'Today';
|
718 |
+
} else if (diffDays <= 3) {
|
719 |
+
badgeColor = 'orange';
|
720 |
+
badgeText = 'Soon';
|
721 |
+
} else if (diffDays <= 7) {
|
722 |
+
badgeColor = 'yellow';
|
723 |
+
badgeText = 'Next week';
|
724 |
+
}
|
725 |
+
}
|
726 |
+
|
727 |
+
// Create type badges
|
728 |
+
let typeBadges = '';
|
729 |
+
if (task.types && task.types.length > 0) {
|
730 |
+
task.types.forEach(type => {
|
731 |
+
let bgClass = 'bg-gray-100';
|
732 |
+
let textClass = 'text-gray-800';
|
733 |
+
|
734 |
+
if (type === 'Flashcards') {
|
735 |
+
bgClass = 'bg-purple-100';
|
736 |
+
textClass = 'text-purple-800';
|
737 |
+
} else if (type === 'Past Papers') {
|
738 |
+
bgClass = 'bg-pink-100';
|
739 |
+
textClass = 'text-pink-800';
|
740 |
+
} else if (type === 'Revision') {
|
741 |
+
bgClass = 'bg-gray-100';
|
742 |
+
textClass = 'text-gray-800';
|
743 |
+
}
|
744 |
+
|
745 |
+
typeBadges += `<span class="text-xs ${bgClass} ${textClass} px-2 py-1 rounded mr-1">${type}</span>`;
|
746 |
+
});
|
747 |
+
}
|
748 |
+
|
749 |
+
taskElement.innerHTML = `
|
750 |
+
<div class="flex justify-between items-start mb-2">
|
751 |
+
<div>
|
752 |
+
<h3 class="font-medium">${task.title}</h3>
|
753 |
+
${task.subject ? `<p class="text-sm text-gray-600 dark:text-gray-300">${task.subject} ${task.topic ? 'Β· ' + task.topic : ''}</p>` : ''}
|
754 |
+
</div>
|
755 |
+
${badgeText ? `<span class="text-xs bg-${badgeColor}-100 dark:bg-${badgeColor}-900 text-${badgeColor}-800 dark:text-${badgeColor}-200 px-2 py-1 rounded">${badgeText}</span>` : ''}
|
756 |
+
</div>
|
757 |
+
${typeBadges ? `<div class="mb-2">${typeBadges}</div>` : ''}
|
758 |
+
<div class="flex items-center justify-between mt-3">
|
759 |
+
<div class="w-full bg-gray-200 dark:bg-gray-600 rounded-full h-2.5 mr-2">
|
760 |
+
<div class="task-progress bg-indigo-600 h-2.5 rounded-full" style="width: ${task.progress}%"></div>
|
761 |
+
</div>
|
762 |
+
<span class="text-sm font-medium">${task.progress}%</span>
|
763 |
+
</div>
|
764 |
+
<div class="flex justify-end mt-3 space-x-2">
|
765 |
+
<button class="edit-task text-indigo-600 dark:text-indigo-400 hover:text-indigo-800 dark:hover:text-indigo-300 text-sm">
|
766 |
+
<i class="fas fa-edit mr-1"></i> Edit
|
767 |
+
</button>
|
768 |
+
<button class="delete-task text-red-600 dark:text-red-400 hover:text-red-800 dark:hover:text-red-300 text-sm">
|
769 |
+
<i class="fas fa-trash-alt mr-1"></i> Delete
|
770 |
+
</button>
|
771 |
+
</div>
|
772 |
+
`;
|
773 |
+
|
774 |
+
taskList.appendChild(taskElement);
|
775 |
+
});
|
776 |
+
|
777 |
+
// Add event listeners to edit and delete buttons
|
778 |
+
document.querySelectorAll('.edit-task').forEach(button => {
|
779 |
+
button.addEventListener('click', (e) => {
|
780 |
+
const taskId = parseInt(e.target.closest('[data-id]').dataset.id);
|
781 |
+
editTask(taskId);
|
782 |
+
});
|
783 |
+
});
|
784 |
+
|
785 |
+
document.querySelectorAll('.delete-task').forEach(button => {
|
786 |
+
button.addEventListener('click', (e) => {
|
787 |
+
const taskId = parseInt(e.target.closest('[data-id]').dataset.id);
|
788 |
+
deleteTask(taskId);
|
789 |
+
});
|
790 |
+
});
|
791 |
+
}
|
792 |
+
|
793 |
+
// Edit Task
|
794 |
+
function editTask(taskId) {
|
795 |
+
const task = tasks.find(t => t.id === taskId);
|
796 |
+
if (!task) return;
|
797 |
+
|
798 |
+
// Fill the form with task data
|
799 |
+
document.getElementById('task-title').value = task.title;
|
800 |
+
document.getElementById('task-subject').value = task.subject || '';
|
801 |
+
|
802 |
+
// Populate topics if subject is selected
|
803 |
+
if (task.subject) {
|
804 |
+
topicSelect.innerHTML = '<option value="">Select Topic</option>';
|
805 |
+
|
806 |
+
for (const unit in wjecSpecs[task.subject]) {
|
807 |
+
const optgroup = document.createElement('optgroup');
|
808 |
+
optgroup.label = unit;
|
809 |
+
|
810 |
+
wjecSpecs[task.subject][unit].forEach(topic => {
|
811 |
+
const option = document.createElement('option');
|
812 |
+
option.value = `${unit} ${topic}`;
|
813 |
+
option.textContent = `${unit} ${topic}`;
|
814 |
+
optgroup.appendChild(option);
|
815 |
+
});
|
816 |
+
|
817 |
+
topicSelect.appendChild(optgroup);
|
818 |
+
}
|
819 |
+
|
820 |
+
document.getElementById('task-topic').value = task.topic || '';
|
821 |
+
}
|
822 |
+
|
823 |
+
// Set task types
|
824 |
+
document.querySelectorAll('input[name="task-type"]').forEach(checkbox => {
|
825 |
+
checkbox.checked = task.types && task.types.includes(checkbox.value);
|
826 |
+
|
827 |
+
// Update the visual state of the labels
|
828 |
+
const label = checkbox.closest('label');
|
829 |
+
const span = label.querySelector('span');
|
830 |
+
|
831 |
+
if (checkbox.checked) {
|
832 |
+
span.classList.add('bg-indigo-100', 'text-indigo-800');
|
833 |
+
span.classList.remove('bg-purple-100', 'bg-pink-100', 'bg-gray-100');
|
834 |
+
} else {
|
835 |
+
const value = checkbox.value;
|
836 |
+
if (value === 'Flashcards') {
|
837 |
+
span.classList.add('bg-purple-100', 'text-purple-800');
|
838 |
+
} else if (value === 'Past Papers') {
|
839 |
+
span.classList.add('bg-pink-100', 'text-pink-800');
|
840 |
+
} else {
|
841 |
+
span.classList.add('bg-gray-100', 'text-gray-800');
|
842 |
+
}
|
843 |
+
span.classList.remove('bg-indigo-100', 'text-indigo-800');
|
844 |
+
}
|
845 |
+
});
|
846 |
+
|
847 |
+
document.getElementById('task-due').value = task.dueDate || '';
|
848 |
+
document.getElementById('task-progress').value = task.progress;
|
849 |
+
|
850 |
+
// Show the modal
|
851 |
+
taskModal.classList.remove('hidden');
|
852 |
+
|
853 |
+
// Change the form to update mode
|
854 |
+
taskForm.dataset.editing = taskId;
|
855 |
+
taskForm.querySelector('button[type="submit"]').textContent = 'Update Task';
|
856 |
+
}
|
857 |
+
|
858 |
+
// Update Task
|
859 |
+
taskForm.addEventListener('submit', (e) => {
|
860 |
+
e.preventDefault();
|
861 |
+
|
862 |
+
const taskId = taskForm.dataset.editing;
|
863 |
+
|
864 |
+
if (taskId) {
|
865 |
+
// Update existing task
|
866 |
+
const taskIndex = tasks.findIndex(t => t.id === parseInt(taskId));
|
867 |
+
if (taskIndex === -1) return;
|
868 |
+
|
869 |
+
const title = document.getElementById('task-title').value;
|
870 |
+
const subject = document.getElementById('task-subject').value;
|
871 |
+
const topic = document.getElementById('task-topic').value;
|
872 |
+
const dueDate = document.getElementById('task-due').value;
|
873 |
+
const progress = document.getElementById('task-progress').value;
|
874 |
+
|
875 |
+
// Get selected task types
|
876 |
+
const taskTypes = [];
|
877 |
+
document.querySelectorAll('input[name="task-type"]:checked').forEach(checkbox => {
|
878 |
+
taskTypes.push(checkbox.value);
|
879 |
+
});
|
880 |
+
|
881 |
+
tasks[taskIndex] = {
|
882 |
+
...tasks[taskIndex],
|
883 |
+
title,
|
884 |
+
subject,
|
885 |
+
topic,
|
886 |
+
types: taskTypes,
|
887 |
+
dueDate,
|
888 |
+
progress: parseInt(progress)
|
889 |
+
};
|
890 |
+
|
891 |
+
saveTasks();
|
892 |
+
renderTasks();
|
893 |
+
updateCharts();
|
894 |
+
updateStats();
|
895 |
+
|
896 |
+
// Reset form and close modal
|
897 |
+
taskForm.reset();
|
898 |
+
delete taskForm.dataset.editing;
|
899 |
+
taskForm.querySelector('button[type="submit"]').textContent = 'Save Task';
|
900 |
+
taskModal.classList.add('hidden');
|
901 |
+
}
|
902 |
+
});
|
903 |
+
|
904 |
+
// Delete Task
|
905 |
+
function deleteTask(taskId) {
|
906 |
+
if (confirm('Are you sure you want to delete this task?')) {
|
907 |
+
tasks = tasks.filter(t => t.id !== taskId);
|
908 |
+
saveTasks();
|
909 |
+
renderTasks();
|
910 |
+
updateCharts();
|
911 |
+
updateStats();
|
912 |
+
}
|
913 |
+
}
|
914 |
+
|
915 |
+
// Save Tasks to Local Storage
|
916 |
+
function saveTasks() {
|
917 |
+
localStorage.setItem('tasks', JSON.stringify(tasks));
|
918 |
+
}
|
919 |
+
|
920 |
+
// Initialize Past Papers
|
921 |
+
function initPapers() {
|
922 |
+
if (papers.length === 0) {
|
923 |
+
// Sample past papers data
|
924 |
+
const years = [2023, 2022, 2021, 2020, 2019, 2018, 2017];
|
925 |
+
const subjects = ['Biology', 'Chemistry', 'Maths'];
|
926 |
+
const types = ['Paper 1', 'Paper 2', 'Paper 3'];
|
927 |
+
|
928 |
+
years.forEach(year => {
|
929 |
+
subjects.forEach(subject => {
|
930 |
+
types.forEach(type => {
|
931 |
+
papers.push({
|
932 |
+
id: `${subject}-${year}-${type.replace(' ', '-')}`,
|
933 |
+
subject,
|
934 |
+
year,
|
935 |
+
type,
|
936 |
+
status: 'Not Started',
|
937 |
+
completed: false
|
938 |
+
});
|
939 |
+
});
|
940 |
+
});
|
941 |
+
});
|
942 |
+
|
943 |
+
localStorage.setItem('papers', JSON.stringify(papers));
|
944 |
+
}
|
945 |
+
}
|
946 |
+
|
947 |
+
// Render Past Papers
|
948 |
+
function renderPapers() {
|
949 |
+
const papersList = document.getElementById('papers-list');
|
950 |
+
papersList.innerHTML = '';
|
951 |
+
|
952 |
+
const subjectFilter = document.getElementById('paper-subject').value;
|
953 |
+
const yearFilter = document.getElementById('paper-year').value;
|
954 |
+
const statusFilter = document.getElementById('paper-status').value;
|
955 |
+
const searchQuery = document.getElementById('paper-search').value.toLowerCase();
|
956 |
+
|
957 |
+
let filteredPapers = papers;
|
958 |
+
|
959 |
+
if (subjectFilter !== 'all') {
|
960 |
+
filteredPapers = filteredPapers.filter(paper => paper.subject === subjectFilter);
|
961 |
+
}
|
962 |
+
|
963 |
+
if (yearFilter !== 'all') {
|
964 |
+
filteredPapers = filteredPapers.filter(paper => paper.year.toString() === yearFilter);
|
965 |
+
}
|
966 |
+
|
967 |
+
if (statusFilter !== 'all') {
|
968 |
+
filteredPapers = filteredPapers.filter(paper => paper.status === statusFilter);
|
969 |
+
}
|
970 |
+
|
971 |
+
if (searchQuery) {
|
972 |
+
filteredPapers = filteredPapers.filter(paper =>
|
973 |
+
paper.subject.toLowerCase().includes(searchQuery) ||
|
974 |
+
paper.type.toLowerCase().includes(searchQuery) ||
|
975 |
+
paper.year.toString().includes(searchQuery)
|
976 |
+
);
|
977 |
+
}
|
978 |
+
|
979 |
+
if (filteredPapers.length === 0) {
|
980 |
+
papersList.innerHTML = '<p class="text-center py-4 text-gray-500">No papers found matching your criteria.</p>';
|
981 |
+
return;
|
982 |
+
}
|
983 |
+
|
984 |
+
// Group papers by subject and year
|
985 |
+
const groupedPapers = {};
|
986 |
+
|
987 |
+
filteredPapers.forEach(paper => {
|
988 |
+
const key = `${paper.subject}-${paper.year}`;
|
989 |
+
if (!groupedPapers[key]) {
|
990 |
+
groupedPapers[key] = [];
|
991 |
+
}
|
992 |
+
groupedPapers[key].push(paper);
|
993 |
+
});
|
994 |
+
|
995 |
+
// Render grouped papers
|
996 |
+
for (const key in groupedPapers) {
|
997 |
+
const [subject, year] = key.split('-');
|
998 |
+
const papersGroup = groupedPapers[key];
|
999 |
+
|
1000 |
+
const groupElement = document.createElement('div');
|
1001 |
+
groupElement.className = 'mb-6';
|
1002 |
+
|
1003 |
+
groupElement.innerHTML = `
|
1004 |
+
<h3 class="font-medium text-lg mb-2">${subject} ${year}</h3>
|
1005 |
+
<div class="bg-white dark:bg-gray-700 rounded-lg shadow divide-y divide-gray-200 dark:divide-gray-600">
|
1006 |
+
${papersGroup.map(paper => `
|
1007 |
+
<div class="p-3 flex items-center justify-between" data-id="${paper.id}">
|
1008 |
+
<div>
|
1009 |
+
<h4 class="font-medium">${paper.type}</h4>
|
1010 |
+
<div class="flex items-center mt-1">
|
1011 |
+
<span class="text-xs px-2 py-1 rounded mr-2
|
1012 |
+
${paper.status === 'Completed' ? 'bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200' :
|
1013 |
+
paper.status === 'In Progress' ? 'bg-yellow-100 dark:bg-yellow-900 text-yellow-800 dark:text-yellow-200' :
|
1014 |
+
'bg-gray-100 dark:bg-gray-600 text-gray-800 dark:text-gray-200'}">
|
1015 |
+
${paper.status}
|
1016 |
+
</span>
|
1017 |
+
</div>
|
1018 |
+
</div>
|
1019 |
+
<div class="flex items-center">
|
1020 |
+
<a href="#" class="text-indigo-600 dark:text-indigo-400 hover:text-indigo-800 dark:hover:text-indigo-300 mr-3">
|
1021 |
+
<i class="fas fa-download"></i>
|
1022 |
+
</a>
|
1023 |
+
<div class="checkbox-container relative inline-block w-5 h-5 mr-2">
|
1024 |
+
<input type="checkbox" class="absolute opacity-0 w-0 h-0" ${paper.status === 'Completed' ? 'checked' : ''}>
|
1025 |
+
<span class="checkmark absolute top-0 left-0 h-5 w-5 border-2 border-gray-300 dark:border-gray-500 rounded"></span>
|
1026 |
+
</div>
|
1027 |
+
</div>
|
1028 |
+
</div>
|
1029 |
+
`).join('')}
|
1030 |
+
</div>
|
1031 |
+
`;
|
1032 |
+
|
1033 |
+
papersList.appendChild(groupElement);
|
1034 |
+
}
|
1035 |
+
|
1036 |
+
// Add event listeners to checkboxes
|
1037 |
+
document.querySelectorAll('.checkbox-container input').forEach(checkbox => {
|
1038 |
+
checkbox.addEventListener('change', (e) => {
|
1039 |
+
const paperId = e.target.closest('[data-id]').dataset.id;
|
1040 |
+
const paperIndex = papers.findIndex(p => p.id === paperId);
|
1041 |
+
|
1042 |
+
if (paperIndex !== -1) {
|
1043 |
+
papers[paperIndex].status = e.target.checked ? 'Completed' : 'Not Started';
|
1044 |
+
localStorage.setItem('papers', JSON.stringify(papers));
|
1045 |
+
updateStats();
|
1046 |
+
}
|
1047 |
+
});
|
1048 |
+
});
|
1049 |
+
}
|
1050 |
+
|
1051 |
+
// Paper Filters
|
1052 |
+
document.getElementById('paper-subject').addEventListener('change', renderPapers);
|
1053 |
+
document.getElementById('paper-year').addEventListener('change', renderPapers);
|
1054 |
+
document.getElementById('paper-status').addEventListener('change', renderPapers);
|
1055 |
+
document.getElementById('paper-search').addEventListener('input', renderPapers);
|
1056 |
+
|
1057 |
+
// Exam Management
|
1058 |
+
const addExamBtn = document.getElementById('add-exam-btn');
|
1059 |
+
const examModal = document.getElementById('exam-modal');
|
1060 |
+
const closeExamModal = document.getElementById('close-exam-modal');
|
1061 |
+
const cancelExam = document.getElementById('cancel-exam');
|
1062 |
+
const examForm = document.getElementById('exam-form');
|
1063 |
+
|
1064 |
+
addExamBtn.addEventListener('click', () => {
|
1065 |
+
examModal.classList.remove('hidden');
|
1066 |
+
});
|
1067 |
+
|
1068 |
+
closeExamModal.addEventListener('click', () => {
|
1069 |
+
examModal.classList.add('hidden');
|
1070 |
+
});
|
1071 |
+
|
1072 |
+
cancelExam.addEventListener('click', () => {
|
1073 |
+
examModal.classList.add('hidden');
|
1074 |
+
});
|
1075 |
+
|
1076 |
+
// Save Exam
|
1077 |
+
examForm.addEventListener('submit', (e) => {
|
1078 |
+
e.preventDefault();
|
1079 |
+
|
1080 |
+
const name = document.getElementById('exam-name').value;
|
1081 |
+
const subject = document.getElementById('exam-subject').value;
|
1082 |
+
const unit = document.getElementById('exam-unit').value;
|
1083 |
+
const date = document.getElementById('exam-date').value;
|
1084 |
+
const time = document.getElementById('exam-time').value;
|
1085 |
+
|
1086 |
+
const newExam = {
|
1087 |
+
id: Date.now(),
|
1088 |
+
name,
|
1089 |
+
subject,
|
1090 |
+
unit,
|
1091 |
+
date,
|
1092 |
+
time,
|
1093 |
+
createdAt: new Date().toISOString()
|
1094 |
+
};
|
1095 |
+
|
1096 |
+
exams.push(newExam);
|
1097 |
+
saveExams();
|
1098 |
+
renderExams();
|
1099 |
+
updateStats();
|
1100 |
+
|
1101 |
+
// Reset form and close modal
|
1102 |
+
examForm.reset();
|
1103 |
+
examModal.classList.add('hidden');
|
1104 |
+
});
|
1105 |
+
|
1106 |
+
// Render Exams
|
1107 |
+
function renderExams() {
|
1108 |
+
const examsList = document.getElementById('exams-list');
|
1109 |
+
examsList.innerHTML = '';
|
1110 |
+
|
1111 |
+
if (exams.length === 0) {
|
1112 |
+
examsList.innerHTML = '<p class="text-center py-4 text-gray-500">No exams added yet. Add your first exam to see the countdown!</p>';
|
1113 |
+
return;
|
1114 |
+
}
|
1115 |
+
|
1116 |
+
// Sort exams by date (soonest first)
|
1117 |
+
const sortedExams = [...exams].sort((a, b) => {
|
1118 |
+
return new Date(a.date) - new Date(b.date);
|
1119 |
+
});
|
1120 |
+
|
1121 |
+
sortedExams.forEach(exam => {
|
1122 |
+
const examElement = document.createElement('div');
|
1123 |
+
examElement.className = 'bg-white dark:bg-gray-700 rounded-lg shadow p-4 exam-pill';
|
1124 |
+
examElement.dataset.id = exam.id;
|
1125 |
+
|
1126 |
+
const today = new Date();
|
1127 |
+
const examDate = new Date(exam.date);
|
1128 |
+
const diffTime = examDate - today;
|
1129 |
+
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
1130 |
+
|
1131 |
+
let statusText = '';
|
1132 |
+
let statusClass = '';
|
1133 |
+
|
1134 |
+
if (diffDays < 0) {
|
1135 |
+
statusText = 'Past';
|
1136 |
+
statusClass = 'bg-gray-100 dark:bg-gray-600 text-gray-800 dark:text-gray-200';
|
1137 |
+
} else if (diffDays === 0) {
|
1138 |
+
statusText = 'Today';
|
1139 |
+
statusClass = 'bg-red-100 dark:bg-red-900 text-red-800 dark:text-red-200';
|
1140 |
+
} else if (diffDays <= 7) {
|
1141 |
+
statusText = `${diffDays} day${diffDays !== 1 ? 's' : ''}`;
|
1142 |
+
statusClass = 'bg-yellow-100 dark:bg-yellow-900 text-yellow-800 dark:text-yellow-200';
|
1143 |
+
} else {
|
1144 |
+
statusText = `${diffDays} day${diffDays !== 1 ? 's' : ''}`;
|
1145 |
+
statusClass = 'bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200';
|
1146 |
+
}
|
1147 |
+
|
1148 |
+
examElement.innerHTML = `
|
1149 |
+
<div class="flex justify-between items-start mb-2">
|
1150 |
+
<div>
|
1151 |
+
<h3 class="font-medium">${exam.name}</h3>
|
1152 |
+
<p class="text-sm text-gray-600 dark:text-gray-300">${exam.subject} ${exam.unit}</p>
|
1153 |
+
</div>
|
1154 |
+
<span class="text-xs ${statusClass} px-2 py-1 rounded">${statusText}</span>
|
1155 |
+
</div>
|
1156 |
+
<div class="flex justify-between items-center mt-3">
|
1157 |
+
<div>
|
1158 |
+
<p class="text-sm text-gray-600 dark:text-gray-300">
|
1159 |
+
<i class="far fa-calendar-alt mr-1"></i> ${formatDate(exam.date)}
|
1160 |
+
${exam.time ? `<span class="ml-2"><i class="far fa-clock mr-1"></i> ${exam.time}</span>` : ''}
|
1161 |
+
</p>
|
1162 |
+
</div>
|
1163 |
+
<div class="flex space-x-2">
|
1164 |
+
<button class="delete-exam text-red-600 dark:text-red-400 hover:text-red-800 dark:hover:text-red-300 text-sm">
|
1165 |
+
<i class="fas fa-trash-alt"></i>
|
1166 |
+
</button>
|
1167 |
+
</div>
|
1168 |
+
</div>
|
1169 |
+
`;
|
1170 |
+
|
1171 |
+
examsList.appendChild(examElement);
|
1172 |
+
});
|
1173 |
+
|
1174 |
+
// Add event listeners to delete buttons
|
1175 |
+
document.querySelectorAll('.delete-exam').forEach(button => {
|
1176 |
+
button.addEventListener('click', (e) => {
|
1177 |
+
const examId = parseInt(e.target.closest('[data-id]').dataset.id);
|
1178 |
+
deleteExam(examId);
|
1179 |
+
});
|
1180 |
+
});
|
1181 |
+
}
|
1182 |
+
|
1183 |
+
// Delete Exam
|
1184 |
+
function deleteExam(examId) {
|
1185 |
+
if (confirm('Are you sure you want to delete this exam?')) {
|
1186 |
+
exams = exams.filter(e => e.id !== examId);
|
1187 |
+
saveExams();
|
1188 |
+
renderExams();
|
1189 |
+
updateStats();
|
1190 |
+
}
|
1191 |
+
}
|
1192 |
+
|
1193 |
+
// Save Exams to Local Storage
|
1194 |
+
function saveExams() {
|
1195 |
+
localStorage.setItem('exams', JSON.stringify(exams));
|
1196 |
+
}
|
1197 |
+
|
1198 |
+
// Format date as DD/MM/YYYY
|
1199 |
+
function formatDate(dateString) {
|
1200 |
+
const date = new Date(dateString);
|
1201 |
+
const day = date.getDate().toString().padStart(2, '0');
|
1202 |
+
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
1203 |
+
const year = date.getFullYear();
|
1204 |
+
return `${day}/${month}/${year}`;
|
1205 |
+
}
|
1206 |
+
|
1207 |
+
// Charts
|
1208 |
+
let biologyRadarChart, chemistryRadarChart, mathsRadarChart, overallChart;
|
1209 |
+
const radarTooltip = document.getElementById('radar-tooltip');
|
1210 |
+
|
1211 |
+
function initCharts() {
|
1212 |
+
// Biology Radar Chart
|
1213 |
+
const biologyCtx = document.getElementById('biology-radar').getContext('2d');
|
1214 |
+
biologyRadarChart = new Chart(biologyCtx, {
|
1215 |
+
type: 'radar',
|
1216 |
+
data: {
|
1217 |
+
labels: [...wjecSpecs.Biology["Unit 3"].map(t => `Unit 3 ${t}`), ...wjecSpecs.Biology["Unit 4"].map(t => `Unit 4 ${t}`)],
|
1218 |
+
datasets: [{
|
1219 |
+
label: 'Completion %',
|
1220 |
+
data: Array([...wjecSpecs.Biology["Unit 3"], ...wjecSpecs.Biology["Unit 4"]].length).fill(0),
|
1221 |
+
backgroundColor: 'rgba(79, 70, 229, 0.2)',
|
1222 |
+
borderColor: 'rgba(79, 70, 229, 1)',
|
1223 |
+
borderWidth: 2,
|
1224 |
+
pointBackgroundColor: 'rgba(79, 70, 229, 1)',
|
1225 |
+
pointRadius: 4
|
1226 |
+
}]
|
1227 |
+
},
|
1228 |
+
options: {
|
1229 |
+
scales: {
|
1230 |
+
r: {
|
1231 |
+
angleLines: {
|
1232 |
+
display: true
|
1233 |
+
},
|
1234 |
+
suggestedMin: 0,
|
1235 |
+
suggestedMax: 100,
|
1236 |
+
ticks: {
|
1237 |
+
stepSize: 20
|
1238 |
+
}
|
1239 |
+
}
|
1240 |
+
},
|
1241 |
+
plugins: {
|
1242 |
+
legend: {
|
1243 |
+
display: false
|
1244 |
+
}
|
1245 |
+
},
|
1246 |
+
onHover: (event, chartElement) => {
|
1247 |
+
if (chartElement.length > 0) {
|
1248 |
+
const index = chartElement[0].index;
|
1249 |
+
const label = biologyRadarChart.data.labels[index];
|
1250 |
+
const value = biologyRadarChart.data.datasets[0].data[index];
|
1251 |
+
|
1252 |
+
// Find related tasks
|
1253 |
+
const relatedTasks = tasks.filter(task =>
|
1254 |
+
task.subject === 'Biology' && task.topic === label
|
1255 |
+
);
|
1256 |
+
|
1257 |
+
// Position tooltip near the point
|
1258 |
+
const canvasPosition = event.nativeEvent;
|
1259 |
+
radarTooltip.style.left = (canvasPosition.offsetX + 10) + 'px';
|
1260 |
+
radarTooltip.style.top = (canvasPosition.offsetY + 10) + 'px';
|
1261 |
+
|
1262 |
+
// Set tooltip content
|
1263 |
+
if (relatedTasks.length > 0) {
|
1264 |
+
let tasksHtml = relatedTasks.map(task =>
|
1265 |
+
`<li class="text-xs">${task.title} (${task.progress}%)</li>`
|
1266 |
+
).join('');
|
1267 |
+
radarTooltip.innerHTML = `
|
1268 |
+
<strong>${label}</strong>
|
1269 |
+
<div>Progress: ${value}%</div>
|
1270 |
+
<div class="mt-1">Related Tasks:</div>
|
1271 |
+
<ul class="list-disc pl-4">${tasksHtml}</ul>
|
1272 |
+
`;
|
1273 |
+
} else {
|
1274 |
+
radarTooltip.innerHTML = `
|
1275 |
+
<strong>${label}</strong>
|
1276 |
+
<div>Progress: ${value}%</div>
|
1277 |
+
<div class="mt-1">No tasks yet</div>
|
1278 |
+
`;
|
1279 |
+
}
|
1280 |
+
|
1281 |
+
radarTooltip.style.display = 'block';
|
1282 |
+
} else {
|
1283 |
+
radarTooltip.style.display = 'none';
|
1284 |
+
}
|
1285 |
+
}
|
1286 |
+
}
|
1287 |
+
});
|
1288 |
+
|
1289 |
+
// Chemistry Radar Chart
|
1290 |
+
const chemistryCtx = document.getElementById('chemistry-radar').getContext('2d');
|
1291 |
+
chemistryRadarChart = new Chart(chemistryCtx, {
|
1292 |
+
type: 'radar',
|
1293 |
+
data: {
|
1294 |
+
labels: [
|
1295 |
+
...wjecSpecs.Chemistry["Unit 1"].map(t => `Unit 1 ${t}`),
|
1296 |
+
...wjecSpecs.Chemistry["Unit 2"].map(t => `Unit 2 ${t}`),
|
1297 |
+
...wjecSpecs.Chemistry["Unit 3"].map(t => `Unit 3 ${t}`),
|
1298 |
+
...wjecSpecs.Chemistry["Unit 4"].map(t => `Unit 4 ${t}`)
|
1299 |
+
],
|
1300 |
+
datasets: [{
|
1301 |
+
label: 'Completion %',
|
1302 |
+
data: Array([
|
1303 |
+
...wjecSpecs.Chemistry["Unit 1"],
|
1304 |
+
...wjecSpecs.Chemistry["Unit 2"],
|
1305 |
+
...wjecSpecs.Chemistry["Unit 3"],
|
1306 |
+
...wjecSpecs.Chemistry["Unit 4"]
|
1307 |
+
].length).fill(0),
|
1308 |
+
backgroundColor: 'rgba(16, 185, 129, 0.2)',
|
1309 |
+
borderColor: 'rgba(16, 185, 129, 1)',
|
1310 |
+
borderWidth: 2,
|
1311 |
+
pointBackgroundColor: 'rgba(16, 185, 129, 1)',
|
1312 |
+
pointRadius: 4
|
1313 |
+
}]
|
1314 |
+
},
|
1315 |
+
options: {
|
1316 |
+
scales: {
|
1317 |
+
r: {
|
1318 |
+
angleLines: {
|
1319 |
+
display: true
|
1320 |
+
},
|
1321 |
+
suggestedMin: 0,
|
1322 |
+
suggestedMax: 100,
|
1323 |
+
ticks: {
|
1324 |
+
stepSize: 20
|
1325 |
+
}
|
1326 |
+
}
|
1327 |
+
},
|
1328 |
+
plugins: {
|
1329 |
+
legend: {
|
1330 |
+
display: false
|
1331 |
+
}
|
1332 |
+
},
|
1333 |
+
onHover: (event, chartElement) => {
|
1334 |
+
if (chartElement.length > 0) {
|
1335 |
+
const index = chartElement[0].index;
|
1336 |
+
const label = chemistryRadarChart.data.labels[index];
|
1337 |
+
const value = chemistryRadarChart.data.datasets[0].data[index];
|
1338 |
+
|
1339 |
+
// Find related tasks
|
1340 |
+
const relatedTasks = tasks.filter(task =>
|
1341 |
+
task.subject === 'Chemistry' && task.topic === label
|
1342 |
+
);
|
1343 |
+
|
1344 |
+
// Position tooltip near the point
|
1345 |
+
const canvasPosition = event.nativeEvent;
|
1346 |
+
radarTooltip.style.left = (canvasPosition.offsetX + 10) + 'px';
|
1347 |
+
radarTooltip.style.top = (canvasPosition.offsetY + 10) + 'px';
|
1348 |
+
|
1349 |
+
// Set tooltip content
|
1350 |
+
if (relatedTasks.length > 0) {
|
1351 |
+
let tasksHtml = relatedTasks.map(task =>
|
1352 |
+
`<li class="text-xs">${task.title} (${task.progress}%)</li>`
|
1353 |
+
).join('');
|
1354 |
+
radarTooltip.innerHTML = `
|
1355 |
+
<strong>${label}</strong>
|
1356 |
+
<div>Progress: ${value}%</div>
|
1357 |
+
<div class="mt-1">Related Tasks:</div>
|
1358 |
+
<ul class="list-disc pl-4">${tasksHtml}</ul>
|
1359 |
+
`;
|
1360 |
+
} else {
|
1361 |
+
radarTooltip.innerHTML = `
|
1362 |
+
<strong>${label}</strong>
|
1363 |
+
<div>Progress: ${value}%</div>
|
1364 |
+
<div class="mt-1">No tasks yet</div>
|
1365 |
+
`;
|
1366 |
+
}
|
1367 |
+
|
1368 |
+
radarTooltip.style.display = 'block';
|
1369 |
+
} else {
|
1370 |
+
radarTooltip.style.display = 'none';
|
1371 |
+
}
|
1372 |
+
}
|
1373 |
+
}
|
1374 |
+
});
|
1375 |
+
|
1376 |
+
// Maths Radar Chart
|
1377 |
+
const mathsCtx = document.getElementById('maths-radar').getContext('2d');
|
1378 |
+
mathsRadarChart = new Chart(mathsCtx, {
|
1379 |
+
type: 'radar',
|
1380 |
+
data: {
|
1381 |
+
labels: [
|
1382 |
+
...wjecSpecs.Maths["Unit 1"].map(t => `Unit 1 ${t}`),
|
1383 |
+
...wjecSpecs.Maths["Unit 2"].map(t => `Unit 2 ${t}`),
|
1384 |
+
...wjecSpecs.Maths["Unit 3"].map(t => `Unit 3 ${t}`),
|
1385 |
+
...wjecSpecs.Maths["Unit 4"].map(t => `Unit 4 ${t}`)
|
1386 |
+
],
|
1387 |
+
datasets: [{
|
1388 |
+
label: 'Completion %',
|
1389 |
+
data: Array([
|
1390 |
+
...wjecSpecs.Maths["Unit 1"],
|
1391 |
+
...wjecSpecs.Maths["Unit 2"],
|
1392 |
+
...wjecSpecs.Maths["Unit 3"],
|
1393 |
+
...wjecSpecs.Maths["Unit 4"]
|
1394 |
+
].length).fill(0),
|
1395 |
+
backgroundColor: 'rgba(245, 158, 11, 0.2)',
|
1396 |
+
borderColor: 'rgba(245, 158, 11, 1)',
|
1397 |
+
borderWidth: 2,
|
1398 |
+
pointBackgroundColor: 'rgba(245, 158, 11, 1)',
|
1399 |
+
pointRadius: 4
|
1400 |
+
}]
|
1401 |
+
},
|
1402 |
+
options: {
|
1403 |
+
scales: {
|
1404 |
+
r: {
|
1405 |
+
angleLines: {
|
1406 |
+
display: true
|
1407 |
+
},
|
1408 |
+
suggestedMin: 0,
|
1409 |
+
suggestedMax: 100,
|
1410 |
+
ticks: {
|
1411 |
+
stepSize: 20
|
1412 |
+
}
|
1413 |
+
}
|
1414 |
+
},
|
1415 |
+
plugins: {
|
1416 |
+
legend: {
|
1417 |
+
display: false
|
1418 |
+
}
|
1419 |
+
},
|
1420 |
+
onHover: (event, chartElement) => {
|
1421 |
+
if (chartElement.length > 0) {
|
1422 |
+
const index = chartElement[0].index;
|
1423 |
+
const label = mathsRadarChart.data.labels[index];
|
1424 |
+
const value = mathsRadarChart.data.datasets[0].data[index];
|
1425 |
+
|
1426 |
+
// Find related tasks
|
1427 |
+
const relatedTasks = tasks.filter(task =>
|
1428 |
+
task.subject === 'Maths' && task.topic === label
|
1429 |
+
);
|
1430 |
+
|
1431 |
+
// Position tooltip near the point
|
1432 |
+
const canvasPosition = event.nativeEvent;
|
1433 |
+
radarTooltip.style.left = (canvasPosition.offsetX + 10) + 'px';
|
1434 |
+
radarTooltip.style.top = (canvasPosition.offsetY + 10) + 'px';
|
1435 |
+
|
1436 |
+
// Set tooltip content
|
1437 |
+
if (relatedTasks.length > 0) {
|
1438 |
+
let tasksHtml = relatedTasks.map(task =>
|
1439 |
+
`<li class="text-xs">${task.title} (${task.progress}%)</li>`
|
1440 |
+
).join('');
|
1441 |
+
radarTooltip.innerHTML = `
|
1442 |
+
<strong>${label}</strong>
|
1443 |
+
<div>Progress: ${value}%</div>
|
1444 |
+
<div class="mt-1">Related Tasks:</div>
|
1445 |
+
<ul class="list-disc pl-4">${tasksHtml}</ul>
|
1446 |
+
`;
|
1447 |
+
} else {
|
1448 |
+
radarTooltip.innerHTML = `
|
1449 |
+
<strong>${label}</strong>
|
1450 |
+
<div>Progress: ${value}%</div>
|
1451 |
+
<div class="mt-1">No tasks yet</div>
|
1452 |
+
`;
|
1453 |
+
}
|
1454 |
+
|
1455 |
+
radarTooltip.style.display = 'block';
|
1456 |
+
} else {
|
1457 |
+
radarTooltip.style.display = 'none';
|
1458 |
+
}
|
1459 |
+
}
|
1460 |
+
}
|
1461 |
+
});
|
1462 |
+
|
1463 |
+
// Overall Progress Chart
|
1464 |
+
const overallCtx = document.getElementById('overall-chart').getContext('2d');
|
1465 |
+
overallChart = new Chart(overallCtx, {
|
1466 |
+
type: 'doughnut',
|
1467 |
+
data: {
|
1468 |
+
labels: ['Biology', 'Chemistry', 'Maths'],
|
1469 |
+
datasets: [{
|
1470 |
+
data: [0, 0, 0],
|
1471 |
+
backgroundColor: [
|
1472 |
+
'rgba(79, 70, 229, 0.7)',
|
1473 |
+
'rgba(16, 185, 129, 0.7)',
|
1474 |
+
'rgba(245, 158, 11, 0.7)'
|
1475 |
+
],
|
1476 |
+
borderColor: [
|
1477 |
+
'rgba(79, 70, 229, 1)',
|
1478 |
+
'rgba(16, 185, 129, 1)',
|
1479 |
+
'rgba(245, 158, 11, 1)'
|
1480 |
+
],
|
1481 |
+
borderWidth: 1
|
1482 |
+
}]
|
1483 |
+
},
|
1484 |
+
options: {
|
1485 |
+
plugins: {
|
1486 |
+
legend: {
|
1487 |
+
position: 'right'
|
1488 |
+
}
|
1489 |
+
},
|
1490 |
+
cutout: '70%'
|
1491 |
+
}
|
1492 |
+
});
|
1493 |
+
}
|
1494 |
+
|
1495 |
+
// Update Charts
|
1496 |
+
function updateCharts() {
|
1497 |
+
// Calculate progress for each topic in Biology
|
1498 |
+
const biologyProgress = [
|
1499 |
+
...wjecSpecs.Biology["Unit 3"].map(topic => {
|
1500 |
+
const topicLabel = `Unit 3 ${topic}`;
|
1501 |
+
const topicTasks = tasks.filter(task =>
|
1502 |
+
task.subject === 'Biology' && task.topic === topicLabel
|
1503 |
+
);
|
1504 |
+
|
1505 |
+
if (topicTasks.length === 0) return 0;
|
1506 |
+
|
1507 |
+
const totalProgress = topicTasks.reduce((sum, task) => sum + task.progress, 0);
|
1508 |
+
return Math.round(totalProgress / topicTasks.length);
|
1509 |
+
}),
|
1510 |
+
...wjecSpecs.Biology["Unit 4"].map(topic => {
|
1511 |
+
const topicLabel = `Unit 4 ${topic}`;
|
1512 |
+
const topicTasks = tasks.filter(task =>
|
1513 |
+
task.subject === 'Biology' && task.topic === topicLabel
|
1514 |
+
);
|
1515 |
+
|
1516 |
+
if (topicTasks.length === 0) return 0;
|
1517 |
+
|
1518 |
+
const totalProgress = topicTasks.reduce((sum, task) => sum + task.progress, 0);
|
1519 |
+
return Math.round(totalProgress / topicTasks.length);
|
1520 |
+
})
|
1521 |
+
];
|
1522 |
+
|
1523 |
+
biologyRadarChart.data.datasets[0].data = biologyProgress;
|
1524 |
+
biologyRadarChart.update();
|
1525 |
+
|
1526 |
+
// Calculate progress for each topic in Chemistry
|
1527 |
+
const chemistryProgress = [
|
1528 |
+
...wjecSpecs.Chemistry["Unit 1"].map(topic => {
|
1529 |
+
const topicLabel = `Unit 1 ${topic}`;
|
1530 |
+
const topicTasks = tasks.filter(task =>
|
1531 |
+
task.subject === 'Chemistry' && task.topic === topicLabel
|
1532 |
+
);
|
1533 |
+
|
1534 |
+
if (topicTasks.length === 0) return 0;
|
1535 |
+
|
1536 |
+
const totalProgress = topicTasks.reduce((sum, task) => sum + task.progress, 0);
|
1537 |
+
return Math.round(totalProgress / topicTasks.length);
|
1538 |
+
}),
|
1539 |
+
...wjecSpecs.Chemistry["Unit 2"].map(topic => {
|
1540 |
+
const topicLabel = `Unit 2 ${topic}`;
|
1541 |
+
const topicTasks = tasks.filter(task =>
|
1542 |
+
task.subject === 'Chemistry' && task.topic === topicLabel
|
1543 |
+
);
|
1544 |
+
|
1545 |
+
if (topicTasks.length === 0) return 0;
|
1546 |
+
|
1547 |
+
const totalProgress = topicTasks.reduce((sum, task) => sum + task.progress, 0);
|
1548 |
+
return Math.round(totalProgress / topicTasks.length);
|
1549 |
+
}),
|
1550 |
+
...wjecSpecs.Chemistry["Unit 3"].map(topic => {
|
1551 |
+
const topicLabel = `Unit 3 ${topic}`;
|
1552 |
+
const topicTasks = tasks.filter(task =>
|
1553 |
+
task.subject === 'Chemistry' && task.topic === topicLabel
|
1554 |
+
);
|
1555 |
+
|
1556 |
+
if (topicTasks.length === 0) return 0;
|
1557 |
+
|
1558 |
+
const totalProgress = topicTasks.reduce((sum, task) => sum + task.progress, 0);
|
1559 |
+
return Math.round(totalProgress / topicTasks.length);
|
1560 |
+
}),
|
1561 |
+
...wjecSpecs.Chemistry["Unit 4"].map(topic => {
|
1562 |
+
const topicLabel = `Unit 4 ${topic}`;
|
1563 |
+
const topicTasks = tasks.filter(task =>
|
1564 |
+
task.subject === 'Chemistry' && task.topic === topicLabel
|
1565 |
+
);
|
1566 |
+
|
1567 |
+
if (topicTasks.length === 0) return 0;
|
1568 |
+
|
1569 |
+
const totalProgress = topicTasks.reduce((sum, task) => sum + task.progress, 0);
|
1570 |
+
return Math.round(totalProgress / topicTasks.length);
|
1571 |
+
})
|
1572 |
+
];
|
1573 |
+
|
1574 |
+
chemistryRadarChart.data.datasets[0].data = chemistryProgress;
|
1575 |
+
chemistryRadarChart.update();
|
1576 |
+
|
1577 |
+
// Calculate progress for each topic in Maths
|
1578 |
+
const mathsProgress = [
|
1579 |
+
...wjecSpecs.Maths["Unit 1"].map(topic => {
|
1580 |
+
const topicLabel = `Unit 1 ${topic}`;
|
1581 |
+
const topicTasks = tasks.filter(task =>
|
1582 |
+
task.subject === 'Maths' && task.topic === topicLabel
|
1583 |
+
);
|
1584 |
+
|
1585 |
+
if (topicTasks.length === 0) return 0;
|
1586 |
+
|
1587 |
+
const totalProgress = topicTasks.reduce((sum, task) => sum + task.progress, 0);
|
1588 |
+
return Math.round(totalProgress / topicTasks.length);
|
1589 |
+
}),
|
1590 |
+
...wjecSpecs.Maths["Unit 2"].map(topic => {
|
1591 |
+
const topicLabel = `Unit 2 ${topic}`;
|
1592 |
+
const topicTasks = tasks.filter(task =>
|
1593 |
+
task.subject === 'Maths' && task.topic === topicLabel
|
1594 |
+
);
|
1595 |
+
|
1596 |
+
if (topicTasks.length === 0) return 0;
|
1597 |
+
|
1598 |
+
const totalProgress = topicTasks.reduce((sum, task) => sum + task.progress, 0);
|
1599 |
+
return Math.round(totalProgress / topicTasks.length);
|
1600 |
+
}),
|
1601 |
+
...wjecSpecs.Maths["Unit 3"].map(topic => {
|
1602 |
+
const topicLabel = `Unit 3 ${topic}`;
|
1603 |
+
const topicTasks = tasks.filter(task =>
|
1604 |
+
task.subject === 'Maths' && task.topic === topicLabel
|
1605 |
+
);
|
1606 |
+
|
1607 |
+
if (topicTasks.length === 0) return 0;
|
1608 |
+
|
1609 |
+
const totalProgress = topicTasks.reduce((sum, task) => sum + task.progress, 0);
|
1610 |
+
return Math.round(totalProgress / topicTasks.length);
|
1611 |
+
}),
|
1612 |
+
...wjecSpecs.Maths["Unit 4"].map(topic => {
|
1613 |
+
const topicLabel = `Unit 4 ${topic}`;
|
1614 |
+
const topicTasks = tasks.filter(task =>
|
1615 |
+
task.subject === 'Maths' && task.topic === topicLabel
|
1616 |
+
);
|
1617 |
+
|
1618 |
+
if (topicTasks.length === 0) return 0;
|
1619 |
+
|
1620 |
+
const totalProgress = topicTasks.reduce((sum, task) => sum + task.progress, 0);
|
1621 |
+
return Math.round(totalProgress / topicTasks.length);
|
1622 |
+
})
|
1623 |
+
];
|
1624 |
+
|
1625 |
+
mathsRadarChart.data.datasets[0].data = mathsProgress;
|
1626 |
+
mathsRadarChart.update();
|
1627 |
+
|
1628 |
+
// Calculate overall subject progress
|
1629 |
+
const biologyTasks = tasks.filter(task => task.subject === 'Biology');
|
1630 |
+
const chemistryTasks = tasks.filter(task => task.subject === 'Chemistry');
|
1631 |
+
const mathsTasks = tasks.filter(task => task.subject === 'Maths');
|
1632 |
+
|
1633 |
+
const biologyAvg
|
1634 |
+
</html>
|
prompts.txt
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
Build me a modern interactive A-Level revision dashboard for WJEC Biology, Chemistry, and Maths. π Core Features: Task Manager: Add/edit/delete tasks with progress bars. Each task should be taggable with filters: "Biology", "Chemistry", "Maths", "Flashcards", "Past Papers", "Revision". Radar Chart Progress Visualisation: Interactive radar/spider chart displaying progress per subject/topic (e.g., ticking off "3.1 Biological Molecules" updates the chart). Topic structure should reflect WJEC specs (can be JSON-based). Past Paper Library (2017β2023): Include a searchable and filterable list of WJEC past papers (2017β2023) by subject and year. Allow tracking of completed papers with tick boxes or progress states (Not Started, In Progress, Completed). Profile Icon: Simple circular icon with a centered "M" and a stylish gradient (e.g., blue-purple). Theme Support: Toggle for Light Mode and Dark Mode.
|
2 |
+
This is great!
|
3 |
+
This is great! Exam Countdown: Add a date selector so I can set my upcoming exam dates manually. The countdown should dynamically update based on the current date and the user-defined exam date. Support setting multiple exams with their own countdowns (e.g., Bio U3 = June 3rd, Chem U1 = June 6th). π― Radar Chart + Topics: Update the radar chart to reflect my actual WJEC topic structure: json Copy Edit { "Biology Unit 3": ["3.1", "3.2", "3.3", "3.4", "3.5", "3.6", "3.7", "3.8"], "Biology Unit 4": ["4.1", "4.2", "4.3", "4.4", "4.5", "4.6"], "Chemistry Unit 1": ["1.1", "1.2", "1.3", "1.4", "1.5", "1.6", "1.7"], "Chemistry Unit 2": ["2.1", "2.2", "2.3", "2.4", "2.5", "2.6", "2.7", "2.8"], "Chemistry Unit 3": ["3.1", "3.2", "3.3", "3.4", "3.5", "3.6", "3.7", "3.8", "3.9"], "Chemistry Unit 4": ["4.1", "4.2", "4.3", "4.4", "4.5", "4.6", "4.7", "4.8"] } Allow checkboxes or toggles per subtopic to update radar chart progress. Bonus: if possible, clicking a segment on the radar should display related tasks or notes. π¨ UI Fixes: Fix the icons for Light/Dark mode toggle and Recent Activities β make them circular instead of pill-shaped. Keep the style clean, modern, and responsive. β
Summary: Add manual exam date setting with live countdowns. Populate radar chart with specific WJEC topics listed above. Update UI for icons to be circles, not pills.
|