fixes to my list w anilist
This commit is contained in:
@@ -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()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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');
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user