@targlobal/mission-control 1.5.7 → 1.5.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.ts CHANGED
@@ -506,7 +506,7 @@ const showDashboard = async () => {
506
506
  console.log(chalk.cyan('┌─────────────────────────────┐'));
507
507
  console.log(chalk.cyan('│') + ` ${chalk.bgCyan.black('TASKS')} ${chalk.bold.white(String(stats.my_tasks).padStart(3))} ${chalk.bgRed.white('CRIT')} ${chalk.bold.red(String(stats.critical_count).padStart(2))} ${chalk.bgYellow.black('OVR')} ${chalk.bold.yellow(String(stats.overdue_tasks).padStart(2))} ` + chalk.cyan('│'));
508
508
  console.log(chalk.cyan('├─────────────────────────────┤'));
509
- console.log(chalk.cyan('│') + ` ${chalk.cyan('[1]')}Tasks ${chalk.cyan('[2]')}New ${chalk.cyan('[3]')}Pay ${chalk.cyan('[4]')}Urg ${chalk.cyan('[5]')}Q ` + chalk.cyan('│'));
509
+ console.log(chalk.cyan('│') + ` ${chalk.cyan('[1]')}Tasks ${chalk.cyan('[2]')}New ${chalk.cyan('[3]')}Pay ${chalk.cyan('[4]')}Urg ${chalk.cyan('[5]')}Smart ` + chalk.cyan('│'));
510
510
  console.log(chalk.cyan('└─────────────────────────────┘'));
511
511
  console.log('');
512
512
 
@@ -528,7 +528,7 @@ const showDashboard = async () => {
528
528
  console.log(chalk.cyan('║') + ' ' + chalk.cyan('║'));
529
529
  console.log(chalk.cyan('╠══════════════════════════════════════════════════════════════════════╣'));
530
530
  console.log(chalk.cyan('║') + chalk.dim(' Quick Actions: ') + chalk.cyan('║'));
531
- console.log(chalk.cyan('║') + ` ${chalk.cyan('[1]')} My Tasks ${chalk.cyan('[2]')} New Task ${chalk.cyan('[3]')} Payouts ${chalk.cyan('[4]')} Urgent ${chalk.cyan('[5]')} Queue ` + chalk.cyan('║'));
531
+ console.log(chalk.cyan('║') + ` ${chalk.cyan('[1]')} My Tasks ${chalk.cyan('[2]')} New Task ${chalk.cyan('[3]')} Payouts ${chalk.cyan('[4]')} Urgent ${chalk.cyan('[5]')} Smart ` + chalk.cyan('║'));
532
532
  console.log(chalk.cyan('║') + ' ' + chalk.cyan('║'));
533
533
  console.log(chalk.cyan('╚══════════════════════════════════════════════════════════════════════╝'));
534
534
  console.log('');
@@ -549,78 +549,24 @@ const showDashboard = async () => {
549
549
  }
550
550
  };
551
551
 
552
- const runSmartQueueDirect = async () => {
553
- setTitle('Smart Queue');
554
- const spinner = ora('Loading queue...').start();
552
+ const runSuperSmartDirect = async () => {
553
+ setTitle('Super Smart');
554
+ const spinner = ora('Loading all pending payouts...').start();
555
555
 
556
556
  try {
557
- // Fetch metrics + all pending payouts in parallel
558
- const [metricsRes, payoutsRes, tarPayRes] = await Promise.all([
559
- api.getPayoutMetrics(),
557
+ // Fetch all pending payouts in parallel across all plans
558
+ const [payoutsRes, tarPayRes] = await Promise.all([
560
559
  api.listPayouts({ status: 'pending', limit: 500 }),
561
560
  api.getTarPayPayouts({ status: 'pending_review', limit: 500 }),
562
561
  ]);
563
562
 
564
- const metrics: PayoutMetrics = metricsRes.data;
565
563
  const payouts: Payout[] = payoutsRes.data || [];
566
564
  const tarPayPayouts: TarPayPayout[] = tarPayRes.results || [];
567
565
 
568
566
  spinner.stop();
569
567
 
570
- // Build plan summary from regular payouts
571
- const byPlan: Record<string, number> = {};
572
- payouts.forEach((p) => {
573
- byPlan[p.plan] = (byPlan[p.plan] || 0) + 1;
574
- });
575
- // Add TarPay payouts
576
- tarPayPayouts.forEach((p) => {
577
- byPlan[p.plan] = (byPlan[p.plan] || 0) + 1;
578
- });
579
-
580
- const totalCount = payouts.length + tarPayPayouts.length;
581
- const planSummary = Object.entries(byPlan)
582
- .map(([plan, count]) => `${(PLAN_ICONS[plan] || '📋')} ${plan.toUpperCase()} ${count}`)
583
- .join(' ');
584
-
585
- // Show overview
586
- console.log('');
587
- console.log(chalk.cyan('╔═══ SMART QUEUE ═══╗'));
588
- console.log(chalk.cyan('║') + ` Pending: ${chalk.bold.yellow(String(totalCount))} payouts (${chalk.green('$' + metrics.pending_amount.toLocaleString())})` );
589
- console.log(chalk.cyan('║') + ` ${planSummary}`);
590
- console.log(chalk.cyan('╚═══════════════════╝'));
591
- console.log('');
592
-
593
- if (totalCount === 0) {
594
- console.log(chalk.green(' ✓ No pending payouts\n'));
595
- return;
596
- }
597
-
598
- // Step 1: Select plan (with back option)
599
- const planChoices = [
600
- { name: chalk.dim('← Back'), value: '__back__' },
601
- ...Object.entries(byPlan).map(([plan, count]) => ({
602
- name: `${PLAN_ICONS[plan] || '📋'} ${plan.charAt(0).toUpperCase() + plan.slice(1)} (${count})`,
603
- value: plan,
604
- })),
605
- { name: '📋 All Plans', value: '__all__' },
606
- ];
607
-
608
- const { selectedPlan } = await inquirer.prompt([
609
- {
610
- type: 'list',
611
- name: 'selectedPlan',
612
- message: 'Select plan:',
613
- choices: planChoices,
614
- },
615
- ]);
616
-
617
- if (selectedPlan === '__back__') return;
618
-
619
- const isTarPayPlan = selectedPlan === 'christmas';
620
- const planFilter = selectedPlan === '__all__' ? undefined : selectedPlan;
621
-
622
- // Unified payout items
623
- interface SmartPayoutItem {
568
+ // Unified payout item with plan + source tracking
569
+ interface SuperSmartItem {
624
570
  id: number;
625
571
  user_email: string;
626
572
  crypto: string;
@@ -628,63 +574,66 @@ const runSmartQueueDirect = async () => {
628
574
  remaining_amount: number;
629
575
  has_partial_payments: boolean;
630
576
  paid_percentage: number;
631
- }
632
-
633
- let smartItems: SmartPayoutItem[] = [];
634
-
635
- if (isTarPayPlan) {
636
- smartItems = tarPayPayouts
637
- .filter((p) => !planFilter || p.plan === planFilter)
638
- .map((p) => ({
639
- id: p.id,
640
- user_email: p.user_email,
641
- crypto: p.currency,
642
- amount: parseFloat(p.amount),
643
- remaining_amount: p.remaining_amount !== undefined ? p.remaining_amount : parseFloat(p.amount),
644
- has_partial_payments: p.has_partial_payments || false,
645
- paid_percentage: p.paid_percentage || 0,
646
- }));
647
- } else {
648
- smartItems = payouts
649
- .filter((p) => !planFilter || p.plan === planFilter)
650
- .map((p) => ({
651
- id: p.id,
652
- user_email: p.user_email,
653
- crypto: p.crypto_type,
654
- amount: p.amount,
655
- remaining_amount: p.remaining_amount !== undefined ? p.remaining_amount : p.amount,
656
- has_partial_payments: p.has_partial_payments || false,
657
- paid_percentage: p.paid_percentage || 0,
658
- }));
659
- }
660
-
661
- if (smartItems.length === 0) {
662
- console.log(chalk.green('\n ✓ No pending payouts for this plan\n'));
663
- return;
664
- }
577
+ plan: string;
578
+ source: 'regular' | 'tarpay';
579
+ }
580
+
581
+ // Build unified list from both sources
582
+ const allItems: SuperSmartItem[] = [
583
+ ...payouts.map((p) => ({
584
+ id: p.id,
585
+ user_email: p.user_email,
586
+ crypto: p.crypto_type,
587
+ amount: p.amount,
588
+ remaining_amount: p.remaining_amount !== undefined ? p.remaining_amount : p.amount,
589
+ has_partial_payments: p.has_partial_payments || false,
590
+ paid_percentage: p.paid_percentage || 0,
591
+ plan: p.plan,
592
+ source: 'regular' as const,
593
+ })),
594
+ ...tarPayPayouts.map((p) => ({
595
+ id: p.id,
596
+ user_email: p.user_email,
597
+ crypto: p.currency,
598
+ amount: parseFloat(p.amount),
599
+ remaining_amount: p.remaining_amount !== undefined ? p.remaining_amount : parseFloat(p.amount),
600
+ has_partial_payments: p.has_partial_payments || false,
601
+ paid_percentage: p.paid_percentage || 0,
602
+ plan: p.plan,
603
+ source: 'tarpay' as const,
604
+ })),
605
+ ];
665
606
 
666
- // Group by currency
667
- const byCurrency: Record<string, SmartPayoutItem[]> = {};
668
- smartItems.forEach((p) => {
607
+ // Group by currency across ALL plans
608
+ const byCurrency: Record<string, SuperSmartItem[]> = {};
609
+ allItems.forEach((p) => {
669
610
  if (!byCurrency[p.crypto]) byCurrency[p.crypto] = [];
670
611
  byCurrency[p.crypto].push(p);
671
612
  });
672
613
 
673
- const currencies = Object.keys(byCurrency);
674
- const planLabel = planFilter ? planFilter.toUpperCase() : 'ALL PLANS';
675
- const planColor = planFilter ? (PLAN_COLORS[planFilter] || chalk.white) : chalk.white;
614
+ const totalCount = allItems.length;
615
+ const totalAmount = allItems.reduce((sum, p) => sum + p.remaining_amount, 0);
616
+ const currencySummary = Object.entries(byCurrency)
617
+ .map(([crypto, items]) => `${chalk.bold(crypto)} ${items.length}`)
618
+ .join(' ');
676
619
 
677
- console.log('\n' + planColor(` 📋 ${planLabel} - Pending Payouts`));
678
- console.log(chalk.dim(' ─────────────────────────────'));
679
- currencies.forEach((crypto) => {
680
- const items = byCurrency[crypto];
681
- const total = items.reduce((sum, p) => sum + p.remaining_amount, 0);
682
- console.log(` ${chalk.bold(crypto.padEnd(5))}: ${String(items.length).padStart(3)} payouts │ Total: ${chalk.green(formatAmount(total, crypto))}`);
683
- });
620
+ // Show overview
621
+ console.log('');
622
+ console.log(chalk.cyan('╔═══ SUPER SMART ═══╗'));
623
+ console.log(chalk.cyan('║') + ` Pending: ${chalk.bold.yellow(String(totalCount))} payouts (${chalk.green('$' + Math.round(totalAmount).toLocaleString())})`);
624
+ console.log(chalk.cyan('║') + ` ${currencySummary}`);
625
+ console.log(chalk.cyan('╚════════════════════╝'));
684
626
  console.log('');
685
627
 
686
- // Step 2: Select currency (with back, skip if only one)
628
+ if (totalCount === 0) {
629
+ console.log(chalk.green(' ✓ No pending payouts\n'));
630
+ return;
631
+ }
632
+
633
+ // Step 1: Select currency (with back option)
634
+ const currencies = Object.keys(byCurrency);
687
635
  let selectedCurrency: string;
636
+
688
637
  if (currencies.length === 1) {
689
638
  selectedCurrency = currencies[0];
690
639
  } else {
@@ -701,7 +650,7 @@ const runSmartQueueDirect = async () => {
701
650
  {
702
651
  type: 'list',
703
652
  name: 'currency',
704
- message: 'Which currency to process?',
653
+ message: 'Select currency:',
705
654
  choices: currencyChoices,
706
655
  },
707
656
  ]);
@@ -711,9 +660,26 @@ const runSmartQueueDirect = async () => {
711
660
  }
712
661
 
713
662
  const currencyItems = byCurrency[selectedCurrency];
663
+
664
+ // Show plan breakdown for selected currency
665
+ const byPlan: Record<string, SuperSmartItem[]> = {};
666
+ currencyItems.forEach((p) => {
667
+ if (!byPlan[p.plan]) byPlan[p.plan] = [];
668
+ byPlan[p.plan].push(p);
669
+ });
670
+
671
+ console.log(`\n ${chalk.bold(selectedCurrency)} - ${currencyItems.length} payouts:`);
672
+ Object.entries(byPlan).forEach(([plan, items]) => {
673
+ const planTotal = items.reduce((sum, p) => sum + p.remaining_amount, 0);
674
+ const icon = PLAN_ICONS[plan] || '📋';
675
+ const color = PLAN_COLORS[plan] || chalk.white;
676
+ console.log(` ${icon} ${color(plan.toUpperCase().padEnd(12))} ${String(items.length).padStart(2)} │ ${chalk.green(formatAmount(planTotal, selectedCurrency))}`);
677
+ });
678
+ console.log('');
679
+
714
680
  const totalPending = currencyItems.reduce((sum, p) => sum + p.remaining_amount, 0);
715
681
 
716
- // Step 3: Ask available amount (with back)
682
+ // Step 2: Ask available amount (with back)
717
683
  const { availableAmount } = await inquirer.prompt([
718
684
  {
719
685
  type: 'input',
@@ -731,40 +697,100 @@ const runSmartQueueDirect = async () => {
731
697
  if (availableAmount.toLowerCase() === 'back' || availableAmount.toLowerCase() === 'q') return;
732
698
 
733
699
  const available = parseFloat(availableAmount);
734
- const smartPercentage = Math.min((available / totalPending) * 100, 100);
735
- const roundedPct = Math.round(smartPercentage * 100) / 100;
700
+ const equalShare = available / currencyItems.length;
701
+
702
+ // Plan abbreviations for preview
703
+ const PLAN_ABBREV: Record<string, string> = {
704
+ hermes: 'HRM',
705
+ alpha: 'ALP',
706
+ mematic: 'MEM',
707
+ validator_v2: 'V2',
708
+ booster: 'BST',
709
+ dumpster: 'DMP',
710
+ christmas: 'XMS',
711
+ recovery: 'RCV',
712
+ };
713
+
714
+ // Smart equal distribution preview (mirrors backend logic)
715
+ // Capped users get only their remaining, surplus redistributed
716
+ let previewTotal = 0;
717
+ const allocations: { item: typeof currencyItems[0]; willPay: number }[] = [];
718
+ let pool = available;
719
+ let unallocated = [...currencyItems];
720
+
721
+ while (unallocated.length > 0 && pool > 0.001) {
722
+ const share = pool / unallocated.length;
723
+ const capped: typeof currencyItems = [];
724
+ const uncapped: typeof currencyItems = [];
725
+
726
+ for (const p of unallocated) {
727
+ const existing = allocations.find((a) => a.item.id === p.id);
728
+ const alreadyAllocated = existing ? existing.willPay : 0;
729
+ const remaining = p.remaining_amount - alreadyAllocated;
730
+
731
+ if (remaining <= share) {
732
+ capped.push(p);
733
+ const amt = remaining;
734
+ if (existing) existing.willPay += amt; else allocations.push({ item: p, willPay: amt });
735
+ pool -= amt;
736
+ } else {
737
+ uncapped.push(p);
738
+ }
739
+ }
740
+
741
+ if (capped.length === 0) {
742
+ // No one capped, give everyone the equal share
743
+ for (const p of uncapped) {
744
+ const existing = allocations.find((a) => a.item.id === p.id);
745
+ if (existing) existing.willPay += share; else allocations.push({ item: p, willPay: share });
746
+ pool -= share;
747
+ }
748
+ break;
749
+ }
750
+
751
+ unallocated = uncapped;
752
+ }
736
753
 
737
754
  // Show preview
738
- console.log('\n' + chalk.cyan(` Processing ${chalk.bold(roundedPct + '%')} of ${currencyItems.length} ${selectedCurrency} payouts (${formatAmount(available, selectedCurrency)} / ${formatAmount(totalPending, selectedCurrency)})`));
755
+ console.log('\n' + chalk.cyan(` Equal distribution: ${chalk.bold(formatAmount(available, selectedCurrency))} across ${currencyItems.length} ${selectedCurrency} payouts`));
739
756
  console.log(chalk.dim(' ─────────────────────────────'));
740
757
 
741
- let previewTotal = 0;
742
- currencyItems.forEach((p) => {
743
- const willPay = p.remaining_amount * (smartPercentage / 100);
744
- previewTotal += willPay;
745
- const email = p.user_email.length > 22 ? p.user_email.substring(0, 20) + '..' : p.user_email;
746
- console.log(` ${chalk.dim('#' + p.id)} ${email.padEnd(22)} ${formatAmount(p.remaining_amount, selectedCurrency).padStart(16)} → pays ${chalk.green(formatAmount(willPay, selectedCurrency))}`);
747
- });
758
+ for (const alloc of allocations) {
759
+ previewTotal += alloc.willPay;
760
+ const p = alloc.item;
761
+ const email = p.user_email.length > 20 ? p.user_email.substring(0, 18) + '..' : p.user_email;
762
+ const abbrev = PLAN_ABBREV[p.plan] || p.plan.substring(0, 3).toUpperCase();
763
+ const planColor = PLAN_COLORS[p.plan] || chalk.white;
764
+ const capped = alloc.willPay < equalShare - 0.01 ? chalk.dim(' (capped)') : '';
765
+ console.log(` ${chalk.dim('#' + p.id)} ${planColor('[' + abbrev + ']')} ${email.padEnd(20)} ${formatAmount(p.remaining_amount, selectedCurrency).padStart(14)} → pays ${chalk.green(formatAmount(alloc.willPay, selectedCurrency))}${capped}`);
766
+ }
748
767
 
749
768
  console.log(chalk.dim(' ─────────────────────────────'));
750
769
  console.log(chalk.bold(` Total to send: ${chalk.green(formatAmount(previewTotal, selectedCurrency))}\n`));
751
770
 
752
- // Step 4: Confirm
771
+ // Step 3: Confirm
753
772
  const { confirmSmart } = await inquirer.prompt([
754
773
  {
755
774
  type: 'confirm',
756
775
  name: 'confirmSmart',
757
- message: chalk.yellow(`⚠️ Pay ${roundedPct}% of ${currencyItems.length} payout(s)?`),
776
+ message: chalk.yellow(`⚠️ Equal distribute ${formatAmount(available, selectedCurrency)} across ${currencyItems.length} payout(s)?`),
758
777
  default: false,
759
778
  },
760
779
  ]);
761
780
 
762
781
  if (confirmSmart) {
763
- const smartIds = currencyItems.map((p) => p.id);
764
- if (isTarPayPlan) {
765
- await processChristmasPayoutsWithProgress(smartIds, 'partial', roundedPct);
766
- } else {
767
- await processPayoutsWithProgress(smartIds, 'partial', roundedPct);
782
+ // Split by source for correct API calls
783
+ const regularIds = currencyItems.filter((p) => p.source === 'regular').map((p) => p.id);
784
+ const tarPayIds = currencyItems.filter((p) => p.source === 'tarpay').map((p) => p.id);
785
+
786
+ if (regularIds.length > 0) {
787
+ await processPayoutsWithProgress(regularIds, 'equal', available);
788
+ }
789
+ if (tarPayIds.length > 0) {
790
+ // TarPay still uses percentage-based for now (christmas payouts)
791
+ const smartPercentage = Math.min((available / totalPending) * 100, 100);
792
+ const roundedPct = Math.round(smartPercentage * 100) / 100;
793
+ await processChristmasPayoutsWithProgress(tarPayIds, 'partial', roundedPct);
768
794
  }
769
795
  } else {
770
796
  console.log(chalk.dim('\n Cancelled\n'));
@@ -821,8 +847,8 @@ const runInteractiveShell = async () => {
821
847
  } else if (cmd === '4') {
822
848
  setTitle('Urgent');
823
849
  await showUrgentCmd();
824
- } else if (cmd === '5' || cmd === 'queue' || cmd === 'sq') {
825
- await runSmartQueueDirect();
850
+ } else if (cmd === '5' || cmd === 'queue' || cmd === 'sq' || cmd === 'super' || cmd === 'ss') {
851
+ await runSuperSmartDirect();
826
852
  await showDashboard();
827
853
  } else {
828
854
  // Regular commands
@@ -1430,8 +1456,8 @@ const selectPayoutsInteractive = async (plan?: string): Promise<number[]> => {
1430
1456
  }
1431
1457
  };
1432
1458
 
1433
- const processPayoutsWithProgress = async (ids: number[], action: 'approve' | 'cancel' | 'partial', percentage?: number) => {
1434
- const actionLabel = action === 'partial' ? `PARTIAL ${percentage}%` : action.toUpperCase();
1459
+ const processPayoutsWithProgress = async (ids: number[], action: 'approve' | 'cancel' | 'partial' | 'equal', percentageOrAmount?: number) => {
1460
+ const actionLabel = action === 'partial' ? `PARTIAL ${percentageOrAmount}%` : action === 'equal' ? `EQUAL DIST $${percentageOrAmount}` : action.toUpperCase();
1435
1461
  console.log('\n' + chalk.cyan('╔══════════════════════════════════════════════════════════════╗'));
1436
1462
  console.log(chalk.cyan('║') + chalk.bold.white(` 🔄 ${actionLabel} - ${ids.length} PAYOUT(S)... `.substring(0, 60)) + chalk.cyan('║'));
1437
1463
  console.log(chalk.cyan('╠══════════════════════════════════════════════════════════════╣'));
@@ -1458,7 +1484,9 @@ const processPayoutsWithProgress = async (ids: number[], action: 'approve' | 'ca
1458
1484
  if (action === 'approve') {
1459
1485
  result = await api.batchApprovePayouts(ids);
1460
1486
  } else if (action === 'partial') {
1461
- result = await api.batchPartialPayouts(ids, percentage!);
1487
+ result = await api.batchPartialPayouts(ids, percentageOrAmount!);
1488
+ } else if (action === 'equal') {
1489
+ result = await api.batchEqualPartialPayouts(ids, percentageOrAmount!);
1462
1490
  } else {
1463
1491
  result = await api.batchCancelPayouts(ids);
1464
1492
  }
@@ -1475,7 +1503,9 @@ const processPayoutsWithProgress = async (ids: number[], action: 'approve' | 'ca
1475
1503
  if (action === 'approve') {
1476
1504
  icon = '✓'; verb = 'APPROVED & SENT';
1477
1505
  } else if (action === 'partial') {
1478
- icon = '%'; verb = `PARTIAL ${percentage}% SENT`;
1506
+ icon = '%'; verb = `PARTIAL ${percentageOrAmount}% SENT`;
1507
+ } else if (action === 'equal') {
1508
+ icon = '⚖'; verb = 'EQUAL DISTRIBUTION SENT';
1479
1509
  } else {
1480
1510
  icon = '↩'; verb = 'CANCELLED & RETURNED';
1481
1511
  }
@@ -1908,114 +1938,11 @@ const runChristmasPayoutsShell = async () => {
1908
1938
  }
1909
1939
  break;
1910
1940
  case 'smart':
1911
- case 'sp': {
1912
- // Smart partial: pay by available balance for Christmas
1913
- const xmasSmartSpinner = ora('Loading Christmas payouts...').start();
1914
- try {
1915
- const xmasData = await api.getTarPayPayouts({
1916
- plan: 'christmas',
1917
- status: 'pending_review',
1918
- limit: 500,
1919
- });
1920
- const xmasPayouts: TarPayPayout[] = xmasData.results || [];
1921
- xmasSmartSpinner.stop();
1922
-
1923
- if (xmasPayouts.length === 0) {
1924
- console.log(chalk.green('\n ✓ No pending Christmas payouts\n'));
1925
- break;
1926
- }
1927
-
1928
- // Group by currency
1929
- const xmasByCurrency: Record<string, TarPayPayout[]> = {};
1930
- xmasPayouts.forEach((p) => {
1931
- if (!xmasByCurrency[p.currency]) xmasByCurrency[p.currency] = [];
1932
- xmasByCurrency[p.currency].push(p);
1933
- });
1934
-
1935
- const xmasCurrencies = Object.keys(xmasByCurrency);
1936
- console.log('\n' + chalk.red(` 🎄 CHRISTMAS - Pending Payouts`));
1937
- console.log(chalk.dim(' ─────────────────────────────'));
1938
- xmasCurrencies.forEach((crypto) => {
1939
- const items = xmasByCurrency[crypto];
1940
- const total = items.reduce((sum, p) => sum + (p.remaining_amount !== undefined ? p.remaining_amount : parseFloat(p.amount)), 0);
1941
- console.log(` ${chalk.bold(crypto.padEnd(5))}: ${String(items.length).padStart(3)} payouts │ Total: ${chalk.green(formatAmount(total, crypto))}`);
1942
- });
1943
- console.log('');
1944
-
1945
- let xmasCurrency: string;
1946
- if (xmasCurrencies.length === 1) {
1947
- xmasCurrency = xmasCurrencies[0];
1948
- } else {
1949
- const { currency } = await inquirer.prompt([
1950
- {
1951
- type: 'list',
1952
- name: 'currency',
1953
- message: 'Which currency to process?',
1954
- choices: xmasCurrencies.map((c) => {
1955
- const items = xmasByCurrency[c];
1956
- const total = items.reduce((sum, p) => sum + (p.remaining_amount !== undefined ? p.remaining_amount : parseFloat(p.amount)), 0);
1957
- return { name: `${c} (${items.length} payouts, ${formatAmount(total, c)})`, value: c };
1958
- }),
1959
- },
1960
- ]);
1961
- xmasCurrency = currency;
1962
- }
1963
-
1964
- const xmasCurrencyPayouts = xmasByCurrency[xmasCurrency];
1965
- const xmasTotalPending = xmasCurrencyPayouts.reduce((sum, p) => sum + (p.remaining_amount !== undefined ? p.remaining_amount : parseFloat(p.amount)), 0);
1966
-
1967
- const { xmasAvailable } = await inquirer.prompt([
1968
- {
1969
- type: 'input',
1970
- name: 'xmasAvailable',
1971
- message: `How much ${xmasCurrency} do you have available? (total pending: ${formatAmount(xmasTotalPending, xmasCurrency)})`,
1972
- validate: (input: string) => {
1973
- const num = parseFloat(input);
1974
- if (isNaN(num) || num <= 0) return 'Please enter a positive number';
1975
- return true;
1976
- },
1977
- },
1978
- ]);
1979
-
1980
- const xmasAvailNum = parseFloat(xmasAvailable);
1981
- const xmasPct = Math.min((xmasAvailNum / xmasTotalPending) * 100, 100);
1982
- const xmasRoundedPct = Math.round(xmasPct * 100) / 100;
1983
-
1984
- console.log('\n' + chalk.cyan(` Processing ${chalk.bold(xmasRoundedPct + '%')} of ${xmasCurrencyPayouts.length} ${xmasCurrency} payouts (${formatAmount(xmasAvailNum, xmasCurrency)} / ${formatAmount(xmasTotalPending, xmasCurrency)})`));
1985
- console.log(chalk.dim(' ─────────────────────────────'));
1986
-
1987
- let xmasPreviewTotal = 0;
1988
- xmasCurrencyPayouts.forEach((p) => {
1989
- const displayAmt = p.remaining_amount !== undefined ? p.remaining_amount : parseFloat(p.amount);
1990
- const willPay = displayAmt * (xmasPct / 100);
1991
- xmasPreviewTotal += willPay;
1992
- const email = p.user_email.length > 22 ? p.user_email.substring(0, 20) + '..' : p.user_email;
1993
- console.log(` ${chalk.dim('#' + p.id)} ${email.padEnd(22)} ${formatAmount(displayAmt, xmasCurrency).padStart(16)} → pays ${chalk.green(formatAmount(willPay, xmasCurrency))}`);
1994
- });
1995
-
1996
- console.log(chalk.dim(' ─────────────────────────────'));
1997
- console.log(chalk.bold(` Total to send: ${chalk.green(formatAmount(xmasPreviewTotal, xmasCurrency))}\n`));
1998
-
1999
- const { confirmXmasSmart } = await inquirer.prompt([
2000
- {
2001
- type: 'confirm',
2002
- name: 'confirmXmasSmart',
2003
- message: chalk.yellow(`⚠️ Pay ${xmasRoundedPct}% of ${xmasCurrencyPayouts.length} payout(s)?`),
2004
- default: false,
2005
- },
2006
- ]);
2007
-
2008
- if (confirmXmasSmart) {
2009
- const xmasSmartIds = xmasCurrencyPayouts.map((p) => p.id);
2010
- await processChristmasPayoutsWithProgress(xmasSmartIds, 'partial', xmasRoundedPct);
2011
- } else {
2012
- console.log(chalk.dim('\n Cancelled\n'));
2013
- }
2014
- } catch (e: any) {
2015
- xmasSmartSpinner.fail(chalk.red('Failed to load Christmas payouts'));
2016
- }
1941
+ case 'sp':
1942
+ case 'super':
1943
+ case 'ss':
1944
+ await runSuperSmartDirect();
2017
1945
  break;
2018
- }
2019
1946
  case 'help':
2020
1947
  case '?':
2021
1948
  console.log(chalk.red('\n 🎄 Christmas Payout Commands:'));
@@ -2028,7 +1955,7 @@ const runChristmasPayoutsShell = async () => {
2028
1955
  console.log(' select, s Interactive selection mode');
2029
1956
  console.log(' approve, a Select & approve payouts');
2030
1957
  console.log(chalk.cyan(' partial, p Select & pay partial %'));
2031
- console.log(chalk.cyan(' smart, sp Smart partial: pay by available balance'));
1958
+ console.log(chalk.cyan(' smart, sp, ss Super Smart: pay by currency across all plans'));
2032
1959
  console.log(' reject Select & reject payouts');
2033
1960
  console.log(' back, q Return to main payouts');
2034
1961
  console.log(chalk.dim('\n Note: Payouts > $50 need approval, < $50 auto-sent'));
@@ -2218,180 +2145,9 @@ const runPayoutsShell = async () => {
2218
2145
  break;
2219
2146
  case 'smart':
2220
2147
  case 'sp':
2221
- // Smart partial: pay by available balance
2222
- const { smartPlan } = await inquirer.prompt([
2223
- {
2224
- type: 'list',
2225
- name: 'smartPlan',
2226
- message: 'Select plan:',
2227
- choices: [
2228
- { name: `${PLAN_ICONS.hermes || '⚡'} Hermes`, value: 'hermes' },
2229
- { name: `${PLAN_ICONS.alpha || '🔷'} Alpha`, value: 'alpha' },
2230
- { name: `${PLAN_ICONS.mematic || '💎'} Mematic`, value: 'mematic' },
2231
- { name: `${PLAN_ICONS.validator_v2 || '🔐'} Validator V2`, value: 'validator_v2' },
2232
- { name: `${PLAN_ICONS.booster || '🚀'} Booster`, value: 'booster' },
2233
- { name: `${PLAN_ICONS.christmas || '🎄'} Christmas`, value: 'christmas' },
2234
- { name: '🔄 Recovery', value: 'recovery' },
2235
- { name: '📋 All Plans', value: undefined },
2236
- ],
2237
- },
2238
- ]);
2239
-
2240
- const isTarPayPlan = smartPlan === 'christmas';
2241
- const smartSpinner = ora('Loading pending payouts...').start();
2242
- try {
2243
- // Unified payout items with common shape
2244
- interface SmartPayoutItem {
2245
- id: number;
2246
- user_email: string;
2247
- crypto: string;
2248
- amount: number;
2249
- remaining_amount: number;
2250
- has_partial_payments: boolean;
2251
- paid_percentage: number;
2252
- }
2253
-
2254
- let smartItems: SmartPayoutItem[] = [];
2255
-
2256
- if (isTarPayPlan) {
2257
- // TarPay payouts (Christmas, etc.)
2258
- const tarData = await api.getTarPayPayouts({
2259
- plan: smartPlan,
2260
- status: 'pending_review',
2261
- limit: 500,
2262
- });
2263
- const tarPayouts: TarPayPayout[] = tarData.results || [];
2264
- smartItems = tarPayouts.map((p) => ({
2265
- id: p.id,
2266
- user_email: p.user_email,
2267
- crypto: p.currency,
2268
- amount: parseFloat(p.amount),
2269
- remaining_amount: p.remaining_amount !== undefined ? p.remaining_amount : parseFloat(p.amount),
2270
- has_partial_payments: p.has_partial_payments || false,
2271
- paid_percentage: p.paid_percentage || 0,
2272
- }));
2273
- } else {
2274
- // Regular payouts
2275
- const smartParams: any = { status: 'pending', limit: 500 };
2276
- if (smartPlan) smartParams.plan = smartPlan;
2277
- const smartData = await api.listPayouts(smartParams);
2278
- const smartPayouts: Payout[] = smartData.data || [];
2279
- smartItems = smartPayouts.map((p) => ({
2280
- id: p.id,
2281
- user_email: p.user_email,
2282
- crypto: p.crypto_type,
2283
- amount: p.amount,
2284
- remaining_amount: p.remaining_amount !== undefined ? p.remaining_amount : p.amount,
2285
- has_partial_payments: p.has_partial_payments || false,
2286
- paid_percentage: p.paid_percentage || 0,
2287
- }));
2288
- }
2289
- smartSpinner.stop();
2290
-
2291
- if (smartItems.length === 0) {
2292
- console.log(chalk.green('\n ✓ No pending payouts found\n'));
2293
- break;
2294
- }
2295
-
2296
- // Group by crypto
2297
- const byCurrency: Record<string, SmartPayoutItem[]> = {};
2298
- smartItems.forEach((p) => {
2299
- if (!byCurrency[p.crypto]) byCurrency[p.crypto] = [];
2300
- byCurrency[p.crypto].push(p);
2301
- });
2302
-
2303
- const currencies = Object.keys(byCurrency);
2304
- const planLabel = smartPlan ? smartPlan.toUpperCase() : 'ALL PLANS';
2305
- const planColor = smartPlan ? (PLAN_COLORS[smartPlan] || chalk.white) : chalk.white;
2306
-
2307
- console.log('\n' + planColor(` 📋 ${planLabel} - Pending Payouts`));
2308
- console.log(chalk.dim(' ─────────────────────────────'));
2309
- currencies.forEach((crypto) => {
2310
- const items = byCurrency[crypto];
2311
- const total = items.reduce((sum, p) => sum + p.remaining_amount, 0);
2312
- console.log(` ${chalk.bold(crypto.padEnd(5))}: ${String(items.length).padStart(3)} payouts │ Total: ${chalk.green(formatAmount(total, crypto))}`);
2313
- });
2314
- console.log('');
2315
-
2316
- // Select currency
2317
- let selectedCurrency: string;
2318
- if (currencies.length === 1) {
2319
- selectedCurrency = currencies[0];
2320
- } else {
2321
- const { currency } = await inquirer.prompt([
2322
- {
2323
- type: 'list',
2324
- name: 'currency',
2325
- message: 'Which currency to process?',
2326
- choices: currencies.map((c) => {
2327
- const items = byCurrency[c];
2328
- const total = items.reduce((sum, p) => sum + p.remaining_amount, 0);
2329
- return { name: `${c} (${items.length} payouts, ${formatAmount(total, c)})`, value: c };
2330
- }),
2331
- },
2332
- ]);
2333
- selectedCurrency = currency;
2334
- }
2335
-
2336
- const currencyItems = byCurrency[selectedCurrency];
2337
- const totalPending = currencyItems.reduce((sum, p) => sum + p.remaining_amount, 0);
2338
-
2339
- // Ask available amount
2340
- const { availableAmount } = await inquirer.prompt([
2341
- {
2342
- type: 'input',
2343
- name: 'availableAmount',
2344
- message: `How much ${selectedCurrency} do you have available? (total pending: ${formatAmount(totalPending, selectedCurrency)})`,
2345
- validate: (input: string) => {
2346
- const num = parseFloat(input);
2347
- if (isNaN(num) || num <= 0) return 'Please enter a positive number';
2348
- return true;
2349
- },
2350
- },
2351
- ]);
2352
-
2353
- const available = parseFloat(availableAmount);
2354
- const smartPercentage = Math.min((available / totalPending) * 100, 100);
2355
- const roundedPct = Math.round(smartPercentage * 100) / 100;
2356
-
2357
- // Show breakdown
2358
- console.log('\n' + chalk.cyan(` Processing ${chalk.bold(roundedPct + '%')} of ${currencyItems.length} ${selectedCurrency} payouts (${formatAmount(available, selectedCurrency)} / ${formatAmount(totalPending, selectedCurrency)})`));
2359
- console.log(chalk.dim(' ─────────────────────────────'));
2360
-
2361
- let previewTotal = 0;
2362
- currencyItems.forEach((p) => {
2363
- const willPay = p.remaining_amount * (smartPercentage / 100);
2364
- previewTotal += willPay;
2365
- const email = p.user_email.length > 22 ? p.user_email.substring(0, 20) + '..' : p.user_email;
2366
- console.log(` ${chalk.dim('#' + p.id)} ${email.padEnd(22)} ${formatAmount(p.remaining_amount, selectedCurrency).padStart(16)} → pays ${chalk.green(formatAmount(willPay, selectedCurrency))}`);
2367
- });
2368
-
2369
- console.log(chalk.dim(' ─────────────────────────────'));
2370
- console.log(chalk.bold(` Total to send: ${chalk.green(formatAmount(previewTotal, selectedCurrency))}\n`));
2371
-
2372
- // Confirm
2373
- const { confirmSmart } = await inquirer.prompt([
2374
- {
2375
- type: 'confirm',
2376
- name: 'confirmSmart',
2377
- message: chalk.yellow(`⚠️ Pay ${roundedPct}% of ${currencyItems.length} payout(s)?`),
2378
- default: false,
2379
- },
2380
- ]);
2381
-
2382
- if (confirmSmart) {
2383
- const smartIds = currencyItems.map((p) => p.id);
2384
- if (isTarPayPlan) {
2385
- await processChristmasPayoutsWithProgress(smartIds, 'partial', roundedPct);
2386
- } else {
2387
- await processPayoutsWithProgress(smartIds, 'partial', roundedPct);
2388
- }
2389
- } else {
2390
- console.log(chalk.dim('\n Cancelled\n'));
2391
- }
2392
- } catch (e: any) {
2393
- smartSpinner.fail(chalk.red('Failed to load payouts'));
2394
- }
2148
+ case 'super':
2149
+ case 'ss':
2150
+ await runSuperSmartDirect();
2395
2151
  break;
2396
2152
  case 'hermes':
2397
2153
  case 'alpha':
@@ -2481,7 +2237,7 @@ const runPayoutsShell = async () => {
2481
2237
  console.log(' select [plan] Interactive payout selection');
2482
2238
  console.log(' approve [ids] Approve payouts (interactive if no IDs)');
2483
2239
  console.log(chalk.cyan(' partial, p Pay partial % (repayment mode)'));
2484
- console.log(chalk.cyan(' smart, sp Smart partial: pay by available balance'));
2240
+ console.log(chalk.cyan(' smart, sp, ss Super Smart: pay by currency across all plans'));
2485
2241
  console.log(chalk.yellow(' all Approve ALL pending payouts'));
2486
2242
  console.log(' cancel [ids] Cancel payouts (interactive if no IDs)');
2487
2243
  console.log(' process Process payouts by plan (interactive)');