Dec 6, 2023
0
Level 1
query regarding adding multiple sessions and show them on a single page with filament
<?php
namespace App\Filament\Pages;
use App\Services\GameTypeService;
use App\Services\SessionService;
use Filament\Actions\Action;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Pages\Page;
class Games extends Page
{
protected static ?string $navigationIcon = 'heroicon-o-document-text';
protected static string $view = 'filament.pages.games';
public $sessions = [];
public function mount(SessionService $sessionService): void
{
$this->sessions = $sessionService->getActiveSessions();
}
protected function getHeaderActions(): array
{
return [
Action::make('add_session')
->form([
TextInput::make('customer_name')
->required()
->maxLength(255),
])->action(function (array $data, SessionService $sessionService): void {
$sessionService->create(array_merge([
'start_time' => now()->toDateTimeString()
], $data));
$this->sessions = $sessionService->getActiveSessions();
}),
];
}
}
<?php
namespace App\Services;
use App\Enums\GeneralSettingEnum;
use App\Models\GameTable;
use App\Models\GameType;
use App\Models\Item;
use App\Models\ItemSession;
use App\Models\Session;
use App\Models\SessionGame;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Log;
class SessionService
{
public function __construct(
public ItemService $itemService,
public SettingService $settingService,
) {
}
public function getSessionGames(Model $session, array $data)
{
return SessionGame::query()
->where('session_id', $session->id)
->where('game_type_id', $data['game_type_id'])
->where('game_table_id', $data['game_table_id'])
->first();
}
public function getActiveSessions(): Collection|array
{
return $this->getActiveSessionsQuery()->get();
}
public function getActiveSession(int $sessionId, int $gameTableId): Model|Builder|null
{
return $this->getActiveSessionsQuery($gameTableId)->where('id', $sessionId)->first();
}
public function getActiveSessionsWithGamesGrouped(): Collection|array
{
$sessions = $this->getActiveSessions();
foreach ($sessions as &$session) {
$session->sessionGames = $session->sessionGames->groupBy('game_table_id');
}
return $sessions;
}
public function getActiveSessionsQuery(int $gameTableId = null): Builder
{
return Session::with([
'sessionGames' => function ($query) use ($gameTableId) {
if ($gameTableId) {
$query->where('game_table_id', $gameTableId);
}
},
'sessionGames.gameType',
'sessionGames.gameTable'
])->latest();
}
public function sessionGamesQuery($sessionId, $gameTableId): Builder
{
return SessionGame::query()->with('gameTable', 'gameType')
->where('session_id', $sessionId)
->where('game_table_id', $gameTableId);
}
public function create(array $sessionData)
{
return Session::create($sessionData);
}
public function addGame(Session $session, array $data): Model
{
$data['start_time'] = Carbon::now()->toDateTimeString();
$this->endOngoingGame($session, GameTable::find($data['game_table_id']));
return $session->sessionGames()->create($data);
}
public function endOngoingGame(Session $session, GameTable $gameTable): void
{
$ongoingGame = $session->activeSessionGames()->where('game_table_id', $gameTable->id)->first();
if ($ongoingGame) {
$this->endGame($ongoingGame);
}
}
public function endGame(SessionGame $sessionGame): void
{
$sessionGame->sub_total = $sessionGame->gameType->price;
$sessionGame->overtime_amount = $this->calculateOvertimeAmount($sessionGame);
$sessionGame->total = $sessionGame->sub_total + $sessionGame->overtime_amount;
$sessionGame->end_time = Carbon::now()->toDateTimeString();
$sessionGame->save();
}
public function calculateOvertimeAmount(SessionGame $sessionGame): float|int
{
$perMinutePrice = $this->settingService->getByKey(GeneralSettingEnum::PER_MINUTE_PRICE);
$duration = Carbon::make($sessionGame->start_time)->diffInMinutes($sessionGame->end_time);
$overtime = $duration - $sessionGame->gameType->time_limit;
return $overtime > 0 ? $overtime * $perMinutePrice : 0;
}
public function addOrUpdateGame(Session $session, array $data): Model
{
return $session->sessionGames()->updateOrCreate([
'session_id' => $session->id,
'game_type_id' => $data['game_type_id'],
'game_table_id' => $data['game_table_id'],
], $data);
}
public function addItems(Session $session, array $sessionItems): void
{
$itemsToAttach = [];
$items = $this->itemService->getItemByIds(array_column($sessionItems, 'item_id'))->pluck(
'price',
'id'
)->toArray();
$existingItems = $session->items;
foreach ($sessionItems as $item) {
$existingItem = $existingItems->firstWhere('id', $item['item_id']);
$existingQuantity = $existingItem ? $existingItem->pivot->quantity : 0;
$existingSubTotal = $existingItem ? $existingItem->pivot->sub_total : 0;
$existingTotal = $existingItem ? $existingItem->pivot->total : 0;
$itemsToAttach[$item['item_id']] = [
'sub_total' => $items[$item['item_id']],
'quantity' => $item['quantity'] + $existingQuantity,
'total' => ($items[$item['item_id']] * $item['quantity']) + $existingTotal,
];
}
$session->items()->syncWithoutDetaching($itemsToAttach);
}
public function updateSessionItemQuantity(Session $session, ItemSession $itemSession, string $item_quantity): void
{
$itemArray = ItemSession::query()->findOrFail($itemSession->id)->pluck(
'sub_total',
'id'
)->toArray();
$itemToAttach = [];
$itemToAttach[$itemSession->id] = [
'sub_total' => $itemArray[$itemSession->id],
'quantity' => $item_quantity,
'total' => $itemArray[$itemSession->id] * $item_quantity,
];
$session->items()->syncWithoutDetaching($itemToAttach);
}
public function sessionItemsQuery(Session $session)
{
return ItemSession::where('session_id', $session->id)->with('item');
}
}
<?php
namespace App\Models;
use App\Models\Scopes\OrderByDescScope;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
class Session extends Model
{
use HasFactory;
protected $fillable = [
'table_id',
'customer_name',
'start_time',
'end_time',
'total',
];
protected static function booted()
{
static::addGlobalScope(new OrderByDescScope());
}
public function items(): BelongsToMany
{
return $this->belongsToMany(Item::class)->withPivot('quantity', 'sub_total', 'total')->withTimestamps();
}
public function sessionGames(): HasMany
{
return $this->hasMany(SessionGame::class);
}
public function activeSessionGames(): HasMany
{
return $this->hasMany(SessionGame::class)->where('end_time', null);
}
public function sessionGamesGrouped()
{
return $this->sessionGames->groupBy('game_table_id');
}
}
<?php
namespace App\Models;
use App\Models\Scopes\OrderByDescScope;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class SessionGame extends Model
{
use HasFactory;
protected $fillable = [
'session_id',
'game_type_id',
'game_table_id',
'start_time',
'end_time',
'sub_total',
'overtime_amount',
'total',
];
protected $appends = [
'duration',
];
protected static function booted()
{
static::addGlobalScope(new OrderByDescScope());
}
public function duration(): Attribute
{
return new Attribute(
get: fn () => Carbon::make($this->start_time)->diffInMinutes($this->end_time),
);
}
public function session(): BelongsTo
{
return $this->belongsTo(Session::class);
}
public function gameType(): BelongsTo
{
return $this->belongsTo(GameType::class);
}
public function gameTable(): BelongsTo
{
return $this->belongsTo(GameTable::class);
}
}
<x-filament-panels::page>
<div class="grid">
@foreach($sessions as $session)
<div class="mt-6">
<livewire:session :session="$session" :id="'session'.$session->id" />
</div>
@endforeach
</div>
</x-filament-panels::page>
<x-filament::section>
<div class="grid gap-y-2 mt-6">
<div class="grid gap-y-2">
<div class="flex gap-x-2 items-center justify-end">
{{ $this->addGame }}
{{ $this->addItems }}
{{ $this->viewItems }}
</div>
</div>
<div class="flex items-center justify-between">
<span class="text-sm font-medium text-gray-500 dark:text-gray-400">
<strong>Customer Name:</strong> {{ $session->customer_name }}
</span>
<span class="flex gap-x-2 text-sm font-medium text-gray-500 dark:text-gray-400">
<strong>Total Time:</strong> <livewire:timer :id="'totalSessionTime'.$session->id" :startTime="$session->start_time" />
</span>
</div>
</div>
<hr>
<div class="grid gap-y-2 mt-6">
<div class="flex items-center gap-x-2 justify-center">
@foreach($session->sessionGamesGrouped() as $gameTableId => $sessionGames)
<livewire:session-games :id="'sessionGame'.$gameTableId" :sessionId="$session->id" :gameTableId="$gameTableId" />
@endforeach
</div>
</div>
<x-filament-actions::modals />
</x-filament::section>
<x-filament::section class="w-full">
<div class="grid gap-y-6">
<div class="grid gap-y-1">
<div class="flex items-center gap-x-2 justify-start">
<span class="text-sm font-medium text-gray-500 dark:text-gray-400">
<strong>Table Number:</strong> {{ $this->activeSessionGame->gameTable->name }}
</span>
</div>
<div class="flex items-center gap-x-2 justify-start">
<span class="text-sm font-medium text-gray-500 dark:text-gray-400">
<strong>Game Type:</strong> {{ $this->activeSessionGame->gameType->name }}
</span>
</div>
</div>
<div class="flex justify-center text-3xl font-semibold tracking-tight text-gray-950 dark:text-white">
<livewire:timer :id="'thisGameTime'.$activeSessionGame->id" :key="$activeSessionGame->id" :startTime="$activeSessionGame->start_time" />
</div>
<div class="flex items-center gap-x-2 justify-center">
{{ $this->addGame }}
</div>
<div class="flex items-center gap-x-2 justify-between">
<span class="flex gap-x-2 text-sm font-medium text-gray-500 dark:text-gray-400">
<strong>Table Games:</strong> {{ $this->sessionGames->count() }} {{ $this->viewGames }}
</span>
<span class="flex gap-x-2 text-sm font-medium text-gray-500 dark:text-gray-400">
<strong>Total Time:</strong> <livewire:timer :id="'sessionGameTotalTime'.$sessionGames->first()->id" :key="$sessionGames->first()->id" :startTime="$sessionGames->first()->start_time" />
</span>
</div>
</div>
<x-filament-actions::modals />
</x-filament::section>
<div id="{{ $id }}">
</div>
@push('scripts')
<script>
function updateLiveClock() {
// Get the current time and calculate the time difference
let now = new Date();
let startTime = new Date("{{ $startTime }}");
let timeDifference = now - startTime;
// Calculate hours, minutes, and seconds
let hours = Math.floor(timeDifference / (1000 * 60 * 60));
let minutes = Math.floor((timeDifference % (1000 * 60 * 60)) / (1000 * 60));
let seconds = Math.floor((timeDifference % (1000 * 60)) / 1000);
// Format the time
let formattedTime = `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
// Update the clock on the page
document.getElementById("{{ $id }}").innerHTML = formattedTime;
}
// Update clock every second
setInterval(updateLiveClock, 1000);
// Initial update
updateLiveClock();
</script>
@endpush
gives error after clicking on add_session link after first session is created
livewire.js?id=c4077c56:4193 Uncaught Snapshot missing on Livewire component with id: G2zQhlTmAXzr51sKc2eB
how to resolve it any suggestions
Please or to participate in this conversation.