Skip to content

Commit

Permalink
#3 Added the closed deals page
Browse files Browse the repository at this point in the history
  • Loading branch information
simba77 committed Nov 26, 2023
1 parent 923b3c0 commit ea8b5cb
Show file tree
Hide file tree
Showing 18 changed files with 605 additions and 25 deletions.
59 changes: 59 additions & 0 deletions assets/components/Analytics/ClosedDealsGroupComponent.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<script setup lang="ts">
import {LockClosedIcon} from "@heroicons/vue/24/outline";
import helpers from "../../helpers";
import {ClosedDealsGroup} from "@/types/analytics";
function formatProfit(asset: { profit: number; currency: string; }) {
return (asset.profit > 0 ? '+' : '-') + ' ' + helpers.formatPrice(Math.abs(asset.profit)) + ' ' + asset.currency;
}
const emits = defineEmits<{ showChildren: [] }>()
defineProps<{
item: ClosedDealsGroup
}>();
</script>

<template>
<tr
class="tr-clickable"
@click="emits('showChildren')"
>
<td class="underline">
<div class="font-extrabold">
<lock-closed-icon
v-if="item.isBlocked"
class="h-3 w-3 inline-block"
/>
{{ item.shortName }}
</div>
<div class="text-gray-500">
<span class="text-xs">{{ item.ticker }}</span>
<span
v-if="item.isShort"
class="bg-red-200 text-red-900 rounded-full inline-flex pr-2 pl-2 items-center ml-2"
>short</span>
</div>
</td>
<td>{{ item.quantity }}</td>
<td>
<div>{{ helpers.formatPrice(item.buyPrice) }} {{ item.currency }}</div>
<div class="text-xs text-gray-500">
{{ helpers.formatPrice(item.fullBuyPrice) }} {{ item.currency }}
</div>
</td>
<td>
<div>{{ helpers.formatPrice(item.sellPrice) }} {{ item.currency }}</div>
<div class="text-xs text-gray-500">
{{ helpers.formatPrice(item.fullSellPrice) }} {{ item.currency }}
</div>
</td>
<td :class="[item.profit > 0 ? 'text-green-600' : 'text-red-700']">
<div>{{ formatProfit(item) }}</div>
<div class="text-xs">
({{ item.profitPercent }}%, {{ item.commission }} {{ item.currency }})
</div>
</td>
</tr>
</template>
54 changes: 54 additions & 0 deletions assets/components/Analytics/ClosedDealsItemComponent.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<script setup lang="ts">
import {LockClosedIcon} from "@heroicons/vue/24/outline";
import helpers from "../../helpers";
import {ClosedDeal} from "@/types/analytics";
function formatProfit(asset: { profit: number; currency: string; }) {
return (asset.profit > 0 ? '+' : '-') + ' ' + helpers.formatPrice(Math.abs(asset.profit)) + ' ' + asset.currency;
}
defineProps<{
item: ClosedDeal,
}>();
</script>

<template>
<tr>
<td v-tooltip="{html: true, content: 'Created At: ' + item.createdAt + '<br>Updated At: ' + item.createdAt}">
<div class="font-extrabold">
<lock-closed-icon
v-if="item.isBlocked"
class="h-3 w-3 inline-block"
/>
{{ item.shortName }}
</div>
<div class="text-gray-500">
<span class="text-xs">{{ item.ticker }}</span>
<span
v-if="item.isShort"
class="bg-red-200 text-red-900 rounded-full inline-flex pr-2 pl-2 items-center ml-2"
>short</span>
</div>
</td>
<td>{{ item.quantity }}</td>
<td>
<div>{{ helpers.formatPrice(item.buyPrice) }} {{ item.currency }}</div>
<div class="text-xs text-gray-500">
{{ helpers.formatPrice(item.fullBuyPrice) }} {{ item.currency }}
</div>
</td>
<td>
<div>{{ helpers.formatPrice(item.sellPrice) }} {{ item.currency }}</div>
<div class="text-xs text-gray-500">
{{ helpers.formatPrice(item.fullSellPrice) }} {{ item.currency }}
</div>
</td>

