refactor(livewire): organizar componentes y vistas por dominio en subnamespaces
- app/Livewire: 34 componentes agrupados en Issues/, Projects/, Phases/, Companies/, Users/, Admin/, Inspections/, Layers/, Media/, Common/ (Client/, Reports/, Forms/, Actions/ ya estaban). Namespaces actualizados. - resources/views/livewire: vistas sueltas movidas a subcarpetas espejo (companies/, users/, phases/, roles/, inspections/, media/, common/); render() actualizado. - Referencias actualizadas sin romper nada: rutas (FQN, nombres de ruta intactos), tags <livewire:...>/@livewire() a alias con punto, y use de los tests. - No tocado: Volt de Breeze (auth/profile/navigation), y el portal cliente (user-nav/client-projects) que ya tenía referencias inconsistentes. Verificado: 69 rutas OK, vistas compilan, suite 69 passing (solo 2 pre-existentes sqlite). autoload regenerado con --ignore-platform-reqs (PHP 8.2). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Issues;
|
||||
|
||||
use App\Models\Issue;
|
||||
use App\Models\Project;
|
||||
use App\Notifications\IssueAssignedNotification;
|
||||
use App\Notifications\IssueReportedNotification;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Livewire\Attributes\Layout;
|
||||
use Livewire\Component;
|
||||
|
||||
#[Layout('layouts.app')]
|
||||
class IssueForm extends Component
|
||||
{
|
||||
public Project $project;
|
||||
public ?Issue $issue = null; // null = create, set = edit
|
||||
|
||||
public $projectUsers = [];
|
||||
|
||||
// Form fields
|
||||
public $title = '';
|
||||
public $description = '';
|
||||
public $status = 'open';
|
||||
public $priority = 'medium';
|
||||
public $type = 'defect';
|
||||
public $assignedTo = '';
|
||||
public $resolutionNotes = '';
|
||||
|
||||
// Optional context (e.g. when reporting from a map feature)
|
||||
public $featureId = null;
|
||||
public $inspectionId = null;
|
||||
public $featureName = null; // shown when the issue is pre-linked to a map element
|
||||
|
||||
public function mount(Project $project, ?Issue $issue = null)
|
||||
{
|
||||
$this->project = $project;
|
||||
|
||||
if ($issue) {
|
||||
abort_unless($issue->project_id === $project->id, 404);
|
||||
}
|
||||
abort_unless($this->canAccessProject() && Auth::user()->can($issue ? 'edit issues' : 'create issues'), 403);
|
||||
|
||||
$this->projectUsers = $project->users()->orderBy('name')->get();
|
||||
|
||||
if ($issue) {
|
||||
$this->issue = $issue;
|
||||
$this->title = $issue->title;
|
||||
$this->description = $issue->description ?? '';
|
||||
$this->status = $issue->status;
|
||||
$this->priority = $issue->priority;
|
||||
$this->type = $issue->type ?? 'defect';
|
||||
$this->assignedTo = $issue->assigned_to ?? '';
|
||||
$this->resolutionNotes = $issue->resolution_notes ?? '';
|
||||
$this->featureId = $issue->feature_id;
|
||||
$this->inspectionId = $issue->inspection_id;
|
||||
$this->featureName = $issue->feature?->name;
|
||||
} elseif ($featureId = request()->integer('feature')) {
|
||||
// Pre-link to a map element when reporting from the project map.
|
||||
$feature = \App\Models\Feature::with('layer.phase')->find($featureId);
|
||||
if ($feature && $feature->layer?->phase?->project_id === $project->id) {
|
||||
$this->featureId = $feature->id;
|
||||
$this->featureName = $feature->name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function canAccessProject(): bool
|
||||
{
|
||||
$user = Auth::user();
|
||||
return $user->can('manage all')
|
||||
|| $this->project->users()->where('user_id', $user->id)->exists();
|
||||
}
|
||||
|
||||
protected function rules(): array
|
||||
{
|
||||
return [
|
||||
'title' => 'required|string|max:255',
|
||||
'description' => 'nullable|string',
|
||||
'status' => 'required|in:' . implode(',', Issue::STATUSES),
|
||||
'priority' => 'required|in:' . implode(',', Issue::PRIORITIES),
|
||||
'type' => 'required|in:' . implode(',', Issue::TYPES),
|
||||
'assignedTo' => 'nullable|exists:users,id',
|
||||
'resolutionNotes' => 'nullable|string',
|
||||
];
|
||||
}
|
||||
|
||||
public function save()
|
||||
{
|
||||
abort_unless(Auth::user()->can($this->issue ? 'edit issues' : 'create issues'), 403);
|
||||
$this->validate();
|
||||
|
||||
$payload = [
|
||||
'title' => $this->title,
|
||||
'description' => $this->description,
|
||||
'status' => $this->status,
|
||||
'priority' => $this->priority,
|
||||
'type' => $this->type,
|
||||
'feature_id' => $this->featureId,
|
||||
'inspection_id' => $this->inspectionId,
|
||||
'assigned_to' => $this->assignedTo ?: null,
|
||||
'resolution_notes' => $this->resolutionNotes ?: null,
|
||||
];
|
||||
|
||||
// Keep resolved_at in sync with the status
|
||||
$payload['resolved_at'] = in_array($this->status, ['resolved', 'closed']) ? now() : null;
|
||||
|
||||
if ($this->issue) {
|
||||
$previousAssignee = $this->issue->assigned_to;
|
||||
// Don't overwrite an existing resolved date
|
||||
if ($this->issue->resolved_at && in_array($this->status, ['resolved', 'closed'])) {
|
||||
unset($payload['resolved_at']);
|
||||
}
|
||||
$this->issue->update($payload);
|
||||
$issue = $this->issue;
|
||||
|
||||
// Notify a newly assigned user (when it changed and isn't the current actor).
|
||||
if ($issue->assigned_to && $issue->assigned_to !== $previousAssignee && $issue->assigned_to !== Auth::id()) {
|
||||
$issue->assignee?->notify(new IssueAssignedNotification($issue));
|
||||
}
|
||||
} else {
|
||||
$issue = Issue::create(array_merge($payload, [
|
||||
'project_id' => $this->project->id,
|
||||
'reported_by' => Auth::id(),
|
||||
]));
|
||||
|
||||
$issue->load(['feature', 'assignee']);
|
||||
$creator = $this->project->creator;
|
||||
if ($creator && $creator->id !== Auth::id()) {
|
||||
$creator->notify(new IssueReportedNotification($issue));
|
||||
}
|
||||
if ($issue->assignee && $issue->assignee->id !== Auth::id()) {
|
||||
$issue->assignee->notify(new IssueReportedNotification($issue));
|
||||
}
|
||||
}
|
||||
|
||||
session()->flash('message', $this->issue ? 'Incidencia actualizada' : 'Incidencia creada');
|
||||
|
||||
return $this->redirectRoute('projects.issues.show', ['project' => $this->project, 'issue' => $issue], navigate: true);
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.issues.issue-form');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user