Skip to content

Commit 415f31b

Browse files
authored
Merge pull request #8 from MujahidAbbas/feature/filament-v5-migration
Upgrade to Filament v5, Livewire v4, Flowforge v4
2 parents cdac342 + 5a43454 commit 415f31b

File tree

33 files changed

+1816
-691
lines changed

33 files changed

+1816
-691
lines changed

app/Jobs/GeneratePrdJob.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,10 @@ public function handle(): void
8484

8585
$response = Prism::text()
8686
->using($providerEnum, $run->model)
87-
->withMaxTokens(4000)
87+
->withMaxTokens(6000)
8888
->withSystemPrompt($system)
8989
->withPrompt($prompt)
90-
->withClientOptions(['timeout' => 120])
90+
->withClientOptions(['timeout' => 150])
9191
->asText();
9292

9393
// Store rate limit info from successful response

app/Jobs/GenerateTechSpecJob.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,10 @@ public function handle(): void
8989

9090
$response = Prism::text()
9191
->using($providerEnum, $run->model)
92-
->withMaxTokens(4000)
92+
->withMaxTokens(8000)
9393
->withSystemPrompt($system)
9494
->withPrompt($prompt)
95-
->withClientOptions(['timeout' => 120])
95+
->withClientOptions(['timeout' => 180])
9696
->asText();
9797

9898
// Store rate limit info from successful response

app/Livewire/Projects/Partials/TemplateSelector.php

Lines changed: 184 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace App\Livewire\Projects\Partials;
44

55
use App\Enums\DocumentType;
6+
use App\Enums\TemplateCategory;
67
use App\Models\Project;
78
use App\Models\Template;
89
use Illuminate\Database\Eloquent\Collection;
@@ -22,6 +23,14 @@ class TemplateSelector extends Component
2223

2324
public ?string $selectedTemplateId = null;
2425

26+
/** Search query for filtering templates */
27+
public string $search = '';
28+
29+
/** Preview slide-over state */
30+
public bool $showPreview = false;
31+
32+
public ?string $previewTemplateId = null;
33+
2534
public function mount(string $projectId, string $documentType): void
2635
{
2736
$this->projectId = $projectId;
@@ -47,6 +56,119 @@ public function groupedTemplates(): SupportCollection
4756
return Template::getGroupedByCategory($this->documentTypeEnum(), auth()->id());
4857
}
4958

