fixing stuff
Some checks failed
Mindforge Web Build and Deploy (internal) / Build Mindforge Web Image (push) Failing after 1m57s
Mindforge Web Build and Deploy (internal) / Deploy Mindforge Web (internal) (push) Has been skipped
Mindforge API Build and Deploy / Build Mindforge API Image (push) Successful in 2m11s
Mindforge API Build and Deploy / Deploy Mindforge API (internal) (push) Successful in 8s
Some checks failed
Mindforge Web Build and Deploy (internal) / Build Mindforge Web Image (push) Failing after 1m57s
Mindforge Web Build and Deploy (internal) / Deploy Mindforge Web (internal) (push) Has been skipped
Mindforge API Build and Deploy / Build Mindforge API Image (push) Successful in 2m11s
Mindforge API Build and Deploy / Deploy Mindforge API (internal) (push) Successful in 8s
This commit is contained in:
@@ -13,6 +13,6 @@ namespace Mindforge.API.Models.Requests
|
||||
public enum FlashcardDifficulty
|
||||
{
|
||||
Easy,
|
||||
Hard
|
||||
Medium
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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."
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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() {
|
||||
<div className="radio-item">
|
||||
<input
|
||||
type="radio"
|
||||
id="difficulty-hard"
|
||||
id="difficulty-medium"
|
||||
name="difficulty"
|
||||
value="Hard"
|
||||
checked={difficulty === 'Hard'}
|
||||
onChange={() => setDifficulty('Hard')}
|
||||
value="Medium"
|
||||
checked={difficulty === 'Medium'}
|
||||
onChange={() => setDifficulty('Medium')}
|
||||
/>
|
||||
<label htmlFor="difficulty-hard" className="radio-label" title="Perguntas mais desafiadoras">
|
||||
Dificil
|
||||
<label htmlFor="difficulty-medium" className="radio-label" title="Perguntas de nivel intermediario">
|
||||
Medio
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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[]) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -100,6 +100,8 @@ export function SpacedReviewComponent() {
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [selectedStatuses, setSelectedStatuses] = useState<FlashcardRagStatus[]>(['Red', 'Amber', 'Green', 'Grey']);
|
||||
const [selectedLibraryIds, setSelectedLibraryIds] = useState<number[]>([]);
|
||||
const [selectedSubjects, setSelectedSubjects] = useState<string[]>([]);
|
||||
const [selectedSubSubjects, setSelectedSubSubjects] = useState<string[]>([]);
|
||||
const [startingSession, setStartingSession] = useState(false);
|
||||
const [sessionCards, setSessionCards] = useState<FlashcardCard[]>([]);
|
||||
const [sessionLibraries, setSessionLibraries] = useState<FlashcardRagLibrary[]>([]);
|
||||
@@ -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) => (
|
||||
<section key={subjectGroup.subject} className="spaced-review-subject">
|
||||
<header className="spaced-review-subject-header">
|
||||
<label className="spaced-review-subject-toggle">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={selectedSubjects.includes(subjectGroup.subject)}
|
||||
onChange={() => toggleSubject(subjectGroup.subject)}
|
||||
/>
|
||||
<h3>{subjectGroup.subject}</h3>
|
||||
</label>
|
||||
<p>{summaryText(
|
||||
subjectGroup.summary.activeCount,
|
||||
subjectGroup.summary.greenPercentage,
|
||||
@@ -352,7 +462,14 @@ export function SpacedReviewComponent() {
|
||||
{subjectGroup.subSubjects.map((subSubjectGroup) => (
|
||||
<div key={`${subjectGroup.subject}::${subSubjectGroup.subSubject}`} className="spaced-review-subsubject-block">
|
||||
<div className="spaced-review-subsubject-header">
|
||||
<label className="spaced-review-subsubject-toggle">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={selectedSubSubjects.includes(`${subjectGroup.subject}::${subSubjectGroup.subSubject}`)}
|
||||
onChange={() => toggleSubSubject(subjectGroup.subject, subSubjectGroup.subSubject)}
|
||||
/>
|
||||
<strong>{subSubjectGroup.subSubject}</strong>
|
||||
</label>
|
||||
<span>{summaryText(
|
||||
subSubjectGroup.summary.activeCount,
|
||||
subSubjectGroup.summary.greenPercentage,
|
||||
|
||||
@@ -26,7 +26,7 @@ export interface CheckFileResponse {
|
||||
result: string;
|
||||
}
|
||||
|
||||
export type FlashcardDifficulty = 'Easy' | 'Hard';
|
||||
export type FlashcardDifficulty = 'Easy' | 'Medium';
|
||||
|
||||
export interface GenerateFlashcardsRequest {
|
||||
filePaths: string[];
|
||||
|
||||
Reference in New Issue
Block a user