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:
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user