From b29ae991c86fe21cf312c1ee621b0fdc3ae95c58 Mon Sep 17 00:00:00 2001 From: Jose Henrique Date: Thu, 11 Jun 2026 19:34:52 -0300 Subject: [PATCH] fixing stuff --- .../Requests/FlashcardGenerateRequest.cs | 2 +- Mindforge.API/Services/FlashcardService.cs | 6 +- Mindforge.Web/src/components/Button.css | 7 + .../src/components/FlashcardComponent.tsx | 16 +-- .../components/FlashcardReviewComponent.tsx | 2 +- .../src/components/SpacedReviewComponent.css | 15 +++ .../src/components/SpacedReviewComponent.tsx | 121 +++++++++++++++++- .../src/services/MindforgeApiService.ts | 2 +- 8 files changed, 155 insertions(+), 16 deletions(-) diff --git a/Mindforge.API/Models/Requests/FlashcardGenerateRequest.cs b/Mindforge.API/Models/Requests/FlashcardGenerateRequest.cs index b08d18b..41449eb 100644 --- a/Mindforge.API/Models/Requests/FlashcardGenerateRequest.cs +++ b/Mindforge.API/Models/Requests/FlashcardGenerateRequest.cs @@ -13,6 +13,6 @@ namespace Mindforge.API.Models.Requests public enum FlashcardDifficulty { Easy, - Hard + Medium } } diff --git a/Mindforge.API/Services/FlashcardService.cs b/Mindforge.API/Services/FlashcardService.cs index e6d040a..bde6744 100644 --- a/Mindforge.API/Services/FlashcardService.cs +++ b/Mindforge.API/Services/FlashcardService.cs @@ -36,9 +36,9 @@ namespace Mindforge.API.Services throw new UserException("Selecione ao menos um arquivo para gerar flashcards."); } - if (request.Amount is < 10 or > 30) + if (request.Amount is < 10 or > 50) { - throw new UserException("A quantidade de flashcards deve estar entre 10 e 30 por arquivo."); + throw new UserException("A quantidade de flashcards deve estar entre 10 e 50 por arquivo."); } var uniqueFilePaths = request.FilePaths @@ -166,7 +166,7 @@ namespace Mindforge.API.Services { var difficultyInstruction = difficulty switch { - FlashcardDifficulty.Hard => "Crie perguntas mais desafiadoras, focando diferencas finas e cobrancas de prova.", + FlashcardDifficulty.Medium => "Crie perguntas de nivel intermediario, combinando conceitos e exigindo raciocĂ­nio moderado.", _ => "Crie perguntas diretas e objetivas para facilitar memorizacao inicial." }; diff --git a/Mindforge.Web/src/components/Button.css b/Mindforge.Web/src/components/Button.css index 5e93a4d..64f5d55 100644 --- a/Mindforge.Web/src/components/Button.css +++ b/Mindforge.Web/src/components/Button.css @@ -30,6 +30,13 @@ transform: translateY(0); } +.btn:disabled { + opacity: 0.45; + cursor: not-allowed; + transform: none; + box-shadow: none; +} + .btn-secondary { background: transparent; color: var(--color-text-creamy); diff --git a/Mindforge.Web/src/components/FlashcardComponent.tsx b/Mindforge.Web/src/components/FlashcardComponent.tsx index 58916e1..d487135 100644 --- a/Mindforge.Web/src/components/FlashcardComponent.tsx +++ b/Mindforge.Web/src/components/FlashcardComponent.tsx @@ -9,10 +9,10 @@ import { Button } from './Button'; import './FlashcardComponent.css'; const minAmount = 10; -const maxAmount = 30; +const maxAmount = 50; function difficultyLabel(difficulty: string) { - return difficulty === 'Hard' ? 'Dificil' : 'Facil'; + return difficulty === 'Medium' ? 'Medio' : 'Facil'; } export function FlashcardComponent() { @@ -98,14 +98,14 @@ export function FlashcardComponent() {
setDifficulty('Hard')} + value="Medium" + checked={difficulty === 'Medium'} + onChange={() => setDifficulty('Medium')} /> -
diff --git a/Mindforge.Web/src/components/FlashcardReviewComponent.tsx b/Mindforge.Web/src/components/FlashcardReviewComponent.tsx index df6a49d..6bc9c60 100644 --- a/Mindforge.Web/src/components/FlashcardReviewComponent.tsx +++ b/Mindforge.Web/src/components/FlashcardReviewComponent.tsx @@ -21,7 +21,7 @@ function groupLibrariesBySubject(libraries: FlashcardLibrarySummary[]) { } function difficultyLabel(difficulty: string) { - return difficulty === 'Hard' ? 'Dificil' : 'Facil'; + return difficulty === 'Medium' ? 'Medio' : 'Facil'; } function shuffleCards(cards: FlashcardCard[]) { diff --git a/Mindforge.Web/src/components/SpacedReviewComponent.css b/Mindforge.Web/src/components/SpacedReviewComponent.css index 0bf325f..d4b7e20 100644 --- a/Mindforge.Web/src/components/SpacedReviewComponent.css +++ b/Mindforge.Web/src/components/SpacedReviewComponent.css @@ -80,6 +80,21 @@ font-size: 1.05rem; } +.spaced-review-subject-toggle, +.spaced-review-subsubject-toggle { + display: flex; + align-items: center; + gap: 0.5rem; + cursor: pointer; +} + +.spaced-review-subject-toggle input[type="checkbox"], +.spaced-review-subsubject-toggle input[type="checkbox"] { + width: 16px; + height: 16px; + accent-color: var(--color-accent); +} + .spaced-review-subject-header p { margin: 0.35rem 0 0; font-size: 0.9rem; diff --git a/Mindforge.Web/src/components/SpacedReviewComponent.tsx b/Mindforge.Web/src/components/SpacedReviewComponent.tsx index 227c90e..3edeb60 100644 --- a/Mindforge.Web/src/components/SpacedReviewComponent.tsx +++ b/Mindforge.Web/src/components/SpacedReviewComponent.tsx @@ -100,6 +100,8 @@ export function SpacedReviewComponent() { const [error, setError] = useState(null); const [selectedStatuses, setSelectedStatuses] = useState(['Red', 'Amber', 'Green', 'Grey']); const [selectedLibraryIds, setSelectedLibraryIds] = useState([]); + const [selectedSubjects, setSelectedSubjects] = useState([]); + const [selectedSubSubjects, setSelectedSubSubjects] = useState([]); const [startingSession, setStartingSession] = useState(false); const [sessionCards, setSessionCards] = useState([]); const [sessionLibraries, setSessionLibraries] = useState([]); @@ -161,6 +163,49 @@ export function SpacedReviewComponent() { }; }, []); + const startSession = async () => { + if (selectedStatuses.length === 0) { + setError('Selecione ao menos um status para iniciar a revisao.'); + return; + } + + if (selectedLibraryIds.length === 0) { + setError('Selecione ao menos um arquivo para iniciar a revisao.'); + return; + } + + if (selectedRagLibraries.length === 0) { + setError('Nenhum arquivo encontrado com os filtros atuais. Ajuste os status ou os arquivos selecionados.'); + return; + } + + setStartingSession(true); + setError(null); + + try { + 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 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; + } + + setSessionLibraries(selectedRagLibraries); + setSessionCards(orderedCards); + setCurrentIndex(0); + setShowAnswer(false); + } catch (err: any) { + setError(err?.message || 'Falha ao iniciar revisao espacada.'); + } finally { + setStartingSession(false); + } + }; + const allRagLibraries = useMemo(() => { if (!dashboard) { return []; @@ -210,6 +255,64 @@ export function SpacedReviewComponent() { setSelectedLibraryIds([...selectedLibraryIds, libraryId]); }; + const toggleSubject = (subject: string) => { + if (selectedSubjects.includes(subject)) { + setSelectedSubjects(selectedSubjects.filter((s) => s !== subject)); + const subjectGroup = dashboard?.subjects.find((s) => s.subject === subject); + if (subjectGroup) { + const libraryIdsToRemove = subjectGroup.subSubjects.flatMap((ss) => + ss.libraries.map((lib) => lib.libraryId)); + setSelectedLibraryIds((current) => current.filter((id) => !libraryIdsToRemove.includes(id))); + const subSubjectKeysToRemove = subjectGroup.subSubjects.map((ss) => `${subject}::${ss.subSubject}`); + setSelectedSubSubjects((current) => current.filter((key) => !subSubjectKeysToRemove.includes(key))); + } + return; + } + + setSelectedSubjects([...selectedSubjects, subject]); + const subjectGroup = dashboard?.subjects.find((s) => s.subject === subject); + if (subjectGroup) { + const libraryIdsToAdd = subjectGroup.subSubjects.flatMap((ss) => + ss.libraries.map((lib) => lib.libraryId)); + setSelectedLibraryIds((current) => [...new Set([...current, ...libraryIdsToAdd])]); + const subSubjectKeysToAdd = subjectGroup.subSubjects.map((ss) => `${subject}::${ss.subSubject}`); + setSelectedSubSubjects((current) => [...new Set([...current, ...subSubjectKeysToAdd])]); + } + }; + + const toggleSubSubject = (subject: string, subSubject: string) => { + const key = `${subject}::${subSubject}`; + if (selectedSubSubjects.includes(key)) { + setSelectedSubSubjects(selectedSubSubjects.filter((k) => k !== key)); + const subjectGroup = dashboard?.subjects.find((s) => s.subject === subject); + const subSubjectGroup = subjectGroup?.subSubjects.find((ss) => ss.subSubject === subSubject); + if (subSubjectGroup) { + const libraryIdsToRemove = subSubjectGroup.libraries.map((lib) => lib.libraryId); + setSelectedLibraryIds((current) => current.filter((id) => !libraryIdsToRemove.includes(id))); + } + const parentSubjectStillSelected = selectedSubSubjects.some((k) => + k.startsWith(`${subject}::`) && k !== key); + if (!parentSubjectStillSelected) { + setSelectedSubjects((current) => current.filter((s) => s !== subject)); + } + return; + } + + setSelectedSubSubjects([...selectedSubSubjects, key]); + const subjectGroup = dashboard?.subjects.find((s) => s.subject === subject); + const subSubjectGroup = subjectGroup?.subSubjects.find((ss) => ss.subSubject === subSubject); + if (subSubjectGroup) { + const libraryIdsToAdd = subSubjectGroup.libraries.map((lib) => lib.libraryId); + setSelectedLibraryIds((current) => [...new Set([...current, ...libraryIdsToAdd])]); + } + const allSubSubjects = subjectGroup?.subSubjects.map((ss) => `${subject}::${ss.subSubject}`) || []; + const allSelected = allSubSubjects.every((k) => + k === key || selectedSubSubjects.includes(k)); + if (allSelected && subject && !selectedSubjects.includes(subject)) { + setSelectedSubjects((current) => [...current, subject]); + } + }; + const startSession = async () => { if (selectedStatuses.length === 0) { setError('Selecione ao menos um status para iniciar a revisao.'); @@ -335,7 +438,14 @@ export function SpacedReviewComponent() { {dashboard.subjects.map((subjectGroup) => (
-

{subjectGroup.subject}

+

{summaryText( subjectGroup.summary.activeCount, subjectGroup.summary.greenPercentage, @@ -352,7 +462,14 @@ export function SpacedReviewComponent() { {subjectGroup.subSubjects.map((subSubjectGroup) => (

- {subSubjectGroup.subSubject} + {summaryText( subSubjectGroup.summary.activeCount, subSubjectGroup.summary.greenPercentage, diff --git a/Mindforge.Web/src/services/MindforgeApiService.ts b/Mindforge.Web/src/services/MindforgeApiService.ts index 7dfdfcc..e74b634 100644 --- a/Mindforge.Web/src/services/MindforgeApiService.ts +++ b/Mindforge.Web/src/services/MindforgeApiService.ts @@ -26,7 +26,7 @@ export interface CheckFileResponse { result: string; } -export type FlashcardDifficulty = 'Easy' | 'Hard'; +export type FlashcardDifficulty = 'Easy' | 'Medium'; export interface GenerateFlashcardsRequest { filePaths: string[];