Improve responsive layout for study and quiz cards
All checks were successful
Automated Container Build / build-and-push (push) Successful in 1m14s

This commit is contained in:
Elijah 2026-07-13 06:32:54 -07:00
parent e3265f03e2
commit 6d1d918355
3 changed files with 78 additions and 41 deletions

View file

@ -99,9 +99,10 @@ function SortableDeckCard({ deck, onEdit, onDelete, classSlug }: any) {
)}
</div>
<div className="flex items-center gap-2 pt-4 border-t border-border-light pl-7">
{deck.progress?.length ? (
<>
<div className="flex flex-col gap-2 border-t border-border-light pt-4 pl-7 sm:flex-row sm:items-center">
<div className="flex w-full min-w-0 gap-2 sm:flex-1">
{deck.progress?.length ? (
<>
<Link href={`/${classSlug}/flashcards/${deck.id}`} className="flex-1 text-center py-2 px-4 rounded-lg bg-primary text-white text-sm font-medium hover:bg-primary-hover transition-all duration-200">
Continue
</Link>
@ -119,23 +120,26 @@ function SortableDeckCard({ deck, onEdit, onDelete, classSlug }: any) {
>
Restart
</button>
</>
) : (
<Link href={`/${classSlug}/flashcards/${deck.id}`} className="flex-1 text-center py-2 px-4 rounded-lg bg-primary text-white text-sm font-medium hover:bg-primary-hover transition-all duration-200">
Study
</Link>
)}
<button onClick={() => onEdit(deck)} className="p-2 rounded-lg text-text-muted hover:text-text-heading hover:bg-bg-surface-alt transition-all duration-200 cursor-pointer" title="Edit">
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
</svg>
</button>
<button onClick={() => onDelete(deck)} className="p-2 rounded-lg text-text-muted hover:text-error hover:bg-error-bg transition-all duration-200 cursor-pointer" title="Delete">
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
</button>
<ShareMenu targetType="DECK" contentId={deck.id} classSlug={classSlug} compact />
</>
) : (
<Link href={`/${classSlug}/flashcards/${deck.id}`} className="flex-1 text-center py-2 px-4 rounded-lg bg-primary text-white text-sm font-medium hover:bg-primary-hover transition-all duration-200">
Study
</Link>
)}
</div>
<div className="flex items-center justify-end gap-2">
<button onClick={() => onEdit(deck)} className="p-2 rounded-lg text-text-muted hover:text-text-heading hover:bg-bg-surface-alt transition-all duration-200 cursor-pointer" title="Edit">
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
</svg>
</button>
<button onClick={() => onDelete(deck)} className="p-2 rounded-lg text-text-muted hover:text-error hover:bg-error-bg transition-all duration-200 cursor-pointer" title="Delete">
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
</button>
<ShareMenu targetType="DECK" contentId={deck.id} classSlug={classSlug} compact />
</div>
</div>
</div>
</div>

View file

@ -35,6 +35,12 @@ interface QuizItem {
groupId: string | null;
sortOrder: number;
_count: { questions: number; attempts: number };
progress: Array<{
mode: string;
currentIndex: number;
orderJson: string;
answersJson: string | null;
}>;
}
interface MaterialGroup {
@ -79,21 +85,48 @@ function SortableQuizCard({ quiz, onEdit, onDelete, classSlug }: any) {
)}
</div>
<div className="flex items-center gap-2 mt-4 pt-4 border-t border-border-light pl-7">
<Link href={`/${classSlug}/quizzes/${quiz.id}`} className="flex-1 text-center py-2 px-4 rounded-lg bg-primary text-white text-sm font-medium hover:bg-primary-hover transition-all duration-200">
Start Quiz
</Link>
<button onClick={() => onEdit(quiz)} className="p-2 rounded-lg text-text-muted hover:text-text-heading hover:bg-bg-surface-alt transition-all duration-200 cursor-pointer" title="Edit">
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
</svg>
</button>
<button onClick={() => onDelete(quiz)} className="p-2 rounded-lg text-text-muted hover:text-error hover:bg-error-bg transition-all duration-200 cursor-pointer" title="Delete">
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
</button>
<ShareMenu targetType="QUIZ" contentId={quiz.id} classSlug={classSlug} compact />
<div className="mt-4 flex flex-col gap-2 border-t border-border-light pt-4 pl-7 sm:flex-row sm:items-center">
<div className="flex w-full min-w-0 gap-2 sm:flex-1">
{quiz.progress?.length ? (
<>
<Link href={`/${classSlug}/quizzes/${quiz.id}`} className="flex-1 text-center py-2 px-4 rounded-lg bg-primary text-white text-sm font-medium hover:bg-primary-hover transition-all duration-200">
Continue
</Link>
<button
onClick={async () => {
if (!confirm("Start over from the beginning? This will clear your current progress for this quiz.")) return;
await fetch("/api/progress", {
method: "DELETE",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ contentType: "QUIZ", contentId: quiz.id, mode: "SEQUENTIAL" }),
}).catch(() => {});
window.location.href = `/${classSlug}/quizzes/${quiz.id}`;
}}
className="flex-1 cursor-pointer rounded-lg border border-border bg-bg-surface-alt px-4 py-2 text-center text-sm font-medium text-text-heading shadow-sm transition-all duration-200 hover:bg-border"
title="Restart"
>
Restart
</button>
</>
) : (
<Link href={`/${classSlug}/quizzes/${quiz.id}`} className="flex-1 text-center py-2 px-4 rounded-lg bg-primary text-white text-sm font-medium hover:bg-primary-hover transition-all duration-200">
Start Quiz
</Link>
)}
</div>
<div className="flex items-center justify-end gap-2">
<button onClick={() => onEdit(quiz)} className="p-2 rounded-lg text-text-muted hover:text-text-heading hover:bg-bg-surface-alt transition-all duration-200 cursor-pointer" title="Edit">
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
</svg>
</button>
<button onClick={() => onDelete(quiz)} className="p-2 rounded-lg text-text-muted hover:text-error hover:bg-error-bg transition-all duration-200 cursor-pointer" title="Delete">
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
</button>
<ShareMenu targetType="QUIZ" contentId={quiz.id} classSlug={classSlug} compact />
</div>
</div>
</div>
</div>

