add new lesson mode and started code refraction

This commit is contained in:
Rene Kievits
2025-12-20 04:31:15 +01:00
parent 6438660b03
commit 4428a2b7be
101 changed files with 12255 additions and 8172 deletions

View File

@@ -3,134 +3,200 @@ import { defineStore } from 'pinia';
const BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:3000';
export const useAppStore = defineStore('app', {
state: () => ({
token: localStorage.getItem('zen_token') || '',
user: null,
queue: [],
collection: [],
stats: {
distribution: {},
forecast: [],
queueLength: 0,
streak: {},
accuracy: {},
ghosts: []
},
batchSize: 20,
drawingAccuracy: 10,
loading: false
}),
state: () => ({
token: localStorage.getItem('zen_token') || '',
user: null,
queue: [],
lessonQueue: [],
collection: [],
stats: {
distribution: {},
forecast: [],
queueLength: 0,
lessonCount: 0,
streak: {},
accuracy: {},
ghosts: [],
},
batchSize: parseInt(localStorage.getItem('zen_batch_size'), 10) || 20,
drawingAccuracy: parseInt(localStorage.getItem('zen_drawing_accuracy'), 10) || 10,
loading: false,
}),
actions: {
async login(apiKey) {
const res = await fetch(`${BASE_URL}/api/auth/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ apiKey })
});
actions: {
async login(apiKey) {
const res = await fetch(`${BASE_URL}/api/auth/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ apiKey }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error || 'Login failed');
const data = await res.json();
if (!res.ok) throw new Error(data.error || 'Login failed');
this.token = data.token;
this.user = data.user;
this.token = data.token;
this.user = data.user;
if (data.user.settings) {
this.batchSize = data.user.settings.batchSize || 20;
this.drawingAccuracy = data.user.settings.drawingAccuracy || 10;
}
if (data.user.settings) {
this.batchSize = data.user.settings.batchSize || 20;
this.drawingAccuracy = data.user.settings.drawingAccuracy || 10;
localStorage.setItem('zen_token', data.token);
// Persist settings to local storage on login
localStorage.setItem('zen_batch_size', this.batchSize);
localStorage.setItem('zen_drawing_accuracy', this.drawingAccuracy);
}
await this.fetchStats();
return data;
},
localStorage.setItem('zen_token', data.token);
async logout() {
try {
if (this.token) {
await fetch(`${BASE_URL}/api/auth/logout`, {
method: 'POST',
headers: this.getHeaders()
});
}
} catch (e) {
console.error("Logout error:", e);
} finally {
this.clearData();
}
},
await this.fetchStats();
return data;
},
clearData() {
this.token = '';
this.user = null;
this.queue = [];
this.stats = {};
localStorage.removeItem('zen_token');
},
async logout() {
try {
if (this.token) {
await fetch(`${BASE_URL}/api/auth/logout`, {
method: 'POST',
headers: this.getHeaders(),
});
}
} catch (e) {
console.error('Logout error:', e);
} finally {
this.clearData();
}
},
getHeaders() {
return {
'Authorization': `Bearer ${this.token}`,
'Content-Type': 'application/json'
};
},
clearData() {
this.token = '';
this.user = null;
this.queue = [];
this.stats = {};
localStorage.removeItem('zen_token');
localStorage.removeItem('zen_batch_size');
localStorage.removeItem('zen_drawing_accuracy');
},
async sync() {
const res = await fetch(`${BASE_URL}/api/sync`, {
method: 'POST',
headers: this.getHeaders(),
body: JSON.stringify({})
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);
return data;
},
getHeaders() {
return {
Authorization: `Bearer ${this.token}`,
'Content-Type': 'application/json',
};
},
async fetchStats() {
if (!this.token) return;
const res = await fetch(`${BASE_URL}/api/stats`, { headers: this.getHeaders() });
if (res.status === 401) return this.logout();
const data = await res.json();
this.stats = data;
return data;
},
async sync() {
const res = await fetch(`${BASE_URL}/api/sync`, {
method: 'POST',
headers: this.getHeaders(),
body: JSON.stringify({}),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);
return data;
},
async fetchQueue(sortMode = 'shuffled') {
if (!this.token) return;
const res = await fetch(`${BASE_URL}/api/queue?limit=${this.batchSize}&sort=${sortMode}`, {
headers: this.getHeaders()
});
if (res.status === 401) return this.logout();
this.queue = await res.json();
},
async fetchStats() {
if (!this.token) return null;
async fetchCollection() {
if (!this.token) return;
const res = await fetch(`${BASE_URL}/api/collection`, { headers: this.getHeaders() });
if (res.status === 401) return this.logout();
this.collection = await res.json();
},
const res = await fetch(`${BASE_URL}/api/stats`, { headers: this.getHeaders() });
if (res.status === 401) {
await this.logout();
return null;
}
async submitReview(subjectId, success) {
const res = await fetch(`${BASE_URL}/api/review`, {
method: 'POST',
headers: this.getHeaders(),
body: JSON.stringify({ subjectId, success })
});
if (res.status === 401) return this.logout();
return await res.json();
},
const data = await res.json();
this.stats = data;
return data;
},
async saveSettings(settings) {
if (settings.batchSize) this.batchSize = settings.batchSize;
if (settings.drawingAccuracy) this.drawingAccuracy = settings.drawingAccuracy;
async fetchQueue(sortMode = 'shuffled') {
if (!this.token) return;
await fetch(`${BASE_URL}/api/settings`, {
method: 'POST',
headers: this.getHeaders(),
body: JSON.stringify(settings)
});
}
}
const res = await fetch(`${BASE_URL}/api/queue?limit=${this.batchSize}&sort=${sortMode}`, {
headers: this.getHeaders(),
});
if (res.status === 401) {
await this.logout();
return;
}
this.queue = await res.json();
},
async fetchLessonQueue() {
if (!this.token) return;
const res = await fetch(`${BASE_URL}/api/lessons?limit=${this.batchSize}`, {
headers: this.getHeaders(),
});
if (res.status === 401) {
await this.logout();
return;
}
this.lessonQueue = await res.json();
},
async fetchCollection() {
if (!this.token) return;
const res = await fetch(`${BASE_URL}/api/collection`, { headers: this.getHeaders() });
if (res.status === 401) {
await this.logout();
return;
}
this.collection = await res.json();
},
async submitReview(subjectId, success) {
const res = await fetch(`${BASE_URL}/api/review`, {
method: 'POST',
headers: this.getHeaders(),
body: JSON.stringify({ subjectId, success }),
});
if (res.status === 401) {
await this.logout();
return null;
}
return res.json();
},
async submitLesson(subjectId) {
const res = await fetch(`${BASE_URL}/api/lesson`, {
method: 'POST',
headers: this.getHeaders(),
body: JSON.stringify({ subjectId }),
});
if (res.status === 401) {
await this.logout();
return null;
}
return res.json();
},
async saveSettings(settings) {
if (settings.batchSize !== undefined) {
this.batchSize = settings.batchSize;
localStorage.setItem('zen_batch_size', settings.batchSize);
}
if (settings.drawingAccuracy !== undefined) {
this.drawingAccuracy = settings.drawingAccuracy;
localStorage.setItem('zen_drawing_accuracy', settings.drawingAccuracy);
}
await fetch(`${BASE_URL}/api/settings`, {
method: 'POST',
headers: this.getHeaders(),
body: JSON.stringify(settings),
});
},
},
});