mirror of
https://github.com/lucasrcsantana/story-generator.git
synced 2025-12-16 13:27:52 +00:00
feat: implementa store global de métricas e corrige processamento de dados
- Adiciona store global usando Zustand para gerenciamento de métricas - Implementa funções específicas para atualização de métricas - Corrige processamento de métricas semanais - Melhora manipulação de estados e performance - Resolve problema de dados vazios nos gráficos
This commit is contained in:
parent
190777dcd0
commit
2175458186
32
package-lock.json
generated
32
package-lock.json
generated
@ -56,7 +56,8 @@
|
||||
"tailwind-merge": "^2.6.0",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"uuid": "^11.0.3",
|
||||
"vitest": "^2.1.8"
|
||||
"vitest": "^2.1.8",
|
||||
"zustand": "^5.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.9.1",
|
||||
@ -11042,6 +11043,35 @@
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
},
|
||||
"node_modules/zustand": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.3.tgz",
|
||||
"integrity": "sha512-14fwWQtU3pH4dE0dOpdMiWjddcH+QzKIgk1cl8epwSE7yag43k/AD/m4L6+K7DytAOr9gGBe3/EXj9g7cdostg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12.20.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": ">=18.0.0",
|
||||
"immer": ">=9.0.6",
|
||||
"react": ">=18.0.0",
|
||||
"use-sync-external-store": ">=1.2.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"immer": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"use-sync-external-store": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,7 +63,8 @@
|
||||
"tailwind-merge": "^2.6.0",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"uuid": "^11.0.3",
|
||||
"vitest": "^2.1.8"
|
||||
"vitest": "^2.1.8",
|
||||
"zustand": "^5.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.9.1",
|
||||
|
||||
@ -1,18 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Calendar, HelpCircle } from 'lucide-react';
|
||||
import { Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer, Bar, ComposedChart } from 'recharts';
|
||||
|
||||
interface WeeklyMetrics {
|
||||
week: string;
|
||||
fluency: number;
|
||||
pronunciation: number;
|
||||
accuracy: number;
|
||||
comprehension: number;
|
||||
wordsPerMinute: number;
|
||||
pauses: number;
|
||||
errors: number;
|
||||
minutesRead: number;
|
||||
}
|
||||
import type { WeeklyReadingMetrics } from '@/types/metrics';
|
||||
|
||||
interface MetricConfig {
|
||||
key: string;
|
||||
@ -44,11 +33,11 @@ const TIME_FILTERS: TimeFilterOption[] = [
|
||||
];
|
||||
|
||||
interface MetricsChartProps {
|
||||
data: WeeklyMetrics[];
|
||||
data: WeeklyReadingMetrics[];
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function MetricsChart({ data, className = '' }: MetricsChartProps) {
|
||||
export function MetricsChart({ data = [], className = '' }: MetricsChartProps) {
|
||||
const [visibleMetrics, setVisibleMetrics] = React.useState<Set<string>>(
|
||||
new Set(METRICS_CONFIG.map(metric => metric.key))
|
||||
);
|
||||
@ -66,7 +55,9 @@ export function MetricsChart({ data, className = '' }: MetricsChartProps) {
|
||||
});
|
||||
};
|
||||
|
||||
const filterDataByTime = (data: WeeklyMetrics[]): WeeklyMetrics[] => {
|
||||
const filterDataByTime = (data: WeeklyReadingMetrics[]): WeeklyReadingMetrics[] => {
|
||||
if (!data || !Array.isArray(data)) return [];
|
||||
|
||||
if (timeFilter === 'all') return data;
|
||||
|
||||
const months = TIME_FILTERS.find(f => f.value === timeFilter)?.months || 12;
|
||||
@ -74,21 +65,23 @@ export function MetricsChart({ data, className = '' }: MetricsChartProps) {
|
||||
cutoffDate.setMonth(cutoffDate.getMonth() - months);
|
||||
|
||||
return data.filter(item => {
|
||||
if (!item?.week) return false;
|
||||
const [year, week] = item.week.split('-W').map(Number);
|
||||
if (!year || !week) return false;
|
||||
const itemDate = new Date(year, 0, 1 + (week - 1) * 7);
|
||||
return itemDate >= cutoffDate;
|
||||
});
|
||||
};
|
||||
|
||||
const filteredData = filterDataByTime(data);
|
||||
const filteredData = React.useMemo(() => filterDataByTime(data), [data, timeFilter]);
|
||||
|
||||
return (
|
||||
<div className={`bg-white rounded-xl shadow-sm border border-gray-200 p-8 ${className}`}>
|
||||
<div className="flex flex-col gap-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="space-y-1">
|
||||
<h2 className="text-xl font-semibold text-gray-900">Evolução das Métricas por Semana</h2>
|
||||
<p className="text-sm text-gray-500">Acompanhe seu progresso ao longo do tempo</p>
|
||||
<h2 className="text-xl font-semibold text-gray-900">Evolução da Leitura por Semana</h2>
|
||||
<p className="text-sm text-gray-500">Acompanhe seu progresso na leitura ao longo do tempo</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
{/* Filtro de Período */}
|
||||
@ -191,9 +184,7 @@ export function MetricsChart({ data, className = '' }: MetricsChartProps) {
|
||||
accuracy: 'Precisão',
|
||||
comprehension: 'Compreensão',
|
||||
wordsPerMinute: 'Palavras/Min',
|
||||
pauses: 'Pausas',
|
||||
errors: 'Erros',
|
||||
minutesRead: 'Minutos Lidos'
|
||||
minutesRead: 'Minutos Lendo'
|
||||
};
|
||||
return [value, metricNames[name] || name];
|
||||
}}
|
||||
@ -233,7 +224,7 @@ export function MetricsChart({ data, className = '' }: MetricsChartProps) {
|
||||
<Bar
|
||||
yAxisId="right"
|
||||
dataKey="minutesRead"
|
||||
name="Minutos Lidos"
|
||||
name="Minutos Lendo"
|
||||
fill="url(#barGradient)"
|
||||
radius={[4, 4, 0, 0]}
|
||||
isAnimationActive={false}
|
||||
|
||||
@ -38,7 +38,7 @@ interface WritingMetricsChartProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function WritingMetricsChart({ data, className = '' }: WritingMetricsChartProps) {
|
||||
export function WritingMetricsChart({ data = [], className = '' }: WritingMetricsChartProps) {
|
||||
const [visibleMetrics, setVisibleMetrics] = React.useState<Set<string>>(
|
||||
new Set(METRICS_CONFIG.map(metric => metric.key))
|
||||
);
|
||||
@ -57,6 +57,8 @@ export function WritingMetricsChart({ data, className = '' }: WritingMetricsChar
|
||||
};
|
||||
|
||||
const filterDataByTime = (data: WeeklyWritingMetrics[]): WeeklyWritingMetrics[] => {
|
||||
if (!data || !Array.isArray(data)) return [];
|
||||
|
||||
if (timeFilter === 'all') return data;
|
||||
|
||||
const months = TIME_FILTERS.find(f => f.value === timeFilter)?.months || 12;
|
||||
@ -64,13 +66,15 @@ export function WritingMetricsChart({ data, className = '' }: WritingMetricsChar
|
||||
cutoffDate.setMonth(cutoffDate.getMonth() - months);
|
||||
|
||||
return data.filter(item => {
|
||||
if (!item?.week) return false;
|
||||
const [year, week] = item.week.split('-W').map(Number);
|
||||
if (!year || !week) return false;
|
||||
const itemDate = new Date(year, 0, 1 + (week - 1) * 7);
|
||||
return itemDate >= cutoffDate;
|
||||
});
|
||||
};
|
||||
|
||||
const filteredData = filterDataByTime(data);
|
||||
const filteredData = React.useMemo(() => filterDataByTime(data), [data, timeFilter]);
|
||||
|
||||
return (
|
||||
<div className={`bg-white rounded-xl shadow-sm border border-gray-200 p-8 ${className}`}>
|
||||
|
||||
@ -7,6 +7,7 @@ import { MetricsChart } from '@/components/dashboard/MetricsChart';
|
||||
import { DashboardMetrics } from '@/components/dashboard/DashboardMetrics';
|
||||
import { WritingMetricsSection } from '@/components/dashboard/WritingMetricsSection';
|
||||
import { WritingMetricsChart } from '@/components/dashboard/WritingMetricsChart';
|
||||
import { useMetricsStore } from '@/stores/metricsStore';
|
||||
import type {
|
||||
DashboardMetrics as DashboardMetricsType,
|
||||
DashboardWeeklyMetrics,
|
||||
@ -49,41 +50,64 @@ interface WeeklyData {
|
||||
minutesRead: number;
|
||||
}
|
||||
|
||||
interface EssayAnalysis {
|
||||
id: string;
|
||||
created_at: string;
|
||||
overall_score: number;
|
||||
suggestions: string;
|
||||
essay_analysis_scores: Array<{
|
||||
adequacy: number;
|
||||
coherence: number;
|
||||
cohesion: number;
|
||||
vocabulary: number;
|
||||
grammar: number;
|
||||
}>;
|
||||
essay_analysis_feedback: Array<{
|
||||
structure_feedback: string;
|
||||
content_feedback: string;
|
||||
language_feedback: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
interface ProcessedEssayAnalysis {
|
||||
id: string;
|
||||
created_at: string;
|
||||
overall_score: number;
|
||||
essay_id: string;
|
||||
scores: {
|
||||
adequacy: number;
|
||||
coherence: number;
|
||||
cohesion: number;
|
||||
vocabulary: number;
|
||||
grammar: number;
|
||||
};
|
||||
feedback: {
|
||||
structure_feedback: string;
|
||||
content_feedback: string;
|
||||
language_feedback: string;
|
||||
};
|
||||
}
|
||||
|
||||
export function StudentDashboardPage() {
|
||||
const navigate = useNavigate();
|
||||
const [student, setStudent] = React.useState<Student | null>(null);
|
||||
const [metrics, setMetrics] = React.useState<DashboardMetricsType>({
|
||||
reading: {
|
||||
totalStories: 0,
|
||||
averageReadingFluency: 0,
|
||||
totalReadingTime: 0,
|
||||
currentLevel: 1,
|
||||
averagePronunciation: 0,
|
||||
averageAccuracy: 0,
|
||||
averageComprehension: 0,
|
||||
averageWordsPerMinute: 0,
|
||||
averagePauses: 0,
|
||||
averageErrors: 0
|
||||
},
|
||||
writing: {
|
||||
totalEssays: 0,
|
||||
averageScore: 0,
|
||||
totalEssaysTime: 0,
|
||||
currentWritingLevel: 1,
|
||||
averageAdequacy: 0,
|
||||
averageCoherence: 0,
|
||||
averageCohesion: 0,
|
||||
averageVocabulary: 0,
|
||||
averageGrammar: 0
|
||||
}
|
||||
});
|
||||
const [weeklyMetrics, setWeeklyMetrics] = React.useState<DashboardWeeklyMetrics>({
|
||||
reading: [],
|
||||
writing: []
|
||||
});
|
||||
const [loading, setLoading] = React.useState(true);
|
||||
const [error, setError] = React.useState<string | null>(null);
|
||||
const [recentStories, setRecentStories] = React.useState<Story[]>([]);
|
||||
|
||||
const {
|
||||
metrics,
|
||||
weeklyMetrics,
|
||||
loading,
|
||||
error,
|
||||
setMetrics,
|
||||
setWeeklyMetrics,
|
||||
updateReadingMetrics,
|
||||
updateWritingMetrics,
|
||||
updateWeeklyReadingMetrics,
|
||||
updateWeeklyWritingMetrics,
|
||||
setLoading,
|
||||
setError,
|
||||
resetMetrics
|
||||
} = useMetricsStore();
|
||||
|
||||
const processWeeklyMetrics = (recordings: Recording[]) => {
|
||||
const weeklyData = recordings.reduce((acc: { [key: string]: WeeklyData }, recording) => {
|
||||
@ -132,9 +156,54 @@ export function StudentDashboardPage() {
|
||||
.sort((a, b) => a.week.localeCompare(b.week));
|
||||
};
|
||||
|
||||
const processWeeklyWritingMetrics = (analyses: ProcessedEssayAnalysis[]) => {
|
||||
const weeklyData = analyses.reduce((acc: { [key: string]: any }, analysis) => {
|
||||
const date = new Date(analysis.created_at);
|
||||
const week = `${date.getFullYear()}-W${Math.ceil((date.getDate() + date.getDay()) / 7)}`;
|
||||
|
||||
if (!acc[week]) {
|
||||
acc[week] = {
|
||||
count: 0,
|
||||
score: 0,
|
||||
adequacy: 0,
|
||||
coherence: 0,
|
||||
cohesion: 0,
|
||||
vocabulary: 0,
|
||||
grammar: 0,
|
||||
minutesWriting: 0
|
||||
};
|
||||
}
|
||||
|
||||
acc[week].count += 1;
|
||||
acc[week].score += analysis.overall_score;
|
||||
acc[week].adequacy += analysis.scores.adequacy;
|
||||
acc[week].coherence += analysis.scores.coherence;
|
||||
acc[week].cohesion += analysis.scores.cohesion;
|
||||
acc[week].vocabulary += analysis.scores.vocabulary;
|
||||
acc[week].grammar += analysis.scores.grammar;
|
||||
acc[week].minutesWriting += 30; // Tempo médio estimado por redação
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return Object.entries(weeklyData)
|
||||
.map(([week, data]: [string, any]) => ({
|
||||
week,
|
||||
score: Math.round(data.score / data.count),
|
||||
adequacy: Math.round(data.adequacy / data.count),
|
||||
coherence: Math.round(data.coherence / data.count),
|
||||
cohesion: Math.round(data.cohesion / data.count),
|
||||
vocabulary: Math.round(data.vocabulary / data.count),
|
||||
grammar: Math.round(data.grammar / data.count),
|
||||
minutesWriting: data.minutesWriting
|
||||
}))
|
||||
.sort((a, b) => a.week.localeCompare(b.week));
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
const fetchDashboardData = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const { data: { session } } = await supabase.auth.getSession();
|
||||
if (!session?.user?.id) return;
|
||||
|
||||
@ -175,10 +244,8 @@ export function StudentDashboardPage() {
|
||||
|
||||
// Processar métricas semanais
|
||||
const weeklyData = processWeeklyMetrics(recordings);
|
||||
setWeeklyMetrics({
|
||||
reading: weeklyData,
|
||||
writing: []
|
||||
});
|
||||
// Atualizar métricas semanais de leitura
|
||||
updateWeeklyReadingMetrics(weeklyData);
|
||||
|
||||
// Buscar histórias recentes com a capa definida
|
||||
const { data: stories, error: storiesError } = await supabase
|
||||
@ -237,31 +304,108 @@ export function StudentDashboardPage() {
|
||||
errors: 0
|
||||
});
|
||||
|
||||
// Calcular médias
|
||||
setMetrics({
|
||||
reading: {
|
||||
totalStories: allStoriesData.length,
|
||||
averageReadingFluency: Math.round(metricsSum.fluency / totalRecordings),
|
||||
totalReadingTime: recordings.length * 2,
|
||||
currentLevel: Math.ceil(metricsSum.fluency / (totalRecordings * 20)),
|
||||
averagePronunciation: Math.round(metricsSum.pronunciation / totalRecordings),
|
||||
averageAccuracy: Math.round(metricsSum.accuracy / totalRecordings),
|
||||
averageComprehension: Math.round(metricsSum.comprehension / totalRecordings),
|
||||
averageWordsPerMinute: Math.round(metricsSum.wordsPerMinute / totalRecordings),
|
||||
averagePauses: Math.round(metricsSum.pauses / totalRecordings),
|
||||
averageErrors: Math.round(metricsSum.errors / totalRecordings)
|
||||
// Atualizar métricas de leitura
|
||||
updateReadingMetrics({
|
||||
totalStories: allStoriesData.length,
|
||||
averageReadingFluency: Math.round(metricsSum.fluency / totalRecordings),
|
||||
totalReadingTime: recordings.length * 2,
|
||||
currentLevel: Math.ceil(metricsSum.fluency / (totalRecordings * 20)),
|
||||
averagePronunciation: Math.round(metricsSum.pronunciation / totalRecordings),
|
||||
averageAccuracy: Math.round(metricsSum.accuracy / totalRecordings),
|
||||
averageComprehension: Math.round(metricsSum.comprehension / totalRecordings),
|
||||
averageWordsPerMinute: Math.round(metricsSum.wordsPerMinute / totalRecordings),
|
||||
averagePauses: Math.round(metricsSum.pauses / totalRecordings),
|
||||
averageErrors: Math.round(metricsSum.errors / totalRecordings)
|
||||
});
|
||||
}
|
||||
|
||||
// Buscar todas as redações do aluno
|
||||
const { data: essays, error: essaysError } = await supabase
|
||||
.from('student_essays')
|
||||
.select(`
|
||||
id,
|
||||
created_at,
|
||||
status,
|
||||
essay_analyses(
|
||||
id,
|
||||
overall_score,
|
||||
suggestions,
|
||||
created_at,
|
||||
essay_analysis_scores(
|
||||
adequacy,
|
||||
coherence,
|
||||
cohesion,
|
||||
vocabulary,
|
||||
grammar
|
||||
),
|
||||
essay_analysis_feedback(
|
||||
structure_feedback,
|
||||
content_feedback,
|
||||
language_feedback
|
||||
)
|
||||
)
|
||||
`)
|
||||
.eq('student_id', session.user.id)
|
||||
.eq('status', 'completed');
|
||||
|
||||
if (essaysError) throw essaysError;
|
||||
|
||||
// Processar métricas semanais de escrita
|
||||
const analyses = essays?.flatMap(essay =>
|
||||
essay.essay_analyses?.map(analysis => ({
|
||||
id: analysis.id,
|
||||
created_at: analysis.created_at,
|
||||
overall_score: analysis.overall_score,
|
||||
essay_id: essay.id,
|
||||
scores: analysis.essay_analysis_scores?.[0] || {
|
||||
adequacy: 0,
|
||||
coherence: 0,
|
||||
cohesion: 0,
|
||||
vocabulary: 0,
|
||||
grammar: 0
|
||||
},
|
||||
writing: {
|
||||
totalEssays: 0,
|
||||
averageScore: 0,
|
||||
totalEssaysTime: 0,
|
||||
currentWritingLevel: 1,
|
||||
averageAdequacy: 0,
|
||||
averageCoherence: 0,
|
||||
averageCohesion: 0,
|
||||
averageVocabulary: 0,
|
||||
averageGrammar: 0
|
||||
feedback: analysis.essay_analysis_feedback?.[0] || {
|
||||
structure_feedback: '',
|
||||
content_feedback: '',
|
||||
language_feedback: ''
|
||||
}
|
||||
}))
|
||||
).filter(Boolean) || [];
|
||||
|
||||
const weeklyWritingData = processWeeklyWritingMetrics(analyses);
|
||||
// Atualizar métricas semanais de escrita
|
||||
updateWeeklyWritingMetrics(weeklyWritingData);
|
||||
|
||||
// Calcular métricas gerais de escrita
|
||||
if (analyses && analyses.length > 0) {
|
||||
const totalAnalyses = analyses.length;
|
||||
const metricsSum = analyses.reduce((acc, analysis) => ({
|
||||
score: acc.score + analysis.overall_score,
|
||||
adequacy: acc.adequacy + analysis.scores.adequacy,
|
||||
coherence: acc.coherence + analysis.scores.coherence,
|
||||
cohesion: acc.cohesion + analysis.scores.cohesion,
|
||||
vocabulary: acc.vocabulary + analysis.scores.vocabulary,
|
||||
grammar: acc.grammar + analysis.scores.grammar
|
||||
}), {
|
||||
score: 0,
|
||||
adequacy: 0,
|
||||
coherence: 0,
|
||||
cohesion: 0,
|
||||
vocabulary: 0,
|
||||
grammar: 0
|
||||
});
|
||||
|
||||
// Atualizar métricas de escrita
|
||||
updateWritingMetrics({
|
||||
totalEssays: essays?.length || 0,
|
||||
averageScore: Math.round(metricsSum.score / totalAnalyses),
|
||||
totalEssaysTime: totalAnalyses * 30,
|
||||
currentWritingLevel: Math.ceil(metricsSum.score / (totalAnalyses * 20)),
|
||||
averageAdequacy: Math.round(metricsSum.adequacy / totalAnalyses),
|
||||
averageCoherence: Math.round(metricsSum.coherence / totalAnalyses),
|
||||
averageCohesion: Math.round(metricsSum.cohesion / totalAnalyses),
|
||||
averageVocabulary: Math.round(metricsSum.vocabulary / totalAnalyses),
|
||||
averageGrammar: Math.round(metricsSum.grammar / totalAnalyses)
|
||||
});
|
||||
}
|
||||
|
||||
@ -274,6 +418,11 @@ export function StudentDashboardPage() {
|
||||
};
|
||||
|
||||
fetchDashboardData();
|
||||
|
||||
// Limpar métricas ao desmontar
|
||||
return () => {
|
||||
resetMetrics();
|
||||
};
|
||||
}, []);
|
||||
|
||||
if (loading) {
|
||||
|
||||
106
src/stores/metricsStore.ts
Normal file
106
src/stores/metricsStore.ts
Normal file
@ -0,0 +1,106 @@
|
||||
import { create } from 'zustand';
|
||||
import type {
|
||||
DashboardMetrics,
|
||||
DashboardWeeklyMetrics,
|
||||
ReadingMetrics,
|
||||
WritingMetrics,
|
||||
WeeklyReadingMetrics,
|
||||
WeeklyWritingMetrics
|
||||
} from '@/types/metrics';
|
||||
|
||||
interface MetricsState {
|
||||
metrics: DashboardMetrics;
|
||||
weeklyMetrics: DashboardWeeklyMetrics;
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
setMetrics: (metrics: DashboardMetrics) => void;
|
||||
setWeeklyMetrics: (weeklyMetrics: DashboardWeeklyMetrics) => void;
|
||||
updateReadingMetrics: (readingMetrics: ReadingMetrics) => void;
|
||||
updateWritingMetrics: (writingMetrics: WritingMetrics) => void;
|
||||
updateWeeklyReadingMetrics: (weeklyReadingMetrics: WeeklyReadingMetrics[]) => void;
|
||||
updateWeeklyWritingMetrics: (weeklyWritingMetrics: WeeklyWritingMetrics[]) => void;
|
||||
setLoading: (loading: boolean) => void;
|
||||
setError: (error: string | null) => void;
|
||||
resetMetrics: () => void;
|
||||
}
|
||||
|
||||
const initialMetrics: DashboardMetrics = {
|
||||
reading: {
|
||||
totalStories: 0,
|
||||
averageReadingFluency: 0,
|
||||
totalReadingTime: 0,
|
||||
currentLevel: 1,
|
||||
averagePronunciation: 0,
|
||||
averageAccuracy: 0,
|
||||
averageComprehension: 0,
|
||||
averageWordsPerMinute: 0,
|
||||
averagePauses: 0,
|
||||
averageErrors: 0
|
||||
},
|
||||
writing: {
|
||||
totalEssays: 0,
|
||||
averageScore: 0,
|
||||
totalEssaysTime: 0,
|
||||
currentWritingLevel: 1,
|
||||
averageAdequacy: 0,
|
||||
averageCoherence: 0,
|
||||
averageCohesion: 0,
|
||||
averageVocabulary: 0,
|
||||
averageGrammar: 0
|
||||
}
|
||||
};
|
||||
|
||||
const initialWeeklyMetrics: DashboardWeeklyMetrics = {
|
||||
reading: [],
|
||||
writing: []
|
||||
};
|
||||
|
||||
export const useMetricsStore = create<MetricsState>((set) => ({
|
||||
metrics: initialMetrics,
|
||||
weeklyMetrics: initialWeeklyMetrics,
|
||||
loading: true,
|
||||
error: null,
|
||||
|
||||
setMetrics: (metrics) => set({ metrics }),
|
||||
|
||||
setWeeklyMetrics: (weeklyMetrics) => set({ weeklyMetrics }),
|
||||
|
||||
updateReadingMetrics: (readingMetrics) => set((state) => ({
|
||||
metrics: {
|
||||
...state.metrics,
|
||||
reading: readingMetrics
|
||||
}
|
||||
})),
|
||||
|
||||
updateWritingMetrics: (writingMetrics) => set((state) => ({
|
||||
metrics: {
|
||||
...state.metrics,
|
||||
writing: writingMetrics
|
||||
}
|
||||
})),
|
||||
|
||||
updateWeeklyReadingMetrics: (weeklyReadingMetrics) => set((state) => ({
|
||||
weeklyMetrics: {
|
||||
...state.weeklyMetrics,
|
||||
reading: weeklyReadingMetrics
|
||||
}
|
||||
})),
|
||||
|
||||
updateWeeklyWritingMetrics: (weeklyWritingMetrics) => set((state) => ({
|
||||
weeklyMetrics: {
|
||||
...state.weeklyMetrics,
|
||||
writing: weeklyWritingMetrics
|
||||
}
|
||||
})),
|
||||
|
||||
setLoading: (loading) => set({ loading }),
|
||||
|
||||
setError: (error) => set({ error }),
|
||||
|
||||
resetMetrics: () => set({
|
||||
metrics: initialMetrics,
|
||||
weeklyMetrics: initialWeeklyMetrics,
|
||||
loading: false,
|
||||
error: null
|
||||
})
|
||||
}));
|
||||
Loading…
Reference in New Issue
Block a user