refactor: redesign UI to match EduAI Refined Workspace

- Replace Bauhaus aesthetic with Material Design 3-inspired layout
- Add working grade/subject/chapter dropdown selectors with placeholder options
- Fix ChatInput layout: remove position:absolute, use flex column flow
- Update sidebar with Material Icons, upgrade card, and clean hover states
- Add TopNav dropdowns with click-outside-to-close behavior
- Modernize message bubbles, action buttons, and welcome state
- Replace SVG icons with Material Symbols throughout
- Apply consistent BEM naming, CSS custom properties, no inline styles
This commit is contained in:
2026-08-15 04:40:26 -04:00
parent e0ae1f91c1
commit ab88ae30f1
15 changed files with 1205 additions and 779 deletions
+183 -28
View File
@@ -1,47 +1,202 @@
/* ========================================
TopNav Component — padhLe
TopNav Component — padhle (Refined Workspace)
======================================== */
import { useState, useRef, useEffect } from "react";
import "./TopNav.css";
const TopNav = ({ activeTab, onTabChange }) => {
const tabs = [
{ id: "chat", label: "AI Chat", icon: "💬" },
{ id: "study", label: "Study Guides", icon: "📖" },
{ id: "progress", label: "Progress", icon: "📊" },
const TopNav = ({ activeGrade, onGradeChange, activeSubject, onSubjectChange, activeChapter, onChapterChange }) => {
const [openDropdown, setOpenDropdown] = useState(null);
const gradeRef = useRef(null);
const subjectRef = useRef(null);
const chapterRef = useRef(null);
// Close dropdown when clicking outside
useEffect(() => {
const handleClickOutside = (e) => {
const refs = [gradeRef, subjectRef, chapterRef];
const isInside = refs.some((ref) => ref.current && ref.current.contains(e.target));
if (!isInside) {
setOpenDropdown(null);
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => document.removeEventListener("mousedown", handleClickOutside);
}, []);
const toggleDropdown = (name) => {
setOpenDropdown((prev) => (prev === name ? null : name));
};
const selectOption = (name, value) => {
setOpenDropdown(null);
switch (name) {
case "grade":
onGradeChange(value);
break;
case "subject":
onSubjectChange(value);
break;
case "chapter":
onChapterChange(value);
break;
}
};
const grades = [
{ id: "choose-standard", label: "Choose standard" },
{ id: "Grade 6", label: "Grade 6" },
{ id: "Grade 7", label: "Grade 7" },
{ id: "Grade 8", label: "Grade 8" },
{ id: "Grade 9", label: "Grade 9" },
{ id: "Grade 10", label: "Grade 10" },
{ id: "Grade 11", label: "Grade 11" },
{ id: "Grade 12", label: "Grade 12" },
];
const subjects = [
{ id: "choose-subject", label: "Choose Subject" },
{ id: "math", label: "Mathematics" },
{ id: "science", label: "Science" },
{ id: "history", label: "History" },
{ id: "language", label: "Language Arts" },
{ id: "english", label: "English" },
{ id: "geography", label: "Geography" },
];
const chapters = [
{ id: "choose-chapter", label: "Choose Chapter" },
{ id: "chapter1", label: "Chapter 1: Rational Numbers" },
{ id: "chapter2", label: "Chapter 2: Linear Equations" },
{ id: "chapter3", label: "Chapter 3: Coordinate Geometry" },
{ id: "chapter4", label: "Chapter 4: Life Processes" },
{ id: "chapter5", label: "Chapter 5: Photosynthesis" },
{ id: "chapter6", label: "Chapter 6: Human Physiology" },
];
const subjectIcons = {
math: "calculate",
science: "science",
history: "account_balance",
language: "menu_book",
english: "edit",
geography: "public",
};
return (
<nav className="topnav">
<div className="topnav__tabs">
{tabs.map((tab) => (
<header className="topnav">
<div className="topnav__selectors">
{/* Grade Selector */}
<div className="topnav__selector" ref={gradeRef}>
<button
key={tab.id}
className={`topnav__tab ${
activeTab === tab.id ? "topnav__tab--active" : ""
}`}
onClick={() => onTabChange(tab.id)}
className={`topnav__pill topnav__pill--dropdown ${openDropdown === "grade" ? "topnav__pill--open" : ""}`}
onClick={() => toggleDropdown("grade")}
aria-label="Select Grade"
aria-expanded={openDropdown === "grade"}
>
<span className="topnav__icon">{tab.icon}</span>
<span className="topnav__label">{tab.label}</span>
<span className="material-symbols-outlined topnav__pill-icon">person_outline</span>
<span className="topnav__pill-label">{activeGrade}</span>
<span className="material-symbols-outlined topnav__pill-arrow">expand_more</span>
</button>
))}
{openDropdown === "grade" && (
<ul className="topnav__dropdown">
{grades.map((grade) => (
<li key={grade.id}>
<button
className={`topnav__dropdown-item ${activeGrade === grade.label ? "topnav__dropdown-item--active" : ""}`}
onClick={() => selectOption("grade", grade.label)}
>
{grade.label}
</button>
</li>
))}
</ul>
)}
</div>
{/* Subject Selector */}
<div className="topnav__selector" ref={subjectRef}>
<button
className={`topnav__pill topnav__pill--dropdown ${openDropdown === "subject" ? "topnav__pill--open" : ""}`}
onClick={() => toggleDropdown("subject")}
aria-label="Select Subject"
aria-expanded={openDropdown === "subject"}
>
<span className={`material-symbols-outlined topnav__pill-icon`}>
{activeSubject === "choose-subject" ? "science" : subjectIcons[activeSubject] || "science"}
</span>
<span className="topnav__pill-label">
{activeSubject === "choose-subject"
? "Choose Subject"
: subjects.find((s) => s.id === activeSubject)?.label || activeSubject}
</span>
<span className="material-symbols-outlined topnav__pill-arrow">expand_more</span>
</button>
{openDropdown === "subject" && (
<ul className="topnav__dropdown">
{subjects.map((subject) => (
<li key={subject.id}>
<button
className={`topnav__dropdown-item ${activeSubject === subject.id ? "topnav__dropdown-item--active" : ""}`}
onClick={() => selectOption("subject", subject.id)}
>
<span className={`material-symbols-outlined topnav__dropdown-icon`}>
{subject.id === "choose-subject" ? "science" : subjectIcons[subject.id]}
</span>
{subject.label}
</button>
</li>
))}
</ul>
)}
</div>
{/* Chapter Selector */}
<div className="topnav__selector" ref={chapterRef}>
<button
className={`topnav__pill topnav__pill--dropdown ${openDropdown === "chapter" ? "topnav__pill--open" : ""}`}
onClick={() => toggleDropdown("chapter")}
aria-label="Select Chapter"
aria-expanded={openDropdown === "chapter"}
>
<span className="material-symbols-outlined topnav__pill-icon">menu_book</span>
<span className="topnav__pill-label">{activeChapter}</span>
<span className="material-symbols-outlined topnav__pill-arrow">expand_more</span>
</button>
{openDropdown === "chapter" && (
<ul className="topnav__dropdown">
{chapters.map((chapter) => (
<li key={chapter.id}>
<button
className={`topnav__dropdown-item ${activeChapter === chapter.label ? "topnav__dropdown-item--active" : ""}`}
onClick={() => selectOption("chapter", chapter.label)}
>
{chapter.label}
</button>
</li>
))}
</ul>
)}
</div>
</div>
<div className="topnav__actions">
<button className="topnav__action" aria-label="Search" title="Search">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<circle cx="11" cy="11" r="8" />
<path d="M21 21l-4.35-4.35" />
</svg>
{/* Search */}
<button className="topnav__action" aria-label="Search">
<span className="material-symbols-outlined">search</span>
</button>
<button className="topnav__action" aria-label="Notifications" title="Notifications">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9" />
<path d="M13.73 21a2 2 0 0 1-3.46 0" />
</svg>
{/* Notifications */}
<button className="topnav__action" aria-label="Notifications">
<span className="material-symbols-outlined">notifications_none</span>
</button>
{/* User Avatar */}
<div className="topnav__avatar">
<span className="topnav__avatar-initial">S</span>
</div>
</div>
</nav>
</header>
);
};