59+
/** Get user's custom templates, filtered by search */
60+
#[Computed]
61+
public function myTemplates(): Collection
62+
{
63+
$query = Template::query()
64+
->where('user_id', auth()->id())
65+
->where('document_type', $this->documentTypeEnum());
66+
67+
if ($this->search) {
68+
$query->where(function ($q) {
69+
$q->where('name', 'like', "%{$this->search}%")
70+
->orWhere('description', 'like', "%{$this->search}%");
71+
});
72+
}
73+
74+
return $query->orderBy('name')->get();
75+
}
76+
77+
/** Get built-in templates grouped by category, filtered by search */
78+
#[Computed]
79+
public function builtInGroupedTemplates(): SupportCollection
80+
{
81+
$query = Template::query()
82+
->where('document_type', $this->documentTypeEnum())
83+
->where(function ($query) {
84+
$query->where('is_built_in', true)
85+
->orWhere('is_community', true)
86+
->orWhere('is_public', true);
87+
})
88+
->whereNull('user_id');
89+
90+
if ($this->search) {
91+
$query->where(function ($q) {
92+
$q->where('name', 'like', "%{$this->search}%")
93+
->orWhere('description', 'like', "%{$this->search}%");
94+
});
95+
}
96+
97+
return $query
98+
->orderBy('sort_order')
99+
->orderByDesc('is_built_in')
100+
->orderByDesc('usage_count')
101+
->get()
102+
->groupBy(fn (Template $template): string => $template->category?->value ?? 'other')
103+
->map(fn (Collection $templates, string $category): array => [
104+
'category' => TemplateCategory::tryFrom($category),
105+
'templates' => $templates,
106+
]);
107+
}
108+
109+
/** Get the recommended template (PlanForge PRD or Tech Spec) */
110+
#[Computed]
111+
public function recommendedTemplate(): ?Template
112+
{
113+
$recommendedName = $this->documentType === 'prd' ? 'PlanForge PRD' : 'PlanForge Tech Spec';
114+
115+
return Template::query()
116+
->where('name', $recommendedName)
117+
->where('is_built_in', true)
118+
->first();
119+
}
120+
121+
/** Get quick option templates (No Template placeholder + alternative) */
122+
#[Computed]
123+
public function quickOptions(): array
124+
{
125+
// Find "No Template" option from core category
126+
$noTemplate = Template::query()
127+
->where('document_type', $this->documentTypeEnum())
128+
->where('name', 'No Template')
129+
->where('is_built_in', true)
130+
->first();
131+
132+
// Find an alternative template (first non-recommended from core)
133+
$recommended = $this->recommendedTemplate;
134+
$alternative = Template::query()
135+
->where('document_type', $this->documentTypeEnum())
136+
->where('category', TemplateCategory::Core)
137+
->where('is_built_in', true)
138+
->when($recommended, fn ($q) => $q->where('id', '!=', $recommended->id))
139+
->when($noTemplate, fn ($q) => $q->where('id', '!=', $noTemplate->id))
140+
->orderBy('sort_order')
141+
->first();
142+
143+
return array_filter([$noTemplate, $alternative]);
144+
}
145+
146+
/** Check if there are any search results */
147+
#[Computed]
148+
public function hasSearchResults(): bool
149+
{
150+
if (! $this->search) {
151+
return true;
152+
}
153+
154+
return $this->myTemplates->isNotEmpty() || $this->builtInGroupedTemplates->isNotEmpty();
155+
}
156+
157+
/** Get total template count for display */
158+
#[Computed]
159+
public function totalTemplateCount(): int
160+
{
161+
return Template::query()
162+
->where('document_type', $this->documentTypeEnum())
163+
->where(function ($query) {
164+
$query->where('user_id', auth()->id())
165+
->orWhere('is_built_in', true)
166+
->orWhere('is_community', true)
167+
->orWhere('is_public', true);
168+
})
169+
->count();
170+
}
171+
50172
#[Computed]
51173
public function sectionCount(): int
52174
{
@@ -63,6 +185,17 @@ public function selectedTemplate(): ?Template
63185
return Template::find($this->selectedTemplateId);
64186
}
65187

188+
/** Get template being previewed */
189+
#[Computed]
190+
public function previewTemplate(): ?Template
191+
{
192+
if (! $this->previewTemplateId) {
193+
return null;
194+
}
195+
196+
return Template::find($this->previewTemplateId);
197+
}
198+
66199
public function selectTemplate(string $templateId): void
67200
{
68201
$project = Project::findOrFail($this->projectId);
@@ -71,7 +204,13 @@ public function selectTemplate(string $templateId): void
71204
$this->selectedTemplateId = $templateId;
72205
$project->update([$this->templateField() => $templateId]);
73206

74-
$this->dispatch('template-selected', templateId: $templateId);
207+
// Dispatch with template details for sticky bar
208+
$template = Template::find($templateId);
209+
$this->dispatch('template-selected',
210+
templateId: $templateId,
211+
templateName: $template?->name ?? 'No Template',
212+
sectionCount: count($template?->sections ?? [])
213+
);
75214
}
76215

77216
public function clearTemplate(): void
@@ -85,6 +224,35 @@ public function clearTemplate(): void
85224
$this->dispatch('template-cleared');
86225
}
87226

