harb/web-app/src/components/collapse/CollapseActive.vue

248 lines
8.6 KiB
Vue
Raw Normal View History

2025-09-23 14:18:04 +02:00
<template>
<FCollapse class="f-collapse-active" @collapse:opened="loadActivePositionData" :loading="loading">
<template v-slot:header>
<div class="collapse-header">
<div class="collapse-header-row1">
<div><span class="subheader2">Tax</span> {{ props.taxRate }} %</div>
<FButton size="tiny" @click="payTax(props.id)">Pay Tax</FButton>
<div class="position-id">
<span class="subheader2">ID</span> <span class="number-small">{{ props.id }}</span>
</div>
</div>
<div class="collapse-header-row2">
<div>
<div class="profit-stats-item">
<div><b>Initial Stake</b></div>
<div>{{ compactNumber(props.amount) }} $KRK</div>
</div>
</div>
<div class="tags-list">
<FTag v-if="tag">{{ tag }}</FTag>
</div>
</div>
<!-- <div class="collapse-amount">
2025-09-23 14:18:04 +02:00
<span class="number-small">{{ compactNumber(props.amount) }}</span>
<span class="caption"> $KRK</span>
</div> -->
</div>
</template>
<div class="collapsed-body">
<div class="profit-stats-wrapper">
<div class="profit-stats-item">
<div><b>Tax Paid</b></div>
<div>{{ taxPaidGes }} $KRK</div>
</div>
<div class="profit-stats-item">
<div><b>Issuance Earned</b></div>
<div>{{ profit }} $KRK</div>
</div>
<div class="profit-stats-item profit-stats-total">
<div><b>Total</b></div>
<div>{{ total.toFixed(5) }} $KRK</div>
</div>
</div>
</div>
<div class="collapsed-body--actions">
<div :class="{ 'collapse-menu-open': showTaxMenu }">
<FButton size="small" dense block outlined v-if="adjustTaxRate.state === 'SignTransaction'">Sign Transaction ...</FButton>
<FButton size="small" dense outlined block v-else-if="adjustTaxRate.state === 'Waiting'" @click="unstakePosition"
>Waiting ...</FButton
>
<FButton size="small" dense block v-else-if="adjustTaxRate.state === 'Action' && !showTaxMenu" @click="showTaxMenu = true"
>Adjust Tax Rate</FButton
>
<template v-else>
<div class="collapse-menu-input">
<FSelect :items="filteredTaxRates" v-model="newTaxRateIndex"> </FSelect>
</div>
<div>
<FButton size="small" dense @click="changeTax(props.id, newTaxRateIndex)">Confirm</FButton>
<FButton size="small" dense outlined @click="showTaxMenu = false">Cancel</FButton>
</div>
</template>
</div>
<div></div>
<div>
<FButton size="small" dense block outlined v-if="unstake.state === 'SignTransaction'">Sign Transaction ...</FButton>
<FButton size="small" dense outlined block v-else-if="unstake.state === 'Waiting'">Waiting ...</FButton>
<FButton size="small" dense block v-else-if="unstake.state === 'Unstakeable'" @click="unstakePosition">Unstake</FButton>
</div>
</div>
</FCollapse>
2025-09-23 14:18:04 +02:00
</template>
<script setup lang="ts">
import FButton from '@/components/fcomponents/FButton.vue';
import FTag from '@/components/fcomponents/FTag.vue';
import FSelect from '@/components/fcomponents/FSelect.vue';
import FCollapse from '@/components/fcomponents/FCollapse.vue';
import { compactNumber, formatBigNumber } from '@/utils/helper';
import { useUnstake } from '@/composables/useUnstake';
import { useAdjustTaxRate } from '@/composables/useAdjustTaxRates';
import { computed, ref, onMounted } from 'vue';
import { getTaxDue, payTax } from '@/contracts/stake';
import { type Position, loadPositions } from '@/composables/usePositions';
import { useStatCollection } from '@/composables/useStatCollection';
2025-10-11 10:55:49 +00:00
import { useWallet } from '@/composables/useWallet';
import { DEFAULT_CHAIN_ID } from '@/config';
import { calculateActivePositionProfit } from 'kraiken-lib/position';
2025-09-23 14:18:04 +02:00
const unstake = useUnstake();
const adjustTaxRate = useAdjustTaxRate();
2025-10-11 10:55:49 +00:00
const wallet = useWallet();
const initialChainId = wallet.account.chainId ?? DEFAULT_CHAIN_ID;
const statCollection = useStatCollection(initialChainId);
const currentChainId = computed(() => wallet.account.chainId ?? DEFAULT_CHAIN_ID);
2025-09-23 14:18:04 +02:00
const props = defineProps<{
taxRate: number;
taxRateIndex?: number;
tresholdIndex: number;
id: bigint;
amount: number;
position: Position;
2025-09-23 14:18:04 +02:00
}>();
const showTaxMenu = ref(false);
const newTaxRateIndex = ref<number | null>(null);
2025-09-23 14:18:04 +02:00
const taxDue = ref<bigint>();
const taxPaidGes = ref<string>();
const profit = ref<number>();
const loading = ref<boolean>(false);
const tag = computed(() => {
// Compare by index instead of decimal to avoid floating-point issues
const idx = props.taxRateIndex ?? props.position.taxRateIndex;
if (typeof idx === 'number' && idx < props.tresholdIndex) {
return 'Low Tax!';
}
return '';
2025-09-23 14:18:04 +02:00
});
const total = computed(() => props.amount + profit.value! + -taxPaidGes.value!);
const filteredTaxRates = computed(() => {
const currentIndex = props.position.taxRateIndex ?? -1;
return adjustTaxRate.taxRates.filter(option => option.index > currentIndex);
});
async function changeTax(id: bigint, nextTaxRateIndex: number | null) {
if (typeof nextTaxRateIndex !== 'number') {
return;
}
await adjustTaxRate.changeTax(id, nextTaxRateIndex);
showTaxMenu.value = false;
2025-09-23 14:18:04 +02:00
}
async function unstakePosition() {
await unstake.exitPosition(props.id);
loading.value = true;
await new Promise(resolve => setTimeout(resolve, 5000));
2025-10-11 10:55:49 +00:00
await loadPositions(currentChainId.value);
loading.value = false;
2025-09-23 14:18:04 +02:00
}
async function loadActivePositionData() {
//loadTaxDue
taxDue.value = await getTaxDue(props.id);
taxPaidGes.value = formatBigNumber(taxDue.value + BigInt(props.position.taxPaid), 18);
2025-09-23 14:18:04 +02:00
//loadTotalSupply
2025-09-23 14:18:04 +02:00
// Calculate issuance earned using kraiken-lib profit calculation
profit.value = calculateActivePositionProfit(
props.position.totalSupplyInit,
statCollection.kraikenTotalSupply,
props.position.share
);
2025-09-23 14:18:04 +02:00
}
onMounted(() => {
const availableRates = filteredTaxRates.value;
if (availableRates.length > 0) {
newTaxRateIndex.value = availableRates[0]?.index ?? null;
} else if (typeof props.position.taxRateIndex === 'number') {
newTaxRateIndex.value = props.position.taxRateIndex;
} else {
newTaxRateIndex.value = adjustTaxRate.taxRates[0]?.index ?? null;
}
2025-09-23 14:18:04 +02:00
});
</script>
<style lang="sass">
@use 'collapse'
.f-collapse
&.f-collapse-active
background-color: #07111B
.collapse-header
width: 100%
display: flex
justify-content: space-between
flex-direction: column
position: relative
gap: 16px
.collapse-header-row1
display: flex
flex-direction: row
gap: 16px
align-items: center
// margin-right: 32px
.position-id
margin-left: auto
.collapse-header-row2
display: flex
justify-content: space-between
>div
width: 50%
min-height: 22px
.profit-stats-item
display: grid
grid-template-columns: 1fr 1fr
gap: 8px
div
&:last-child
text-align: right
.tags-list
margin-right: 32px
text-align: right
.collapsableDiv
.collapsed-body
display: flex
flex-direction: column
margin-right: 32px
.collapsed-body--header
justify-content: space-between
.profit-stats-wrapper
display: flex
flex-direction: column
gap: 4px
margin: 0
margin-top: 4px
width: 100%
color: white
@media (min-width: 768px)
width: 50%
margin: 4px auto 0 0
.profit-stats-item
display: grid
grid-template-columns: 1fr 1fr
gap: 8px
div
&:last-child
text-align: right
&.profit-stats-total
border-top: 2px solid var(--color-font)
padding-top: 2px
.collapsed-body--actions
.collapse-menu-open
display: flex
align-items: center
gap: 4px
flex: 1 1 auto
.collapse-menu-input
display: flex
align-items: center
</style>