| Name | Size | Mode | Actions |
|---|---|---|---|
| config.js | 415 | 0666 | editdlrm |
| importer.js | 26616 | 0666 | editdlrm |
| settings.js | 1919 | 0666 | editdlrm |
/home/techb158/trello-import.abdallabala.com/public/js/importer.js (26616B)
" + "" + state.mappedRows.length + " rows" + "" + errorCount + " errors" + "" + warningCount + " warnings" + (strict ? "Strict mode on" : "") + "
"; var previewRows = state.mappedRows.slice(0, 50); $("previewTable").innerHTML = "| Row | Name | List | Due | Labels | Members | Checklist items | Issues | " + "
|---|---|---|---|---|---|---|---|
| " + row.sourceRow + " | " + "" + escapeHtml(row.name) + " | " + "" + escapeHtml(row.listName) + " | " + "" + escapeHtml(row.due ? row.due.slice(0, 10) : "") + " | " + "" + escapeHtml(row.labels.join(", ")) + " | " + "" + escapeHtml(row.members.join(", ")) + " | " + "" + row.checklists.reduce(function (sum, list) { return sum + list.items.length; }, 0) + " | " + "" + (issues || "") + " | " + "
Showing first 50 rows only.
" : ""); $("importButton").disabled = errorCount > 0; setVisible("previewSection", true); setStatus(errorCount ? "Preview has errors. Fix mappings or source data before importing." : "Preview ready. Review the rows before importing.", errorCount ? "error" : "success"); } function enrichPreviewResolution(rows) { var strict = $("strictCheckbox").checked; var createLists = $("createListsCheckbox").checked; var createLabels = $("createLabelsCheckbox").checked; rows.forEach(function (row) { if (row.listName && !findList(row.listName) && !createLists) { var message = "List does not exist: " + row.listName; (strict ? row.errors : row.warnings).push(message); } row.labels.forEach(function (label) { if (label && !findLabel(label) && !createLabels) { var labelMessage = "Label does not exist: " + label; (strict ? row.errors : row.warnings).push(labelMessage); } }); row.members.forEach(function (member) { if (member && !findMember(member)) { var memberMessage = "Member not found on board: " + member; (strict ? row.errors : row.warnings).push(memberMessage); } }); }); } function findList(name) { var key = normalizeKey(name); return state.lists.find(function (list) { return normalizeKey(list.name) === key || list.id === name; }); } function findLabel(name) { var key = normalizeKey(name); return state.labels.find(function (label) { return normalizeKey(label.name) === key || normalizeKey(label.color) === key || label.id === name; }); } function findMember(name) { var key = normalizeKey(String(name).replace(/^@/, "")); return state.members.find(function (member) { return normalizeKey(member.username) === key || normalizeKey(member.fullName) === key || normalizeKey(member.id) === key; }); } async function init() { state.t = window.TrelloPowerUp.iframe(options); bindEvents(); if (!isConfigured()) { setStatus("Set your Trello Power-Up API key in public/js/config.js before using the importer.", "error"); disableMainControls(true); return; } await state.t.render(async function () { await refreshAuthAndBoardData(false); }); } function bindEvents() { $("authorizeButton").addEventListener("click", authorize); $("reloadButton").addEventListener("click", function () { refreshAuthAndBoardData(true); }); $("parseButton").addEventListener("click", parseDataFromUi); $("previewButton").addEventListener("click", renderPreview); $("importButton").addEventListener("click", importCards); $("closeButton").addEventListener("click", function () { state.t.closeModal(); }); $("sampleCsvButton").addEventListener("click", function () { $("pasteInput").value = sampleCsv; }); $("sampleJsonButton").addEventListener("click", function () { $("pasteInput").value = sampleJson; }); } function disableMainControls(disabled) { ["authorizeButton", "reloadButton", "parseButton", "previewButton", "importButton"].forEach(function (id) { $(id).disabled = disabled; }); } async function authorize() { try { setStatus("Opening Trello authorization.", "info"); var client = await state.t.getRestApi(); await client.authorize({ scope: "read,write", expiration: "never" }); setStatus("Authorized. Loading board data.", "success"); await refreshAuthAndBoardData(true); } catch (error) { setStatus("Authorization was not completed: " + errorMessage(error), "error"); } } async function refreshAuthAndBoardData(forceMessage) { try { var client = await state.t.getRestApi(); var authorized = await client.isAuthorized(); $("authorizeButton").textContent = authorized ? "Reauthorize" : "Authorize Trello"; if (!authorized) { setStatus("Authorize the Power-Up before importing. The importer needs read and write access to create cards on this board.", "warning"); return; } state.token = await client.getToken(); state.board = await state.t.board("id", "name", "url"); await loadBoardData(); if (forceMessage) setStatus("Loaded board data for “" + state.board.name + "”.", "success"); else setStatus("Authorized and ready. Choose a JSON or CSV file to begin.", "success"); } catch (error) { setStatus("Could not load board data: " + errorMessage(error), "error"); } } async function loadBoardData() { if (!state.board || !state.board.id) throw new Error("Could not determine the current Trello board."); var boardId = state.board.id; var lists = await api("boards/" + boardId + "/lists", "GET", { filter: "open", fields: "name" }); var labels = await api("boards/" + boardId + "/labels", "GET", { fields: "name,color" }); var members = await api("boards/" + boardId + "/members", "GET", { fields: "fullName,username,initials" }); state.lists = lists || []; state.labels = labels || []; state.members = members || []; renderDefaultLists(); } async function parseDataFromUi() { try { var file = $("fileInput").files[0]; var pasted = $("pasteInput").value; var text = pasted.trim() ? pasted : file ? await readFile(file) : ""; state.rawRows = parseInput(text, file && file.name); state.headers = collectHeaders(state.rawRows); setStatus("Parsed " + state.rawRows.length + " rows. Confirm the field mapping.", "success"); renderMapping(); setVisible("previewSection", false); setVisible("progressSection", false); } catch (error) { setStatus("Parse failed: " + errorMessage(error), "error"); } } function readFile(file) { return new Promise(function (resolve, reject) { var reader = new FileReader(); reader.onload = function () { resolve(String(reader.result || "")); }; reader.onerror = function () { reject(reader.error || new Error("Could not read file.")); }; reader.readAsText(file); }); } async function importCards() { var rows = state.mappedRows.length ? state.mappedRows : mapRows(); enrichPreviewResolution(rows); var errorRows = rows.filter(function (row) { return row.errors.length > 0; }); if (errorRows.length) { setStatus("Import blocked because preview still has errors.", "error"); return; } $("importButton").disabled = true; setVisible("progressSection", true); clearLog(); updateProgress(0, rows.length); log("Starting import of " + rows.length + " cards."); var imported = 0; var failed = 0; var delayMs = Math.max(0, parseInt($("rateLimitInput").value, 10) || 0); for (var i = 0; i < rows.length; i += 1) { var row = rows[i]; try { await importSingleCard(row); imported += 1; log("✓ Row " + row.sourceRow + ": " + row.name); } catch (error) { failed += 1; log("✗ Row " + row.sourceRow + ": " + row.name + ", " + errorMessage(error)); } updateProgress(i + 1, rows.length); if (delayMs && i < rows.length - 1) await sleep(delayMs); } setStatus("Import finished. " + imported + " cards created, " + failed + " failed.", failed ? "warning" : "success"); log("Finished. Created: " + imported + ". Failed: " + failed + "."); $("importButton").disabled = false; } async function importSingleCard(row) { var list = await resolveList(row.listName); var labelIds = await resolveLabels(row.labels); var memberIds = resolveMembers(row.members); var payload = { idList: list.id, name: row.name, desc: row.desc, pos: row.pos || "bottom" }; if (row.due) payload.due = row.due; if (row.start) payload.start = row.start; if (labelIds.length) payload.idLabels = labelIds.join(","); if (memberIds.length) payload.idMembers = memberIds.join(","); if (row.urlSource && /^https?:\/\//i.test(row.urlSource)) payload.urlSource = row.urlSource; var card = await api("cards", "POST", payload); for (var c = 0; c < row.comments.length; c += 1) { await api("cards/" + card.id + "/actions/comments", "POST", { text: row.comments[c] }); } for (var l = 0; l < row.checklists.length; l += 1) { var checklist = row.checklists[l]; var created = await api("cards/" + card.id + "/checklists", "POST", { name: checklist.name || "Imported checklist", pos: "bottom" }); for (var itemIndex = 0; itemIndex < checklist.items.length; itemIndex += 1) { await api("checklists/" + created.id + "/checkItems", "POST", { name: checklist.items[itemIndex], checked: "false", pos: "bottom" }); } } return card; } async function resolveList(name) { var existing = findList(name); if (existing) return existing; if (!$("createListsCheckbox").checked) throw new Error("List does not exist: " + name); var list = await api("lists", "POST", { name: name, idBoard: state.board.id, pos: "bottom" }); state.lists.push(list); renderDefaultLists(); return list; } async function resolveLabels(names) { var ids = []; for (var i = 0; i < names.length; i += 1) { var name = names[i]; var existing = findLabel(name); if (existing) { ids.push(existing.id); continue; } if (!$("createLabelsCheckbox").checked) throw new Error("Label does not exist: " + name); var label = await api("labels", "POST", { name: name, color: "null", idBoard: state.board.id }); state.labels.push(label); ids.push(label.id); } return ids; } function resolveMembers(names) { return names.map(function (name) { return findMember(name); }).filter(Boolean).map(function (member) { return member.id; }); } async function api(path, method, params) { if (!state.token) throw new Error("Missing Trello token. Reauthorize and try again."); var url = new URL("https://api.trello.com/1/" + path.replace(/^\//, "")); url.searchParams.set("key", options.appKey); url.searchParams.set("token", state.token); var fetchOptions = { method: method || "GET" }; var data = params || {}; if ((method || "GET").toUpperCase() === "GET") { Object.keys(data).forEach(function (key) { if (data[key] !== undefined && data[key] !== null && data[key] !== "") url.searchParams.set(key, data[key]); }); } else { fetchOptions.headers = { "Content-Type": "application/x-www-form-urlencoded" }; var body = new URLSearchParams(); Object.keys(data).forEach(function (key) { if (data[key] !== undefined && data[key] !== null && data[key] !== "") body.set(key, data[key]); }); fetchOptions.body = body.toString(); } var response = await fetch(url.toString(), fetchOptions); var text = await response.text(); var payload = null; try { payload = text ? JSON.parse(text) : null; } catch (e) { payload = text; } if (!response.ok) { throw new Error(typeof payload === "string" ? payload : JSON.stringify(payload)); } return payload; } function updateProgress(done, total) { var percent = total ? Math.round((done / total) * 100) : 0; $("progressBar").style.width = percent + "%"; $("progressText").textContent = done + " of " + total + " rows processed."; } function log(message) { var el = $("log"); el.textContent += message + "\n"; el.scrollTop = el.scrollHeight; } function clearLog() { $("log").textContent = ""; } function sleep(ms) { return new Promise(function (resolve) { setTimeout(resolve, ms); }); } function errorMessage(error) { if (!error) return "Unknown error"; return error.message || String(error); } document.addEventListener("DOMContentLoaded", function () { init().catch(function (error) { setStatus("Could not initialize importer: " + errorMessage(error), "error"); }); }); })();