From 607d00c908dd2cd0d63682958cd0b66e53a7ee11 Mon Sep 17 00:00:00 2001 From: KeshavAnandCode Date: Mon, 22 Dec 2025 19:39:58 -0600 Subject: [PATCH] Fixed assignment logs page to be flatter --- src/components/AssignmentLogs.css | 420 +++++++++++++++++++----------- src/components/AssignmentLogs.jsx | 98 ++++--- 2 files changed, 321 insertions(+), 197 deletions(-) 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 ; - if (change > 0) return ; - return ; + if (!change) return ; + if (change > 0) return ; + return ; }; const getChangeClass = (change) => { @@ -184,13 +183,19 @@ export const AssignmentLogs = () => { const sortedLogs = sortLogs(logs, sortBy); const processedLogs = calculateGradeChanges(sortedLogs); + + // Pagination + const totalPages = Math.ceil(processedLogs.length / ITEMS_PER_PAGE); + const startIndex = (currentPage - 1) * ITEMS_PER_PAGE; + const endIndex = startIndex + ITEMS_PER_PAGE; + const currentLogs = processedLogs.slice(startIndex, endIndex); return (

Assignment Logs

-

Track all grade updates and changes

+

Tracking {processedLogs.length} assignments

@@ -218,42 +223,55 @@ export const AssignmentLogs = () => {

No assignment logs found

) : ( -
- {processedLogs.map((log, idx) => ( -
-
-
-
{log.className}
-
{log.assignmentName}
-
- {log.category} - - {log.period} - {log.dueDate && ( - <> - - Due: {log.dueDate} - - )} -
+ <> +
+ {currentLogs.map((log, idx) => ( +
+
{log.className}
+
{log.assignmentName}
+
+ {log.category}
-
-
{log.newGrade}
+
{log.dueDate}
+
{log.newGrade}
+
{log.gradeChange !== 0 && (
{getChangeIcon(log.gradeChange)} - {Math.abs(log.gradeChange).toFixed(2)} + {Math.abs(log.gradeChange).toFixed(1)}
)} +
+ + {formatDate(log.timestamp)} +
-
- - {formatDate(log.timestamp)} -
+ ))} +
+ +
+
+ + + Page {currentPage} of {totalPages} + +
- ))} -
+
+ )}
);