/* * This file is part of Linode Manager Classic. * * Linode Manager Classic is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Linode Manager Classic is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Linode Manager Classic. If not, see . */ import { settings, elements, apiDelete, apiGet, apiPost, parseParams, setupHeader, timeString } from "/global.js"; (function() { // Element names specific to this page elements.active = "active"; elements.address = "address"; elements.balance = "balance"; elements.balanceNegative = "balance-negative"; elements.balancePositive = "balance-positive"; elements.balanceStatus = "balance-status"; elements.billingActivity = "billing-activity"; elements.current = "current"; elements.defaultPrefix = "default-payment-"; elements.deletePrefix = "delete-payment-"; elements.email = "email"; elements.gdpr = "gdpr"; elements.gdprDate = "gdpr-date"; elements.info = "info"; elements.lmcRow = "lmc-tr1"; elements.lmcRowAlt = "lmc-tr2"; elements.lmcRowStandard = "lmc-tr3"; elements.managed = "managed"; elements.managedButton = "managed-button"; elements.pay = "pay"; elements.paymentMethods = "payment-methods"; elements.promotions = "promotions"; elements.promotionsTable = "promotions-table"; elements.uninvoiced = "uninvoiced"; elements.uninvoicedBalance = "uninvoiced-balance"; // Data received from API calls var data = {}; data.account = {}; data.invoices = []; data.numLinodes = 0; data.paymentMethods = []; data.payments = []; // Static references to UI elements var ui = {}; ui.active = {}; ui.address = {}; ui.balance = {}; ui.balanceStatus = {}; ui.billingActivity = {}; ui.current = {}; ui.email = {}; ui.gdpr = []; ui.gdprDate = {}; ui.managed = {}; ui.managedButton = {}; ui.pay = {}; ui.paymentMethods = {}; ui.promotions = []; ui.promotionsTable = {}; ui.uninvoiced = {}; ui.uninvoicedBalance = {}; // Temporary state var state = {}; state.haveInvoices = false; state.havePayments = false; // Creates a row for the payment methods table var createMethodRow = function(method) { var typeNames = { "credit_card": "Credit Card", "google_pay": "Google Pay", "paypal": "PayPal" }; var row = document.createElement("tr"); row.className = elements.lmcRowStandard; var type = document.createElement("td"); if (typeNames[method.type]) type.innerHTML = typeNames[method.type]; else type.innerHTML = method.type; row.appendChild(type); var details = document.createElement("td"); if (method.type == "credit_card" || method.type == "google_pay") { var ccInfo = document.createElement("span"); ccInfo.innerHTML = method.data.card_type + " xxxxxxxxxxxx" + method.data.last_four + " Exp: " + method.data.expiry; var br = document.createElement("br"); details.appendChild(ccInfo); details.appendChild(br); var monthYear = method.data.expiry.split("/"); var expireDate = new Date(parseInt(monthYear[1]), parseInt(monthYear[0]), 0); var now = new Date(); if (expireDate - now > 0) { var duration = document.createElement("span"); duration.innerHTML = "Expires " + timeString(now - expireDate, true); details.appendChild(duration); } else { var expired = document.createElement("strong"); expired.innerHTML = "Expired!"; details.appendChild(expired); } } else if (method.type == "paypal") { var email = document.createElement("span"); email.innerHTML = method.data.email; details.appendChild(email); } row.appendChild(details); var options = document.createElement("td"); if (method.is_default) { var defaultMsg = document.createElement("strong"); defaultMsg.innerHTML = "This is the default payment method"; options.appendChild(defaultMsg); } else { var defaultLink = document.createElement("a"); defaultLink.id = elements.defaultPrefix + method.id; defaultLink.href = "#"; defaultLink.innerHTML = "Make default"; defaultLink.addEventListener("click", defaultMethod); var separator = document.createElement("span"); separator.innerHTML = " | "; var deleteLink = document.createElement("a"); deleteLink.id = elements.deletePrefix + method.id; deleteLink.href = "#"; deleteLink.innerHTML = "Delete"; deleteLink.addEventListener("click", deleteMethod); options.appendChild(defaultLink); options.appendChild(separator); options.appendChild(deleteLink); } row.appendChild(options); return row; }; // Creates a row for the promotion table var createPromoRow = function(promo) { var row = document.createElement("tr"); row.className = elements.lmcRowStandard; var name = document.createElement("td"); name.innerHTML = promo.summary; row.appendChild(name); var details = document.createElement("td"); var line1 = document.createElement("span"); var expire = new Date(promo.expire_dt + "Z"); line1.innerHTML = "$" + promo.credit_remaining + " remaining. Exp: " + expire.toLocaleDateString(); var br = document.createElement("br"); var line2 = document.createElement("span"); line2.innerHTML = "($" + promo.this_month_credit_remaining + " remaining this month. Monthly cap: $" + promo.credit_monthly_cap + ")"; details.appendChild(line1); details.appendChild(br); details.appendChild(line2); row.appendChild(details); var description = document.createElement("td"); description.className = elements.info; description.innerHTML = promo.description; row.appendChild(description); return row; }; // Creates a row for the recent activity table var createRecentRow = function(recent, alt) { var row = document.createElement("tr"); if (alt) row.className = elements.lmcRowAlt; else row.className = elements.lmcRow; var dateCell = document.createElement("td"); var date = document.createElement("span"); var recentDate = new Date(recent.date + "Z"); var now = new Date(); date.innerHTML = recentDate.toLocaleDateString(); var br = document.createElement("br"); var ago = document.createElement("span"); ago.className = elements.info; ago.innerHTML = timeString(now - recentDate, true); dateCell.appendChild(date); dateCell.appendChild(br); dateCell.appendChild(ago); row.appendChild(dateCell); if (recent.label) { // Invoice stuff var linkCell = document.createElement("td"); var link = document.createElement("a"); link.href = "/account/invoice?iid=" + recent.id; link.innerHTML = recent.label; linkCell.appendChild(link); row.appendChild(linkCell); var total = document.createElement("td"); if (recent.total < 0) total.innerHTML = "($" + (-recent.total).toFixed(2) + ")"; else total.innerHTML = "$" + recent.total.toFixed(2); row.appendChild(total); } else { // Payment stuff var thanks = document.createElement("td"); thanks.innerHTML = "Payment. Thank you"; row.appendChild(thanks); var total = document.createElement("td"); if (total > 0) total.innerHTML = "($" + recent.total.toFixed(2) + ")"; else total.innerHTML = "$" + (-recent.total).toFixed(2); row.appendChild(total); } return row; }; // Handler for making a payment method default var defaultMethod = function(event) { var id = event.currentTarget.id.slice(elements.defaultPrefix.length); if (!confirm("Make this the default payment method?")) return; apiPost("/account/payment-methods/" + id + "/make-default", {}, function(response) { location.reload(); }); }; // Handler for deleting a payment method var deleteMethod = function(event) { var id = event.currentTarget.id.slice(elements.deletePrefix.length); if (!confirm("Delete this payment method?")) return; apiDelete("/account/payment-methods/" + id, function(response) { location.reload(); }); }; // Callback for account details API call var displayAccount = function(response) { data.account = response; // Contact info var br = document.createElement("br"); if (data.account.company.length) { var company = document.createElement("span"); company.innerHTML = data.account.company; ui.address.appendChild(company); ui.address.appendChild(br); } var name = document.createElement("span"); name.innerHTML = data.account.first_name + " " + data.account.last_name; ui.address.appendChild(name); ui.address.appendChild(br.cloneNode(true)); var address1 = document.createElement("span"); address1.innerHTML = data.account.address_1; ui.address.appendChild(address1); ui.address.appendChild(br.cloneNode(true)); if (data.account.address_2.length) { var address2 = document.createElement("span"); address2.innerHTML = data.account.address_2; ui.address.appendChild(address2); ui.address.appendChild(br.cloneNode(true)); } var address3 = document.createElement("span"); address3.innerHTML = data.account.city + ", " + data.account.state + " " + data.account.zip; ui.address.appendChild(address3); // Email ui.email.innerHTML = data.account.email; // Account balance if (data.account.balance == 0) { ui.balance.className = elements.balancePositive; ui.current.innerHTML = "Your account is current."; } else if (data.account.balance < 0) { ui.balance.className = elements.balancePositive; ui.balanceStatus.innerHTML = " credit"; ui.current.innerHTML = "This will be applied towards future invoices."; data.account.balance = -data.account.balance; } else { ui.balance.className = elements.balanceNegative; ui.balanceStatus.innerHTML = " due "; ui.pay.href += "?amount=" + (-data.account.balance); ui.pay.style.display = "initial"; ui.current.innerHTML = "Please pay now to avoid any service interruptions."; } ui.balance.innerHTML = "$" + data.account.balance.toFixed(2); ui.uninvoicedBalance.innerHTML = "$" + data.account.balance_uninvoiced.toFixed(2); if (data.account.balance_uninvoiced > 0) ui.uninvoiced.style.display = "table-row"; // Promotions if (data.account.active_promotions.length) { for (var i = 0; i < ui.promotions.length; i++) ui.promotions[i].style.display = "table-row-group"; } for (var i = 0; i < data.account.active_promotions.length; i++) ui.promotionsTable.appendChild(createPromoRow(data.account.active_promotions[i])); var active = new Date(data.account.active_since + "Z"); ui.active.innerHTML = active.toLocaleDateString(); }; // Callback for invoices API call var displayInvoices = function(response) { data.invoices = data.invoices.concat(response.data); // Request the next page if there are more if (response.page != response.pages) { apiGet("/account/invoices?page=" + (response.page + 1), displayInvoices, null); return; } state.haveInvoices = true; if (state.havePayments) displayRecent(); }; // Callback for linodes API call var displayLinodes = function(response) { data.numLinodes = response.results; ui.managedButton.disabled = false; }; // Callback for payment methods API call var displayMethods = function(response) { data.paymentMethods = data.paymentMethods.concat(response.data); // Request the next page if there are more if (response.page != response.pages) { apiGet("/account/payment-methods?page=" + (response.page + 1), displayMethods, null); return; } for (var i = 0; i < data.paymentMethods.length; i++) ui.paymentMethods.appendChild(createMethodRow(data.paymentMethods[i])); }; // Callback for payments API call var displayPayments = function(response) { data.payments = data.payments.concat(response.data); // Request the next page if there are more if (response.page != response.pages) { apiGet("/account/payments?page=" + (response.page + 1), displayPayments, null); return; } state.havePayments = true; if (state.haveInvoices) displayRecent(); }; // Populates table with recent invoices and payments var displayRecent = function() { // Combine to single array and sort by date var recent = data.invoices.concat(data.payments); recent.sort(function(a, b) { var aDate = new Date(a.date + "Z"); var bDate = new Date(b.date + "Z"); return aDate - bDate; }); // Insert items into table for (var i = recent.length - 1, count = 0; i >= 0, count < 4; i--, count++) ui.billingActivity.appendChild(createRecentRow(recent[i], ui.billingActivity.children.length % 2)); }; // Callback for account settings API call var displaySettings = function(response) { // Show managed signup if not managed if (!response.managed) ui.managed.style.display = "table"; }; // Click handler for managed button var handleManaged = function(event) { if (event.currentTarget.disabled) return; if (!confirm("Linode Managed costs an additional $100/mo per Linode. This will increase your projected monthly bill by $" + (data.numLinodes * 100) + ". Are you sure?")) return; apiPost("/account/settings/managed-enable", {}, function(response) { location.reload(); }); }; // Initial setup var setup = function() { // Parse URL parameters data.params = parseParams(); setupHeader(); // Get element references ui.active = document.getElementById(elements.active); ui.address = document.getElementById(elements.address); ui.balance = document.getElementById(elements.balance); ui.balanceStatus = document.getElementById(elements.balanceStatus); ui.billingActivity = document.getElementById(elements.billingActivity); ui.current = document.getElementById(elements.current); ui.email = document.getElementById(elements.email); ui.gdpr = document.getElementsByClassName(elements.gdpr); ui.gdprDate = document.getElementById(elements.gdprDate); ui.managed = document.getElementById(elements.managed); ui.managedButton = document.getElementById(elements.managedButton); ui.pay = document.getElementById(elements.pay); ui.paymentMethods = document.getElementById(elements.paymentMethods); ui.promotions = document.getElementsByClassName(elements.promotions); ui.promotionsTable = document.getElementById(elements.promotionsTable); ui.uninvoiced = document.getElementById(elements.uninvoiced); ui.uninvoicedBalance = document.getElementById(elements.uninvoicedBalance); // Register event handlers ui.managedButton.addEventListener("click", handleManaged); // Get data from API apiGet("/account", displayAccount, null); apiGet("/account/settings", displaySettings, null); apiGet("/account/invoices", displayInvoices, null); apiGet("/account/payments", displayPayments, null); apiGet("/account/payment-methods", displayMethods, null); apiGet("/linode/instances", displayLinodes, null); }; // Attach onload handler window.addEventListener("load", setup); })();