Compare commits

..
6 Commits
31 changed files with 269 additions and 95 deletions
@@ -1,6 +1,6 @@
import { ServiceUnavailableException } from '@nestjs/common';
import type { ConfigService } from '@nestjs/config';
import { BitcoinWalletSyncStatus } from '../types/BitcoinWalletSyncStatus';
import { WalletSyncStatus } from '../../../types/wallet/WalletSyncStatus';
import type { ElectrumWalletRpcClient } from './ElectrumWalletRpcClient';
import { BitcoinWalletAdminService } from './BitcoinWalletAdminService';
@@ -51,7 +51,7 @@ describe('BitcoinWalletAdminService', () => {
rpcVersion: '4.8.1',
blockHeight: 900_000,
serverHeight: 900_000,
syncStatus: BitcoinWalletSyncStatus.Synced,
syncStatus: WalletSyncStatus.Synced,
balanceBtc: '0.00150000',
confirmedBalanceBtc: '0.00150000'
})
@@ -63,7 +63,7 @@ describe('BitcoinWalletAdminService', () => {
const status = await service.getStatus();
expect(status.syncStatus).toBe(BitcoinWalletSyncStatus.Syncing);
expect(status.syncStatus).toBe(WalletSyncStatus.Syncing);
});
it('throws when RPC calls fail', async () => {
@@ -2,7 +2,7 @@ import { Injectable, ServiceUnavailableException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { convertBtcAtomicToBtc } from '../../../utils/bitcoin/convertBtcAtomicToBtc';
import type { Config } from '../../../types/Config';
import { BitcoinWalletSyncStatus } from '../types/BitcoinWalletSyncStatus';
import { WalletSyncStatus } from '../../../types/wallet/WalletSyncStatus';
import type { BitcoinWalletStatusView } from '../types/BitcoinWalletStatusView';
import { ElectrumWalletRpcClient } from './ElectrumWalletRpcClient';
@@ -47,15 +47,15 @@ export class BitcoinWalletAdminService {
isSynchronized: boolean,
blockHeight: number | null,
serverHeight: number | null
): BitcoinWalletSyncStatus {
): WalletSyncStatus {
if (isSynchronized) {
return BitcoinWalletSyncStatus.Synced;
return WalletSyncStatus.Synced;
}
if (blockHeight === null || serverHeight === null) {
return BitcoinWalletSyncStatus.Unknown;
return WalletSyncStatus.Unknown;
}
return BitcoinWalletSyncStatus.Syncing;
return WalletSyncStatus.Syncing;
}
}
@@ -1,12 +1,12 @@
import { ElectrumNetwork } from '../../../types/ElectrumNetwork';
import { BitcoinWalletSyncStatus } from './BitcoinWalletSyncStatus';
import { WalletSyncStatus } from '../../../types/wallet/WalletSyncStatus';
export interface BitcoinWalletStatusView {
network: ElectrumNetwork;
rpcVersion: string;
blockHeight: number | null;
serverHeight: number | null;
syncStatus: BitcoinWalletSyncStatus;
syncStatus: WalletSyncStatus;
balanceBtc: string;
confirmedBalanceBtc: string;
}
@@ -1,5 +0,0 @@
export enum BitcoinWalletSyncStatus {
Synced = 'synced',
Syncing = 'syncing',
Unknown = 'unknown'
}
@@ -2,7 +2,7 @@ import { BadRequestException, ServiceUnavailableException } from '@nestjs/common
import type { ConfigService } from '@nestjs/config';
import axios from 'axios';
import type { AuthService } from '../../auth/services/AuthService';
import { MoneroWalletSyncStatus } from '../types/MoneroWalletSyncStatus';
import { WalletSyncStatus } from '../../../types/wallet/WalletSyncStatus';
import type { MoneroWalletRpcClient } from './MoneroWalletRpcClient';
import { MoneroWalletAdminService } from './MoneroWalletAdminService';
@@ -76,7 +76,7 @@ describe('MoneroWalletAdminService', () => {
rpcVersion: '0.18.3.1',
walletHeight: 3_000_000,
daemonHeight: 3_000_000,
syncStatus: MoneroWalletSyncStatus.Synced,
syncStatus: WalletSyncStatus.Synced,
balanceXmr: '2.00000000',
unlockedBalanceXmr: '1.00000000'
})
@@ -137,7 +137,7 @@ describe('MoneroWalletAdminService', () => {
const status = await service.getStatus();
expect(status.syncStatus).toBe(MoneroWalletSyncStatus.Unknown);
expect(status.syncStatus).toBe(WalletSyncStatus.Unknown);
expect(status.daemonHeight).toBeNull();
});
@@ -149,7 +149,7 @@ describe('MoneroWalletAdminService', () => {
const status = await service.getStatus();
expect(status.syncStatus).toBe(MoneroWalletSyncStatus.Synced);
expect(status.syncStatus).toBe(WalletSyncStatus.Synced);
});
it('throws when reveal seed RPC fails', async () => {
@@ -7,7 +7,7 @@ import type { Config } from '../../../types/Config';
import type { MoneroDaemonGetInfoResult } from '../types/MoneroDaemonGetInfoResult';
import type { MoneroWalletRevealSeedResult } from '../types/MoneroWalletRevealSeedResult';
import type { MoneroWalletStatusView } from '../types/MoneroWalletStatusView';
import { MoneroWalletSyncStatus } from '../types/MoneroWalletSyncStatus';
import { WalletSyncStatus } from '../../../types/wallet/WalletSyncStatus';
import type { MoneroWalletWithdrawResult } from '../types/MoneroWalletWithdrawResult';
import { MoneroWalletRpcClient } from './MoneroWalletRpcClient';
@@ -72,7 +72,7 @@ export class MoneroWalletAdminService {
throw new BadRequestException('No unlocked balance to withdraw.');
}
if (this.resolveSyncStatus(walletHeight, daemonHeight) !== MoneroWalletSyncStatus.Synced) {
if (this.resolveSyncStatus(walletHeight, daemonHeight) !== WalletSyncStatus.Synced) {
throw new BadRequestException('Wallet is still syncing. Try again after sync completes.');
}
@@ -125,11 +125,11 @@ export class MoneroWalletAdminService {
}
}
private resolveSyncStatus(walletHeight: number, daemonHeight: number | null): MoneroWalletSyncStatus {
private resolveSyncStatus(walletHeight: number, daemonHeight: number | null): WalletSyncStatus {
if (daemonHeight === null) {
return MoneroWalletSyncStatus.Unknown;
return WalletSyncStatus.Unknown;
}
return walletHeight >= daemonHeight - 1 ? MoneroWalletSyncStatus.Synced : MoneroWalletSyncStatus.Syncing;
return walletHeight >= daemonHeight - 1 ? WalletSyncStatus.Synced : WalletSyncStatus.Syncing;
}
}
@@ -1,12 +1,12 @@
import { MoneroNetwork } from '../../../types/MoneroNetwork';
import { MoneroWalletSyncStatus } from './MoneroWalletSyncStatus';
import { WalletSyncStatus } from '../../../types/wallet/WalletSyncStatus';
export interface MoneroWalletStatusView {
network: MoneroNetwork;
rpcVersion: string;
walletHeight: number;
daemonHeight: number | null;
syncStatus: MoneroWalletSyncStatus;
syncStatus: WalletSyncStatus;
balanceXmr: string;
unlockedBalanceXmr: string;
}
@@ -1,4 +1,4 @@
export enum MoneroWalletSyncStatus {
export enum WalletSyncStatus {
Synced = 'synced',
Syncing = 'syncing',
Unknown = 'unknown'
+2 -1
View File
@@ -11,6 +11,7 @@ export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
BitcoinWallet: typeof import('./components/wallet/BitcoinWallet.vue')['default']
CmsListPagination: typeof import('./components/CmsListPagination.vue')['default']
CreateOrEditCategoryModal: typeof import('./components/CreateOrEditCategoryModal.vue')['default']
CreateOrEditDiscountCodeModal: typeof import('./components/CreateOrEditDiscountCodeModal.vue')['default']
@@ -56,9 +57,9 @@ declare module 'vue' {
MoneroWallet: typeof import('./components/wallet/MoneroWallet.vue')['default']
OrderCartPanel: typeof import('./components/OrderCartPanel.vue')['default']
OrderChatPanel: typeof import('./components/OrderChatPanel.vue')['default']
OrderInvoicePaymentPanel: typeof import('./components/OrderInvoicePaymentPanel.vue')['default']
OrderLineAutoFulfillmentModal: typeof import('./components/OrderLineAutoFulfillmentModal.vue')['default']
OrderManualShippingQuotePanel: typeof import('./components/OrderManualShippingQuotePanel.vue')['default']
OrderMoneroPaymentPanel: typeof import('./components/OrderMoneroPaymentPanel.vue')['default']
OrderPaymentPanel: typeof import('./components/OrderPaymentPanel.vue')['default']
OrderSummaryPanel: typeof import('./components/OrderSummaryPanel.vue')['default']
RichTextEditor: typeof import('./components/RichTextEditor.vue')['default']
@@ -8,14 +8,12 @@
</el-descriptions-item>
<el-descriptions-item label="Expected total">
<span class="mono"
>{{ invoice.expectedTotalCrypto }} {{ paymentMethodCryptoCurrency[invoice.paymentMethod] }}</span
>
<span class="mono">{{ invoice.expectedTotalCrypto }} {{ invoice.paymentLabel }}</span>
</el-descriptions-item>
<el-descriptions-item v-if="fiatPerXmrAtCreation !== undefined" :label="`Rate at ${rateLabel}`">
{{ formatFiatPrice(fiatPerXmrAtCreation, config.shopFiatCurrency) }} /
{{ paymentMethodCryptoCurrency[invoice.paymentMethod] }}
<el-descriptions-item v-if="fiatPerCryptoAtCreation !== undefined" :label="`Rate at ${rateLabel}`">
{{ formatFiatPrice(fiatPerCryptoAtCreation, config.shopFiatCurrency) }} /
{{ invoice.paymentLabel }}
</el-descriptions-item>
</el-descriptions>
@@ -37,9 +35,7 @@
<el-table-column label="Amount" width="140">
<template #default="{ row }">
<span class="mono"
>{{ row.amountCrypto }} {{ paymentMethodCryptoCurrency[invoice.paymentMethod] }}</span
>
<span class="mono">{{ row.amountCrypto }} {{ invoice.paymentLabel }}</span>
</template>
</el-table-column>
@@ -69,7 +65,7 @@
import { computed, type PropType } from 'vue';
import { config } from '@/config';
import type { InvoiceExtended } from '@/types/payment/InvoiceExtended';
import { paymentMethodCryptoCurrency } from '@/types/payment/PaymentMethod';
import { PaymentMethod } from '@/types/payment/PaymentMethod';
import { formatDate } from '@/utils/formatDate';
import { formatFiatPrice } from '@/utils/formatFiatPrice';
import { resolveInvoiceStatusTagType } from '@/utils/order/resolveInvoiceStatusTagType';
@@ -85,13 +81,25 @@ const props = defineProps({
},
emptyText: {
type: String,
default: 'No Monero payment session.'
default: 'No payment session.'
}
});
const payments = computed(() => props.invoice?.payments ?? []);
const fiatPerXmrAtCreation = computed(() => props.invoice?.moneroDetails?.fiatPerXmrAtCreation);
const fiatPerCryptoAtCreation = computed(() => {
const invoice = props.invoice;
if (!invoice) {
return undefined;
}
if (invoice.paymentMethod === PaymentMethod.Btc) {
return invoice.btcDetails?.fiatPerBtcAtCreation;
}
return invoice.moneroDetails?.fiatPerXmrAtCreation;
});
</script>
<style scoped>
@@ -53,10 +53,9 @@
</el-descriptions>
<template v-if="order.shippingInvoice">
<order-monero-payment-panel
<order-invoice-payment-panel
:invoice="order.shippingInvoice"
rate-label="quote"
empty-text="Shipping payment session not created yet."
/>
</template>
+1 -1
View File
@@ -4,7 +4,7 @@
<span>Order payment</span>
</template>
<order-monero-payment-panel
<order-invoice-payment-panel
:invoice="order.checkoutInvoice"
rate-label="checkout"
empty-text="No checkout payment session."
@@ -0,0 +1,93 @@
<template>
<div v-loading="loading" class="detail-loading-host" element-loading-text="Loading wallet…">
<el-empty v-if="!loading && loadError" description="Failed to load wallet status" />
<template v-if="!loading && !loadError && walletStatus">
<div v-if="walletStatus.syncStatus !== WalletSyncStatus.Synced" class="mb-16">
<el-alert type="warning" :closable="false" show-icon title="Wallet is syncing. Please wait." />
</div>
<el-card shadow="never">
<template #header>
<div class="flex items-center justify-between gap-16">
<span>Status</span>
<el-button type="default" :loading="refreshing" @click="refreshStatus">Refresh</el-button>
</div>
</template>
<el-descriptions :column="1" border>
<el-descriptions-item label="Network">{{ walletStatus.network }}</el-descriptions-item>
<el-descriptions-item label="RPC version">{{ walletStatus.rpcVersion }}</el-descriptions-item>
<el-descriptions-item label="Block height">
{{ walletStatus.blockHeight ?? 'Unavailable' }}
</el-descriptions-item>
<el-descriptions-item label="Server height">
{{ walletStatus.serverHeight ?? 'Unavailable' }}
</el-descriptions-item>
<el-descriptions-item label="Sync">
<el-tag :type="resolveWalletSyncStatusTagType(walletStatus.syncStatus)" size="small">
{{ resolveWalletSyncStatusLabel(walletStatus.syncStatus) }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="Total balance">{{ walletStatus.balanceBtc }} BTC</el-descriptions-item>
<el-descriptions-item label="Confirmed balance">
{{ walletStatus.confirmedBalanceBtc }} BTC
</el-descriptions-item>
</el-descriptions>
</el-card>
</template>
</div>
</template>
<script setup lang="ts">
import { ElMessage } from 'element-plus';
import { onBeforeMount, ref } from 'vue';
import { storeToRefs } from 'pinia';
import { WalletSyncStatus } from '@/types/wallet/WalletSyncStatus';
import { useBitcoinWalletStore } from '@/stores/bitcoinWallet';
import { resolveAxiosErrorMessage } from '@/utils/resolveAxiosErrorMessage';
import { resolveWalletSyncStatusLabel } from '@/utils/wallet/resolveWalletSyncStatusLabel';
import { resolveWalletSyncStatusTagType } from '@/utils/wallet/resolveWalletSyncStatusTagType';
const bitcoinWalletStore = useBitcoinWalletStore();
const { status: walletStatus } = storeToRefs(bitcoinWalletStore);
const { fetchStatus } = bitcoinWalletStore;
const loading = ref(true);
const loadError = ref(false);
const refreshing = ref(false);
onBeforeMount(async () => {
loading.value = true;
await loadWalletStatus();
loading.value = false;
});
const loadWalletStatus = async (): Promise<void> => {
loadError.value = false;
try {
await fetchStatus();
} catch (error) {
loadError.value = true;
ElMessage.error(resolveAxiosErrorMessage(error, 'Failed to load wallet status'));
}
};
const refreshStatus = async (): Promise<void> => {
refreshing.value = true;
try {
await loadWalletStatus();
ElMessage.success('Wallet status refreshed');
} finally {
refreshing.value = false;
}
};
</script>
+7 -27
View File
@@ -3,7 +3,7 @@
<el-empty v-if="!loading && loadError" description="Failed to load wallet status" />
<template v-if="!loading && !loadError && walletStatus">
<div v-if="walletStatus.syncStatus !== MoneroWalletSyncStatus.Synced" class="mb-16">
<div v-if="walletStatus.syncStatus !== WalletSyncStatus.Synced" class="mb-16">
<el-alert type="warning" :closable="false" show-icon title="Wallet is syncing. Please wait." />
</div>
@@ -23,8 +23,8 @@
{{ walletStatus.daemonHeight ?? 'Unavailable' }}
</el-descriptions-item>
<el-descriptions-item label="Sync">
<el-tag :type="resolveMoneroWalletSyncStatusTagType(walletStatus.syncStatus)" size="small">
{{ resolveMoneroWalletSyncStatusLabel(walletStatus.syncStatus) }}
<el-tag :type="resolveWalletSyncStatusTagType(walletStatus.syncStatus)" size="small">
{{ resolveWalletSyncStatusLabel(walletStatus.syncStatus) }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="Total balance">{{ walletStatus.balanceXmr }} XMR</el-descriptions-item>
@@ -90,10 +90,12 @@
import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus';
import { computed, onBeforeMount, reactive, ref } from 'vue';
import { storeToRefs } from 'pinia';
import { MoneroWalletSyncStatus } from '@/types/moneroWallet/MoneroWalletSyncStatus';
import { WalletSyncStatus } from '@/types/wallet/WalletSyncStatus';
import { useMoneroWalletStore } from '@/stores/moneroWallet';
import { isMoneroStandardAddress } from '@/utils/monero/isMoneroStandardAddress';
import { resolveAxiosErrorMessage } from '@/utils/resolveAxiosErrorMessage';
import { resolveWalletSyncStatusLabel } from '@/utils/wallet/resolveWalletSyncStatusLabel';
import { resolveWalletSyncStatusTagType } from '@/utils/wallet/resolveWalletSyncStatusTagType';
const moneroWalletStore = useMoneroWalletStore();
@@ -125,7 +127,7 @@ const withdrawFormRules = computed<FormRules>(() => ({
destinationAddress: [
{
validator: (_rule, value, callback) => {
if (walletStatus.value?.syncStatus !== MoneroWalletSyncStatus.Synced) {
if (walletStatus.value?.syncStatus !== WalletSyncStatus.Synced) {
callback(new Error('Wait until the wallet finishes syncing before withdrawing'));
return;
@@ -287,26 +289,4 @@ const onRevealSeedClick = async (): Promise<void> => {
const clearSeed = (): void => {
revealedMnemonic.value = '';
};
const resolveMoneroWalletSyncStatusLabel = (syncStatus: MoneroWalletSyncStatus): string => {
switch (syncStatus) {
case MoneroWalletSyncStatus.Synced:
return 'Synced';
case MoneroWalletSyncStatus.Syncing:
return 'Syncing';
default:
return 'Unknown';
}
};
const resolveMoneroWalletSyncStatusTagType = (syncStatus: MoneroWalletSyncStatus): 'success' | 'warning' | 'info' => {
switch (syncStatus) {
case MoneroWalletSyncStatus.Synced:
return 'success';
case MoneroWalletSyncStatus.Syncing:
return 'warning';
default:
return 'info';
}
};
</script>
+2 -1
View File
@@ -7,7 +7,8 @@ export const ROUTE_NAMES = {
DiscountCodes: 'DiscountCodes',
ShopSettings: 'ShopSettings',
Notifications: 'Notifications',
Wallet: 'Wallet',
MoneroWallet: 'MoneroWallet',
BitcoinWallet: 'BitcoinWallet',
Orders: 'Orders',
OrderDetail: 'OrderDetail'
} as const;
+18 -3
View File
@@ -57,9 +57,10 @@ const router = createRouter({
path: '/settings',
component: () => import('../views/CmsSettingsLayout.vue'),
meta: { requiresAuth: true, title: 'CMS - Settings', activeMenu: '/settings' },
redirect: { name: ROUTE_NAMES.ShopSettings },
children: [
{
path: '',
path: 'shop',
name: ROUTE_NAMES.ShopSettings,
component: () => import('../views/CmsShopSettingsView.vue'),
meta: { requiresAuth: true, title: 'CMS - Shop settings', activeMenu: '/settings' }
@@ -74,9 +75,23 @@ const router = createRouter({
},
{
path: '/wallet',
name: ROUTE_NAMES.Wallet,
component: () => import('../views/CmsWalletView.vue'),
meta: { requiresAuth: true, title: 'CMS - Wallet' }
meta: { requiresAuth: true, title: 'CMS - Wallet', activeMenu: '/wallet' },
redirect: { name: ROUTE_NAMES.MoneroWallet },
children: [
{
path: 'monero',
name: ROUTE_NAMES.MoneroWallet,
component: () => import('../components/wallet/MoneroWallet.vue'),
meta: { requiresAuth: true, title: 'CMS - Monero wallet', activeMenu: '/wallet' }
},
{
path: 'bitcoin',
name: ROUTE_NAMES.BitcoinWallet,
component: () => import('../components/wallet/BitcoinWallet.vue'),
meta: { requiresAuth: true, title: 'CMS - Bitcoin wallet', activeMenu: '/wallet' }
}
]
},
{
path: '/:pathMatch(.*)*',
+21
View File
@@ -0,0 +1,21 @@
import { defineStore } from 'pinia';
import { ref } from 'vue';
import { api } from '@/plugins/axios';
import type { BitcoinWalletStatus } from '@/types/bitcoinWallet/BitcoinWalletStatus';
export const useBitcoinWalletStore = defineStore('bitcoinWallet', () => {
const status = ref<BitcoinWalletStatus | null>(null);
const fetchStatus = async (): Promise<BitcoinWalletStatus> => {
const { data } = await api.get<BitcoinWalletStatus>('/bitcoin-wallet');
status.value = data;
return data;
};
return {
status,
fetchStatus
};
});
@@ -0,0 +1 @@
export type BitcoinNetwork = 'mainnet' | 'testnet4';
@@ -0,0 +1,12 @@
import type { BitcoinNetwork } from './BitcoinNetwork';
import type { WalletSyncStatus } from '../wallet/WalletSyncStatus';
export interface BitcoinWalletStatus {
network: BitcoinNetwork;
rpcVersion: string;
blockHeight: number | null;
serverHeight: number | null;
syncStatus: WalletSyncStatus;
balanceBtc: string;
confirmedBalanceBtc: string;
}
@@ -1,12 +1,12 @@
import type { MoneroNetwork } from './MoneroNetwork';
import type { MoneroWalletSyncStatus } from './MoneroWalletSyncStatus';
import type { WalletSyncStatus } from '../wallet/WalletSyncStatus';
export interface MoneroWalletStatus {
network: MoneroNetwork;
rpcVersion: string;
walletHeight: number;
daemonHeight: number | null;
syncStatus: MoneroWalletSyncStatus;
syncStatus: WalletSyncStatus;
balanceXmr: string;
unlockedBalanceXmr: string;
}
+1
View File
@@ -4,6 +4,7 @@ import type { InvoiceStatusLabel } from './InvoiceStatusLabel';
export type InvoiceExtended = Omit<Invoice, 'payments'> & {
statusLabel: InvoiceStatusLabel | null;
paymentLabel: string;
expectedTotalCrypto: string;
payments: InvoicePaymentExtended[];
};
-5
View File
@@ -2,8 +2,3 @@ export enum PaymentMethod {
Xmr = 'xmr',
Btc = 'btc'
}
export const paymentMethodCryptoCurrency: Record<PaymentMethod, string> = {
[PaymentMethod.Xmr]: 'XMR',
[PaymentMethod.Btc]: 'BTC'
};
@@ -1,4 +0,0 @@
export interface BitcoinConfirmationTier {
upToTotalFiat?: string;
minConfirmations: number;
}
@@ -1,4 +1,4 @@
export interface MoneroConfirmationTier {
export interface ConfirmationTier {
upToTotalFiat?: string;
minConfirmations: number;
}
@@ -1,5 +1,5 @@
import type { BitcoinConfirmationTier } from './BitcoinConfirmationTier';
import type { ConfirmationTier } from './ConfirmationTier';
export interface ShopSettingsBitcoin {
confirmationTiers: BitcoinConfirmationTier[];
confirmationTiers: ConfirmationTier[];
}
@@ -1,5 +1,5 @@
import type { MoneroConfirmationTier } from './MoneroConfirmationTier';
import type { ConfirmationTier } from './ConfirmationTier';
export interface ShopSettingsMonero {
confirmationTiers: MoneroConfirmationTier[];
confirmationTiers: ConfirmationTier[];
}
@@ -1,4 +1,4 @@
export enum MoneroWalletSyncStatus {
export enum WalletSyncStatus {
Synced = 'synced',
Syncing = 'syncing',
Unknown = 'unknown'
@@ -0,0 +1,12 @@
import { WalletSyncStatus } from '@/types/wallet/WalletSyncStatus';
export const resolveWalletSyncStatusLabel = (syncStatus: WalletSyncStatus): string => {
switch (syncStatus) {
case WalletSyncStatus.Synced:
return 'Synced';
case WalletSyncStatus.Syncing:
return 'Syncing';
default:
return 'Unknown';
}
};
@@ -0,0 +1,12 @@
import { WalletSyncStatus } from '@/types/wallet/WalletSyncStatus';
export const resolveWalletSyncStatusTagType = (syncStatus: WalletSyncStatus): 'success' | 'warning' | 'info' => {
switch (syncStatus) {
case WalletSyncStatus.Synced:
return 'success';
case WalletSyncStatus.Syncing:
return 'warning';
default:
return 'info';
}
};
+10 -3
View File
@@ -37,7 +37,14 @@
<el-descriptions-item label="Monero confirmation tiers">
<ul class="m-0 pl-20">
<li v-for="(tier, index) in settings.monero.confirmationTiers" :key="index">
{{ formatMoneroConfirmationTier(tier, settings.shopFiatCurrency) }}
{{ formatConfirmationTier(tier, settings.shopFiatCurrency) }}
</li>
</ul>
</el-descriptions-item>
<el-descriptions-item label="Bitcoin confirmation tiers">
<ul class="m-0 pl-20">
<li v-for="(tier, index) in settings.bitcoin.confirmationTiers" :key="index">
{{ formatConfirmationTier(tier, settings.shopFiatCurrency) }}
</li>
</ul>
</el-descriptions-item>
@@ -210,7 +217,7 @@
<script setup lang="ts">
import { config } from '@/config';
import { useShopSettingsStore } from '@/stores/shopSettings';
import type { MoneroConfirmationTier } from '@/types/shopSettings/MoneroConfirmationTier';
import type { ConfirmationTier } from '@/types/shopSettings/ConfirmationTier';
import { buildUploadHint } from '@/utils/upload/buildUploadHint';
import { resolveAxiosErrorMessage } from '@/utils/resolveAxiosErrorMessage';
import { resolveUploadPublicUrl } from '@/utils/upload/resolveUploadPublicUrl';
@@ -460,7 +467,7 @@ const submitShippingNote = async (): Promise<void> => {
}
};
const formatMoneroConfirmationTier = (tier: MoneroConfirmationTier, currency: string): string => {
const formatConfirmationTier = (tier: ConfirmationTier, currency: string): string => {
const requirement = tier.minConfirmations === 0 ? '0 (tx-detected)' : `${tier.minConfirmations} confirmations`;
if (tier.upToTotalFiat === undefined) {
+26 -1
View File
@@ -2,6 +2,31 @@
<div>
<h3 class="m-0 mb-16">Wallet</h3>
<monero-wallet />
<el-tabs :model-value="activeTab" class="mb-24" @tab-change="onTabChange">
<el-tab-pane label="Monero (XMR)" name="monero" />
<el-tab-pane label="Bitcoin (BTC)" name="bitcoin" />
</el-tabs>
<router-view />
</div>
</template>
<script setup lang="ts">
import { ROUTE_NAMES } from '@/consts/routeNames';
import { computed } from 'vue';
import { useRoute, useRouter } from 'vue-router';
const route = useRoute();
const router = useRouter();
const activeTab = computed(() => (route.name === ROUTE_NAMES.BitcoinWallet ? 'bitcoin' : 'monero'));
const onTabChange = (tabName: string | number) => {
if (tabName === 'bitcoin') {
router.push({ name: ROUTE_NAMES.BitcoinWallet });
return;
}
router.push({ name: ROUTE_NAMES.MoneroWallet });
};
</script>