wip more dashboard widgets
This commit is contained in:
+18
-4
@@ -3,11 +3,14 @@
|
||||
namespace App\Models;
|
||||
|
||||
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
|
||||
use Laravel\Sanctum\HasApiTokens;
|
||||
use Laravel\Jetstream\HasProfilePhoto;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Laravel\Fortify\TwoFactorAuthenticatable;
|
||||
use Staudenmeir\EloquentHasManyDeep\HasManyDeep;
|
||||
use Illuminate\Database\Eloquent\Concerns\HasUuids;
|
||||
use Staudenmeir\EloquentHasManyDeep\HasRelationships;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
|
||||
@@ -19,6 +22,7 @@ class User extends Authenticatable
|
||||
use Notifiable;
|
||||
use TwoFactorAuthenticatable;
|
||||
use HasUuids;
|
||||
use HasRelationships;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
@@ -70,8 +74,18 @@ class User extends Authenticatable
|
||||
return $this->belongsToMany(Portfolio::class)->withPivot('owner');
|
||||
}
|
||||
|
||||
// public function daily_changes()
|
||||
// {
|
||||
// return $this->hasMany(DailyChange::class);
|
||||
// }
|
||||
public function holdings(): HasManyDeep
|
||||
{
|
||||
return $this->hasManyDeep(Holding::class, ['portfolio_user', Portfolio::class]);
|
||||
}
|
||||
|
||||
public function transactions(): HasManyDeep
|
||||
{
|
||||
return $this->hasManyDeep(Transaction::class, ['portfolio_user', Portfolio::class]);
|
||||
}
|
||||
|
||||
public function daily_change(): HasManyDeep
|
||||
{
|
||||
return $this->hasManyDeep(DailyChange::class, ['portfolio_user', Portfolio::class]);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -14,7 +14,8 @@
|
||||
"livewire/volt": "^1.6",
|
||||
"maatwebsite/excel": "^3.1",
|
||||
"predis/predis": "^2.2",
|
||||
"robsontenorio/mary": "^1.35"
|
||||
"robsontenorio/mary": "^1.35",
|
||||
"staudenmeir/eloquent-has-many-deep": "^1.20"
|
||||
},
|
||||
"require-dev": {
|
||||
"fakerphp/faker": "^1.23",
|
||||
|
||||
Generated
+109
-1
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "4dcc44afdbd155cb01d0e00deb3bb4fe",
|
||||
"content-hash": "87ff0113a9121d64fb4518b040767e96",
|
||||
"packages": [
|
||||
{
|
||||
"name": "bacon/bacon-qr-code",
|
||||
@@ -4790,6 +4790,114 @@
|
||||
],
|
||||
"time": "2024-07-26T15:41:31+00:00"
|
||||
},
|
||||
{
|
||||
"name": "staudenmeir/eloquent-has-many-deep",
|
||||
"version": "v1.20.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/staudenmeir/eloquent-has-many-deep.git",
|
||||
"reference": "edcce8d8559d9559c997c177de06eb4bc173956d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/staudenmeir/eloquent-has-many-deep/zipball/edcce8d8559d9559c997c177de06eb4bc173956d",
|
||||
"reference": "edcce8d8559d9559c997c177de06eb4bc173956d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"illuminate/database": "^11.0",
|
||||
"php": "^8.2",
|
||||
"staudenmeir/eloquent-has-many-deep-contracts": "^1.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"awobaz/compoships": "^2.3",
|
||||
"barryvdh/laravel-ide-helper": "^3.0",
|
||||
"illuminate/pagination": "^11.0",
|
||||
"korridor/laravel-has-many-merged": "^1.1",
|
||||
"mockery/mockery": "^1.6",
|
||||
"orchestra/testbench": "^9.0",
|
||||
"phpstan/phpstan": "^1.10",
|
||||
"phpunit/phpunit": "^11.0",
|
||||
"staudenmeir/eloquent-json-relations": "^1.11",
|
||||
"staudenmeir/laravel-adjacency-list": "^1.21"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Staudenmeir\\EloquentHasManyDeep\\IdeHelperServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Staudenmeir\\EloquentHasManyDeep\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jonas Staudenmeir",
|
||||
"email": "mail@jonas-staudenmeir.de"
|
||||
}
|
||||
],
|
||||
"description": "Laravel Eloquent HasManyThrough relationships with unlimited levels",
|
||||
"support": {
|
||||
"issues": "https://github.com/staudenmeir/eloquent-has-many-deep/issues",
|
||||
"source": "https://github.com/staudenmeir/eloquent-has-many-deep/tree/v1.20.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://paypal.me/JonasStaudenmeir",
|
||||
"type": "custom"
|
||||
}
|
||||
],
|
||||
"time": "2024-07-12T22:08:11+00:00"
|
||||
},
|
||||
{
|
||||
"name": "staudenmeir/eloquent-has-many-deep-contracts",
|
||||
"version": "v1.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/staudenmeir/eloquent-has-many-deep-contracts.git",
|
||||
"reference": "bcbe1a921caad7201b324e297eddb696d4bd8647"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/staudenmeir/eloquent-has-many-deep-contracts/zipball/bcbe1a921caad7201b324e297eddb696d4bd8647",
|
||||
"reference": "bcbe1a921caad7201b324e297eddb696d4bd8647",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"illuminate/database": "^11.0",
|
||||
"php": "^8.2"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Staudenmeir\\EloquentHasManyDeepContracts\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jonas Staudenmeir",
|
||||
"email": "mail@jonas-staudenmeir.de"
|
||||
}
|
||||
],
|
||||
"description": "Contracts for staudenmeir/eloquent-has-many-deep",
|
||||
"support": {
|
||||
"issues": "https://github.com/staudenmeir/eloquent-has-many-deep-contracts/issues",
|
||||
"source": "https://github.com/staudenmeir/eloquent-has-many-deep-contracts/tree/v1.2"
|
||||
},
|
||||
"time": "2024-01-18T01:20:44+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/clock",
|
||||
"version": "v7.1.1",
|
||||
|
||||
@@ -58,17 +58,33 @@
|
||||
@if (!$user->portfolios->isEmpty())
|
||||
<x-ib-card title="{{ __('Top performers') }}" class="md:col-span-3">
|
||||
|
||||
@php
|
||||
$users = App\Models\User::take(3)->get();
|
||||
@endphp
|
||||
|
||||
@foreach($users as $user)
|
||||
<x-list-item no-separator :item="$user" avatar="profile_photo_url" link="/docs/installation" />
|
||||
@foreach($user->holdings->sortBy('market_gain_percent')->where('quantity', '>', 0)->take(5) as $holding)
|
||||
<x-list-item no-separator :item="$holding" link="{{ route('portfolio.show', ['portfolio' => $holding->portfolio_id]) }}">
|
||||
|
||||
@php
|
||||
$gainPercent = (($holding->market_data?->market_value - $holding->average_cost_basis) / $holding->average_cost_basis) * 100;
|
||||
@endphp
|
||||
|
||||
<x-slot:value class="flex items-center">
|
||||
|
||||
{{ $holding->symbol }}
|
||||
|
||||
<x-badge class="{{ $gainPercent > 0 ? 'badge-success' : 'badge-error' }} ml-2 badge-sm" >
|
||||
<x-slot:value>
|
||||
{{ Number::percentage($gainPercent) }}
|
||||
</x-slot:value>
|
||||
</x-badge>
|
||||
|
||||
</x-slot:value>
|
||||
<x-slot:sub-value>
|
||||
{{ $holding->market_data?->name }}
|
||||
</x-slot:sub-value>
|
||||
</x-list-item>
|
||||
@endforeach
|
||||
|
||||
</x-ib-card>
|
||||
@endif
|
||||
|
||||
|
||||
{{-- @if (!$user->portfolios->isEmpty())
|
||||
<x-ib-card title="{{ __('Top headlines') }}" class="md:col-span-3">
|
||||
|
||||
@@ -83,20 +99,6 @@
|
||||
</x-ib-card>
|
||||
@endif --}}
|
||||
|
||||
@if (!$user->portfolios->isEmpty())
|
||||
<x-ib-card title="{{ __('Recent activity') }}" class="md:col-span-4">
|
||||
|
||||
@php
|
||||
$users = App\Models\User::take(3)->get();
|
||||
@endphp
|
||||
|
||||
@foreach($users as $user)
|
||||
<x-list-item no-separator :item="$user" avatar="profile_photo_url" link="/docs/installation" />
|
||||
@endforeach
|
||||
|
||||
</x-ib-card>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
|
||||
</x-app-layout>
|
||||
@@ -61,5 +61,42 @@ new class extends Component {
|
||||
|
||||
<div class="">
|
||||
|
||||
<x-table wire:loading.remove :headers="$headers" :rows="$this->holdings()" :sort-by="$sortBy" />
|
||||
<x-table wire:loading.remove :headers="$headers" :rows="$this->holdings()" :sort-by="$sortBy">
|
||||
@scope('cell_average_cost_basis', $row)
|
||||
{{ Number::currency($row->average_cost_basis ?? 0) }}
|
||||
@endscope
|
||||
@scope('cell_total_cost_basis', $row)
|
||||
{{ Number::currency($row->total_cost_basis ?? 0) }}
|
||||
@endscope
|
||||
@scope('cell_realized_gain_dollars', $row)
|
||||
{{ Number::currency($row->realized_gain_dollars ?? 0) }}
|
||||
@endscope
|
||||
@scope('cell_market_gain_dollars', $row)
|
||||
{{ Number::currency($row->market_gain_dollars ?? 0) }}
|
||||
@endscope
|
||||
@scope('cell_market_gain_percent', $row)
|
||||
{{ Number::percentage($row->market_gain_percent ?? 0) }}
|
||||
@endscope
|
||||
@scope('cell_market_data_market_value', $row)
|
||||
{{ Number::currency($row->market_data_market_value ?? 0) }}
|
||||
@endscope
|
||||
@scope('cell_market_data_fifty_two_week_low', $row)
|
||||
{{ Number::currency($row->market_data_fifty_two_week_low ?? 0) }}
|
||||
@endscope
|
||||
@scope('cell_market_data_fifty_two_week_high', $row)
|
||||
{{ Number::currency($row->market_data_fifty_two_week_high ?? 0) }}
|
||||
@endscope
|
||||
@scope('cell_total_market_value', $row)
|
||||
{{ Number::currency($row->total_market_value ?? 0) }}
|
||||
@endscope
|
||||
@scope('cell_dividends_earned', $row)
|
||||
{{ Number::currency($row->dividends_earned ?? 0) }}
|
||||
@endscope
|
||||
@scope('cell_dividends_earned', $row)
|
||||
{{ Number::currency($row->dividends_earned ?? 0) }}
|
||||
@endscope
|
||||
@scope('cell_market_data_updated_at', $row)
|
||||
{{ \Carbon\Carbon::parse($row->market_data_updated_at)->diffForHumans() }}
|
||||
@endscope
|
||||
</x-table>
|
||||
</div>
|
||||
@@ -43,10 +43,11 @@ new class extends Component {
|
||||
$dailyChangeQuery->selectRaw('date,
|
||||
SUM(total_market_value) as total_market_value,
|
||||
SUM(total_cost_basis) as total_cost_basis,
|
||||
SUM(total_gain) as total_gain,
|
||||
SUM(total_dividends_earned) as total_dividends_earned,
|
||||
SUM(realized_gains) as realized_gains'
|
||||
SUM(total_gain) as total_gain'
|
||||
)->groupBy('date');
|
||||
|
||||
// SUM(total_dividends_earned) as total_dividends_earned,
|
||||
// SUM(realized_gains) as realized_gains
|
||||
}
|
||||
|
||||
if ($filterMethod['method']) {
|
||||
@@ -70,14 +71,14 @@ new class extends Component {
|
||||
'name' => __('Market Gain'),
|
||||
'data' => $dailyChange->map(fn($data) => [$data->date, $data->total_gain])->toArray()
|
||||
],
|
||||
[
|
||||
'name' => __('Dividends Earned'),
|
||||
'data' => $dailyChange->map(fn($data) => [$data->date, $data->total_dividends_earned])->toArray()
|
||||
],
|
||||
[
|
||||
'name' => __('Realized Gains'),
|
||||
'data' => $dailyChange->map(fn($data) => [$data->date, $data->realized_gains])->toArray()
|
||||
],
|
||||
// [
|
||||
// 'name' => __('Dividends Earned'),
|
||||
// 'data' => $dailyChange->map(fn($data) => [$data->date, $data->total_dividends_earned])->toArray()
|
||||
// ],
|
||||
// [
|
||||
// 'name' => __('Realized Gains'),
|
||||
// 'data' => $dailyChange->map(fn($data) => [$data->date, $data->realized_gains])->toArray()
|
||||
// ],
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
@@ -81,29 +81,15 @@
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="mt-6 grid md:grid-cols-7 gap-5">
|
||||
|
||||
<x-ib-card title="{{ __('Holdings') }}" class="md:col-span-4">
|
||||
|
||||
|
||||
@livewire('holdings-table', [
|
||||
'portfolio' => $portfolio
|
||||
])
|
||||
|
||||
</x-ib-card>
|
||||
|
||||
{{-- <x-ib-card title="{{ __('Top performers') }}" class="md:col-span-3">
|
||||
|
||||
@php
|
||||
$users = App\Models\User::take(3)->get();
|
||||
@endphp
|
||||
|
||||
@foreach($users as $user)
|
||||
<x-list-item no-separator :item="$user" avatar="profile_photo_url" link="/docs/installation" />
|
||||
@endforeach
|
||||
|
||||
</x-ib-card> --}}
|
||||
|
||||
{{-- <x-ib-card title="{{ __('Top headlines') }}" class="md:col-span-3">
|
||||
|
||||
@@ -125,6 +111,34 @@
|
||||
|
||||
</x-ib-card>
|
||||
|
||||
<x-ib-card title="{{ __('Top performers') }}" class="md:col-span-3">
|
||||
|
||||
@foreach($portfolio->holdings->sortBy('market_gain_percent')->where('quantity', '>', 0)->take(5) as $holding)
|
||||
<x-list-item no-separator :item="$holding">
|
||||
|
||||
@php
|
||||
$gainPercent = (($holding->market_data?->market_value - $holding->average_cost_basis) / $holding->average_cost_basis) * 100;
|
||||
@endphp
|
||||
|
||||
<x-slot:value class="flex items-center">
|
||||
|
||||
{{ $holding->symbol }}
|
||||
|
||||
<x-badge class="{{ $gainPercent > 0 ? 'badge-success' : 'badge-error' }} ml-2 badge-sm" >
|
||||
<x-slot:value>
|
||||
{{ Number::percentage($gainPercent) }}
|
||||
</x-slot:value>
|
||||
</x-badge>
|
||||
|
||||
</x-slot:value>
|
||||
<x-slot:sub-value>
|
||||
{{ $holding->market_data?->name }}
|
||||
</x-slot:sub-value>
|
||||
</x-list-item>
|
||||
@endforeach
|
||||
|
||||
</x-ib-card>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -9,6 +9,10 @@ Route::get('/', function () {
|
||||
return view('welcome');
|
||||
});
|
||||
|
||||
Route::get('/test', function () {
|
||||
dd(auth()->user()->holdings()->toSql());
|
||||
});
|
||||
|
||||
Route::middleware(['auth:sanctum', config('jetstream.auth_session'), 'verified'])->group(function () {
|
||||
|
||||
Route::get('/dashboard', [DashboardController::class, 'show'])->name('dashboard');
|
||||
|
||||
Reference in New Issue
Block a user