Improve responsive layout for study and quiz cards
All checks were successful
Automated Container Build / build-and-push (push) Successful in 1m14s
All checks were successful
Automated Container Build / build-and-push (push) Successful in 1m14s
This commit is contained in:
parent
e3265f03e2
commit
6d1d918355
3 changed files with 78 additions and 41 deletions
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue