From df385493584e6bee9adcb46bdb473b987425dbd0 Mon Sep 17 00:00:00 2001 From: Patoo <262265744+patoo0x@users.noreply.github.com> Date: Wed, 15 Jul 2026 12:09:49 -0700 Subject: [PATCH 1/2] =?UTF-8?q?feat(admin):=20adopt=20capability=20nomencl?= =?UTF-8?q?ature=20=E2=80=94=20retire=20Pro/International,=20Merchant=20?= =?UTF-8?q?=E2=86=92=20Business=20(ENG-516)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Account Hub and Account Management adopt the light-headline-status naming: accounts lead with Trial / Verified / Business, with capability badges (Bank payout, USD account) as supporting detail. Upgrade-request levels read as the capability being requested. The GraphQL account fragment now fetches statusHeadline + capabilities from the flash backend (requires lnflash/flash#452), with a level-based fallback for older backends. The change-level dialog uses disambiguated internal labels since L1 and L2 share the Verified headline. Co-Authored-By: Claude Fable 5 --- .../page/account_hub/account_hub.js | 99 ++++++++++++++++--- .../account_management/account_management.js | 20 ++-- admin_panel/api/graphql_client.py | 7 ++ 3 files changed, 104 insertions(+), 22 deletions(-) diff --git a/admin_panel/admin_panel/page/account_hub/account_hub.js b/admin_panel/admin_panel/page/account_hub/account_hub.js index d28c446..f288cab 100644 --- a/admin_panel/admin_panel/page/account_hub/account_hub.js +++ b/admin_panel/admin_panel/page/account_hub/account_hub.js @@ -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 = { @@ -90,6 +125,38 @@ 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) => `${b}`) + .join(" "); +} + const RESULT_STATUS_TONE = { // account statuses (UPPERCASE) — account search results [ACCOUNT_STATUSES.ACTIVE]: "ok", @@ -353,6 +420,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); } @@ -454,6 +522,7 @@ class AccountHub {
+
- +
@@ -570,6 +639,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"), @@ -693,7 +763,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); @@ -896,8 +966,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 = $(`
@@ -934,8 +1004,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)); @@ -1039,8 +1110,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)); @@ -1321,7 +1392,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 = $(` @@ -1359,10 +1430,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, })); diff --git a/admin_panel/admin_panel/page/account_management/account_management.js b/admin_panel/admin_panel/page/account_management/account_management.js index dbc0e1a..e321a5e 100644 --- a/admin_panel/admin_panel/page/account_management/account_management.js +++ b/admin_panel/admin_panel/page/account_management/account_management.js @@ -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 = { @@ -344,10 +348,10 @@ class FlashAccountManager {
diff --git a/admin_panel/api/graphql_client.py b/admin_panel/api/graphql_client.py index 632922e..171faa7 100644 --- a/admin_panel/api/graphql_client.py +++ b/admin_panel/api/graphql_client.py @@ -133,6 +133,13 @@ def execute_and_extract( username npub level + statusHeadline + capabilities { + verified + bankPayout + business + usdAccount + } status title erpParty From 42b4eaed6e4145000650df4ae53416bb1b5b9c04 Mon Sep 17 00:00:00 2001 From: Dread Date: Wed, 15 Jul 2026 22:59:17 -0700 Subject: [PATCH 2/2] fix(admin): prettier format + disambiguate change-level dialog messages (ENG-516) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review follow-ups on #55: - prettier: format account_hub.js (was failing the pre-commit CI hook — the new capabilityBadgesHtml chain wasn't prettier-clean). - Change-level dialog now uses the disambiguated ADMIN_LEVEL_OPTIONS labels in its ERP-party and success messages (was ACCOUNT_LEVEL_LABELS, which renders "Verified" for both L1 and L2 — misleading, since L1 needs no ERP party). Verified: prettier --check clean; node --check OK. Co-Authored-By: Claude Opus 4.8 (1M context) --- admin_panel/admin_panel/page/account_hub/account_hub.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/admin_panel/admin_panel/page/account_hub/account_hub.js b/admin_panel/admin_panel/page/account_hub/account_hub.js index f288cab..30e6df5 100644 --- a/admin_panel/admin_panel/page/account_hub/account_hub.js +++ b/admin_panel/admin_panel/page/account_hub/account_hub.js @@ -152,9 +152,7 @@ function capabilityBadgesHtml(capabilities) { const badges = []; if (capabilities.bankPayout) badges.push("Bank payout"); if (capabilities.usdAccount) badges.push("USD account"); - return badges - .map((b) => `${b}`) - .join(" "); + return badges.map((b) => `${b}`).join(" "); } const RESULT_STATUS_TONE = { @@ -1476,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; @@ -1504,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", },