diff --git a/Mindforge.API/Models/Flashcards/FlashcardModels.cs b/Mindforge.API/Models/Flashcards/FlashcardModels.cs index 8d641f6..edef03d 100644 --- a/Mindforge.API/Models/Flashcards/FlashcardModels.cs +++ b/Mindforge.API/Models/Flashcards/FlashcardModels.cs @@ -54,7 +54,7 @@ namespace Mindforge.API.Models.Flashcards { public string SubSubject { get; set; } = string.Empty; public FlashcardRagSummary Summary { get; set; } = new(); - public List Cards { get; set; } = []; + public List Libraries { get; set; } = []; } public class FlashcardRagSummary @@ -68,17 +68,16 @@ namespace Mindforge.API.Models.Flashcards public double AttentionPercentage { get; set; } } - public class FlashcardRagCard + public class FlashcardRagLibrary { - public long CardId { get; set; } public long LibraryId { get; set; } + public string FilePath { get; set; } = string.Empty; public string FileName { get; set; } = string.Empty; public string Subject { get; set; } = string.Empty; public string SubSubject { get; set; } = string.Empty; - public string Front { get; set; } = string.Empty; - public string Back { get; set; } = string.Empty; public int CorrectCount { get; set; } public int IncorrectCount { get; set; } + public int CardCount { get; set; } public int TotalAnswers { get; set; } public double PerformanceRate { get; set; } public DateTime? LastReviewedAt { get; set; } diff --git a/Mindforge.API/Services/FlashcardService.cs b/Mindforge.API/Services/FlashcardService.cs index e3834cb..e6d040a 100644 --- a/Mindforge.API/Services/FlashcardService.cs +++ b/Mindforge.API/Services/FlashcardService.cs @@ -103,26 +103,31 @@ namespace Mindforge.API.Services var now = DateTime.UtcNow; var cards = await _flashcardRepository.GetAllCardsWithLibraryAsync(); - var ragCards = cards - .Select(card => BuildRagCard(card, now)) + var ragLibraries = cards + .GroupBy(card => card.LibraryId) + .Select(group => BuildRagLibrary(group.ToList(), now)) .ToList(); - var subjectGroups = ragCards - .GroupBy(card => card.Subject, StringComparer.OrdinalIgnoreCase) + var subjectGroups = ragLibraries + .GroupBy(library => library.Subject, StringComparer.OrdinalIgnoreCase) .OrderBy(group => group.Key, StringComparer.OrdinalIgnoreCase) .Select(subjectGroup => { var subSubjectGroups = subjectGroup - .GroupBy(card => card.SubSubject, StringComparer.OrdinalIgnoreCase) + .GroupBy(library => library.SubSubject, StringComparer.OrdinalIgnoreCase) .OrderBy(group => group.Key, StringComparer.OrdinalIgnoreCase) .Select(subSubjectGroup => { - var subSubjectCards = subSubjectGroup.ToList(); + var subSubjectLibraries = subSubjectGroup + .OrderBy(library => StatusSortOrder(library.RagStatus)) + .ThenBy(library => library.FileName, StringComparer.OrdinalIgnoreCase) + .ToList(); + return new FlashcardRagSubSubjectGroup { SubSubject = subSubjectGroup.Key, - Summary = BuildSummary(subSubjectCards), - Cards = subSubjectCards + Summary = BuildSummary(subSubjectLibraries), + Libraries = subSubjectLibraries }; }) .ToList(); @@ -312,32 +317,37 @@ namespace Mindforge.API.Services return string.Join(" / ", segments[subSubjectStart..subSubjectEnd]); } - private static FlashcardRagCard BuildRagCard(FlashcardCardWithLibrary card, DateTime referenceTime) + private static FlashcardRagLibrary BuildRagLibrary( + IReadOnlyList cards, + DateTime referenceTime) { - var subject = string.IsNullOrWhiteSpace(card.Subject) - ? ExtractSubject(card.FilePath) - : card.Subject; - var subSubject = ExtractSubSubject(card.FilePath); - var totalAnswers = card.CorrectCount + card.IncorrectCount; + var firstCard = cards[0]; + var subject = string.IsNullOrWhiteSpace(firstCard.Subject) + ? ExtractSubject(firstCard.FilePath) + : firstCard.Subject; + var subSubject = ExtractSubSubject(firstCard.FilePath); + var correctCount = cards.Sum(card => card.CorrectCount); + var incorrectCount = cards.Sum(card => card.IncorrectCount); + var totalAnswers = correctCount + incorrectCount; + var lastReviewedAt = cards.Max(card => card.LastReviewedAt); var performanceRate = totalAnswers == 0 ? 0 - : (double)card.CorrectCount / totalAnswers; + : (double)correctCount / totalAnswers; - return new FlashcardRagCard + return new FlashcardRagLibrary { - CardId = card.Id, - LibraryId = card.LibraryId, - FileName = card.FileName, + LibraryId = firstCard.LibraryId, + FilePath = firstCard.FilePath, + FileName = firstCard.FileName, Subject = subject, SubSubject = subSubject, - Front = card.Front, - Back = card.Back, - CorrectCount = card.CorrectCount, - IncorrectCount = card.IncorrectCount, + CorrectCount = correctCount, + IncorrectCount = incorrectCount, + CardCount = cards.Count, TotalAnswers = totalAnswers, PerformanceRate = performanceRate, - LastReviewedAt = card.LastReviewedAt, - RagStatus = DetermineRagStatus(card.LastReviewedAt, performanceRate, referenceTime) + LastReviewedAt = lastReviewedAt, + RagStatus = DetermineRagStatus(lastReviewedAt, performanceRate, referenceTime) }; } @@ -369,13 +379,13 @@ namespace Mindforge.API.Services return "Amber"; } - private static FlashcardRagSummary BuildSummary(IEnumerable cards) + private static FlashcardRagSummary BuildSummary(IEnumerable libraries) { - var cardList = cards.ToList(); - var greenCount = cardList.Count(card => card.RagStatus == "Green"); - var amberCount = cardList.Count(card => card.RagStatus == "Amber"); - var redCount = cardList.Count(card => card.RagStatus == "Red"); - var greyCount = cardList.Count(card => card.RagStatus == "Grey"); + var libraryList = libraries.ToList(); + var greenCount = libraryList.Count(library => library.RagStatus == "Green"); + var amberCount = libraryList.Count(library => library.RagStatus == "Amber"); + var redCount = libraryList.Count(library => library.RagStatus == "Red"); + var greyCount = libraryList.Count(library => library.RagStatus == "Grey"); var activeCount = greenCount + amberCount + redCount; var greenPercentage = activeCount == 0 @@ -397,6 +407,17 @@ namespace Mindforge.API.Services }; } + private static int StatusSortOrder(string status) + { + return status switch + { + "Red" => 0, + "Amber" => 1, + "Green" => 2, + _ => 3 + }; + } + private class FlashcardJsonPayload { public List Flashcards { get; set; } = []; diff --git a/Mindforge.Web/src/components/SpacedReviewComponent.css b/Mindforge.Web/src/components/SpacedReviewComponent.css index 49fefa6..0bf325f 100644 --- a/Mindforge.Web/src/components/SpacedReviewComponent.css +++ b/Mindforge.Web/src/components/SpacedReviewComponent.css @@ -1,6 +1,6 @@ .spaced-review-container { width: 100%; - max-width: 980px; + max-width: 1020px; margin: 0 auto; display: flex; flex-direction: column; @@ -48,16 +48,16 @@ .spaced-review-filter { display: flex; align-items: center; - gap: 0.4rem; + gap: 0.45rem; border: 1px solid rgba(255, 255, 255, 0.14); border-radius: 999px; - padding: 0.35rem 0.7rem; + padding: 0.35rem 0.8rem; background: rgba(255, 255, 255, 0.05); font-size: 0.88rem; } .spaced-review-filter input[type="checkbox"], -.spaced-review-subsubject-item input[type="checkbox"] { +.spaced-review-library-item input[type="checkbox"] { width: 15px; height: 15px; accent-color: var(--color-accent); @@ -77,7 +77,7 @@ .spaced-review-subject-header h3 { margin: 0; - font-size: 1.03rem; + font-size: 1.05rem; } .spaced-review-subject-header p { @@ -95,39 +95,171 @@ .spaced-review-subsubject-list { display: grid; - gap: 0.6rem; - margin-top: 0.85rem; + gap: 0.8rem; + margin-top: 0.95rem; } -.spaced-review-subsubject-item { - display: flex; - align-items: flex-start; - gap: 0.7rem; +.spaced-review-subsubject-block { border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 10px; + padding: 0.7rem; + background: rgba(255, 255, 255, 0.03); +} + +.spaced-review-subsubject-header { + display: flex; + flex-direction: column; + gap: 0.15rem; + margin-bottom: 0.6rem; +} + +.spaced-review-subsubject-header strong { + font-size: 0.93rem; +} + +.spaced-review-subsubject-header span { + font-size: 0.82rem; + color: rgba(255, 255, 255, 0.78); +} + +.spaced-review-subsubject-header small { + font-size: 0.78rem; + color: rgba(255, 255, 255, 0.6); +} + +.spaced-review-library-list { + display: grid; + gap: 0.55rem; +} + +.spaced-review-library-item { + display: flex; + align-items: center; + gap: 0.7rem; + border: 1px solid rgba(255, 255, 255, 0.12); + border-left-width: 4px; + border-radius: 10px; padding: 0.6rem 0.7rem; background: rgba(255, 255, 255, 0.03); cursor: pointer; } -.spaced-review-subsubject-texts { +.spaced-review-library-item.selected { + box-shadow: 0 0 0 1px rgba(var(--color-accent-rgb), 0.45); +} + +.spaced-review-library-texts { + flex: 1; display: flex; flex-direction: column; - gap: 0.15rem; + gap: 0.12rem; + min-width: 0; } -.spaced-review-subsubject-texts strong { - font-size: 0.93rem; +.spaced-review-library-texts strong { + font-size: 0.91rem; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } -.spaced-review-subsubject-texts span { - font-size: 0.82rem; +.spaced-review-library-texts span { + font-size: 0.8rem; color: rgba(255, 255, 255, 0.78); } -.spaced-review-subsubject-texts small { - font-size: 0.78rem; - color: rgba(255, 255, 255, 0.6); +.spaced-review-library-texts small { + font-size: 0.77rem; + color: rgba(255, 255, 255, 0.62); +} + +.rag-badge, +.rag-badge-inline { + display: inline-flex; + align-items: center; + gap: 0.35rem; + border-radius: 999px; + border: 1px solid rgba(255, 255, 255, 0.18); + padding: 0.22rem 0.55rem; + font-size: 0.76rem; + font-weight: 700; + line-height: 1; +} + +.rag-icon { + width: 16px; + height: 16px; + border-radius: 999px; + display: inline-flex; + align-items: center; + justify-content: center; + font-size: 0.74rem; + font-weight: 700; + background: rgba(255, 255, 255, 0.18); +} + +.rag-red { + border-left-color: #ff5d5d; +} + +.rag-red .rag-badge, +.rag-badge.rag-red, +.spaced-review-filter.rag-red { + background: rgba(255, 93, 93, 0.15); +} + +.rag-red .rag-icon, +.rag-badge.rag-red .rag-icon, +.rag-badge-inline.rag-red .rag-icon { + background: rgba(255, 93, 93, 0.35); +} + +.rag-amber { + border-left-color: #ffbe55; +} + +.rag-amber .rag-badge, +.rag-badge.rag-amber, +.spaced-review-filter.rag-amber { + background: rgba(255, 190, 85, 0.16); +} + +.rag-amber .rag-icon, +.rag-badge.rag-amber .rag-icon, +.rag-badge-inline.rag-amber .rag-icon { + background: rgba(255, 190, 85, 0.36); +} + +.rag-green { + border-left-color: #46d18a; +} + +.rag-green .rag-badge, +.rag-badge.rag-green, +.spaced-review-filter.rag-green { + background: rgba(70, 209, 138, 0.16); +} + +.rag-green .rag-icon, +.rag-badge.rag-green .rag-icon, +.rag-badge-inline.rag-green .rag-icon { + background: rgba(70, 209, 138, 0.35); +} + +.rag-grey { + border-left-color: #9aa6b5; +} + +.rag-grey .rag-badge, +.rag-badge.rag-grey, +.spaced-review-filter.rag-grey { + background: rgba(154, 166, 181, 0.16); +} + +.rag-grey .rag-icon, +.rag-badge.rag-grey .rag-icon, +.rag-badge-inline.rag-grey .rag-icon { + background: rgba(154, 166, 181, 0.35); } .spaced-review-footer { @@ -182,6 +314,11 @@ .spaced-review-card header { margin-bottom: 0.8rem; + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.8rem; + flex-wrap: wrap; } .spaced-review-card small { @@ -216,6 +353,10 @@ } @media (max-width: 760px) { + .spaced-review-library-item { + flex-wrap: wrap; + } + .spaced-review-session-actions { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); diff --git a/Mindforge.Web/src/components/SpacedReviewComponent.tsx b/Mindforge.Web/src/components/SpacedReviewComponent.tsx index c4387eb..e8e81b1 100644 --- a/Mindforge.Web/src/components/SpacedReviewComponent.tsx +++ b/Mindforge.Web/src/components/SpacedReviewComponent.tsx @@ -2,8 +2,8 @@ import { useEffect, useMemo, useState } from 'preact/hooks'; import { MindforgeApiService, type FlashcardCard, - type FlashcardRagCard, type FlashcardRagDashboardResponse, + type FlashcardRagLibrary, type FlashcardRagStatus, } from '../services/MindforgeApiService'; import { Button } from './Button'; @@ -12,15 +12,29 @@ import './SpacedReviewComponent.css'; interface RagStatusOption { status: FlashcardRagStatus; label: string; + icon: string; +} + +interface RagStatusMeta { + label: string; + icon: string; + className: string; } const STATUS_OPTIONS: RagStatusOption[] = [ - { status: 'Red', label: 'Vermelho' }, - { status: 'Amber', label: 'Amarelo' }, - { status: 'Green', label: 'Verde' }, - { status: 'Grey', label: 'Cinza' }, + { status: 'Red', label: 'Vermelho', icon: '!' }, + { status: 'Amber', label: 'Amarelo', icon: '*' }, + { status: 'Green', label: 'Verde', icon: 'v' }, + { status: 'Grey', label: 'Cinza', icon: '-' }, ]; +const STATUS_META_BY_STATUS: Record = { + Red: { label: 'Vermelho', icon: '!', className: 'rag-red' }, + Amber: { label: 'Amarelo', icon: '*', className: 'rag-amber' }, + Green: { label: 'Verde', icon: 'v', className: 'rag-green' }, + Grey: { label: 'Cinza', icon: '-', className: 'rag-grey' }, +}; + const STATUS_PRIORITY: Record = { Red: 0, Amber: 1, @@ -28,16 +42,12 @@ const STATUS_PRIORITY: Record = { Grey: 3, }; -function buildGroupKey(subject: string, subSubject: string) { - return `${subject}::${subSubject}`; -} - function shuffleCards(cards: FlashcardCard[]) { const shuffled = [...cards]; - for (let i = shuffled.length - 1; i > 0; i--) { - const randomIndex = Math.floor(Math.random() * (i + 1)); - [shuffled[i], shuffled[randomIndex]] = [shuffled[randomIndex], shuffled[i]]; + for (let index = shuffled.length - 1; index > 0; index--) { + const randomIndex = Math.floor(Math.random() * (index + 1)); + [shuffled[index], shuffled[randomIndex]] = [shuffled[randomIndex], shuffled[index]]; } return shuffled; @@ -59,12 +69,25 @@ function formatPerformance(rate: number) { return `${Math.round(rate * 100)}%`; } -function orderCardsForSession(cards: FlashcardCard[], ragByCardId: Map) { +function formatLastReviewed(value?: string | null) { + if (!value) { + return 'Nunca revisado'; + } + + const date = new Date(value); + if (Number.isNaN(date.getTime())) { + return 'Data invalida'; + } + + return date.toLocaleDateString('pt-BR'); +} + +function orderCardsForSession(cards: FlashcardCard[], ragByLibraryId: Map) { const buckets: FlashcardCard[][] = [[], [], [], []]; cards.forEach((card) => { - const ragCard = ragByCardId.get(card.id); - const status = ragCard?.ragStatus || 'Grey'; + const ragLibrary = ragByLibraryId.get(card.libraryId); + const status = ragLibrary?.ragStatus || 'Grey'; buckets[STATUS_PRIORITY[status]].push(card); }); @@ -76,10 +99,10 @@ export function SpacedReviewComponent() { const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [selectedStatuses, setSelectedStatuses] = useState(['Red', 'Amber']); - const [selectedGroupKeys, setSelectedGroupKeys] = useState([]); + const [selectedLibraryIds, setSelectedLibraryIds] = useState([]); const [startingSession, setStartingSession] = useState(false); const [sessionCards, setSessionCards] = useState([]); - const [sessionSourceCards, setSessionSourceCards] = useState([]); + const [sessionLibraries, setSessionLibraries] = useState([]); const [currentIndex, setCurrentIndex] = useState(0); const [showAnswer, setShowAnswer] = useState(false); const [submittingAnswer, setSubmittingAnswer] = useState(false); @@ -92,19 +115,19 @@ export function SpacedReviewComponent() { const response = await MindforgeApiService.getFlashcardRagStatus(); setDashboard(response); - const allGroupKeys = response.subjects.flatMap((subjectGroup) => - subjectGroup.subSubjects.map((subSubjectGroup) => - buildGroupKey(subjectGroup.subject, subSubjectGroup.subSubject)), + const allLibraryIds = response.subjects.flatMap((subjectGroup) => + subjectGroup.subSubjects.flatMap((subSubjectGroup) => + subSubjectGroup.libraries.map((library) => library.libraryId)), ); - setSelectedGroupKeys((current) => { + setSelectedLibraryIds((current) => { if (!preserveSelection || current.length === 0) { - return allGroupKeys; + return allLibraryIds; } - const available = new Set(allGroupKeys); - const kept = current.filter((key) => available.has(key)); - return kept.length > 0 ? kept : allGroupKeys; + const available = new Set(allLibraryIds); + const kept = current.filter((libraryId) => available.has(libraryId)); + return kept.length > 0 ? kept : allLibraryIds; }); } catch (err: any) { setError(err?.message || 'Falha ao carregar status de revisao espacada.'); @@ -120,6 +143,7 @@ export function SpacedReviewComponent() { if (cancelled) { return; } + await loadDashboard(false); } @@ -129,30 +153,32 @@ export function SpacedReviewComponent() { }; }, []); - const ragCards = useMemo(() => { + const allRagLibraries = useMemo(() => { if (!dashboard) { return []; } return dashboard.subjects.flatMap((subjectGroup) => - subjectGroup.subSubjects.flatMap((subSubjectGroup) => subSubjectGroup.cards)); + subjectGroup.subSubjects.flatMap((subSubjectGroup) => subSubjectGroup.libraries)); }, [dashboard]); - const selectedRagCards = useMemo(() => { - const selectedGroups = new Set(selectedGroupKeys); - const selectedStatusSet = new Set(selectedStatuses); + const selectedRagLibraries = useMemo(() => { + const statusSet = new Set(selectedStatuses); + const libraryIdSet = new Set(selectedLibraryIds); - return ragCards.filter((card) => - selectedGroups.has(buildGroupKey(card.subject, card.subSubject)) - && selectedStatusSet.has(card.ragStatus)); - }, [ragCards, selectedGroupKeys, selectedStatuses]); + return allRagLibraries.filter((library) => + libraryIdSet.has(library.libraryId) && statusSet.has(library.ragStatus)); + }, [allRagLibraries, selectedLibraryIds, selectedStatuses]); - const sessionSourceById = useMemo(() => { - return new Map(sessionSourceCards.map((card) => [card.cardId, card])); - }, [sessionSourceCards]); + const sessionLibraryById = useMemo(() => { + return new Map(sessionLibraries.map((library) => [library.libraryId, library])); + }, [sessionLibraries]); const currentCard = sessionCards[currentIndex]; - const currentCardMetadata = currentCard ? sessionSourceById.get(currentCard.id) : undefined; + const currentLibrary = currentCard ? sessionLibraryById.get(currentCard.libraryId) : undefined; + const currentStatusMeta = currentLibrary + ? STATUS_META_BY_STATUS[currentLibrary.ragStatus] + : STATUS_META_BY_STATUS.Grey; const progressPercent = sessionCards.length > 0 ? ((currentIndex + 1) / sessionCards.length) * 100 @@ -167,13 +193,13 @@ export function SpacedReviewComponent() { setSelectedStatuses([...selectedStatuses, status]); }; - const toggleGroup = (groupKey: string) => { - if (selectedGroupKeys.includes(groupKey)) { - setSelectedGroupKeys(selectedGroupKeys.filter((key) => key !== groupKey)); + const toggleLibrary = (libraryId: number) => { + if (selectedLibraryIds.includes(libraryId)) { + setSelectedLibraryIds(selectedLibraryIds.filter((id) => id !== libraryId)); return; } - setSelectedGroupKeys([...selectedGroupKeys, groupKey]); + setSelectedLibraryIds([...selectedLibraryIds, libraryId]); }; const startSession = async () => { @@ -182,13 +208,13 @@ export function SpacedReviewComponent() { return; } - if (selectedGroupKeys.length === 0) { - setError('Selecione ao menos uma submateria para iniciar a revisao.'); + if (selectedLibraryIds.length === 0) { + setError('Selecione ao menos um arquivo para iniciar a revisao.'); return; } - if (selectedRagCards.length === 0) { - setError('Nenhum card encontrado com os filtros selecionados.'); + if (selectedRagLibraries.length === 0) { + setError('Nenhum arquivo encontrado com os filtros selecionados.'); return; } @@ -196,19 +222,19 @@ export function SpacedReviewComponent() { setError(null); try { - const ragByCardId = new Map(selectedRagCards.map((card) => [card.cardId, card])); - const libraryIds = Array.from(new Set(selectedRagCards.map((card) => card.libraryId))); + const ragByLibraryId = new Map(selectedRagLibraries.map((library) => [library.libraryId, library])); + const libraryIds = Array.from(new Set(selectedRagLibraries.map((library) => library.libraryId))); const response = await MindforgeApiService.createFlashcardReviewSession({ libraryIds }); - const selectedCardIds = new Set(selectedRagCards.map((card) => card.cardId)); - const filteredCards = response.cards.filter((card) => selectedCardIds.has(card.id)); - const orderedCards = orderCardsForSession(filteredCards, ragByCardId); + const allowedLibraryIds = new Set(libraryIds); + const filteredCards = response.cards.filter((card) => allowedLibraryIds.has(card.libraryId)); + const orderedCards = orderCardsForSession(filteredCards, ragByLibraryId); if (orderedCards.length === 0) { setError('Os filtros selecionados nao retornaram cards para revisar.'); return; } - setSessionSourceCards(selectedRagCards); + setSessionLibraries(selectedRagLibraries); setSessionCards(orderedCards); setCurrentIndex(0); setShowAnswer(false); @@ -221,7 +247,7 @@ export function SpacedReviewComponent() { const endSession = () => { setSessionCards([]); - setSessionSourceCards([]); + setSessionLibraries([]); setCurrentIndex(0); setShowAnswer(false); setSubmittingAnswer(false); @@ -232,6 +258,7 @@ export function SpacedReviewComponent() { if (currentIndex === 0) { return; } + setCurrentIndex(currentIndex - 1); setShowAnswer(false); }; @@ -267,7 +294,7 @@ export function SpacedReviewComponent() { return (

Revisao espacada

-

Acompanhe o status RAG dos cards por materia e submateria.

+

Acompanhe o status RAG por arquivo de flashcards.

{error &&
{error}
} @@ -282,13 +309,16 @@ export function SpacedReviewComponent() { <>
{STATUS_OPTIONS.map((option) => ( -
@@ -311,31 +341,52 @@ export function SpacedReviewComponent() {
- {subjectGroup.subSubjects.map((subSubjectGroup) => { - const groupKey = buildGroupKey(subjectGroup.subject, subSubjectGroup.subSubject); - const checked = selectedGroupKeys.includes(groupKey); + {subjectGroup.subSubjects.map((subSubjectGroup) => ( +
+
+ {subSubjectGroup.subSubject} + {summaryText( + subSubjectGroup.summary.activeCount, + subSubjectGroup.summary.greenPercentage, + subSubjectGroup.summary.attentionPercentage, + )} + + Arquivos: {subSubjectGroup.libraries.length} | Cinza: {subSubjectGroup.summary.greyCount} + +
- return ( - - ); - })} +
+ {subSubjectGroup.libraries.map((library) => { + const statusMeta = STATUS_META_BY_STATUS[library.ragStatus]; + const selected = selectedLibraryIds.includes(library.libraryId); + + return ( + + ); + })} +
+
+ ))}
))} @@ -345,11 +396,11 @@ export function SpacedReviewComponent() {

- Cards selecionados: {selectedRagCards.length} + Arquivos selecionados: {selectedRagLibraries.length}