feat: Add list view for shared flashcards and fix restart button layout
All checks were successful
Automated Container Build / build-and-push (push) Successful in 55s
All checks were successful
Automated Container Build / build-and-push (push) Successful in 55s
This commit is contained in:
parent
7d2c466e9b
commit
e4b32c6bf2
2 changed files with 105 additions and 16 deletions
|
|
@ -3,6 +3,7 @@
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { FlashcardViewer } from "@/components/flashcards/FlashcardViewer";
|
import { FlashcardViewer } from "@/components/flashcards/FlashcardViewer";
|
||||||
import { QuizViewer } from "@/components/quizzes/QuizViewer";
|
import { QuizViewer } from "@/components/quizzes/QuizViewer";
|
||||||
|
import { CardList } from "@/components/flashcards/CardList";
|
||||||
|
|
||||||
interface SharedViewerProps {
|
interface SharedViewerProps {
|
||||||
type: string;
|
type: string;
|
||||||
|
|
@ -11,6 +12,7 @@ interface SharedViewerProps {
|
||||||
|
|
||||||
export function SharedViewer({ type, data }: SharedViewerProps) {
|
export function SharedViewer({ type, data }: SharedViewerProps) {
|
||||||
const [restartKey, setRestartKey] = useState(0);
|
const [restartKey, setRestartKey] = useState(0);
|
||||||
|
const [view, setView] = useState<"study" | "list">("study");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -29,26 +31,57 @@ export function SharedViewer({ type, data }: SharedViewerProps) {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div className="flex items-center gap-2 md:gap-4 self-end md:self-auto">
|
||||||
<button
|
{view === "study" && (
|
||||||
onClick={() => setRestartKey(k => k + 1)}
|
<button
|
||||||
className="p-1.5 rounded-lg text-text-muted hover:text-text-heading hover:bg-bg-surface-alt transition-all duration-200 cursor-pointer"
|
onClick={() => setRestartKey(k => k + 1)}
|
||||||
title="Restart Session"
|
className="p-1.5 rounded-lg text-text-muted hover:text-text-heading hover:bg-bg-surface-alt transition-all duration-200 cursor-pointer"
|
||||||
>
|
title="Restart Session"
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
>
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
</svg>
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
||||||
</button>
|
</svg>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{type === "flashcards" && (
|
||||||
|
<div className="flex bg-bg-surface-alt rounded-lg p-0.5 border border-border-light">
|
||||||
|
<button
|
||||||
|
onClick={() => setView("study")}
|
||||||
|
className={`px-4 py-2 rounded-md text-sm font-medium transition-all duration-200 cursor-pointer ${
|
||||||
|
view === "study"
|
||||||
|
? "bg-bg-surface text-text-heading shadow-sm"
|
||||||
|
: "text-text-muted hover:text-text-heading"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
Study
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setView("list")}
|
||||||
|
className={`px-4 py-2 rounded-md text-sm font-medium transition-all duration-200 cursor-pointer ${
|
||||||
|
view === "list"
|
||||||
|
? "bg-bg-surface text-text-heading shadow-sm"
|
||||||
|
: "text-text-muted hover:text-text-heading"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
List
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{type === "flashcards" ? (
|
{type === "flashcards" ? (
|
||||||
<FlashcardViewer
|
view === "study" ? (
|
||||||
key={restartKey}
|
<FlashcardViewer
|
||||||
cards={data.cards}
|
key={restartKey}
|
||||||
deckId={data.id}
|
cards={data.cards}
|
||||||
isShared={true}
|
deckId={data.id}
|
||||||
/>
|
isShared={true}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<CardList cards={data.cards} />
|
||||||
|
)
|
||||||
) : (
|
) : (
|
||||||
<QuizViewer
|
<QuizViewer
|
||||||
key={restartKey}
|
key={restartKey}
|
||||||
|
|
|
||||||
56
src/components/flashcards/CardList.tsx
Normal file
56
src/components/flashcards/CardList.tsx
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import ReactMarkdown from "react-markdown";
|
||||||
|
import remarkGfm from "remark-gfm";
|
||||||
|
|
||||||
|
interface Card {
|
||||||
|
id: string;
|
||||||
|
front: string;
|
||||||
|
back: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CardListProps {
|
||||||
|
cards: Card[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CardList({ cards }: CardListProps) {
|
||||||
|
return (
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="text-sm text-text-secondary mb-2">
|
||||||
|
{cards.length} {cards.length === 1 ? "card" : "cards"}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{cards.map((card, index) => (
|
||||||
|
<div
|
||||||
|
key={card.id}
|
||||||
|
className="bg-bg-surface rounded-xl border border-border-light shadow-[var(--shadow-card)] overflow-hidden"
|
||||||
|
>
|
||||||
|
<div className="p-4 md:p-6 space-y-4">
|
||||||
|
<div>
|
||||||
|
<div className="text-xs font-medium text-text-muted uppercase tracking-wider mb-2 flex items-center gap-2">
|
||||||
|
<span className="w-6 h-6 rounded-full bg-bg-surface-alt flex items-center justify-center text-text-secondary">
|
||||||
|
{index + 1}
|
||||||
|
</span>
|
||||||
|
Front
|
||||||
|
</div>
|
||||||
|
<div className="markdown-content text-sm text-text-body bg-bg-base/50 p-4 rounded-xl border border-border-light/50">
|
||||||
|
<ReactMarkdown remarkPlugins={[remarkGfm]}>{card.front}</ReactMarkdown>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="text-xs font-medium text-text-muted uppercase tracking-wider mb-2 flex items-center gap-2">
|
||||||
|
<span className="w-6 h-6 rounded-full bg-bg-surface-alt flex items-center justify-center text-text-secondary">
|
||||||
|
{index + 1}
|
||||||
|
</span>
|
||||||
|
Back
|
||||||
|
</div>
|
||||||
|
<div className="markdown-content text-sm text-text-body bg-bg-base/50 p-4 rounded-xl border border-border-light/50">
|
||||||
|
<ReactMarkdown remarkPlugins={[remarkGfm]}>{card.back}</ReactMarkdown>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue