2023-02-27 20:41:58 -03:00

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 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 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>