View file

@ -350,11 +350,11 @@ export function QuizViewer({ quiz, retakeIds, isShared = false, onFinished }: Qu
<div className="px-5 pb-6 pt-6 md:px-10 md:pb-10 md:pt-8">
{/* Tags */}
<div className="flex items-center justify-between mb-8">
<span className="inline-flex px-3 py-1 rounded-md bg-primary/10 text-primary text-xs font-bold uppercase tracking-wider">
<div className="mb-8 flex items-start justify-between gap-3">
<span className="inline-flex w-fit min-w-0 max-w-[48%] px-3 py-1 rounded-md bg-primary/10 text-primary text-xs font-bold uppercase tracking-wider leading-snug whitespace-normal">
{currentQ.category}
</span>
<span className="inline-flex px-3 py-1 rounded-md bg-amber-500/10 text-amber-600 text-xs font-bold uppercase tracking-wider">
<span className="inline-flex w-fit min-w-0 max-w-[48%] justify-end px-3 py-1 rounded-md bg-amber-500/10 text-right text-amber-600 text-xs font-bold uppercase tracking-wider leading-snug whitespace-normal">
{currentQ.type === "MULTIPLE_CHOICE" ? "Multiple Choice" : "Select All That Apply"}
</span>
</div>
@ -382,7 +382,7 @@ export function QuizViewer({ quiz, retakeIds, isShared = false, onFinished }: Qu
} else if (!isSelected && opt.isCorrect) {
stateClass = "border-success/50 bg-success/5 text-text-heading";
rightIcon = (
<div className="text-success flex items-center gap-1.5 text-sm font-bold">
<div className="flex items-center gap-1.5 text-sm font-bold text-success">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2.5} d="M5 13l4 4L19 7" />
</svg>
@ -400,7 +400,7 @@ export function QuizViewer({ quiz, retakeIds, isShared = false, onFinished }: Qu
<div
key={opt.id}
onClick={() => toggleOption(opt.id)}
className={`flex min-h-14 items-center gap-4 rounded-2xl border px-4 py-4 transition-all duration-200 md:px-5 ${stateClass}`}
className={`grid min-h-14 grid-cols-[auto_minmax(0,1fr)] items-center gap-x-4 gap-y-2 rounded-2xl border px-4 py-4 transition-all duration-200 md:flex md:gap-4 md:px-5 ${stateClass}`}
>
<div className="flex-shrink-0">
<div className={`w-5 h-5 rounded ${currentQ.type === 'SATA' ? 'border' : 'border rounded-full'} flex items-center justify-center transition-colors ${
@ -424,7 +424,7 @@ export function QuizViewer({ quiz, retakeIds, isShared = false, onFinished }: Qu
{opt.text}
</div>
{rightIcon && (
<div className="flex-shrink-0 pl-2">
<div className="col-start-2 min-w-0 md:flex-shrink-0 md:pl-2">
{rightIcon}
</div>
)}