227+
/** Open preview slide-over for a template */
228+
public function preview(string $templateId): void
229+
{
230+
$this->previewTemplateId = $templateId;
231+
$this->showPreview = true;
232+
}
233+
234+
/** Close preview slide-over */
235+
public function closePreview(): void
236+
{
237+
$this->showPreview = false;
238+
$this->previewTemplateId = null;
239+
}
240+
241+
/** Select template from preview and close slide-over */
242+
public function selectFromPreview(): void
243+
{
244+
if ($this->previewTemplateId) {
245+
$this->selectTemplate($this->previewTemplateId);
246+
$this->closePreview();
247+
}
248+
}
249+
250+
/** Clear search query */
251+
public function clearSearch(): void
252+
{
253+
$this->search = '';
254+
}
255+
88256
public function render(): View
89257
{
90258
return view('livewire.projects.partials.template-selector');
@@ -112,6 +280,21 @@ private function applyUserDefaultTemplate(Project $project): void
112280
if ($defaultTemplateId) {
113281
$this->selectedTemplateId = $defaultTemplateId;
114282
$project->update([$this->templateField() => $defaultTemplateId]);
283+
284+
return;
285+
}
286+
287+
// Fall back to recommended template if no user default
288+
$this->applyRecommendedTemplate($project);
289+
}
290+
291+
private function applyRecommendedTemplate(Project $project): void
292+
{
293+
$recommended = $this->recommendedTemplate;
294+
295+
if ($recommended) {
296+
$this->selectedTemplateId = $recommended->id;
297+
$project->update([$this->templateField() => $recommended->id]);
115298
}
116299
}
117300
}

app/Livewire/Projects/Tabs/Prd.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ public function mount(string $projectId): void
3434
$this->loadContent();
3535
}
3636

37+
#[Computed]
38+
public function project(): Project
39+
{
40+
return Project::with('prdTemplate')->findOrFail($this->projectId);
41+
}
42+
3743
#[Computed]
3844
public function document(): ?Document
3945
{

app/Livewire/Projects/Tabs/Tech.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ public function mount(string $projectId): void
3737
$this->loadContent();
3838
}
3939

40+
#[Computed]
41+
public function project(): Project
42+
{
43+
return Project::with('techTemplate')->findOrFail($this->projectId);
44+
}
45+
4046
#[Computed]
4147
public function document(): ?Document
4248
{

app/Livewire/Projects/Workspace.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,16 @@ public function regenerateTech(): void
7979
$this->dispatch('planRunStarted', runId: $run->id);
8080
}
8181

82+
/**
83+
* Handle startPlanRun event from child components (PRD/Tech tabs)
84+
* to trigger generation when a template is selected
85+
*/
86+
#[On('startPlanRun')]
87+
public function handleStartPlanRun(): void
88+
{
89+
$this->generate();
90+
}
91+
8292
#[On('docUpdated')]
8393
#[On('planRunCompleted')]
8494
public function refreshProject(): void

composer.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,19 @@
77
"license": "MIT",
88
"require": {
99
"php": "^8.2",
10-
"filament/filament": "^4.0",
10+
"filament/filament": "^5.0",
1111
"firebase/php-jwt": "^7.0",
1212
"laravel/framework": "^12.0",
1313
"laravel/tinker": "^2.10.1",
1414
"league/commonmark": "^2.8",
15-
"livewire/livewire": "^3.6.4",
15+
"livewire/livewire": "^4.0",
1616
"livewire/volt": "^1.7.0",
1717
"prism-php/prism": "^0.99.7",
18-
"relaticle/flowforge": "^3.0"
18+
"relaticle/flowforge": "^4.0"
1919
},
2020
"require-dev": {
2121
"fakerphp/faker": "^1.23",
22-
"laravel/boost": "^1.8",
22+
"laravel/boost": "^2.0",
2323
"laravel/breeze": "^2.3",
2424
"laravel/pail": "^1.2.2",
2525
"laravel/pint": "^1.24",

0 commit comments

Comments
 (0)