New: Ui Redesign

This commit is contained in:
Elijah 2026-07-11 13:48:02 -07:00
parent 009d1eb065
commit 631dfa5923
22 changed files with 464 additions and 524 deletions

View file

@ -90,9 +90,9 @@ export default function DeckStudyPage() {
}
return (
<div className="max-w-4xl mx-auto w-full py-2 md:py-4">
<div className="mx-auto w-full max-w-5xl py-1 md:py-3">
{/* Header */}
<div className="flex flex-col md:flex-row items-start md:items-center justify-between gap-4 mb-6">
<div className="mb-7 flex flex-col items-start justify-between gap-4 rounded-2xl border border-border-light bg-bg-surface/70 p-4 shadow-[var(--shadow-card)] sm:p-5 md:flex-row md:items-center">
<div>
<button
onClick={() => router.push(`/${classSlug}/flashcards`)}
@ -103,7 +103,8 @@ export default function DeckStudyPage() {
</svg>
Back to decks
</button>
<h1 className="text-xl font-bold text-text-heading">{deck.name}</h1>
<p className="text-xs font-bold uppercase tracking-[0.16em] text-primary">Flashcard session</p>
<h1 className="editorial-title mt-1 text-3xl text-text-heading">{deck.name}</h1>
{deck.description && (
<p className="text-sm text-text-secondary mt-1">{deck.description}</p>
)}
@ -126,7 +127,7 @@ export default function DeckStudyPage() {
<div className="flex items-center gap-2 md:gap-4 ml-auto">
<ShareMenu targetType="DECK" contentId={deck.id} classSlug={classSlug} />
<div className="flex bg-bg-surface-alt rounded-lg p-0.5 border border-border-light">
<div className="flex rounded-xl border border-border-light bg-bg-surface-alt p-1">
<button
onClick={() => setView("study")}
className={`px-4 py-2 rounded-md text-sm font-medium transition-all duration-200 cursor-pointer ${

View file

@ -76,8 +76,8 @@ function SortableDeckCard({ deck, onEdit, onDelete, classSlug }: any) {
const progressLabel = getProgressLabel(deck);
return (
<div ref={setNodeRef} style={style} className="group flex flex-col bg-bg-surface rounded-xl border border-border-light shadow-[var(--shadow-card)] hover:shadow-[var(--shadow-card-hover)] hover:border-primary/20 transition-all duration-300 h-full">
<div className="p-4 flex flex-col flex-1 h-full">
<div ref={setNodeRef} style={style} className="group flex h-full flex-col rounded-2xl border border-border-light bg-bg-surface shadow-[var(--shadow-card)] transition-all duration-300 hover:-translate-y-0.5 hover:border-primary/25 hover:shadow-[var(--shadow-card-hover)]">
<div className="flex h-full flex-1 flex-col p-5">
<div className="flex items-start gap-2">
<div {...attributes} {...listeners} className="mt-1 cursor-grab active:cursor-grabbing text-text-muted hover:text-text-heading">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@ -85,7 +85,7 @@ function SortableDeckCard({ deck, onEdit, onDelete, classSlug }: any) {
</svg>
</div>
<div className="flex-1">
<h3 className="text-lg font-semibold text-text-heading mb-1">{deck.name}</h3>
<h3 className="mb-1 text-lg font-extrabold text-text-heading">{deck.name}</h3>
{deck.description && <p className="text-sm text-text-secondary mb-3 line-clamp-2">{deck.description}</p>}
</div>
</div>
@ -369,15 +369,15 @@ export default function FlashcardsPage() {
const activeDeck = decks.find(d => d.id === activeId);
return (
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8 pb-20">
<div className="pb-20">
{/* Header */}
<div className="flex items-center justify-between mb-6">
<h2 className="text-xl font-semibold text-text-heading">Flashcard Decks</h2>
<div className="flex items-center gap-3">
<button onClick={() => setIsCreatingGroup(true)} className="inline-flex items-center gap-2 px-4 py-2.5 rounded-lg bg-bg-surface-alt text-text-heading font-medium border border-border-light hover:bg-border transition-all duration-200 shadow-sm cursor-pointer">
<div className="mb-6 flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
<div><p className="text-xs font-bold uppercase tracking-[0.16em] text-primary">Collection</p><h2 className="editorial-title mt-1 text-3xl text-text-heading">Flashcard decks</h2></div>
<div className="grid grid-cols-2 gap-3 sm:flex">
<button onClick={() => setIsCreatingGroup(true)} className="inline-flex min-h-12 items-center justify-center gap-2 rounded-xl border border-border-light bg-bg-surface px-4 font-bold text-text-heading shadow-sm transition-colors hover:bg-bg-surface-alt">
Add Group
</button>
<button onClick={() => setShowImport(true)} className="inline-flex items-center gap-2 px-4 py-2.5 rounded-lg bg-primary text-white font-medium hover:bg-primary-hover transition-all duration-200 shadow-sm cursor-pointer">
<button onClick={() => setShowImport(true)} className="inline-flex min-h-12 items-center justify-center gap-2 rounded-xl bg-primary px-4 font-bold text-white shadow-sm transition-colors hover:bg-primary-hover">
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
</svg>
@ -387,7 +387,7 @@ export default function FlashcardsPage() {
</div>
{isCreatingGroup && (
<div className="mb-6 p-4 bg-bg-surface rounded-xl border border-primary/20 shadow-sm flex items-center gap-3">
<div className="animate-slide-up mb-6 flex flex-col gap-3 rounded-2xl border border-primary/20 bg-bg-surface p-5 shadow-sm sm:flex-row sm:items-center">
<input
autoFocus
type="text"
@ -407,8 +407,8 @@ export default function FlashcardsPage() {
{editingId && (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/40 backdrop-blur-sm">
<div className="bg-bg-surface rounded-xl p-6 w-full max-w-md shadow-lg border border-border">
<h3 className="text-lg font-semibold text-text-heading mb-4">Edit Deck</h3>
<div className="w-full max-w-md rounded-3xl border border-border bg-bg-surface p-6 shadow-[var(--shadow-modal)]">
<h3 className="editorial-title mb-4 text-2xl text-text-heading">Edit deck</h3>
<input
type="text"
value={editName}
@ -443,7 +443,7 @@ export default function FlashcardsPage() {
<DndContext sensors={sensors} collisionDetection={pointerWithin} onDragStart={handleDragStart} onDragOver={handleDragOver} onDragEnd={handleDragEnd}>
<div className="space-y-8">
{groupedDecks.map(group => (
<div key={group.id} className="bg-bg-surface-alt/30 p-4 rounded-xl border border-border-light">
<div key={group.id} className="rounded-2xl border border-border-light bg-bg-surface/70 p-4 shadow-[var(--shadow-card)] sm:p-5">
<div className={`flex items-center justify-between ${collapsedGroups[group.id] ? "" : "mb-4"}`}>
{editingGroupId === group.id ? (
<div className="flex items-center gap-2">
@ -456,7 +456,7 @@ export default function FlashcardsPage() {
<button onClick={() => toggleGroup(group.id)} className="p-1 rounded text-text-muted hover:text-text-heading hover:bg-bg-surface transition-colors cursor-pointer" title={collapsedGroups[group.id] ? "Expand" : "Collapse"}>
<svg className={`w-5 h-5 transition-transform duration-200 ${collapsedGroups[group.id] ? '-rotate-90' : ''}`} fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" /></svg>
</button>
<h3 className="text-lg font-semibold text-text-heading">{group.name}</h3>
<h3 className="text-lg font-extrabold text-text-heading">{group.name}</h3>
</div>
)}
@ -484,7 +484,7 @@ export default function FlashcardsPage() {
))}
{/* Uncategorized */}
<div className="bg-bg-surface-alt/30 p-4 rounded-xl border border-border-light">
<div className="rounded-2xl border border-border-light bg-bg-surface/70 p-4 shadow-[var(--shadow-card)] sm:p-5">
<div className={`flex items-center gap-2 ${collapsedGroups["uncategorized"] ? "" : "mb-4"}`}>
<button onClick={() => toggleGroup("uncategorized")} className="p-1 rounded text-text-muted hover:text-text-heading hover:bg-bg-surface transition-colors cursor-pointer" title={collapsedGroups["uncategorized"] ? "Expand" : "Collapse"}>
<svg className={`w-5 h-5 transition-transform duration-200 ${collapsedGroups["uncategorized"] ? '-rotate-90' : ''}`} fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" /></svg>

View file

@ -11,7 +11,7 @@ export default async function ClassLayout(props: LayoutProps<"/[classSlug]">) {
}
return (
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div className="app-page">
{/* Dynamic Header & Tabs */}
<ClassHeader classSlug={classSlug} className={classData.name} />

View file

@ -124,9 +124,9 @@ export default function QuizStudyPage() {
}
return (
<div className="max-w-4xl mx-auto w-full py-2 md:py-4">
<div className="mx-auto w-full max-w-5xl py-1 md:py-3">
{/* Header */}
<div className="flex flex-col md:flex-row items-start justify-between gap-4 mb-6">
<div className="mb-7 flex flex-col items-start justify-between gap-4 rounded-2xl border border-border-light bg-bg-surface/70 p-4 shadow-[var(--shadow-card)] sm:p-5 md:flex-row">
<div>
<button
onClick={() => router.push(`/${classSlug}/quizzes`)}
@ -142,7 +142,7 @@ export default function QuizStudyPage() {
onClick={() => setShowTopics(!showTopics)}
className="flex items-center gap-2 group cursor-pointer text-left focus:outline-none mb-2"
>
<h1 className="text-3xl font-bold font-serif text-text-heading group-hover:text-primary transition-colors">
<h1 className="editorial-title text-3xl text-text-heading transition-colors group-hover:text-primary sm:text-4xl">
{quiz.name}
</h1>
<svg
@ -180,7 +180,7 @@ export default function QuizStudyPage() {
<ShareMenu targetType="QUIZ" contentId={quiz.id} classSlug={classSlug} />
{attempts.length > 0 && (
<div className="flex bg-bg-surface-alt rounded-lg p-0.5 border border-border-light">
<div className="flex rounded-xl border border-border-light bg-bg-surface-alt p-1">
<button
onClick={() => setView("history")}
className={`px-4 py-2 rounded-md text-sm font-medium transition-all duration-200 cursor-pointer ${

View file

@ -54,8 +54,8 @@ function SortableQuizCard({ quiz, onEdit, onDelete, classSlug }: any) {
};
return (
<div ref={setNodeRef} style={style} className="group bg-bg-surface rounded-xl border border-border-light shadow-[var(--shadow-card)] hover:shadow-[var(--shadow-card-hover)] hover:border-primary/20 transition-all duration-300">
<div className="p-4 flex flex-col flex-1 h-full">
<div ref={setNodeRef} style={style} className="group rounded-2xl border border-border-light bg-bg-surface shadow-[var(--shadow-card)] transition-all duration-300 hover:-translate-y-0.5 hover:border-primary/25 hover:shadow-[var(--shadow-card-hover)]">
<div className="flex h-full flex-1 flex-col p-5">
<div className="flex items-start gap-2">
<div {...attributes} {...listeners} className="mt-1 cursor-grab active:cursor-grabbing text-text-muted hover:text-text-heading">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@ -63,7 +63,7 @@ function SortableQuizCard({ quiz, onEdit, onDelete, classSlug }: any) {
</svg>
</div>
<div className="flex-1">
<h3 className="text-lg font-semibold text-text-heading mb-1">{quiz.name}</h3>
<h3 className="mb-1 text-lg font-extrabold text-text-heading">{quiz.name}</h3>
{quiz.description && <p className="text-sm text-text-secondary mb-3 line-clamp-2">{quiz.description}</p>}
</div>
</div>
@ -343,15 +343,15 @@ export default function QuizzesPage() {
const activeQuiz = quizzes.find(q => q.id === activeId);
return (
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8 pb-20">
<div className="pb-20">
{/* Header */}
<div className="flex items-center justify-between mb-6">
<h2 className="text-xl font-semibold text-text-heading">Quizzes</h2>
<div className="flex items-center gap-3">
<button onClick={() => setIsCreatingGroup(true)} className="inline-flex items-center gap-2 px-4 py-2.5 rounded-lg bg-bg-surface-alt text-text-heading font-medium border border-border-light hover:bg-border transition-all duration-200 shadow-sm cursor-pointer">
<div className="mb-6 flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
<div><p className="text-xs font-bold uppercase tracking-[0.16em] text-primary">Practice</p><h2 className="editorial-title mt-1 text-3xl text-text-heading">Practice quizzes</h2></div>
<div className="grid grid-cols-2 gap-3 sm:flex">
<button onClick={() => setIsCreatingGroup(true)} className="inline-flex min-h-12 items-center justify-center gap-2 rounded-xl border border-border-light bg-bg-surface px-4 font-bold text-text-heading shadow-sm transition-colors hover:bg-bg-surface-alt">
Add Group
</button>
<button onClick={() => setShowImport(true)} className="inline-flex items-center gap-2 px-4 py-2.5 rounded-lg bg-primary text-white font-medium hover:bg-primary-hover transition-all duration-200 shadow-sm cursor-pointer">
<button onClick={() => setShowImport(true)} className="inline-flex min-h-12 items-center justify-center gap-2 rounded-xl bg-primary px-4 font-bold text-white shadow-sm transition-colors hover:bg-primary-hover">
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
</svg>
@ -361,7 +361,7 @@ export default function QuizzesPage() {
</div>
{isCreatingGroup && (
<div className="mb-6 p-4 bg-bg-surface rounded-xl border border-primary/20 shadow-sm flex items-center gap-3">
<div className="animate-slide-up mb-6 flex flex-col gap-3 rounded-2xl border border-primary/20 bg-bg-surface p-5 shadow-sm sm:flex-row sm:items-center">
<input
autoFocus
type="text"
@ -382,8 +382,8 @@ export default function QuizzesPage() {
{/* Editing quiz modal/inline - simplified as a modal-like overlay for simplicity when editing */}
{editingId && (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/40 backdrop-blur-sm">
<div className="bg-bg-surface rounded-xl p-6 w-full max-w-md shadow-lg border border-border">
<h3 className="text-lg font-semibold text-text-heading mb-4">Edit Quiz</h3>
<div className="w-full max-w-md rounded-3xl border border-border bg-bg-surface p-6 shadow-[var(--shadow-modal)]">
<h3 className="editorial-title mb-4 text-2xl text-text-heading">Edit quiz</h3>
<input
type="text"
value={editName}
@ -418,7 +418,7 @@ export default function QuizzesPage() {
<DndContext sensors={sensors} collisionDetection={pointerWithin} onDragStart={handleDragStart} onDragOver={handleDragOver} onDragEnd={handleDragEnd}>
<div className="space-y-8">
{groupedQuizzes.map(group => (
<div key={group.id} className="bg-bg-surface-alt/30 p-4 rounded-xl border border-border-light">
<div key={group.id} className="rounded-2xl border border-border-light bg-bg-surface/70 p-4 shadow-[var(--shadow-card)] sm:p-5">
<div className={`flex items-center justify-between ${collapsedGroups[group.id] ? "" : "mb-4"}`}>
{editingGroupId === group.id ? (
<div className="flex items-center gap-2">
@ -431,7 +431,7 @@ export default function QuizzesPage() {
<button onClick={() => toggleGroup(group.id)} className="p-1 rounded text-text-muted hover:text-text-heading hover:bg-bg-surface transition-colors cursor-pointer" title={collapsedGroups[group.id] ? "Expand" : "Collapse"}>
<svg className={`w-5 h-5 transition-transform duration-200 ${collapsedGroups[group.id] ? '-rotate-90' : ''}`} fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" /></svg>
</button>
<h3 className="text-lg font-semibold text-text-heading">{group.name}</h3>
<h3 className="text-lg font-extrabold text-text-heading">{group.name}</h3>
</div>
)}
@ -459,7 +459,7 @@ export default function QuizzesPage() {
))}
{/* Uncategorized */}
<div className="bg-bg-surface-alt/30 p-4 rounded-xl border border-border-light">
<div className="rounded-2xl border border-border-light bg-bg-surface/70 p-4 shadow-[var(--shadow-card)] sm:p-5">
<div className={`flex items-center gap-2 ${collapsedGroups["uncategorized"] ? "" : "mb-4"}`}>
<button onClick={() => toggleGroup("uncategorized")} className="p-1 rounded text-text-muted hover:text-text-heading hover:bg-bg-surface transition-colors cursor-pointer" title={collapsedGroups["uncategorized"] ? "Expand" : "Collapse"}>
<svg className={`w-5 h-5 transition-transform duration-200 ${collapsedGroups["uncategorized"] ? '-rotate-90' : ''}`} fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" /></svg>