<td :class="[item.profit > 0 ? 'text-green-600' : 'text-red-700']">
<div>{{ formatProfit(item) }}</div>
<div class="text-xs">
({{ item.profitPercent }}%, {{ item.commission }} {{ item.currency }})
</div>
</td>
</tr>
</template>
111 changes: 111 additions & 0 deletions assets/components/Analytics/ClosedDealsTableComponent.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<script setup lang="ts">
import {useDealsGroup} from "@/composable/useDealsGroup";
import ClosedDealsGroupComponent from "@/components/Analytics/ClosedDealsGroupComponent.vue";
import ClosedDealsItemComponent from "@/components/Analytics/ClosedDealsItemComponent.vue";
import {ClosedDealsListItem} from "@/types/analytics";
defineProps<{ assets: ClosedDealsListItem[] }>()
const dealsGroup = useDealsGroup()
</script>

<template>
<table class="simple-table sub-table white-header">
<thead>
<tr>
<th>Name</th>
<th>Quantity</th>
<th>Buy Price</th>
<th>Sell Price</th>
<th>
Profit
<div class="text-xs text-gray-400">
(percent, commission)
</div>
</th>
</tr>
</thead>

<tbody>
<template
v-for="(asset, i) in assets"
:key="i"
>
<!-- Asset block -->
<!-- Parent row with assets -->
<closed-deals-group-component
:item="asset.groupData"
:clickable="true"
@show-children="dealsGroup.toggleGroup(asset.groupData.ticker)"
/>

<!-- Children table -->
<tr v-if="dealsGroup.openedGroups.value[asset.groupData.ticker]">
<td
colspan="111"
class="!p-2 !bg-white"
>
<table class="simple-table sub-table white-header">
<thead>
<tr>
<th>Name</th>
<th>Quantity</th>
<th>Buy Price</th>
<th>Sell Price</th>
<th>
Profit
<div class="text-xs text-gray-400">
(percent, commission)
</div>
</th>
</tr>
</thead>
<tbody>
<template
v-for="(subItem, subIndex) in asset.deals"
:key="'sub' + subIndex"
>
<closed-deals-item-component :item="subItem" />
</template>
</tbody>
</table>
</td>
</tr>
</template>

<!-- Total row -->
<!-- <tr class="font-bold">
<td>Subtotal:</td>
<td />
<td>
<div v-if="!summary.isBaseCurrency">
{{ helpers.formatPrice(summary.buyPrice) }} $
</div>
<div>{{ helpers.formatPrice(summary.buyPriceInBaseCurrency) }} ₽</div>
</td>
<td>
<div v-if="!summary.isBaseCurrency">
{{ helpers.formatPrice(summary.currentPrice) }} $
</div>
<div>{{ helpers.formatPrice(summary.currentPriceInBaseCurrency) }} ₽</div>
</td>
<td />
<td :class="[summary.profit > 0 ? 'text-green-600' : 'text-red-700']">
<div v-if="!summary.isBaseCurrency">
{{ helpers.formatPrice(summary.profit) }} $
</div>
<div>{{ helpers.formatPrice(summary.profitInBaseCurrency) }} ₽</div>
<div class="text-xs">
({{ summary.profitPercent }}%)
</div>
</td>
<td />
<td />
<td class="table-actions" />
</tr>-->
</tbody>
</table>
</template>

