Skip to content

Commit aeb380a

Browse files
Refactor profile form to use InteractsWithForms
1 parent 92ecd02 commit aeb380a

File tree

2 files changed

+217
-18
lines changed

2 files changed

+217
-18
lines changed

app/Filament/App/Pages/UpdateProfileInformationPage.php

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,15 @@
66
use UnitEnum;
77
use BackedEnum;
88
use Filament\Forms\Components\TextInput;
9+
use Filament\Forms\Concerns\InteractsWithForms;
10+
use Filament\Forms\Form;
911
use Filament\Pages\Page;
1012
use Illuminate\Support\Facades\Auth;
1113

1214
class UpdateProfileInformationPage extends Page
1315
{
16+
use InteractsWithForms;
17+
1418
protected string $view = 'filament.pages.profile.update-profile-information';
1519

1620
protected static string | \BackedEnum | null $navigationIcon = 'heroicon-o-user';
@@ -21,9 +25,7 @@ class UpdateProfileInformationPage extends Page
2125

2226
protected static ?string $title = 'Profile';
2327

24-
public $name;
25-
26-
public $email;
28+
public ?array $data = [];
2729

2830
public function mount(): void
2931
{
@@ -33,26 +35,23 @@ public function mount(): void
3335
]);
3436
}
3537

36-
protected function getFormSchema(): array
38+
public function form(Form $form): Form
3739
{
38-
return [
39-
TextInput::make('name')
40-
->label('Name')
41-
->required(),
42-
TextInput::make('email')
43-
->label('Email Address')
44-
->required(),
45-
];
40+
return $form
41+
->schema([
42+
TextInput::make('name')
43+
->label('Name')
44+
->required(),
45+
TextInput::make('email')
46+
->label('Email Address')
47+
->required(),
48+
])
49+
->statePath('data');
4650
}
4751

