diff --git a/app/Models/User.php b/app/Models/User.php index 09e2523..bc79650 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -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]); + } } diff --git a/composer.json b/composer.json index b853356..c490d9d 100644 --- a/composer.json +++ b/composer.json @@ -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", diff --git a/composer.lock b/composer.lock index 449cb46..6a2bb40 100644 --- a/composer.lock +++ b/composer.lock @@ -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", diff --git a/resources/views/dashboard.blade.php b/resources/views/dashboard.blade.php index 2887596..e8508b7 100644 --- a/resources/views/dashboard.blade.php +++ b/resources/views/dashboard.blade.php @@ -58,17 +58,33 @@ @if (!$user->portfolios->isEmpty()) - @php - $users = App\Models\User::take(3)->get(); - @endphp - - @foreach($users as $user) - + @foreach($user->holdings->sortBy('market_gain_percent')->where('quantity', '>', 0)->take(5) as $holding) + + + @php + $gainPercent = (($holding->market_data?->market_value - $holding->average_cost_basis) / $holding->average_cost_basis) * 100; + @endphp + + + + {{ $holding->symbol }} + + + + {{ Number::percentage($gainPercent) }} + + + + + + {{ $holding->market_data?->name }} + + @endforeach @endif - + {{-- @if (!$user->portfolios->isEmpty()) @@ -83,20 +99,6 @@ @endif --}} - @if (!$user->portfolios->isEmpty()) - - - @php - $users = App\Models\User::take(3)->get(); - @endphp - - @foreach($users as $user) - - @endforeach - - - @endif - \ No newline at end of file diff --git a/resources/views/livewire/holdings-table.blade.php b/resources/views/livewire/holdings-table.blade.php index 9b84e49..f775631 100644 --- a/resources/views/livewire/holdings-table.blade.php +++ b/resources/views/livewire/holdings-table.blade.php @@ -61,5 +61,42 @@ new class extends Component {
- + + @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 +
\ No newline at end of file diff --git a/resources/views/livewire/portfolio-performance-chart.blade.php b/resources/views/livewire/portfolio-performance-chart.blade.php index 72eab9d..92155d1 100644 --- a/resources/views/livewire/portfolio-performance-chart.blade.php +++ b/resources/views/livewire/portfolio-performance-chart.blade.php @@ -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() + // ], ] ]; } diff --git a/resources/views/portfolio/show.blade.php b/resources/views/portfolio/show.blade.php index b42a86f..8b80829 100644 --- a/resources/views/portfolio/show.blade.php +++ b/resources/views/portfolio/show.blade.php @@ -81,29 +81,15 @@ -
- @livewire('holdings-table', [ 'portfolio' => $portfolio ]) - - {{-- - - @php - $users = App\Models\User::take(3)->get(); - @endphp - - @foreach($users as $user) - - @endforeach - - --}} {{-- @@ -125,6 +111,34 @@ + + + @foreach($portfolio->holdings->sortBy('market_gain_percent')->where('quantity', '>', 0)->take(5) as $holding) + + + @php + $gainPercent = (($holding->market_data?->market_value - $holding->average_cost_basis) / $holding->average_cost_basis) * 100; + @endphp + + + + {{ $holding->symbol }} + + + + {{ Number::percentage($gainPercent) }} + + + + + + {{ $holding->market_data?->name }} + + + @endforeach + + +
\ No newline at end of file diff --git a/routes/web.php b/routes/web.php index 8373108..25761aa 100644 --- a/routes/web.php +++ b/routes/web.php @@ -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');