427 lines
13 KiB
Vue
427 lines
13 KiB
Vue
<script setup lang="ts">
|
|
import { withdrawDeposit } from "@/blockchain/buyerMethods";
|
|
import { NetworkEnum } from "@/model/NetworkEnum";
|
|
import type { ValidDeposit } from "@/model/ValidDeposit";
|
|
import type { WalletTransaction } from "@/model/WalletTransaction";
|
|
import { useEtherStore } from "@/store/ether";
|
|
import { storeToRefs } from "pinia";
|
|
import { ref, watch, onMounted } from "vue";
|
|
import SpinnerComponent from "../SpinnerComponent.vue";
|
|
import { decimalCount } from "@/utils/decimalCount";
|
|
import { debounce } from "@/utils/debounce";
|
|
import { useFloating, arrow, offset, flip, shift } from "@floating-ui/vue";
|
|
|
|
const etherStore = useEtherStore();
|
|
|
|
// props
|
|
const props = defineProps<{
|
|
validDeposits: ValidDeposit[];
|
|
walletTransactions: WalletTransaction[];
|
|
activeLockAmount: number;
|
|
}>();
|
|
|
|
const emit = defineEmits(["depositWithdrawn"]);
|
|
|
|
const { loadingWalletTransactions } = storeToRefs(etherStore);
|
|
const remaining = ref<number>(0.0);
|
|
const itemsToShow = ref<WalletTransaction[]>([]);
|
|
const withdrawAmount = ref<string>("");
|
|
const withdrawButtonOpacity = ref<number>(0.6);
|
|
const withdrawButtonCursor = ref<string>("not-allowed");
|
|
const isCollapsibleOpen = ref<boolean>(false);
|
|
const validDecimals = ref<boolean>(true);
|
|
const validWithdrawAmount = ref<boolean>(true);
|
|
const enableConfirmButton = ref<boolean>(false);
|
|
const showInfoTooltip = ref<boolean>(false);
|
|
const floatingArrow = ref(null);
|
|
|
|
const reference = ref<HTMLElement | null>(null);
|
|
const floating = ref<HTMLElement | null>(null);
|
|
const infoText = ref<HTMLElement | null>(null);
|
|
|
|
// Debounce methods
|
|
const handleInputEvent = (event: any): void => {
|
|
const { value } = event.target;
|
|
|
|
if (decimalCount(String(value)) > 2) {
|
|
validDecimals.value = false;
|
|
enableConfirmButton.value = false;
|
|
return;
|
|
}
|
|
validDecimals.value = true;
|
|
|
|
if (value > remaining.value) {
|
|
validWithdrawAmount.value = false;
|
|
enableConfirmButton.value = false;
|
|
return;
|
|
}
|
|
validWithdrawAmount.value = true;
|
|
enableConfirmButton.value = true;
|
|
};
|
|
|
|
const callWithdraw = async () => {
|
|
if (withdrawAmount.value) {
|
|
const withdraw = await withdrawDeposit(withdrawAmount.value);
|
|
if (withdraw) {
|
|
console.log(withdraw);
|
|
alert("Saque realizado!");
|
|
emit("depositWithdrawn");
|
|
}
|
|
}
|
|
};
|
|
|
|
watch(withdrawAmount, (): void => {
|
|
if (!withdrawAmount.value) {
|
|
withdrawButtonOpacity.value = 0.7;
|
|
withdrawButtonCursor.value = "not-allowed";
|
|
} else {
|
|
withdrawButtonOpacity.value = 1;
|
|
withdrawButtonCursor.value = "pointer";
|
|
}
|
|
});
|
|
|
|
const getRemaining = (): number => {
|
|
if (props.validDeposits instanceof Array) {
|
|
// Here we are getting only the first element of the list because
|
|
// in this release only the BRL token is being used.
|
|
const deposit = props.validDeposits[0];
|
|
remaining.value = deposit ? deposit.remaining : 0;
|
|
return deposit ? deposit.remaining : 0;
|
|
}
|
|
return 0;
|
|
};
|
|
|
|
const getExplorer = (): string => {
|
|
return etherStore.networkName == NetworkEnum.ethereum
|
|
? "Etherscan"
|
|
: "Polygonscan";
|
|
};
|
|
|
|
const showInitialItems = (): void => {
|
|
itemsToShow.value = props.walletTransactions.slice(0, 3);
|
|
};
|
|
|
|
const openEtherscanUrl = (transactionHash: string): void => {
|
|
const networkUrl =
|
|
etherStore.networkName == NetworkEnum.ethereum
|
|
? "goerli.etherscan.io"
|
|
: "mumbai.polygonscan.com";
|
|
const url = `https://${networkUrl}/tx/${transactionHash}`;
|
|
window.open(url, "_blank");
|
|
};
|
|
|
|
const loadMore = (): void => {
|
|
const itemsShowing = itemsToShow.value.length;
|
|
itemsToShow.value?.push(
|
|
...props.walletTransactions.slice(itemsShowing, itemsShowing + 3)
|
|
);
|
|
};
|
|
|
|
const getEventName = (event: string | undefined): string => {
|
|
if (!event) return "Desconhecido";
|
|
|
|
const possibleEventName: { [key: string]: string } = {
|
|
DepositAdded: "Oferta",
|
|
LockAdded: "Reserva",
|
|
LockReleased: "Compra",
|
|
DepositWithdrawn: "Retirada",
|
|
};
|
|
|
|
return possibleEventName[event];
|
|
};
|
|
|
|
onMounted(() => {
|
|
useFloating(reference, floating, {
|
|
placement: "right",
|
|
middleware: [
|
|
offset(10),
|
|
flip(),
|
|
shift(),
|
|
arrow({ element: floatingArrow }),
|
|
],
|
|
});
|
|
});
|
|
|
|
// watch props changes
|
|
watch(props, async (): Promise<void> => {
|
|
const itemsToShowQty = itemsToShow.value.length;
|
|
if (itemsToShowQty == 0) showInitialItems();
|
|
else
|
|
itemsToShow.value =
|
|
props.walletTransactions.length > itemsToShowQty
|
|
? props.walletTransactions.slice(0, itemsToShowQty)
|
|
: props.walletTransactions;
|
|
});
|
|
|
|
// initial itemsToShow valueb
|
|
showInitialItems();
|
|
</script>
|
|
|
|
<template>
|
|
<div class="blur-container" v-if="loadingWalletTransactions">
|
|
<SpinnerComponent width="8" height="8"></SpinnerComponent>
|
|
</div>
|
|
<div class="blur-container" v-if="!loadingWalletTransactions">
|
|
<div
|
|
class="w-full bg-white p-4 sm:p-6 rounded-lg"
|
|
v-if="props.validDeposits.length > 0"
|
|
>
|
|
<div class="flex justify-between items-center">
|
|
<div>
|
|
<p class="text-sm leading-5 font-medium text-gray-600">
|
|
Saldo disponível
|
|
</p>
|
|
<p class="text-xl leading-7 font-semibold text-gray-900">
|
|
{{ getRemaining() }} BRZ
|
|
</p>
|
|
<div class="flex gap-2 w-32 sm:w-56" v-if="activeLockAmount != 0">
|
|
<span class="text-xs font-normal text-gray-400" ref="infoText">{{
|
|
`com ${activeLockAmount.toFixed(2)} BRZ em lock`
|
|
}}</span>
|
|
<div
|
|
class="absolute mt-[2px] md-view"
|
|
:style="{ left: `${(infoText?.clientWidth ?? 108) + 4}px` }"
|
|
>
|
|
<img
|
|
alt="info image"
|
|
src="@/assets/info.svg"
|
|
aria-describedby="tooltip"
|
|
ref="reference"
|
|
@mouseover="showInfoTooltip = true"
|
|
@mouseout="showInfoTooltip = false"
|
|
/>
|
|
<div
|
|
role="tooltip"
|
|
ref="floating"
|
|
class="w-56 z-50 tooltip md-view"
|
|
v-if="showInfoTooltip"
|
|
>
|
|
Valor “em lock” significa que a quantia está aguardando
|
|
confirmação de compra e só estará disponível para saque caso a
|
|
transação expire.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div v-show="!isCollapsibleOpen" class="flex justify-end items-center">
|
|
<div
|
|
class="flex gap-2 cursor-pointer items-center justify-self-center border-2 p-2 border-amber-300 rounded-md"
|
|
@click="[(isCollapsibleOpen = true)]"
|
|
>
|
|
<img
|
|
alt="Withdraw image"
|
|
src="@/assets/withdraw.svg"
|
|
class="w-3 h-3 sm:w-4 sm:h-4"
|
|
/>
|
|
<span class="last-release-info">Sacar</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="pt-5">
|
|
<div v-show="isCollapsibleOpen" class="py-2 w-100">
|
|
<p class="text-sm leading-5 font-medium">Valor do saque</p>
|
|
<input
|
|
type="number"
|
|
name=""
|
|
@input="debounce(handleInputEvent, 500)($event)"
|
|
placeholder="0"
|
|
class="text-2xl text-gray-900 w-full outline-none"
|
|
v-model="withdrawAmount"
|
|
/>
|
|
</div>
|
|
<div class="flex justify-center" v-if="!validDecimals">
|
|
<span class="text-red-500 font-normal text-sm"
|
|
>Por favor utilize no máximo 2 casas decimais</span
|
|
>
|
|
</div>
|
|
<div class="flex justify-center" v-else-if="!validWithdrawAmount">
|
|
<span class="text-red-500 font-normal text-sm"
|
|
>Saldo insuficiente</span
|
|
>
|
|
</div>
|
|
<hr v-show="isCollapsibleOpen" class="pb-3" />
|
|
<div
|
|
v-show="isCollapsibleOpen"
|
|
class="flex justify-between items-center"
|
|
>
|
|
<h1
|
|
@click="[(isCollapsibleOpen = false)]"
|
|
class="text-black font-medium cursor-pointer"
|
|
>
|
|
Cancelar
|
|
</h1>
|
|
|
|
<div
|
|
v-if="enableConfirmButton"
|
|
class="withdraw-button flex gap-2 items-center justify-self-center border-2 p-2 border-amber-300 rounded-md"
|
|
@click="callWithdraw"
|
|
>
|
|
<img
|
|
alt="Withdraw image"
|
|
src="@/assets/withdraw.svg"
|
|
class="w-3 h-3 sm:w-4 sm:h-4"
|
|
/>
|
|
<span class="last-release-info">Sacar</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div
|
|
class="w-full bg-white p-4 sm:p-6 rounded-lg"
|
|
v-for="item in itemsToShow"
|
|
:key="item.blockNumber"
|
|
>
|
|
<div class="item-container">
|
|
<div class="flex flex-col self-start">
|
|
<span class="text-xs sm:text-sm leading-5 font-medium text-gray-600">
|
|
{{ getEventName(item.event) }}
|
|
</span>
|
|
<span
|
|
class="text-xl sm:text-xl leading-7 font-semibold text-gray-900"
|
|
>
|
|
{{ item.amount }}
|
|
BRZ
|
|
</span>
|
|
</div>
|
|
<div>
|
|
<div
|
|
class="bg-amber-300 status-text"
|
|
v-if="getEventName(item.event) == 'Reserva' && item.lockStatus == 1"
|
|
>
|
|
Em Aberto
|
|
</div>
|
|
<div
|
|
class="bg-[#94A3B8] status-text"
|
|
v-if="getEventName(item.event) == 'Reserva' && item.lockStatus == 2"
|
|
>
|
|
Expirado
|
|
</div>
|
|
<div
|
|
class="bg-emerald-300 status-text"
|
|
v-if="
|
|
(getEventName(item.event) == 'Reserva' && item.lockStatus == 3) ||
|
|
getEventName(item.event) != 'Reserva'
|
|
"
|
|
>
|
|
Finalizado
|
|
</div>
|
|
<div
|
|
class="flex gap-2 cursor-pointer items-center justify-self-center w-full"
|
|
@click="openEtherscanUrl(item.transactionHash)"
|
|
v-if="getEventName(item.event) != 'Reserva' || item.lockStatus != 1"
|
|
>
|
|
<span class="last-release-info">{{ getExplorer() }}</span>
|
|
<img
|
|
alt="Redirect image"
|
|
src="@/assets/redirect.svg"
|
|
class="w-3 h-3 sm:w-4 sm:h-4"
|
|
/>
|
|
</div>
|
|
<div
|
|
class="flex gap-2 justify-self-center w-full"
|
|
v-if="getEventName(item.event) == 'Reserva' && item.lockStatus == 1"
|
|
>
|
|
<RouterLink
|
|
:to="{
|
|
name: 'home',
|
|
force: true,
|
|
state: { lockID: item.transactionID },
|
|
}"
|
|
class="router-button"
|
|
>Continuar</RouterLink
|
|
>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div
|
|
class="flex flex-col justify-center items-center w-full mt-2 gap-2"
|
|
v-if="
|
|
itemsToShow.length != 0 &&
|
|
itemsToShow.length != props.walletTransactions.length
|
|
"
|
|
>
|
|
<button
|
|
type="button"
|
|
class="text-white font-semibold"
|
|
@click="loadMore()"
|
|
>
|
|
Carregar mais
|
|
</button>
|
|
<span class="text-gray-300">
|
|
({{ itemsToShow.length }} de {{ props.walletTransactions.length }}
|
|
transações )
|
|
</span>
|
|
</div>
|
|
|
|
<span class="font-bold text-gray-300" v-if="itemsToShow.length == 0">
|
|
Não há nenhuma transação anterior
|
|
</span>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.page {
|
|
@apply flex flex-col items-center justify-center w-full mt-16;
|
|
}
|
|
|
|
p {
|
|
@apply text-gray-900;
|
|
}
|
|
|
|
.text-container {
|
|
@apply flex flex-col items-center justify-center gap-4;
|
|
}
|
|
|
|
.item-container {
|
|
@apply flex justify-between items-center;
|
|
}
|
|
|
|
.status-text {
|
|
@apply text-xs sm:text-base font-medium text-gray-900 rounded-lg text-center mb-2 px-2 py-1 mt-4;
|
|
}
|
|
.text {
|
|
@apply text-white text-center;
|
|
}
|
|
|
|
.blur-container {
|
|
@apply flex flex-col justify-center items-center px-4 py-3 sm:px-8 sm:py-6 gap-4 rounded-lg shadow-md shadow-gray-600 backdrop-blur-md w-auto;
|
|
}
|
|
|
|
.grid-container {
|
|
@apply grid grid-cols-4 grid-flow-row items-center px-8 py-6 gap-4 rounded-lg shadow-md shadow-gray-600 backdrop-blur-md mt-10 w-auto;
|
|
}
|
|
|
|
.last-release-info {
|
|
@apply font-medium text-xs sm:text-sm text-gray-900 justify-self-center;
|
|
}
|
|
|
|
.tooltip {
|
|
@apply bg-white text-gray-900 font-medium text-xs md:text-base px-3 py-2 rounded border-2 border-emerald-500 left-5 top-[-3rem];
|
|
}
|
|
|
|
.router-button {
|
|
@apply rounded-lg border-amber-300 border-2 px-3 py-2 text-gray-900 font-semibold sm:text-base text-xs hover:bg-transparent w-full text-center;
|
|
}
|
|
|
|
.withdraw-button {
|
|
opacity: v-bind(withdrawButtonOpacity);
|
|
cursor: v-bind(withdrawButtonCursor);
|
|
}
|
|
|
|
input[type="number"] {
|
|
-moz-appearance: textfield;
|
|
}
|
|
|
|
input[type="number"]::-webkit-inner-spin-button,
|
|
input[type="number"]::-webkit-outer-spin-button {
|
|
-webkit-appearance: none;
|
|
}
|
|
|
|
@media screen and (max-width: 640px) {
|
|
.md-view {
|
|
display: none;
|
|
}
|
|
}
|
|
</style>
|