Commit the Projects/Tasks/Ideas/TaskComments feature
This was already implemented, migrated, and running live, but had never been committed. Bringing it into git: Project/Task/Idea/ TaskComment models, their JSON API (CRUD + status transitions, task comments, idea<->project linking), Livewire UI pages, and the nav/route wiring for them. All 42 of its own tests pass. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,209 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Ideas;
|
||||
|
||||
use App\Models\Idea;
|
||||
use App\Models\Project;
|
||||
use Livewire\Attributes\Layout;
|
||||
use Livewire\Component;
|
||||
|
||||
#[Layout('components.layouts.app')]
|
||||
class Index extends Component
|
||||
{
|
||||
public bool $showForm = false;
|
||||
|
||||
public ?int $editingId = null;
|
||||
|
||||
public string $title = '';
|
||||
|
||||
public ?string $description = null;
|
||||
|
||||
public string $status = 'new';
|
||||
|
||||
public ?int $projectId = null;
|
||||
|
||||
public int $priority = 0;
|
||||
|
||||
public ?string $category = null;
|
||||
|
||||
public ?string $tags = null;
|
||||
|
||||
public string $search = '';
|
||||
|
||||
public string $statusFilter = '';
|
||||
|
||||
public string $categoryFilter = '';
|
||||
|
||||
public ?int $projectFilter = null;
|
||||
|
||||
public bool $unlinkedFilter = false;
|
||||
|
||||
public function render()
|
||||
{
|
||||
$query = Idea::with(['project', 'user']);
|
||||
|
||||
if ($this->search) {
|
||||
$query->search($this->search);
|
||||
}
|
||||
|
||||
if ($this->statusFilter) {
|
||||
$query->where('status', $this->statusFilter);
|
||||
}
|
||||
|
||||
if ($this->categoryFilter) {
|
||||
$query->where('category', $this->categoryFilter);
|
||||
}
|
||||
|
||||
if ($this->projectFilter) {
|
||||
$query->where('project_id', $this->projectFilter);
|
||||
}
|
||||
|
||||
if ($this->unlinkedFilter) {
|
||||
$query->unlinked();
|
||||
}
|
||||
|
||||
$ideas = $query->orderBy('priority', 'desc')->orderBy('created_at', 'desc')->get();
|
||||
|
||||
return view('livewire.ideas.index', [
|
||||
'ideas' => $ideas,
|
||||
'projects' => Project::orderBy('name')->get(),
|
||||
'statuses' => Idea::STATUSES,
|
||||
'categories' => Idea::CATEGORIES,
|
||||
'totalIdeas' => Idea::count(),
|
||||
'newIdeas' => Idea::new()->count(),
|
||||
'consideredIdeas' => Idea::considered()->count(),
|
||||
'plannedIdeas' => Idea::planned()->count(),
|
||||
'implementedIdeas' => Idea::implemented()->count(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function create(): void
|
||||
{
|
||||
$this->resetForm();
|
||||
// Pre-select the filtered project if one is selected
|
||||
if ($this->projectFilter) {
|
||||
$this->projectId = $this->projectFilter;
|
||||
}
|
||||
$this->showForm = true;
|
||||
}
|
||||
|
||||
public function edit(int $id): void
|
||||
{
|
||||
$idea = Idea::findOrFail($id);
|
||||
$this->editingId = $idea->id;
|
||||
$this->title = $idea->title;
|
||||
$this->description = $idea->description;
|
||||
$this->status = $idea->status;
|
||||
$this->projectId = $idea->project_id;
|
||||
$this->priority = $idea->priority;
|
||||
$this->category = $idea->category;
|
||||
$this->tags = $idea->tags;
|
||||
$this->showForm = true;
|
||||
}
|
||||
|
||||
public function save(): void
|
||||
{
|
||||
$validated = $this->validate([
|
||||
'title' => ['required', 'string', 'max:255'],
|
||||
'description' => ['nullable', 'string'],
|
||||
'status' => ['required', 'string', 'in:' . implode(',', Idea::STATUSES)],
|
||||
'projectId' => ['nullable', 'integer', 'exists:projects,id'],
|
||||
'priority' => ['integer', 'min:0', 'max:10'],
|
||||
'category' => ['nullable', 'string', 'in:' . implode(',', Idea::CATEGORIES)],
|
||||
'tags' => ['nullable', 'string', 'max:255'],
|
||||
]);
|
||||
|
||||
Idea::updateOrCreate(['id' => $this->editingId], [
|
||||
'title' => $validated['title'],
|
||||
'description' => $validated['description'],
|
||||
'status' => $validated['status'],
|
||||
'project_id' => $validated['projectId'],
|
||||
'priority' => $validated['priority'],
|
||||
'category' => $validated['category'],
|
||||
'tags' => $validated['tags'],
|
||||
'user_id' => auth()->id(),
|
||||
]);
|
||||
|
||||
$this->resetForm();
|
||||
$this->showForm = false;
|
||||
}
|
||||
|
||||
public function delete(int $id): void
|
||||
{
|
||||
Idea::findOrFail($id)->delete();
|
||||
}
|
||||
|
||||
public function changeStatus(int $id, string $newStatus): void
|
||||
{
|
||||
if (!in_array($newStatus, Idea::STATUSES)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Idea::findOrFail($id)->update(['status' => $newStatus]);
|
||||
}
|
||||
|
||||
public function linkToProject(int $id, int $projectId): void
|
||||
{
|
||||
Idea::findOrFail($id)->update(['project_id' => $projectId]);
|
||||
}
|
||||
|
||||
public function unlinkFromProject(int $id): void
|
||||
{
|
||||
Idea::findOrFail($id)->update(['project_id' => null]);
|
||||
}
|
||||
|
||||
public function clearFilters(): void
|
||||
{
|
||||
$this->search = '';
|
||||
$this->statusFilter = '';
|
||||
$this->categoryFilter = '';
|
||||
$this->projectFilter = null;
|
||||
$this->unlinkedFilter = false;
|
||||
}
|
||||
|
||||
public function resetForm(): void
|
||||
{
|
||||
$this->editingId = null;
|
||||
$this->title = '';
|
||||
$this->description = null;
|
||||
$this->status = 'new';
|
||||
$this->projectId = null;
|
||||
$this->priority = 0;
|
||||
$this->category = null;
|
||||
$this->tags = null;
|
||||
$this->resetValidation();
|
||||
}
|
||||
|
||||
public function getStatusColor(string $status): string
|
||||
{
|
||||
return match ($status) {
|
||||
'new' => 'bg-blue-100 text-blue-800',
|
||||
'considered' => 'bg-yellow-100 text-yellow-800',
|
||||
'planned' => 'bg-purple-100 text-purple-800',
|
||||
'rejected' => 'bg-red-100 text-red-800',
|
||||
'implemented' => 'bg-green-100 text-green-800',
|
||||
default => 'bg-gray-100 text-gray-800',
|
||||
};
|
||||
}
|
||||
|
||||
public function getCategoryColor(string $category): string
|
||||
{
|
||||
return match ($category) {
|
||||
'feature' => 'bg-blue-100 text-blue-800',
|
||||
'improvement' => 'bg-purple-100 text-purple-800',
|
||||
'bugfix' => 'bg-red-100 text-red-800',
|
||||
'research' => 'bg-yellow-100 text-yellow-800',
|
||||
'other' => 'bg-gray-100 text-gray-800',
|
||||
default => 'bg-gray-100 text-gray-800',
|
||||
};
|
||||
}
|
||||
|
||||
public function getPriorityLabel(int $priority): string
|
||||
{
|
||||
if ($priority === 0) return 'None';
|
||||
if ($priority <= 3) return 'Low';
|
||||
if ($priority <= 6) return 'Medium';
|
||||
if ($priority <= 8) return 'High';
|
||||
return 'Critical';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user