feat(issues): build the rich Issues screen (recover yesterday's draft)

Rebuilt IssueManager to match the 403-line draft view that had no companion
component:
- Modal create/edit form (title, description, priority, status, assignee,
  resolution notes shown when resolved/closed)
- Stats bar (open/in_review/resolved/closed/total) and a styled table
- New methods: openForm/closeForm, resolve, close (+ existing save/delete),
  projectUsers for the assignee dropdown, resolved_at kept in sync with status
- render() now points to livewire.issues.issue-manager; deleted the old
  89-line stub livewire/issue-manager.blade.php
The Issue model already had everything (resolution_notes, resolved_at,
priority_color/status_color accessors, reporter/feature/assignee relations).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-17 14:16:14 +02:00
parent 860c502f32
commit 7df6d208d9
2 changed files with 114 additions and 147 deletions
@@ -1,89 +0,0 @@
<div>
{{-- Issue form --}}
@if($editing)
<div class="card bg-base-100 shadow mb-4">
<div class="card-body">
<h3 class="card-title text-base">
{{ $editingId ? 'Editar Issue' : 'Nuevo Issue' }}
</h3>
<div class="form-control mb-3">
<label class="label"><span class="label-text">Título *</span></label>
<input type="text" wire:model="title" class="input input-bordered" placeholder="Título del issue" />
@error('title') <span class="text-error text-xs mt-1">{{ $message }}</span> @enderror
</div>
<div class="form-control mb-3">
<label class="label"><span class="label-text">Descripción</span></label>
<textarea wire:model="description" class="textarea textarea-bordered" rows="3" placeholder="Descripción..."></textarea>
</div>
<div class="grid grid-cols-2 gap-3 mb-3">
<div class="form-control">
<label class="label"><span class="label-text">Estado</span></label>
<select wire:model="status" class="select select-bordered">
<option value="open">Abierto</option>
<option value="in_review">En revisión</option>
<option value="resolved">Resuelto</option>
<option value="closed">Cerrado</option>
</select>
</div>
<div class="form-control">
<label class="label"><span class="label-text">Prioridad</span></label>
<select wire:model="priority" class="select select-bordered">
<option value="low">Baja</option>
<option value="medium">Media</option>
<option value="high">Alta</option>
<option value="critical">Crítica</option>
</select>
</div>
</div>
<div class="flex gap-2 justify-end mt-2">
<button wire:click="cancel" class="btn btn-ghost btn-sm">Cancelar</button>
<button wire:click="save" class="btn btn-primary btn-sm">Guardar</button>
</div>
</div>
</div>
@else
<div class="flex justify-end mb-3">
<button wire:click="create" class="btn btn-primary btn-sm">
+ Nuevo Issue
</button>
</div>
@endif
{{-- Issue list --}}
<div class="space-y-2">
@forelse($issues as $issue)
<div class="card bg-base-100 shadow-sm border border-base-200">
<div class="card-body py-3 px-4">
<div class="flex items-start justify-between gap-2">
<div class="flex-1">
<p class="font-medium text-sm">{{ $issue->title }}</p>
@if($issue->description)
<p class="text-xs text-base-content/60 mt-0.5 line-clamp-2">{{ $issue->description }}</p>
@endif
<div class="flex gap-2 mt-1">
<span class="badge badge-xs" style="background-color: {{ $issue->priority_color }}; color: #fff;">
{{ ucfirst($issue->priority) }}
</span>
<span class="badge badge-xs badge-outline">
{{ ucfirst(str_replace('_', ' ', $issue->status)) }}
</span>
</div>
</div>
<div class="flex gap-1 shrink-0">
<button wire:click="edit({{ $issue->id }})" class="btn btn-ghost btn-xs">Editar</button>
<button wire:click="delete({{ $issue->id }})"
wire:confirm="¿Eliminar este issue?"
class="btn btn-ghost btn-xs text-error">Eliminar</button>
</div>
</div>
</div>
</div>
@empty
<p class="text-center text-sm text-base-content/50 py-6">No hay issues registrados</p>
@endforelse
</div>
</div>