added manual match modal for local library
This commit is contained in:
@@ -387,6 +387,7 @@ const DashboardApp = {
|
||||
},
|
||||
|
||||
Library: {
|
||||
tempMatchContext: null,
|
||||
loadStats: async function() {
|
||||
const types = ['anime', 'manga', 'novels'];
|
||||
const elements = { 'anime': 'local-anime-count', 'manga': 'local-manga-count', 'novels': 'local-novel-count' };
|
||||
@@ -446,7 +447,7 @@ const DashboardApp = {
|
||||
const meta = entry.metadata || {};
|
||||
let poster = meta.coverImage?.large || '/public/assets/placeholder.svg';
|
||||
|
||||
let title = isMatched ? (meta.title?.english || meta.title?.romaji) : entry.folder_name;
|
||||
let title = isMatched ? (meta.title?.english || meta.title?.romaji) : entry.path;
|
||||
if (!isMatched) title = title.replace(/\[.*?\]|\(.*?\)|\.mkv|\.mp4/g, '').trim();
|
||||
|
||||
const url = isMatched ? (type === 'anime' ? `/anime/${meta.id}` : `/book/${meta.id}`) : '#';
|
||||
@@ -459,12 +460,12 @@ const DashboardApp = {
|
||||
${!isMatched ? `<div class="unmatched-badge"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><path d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/></svg> UNMATCHED</div>` : ''}
|
||||
</div>
|
||||
<div class="item-content">
|
||||
<h3 class="item-title" title="${entry.folder_name}">${title}</h3>
|
||||
<h3 class="item-title" title="${entry.path}">${title}</h3>
|
||||
<div class="item-meta">
|
||||
<span class="meta-pill type-pill">${entry.files ? entry.files.length : 0} FILES</span>
|
||||
<span class="meta-pill type-pill">${entry.files} FILES</span>
|
||||
${isMatched ? '<span class="meta-pill status-pill">MATCHED</span>' : ''}
|
||||
</div>
|
||||
<div class="folder-path-tooltip">${entry.folder_name}</div>
|
||||
<div class="folder-path-tooltip">${entry.path}</div>
|
||||
</div>
|
||||
<button class="edit-icon-btn" onclick="DashboardApp.Library.openManualMatch('${entry.id}', '${type}')" title="${isMatched ? 'Rematch' : 'Fix Match'}">
|
||||
<svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/></svg>
|
||||
@@ -505,16 +506,67 @@ const DashboardApp = {
|
||||
},
|
||||
|
||||
openManualMatch: function(id, type) {
|
||||
const newId = prompt("Enter AniList ID to force match:");
|
||||
if (newId) {
|
||||
fetch(`${API_BASE}/library/${type}/${id}/match`, {
|
||||
const item = DashboardApp.State.localLibraryData.find(x => x.id === id);
|
||||
const pathName = item ? item.path : 'Unknown path';
|
||||
|
||||
this.tempMatchContext = { id, type };
|
||||
|
||||
document.getElementById('manual-match-path').textContent = pathName;
|
||||
document.getElementById('manual-match-id').value = '';
|
||||
|
||||
const modal = document.getElementById('manual-match-modal');
|
||||
modal.classList.remove('hidden');
|
||||
|
||||
setTimeout(() => document.getElementById('manual-match-id').focus(), 100);
|
||||
},
|
||||
|
||||
closeManualMatch: function() {
|
||||
document.getElementById('manual-match-modal').classList.add('hidden');
|
||||
this.tempMatchContext = null;
|
||||
},
|
||||
|
||||
submitManualMatch: async function() {
|
||||
if (!this.tempMatchContext) return;
|
||||
|
||||
const newId = document.getElementById('manual-match-id').value;
|
||||
if (!newId) {
|
||||
alert("Please enter a valid ID");
|
||||
return;
|
||||
}
|
||||
|
||||
const { id, type } = this.tempMatchContext;
|
||||
|
||||
const confirmBtn = document.querySelector('#manual-match-modal .btn-primary');
|
||||
const originalText = confirmBtn.textContent;
|
||||
confirmBtn.textContent = "Matching...";
|
||||
confirmBtn.disabled = true;
|
||||
|
||||
try {
|
||||
const res = await fetch(`${API_BASE}/library/${type}/${id}/match`, {
|
||||
method: 'POST',
|
||||
headers: { ...window.AuthUtils.getSimpleAuthHeaders(), 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ source: 'anilist', matched_id: parseInt(newId) })
|
||||
}).then(res => {
|
||||
if(res.ok) { alert("Matched! Refreshing..."); this.loadContent(type); }
|
||||
else { alert("Failed to match."); }
|
||||
headers: {
|
||||
...window.AuthUtils.getSimpleAuthHeaders(),
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
source: 'anilist',
|
||||
matched_id: parseInt(newId)
|
||||
})
|
||||
});
|
||||
|
||||
if(res.ok) {
|
||||
this.closeManualMatch();
|
||||
this.loadContent(type);
|
||||
} else {
|
||||
const errData = await res.json();
|
||||
alert("Failed to match: " + (errData.error || "Unknown error"));
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
alert("Connection error");
|
||||
} finally {
|
||||
confirmBtn.textContent = originalText;
|
||||
confirmBtn.disabled = false;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user