improving ui
This commit is contained in:
@@ -82,7 +82,6 @@ export function FlashcardReviewComponent() {
|
||||
library.id,
|
||||
{
|
||||
fileName: library.fileName,
|
||||
subject: library.subject || 'Geral',
|
||||
difficultyLabel: difficultyLabel(library.difficulty),
|
||||
},
|
||||
]),
|
||||
|
||||
@@ -203,8 +203,7 @@
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.study-tag,
|
||||
.study-status-badge {
|
||||
.study-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
@@ -214,53 +213,13 @@
|
||||
border: 1px solid rgba(82, 54, 17, .12);
|
||||
}
|
||||
|
||||
.study-status-badge {
|
||||
font-size: 11px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.study-status-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 999px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(82, 54, 17, .10);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.study-status-badge.rag-red {
|
||||
color: var(--red-deep);
|
||||
background: rgba(183, 91, 77, 0.12);
|
||||
border-color: rgba(183, 91, 77, 0.22);
|
||||
}
|
||||
|
||||
.study-status-badge.rag-amber {
|
||||
color: #74531c;
|
||||
background: rgba(199, 149, 57, 0.14);
|
||||
border-color: rgba(199, 149, 57, 0.24);
|
||||
}
|
||||
|
||||
.study-status-badge.rag-green {
|
||||
color: var(--green-deep);
|
||||
background: rgba(79, 143, 90, 0.12);
|
||||
border-color: rgba(79, 143, 90, 0.22);
|
||||
}
|
||||
|
||||
.study-status-badge.rag-grey {
|
||||
color: var(--muted);
|
||||
background: rgba(123, 106, 80, 0.10);
|
||||
border-color: rgba(123, 106, 80, 0.18);
|
||||
}
|
||||
|
||||
.study-card-question,
|
||||
.study-card-answer {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: grid;
|
||||
align-content: center;
|
||||
gap: 16px;
|
||||
gap: 18px;
|
||||
min-height: 190px;
|
||||
color: var(--ink);
|
||||
font-family: "Segoe UI", Inter, Avenir, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
@@ -271,14 +230,14 @@
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.study-back-question,
|
||||
.study-back-answer {
|
||||
.study-back-answer,
|
||||
.study-back-question {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.study-back-question span,
|
||||
.study-back-answer span {
|
||||
.study-back-answer span,
|
||||
.study-back-question span {
|
||||
color: var(--blue-deep);
|
||||
font-size: 11px;
|
||||
font-weight: 950;
|
||||
@@ -286,30 +245,21 @@
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.study-back-question p,
|
||||
.study-back-answer p {
|
||||
.study-back-answer p,
|
||||
.study-back-question p {
|
||||
margin: 0;
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.study-back-question p {
|
||||
color: #66543d;
|
||||
font-size: clamp(15px, 1.7vw, 18px);
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.study-back-answer p {
|
||||
font-size: clamp(20px, 2.65vw, 24px);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.study-card-details {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
font-weight: 800;
|
||||
.study-back-question p {
|
||||
color: #66543d;
|
||||
font-size: clamp(15px, 1.7vw, 18px);
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.study-card-footer {
|
||||
@@ -324,19 +274,6 @@
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.study-spacebar {
|
||||
padding: 4px 9px;
|
||||
border-radius: 8px;
|
||||
color: #4f3a1d;
|
||||
background: rgba(255,255,255,.58);
|
||||
border: 1px solid rgba(82, 54, 17, .14);
|
||||
box-shadow: inset 0 -2px 0 rgba(82, 54, 17, .08);
|
||||
font-size: 11px;
|
||||
font-weight: 950;
|
||||
letter-spacing: .08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.study-controls {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
@@ -519,7 +456,7 @@
|
||||
|
||||
.study-queue-item {
|
||||
display: grid;
|
||||
grid-template-columns: 38px minmax(0, 1fr) auto;
|
||||
grid-template-columns: 38px minmax(0, 1fr);
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
@@ -536,11 +473,6 @@
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.study-queue-item span {
|
||||
color: var(--muted);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.study-queue-number {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
|
||||
@@ -5,13 +5,7 @@ import './FlashcardStudySession.css';
|
||||
|
||||
export interface FlashcardStudySessionLibraryMeta {
|
||||
fileName?: string;
|
||||
subject?: string;
|
||||
subSubject?: string;
|
||||
difficultyLabel?: string;
|
||||
statusLabel?: string;
|
||||
statusIcon?: string;
|
||||
statusClassName?: string;
|
||||
footerDetails?: string[];
|
||||
}
|
||||
|
||||
interface FlashcardStudySessionProps {
|
||||
@@ -105,13 +99,13 @@ function resetTimeout(timeoutRef: { current: number | null }) {
|
||||
}
|
||||
|
||||
function formatFooter(meta: FlashcardStudySessionLibraryMeta | undefined) {
|
||||
const main = [meta?.fileName, meta?.subSubject].filter(Boolean).join(' - ');
|
||||
return main || 'Arquivo';
|
||||
return meta?.fileName || 'Arquivo';
|
||||
}
|
||||
|
||||
export function FlashcardStudySession({ cards, libraryMetaById, onAnswer, onEnd }: FlashcardStudySessionProps) {
|
||||
const [currentIndex, setCurrentIndex] = useState(0);
|
||||
const [showAnswer, setShowAnswer] = useState(false);
|
||||
const [showBackQuestion, setShowBackQuestion] = useState(false);
|
||||
const [submittingAnswer, setSubmittingAnswer] = useState(false);
|
||||
const [cardExiting, setCardExiting] = useState(false);
|
||||
const [stampState, setStampState] = useState<'correct' | 'wrong' | null>(null);
|
||||
@@ -134,6 +128,7 @@ export function FlashcardStudySession({ cards, libraryMetaById, onAnswer, onEnd
|
||||
useEffect(() => {
|
||||
setCurrentIndex(0);
|
||||
setShowAnswer(false);
|
||||
setShowBackQuestion(false);
|
||||
setSubmittingAnswer(false);
|
||||
setCardExiting(false);
|
||||
setStampState(null);
|
||||
@@ -165,10 +160,10 @@ export function FlashcardStudySession({ cards, libraryMetaById, onAnswer, onEnd
|
||||
if (!showAnswer) {
|
||||
setShowAnswer(true);
|
||||
}
|
||||
} else if (e.code === 'KeyC' && showAnswer && !submittingAnswer) {
|
||||
} else if (e.code === 'KeyC' && showAnswer && !showBackQuestion && !submittingAnswer) {
|
||||
e.preventDefault();
|
||||
void registerReviewAnswer(true);
|
||||
} else if (e.code === 'KeyW' && showAnswer && !submittingAnswer) {
|
||||
} else if (e.code === 'KeyW' && showAnswer && !showBackQuestion && !submittingAnswer) {
|
||||
e.preventDefault();
|
||||
void registerReviewAnswer(false);
|
||||
}
|
||||
@@ -176,12 +171,13 @@ export function FlashcardStudySession({ cards, libraryMetaById, onAnswer, onEnd
|
||||
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
return () => window.removeEventListener('keydown', handleKeyDown);
|
||||
}, [cards.length, showAnswer, submittingAnswer, currentCard]);
|
||||
}, [cards.length, currentCard, showAnswer, showBackQuestion, submittingAnswer]);
|
||||
|
||||
const goToPrevious = () => {
|
||||
if (currentIndex === 0 || submittingAnswer) return;
|
||||
setCurrentIndex(currentIndex - 1);
|
||||
setShowAnswer(false);
|
||||
setShowBackQuestion(false);
|
||||
setFlipped(false);
|
||||
setStampState(null);
|
||||
setCardExiting(false);
|
||||
@@ -196,6 +192,7 @@ export function FlashcardStudySession({ cards, libraryMetaById, onAnswer, onEnd
|
||||
|
||||
setCurrentIndex(currentIndex + 1);
|
||||
setShowAnswer(false);
|
||||
setShowBackQuestion(false);
|
||||
setFlipped(false);
|
||||
setStampState(null);
|
||||
setCardExiting(false);
|
||||
@@ -204,7 +201,7 @@ export function FlashcardStudySession({ cards, libraryMetaById, onAnswer, onEnd
|
||||
};
|
||||
|
||||
const registerReviewAnswer = async (correct: boolean) => {
|
||||
if (!currentCard || !showAnswer || submittingAnswer) return;
|
||||
if (!currentCard || !showAnswer || showBackQuestion || submittingAnswer) return;
|
||||
|
||||
resetTimeout(stampTimerRef);
|
||||
resetTimeout(advanceTimerRef);
|
||||
@@ -277,22 +274,38 @@ export function FlashcardStudySession({ cards, libraryMetaById, onAnswer, onEnd
|
||||
<div class="study-stage">
|
||||
<div
|
||||
class={`study-flashcard${flipped ? ' is-flipped' : ''}${cardExiting ? ' is-reviewed' : ''}`}
|
||||
onClick={() => { if (!showAnswer && !submittingAnswer) setShowAnswer(true); }}
|
||||
onClick={() => {
|
||||
if (submittingAnswer) return;
|
||||
if (!showAnswer) {
|
||||
setShowAnswer(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!showBackQuestion) {
|
||||
setShowBackQuestion(true);
|
||||
}
|
||||
}}
|
||||
tabIndex={0}
|
||||
role="button"
|
||||
aria-label={showAnswer ? 'Flashcard revelado' : 'Clique ou pressione Espaço para revelar'}
|
||||
aria-label={
|
||||
!showAnswer
|
||||
? 'Clique ou pressione Espaço para revelar'
|
||||
: showBackQuestion
|
||||
? 'Flashcard com pergunta e resposta visíveis'
|
||||
: 'Clique para mostrar a pergunta junto da resposta'
|
||||
}
|
||||
>
|
||||
<div class="study-card-face">
|
||||
<div class="study-card-meta">
|
||||
<span class="study-tag">{currentMeta?.subject || 'Geral'}</span>
|
||||
<span>{currentMeta?.difficultyLabel || currentMeta?.statusLabel || 'Revisão'}</span>
|
||||
</div>
|
||||
{currentMeta?.difficultyLabel && (
|
||||
<div class="study-card-meta">
|
||||
<span class="study-tag">{currentMeta.difficultyLabel}</span>
|
||||
</div>
|
||||
)}
|
||||
<div class="study-card-question">
|
||||
{currentCard.front}
|
||||
</div>
|
||||
<div class="study-card-footer">
|
||||
<span>{formatFooter(currentMeta)}</span>
|
||||
<span class="study-spacebar">Espaço</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -304,35 +317,22 @@ export function FlashcardStudySession({ cards, libraryMetaById, onAnswer, onEnd
|
||||
)}
|
||||
<div class="study-card-meta">
|
||||
<span class="study-tag">Resposta</span>
|
||||
{currentMeta?.statusLabel ? (
|
||||
<span class={`study-status-badge ${currentMeta.statusClassName || ''}`}>
|
||||
{currentMeta.statusIcon && <span class="study-status-icon">{currentMeta.statusIcon}</span>}
|
||||
{currentMeta.statusLabel}
|
||||
</span>
|
||||
) : (
|
||||
<span>{currentMeta?.difficultyLabel || 'Revisão'}</span>
|
||||
)}
|
||||
{currentMeta?.difficultyLabel && <span>{currentMeta.difficultyLabel}</span>}
|
||||
</div>
|
||||
<div class="study-card-answer">
|
||||
<div class="study-back-question">
|
||||
<span>Pergunta</span>
|
||||
<p>{currentCard.front}</p>
|
||||
</div>
|
||||
<div class="study-back-answer">
|
||||
<span>Resposta</span>
|
||||
<p>{currentCard.back}</p>
|
||||
</div>
|
||||
{currentMeta?.footerDetails && currentMeta.footerDetails.length > 0 && (
|
||||
<div class="study-card-details">
|
||||
{currentMeta.footerDetails.map((detail) => (
|
||||
<span key={detail}>{detail}</span>
|
||||
))}
|
||||
{showBackQuestion && (
|
||||
<div class="study-back-question">
|
||||
<span>Pergunta</span>
|
||||
<p>{currentCard.front}</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div class="study-card-footer">
|
||||
<span>{formatFooter(currentMeta)}</span>
|
||||
<span class="study-spacebar">C / W</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -342,14 +342,14 @@ export function FlashcardStudySession({ cards, libraryMetaById, onAnswer, onEnd
|
||||
<button
|
||||
class="study-review-button correct"
|
||||
onClick={() => void registerReviewAnswer(true)}
|
||||
disabled={!showAnswer || submittingAnswer}
|
||||
disabled={!showAnswer || showBackQuestion || submittingAnswer}
|
||||
>
|
||||
Correto
|
||||
</button>
|
||||
<button
|
||||
class="study-review-button wrong"
|
||||
onClick={() => void registerReviewAnswer(false)}
|
||||
disabled={!showAnswer || submittingAnswer}
|
||||
disabled={!showAnswer || showBackQuestion || submittingAnswer}
|
||||
>
|
||||
Incorreto
|
||||
</button>
|
||||
@@ -386,16 +386,12 @@ export function FlashcardStudySession({ cards, libraryMetaById, onAnswer, onEnd
|
||||
<div class="study-panel-card">
|
||||
<h3>Fila</h3>
|
||||
<div class="study-queue">
|
||||
{cards.slice(currentIndex, currentIndex + 5).map((card, index) => {
|
||||
const meta = libraryMetaById.get(card.libraryId);
|
||||
return (
|
||||
<div key={card.id} class="study-queue-item">
|
||||
<span class="study-queue-number">{currentIndex + index + 1}</span>
|
||||
<strong>{card.front.substring(0, 40)}{card.front.length > 40 ? '...' : ''}</strong>
|
||||
<span>{meta?.subject || ''}</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
{cards.slice(currentIndex, currentIndex + 5).map((card, index) => (
|
||||
<div key={card.id} class="study-queue-item">
|
||||
<span class="study-queue-number">{currentIndex + index + 1}</span>
|
||||
<strong>{card.front.substring(0, 40)}{card.front.length > 40 ? '...' : ''}</strong>
|
||||
</div>
|
||||
))}
|
||||
{remainingCount > 5 && (
|
||||
<div class="study-queue-more">
|
||||
+{remainingCount - 5} restantes
|
||||
|
||||
@@ -226,24 +226,12 @@ export function SpacedReviewComponent() {
|
||||
|
||||
const sessionLibraryMetaById = useMemo(() => {
|
||||
return new Map<number, FlashcardStudySessionLibraryMeta>(
|
||||
sessionLibraries.map((library) => {
|
||||
const statusMeta = STATUS_META_BY_STATUS[library.ragStatus];
|
||||
return [
|
||||
library.libraryId,
|
||||
{
|
||||
fileName: library.fileName,
|
||||
subject: library.subject || 'Geral',
|
||||
subSubject: library.subSubject || 'Geral',
|
||||
statusLabel: statusMeta.label,
|
||||
statusIcon: statusMeta.icon,
|
||||
statusClassName: statusMeta.className,
|
||||
footerDetails: [
|
||||
`Desempenho do arquivo: ${formatPerformance(library.performanceRate)}`,
|
||||
`Última revisão: ${formatLastReviewed(library.lastReviewedAt)}`,
|
||||
],
|
||||
},
|
||||
];
|
||||
}),
|
||||
sessionLibraries.map((library) => [
|
||||
library.libraryId,
|
||||
{
|
||||
fileName: library.fileName,
|
||||
},
|
||||
]),
|
||||
);
|
||||
}, [sessionLibraries]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user