(() => {
const { useMemo, useState } = React;
function asArray(value) {
return Array.isArray(value) ? value : [];
}
function toNumber(value) {
const parsed = Number(value);
return Number.isFinite(parsed) ? parsed : 0;
}
function asUtcDate(value) {
const text = String(value || "").trim();
if (!text) {
return null;
}
const normalized = text.includes("T") ? text : text.replace(" ", "T");
const date = new Date(`${normalized}Z`);
return Number.isNaN(date.getTime()) ? null : date;
}
function formatDateTime(value, fallback = "Unavailable") {
const date = asUtcDate(value);
if (!date) {
return fallback;
}
return date.toLocaleString("en-GB", {
day: "2-digit",
month: "short",
year: "numeric",
hour: "2-digit",
minute: "2-digit",
timeZone: "UTC"
}) + " UTC";
}
function formatDate(value, fallback = "Unavailable") {
const date = asUtcDate(value);
if (!date) {
return fallback;
}
return date.toLocaleDateString("en-GB", {
day: "2-digit",
month: "short",
year: "numeric",
timeZone: "UTC"
});
}
function formatMoney(value, currency = "USD", signed = false) {
const amount = toNumber(value);
const prefix = signed && amount > 0 ? "+" : signed && amount < 0 ? "-" : "";
return `${String(currency || "USD").toUpperCase()} ${prefix}${Math.abs(amount).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2
})}`;
}
function formatNumber(value, options = {}) {
return toNumber(value).toLocaleString(undefined, options);
}
function formatPips(value, signed = false) {
const amount = Number(value);
if (!Number.isFinite(amount)) {
return "Not matched";
}
const prefix = signed && amount > 0 ? "+" : signed && amount < 0 ? "-" : "";
return `${prefix}${Math.abs(amount).toLocaleString(undefined, {
minimumFractionDigits: 1,
maximumFractionDigits: 1
})} pips`;
}
function badgeClassForNumber(value) {
const amount = toNumber(value);
if (amount < 0) {
return "badge-danger";
}
if (amount === 0) {
return "badge-gold";
}
return "";
}
function accountKeyFromItem(item) {
const login = String(item && item.account_login ? item.account_login : "").trim() || "unknown";
const server = String(item && item.server_name ? item.server_name : "").trim() || "unknown";
return `${login}|${server}`;
}
function computeSummary(items) {
const summary = {
total_trades: asArray(items).length,
closed_trades: 0,
winning_trades: 0,
losing_trades: 0,
profit: 0,
loss: 0,
pips_won: 0,
pips_lost: 0,
net_total: 0
};
asArray(items).forEach((item) => {
const net = toNumber(item.net_result);
const pips = Number(item.pips);
const entry = String(item.deal_entry || "").toLowerCase();
summary.net_total += net;
if (net > 0) {
summary.profit += net;
summary.winning_trades += 1;
} else if (net < 0) {
summary.loss += Math.abs(net);
summary.losing_trades += 1;
}
if (["out", "out_by", "inout"].includes(entry)) {
summary.closed_trades += 1;
}
if (Number.isFinite(pips)) {
if (pips > 0) {
summary.pips_won += pips;
} else if (pips < 0) {
summary.pips_lost += Math.abs(pips);
}
}
});
return summary;
}
function computeRunningChartPoints(items) {
let running = 0;
return asArray(items)
.slice()
.sort((left, right) => {
const leftTime = asUtcDate(left.deal_time);
const rightTime = asUtcDate(right.deal_time);
const leftValue = leftTime ? leftTime.getTime() : 0;
const rightValue = rightTime ? rightTime.getTime() : 0;
if (leftValue === rightValue) {
return toNumber(left.id) - toNumber(right.id);
}
return leftValue - rightValue;
})
.map((item) => {
running += toNumber(item.net_result);
return {
label: formatDateTime(item.deal_time, "Recent trade"),
symbol: String(item.symbol || "").trim(),
value: Number(running.toFixed(2))
};
});
}
function buildChartGeometry(points, width = 860, height = 250) {
const list = asArray(points);
if (!list.length) {
return null;
}
const padX = 28;
const padTop = 18;
const padBottom = 30;
const values = list.map((point) => toNumber(point.value));
let minValue = Math.min(...values);
let maxValue = Math.max(...values);
if (Math.abs(maxValue - minValue) < 0.01) {
minValue -= 1;
maxValue += 1;
}
const plotWidth = width - padX * 2;
const plotHeight = height - padTop - padBottom;
const range = maxValue - minValue || 1;
const coords = list.map((point, index) => {
const x = padX + plotWidth * (index / Math.max(list.length - 1, 1));
const y = padTop + ((maxValue - toNumber(point.value)) / range) * plotHeight;
return `${x.toFixed(2)},${y.toFixed(2)}`;
});
const zeroLineY = minValue <= 0 && maxValue >= 0
? padTop + ((maxValue - 0) / range) * plotHeight
: null;
return {
width,
height,
padX,
polyline: coords.join(" "),
area: `${padX},${height - padBottom} ${coords.join(" ")} ${width - padX},${height - padBottom}`,
maxLabel: formatMoney(maxValue),
minLabel: formatMoney(minValue),
startLabel: String(list[0].label || "Recent history"),
endLabel: String(list[list.length - 1].label || "Latest result"),
zeroLineY: zeroLineY == null ? null : zeroLineY.toFixed(2)
};
}
function NavLink({ item, active }) {
return (
{item.label}
);
}
function PortalShell({ data }) {
const [menuOpen, setMenuOpen] = useState(false);
const navItems = asArray(data.navItems);
return (
Switch across the full portal from the top menu and keep mobile navigation open when you need it.
Jump between pricing, partners, training, and the 7-day demo registration area from one responsive menu.