|
|
@@ -1,63 +1,171 @@
|
|
|
<script>
|
|
|
+ import { onMount } from 'svelte';
|
|
|
+ import { get } from 'svelte/store';
|
|
|
import Header from '$lib/layout/Header.svelte';
|
|
|
import StatsCard from '$lib/components/StatsCard.svelte';
|
|
|
import PaymentMenu from '$lib/components/wallet/PaymentMenu.svelte';
|
|
|
import SellMenu from '$lib/components/wallet/SellMenu.svelte';
|
|
|
import tokensIcon from '$lib/assets/icons/sidebar/tokens.svg?raw';
|
|
|
import walletIcon from '$lib/assets/icons/sidebar/wallet.svg?raw';
|
|
|
+ import { authToken } from '$lib/utils/stores';
|
|
|
|
|
|
const breadcrumb = [{ label: 'Início' }, { label: 'wallet', active: true }];
|
|
|
+ const apiUrl = import.meta.env.VITE_API_URL;
|
|
|
|
|
|
- let easyTokens = [
|
|
|
- { id: 1, label: 'EasyToken Soja', amount: 1 },
|
|
|
- { id: 2, label: 'EasyToken Milho', amount: 1 },
|
|
|
- { id: 3, name: 'EasyToken Café', balance: 1 }
|
|
|
- ];
|
|
|
+ let walletTokens = [];
|
|
|
let easyCoinBalance = 0;
|
|
|
+ let tokensLoading = false;
|
|
|
+ let tokensError = '';
|
|
|
|
|
|
let isPaymentOpen = false;
|
|
|
let isSellOpen = false;
|
|
|
|
|
|
let rateBRLPerEasyCoin = 100;
|
|
|
|
|
|
- function openPayment() { isPaymentOpen = true; }
|
|
|
- function closePayment() { isPaymentOpen = false; }
|
|
|
- function confirmPayment(method) { isPaymentOpen = false; }
|
|
|
+ onMount(() => {
|
|
|
+ void fetchWalletTokens();
|
|
|
+ });
|
|
|
|
|
|
- function openSell() { isSellOpen = true; }
|
|
|
- function closeSell() { isSellOpen = false; }
|
|
|
+ function openPayment() {
|
|
|
+ isPaymentOpen = true;
|
|
|
+ }
|
|
|
+ function closePayment() {
|
|
|
+ isPaymentOpen = false;
|
|
|
+ }
|
|
|
+ function confirmPayment(method) {
|
|
|
+ isPaymentOpen = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ function openSell() {
|
|
|
+ isSellOpen = true;
|
|
|
+ }
|
|
|
+ function closeSell() {
|
|
|
+ isSellOpen = false;
|
|
|
+ }
|
|
|
function confirmSell(e) {
|
|
|
isSellOpen = false;
|
|
|
}
|
|
|
|
|
|
+ async function parseResponse(res) {
|
|
|
+ const raw = await res.text();
|
|
|
+ return raw ? JSON.parse(raw) : null;
|
|
|
+ }
|
|
|
+
|
|
|
+ async function fetchWalletTokens() {
|
|
|
+ if (!apiUrl) return;
|
|
|
+ tokensLoading = true;
|
|
|
+ tokensError = '';
|
|
|
+ try {
|
|
|
+ const token = get(authToken);
|
|
|
+ if (!token) {
|
|
|
+ throw new Error('Sessão expirada. Faça login novamente.');
|
|
|
+ }
|
|
|
+ const res = await fetch(`${apiUrl}/wallet/tokens`, {
|
|
|
+ method: 'POST',
|
|
|
+ headers: {
|
|
|
+ 'content-type': 'application/json',
|
|
|
+ Authorization: `Bearer ${token}`
|
|
|
+ }
|
|
|
+ });
|
|
|
+ const body = await parseResponse(res);
|
|
|
+ if (!res.ok || body?.status !== 'ok') {
|
|
|
+ throw new Error(body?.msg ?? 'Falha ao carregar tokens da wallet.');
|
|
|
+ }
|
|
|
+ const payload = body?.data ?? {};
|
|
|
+ walletTokens = Array.isArray(payload?.tokens) ? payload.tokens : [];
|
|
|
+ easyCoinBalance = resolveEasyCoinBalance(payload);
|
|
|
+ } catch (err) {
|
|
|
+ console.error('[Wallet] Erro ao buscar tokens:', err);
|
|
|
+ walletTokens = [];
|
|
|
+ easyCoinBalance = 0;
|
|
|
+ tokensError = err?.message ?? 'Não foi possível carregar os tokens.';
|
|
|
+ } finally {
|
|
|
+ tokensLoading = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function resolveEasyCoinBalance(payload) {
|
|
|
+ const candidates = [
|
|
|
+ payload?.wallet?.easycoin_balance,
|
|
|
+ payload?.wallet?.easyCoinBalance,
|
|
|
+ payload?.wallet?.balance,
|
|
|
+ payload?.easycoin_balance,
|
|
|
+ payload?.easyCoinBalance,
|
|
|
+ payload?.easycoin,
|
|
|
+ payload?.balance
|
|
|
+ ];
|
|
|
+ const firstValue = candidates.find((value) => value !== undefined && value !== null && value !== '');
|
|
|
+ return Number(firstValue ?? 0);
|
|
|
+ }
|
|
|
+
|
|
|
function formatToken(n) {
|
|
|
return new Intl.NumberFormat('pt-BR', { minimumFractionDigits: 0, maximumFractionDigits: 6 }).format(Number(n || 0));
|
|
|
}
|
|
|
function formatCoin(n) {
|
|
|
return new Intl.NumberFormat('pt-BR', { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(Number(n || 0));
|
|
|
}
|
|
|
+
|
|
|
+ function tokenCardLabel(token) {
|
|
|
+ return token?.cpr_product_name ?? token?.token_content ?? token?.token_external_id ?? token?.token_id ?? 'EasyToken';
|
|
|
+ }
|
|
|
+
|
|
|
+ function tokenCardSubtitle(token) {
|
|
|
+ if (token?.token_city && token?.token_uf) {
|
|
|
+ return `${token.token_city}/${token.token_uf}`;
|
|
|
+ }
|
|
|
+ if (token?.token_external_id) {
|
|
|
+ return `ID ${token.token_external_id}`;
|
|
|
+ }
|
|
|
+ return 'Saldo atual';
|
|
|
+ }
|
|
|
+
|
|
|
+ function tokenAmount(token) {
|
|
|
+ const candidates = [
|
|
|
+ token?.token_commodities_amount,
|
|
|
+ token?.token_amount,
|
|
|
+ token?.token_balance,
|
|
|
+ token?.balance,
|
|
|
+ token?.amount
|
|
|
+ ];
|
|
|
+ const value = candidates.find((item) => item !== undefined && item !== null && item !== '');
|
|
|
+ return Number(value ?? 0);
|
|
|
+ }
|
|
|
</script>
|
|
|
|
|
|
<div>
|
|
|
<Header title="Wallet" subtitle="Saldos e operações" breadcrumb={breadcrumb} />
|
|
|
|
|
|
<div class="p-4 space-y-6">
|
|
|
+ {#if tokensError}
|
|
|
+ <div class="rounded border border-red-200 bg-red-50 text-red-700 px-3 py-2 text-sm">{tokensError}</div>
|
|
|
+ {/if}
|
|
|
+
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
|
- {#each easyTokens as t}
|
|
|
- <div class="rounded-lg overflow-hidden">
|
|
|
- <StatsCard
|
|
|
- title={t.label || t.name || 'EasyToken'}
|
|
|
- value={formatToken(t.amount || t.balance || 0)}
|
|
|
- change="Saldo atual"
|
|
|
- iconSvg={tokensIcon}
|
|
|
- />
|
|
|
+ {#if tokensLoading && !walletTokens.length}
|
|
|
+ <div class="rounded-lg border border-dashed border-gray-300 dark:border-gray-600 bg-white/60 dark:bg-gray-800/60 p-4 text-center text-sm text-gray-500">
|
|
|
+ Carregando tokens...
|
|
|
+ </div>
|
|
|
+ {:else if walletTokens.length}
|
|
|
+ {#each walletTokens as token (token?.token_external_id ?? token?.token_id ?? token?.cpr_id ?? token)}
|
|
|
+ <div class="rounded-lg overflow-hidden">
|
|
|
+ <StatsCard
|
|
|
+ title={tokenCardLabel(token)}
|
|
|
+ value={formatToken(tokenAmount(token))}
|
|
|
+ change={tokenCardSubtitle(token)}
|
|
|
+ iconSvg={tokensIcon}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ {/each}
|
|
|
+ {:else}
|
|
|
+ <div class="rounded-lg border border-dashed border-gray-300 dark:border-gray-600 bg-white/60 dark:bg-gray-800/60 p-4 text-center text-sm text-gray-500">
|
|
|
+ Nenhum token encontrado para sua conta.
|
|
|
</div>
|
|
|
- {/each}
|
|
|
+ {/if}
|
|
|
<div class="rounded-lg overflow-hidden">
|
|
|
<StatsCard
|
|
|
title="EasyCoin"
|
|
|
value={formatCoin(easyCoinBalance)}
|
|
|
- change="Saldo atual"
|
|
|
+ change={tokensLoading ? 'Atualizando...' : 'Saldo atual'}
|
|
|
iconSvg={walletIcon}
|
|
|
/>
|
|
|
</div>
|