6 changes: 3 additions & 3 deletions assets/components/PageComponent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ const navigation = [
current: route.name === 'Accounts'
},
{
name: 'Sold Assets',
routeName: 'SoldAssets',
current: route.name === 'SoldAssets'
name: 'Closed Deals',
routeName: 'ClosedDeals',
current: route.name === 'ClosedDeals'
}
]
const userNavigation = [
Expand Down
16 changes: 6 additions & 10 deletions assets/composable/useAnalytics.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
import {ref} from "vue";
import {AssetsGroup} from "@/types/account";
import axios from "axios";
import useAsync from "@/utils/use-async";
import {ClosedDealsListItem} from "@/types/analytics";

const assets = ref<AssetsGroup[]>([])
const closedDeals = ref<{deals: ClosedDealsListItem[]}>()

export default function () {
async function getAssets() {
assets.value = await axios.get('/api/analytics').then((response) => response.data);
async function getClosedDeals() {
closedDeals.value = await axios.get('/api/analytics/closed-deals').then((response) => response.data);
}

const {loading, run: asyncGetAssets} = useAsync(getAssets)

return {
assets,
loading,
getAssets: asyncGetAssets
getClosedDeals,
closedDeals
}
}
22 changes: 22 additions & 0 deletions assets/pages/Accounts/ClosedDealsPage.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<script setup lang="ts">
import PageComponent from "../../components/PageComponent.vue";
import useAnalytics from "@/composable/useAnalytics";
import useAsync from "@/utils/use-async";
import PreloaderComponent from "@/components/Common/PreloaderComponent.vue";
import ClosedDealsTableComponent from "@/components/Analytics/ClosedDealsTableComponent.vue";
const {closedDeals, getClosedDeals} = useAnalytics()
const {loading, run} = useAsync(() => getClosedDeals())
run()
</script>

<template>
<page-component title="Closed Deals">
<preloader-component v-if="loading" />
<div v-else-if="closedDeals">
<closed-deals-table-component :assets="closedDeals.deals" />
</div>
</page-component>
</template>
9 changes: 0 additions & 9 deletions assets/pages/Accounts/SoldAssetsPage.vue

This file was deleted.

6 changes: 3 additions & 3 deletions assets/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,12 +135,12 @@ const routes: Array<RouteRecordRaw> = [
component: () => import('./pages/Accounts/AssetForm.vue')
},
{
name: 'SoldAssets',
path: '/sold-assets',
name: 'ClosedDeals',
path: '/closed-deals',
meta: {
requiresAuth: true,
},
component: () => import('./pages/Accounts/SoldAssetsPage.vue')
component: () => import('./pages/Accounts/ClosedDealsPage.vue')
},

// Savings
Expand Down
40 changes: 40 additions & 0 deletions assets/types/analytics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
export interface ClosedDealsListItem {
groupData: ClosedDealsGroup
deals: ClosedDeal[]
}

export interface ClosedDealsGroup {
ticker: string
shortName: string
quantity: number
buyPrice: number
fullBuyPrice: number
sellPrice: number
fullSellPrice: number
profit: number
profitPercent: number
commission: number
currency: string
isShort: boolean
isBlocked: boolean
}

export interface ClosedDeal {
id: number
accountId: number
ticker: string
shortName: string
quantity: number
buyPrice: number
fullBuyPrice: number
sellPrice: number
fullSellPrice: number
profit: number
profitPercent: number
commission: number
currency: string
isShort: boolean
isBlocked: boolean
createdAt: string
updatedAt: string
}
27 changes: 27 additions & 0 deletions src/Controller/AnalyticsController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace App\Controller;

use App\Entity\User;
use App\Request\DTO\Deals\DealsFilterRequestDTO;
use App\Services\Deals\ClosedDealsService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Attribute\MapQueryString;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\CurrentUser;

class AnalyticsController extends AbstractController
{
#[Route('/analytics/closed-deals', name: 'app_analytics_closed_deals', methods: 'GET')]
public function closedDeals(
ClosedDealsService $dealsService,
#[MapQueryString] ?DealsFilterRequestDTO $filter,
#[CurrentUser] ?User $user,
): JsonResponse {
$deals = $dealsService->getDeals($user, $filter);
return $this->json(['deals' => $deals]);
}
}
Loading

0 comments on commit ea8b5cb

Please sign in to comment.