fixes to my list w anilist

This commit is contained in:
2025-12-07 15:09:27 +01:00
parent 220bbed16a
commit b2f6b489aa
5 changed files with 132 additions and 41 deletions

View File

@@ -119,6 +119,8 @@ export async function getUserAniList(appUserId: number) {
entries { entries {
media { media {
id id
type
format
title { romaji english userPreferred } title { romaji english userPreferred }
coverImage { extraLarge } coverImage { extraLarge }
chapters chapters
@@ -180,14 +182,22 @@ export async function getUserAniList(appUserId: number) {
media?.chapters || media?.chapters ||
(media?.volumes ? media.volumes * 10 : 0); (media?.volumes ? media.volumes * 10 : 0);
const resolvedType =
type === 'MANGA' &&
(media?.format === 'LIGHT_NOVEL' || media?.format === 'NOVEL')
? 'NOVEL'
: type;
result.push({ result.push({
user_id: appUserId, user_id: appUserId,
// ✅ FIX CLAVE
entry_id: media.id, entry_id: media.id,
source: 'anilist', source: 'anilist',
entry_type: type,
// ✅ AHORA TU FRONT RECIBE NOVEL
entry_type: resolvedType,
status: entry.status, status: entry.status,
progress: entry.progress || 0, progress: entry.progress || 0,
score: entry.score || null, score: entry.score || null,
@@ -197,7 +207,6 @@ export async function getUserAniList(appUserId: number) {
notes: entry.notes || null, notes: entry.notes || null,
is_private: entry.private ? 1 : 0, is_private: entry.private ? 1 : 0,
// ✅ CAMPOS QUE EL FRONTEND NECESITA
title: media?.title?.userPreferred title: media?.title?.userPreferred
|| media?.title?.english || media?.title?.english
|| media?.title?.romaji || media?.title?.romaji
@@ -206,10 +215,9 @@ export async function getUserAniList(appUserId: number) {
poster: media?.coverImage?.extraLarge poster: media?.coverImage?.extraLarge
|| 'https://placehold.co/400x600?text=No+Cover', || 'https://placehold.co/400x600?text=No+Cover',
total_episodes: type === 'ANIME' ? totalEpisodes : undefined, total_episodes: resolvedType === 'ANIME' ? totalEpisodes : undefined,
total_chapters: type === 'MANGA' ? totalChapters : undefined, total_chapters: resolvedType !== 'ANIME' ? totalChapters : undefined,
// ✅ PARA ORDER BY EN EL FRONT
updated_at: new Date().toISOString() updated_at: new Date().toISOString()
}); });
} }

View File

@@ -106,7 +106,11 @@ function openAddToListModal() {
if (isInList && currentListEntry) { if (isInList && currentListEntry) {
statusEl.value = currentListEntry.status || 'PLANNING'; const statusReverseMap = {
CURRENT: 'WATCHING'
};
statusEl.value = statusReverseMap[currentListEntry.status] || currentListEntry.status || 'PLANNING';
progressEl.value = currentListEntry.progress || 0; progressEl.value = currentListEntry.progress || 0;
scoreEl.value = currentListEntry.score || ''; scoreEl.value = currentListEntry.score || '';
@@ -144,7 +148,18 @@ function closeAddToListModal() {
// Función saveToList actualizada con todos los campos extendidos // Función saveToList actualizada con todos los campos extendidos
async function saveToList() { async function saveToList() {
const status = document.getElementById('entry-status').value; const uiStatus = document.getElementById('entry-status').value;
const anilistStatusMap = {
WATCHING: 'CURRENT',
COMPLETED: 'COMPLETED',
PLANNING: 'PLANNING',
PAUSED: 'PAUSED',
DROPPED: 'DROPPED',
REPEATING: 'REPEATING'
};
const status = anilistStatusMap[uiStatus];
const progress = parseInt(document.getElementById('entry-progress').value) || 0; const progress = parseInt(document.getElementById('entry-progress').value) || 0;
const scoreValue = document.getElementById('entry-score').value; const scoreValue = document.getElementById('entry-score').value;
const score = scoreValue ? parseFloat(scoreValue) : null; const score = scoreValue ? parseFloat(scoreValue) : null;

View File

@@ -188,7 +188,18 @@ function closeAddToListModal() {
*/ */
async function saveToList() { async function saveToList() {
// Datos comunes // Datos comunes
const status = document.getElementById('entry-status').value; const uiStatus = document.getElementById('entry-status').value;
const anilistStatusMap = {
WATCHING: 'CURRENT',
COMPLETED: 'COMPLETED',
PLANNING: 'PLANNING',
PAUSED: 'PAUSED',
DROPPED: 'DROPPED',
REPEATING: 'REPEATING'
};
const status = anilistStatusMap[uiStatus];
const progress = parseInt(document.getElementById('entry-progress').value) || 0; const progress = parseInt(document.getElementById('entry-progress').value) || 0;
const scoreValue = document.getElementById('entry-score').value; const scoreValue = document.getElementById('entry-score').value;
const score = scoreValue ? parseFloat(scoreValue) : null; const score = scoreValue ? parseFloat(scoreValue) : null;

View File

@@ -172,7 +172,7 @@ function applyFilters() {
} }
if (typeFilter !== 'all') { if (typeFilter !== 'all') {
filtered = filtered.filter(item => (item.entry_type || 'ANIME') === typeFilter); filtered = filtered.filter(item => item.entry_type === typeFilter);
} }
switch (sortFilter) { switch (sortFilter) {
@@ -226,7 +226,7 @@ function createListItem(item) {
const score = item.score ? item.score.toFixed(1) : null; const score = item.score ? item.score.toFixed(1) : null;
const repeatCount = item.repeat_count || 0; const repeatCount = item.repeat_count || 0;
const entryType = (item.entry_type || 'ANIME').toUpperCase(); const entryType = (item.entry_type).toUpperCase();
let unitLabel = 'units'; let unitLabel = 'units';
if (entryType === 'ANIME') { if (entryType === 'ANIME') {
unitLabel = 'episodes'; unitLabel = 'episodes';
@@ -239,7 +239,7 @@ function createListItem(item) {
const statusLabels = { const statusLabels = {
'WATCHING': entryType === 'ANIME' ? 'Watching' : 'Reading', 'WATCHING': entryType === 'ANIME' ? 'Watching' : 'Reading',
'COMPLETED': 'Completed', 'COMPLETED': 'Completed',
'PLANNING': entryType === 'ANIME' ? 'Plan to Watch' : 'Plan to Read', 'PLANNING': 'Planning',
'PAUSED': 'Paused', 'PAUSED': 'Paused',
'DROPPED': 'Dropped' 'DROPPED': 'Dropped'
}; };
@@ -294,7 +294,14 @@ function openEditModal(item) {
currentEditingEntry = item; currentEditingEntry = item;
// Campos existentes // Campos existentes
document.getElementById('edit-status').value = item.status; let modalStatus = item.status;
const type = item.entry_type?.toUpperCase();
if ((type === 'MANGA' || type === 'NOVEL') && item.status === 'READING') {
modalStatus = 'WATCHING'; // solo para mostrar correctamente
}
document.getElementById('edit-status').value = modalStatus;
document.getElementById('edit-progress').value = item.progress || 0; document.getElementById('edit-progress').value = item.progress || 0;
// Asegura que el score se muestre si existe. // Asegura que el score se muestre si existe.
document.getElementById('edit-score').value = item.score !== null && item.score !== undefined ? item.score : ''; document.getElementById('edit-score').value = item.score !== null && item.score !== undefined ? item.score : '';
@@ -309,7 +316,7 @@ function openEditModal(item) {
document.getElementById('edit-is-private').checked = item.is_private === 1 || item.is_private === true; document.getElementById('edit-is-private').checked = item.is_private === 1 || item.is_private === true;
const entryType = (item.entry_type || 'ANIME').toUpperCase(); const entryType = (item.entry_type).toUpperCase();
const progressLabel = document.querySelector('label[for="edit-progress"]'); const progressLabel = document.querySelector('label[for="edit-progress"]');
if (progressLabel) { if (progressLabel) {
if (entryType === 'MANGA') { if (entryType === 'MANGA') {
@@ -327,6 +334,17 @@ function openEditModal(item) {
document.getElementById('edit-progress').max = totalUnits; document.getElementById('edit-progress').max = totalUnits;
document.getElementById('edit-modal').classList.add('active'); document.getElementById('edit-modal').classList.add('active');
const statusSelect = document.getElementById('edit-status');
const type2 = item.entry_type?.toUpperCase();
[...statusSelect.options].forEach(opt => {
if (opt.value === 'WATCHING') {
opt.textContent = (type2 === 'MANGA' || type2 === 'NOVEL')
? 'Reading'
: 'Watching';
}
});
} }
function closeEditModal() { function closeEditModal() {
@@ -338,14 +356,22 @@ function closeEditModal() {
async function saveEntry() { async function saveEntry() {
if (!currentEditingEntry) return; if (!currentEditingEntry) return;
// Campos existentes let status = document.getElementById('edit-status').value;
const status = document.getElementById('edit-status').value;
const anilistStatusMap = {
WATCHING: 'CURRENT',
COMPLETED: 'COMPLETED',
PLANNING: 'PLANNING',
PAUSED: 'PAUSED',
DROPPED: 'DROPPED'
};
const anilistStatus = anilistStatusMap[status];
const progress = parseInt(document.getElementById('edit-progress').value) || 0; const progress = parseInt(document.getElementById('edit-progress').value) || 0;
// Usar null si el score está vacío
const scoreValue = document.getElementById('edit-score').value; const scoreValue = document.getElementById('edit-score').value;
const score = scoreValue ? parseFloat(scoreValue) : null; const score = scoreValue ? parseFloat(scoreValue) : null;
// Nuevos campos
const start_date = document.getElementById('edit-start-date').value || null; const start_date = document.getElementById('edit-start-date').value || null;
const end_date = document.getElementById('edit-end-date').value || null; const end_date = document.getElementById('edit-end-date').value || null;
const repeat_count = parseInt(document.getElementById('edit-repeat-count').value) || 0; const repeat_count = parseInt(document.getElementById('edit-repeat-count').value) || 0;
@@ -360,32 +386,55 @@ async function saveEntry() {
body: JSON.stringify({ body: JSON.stringify({
entry_id: currentEditingEntry.entry_id, entry_id: currentEditingEntry.entry_id,
source: currentEditingEntry.source, source: currentEditingEntry.source,
entry_type: currentEditingEntry.entry_type || 'ANIME', entry_type: currentEditingEntry.entry_type,
status: status, status: anilistStatus,
progress: progress, progress,
score: score, score,
// Nuevos datos a enviar al backend start_date,
start_date: start_date, end_date,
end_date: end_date, repeat_count,
repeat_count: repeat_count, notes,
notes: notes, is_private
is_private: is_private
}) })
}); });
if (!response.ok) { if (!response.ok) throw new Error('Failed to update entry');
throw new Error('Failed to update entry');
// ✅ ACTUALIZAR EN MEMORIA
const index = currentList.findIndex(e =>
e.entry_id === currentEditingEntry.entry_id &&
e.source === currentEditingEntry.source
);
if (index !== -1) {
currentList[index] = {
...currentList[index],
status,
progress,
score,
start_date,
end_date,
repeat_count,
notes,
is_private,
updated_at: new Date().toISOString()
};
} }
filteredList = [...currentList];
updateStats();
applyFilters();
closeEditModal(); closeEditModal();
await loadList();
showNotification('Entry updated successfully!', 'success'); showNotification('Entry updated successfully!', 'success');
} catch (error) { } catch (error) {
console.error('Error updating entry:', error); console.error('Error updating entry:', error);
showNotification('Failed to update entry', 'error'); showNotification('Failed to update entry', 'error');
} }
} }
async function deleteEntry() { async function deleteEntry() {
if (!currentEditingEntry) return; if (!currentEditingEntry) return;
@@ -402,13 +451,21 @@ async function deleteEntry() {
} }
); );
if (!response.ok) { if (!response.ok) throw new Error('Failed to delete entry');
throw new Error('Failed to delete entry');
} // ✅ ELIMINAR EN MEMORIA
currentList = currentList.filter(item =>
!(item.entry_id === currentEditingEntry.entry_id &&
item.source === currentEditingEntry.source)
);
filteredList = [...currentList];
updateStats();
applyFilters();
closeEditModal(); closeEditModal();
await loadList();
showNotification('Entry removed from list', 'success'); showNotification('Entry removed from list', 'success');
} catch (error) { } catch (error) {
console.error('Error deleting entry:', error); console.error('Error deleting entry:', error);
showNotification('Failed to remove entry', 'error'); showNotification('Failed to remove entry', 'error');

View File

@@ -87,7 +87,7 @@
<div class="container"> <div class="container">
<div class="header-section"> <div class="header-section">
<h1 class="page-title">My Anime List</h1> <h1 class="page-title">My List</h1>
<div class="stats-row"> <div class="stats-row">
<div class="stat-card"> <div class="stat-card">
<span class="stat-value" id="total-count">0</span> <span class="stat-value" id="total-count">0</span>
@@ -103,7 +103,7 @@
</div> </div>
<div class="stat-card"> <div class="stat-card">
<span class="stat-value" id="planned-count">0</span> <span class="stat-value" id="planned-count">0</span>
<span class="stat-label">Plan to Watch</span> <span class="stat-label">Planning</span>
</div> </div>
</div> </div>
</div> </div>
@@ -115,7 +115,7 @@
<option value="all">All Status</option> <option value="all">All Status</option>
<option value="WATCHING">Watching</option> <option value="WATCHING">Watching</option>
<option value="COMPLETED">Completed</option> <option value="COMPLETED">Completed</option>
<option value="PLANNING">Plan to Watch</option> <option value="PLANNING">Planning</option>
<option value="PAUSED">Paused</option> <option value="PAUSED">Paused</option>
<option value="DROPPED">Dropped</option> <option value="DROPPED">Dropped</option>
</select> </select>
@@ -181,7 +181,7 @@
</svg> </svg>
<h2>Your list is empty</h2> <h2>Your list is empty</h2>
<p>Start adding anime to track your progress</p> <p>Start adding anime to track your progress</p>
<button class="btn-primary" onclick="window.location.href='/anime'">Browse Anime</button> <button class="btn-primary" onclick="window.location.href='/anime'">Browse Content</button>
</div> </div>
<div id="list-container" class="list-grid"></div> <div id="list-container" class="list-grid"></div>
@@ -200,7 +200,7 @@
<select id="edit-status" class="form-input"> <select id="edit-status" class="form-input">
<option value="WATCHING">Watching</option> <option value="WATCHING">Watching</option>
<option value="COMPLETED">Completed</option> <option value="COMPLETED">Completed</option>
<option value="PLANNING">Plan to Watch</option> <option value="PLANNING">Planning</option>
<option value="PAUSED">Paused</option> <option value="PAUSED">Paused</option>
<option value="DROPPED">Dropped</option> <option value="DROPPED">Dropped</option>
</select> </select>