Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 85 additions & 16 deletions admin_panel/admin_panel/page/account_hub/account_hub.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,46 @@ const ACCOUNT_STATUSES = {
CLOSED: "CLOSED",
};

// ENG-516 nomenclature: Pro and International are retired, Merchant is now
// Business, and levels are internal. The account leads with one headline word
// (Trial → Verified → Business, the "light headline status" decision) with
// capability badges as supporting detail. L1 and L2 both read "Verified" —
// bank payout is a badge, not a tier.
const ACCOUNT_LEVEL_LABELS = {
[ACCOUNT_LEVELS.ZERO]: "Trial",
[ACCOUNT_LEVELS.ONE]: "Personal",
[ACCOUNT_LEVELS.TWO]: "Pro",
[ACCOUNT_LEVELS.THREE]: "Merchant",
[ACCOUNT_LEVELS.ONE]: "Verified",
[ACCOUNT_LEVELS.TWO]: "Verified",
[ACCOUNT_LEVELS.THREE]: "Business",
};

// On an upgrade request, requested_level reads as the capability being
// requested, not a tier the user picked.
const REQUESTED_LEVEL_LABELS = {
[ACCOUNT_LEVELS.ZERO]: "Trial",
[ACCOUNT_LEVELS.ONE]: "Verified",
[ACCOUNT_LEVELS.TWO]: "Bank payout",
[ACCOUNT_LEVELS.THREE]: "Business",
};

const STATUS_HEADLINE_LABELS = {
TRIAL: "Trial",
VERIFIED: "Verified",
BUSINESS: "Business",
};

const STATUS_HEADLINE_BADGES = {
TRIAL: "badge-trial",
VERIFIED: "badge-personal",
BUSINESS: "badge-merchant",
};

// Internal levels for the admin change-level action — disambiguated with the
// level number because L1 and L2 share the "Verified" headline.
const ADMIN_LEVEL_OPTIONS = {
[ACCOUNT_LEVELS.ZERO]: "Trial (L0)",
[ACCOUNT_LEVELS.ONE]: "Verified (L1)",
[ACCOUNT_LEVELS.TWO]: "Verified + Bank payout (L2)",
[ACCOUNT_LEVELS.THREE]: "Business (L3)",
};

