157 lines
6.3 KiB
PHP
157 lines
6.3 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\Task;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Validation\Rule;
|
|
|
|
class TaskController extends Controller {
|
|
public function index(Request $r) {
|
|
$tasks = $r->user()->tasks()
|
|
->with(['project:id,name,color', 'tags:id,name,color'])
|
|
->orderBy('done')
|
|
->orderByRaw("FIELD(priority,'high','med','low')")
|
|
->orderBy('created_at')
|
|
->get();
|
|
return response()->json($tasks);
|
|
}
|
|
|
|
public function pending(Request $r): JsonResponse {
|
|
$tasks = $r->user()->tasks()
|
|
->active()
|
|
->with('project:id,name,color')
|
|
->orderByRaw("FIELD(priority,'high','med','low')")
|
|
->orderBy('created_at')
|
|
->limit(20)
|
|
->get();
|
|
return response()->json($tasks->map(fn ($t) => [
|
|
'id' => $t->id,
|
|
'title' => $t->title,
|
|
'priority' => $t->priority,
|
|
'project' => $t->project?->name,
|
|
'next_due_in' => $t->next_due_in,
|
|
'estimate_min' => $t->estimate_min,
|
|
]));
|
|
}
|
|
|
|
public function store(Request $r): JsonResponse {
|
|
$data = $r->validate([
|
|
'title' => 'required|string|max:120',
|
|
'description' => 'nullable|string',
|
|
'priority' => 'nullable|in:low,med,high',
|
|
'estimate_min' => 'nullable|integer|min:1|max:9999',
|
|
'deadline' => 'nullable|date',
|
|
'project_id' => 'nullable|integer|exists:projects,id',
|
|
'recurrence' => 'nullable|in:daily,weekly,monthly',
|
|
'tags' => 'nullable|array',
|
|
'tags.*' => 'string|max:30',
|
|
]);
|
|
$task = $r->user()->tasks()->create([
|
|
'title' => $data['title'],
|
|
'description' => $data['description'] ?? null,
|
|
'priority' => $data['priority'] ?? 'med',
|
|
'estimate_min' => $data['estimate_min'] ?? null,
|
|
'deadline' => $data['deadline'] ?? null,
|
|
'project_id' => $data['project_id'] ?? null,
|
|
'recurrence' => $data['recurrence'] ?? null,
|
|
]);
|
|
if (! empty($data['tags'])) {
|
|
$this->syncTags($task, $data['tags']);
|
|
}
|
|
return response()->json($task->load('project', 'tags'), 201);
|
|
}
|
|
|
|
public function update(Request $r, Task $task): JsonResponse {
|
|
abort_unless($task->user_id === $r->user()->id, 403);
|
|
$data = $r->validate([
|
|
'title' => 'sometimes|required|string|max:120',
|
|
'description' => 'nullable|string',
|
|
'priority' => 'nullable|in:low,med,high',
|
|
'estimate_min' => 'nullable|integer|min:1|max:9999',
|
|
'deadline' => 'nullable|date',
|
|
'project_id' => 'nullable|integer|exists:projects,id',
|
|
'recurrence' => 'nullable|in:daily,weekly,monthly',
|
|
'tags' => 'nullable|array',
|
|
]);
|
|
$task->update(array_intersect_key($data, array_flip([
|
|
'title', 'description', 'priority', 'estimate_min',
|
|
'deadline', 'project_id', 'recurrence',
|
|
])));
|
|
if (array_key_exists('tags', $data)) {
|
|
$this->syncTags($task, $data['tags']);
|
|
}
|
|
return response()->json($task->load('project', 'tags'));
|
|
}
|
|
|
|
public function done(Request $r, Task $task): JsonResponse {
|
|
abort_unless($task->user_id === $r->user()->id, 403);
|
|
$task->update(['done' => true, 'done_at' => now()]);
|
|
// Si la tarea es recurrente, generamos la siguiente
|
|
if ($task->recurrence) {
|
|
$next = $task->replicate(['done', 'done_at', 'snooze_until']);
|
|
$next->done = false;
|
|
$next->done_at = null;
|
|
$next->snooze_until = null;
|
|
$next->deadline = match ($task->recurrence) {
|
|
'daily' => $task->deadline?->addDay() ?? now()->addDay(),
|
|
'weekly' => $task->deadline?->addWeek() ?? now()->addWeek(),
|
|
'monthly' => $task->deadline?->addMonth() ?? now()->addMonth(),
|
|
default => null,
|
|
};
|
|
$next->save();
|
|
}
|
|
return response()->json(['ok' => true, 'id' => $task->id]);
|
|
}
|
|
|
|
public function snooze(Request $r, Task $task): JsonResponse {
|
|
abort_unless($task->user_id === $r->user()->id, 403);
|
|
$minutes = (int) $r->input('minutes', 5);
|
|
$minutes = max(1, min(720, $minutes));
|
|
$task->update(['snooze_until' => now()->addMinutes($minutes)]);
|
|
return response()->json(['ok' => true, 'id' => $task->id, 'minutes' => $minutes]);
|
|
}
|
|
|
|
public function destroy(Request $r, Task $task): JsonResponse {
|
|
abort_unless($task->user_id === $r->user()->id, 403);
|
|
$task->delete();
|
|
return response()->json(['ok' => true]);
|
|
}
|
|
|
|
public function restore(Request $r, Task $task): JsonResponse {
|
|
abort_unless($task->user_id === $r->user()->id, 403);
|
|
$task->update(['done' => false, 'done_at' => null, 'snooze_until' => null]);
|
|
return response()->json($task);
|
|
}
|
|
|
|
/** Estadísticas rápidas para el dashboard */
|
|
public function stats(Request $r): JsonResponse {
|
|
$userId = $r->user()->id;
|
|
$today = now()->startOfDay();
|
|
return response()->json([
|
|
'pending' => Task::forUser($userId)->active()->count(),
|
|
'today' => Task::forUser($userId)->active()
|
|
->where('created_at', '>=', $today)->count(),
|
|
'done_today'=> Task::forUser($userId)->where('done', true)
|
|
->where('done_at', '>=', $today)->count(),
|
|
'overdue' => Task::forUser($userId)->active()
|
|
->where('deadline', '<', now())->count(),
|
|
'total_done'=> Task::forUser($userId)->where('done', true)->count(),
|
|
]);
|
|
}
|
|
|
|
private function syncTags(Task $task, array $tagNames): void {
|
|
$userId = $task->user_id;
|
|
$ids = [];
|
|
foreach ($tagNames as $name) {
|
|
$tag = \App\Models\Tag::firstOrCreate(
|
|
['user_id' => $userId, 'name' => $name],
|
|
['color' => '#a3a3a3']
|
|
);
|
|
$ids[] = $tag->id;
|
|
}
|
|
$task->tags()->sync($ids);
|
|
}
|
|
}
|