4852
public function submit(): void
4953
{
50-
$this->form->getState();
51-
52-
$state = array_filter([
53-
'name' => $this->name,
54-
'email' => $this->email,
55-
]);
54+
$state = array_filter($this->form->getState());
5655

5756
$user = Auth::user();
5857

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
<?php
2+
3+
namespace App\Livewire;
4+
5+
use App\Models\UserChecklist;
6+
use App\Models\Person;
7+
use App\Models\Family;
8+
use Illuminate\Contracts\View\View;
9+
use Livewire\Component;
10+
use Illuminate\Support\Collection;
11+
use Illuminate\Support\Carbon;
12+
13+
class ResearchProgressWidget extends Component
14+
{
15+
public int $selectedPeriod = 30; // days
16+
public string $selectedSubjectType = 'all';
17+
public bool $showDetails = false;
18+
19+
/** @var array<string, mixed> */
20+
public array $stats = [
21+
'total_checklists' => 0,
22+
'completed_checklists' => 0,
23+
'in_progress_checklists' => 0,
24+
'completion_rate' => 0,
25+
'overall_progress' => 0,
26+
];
27+
28+
/** @var array<string, mixed> */
29+
public array $recentActivity = [
30+
'items' => [],
31+
];
32+
33+
/** @var array<string, Collection> */
34+
public array $upcomingDeadlines = [
35+
'overdue' => null,
36+
'upcoming' => null,
37+
];
38+
39+
/** @var array<string, mixed> */
40+
public array $subjectProgress = [
41+
'person_progress' => [
42+
'completed' => 0,
43+
'total' => 0,
44+
'progress_percentage' => 0,
45+
],
46+
'family_progress' => [
47+
'completed' => 0,
48+
'total' => 0,
49+
'progress_percentage' => 0,
50+
],
51+
'top_persons' => null,
52+
'top_families' => null,
53+
];
54+
55+
public function mount(): void
56+
{
57+
// Initialize collections to avoid null method calls in the Blade
58+
$this->upcomingDeadlines['overdue'] = collect();
59+
$this->upcomingDeadlines['upcoming'] = collect();
60+
$this->subjectProgress['top_persons'] = collect();
61+
$this->subjectProgress['top_families'] = collect();
62+
63+
$this->refreshData();
64+
}
65+
66+
public function updatedSelectedPeriod(): void
67+
{
68+
$this->refreshData();
69+
}
70+
71+
public function updatedSelectedSubjectType(): void
72+
{
73+
$this->refreshData();
74+
}
75+
76+
public function toggleDetails(): void
77+
{
78+
$this->showDetails = ! $this->showDetails;
79+
}
80+
81+
public function render(): View
82+
{
83+
return view('livewire.research-progress-widget');
84+
}
85+
86+
protected function refreshData(): void
87+
{
88+
$userId = auth()->id();
89+
if (! $userId) {
90+
return;
91+
}
92+
93+
$periodStart = Carbon::now()->subDays($this->selectedPeriod);
94+
95+
// Base query for user's checklists
96+
$base = UserChecklist::query()->where('user_id', $userId);
97+
if ($this->selectedSubjectType !== 'all') {
98+
$base->where('subject_type', $this->selectedSubjectType);
99+
}
100+
101+
$total = (clone $base)->count();
102+
$completed = (clone $base)->completed()->count();
103+
$inProgress = (clone $base)->active()->count();
104+
105+
$completionRate = $total > 0 ? round(($completed / $total) * 100, 2) : 0.0;
106+
// Overall progress approximated by completion rate when item-level data not available
107+
$overallProgress = $completionRate;
108+
109+
$this->stats = [
110+
'total_checklists' => $total,
111+
'completed_checklists' => $completed,
112+
'in_progress_checklists' => $inProgress,
113+
'completion_rate' => $completionRate,
114+
'overall_progress' => $overallProgress,
115+
];
116+
117+
// Recent activity: latest completed checklists in period, adapted to Blade structure
118+
$recentCompleted = (clone $base)
119+
->whereNotNull('completed_at')
120+
->where('completed_at', '>=', $periodStart)
121+
->orderByDesc('completed_at')
122+
->limit(20)
123+
->get(['id','name','completed_at','subject_type','subject_id']);
124+
125+
// Map to objects with userChecklist relation-like structure expected by the Blade
126+
$this->recentActivity = [
127+
'items' => $recentCompleted->map(function (UserChecklist $c) {
128+
return (object) [
129+
'userChecklist' => $c,
130+
'completed_at' => $c->completed_at,
131+
];
132+
}),
133+
];
134+
135+
// Upcoming deadlines
136+
$this->upcomingDeadlines['overdue'] = (clone $base)->overdue()->orderBy('due_date')->limit(20)->get();
137+
$this->upcomingDeadlines['upcoming'] = (clone $base)
138+
->whereNotNull('due_date')
139+
->where('due_date', '>=', Carbon::today())
140+
->where('status', '!=', UserChecklist::STATUS_COMPLETED)
141+
->orderBy('due_date')
142+
->limit(20)
143+
->get();
144+
145+
// Subject progress
146+
$personTotal = (clone $base)->where('subject_type', Person::class)->count();
147+
$personCompleted = (clone $base)->where('subject_type', Person::class)->completed()->count();
148+
$familyTotal = (clone $base)->where('subject_type', Family::class)->count();
149+
$familyCompleted = (clone $base)->where('subject_type', Family::class)->completed()->count();
150+
151+
$this->subjectProgress['person_progress'] = [
152+
'completed' => $personCompleted,
153+
'total' => $personTotal,
154+
'progress_percentage' => $personTotal > 0 ? round(($personCompleted / $personTotal) * 100, 2) : 0,
155+
];
156+
$this->subjectProgress['family_progress'] = [
157+
'completed' => $familyCompleted,
158+
'total' => $familyTotal,
159+
'progress_percentage' => $familyTotal > 0 ? round(($familyCompleted / $familyTotal) * 100, 2) : 0,
160+
];
161+
162+
// Top subjects by checklist count (completed first, then total)
163+
$topPersons = (clone $base)
164+
->where('subject_type', Person::class)
165+
->selectRaw('subject_id, sum(case when status = ? then 1 else 0 end) as completed_count, count(*) as total_count', [UserChecklist::STATUS_COMPLETED])
166+
->groupBy('subject_id')
167+
->orderByDesc('completed_count')
168+
->orderByDesc('total_count')
169+
->limit(5)
170+
->get()
171+
->map(function ($row) {
172+
$person = Person::find($row->subject_id);
173+
if ($person) {
174+
$person->progress_percentage = $row->total_count > 0 ? round(($row->completed_count / $row->total_count) * 100, 0) : 0;
175+
}
176+
return $person;
177+
})
178+
->filter();
179+
180+
$topFamilies = (clone $base)
181+
->where('subject_type', Family::class)
182+
->selectRaw('subject_id, sum(case when status = ? then 1 else 0 end) as completed_count, count(*) as total_count', [UserChecklist::STATUS_COMPLETED])
183+
->groupBy('subject_id')
184+
->orderByDesc('completed_count')
185+
->orderByDesc('total_count')
186+
->limit(5)
187+
->get()
188+
->map(function ($row) {
189+
$family = Family::find($row->subject_id);
190+
if ($family) {
191+
$family->progress_percentage = $row->total_count > 0 ? round(($row->completed_count / $row->total_count) * 100, 0) : 0;
192+
}
193+
return $family;
194+
})
195+
->filter();
196+
197+
$this->subjectProgress['top_persons'] = $topPersons;
198+
$this->subjectProgress['top_families'] = $topFamilies;
199+
}
200+
}

0 commit comments

Comments
 (0)