const ACCOUNT_LEVEL_BADGES = {
Expand Down Expand Up @@ -90,6 +125,36 @@ function getLevelLabel(level) {
return ACCOUNT_LEVEL_LABELS[level] || level;
}

function getRequestedLevelLabel(level) {
return REQUESTED_LEVEL_LABELS[level] || level;
}

// Headline status straight from the backend when available (ENG-516);
// falls back to the stored level for older backends.
function getHeadlineLabel(account) {
if (account.statusHeadline) {
return STATUS_HEADLINE_LABELS[account.statusHeadline] || account.statusHeadline;
}
return getLevelLabel(account.level);
}

function getHeadlineBadge(account) {
if (account.statusHeadline) {
return STATUS_HEADLINE_BADGES[account.statusHeadline] || "badge-trial";
}
return getLevelBadge(account.level);
}

// Supporting capability badges next to the headline. verified/business are
// already the headline; bankPayout and usdAccount are the orthogonal extras.
function capabilityBadgesHtml(capabilities) {
if (!capabilities) return "";
const badges = [];
if (capabilities.bankPayout) badges.push("Bank payout");
if (capabilities.usdAccount) badges.push("USD account");
return badges.map((b) => `<span class="ah-badge badge-capability">${b}</span>`).join(" ");
}

const RESULT_STATUS_TONE = {
// account statuses (UPPERCASE) — account search results
[ACCOUNT_STATUSES.ACTIVE]: "ok",
Expand Down Expand Up @@ -353,6 +418,7 @@ class AccountHub {
.badge-personal { background: var(--ah-accent-soft); color: var(--ah-accent-ink); opacity: 0.85; }
.badge-business { background: var(--ah-accent-soft); color: var(--ah-accent-ink); }
.badge-merchant { background: var(--ah-accent); color: #fff; }
.badge-capability { background: var(--ah-line-soft); color: var(--ah-ink2); }
.badge-pending { background: var(--ah-warn-bg); color: var(--ah-warn); }
.badge-approved { background: var(--ah-accent-soft); color: var(--ah-accent-ink); }
.badge-rejected { background: var(--ah-serious-bg); color: var(--ah-serious); }
Expand Down Expand Up @@ -454,6 +520,7 @@ class AccountHub {
<div class="ah-ident-top">
<span class="detail-username-display ah-ident-name"></span>
<span class="ah-badge detail-level-badge"></span>
<span class="detail-cap-badges"></span>
<span class="ah-badge detail-status-badge"></span>
<div class="ah-ident-actions">
<button class="ah-btn ah-btn-sm ah-btn-primary btn-change-level">
Expand All @@ -479,7 +546,7 @@ class AccountHub {
<button class="ah-tab active" data-tab="overview">Overview</button>
<button class="ah-tab" data-tab="wallets">Wallets</button>
<button class="ah-tab" data-tab="documents">Documents</button>
<button class="ah-tab" data-tab="merchant">Merchant</button>
<button class="ah-tab" data-tab="merchant">Business</button>
<button class="ah-tab" data-tab="upgrade">Upgrade History</button>
</div>

Expand Down Expand Up @@ -570,6 +637,7 @@ class AccountHub {
detailContent: main.find(".ah-detail-content"),
detailUsername: main.find(".detail-username-display"),
detailLevelBadge: main.find(".detail-level-badge"),
detailCapBadges: main.find(".detail-cap-badges"),
detailStatusBadge: main.find(".detail-status-badge"),
identMeta: main.find(".detail-ident-meta"),
identBalances: main.find(".detail-ident-balances"),
Expand Down Expand Up @@ -693,7 +761,7 @@ class AccountHub {
[account.phone_number, account.email].filter(Boolean).join(" · ") || "—";
const level = account.requested_level || "ZERO";
const initial = (displayName || "?")[0].toUpperCase();
const levelLabel = getLevelLabel(level);
const levelLabel = getRequestedLevelLabel(level);
const levelBadge = getLevelBadge(level);
const dotHtml = statusDotHtml(account.status);

Expand Down Expand Up @@ -896,8 +964,8 @@ class AccountHub {
account.owner?.email?.address ||
account.username ||
account.id;
const levelLabel = getLevelLabel(account.level);
const levelBadge = getLevelBadge(account.level);
const levelLabel = getHeadlineLabel(account);
const levelBadge = getHeadlineBadge(account);

const item = $(`
<div class="ah-result-item" data-uuid="${account.uuid}">
Expand Down Expand Up @@ -934,8 +1002,9 @@ class AccountHub {
// Update header
this.$.detailUsername.text(account.username || "Unknown");
this.$.detailLevelBadge
.text(getLevelLabel(account.level))
.attr("class", "ah-badge " + getLevelBadge(account.level));
.text(getHeadlineLabel(account))
.attr("class", "ah-badge " + getHeadlineBadge(account));
this.$.detailCapBadges.html(capabilityBadgesHtml(account.capabilities));
this.$.detailStatusBadge
.text(getStatusLabel(account.status))
.attr("class", "ah-badge " + getStatusBadge(account.status));
Expand Down Expand Up @@ -1039,8 +1108,8 @@ class AccountHub {

// Account State
this.$.ovLevelBadge
.text(getLevelLabel(account.level))
.attr("class", "ah-badge " + getLevelBadge(account.level));
.text(getHeadlineLabel(account))
.attr("class", "ah-badge " + getHeadlineBadge(account));
this.$.ovStatusBadge
.text(getStatusLabel(account.status))
.attr("class", "ah-badge " + getStatusBadge(account.status));
Expand Down Expand Up @@ -1321,7 +1390,7 @@ class AccountHub {
requests.forEach((r) => {
const statusLabel = getStatusLabel(r.status || "PENDING");
const statusBadge = getStatusBadge(r.status || "PENDING");
const levelLabel = getLevelLabel(r.requested_level);
const levelLabel = getRequestedLevelLabel(r.requested_level);
const levelBadge = getLevelBadge(r.requested_level);

const row = $(`
Expand Down Expand Up @@ -1359,10 +1428,10 @@ class AccountHub {
const account = this.current_account;

const currentLevel = account.level;
const options = Object.keys(ACCOUNT_LEVEL_LABELS)
const options = Object.keys(ADMIN_LEVEL_OPTIONS)
.filter((k) => k !== currentLevel)
.map((k) => ({
label: ACCOUNT_LEVEL_LABELS[k],
label: ADMIN_LEVEL_OPTIONS[k],
value: k,
}));

Expand Down Expand Up @@ -1405,7 +1474,7 @@ class AccountHub {
title: "ERP Party Required",
indicator: "orange",
message: `${
ACCOUNT_LEVEL_LABELS[selectedOption.value]
ADMIN_LEVEL_OPTIONS[selectedOption.value]
} accounts require an ERP party, and this account has none. Approve an account upgrade request instead — that flow creates the ERP Customer, Address, and Bank Account records.`,
});
return;
Expand Down Expand Up @@ -1433,7 +1502,7 @@ class AccountHub {
frappe.show_alert(
{
message: `Account level updated to ${
ACCOUNT_LEVEL_LABELS[selectedOption.value]
ADMIN_LEVEL_OPTIONS[selectedOption.value]
}`,
indicator: "green",
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,15 @@ const AccountStatus = {
CLOSED: "Closed",
};

// ENG-516 nomenclature: Pro and International are retired, Merchant is now
// Business, L1 is Verified. Levels are internal, so this admin page shows the
// new name with the level number for disambiguation (L1 and L2 share the
// "Verified" headline — L2 adds the bank-payout capability).
const ACCOUNT_LEVEL_MAP = {
[AccountLevels.TRIAL]: "Trial",
[AccountLevels.PERSONAL]: "Personal",
[AccountLevels.PRO]: "Pro",
[AccountLevels.MERCHANT]: "Merchant",
[AccountLevels.TRIAL]: "Trial (L0)",
[AccountLevels.PERSONAL]: "Verified (L1)",
[AccountLevels.PRO]: "Verified + Bank payout (L2)",
[AccountLevels.MERCHANT]: "Business (L3)",
};

const LEVEL_BADGE_MAP = {
Expand Down Expand Up @@ -344,10 +348,10 @@ class FlashAccountManager {
</select>
<select id="filter-level" class="modern-search-input modern-search-select">
<option value="">Requested Level (All)</option>
<option value="${AccountLevels.TRIAL}">Trial</option>
<option value="${AccountLevels.PERSONAL}">Personal</option>
<option value="${AccountLevels.PRO}">Pro</option>
<option value="${AccountLevels.MERCHANT}">Merchant</option>
<option value="${AccountLevels.TRIAL}">Trial (L0)</option>
<option value="${AccountLevels.PERSONAL}">Verified (L1)</option>
<option value="${AccountLevels.PRO}">Verified + Bank payout (L2)</option>
<option value="${AccountLevels.MERCHANT}">Business (L3)</option>
</select>
</div>
</div>
Expand Down
7 changes: 7 additions & 0 deletions admin_panel/api/graphql_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,13 @@ def execute_and_extract(
username
npub
level
statusHeadline
capabilities {
verified
bankPayout
business
usdAccount
}
status
title
erpParty
Expand Down
Loading