diff --git a/src/components/AssignmentLogs.css b/src/components/AssignmentLogs.css
index b797b66..0afe74b 100644
--- a/src/components/AssignmentLogs.css
+++ b/src/components/AssignmentLogs.css
@@ -1,10 +1,14 @@
.logs-container {
- max-width: 1200px;
+ max-width: 1400px;
margin: 0 auto;
}
.logs-header {
- margin-bottom: 2rem;
+ margin-bottom: 1.5rem;
+ display: flex;
+ justify-content: space-between;
+ align-items: start;
+ gap: 2rem;
}
.logs-header h2 {
@@ -44,7 +48,9 @@
}
@keyframes spin {
- to { transform: rotate(360deg); }
+ to {
+ transform: rotate(360deg);
+ }
}
.retry-btn {
@@ -62,154 +68,6 @@
background-color: #6d28d9;
}
-.logs-list {
- display: flex;
- flex-direction: column;
- gap: 1rem;
-}
-
-.log-card {
- background-color: #2b2b2b;
- border: 1px solid #374151;
- border-radius: 0.5rem;
- padding: 1.25rem;
- transition: all 0.2s;
-}
-
-.log-card:hover {
- border-color: #7c3aed;
- box-shadow: 0 4px 12px rgba(124, 58, 237, 0.2);
-}
-
-.log-main {
- display: flex;
- justify-content: space-between;
- align-items: start;
- gap: 1rem;
- margin-bottom: 0.75rem;
-}
-
-.log-info {
- flex: 1;
-}
-
-.log-class {
- color: #d8b4fe;
- font-weight: 600;
- font-size: 1.125rem;
- margin-bottom: 0.25rem;
-}
-
-.log-assignment {
- color: #e0e0e0;
- font-size: 0.875rem;
- margin-bottom: 0.5rem;
-}
-
-.log-meta {
- display: flex;
- align-items: center;
- gap: 0.5rem;
- font-size: 0.75rem;
- color: #9ca3af;
-}
-
-.log-category {
- background-color: rgba(124, 58, 237, 0.2);
- color: #c4b5fd;
- padding: 0.125rem 0.5rem;
- border-radius: 0.25rem;
-}
-
-.log-separator {
- color: #4b5563;
-}
-
-.log-period {
- color: #9ca3af;
-}
-
-.log-grade-info {
- display: flex;
- flex-direction: column;
- align-items: flex-end;
- gap: 0.5rem;
-}
-
-.log-grade {
- font-size: 2rem;
- font-weight: 700;
- color: #4ade80;
-}
-
-.log-change {
- display: flex;
- align-items: center;
- gap: 0.25rem;
- padding: 0.25rem 0.5rem;
- border-radius: 0.25rem;
- font-size: 0.875rem;
- font-weight: 600;
-}
-
-.change-positive {
- background-color: rgba(74, 222, 128, 0.1);
- color: #4ade80;
-}
-
-.change-negative {
- background-color: rgba(248, 113, 113, 0.1);
- color: #f87171;
-}
-
-.change-none {
- background-color: rgba(156, 163, 175, 0.1);
- color: #9ca3af;
-}
-
-.log-footer {
- display: flex;
- align-items: center;
- gap: 0.5rem;
- padding-top: 0.75rem;
- border-top: 1px solid #374151;
- color: #6b7280;
- font-size: 0.75rem;
-}
-
-.log-timestamp {
- color: #9ca3af;
-}
-
-.log-due-date {
- color: #fbbf24;
- font-weight: 500;
-}
-
-@media (max-width: 768px) {
- .log-main {
- flex-direction: column;
- }
-
- .log-grade-info {
- align-items: flex-start;
- flex-direction: row;
- gap: 1rem;
- }
-
- .log-grade {
- font-size: 1.5rem;
- }
-}
-
-.logs-header {
- margin-bottom: 2rem;
- display: flex;
- justify-content: space-between;
- align-items: start;
- gap: 2rem;
-}
-
.sort-controls {
display: flex;
align-items: center;
@@ -239,23 +97,271 @@
box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.1);
}
-.log-due-date {
- color: #fbbf24;
- font-weight: 500;
+/* Table-like list */
+.logs-list {
+ background-color: #2b2b2b;
+ border: 1px solid #374151;
+ border-radius: 0.5rem;
+ overflow: hidden;
}
+.log-card {
+ display: grid;
+ grid-template-columns: 1.5fr 2.5fr 0.8fr 1fr 0.8fr 1fr;
+ align-items: center;
+ gap: 1rem;
+ padding: 0.75rem 1rem;
+ border-bottom: 1px solid #374151;
+ transition: background-color 0.15s;
+ min-height: 50px;
+}
+
+.log-card:last-child {
+ border-bottom: none;
+}
+
+.log-card:hover {
+ background-color: rgba(124, 58, 237, 0.05);
+}
+
+.log-class {
+ color: #d8b4fe;
+ font-weight: 600;
+ font-size: 0.875rem;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.log-assignment {
+ color: #e0e0e0;
+ font-size: 0.875rem;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.log-meta {
+ display: flex;
+ align-items: center;
+ gap: 0.25rem;
+}
+
+.log-category {
+ background-color: rgba(124, 58, 237, 0.2);
+ color: #c4b5fd;
+ padding: 0.125rem 0.375rem;
+ border-radius: 0.25rem;
+ font-size: 0.7rem;
+ white-space: nowrap;
+}
+
+.log-due-date {
+ color: #fbbf24;
+ font-size: 0.75rem;
+ white-space: nowrap;
+}
+
+.log-grade {
+ font-size: 1.125rem;
+ font-weight: 700;
+ color: #4ade80;
+ text-align: right;
+}
+
+.log-change-timestamp {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-end;
+ gap: 0.25rem;
+}
+
+.log-change {
+ display: flex;
+ align-items: center;
+ gap: 0.25rem;
+ font-size: 0.75rem;
+ font-weight: 600;
+}
+
+.change-positive {
+ color: #4ade80;
+}
+
+.change-negative {
+ color: #f87171;
+}
+
+.change-none {
+ color: #9ca3af;
+}
+
+.log-timestamp {
+ color: #6b7280;
+ font-size: 0.7rem;
+ display: flex;
+ align-items: center;
+ gap: 0.25rem;
+ white-space: nowrap;
+}
+
+/* Pagination */
+.pagination {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ gap: 1rem;
+ margin-top: 1.5rem;
+ padding: 1rem;
+}
+
+.pagination-info {
+ color: #9ca3af;
+ font-size: 0.875rem;
+ font-family: "JetBrains Mono", monospace;
+}
+
+.pagination-buttons {
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+}
+
+.pagination-btn {
+ background-color: #2b2b2b;
+ border: 1px solid #374151;
+ color: #e0e0e0;
+ padding: 0.5rem 0.75rem;
+ border-radius: 0.375rem;
+ font-family: "JetBrains Mono", monospace;
+ font-size: 0.875rem;
+ cursor: pointer;
+ transition: all 0.2s;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.pagination-btn:hover:not(:disabled) {
+ border-color: #7c3aed;
+ background-color: #323232;
+}
+
+.pagination-btn:disabled {
+ opacity: 0.3;
+ cursor: not-allowed;
+}
+
+/* Tablet */
+@media (max-width: 1024px) {
+ .log-card {
+ grid-template-columns: 1.2fr 2fr 0.7fr 0.8fr 0.7fr 1fr;
+ gap: 0.75rem;
+ padding: 0.65rem 0.75rem;
+ }
+
+ .log-class,
+ .log-assignment {
+ font-size: 0.8rem;
+ }
+
+ .log-grade {
+ font-size: 1rem;
+ }
+}
+
+/* Mobile */
@media (max-width: 768px) {
.logs-header {
flex-direction: column;
gap: 1rem;
}
-
+
.sort-controls {
width: 100%;
}
-
+
.sort-dropdown {
flex: 1;
width: 100%;
}
-}
\ No newline at end of file
+
+ .log-card {
+ grid-template-columns: 1fr;
+ gap: 0.5rem;
+ padding: 0.875rem;
+ }
+
+ .log-class {
+ font-size: 0.95rem;
+ font-weight: 700;
+ }
+
+ .log-assignment {
+ font-size: 0.85rem;
+ margin-top: 0.125rem;
+ }
+
+ .log-meta {
+ margin-top: 0.25rem;
+ flex-wrap: wrap;
+ }
+
+ .log-due-date {
+ font-size: 0.75rem;
+ }
+
+ .log-grade {
+ font-size: 1.25rem;
+ text-align: left;
+ margin-top: 0.5rem;
+ }
+
+ .log-change-timestamp {
+ align-items: flex-start;
+ margin-top: 0.25rem;
+ }
+
+ .log-timestamp {
+ font-size: 0.7rem;
+ }
+
+ .pagination {
+ padding: 0.75rem;
+ }
+
+ .pagination-info {
+ font-size: 0.75rem;
+ }
+
+ .pagination-btn {
+ padding: 0.5rem;
+ }
+}
+
+/* Small mobile */
+@media (max-width: 480px) {
+ .logs-header h2 {
+ font-size: 1.5rem;
+ }
+
+ .log-card {
+ padding: 0.75rem;
+ }
+
+ .log-class {
+ font-size: 0.875rem;
+ }
+
+ .log-assignment {
+ font-size: 0.8rem;
+ }
+
+ .log-category {
+ font-size: 0.65rem;
+ }
+
+ .log-grade {
+ font-size: 1.125rem;
+ }
+}
diff --git a/src/components/AssignmentLogs.jsx b/src/components/AssignmentLogs.jsx
index abe4ef3..8988571 100644
--- a/src/components/AssignmentLogs.jsx
+++ b/src/components/AssignmentLogs.jsx
@@ -1,28 +1,34 @@
import { useState, useEffect } from 'react';
-import { Clock, TrendingUp, TrendingDown, Minus, ArrowUpDown } from 'lucide-react';
+import { Clock, TrendingUp, TrendingDown, Minus, ArrowUpDown, ChevronLeft, ChevronRight } from 'lucide-react';
import { api } from '../services/api';
import { useAuth } from '../contexts/AuthContext';
import './AssignmentLogs.css';
+const ITEMS_PER_PAGE = 20;
+
export const AssignmentLogs = () => {
const { user } = useAuth();
const [logs, setLogs] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [sortBy, setSortBy] = useState('due-newest');
+ const [currentPage, setCurrentPage] = useState(1);
useEffect(() => {
fetchLogs();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
+ useEffect(() => {
+ setCurrentPage(1); // Reset to page 1 when sort changes
+ }, [sortBy]);
+
const fetchLogs = async () => {
setLoading(true);
setError(null);
try {
const result = await api.getGrades(user.username);
- // Transform classes into logs
const allLogs = [];
result.user.classes.forEach(cls => {
cls.assignments.forEach(assignment => {
@@ -63,7 +69,6 @@ export const AssignmentLogs = () => {
case 'time-newest':
sorted.sort((a, b) => {
const timeDiff = new Date(b.timestamp) - new Date(a.timestamp);
- // Tie-break with due date
if (timeDiff === 0) {
return new Date(b.dueDate) - new Date(a.dueDate);
}
@@ -74,7 +79,6 @@ export const AssignmentLogs = () => {
case 'time-oldest':
sorted.sort((a, b) => {
const timeDiff = new Date(a.timestamp) - new Date(b.timestamp);
- // Tie-break with due date
if (timeDiff === 0) {
return new Date(a.dueDate) - new Date(b.dueDate);
}
@@ -85,7 +89,6 @@ export const AssignmentLogs = () => {
case 'class-az':
sorted.sort((a, b) => {
const classCompare = a.className.localeCompare(b.className);
- // Within same class, sort assignments A-Z
if (classCompare === 0) {
return a.assignmentName.localeCompare(b.assignmentName);
}
@@ -96,7 +99,6 @@ export const AssignmentLogs = () => {
case 'class-za':
sorted.sort((a, b) => {
const classCompare = b.className.localeCompare(a.className);
- // Within same class, sort assignments Z-A
if (classCompare === 0) {
return b.assignmentName.localeCompare(a.assignmentName);
}
@@ -121,7 +123,6 @@ export const AssignmentLogs = () => {
const calculateGradeChanges = (sortedLogs) => {
return sortedLogs.map((log, index) => {
- // Look for an older version of the same assignment
const olderVersion = sortedLogs.slice(index + 1).find(
oldLog =>
oldLog.assignmentName === log.assignmentName &&
@@ -130,7 +131,6 @@ export const AssignmentLogs = () => {
);
if (olderVersion) {
- // Parse scores (remove % if present)
const newScore = parseFloat(log.newGrade.toString().replace('%', ''));
const oldScore = parseFloat(olderVersion.newGrade.toString().replace('%', ''));
return { ...log, gradeChange: newScore - oldScore };
@@ -145,7 +145,6 @@ export const AssignmentLogs = () => {
return date.toLocaleString('en-US', {
month: 'short',
day: 'numeric',
- year: 'numeric',
hour: 'numeric',
minute: '2-digit',
hour12: true
@@ -153,9 +152,9 @@ export const AssignmentLogs = () => {
};
const getChangeIcon = (change) => {
- if (!change) return
Track all grade updates and changes
+Tracking {processedLogs.length} assignments
No assignment logs found