feat(tasks): complete task management system with Kanban, Calendar, notifications

- Add Task model with subtasks, priorities, status transitions, dates, hours
- Add Comment polymorphic model for tasks/issues/projects
- Livewire components: TaskManager (list+filters), TaskForm (modal), TaskDetail, TaskKanban (drag&drop), TaskCalendar (FullCalendar)
- TaskPolicy with permissions (view/create/edit/delete/assign/manage all)
- Notifications: assigned, status change, overdue, comment added
- Daily overdue notification job scheduled
- Dashboard widget unifies IssueTask + Task
- i18n: en/es/fr/ru (393 keys each)
- Routes, navigation, offline sync support
- 101 tests passing, Pint compliant on new files
This commit is contained in:
Javier Braña
2026-08-03 13:44:10 +02:00
parent a65d4b12f2
commit ba614bddbc
62 changed files with 5878 additions and 382 deletions
@@ -18,11 +18,11 @@
<div tabindex="0" class="dropdown-content z-[50] menu p-0 shadow-lg bg-base-100 rounded-box w-80 mt-1 border border-base-200">
<!-- Header -->
<div class="flex items-center justify-between px-4 py-3 border-b border-base-200">
<span class="font-semibold text-base-content">Notificaciones</span>
<span class="font-semibold text-base-content">{{ __('Notifications') }}</span>
@if($unreadCount > 0)
<button wire:click="markAllAsRead"
class="text-xs text-primary hover:underline focus:outline-none">
Marcar todas
{{ __('Mark all as read') }}
</button>
@endif
</div>
@@ -48,7 +48,7 @@
<!-- Content -->
<div class="flex-1 min-w-0">
<p class="text-sm text-base-content leading-snug">
{{ $data['message'] ?? 'Notificación' }}
{{ $data['message'] ?? __('Notification') }}
</p>
<p class="text-xs text-base-content/50 mt-1">
{{ $createdAt->diffForHumans() }}
@@ -59,7 +59,7 @@
@if($isUnread)
<button wire:click="markAsRead('{{ $notification['id'] }}')"
class="shrink-0 text-base-content/40 hover:text-primary focus:outline-none"
title="Marcar como leída">
title="{{ __('Mark as read') }}">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd" />
</svg>
@@ -68,7 +68,7 @@
</li>
@empty
<li class="px-4 py-8 text-center text-sm text-base-content/50">
No hay notificaciones
{{ __('No notifications') }}
</li>
@endforelse
</ul>
@@ -78,7 +78,7 @@
<div class="border-t border-base-200 px-4 py-2 text-center">
<button wire:click="markAllAsRead"
class="btn btn-ghost btn-xs w-full text-primary">
Marcar todas como leídas
{{ __('Mark all as read') }}
</button>
</div>
@endif