489 lines
16 KiB
JavaScript
489 lines
16 KiB
JavaScript
|
|
let loansContracts = [];
|
||
|
|
let loansLoadedForKey = "";
|
||
|
|
let loansLoadInFlight = false;
|
||
|
|
let selectedLoanContractHash = "";
|
||
|
|
let activeLoansStatusFilter = "all";
|
||
|
|
let loansStateMessage = "";
|
||
|
|
|
||
|
|
function loanFormatAtomic(value) {
|
||
|
|
if (typeof atomicToDecimal === "function") {
|
||
|
|
return atomicToDecimal(BigInt(value || "0"));
|
||
|
|
}
|
||
|
|
if (typeof formatAtomicBalance === "function") {
|
||
|
|
return formatAtomicBalance(value || "0");
|
||
|
|
}
|
||
|
|
return String(value || "0");
|
||
|
|
}
|
||
|
|
|
||
|
|
function loanShort(value, left = 8, right = 8) {
|
||
|
|
if (typeof truncateMiddle === "function") {
|
||
|
|
return truncateMiddle(value, left, right);
|
||
|
|
}
|
||
|
|
const text = String(value || "");
|
||
|
|
return text.length > left + right + 3 ? `${text.slice(0, left)}...${text.slice(-right)}` : text;
|
||
|
|
}
|
||
|
|
|
||
|
|
function escapeLoanText(value) {
|
||
|
|
return String(value ?? "")
|
||
|
|
.replaceAll("&", "&")
|
||
|
|
.replaceAll("<", "<")
|
||
|
|
.replaceAll(">", ">")
|
||
|
|
.replaceAll('"', """)
|
||
|
|
.replaceAll("'", "'");
|
||
|
|
}
|
||
|
|
|
||
|
|
function activeLoanRole(contract) {
|
||
|
|
const active = String(activeWalletAddressShort || "").trim().toLowerCase();
|
||
|
|
if (String(contract.lender || "").trim().toLowerCase() === active) {
|
||
|
|
return "lender";
|
||
|
|
}
|
||
|
|
if (String(contract.borrower || "").trim().toLowerCase() === active) {
|
||
|
|
return "borrower";
|
||
|
|
}
|
||
|
|
return "unknown";
|
||
|
|
}
|
||
|
|
|
||
|
|
function loanRoleLabel(role) {
|
||
|
|
return role === "lender" ? "Lending" : role === "borrower" ? "Borrowing" : "Related";
|
||
|
|
}
|
||
|
|
|
||
|
|
function loanCounterparty(contract) {
|
||
|
|
return activeLoanRole(contract) === "lender" ? contract.borrower : contract.lender;
|
||
|
|
}
|
||
|
|
|
||
|
|
function loanStatusClass(status) {
|
||
|
|
const normalized = String(status || "").toLowerCase();
|
||
|
|
if (normalized === "delinquent") {
|
||
|
|
return "due";
|
||
|
|
}
|
||
|
|
if (normalized === "inactive") {
|
||
|
|
return "claimable";
|
||
|
|
}
|
||
|
|
return "active";
|
||
|
|
}
|
||
|
|
|
||
|
|
function loanStatusLabel(status) {
|
||
|
|
const normalized = String(status || "").toLowerCase();
|
||
|
|
if (normalized === "inactive") {
|
||
|
|
return "Completed";
|
||
|
|
}
|
||
|
|
return normalized ? normalized.charAt(0).toUpperCase() + normalized.slice(1) : "Unknown";
|
||
|
|
}
|
||
|
|
|
||
|
|
function loanPaymentCount(contract) {
|
||
|
|
return Array.isArray(contract.payments) ? contract.payments.length : 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
function loanAmountLabel(value, asset) {
|
||
|
|
return `${loanFormatAtomic(value)} ${String(asset || "").trim()}`.trim();
|
||
|
|
}
|
||
|
|
|
||
|
|
function addUtcMonths(timestampSeconds, monthsToAdd) {
|
||
|
|
const start = new Date(Number(timestampSeconds || 0) * 1000);
|
||
|
|
if (Number.isNaN(start.getTime())) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
const year = start.getUTCFullYear();
|
||
|
|
const month = start.getUTCMonth() + Number(monthsToAdd || 0);
|
||
|
|
const day = start.getUTCDate();
|
||
|
|
const lastDay = new Date(Date.UTC(year, month + 1, 0)).getUTCDate();
|
||
|
|
return new Date(Date.UTC(
|
||
|
|
year,
|
||
|
|
month,
|
||
|
|
Math.min(day, lastDay),
|
||
|
|
start.getUTCHours(),
|
||
|
|
start.getUTCMinutes(),
|
||
|
|
start.getUTCSeconds()
|
||
|
|
));
|
||
|
|
}
|
||
|
|
|
||
|
|
function loanDueDateForPayment(contract, paymentIndex) {
|
||
|
|
const startTimestamp = Number(contract.start_timestamp || 0);
|
||
|
|
if (!startTimestamp || !paymentIndex) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
const period = String(contract.payment_period || "").trim().toLowerCase();
|
||
|
|
if (period === "daily") {
|
||
|
|
return new Date((startTimestamp + paymentIndex * 86_400) * 1000);
|
||
|
|
}
|
||
|
|
if (period === "weekly") {
|
||
|
|
return new Date((startTimestamp + paymentIndex * 7 * 86_400) * 1000);
|
||
|
|
}
|
||
|
|
if (period === "monthly") {
|
||
|
|
return addUtcMonths(startTimestamp, paymentIndex);
|
||
|
|
}
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
function formatLoanDueDate(date) {
|
||
|
|
if (!(date instanceof Date) || Number.isNaN(date.getTime())) {
|
||
|
|
return "--";
|
||
|
|
}
|
||
|
|
return date.toLocaleString(undefined, {
|
||
|
|
year: "numeric",
|
||
|
|
month: "short",
|
||
|
|
day: "numeric",
|
||
|
|
hour: "numeric",
|
||
|
|
minute: "2-digit",
|
||
|
|
timeZoneName: "short"
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
function nextLoanPaymentDueLabel(contract) {
|
||
|
|
if (String(contract.status || "").toLowerCase() === "inactive") {
|
||
|
|
return "No remaining payments";
|
||
|
|
}
|
||
|
|
const paymentAmount = BigInt(contract.payment_amount || "0");
|
||
|
|
const confirmedPaid = BigInt(contract.confirmed_paid || "0");
|
||
|
|
const paymentNumber = Number(contract.payment_number || 0);
|
||
|
|
if (!paymentNumber || paymentAmount <= 0n) {
|
||
|
|
return "--";
|
||
|
|
}
|
||
|
|
const completedPayments = Number(confirmedPaid / paymentAmount);
|
||
|
|
if (completedPayments >= paymentNumber) {
|
||
|
|
return "No remaining payments";
|
||
|
|
}
|
||
|
|
return formatLoanDueDate(loanDueDateForPayment(contract, completedPayments + 1));
|
||
|
|
}
|
||
|
|
|
||
|
|
function sortedLoans() {
|
||
|
|
const query = String(loansSearch?.value || "").trim().toLowerCase();
|
||
|
|
const roleFilter = loansRoleFilter?.value || "all";
|
||
|
|
|
||
|
|
return loansContracts
|
||
|
|
.filter((contract) => {
|
||
|
|
const role = activeLoanRole(contract);
|
||
|
|
const status = String(contract.status || "").toLowerCase();
|
||
|
|
const searchText = [
|
||
|
|
contract.contract,
|
||
|
|
contract.lender,
|
||
|
|
contract.borrower,
|
||
|
|
contract.loan_asset,
|
||
|
|
contract.collateral,
|
||
|
|
status,
|
||
|
|
role
|
||
|
|
].join(" ").toLowerCase();
|
||
|
|
|
||
|
|
if (roleFilter !== "all" && role !== roleFilter) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
if (activeLoansStatusFilter !== "all" && status !== activeLoansStatusFilter) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
return !query || searchText.includes(query);
|
||
|
|
})
|
||
|
|
.sort((left, right) => Number(right.start_timestamp || 0) - Number(left.start_timestamp || 0));
|
||
|
|
}
|
||
|
|
|
||
|
|
function renderLoanCard(contract) {
|
||
|
|
const role = activeLoanRole(contract);
|
||
|
|
const selected = contract.contract === selectedLoanContractHash;
|
||
|
|
const counterparty = loanCounterparty(contract);
|
||
|
|
const principal = loanAmountLabel(contract.loan_amount, contract.loan_asset);
|
||
|
|
const remaining = loanAmountLabel(contract.remaining_after_pending, contract.loan_asset);
|
||
|
|
const payments = `${loanPaymentCount(contract)} / ${contract.payment_number || 0}`;
|
||
|
|
const nextDue = nextLoanPaymentDueLabel(contract);
|
||
|
|
|
||
|
|
return `
|
||
|
|
<button class="loan-card ${selected ? "active" : ""}" type="button" data-loan-contract="${escapeLoanText(contract.contract)}">
|
||
|
|
<div class="loan-card-top">
|
||
|
|
<div>
|
||
|
|
<strong>Loan ${escapeLoanText(loanShort(contract.contract, 6, 6))}</strong>
|
||
|
|
<span>${activeLoanRole(contract) === "lender" ? "Borrower" : "Lender"} ${escapeLoanText(loanShort(counterparty, 8, 8))}</span>
|
||
|
|
</div>
|
||
|
|
<div class="loan-badges">
|
||
|
|
<span class="role-badge ${role === "lender" ? "lending" : "borrowing"}">${escapeLoanText(loanRoleLabel(role))}</span>
|
||
|
|
<span class="loan-status ${loanStatusClass(contract.status)}">${escapeLoanText(loanStatusLabel(contract.status))}</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="loan-metrics">
|
||
|
|
<div><span>Principal</span><b>${escapeLoanText(principal)}</b></div>
|
||
|
|
<div><span>Remaining</span><b>${escapeLoanText(remaining)}</b></div>
|
||
|
|
<div><span>Payments</span><b>${escapeLoanText(payments)}</b></div>
|
||
|
|
</div>
|
||
|
|
<div class="loan-next-due">
|
||
|
|
<span>Next payment due by:</span>
|
||
|
|
<b>${escapeLoanText(nextDue)}</b>
|
||
|
|
</div>
|
||
|
|
</button>
|
||
|
|
`;
|
||
|
|
}
|
||
|
|
|
||
|
|
function selectedLoanContract() {
|
||
|
|
return loansContracts.find((contract) => contract.contract === selectedLoanContractHash) || null;
|
||
|
|
}
|
||
|
|
|
||
|
|
function detailItem(label, value) {
|
||
|
|
return `<div><span>${escapeLoanText(label)}</span><b>${escapeLoanText(value || "--")}</b></div>`;
|
||
|
|
}
|
||
|
|
|
||
|
|
function renderLoanPaymentHistory(contract) {
|
||
|
|
if (!loanPaymentHistory) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const payments = Array.isArray(contract?.payments) ? contract.payments : [];
|
||
|
|
if (!payments.length) {
|
||
|
|
loanPaymentHistory.innerHTML = '<div class="subtle-state">No confirmed payments recorded for this loan.</div>';
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
loanPaymentHistory.innerHTML = `
|
||
|
|
<table>
|
||
|
|
<thead>
|
||
|
|
<tr>
|
||
|
|
<th>Date</th>
|
||
|
|
<th>Amount</th>
|
||
|
|
<th>Payer</th>
|
||
|
|
<th>Tx Hash</th>
|
||
|
|
</tr>
|
||
|
|
</thead>
|
||
|
|
<tbody>
|
||
|
|
${payments.map((payment) => `
|
||
|
|
<tr>
|
||
|
|
<td>${escapeLoanText(payment.date || "--")}</td>
|
||
|
|
<td>${escapeLoanText(payment.amount || "0")} ${escapeLoanText(contract.loan_asset || "")}</td>
|
||
|
|
<td>${escapeLoanText(loanShort(payment.payer || "", 8, 8))}</td>
|
||
|
|
<td>${escapeLoanText(loanShort(payment.txid || "", 8, 8))}</td>
|
||
|
|
</tr>
|
||
|
|
`).join("")}
|
||
|
|
</tbody>
|
||
|
|
</table>
|
||
|
|
`;
|
||
|
|
}
|
||
|
|
|
||
|
|
function renderLoanDetail() {
|
||
|
|
const contract = selectedLoanContract();
|
||
|
|
if (!loanDetailGrid) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (!contract) {
|
||
|
|
loanDetailGrid.innerHTML = detailItem("Selected Loan", "Select a loan to view details.");
|
||
|
|
if (loanPaymentHistory) {
|
||
|
|
loanPaymentHistory.innerHTML = '<div class="subtle-state">No loan selected.</div>';
|
||
|
|
}
|
||
|
|
loanCopyContractButton?.toggleAttribute("disabled", true);
|
||
|
|
loanMakePaymentButton?.toggleAttribute("disabled", true);
|
||
|
|
loanClaimCollateralButton?.toggleAttribute("disabled", true);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
const role = activeLoanRole(contract);
|
||
|
|
const collateral = loanAmountLabel(contract.collateral_amount, contract.collateral);
|
||
|
|
const payment = loanAmountLabel(contract.payment_amount, contract.loan_asset);
|
||
|
|
const confirmed = loanAmountLabel(contract.confirmed_paid, contract.loan_asset);
|
||
|
|
const pending = loanAmountLabel(contract.pending_paid, contract.loan_asset);
|
||
|
|
const remaining = loanAmountLabel(contract.remaining_after_pending, contract.loan_asset);
|
||
|
|
|
||
|
|
loanDetailGrid.innerHTML = [
|
||
|
|
detailItem("Contract Hash", contract.contract),
|
||
|
|
detailItem("Role", loanRoleLabel(role)),
|
||
|
|
detailItem("Status", loanStatusLabel(contract.status)),
|
||
|
|
detailItem("Principal", loanAmountLabel(contract.loan_amount, contract.loan_asset)),
|
||
|
|
detailItem("Payment Amount", payment),
|
||
|
|
detailItem("Collateral", collateral),
|
||
|
|
detailItem("Payment Schedule", `${contract.payment_number || 0} ${contract.payment_period || "payments"}`),
|
||
|
|
detailItem("Confirmed Paid", confirmed),
|
||
|
|
detailItem("Pending Total", pending),
|
||
|
|
detailItem("Remaining", remaining),
|
||
|
|
detailItem("Lender", contract.lender),
|
||
|
|
detailItem("Borrower", contract.borrower)
|
||
|
|
].join("");
|
||
|
|
|
||
|
|
renderLoanPaymentHistory(contract);
|
||
|
|
loanCopyContractButton?.toggleAttribute("disabled", false);
|
||
|
|
loanMakePaymentButton?.toggleAttribute(
|
||
|
|
"disabled",
|
||
|
|
!(role === "borrower" && String(contract.status || "").toLowerCase() !== "inactive")
|
||
|
|
);
|
||
|
|
loanClaimCollateralButton?.toggleAttribute("disabled", String(contract.status || "").toLowerCase() === "inactive");
|
||
|
|
}
|
||
|
|
|
||
|
|
function renderLoansPage() {
|
||
|
|
if (!loansList) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const rows = sortedLoans();
|
||
|
|
if (!selectedLoanContractHash && rows.length) {
|
||
|
|
selectedLoanContractHash = rows[0].contract;
|
||
|
|
}
|
||
|
|
if (selectedLoanContractHash && !loansContracts.some((contract) => contract.contract === selectedLoanContractHash)) {
|
||
|
|
selectedLoanContractHash = rows[0]?.contract || "";
|
||
|
|
}
|
||
|
|
|
||
|
|
loansList.innerHTML = rows.map(renderLoanCard).join("");
|
||
|
|
if (!walletLoaded) {
|
||
|
|
loansState && (loansState.textContent = "Load a wallet to view loans.");
|
||
|
|
} else if (loansLoadInFlight && !loansContracts.length) {
|
||
|
|
loansState && (loansState.textContent = "Loading loans...");
|
||
|
|
} else if (loansStateMessage) {
|
||
|
|
loansState && (loansState.textContent = loansStateMessage);
|
||
|
|
} else if (!loansContracts.length) {
|
||
|
|
loansState && (loansState.textContent = "No loan contracts found for this wallet.");
|
||
|
|
} else if (!rows.length) {
|
||
|
|
loansState && (loansState.textContent = "No loans match the current filters.");
|
||
|
|
} else {
|
||
|
|
loansState && (loansState.textContent = `${rows.length} loan${rows.length === 1 ? "" : "s"} shown`);
|
||
|
|
}
|
||
|
|
renderLoanDetail();
|
||
|
|
}
|
||
|
|
|
||
|
|
async function loadLoansPage({ force = false } = {}) {
|
||
|
|
const invoke = window.__TAURI__?.core?.invoke;
|
||
|
|
const broadcastNode = activeBroadcastNode();
|
||
|
|
if (!walletLoaded) {
|
||
|
|
loansContracts = [];
|
||
|
|
selectedLoanContractHash = "";
|
||
|
|
loansStateMessage = "Load a wallet to view loans.";
|
||
|
|
renderLoansPage();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (!invoke) {
|
||
|
|
loansContracts = [];
|
||
|
|
selectedLoanContractHash = "";
|
||
|
|
loansStateMessage = "Loan lookup skipped: Tauri invoke is unavailable.";
|
||
|
|
renderLoansPage();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (!broadcastNode) {
|
||
|
|
loansContracts = [];
|
||
|
|
selectedLoanContractHash = "";
|
||
|
|
loansStateMessage = "Loan lookup skipped: no broadcast node is selected.";
|
||
|
|
renderLoansPage();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const key = `${activeWalletAddressShort || activeWalletAddressHex}|${broadcastNode}`;
|
||
|
|
if (!force && loansLoadedForKey === key) {
|
||
|
|
renderLoansPage();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (loansLoadInFlight) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
loansLoadInFlight = true;
|
||
|
|
loansStateMessage = "Loading loans...";
|
||
|
|
renderLoansPage();
|
||
|
|
try {
|
||
|
|
const contracts = await invokeWalletRpcWithBackoff(
|
||
|
|
"loan contracts",
|
||
|
|
() => invoke("loan_contracts", { broadcastNode }),
|
||
|
|
() => currentView === "loans" && activeBroadcastNode() === broadcastNode
|
||
|
|
);
|
||
|
|
loansContracts = Array.isArray(contracts) ? contracts : [];
|
||
|
|
loansLoadedForKey = key;
|
||
|
|
selectedLoanContractHash = loansContracts[0]?.contract || "";
|
||
|
|
loansStateMessage = loansContracts.length
|
||
|
|
? ""
|
||
|
|
: "Loan contract command returned 0 contracts for this wallet.";
|
||
|
|
} catch (error) {
|
||
|
|
loansContracts = [];
|
||
|
|
selectedLoanContractHash = "";
|
||
|
|
loansStateMessage = `Loan contract lookup failed: ${String(error)}`;
|
||
|
|
} finally {
|
||
|
|
loansLoadInFlight = false;
|
||
|
|
renderLoansPage();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function prepareLoansPage({ force = false } = {}) {
|
||
|
|
renderLoansPage();
|
||
|
|
loadLoansPage({ force });
|
||
|
|
}
|
||
|
|
|
||
|
|
function resetLoansPage() {
|
||
|
|
loansContracts = [];
|
||
|
|
loansLoadedForKey = "";
|
||
|
|
loansLoadInFlight = false;
|
||
|
|
selectedLoanContractHash = "";
|
||
|
|
activeLoansStatusFilter = "all";
|
||
|
|
loansStateMessage = "";
|
||
|
|
loansSearch && (loansSearch.value = "");
|
||
|
|
loansRoleFilter && (loansRoleFilter.value = "all");
|
||
|
|
loansStatusButtons.forEach((button) => {
|
||
|
|
button.classList.toggle("active", button.dataset.loansStatusFilter === "all");
|
||
|
|
});
|
||
|
|
renderLoansPage();
|
||
|
|
}
|
||
|
|
|
||
|
|
loansSearch?.addEventListener("input", renderLoansPage);
|
||
|
|
loansRoleFilter?.addEventListener("change", renderLoansPage);
|
||
|
|
loansRefreshButton?.addEventListener("click", () => loadLoansPage({ force: true }));
|
||
|
|
loansCreateButton?.addEventListener("click", () => {
|
||
|
|
setActiveView("send");
|
||
|
|
setActiveTransactionType("Create Loan");
|
||
|
|
});
|
||
|
|
loansStatusButtons.forEach((button) => {
|
||
|
|
button.addEventListener("click", () => {
|
||
|
|
activeLoansStatusFilter = button.dataset.loansStatusFilter || "all";
|
||
|
|
loansStatusButtons.forEach((item) => item.classList.toggle("active", item === button));
|
||
|
|
selectedLoanContractHash = "";
|
||
|
|
renderLoansPage();
|
||
|
|
});
|
||
|
|
});
|
||
|
|
loansList?.addEventListener("click", (event) => {
|
||
|
|
const card = event.target.closest("[data-loan-contract]");
|
||
|
|
if (!card) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
selectedLoanContractHash = card.dataset.loanContract || "";
|
||
|
|
renderLoansPage();
|
||
|
|
});
|
||
|
|
loanCopyContractButton?.addEventListener("click", async () => {
|
||
|
|
const contract = selectedLoanContract();
|
||
|
|
if (!contract) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const copied = typeof writeClipboardText === "function"
|
||
|
|
? await writeClipboardText(contract.contract)
|
||
|
|
: false;
|
||
|
|
if (!copied) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const original = loanCopyContractButton.textContent;
|
||
|
|
loanCopyContractButton.textContent = "Copied";
|
||
|
|
setTimeout(() => {
|
||
|
|
loanCopyContractButton.textContent = original;
|
||
|
|
}, 900);
|
||
|
|
});
|
||
|
|
loanMakePaymentButton?.addEventListener("click", () => {
|
||
|
|
const contract = selectedLoanContract();
|
||
|
|
if (typeof openTransactionType === "function") {
|
||
|
|
openTransactionType("Loan Payment");
|
||
|
|
} else {
|
||
|
|
setActiveView("send");
|
||
|
|
setActiveTransactionType("Loan Payment");
|
||
|
|
showTransactionForm("Loan Payment");
|
||
|
|
}
|
||
|
|
const selectContract = () => {
|
||
|
|
if (contract?.contract && loanPaymentContractSelect) {
|
||
|
|
loanPaymentContractSelect.value = contract.contract;
|
||
|
|
updateLoanPaymentDerivedFields(true);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
if (typeof loadLoanPaymentContracts === "function") {
|
||
|
|
loadLoanPaymentContracts(false).then(selectContract).catch(() => {});
|
||
|
|
} else {
|
||
|
|
setTimeout(selectContract, 0);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
loanClaimCollateralButton?.addEventListener("click", () => {
|
||
|
|
const contract = selectedLoanContract();
|
||
|
|
if (typeof openTransactionType === "function") {
|
||
|
|
openTransactionType("Collateral Claim");
|
||
|
|
} else {
|
||
|
|
setActiveView("send");
|
||
|
|
setActiveTransactionType("Collateral Claim");
|
||
|
|
showTransactionForm("Collateral Claim");
|
||
|
|
}
|
||
|
|
const selectContract = () => {
|
||
|
|
if (contract?.contract && collateralClaimContractSelect) {
|
||
|
|
collateralClaimContractSelect.value = contract.contract;
|
||
|
|
updateCollateralClaimDetails();
|
||
|
|
}
|
||
|
|
};
|
||
|
|
if (typeof loadCollateralClaimContracts === "function") {
|
||
|
|
loadCollateralClaimContracts(false).then(selectContract).catch(() => {});
|
||
|
|
} else {
|
||
|
|
setTimeout(selectContract, 0);
|
||
|
|
}
|
||
|
|
});
|