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:
@@ -2,11 +2,11 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Feature;
|
||||
use App\Models\Inspection;
|
||||
use App\Models\PendingSync;
|
||||
use App\Models\Phase;
|
||||
use App\Models\Inspection;
|
||||
use App\Models\Feature;
|
||||
use App\Models\Media;
|
||||
use App\Models\Task;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
@@ -16,7 +16,7 @@ class OfflineSyncController extends Controller
|
||||
public function storePending(Request $request)
|
||||
{
|
||||
$payload = $request->validate([
|
||||
'action' => 'required|in:progress_update,inspection,feature_create,media_upload,task_complete',
|
||||
'action' => 'required|in:progress_update,inspection,feature_create,media_upload,task_complete,task_create,task_update',
|
||||
'payload' => 'required|array',
|
||||
]);
|
||||
$pending = PendingSync::create([
|
||||
@@ -24,6 +24,7 @@ class OfflineSyncController extends Controller
|
||||
'action' => $payload['action'],
|
||||
'payload' => $payload['payload'],
|
||||
]);
|
||||
|
||||
return response()->json(['queued' => true]);
|
||||
}
|
||||
|
||||
@@ -89,6 +90,22 @@ class OfflineSyncController extends Controller
|
||||
// For now, just log and mark as success
|
||||
\Log::info('Task completed offline', $pending->payload);
|
||||
$result['success'] = true;
|
||||
} elseif ($pending->action === 'task_create') {
|
||||
$task = Task::create($pending->payload);
|
||||
$result['success'] = true;
|
||||
$result['data'] = ['task_id' => $task->id, 'uuid' => $task->uuid];
|
||||
} elseif ($pending->action === 'task_update') {
|
||||
$task = Task::find($pending->payload['id'] ?? null);
|
||||
if ($task) {
|
||||
// Remove fields that shouldn't be mass updated
|
||||
$data = $pending->payload;
|
||||
unset($data['id'], $data['uuid'], $data['created_at'], $data['updated_at'], $data['deleted_at']);
|
||||
$task->update($data);
|
||||
$result['success'] = true;
|
||||
$result['data'] = ['task_id' => $task->id];
|
||||
} else {
|
||||
$result['error'] = 'Task not found';
|
||||
}
|
||||
} else {
|
||||
$result['error'] = 'Unknown action type';
|
||||
}
|
||||
@@ -103,6 +120,7 @@ class OfflineSyncController extends Controller
|
||||
|
||||
$results[] = $result;
|
||||
}
|
||||
|
||||
return response()->json(['synced' => $results]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,11 +16,12 @@ class SetLocale
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
$locale = null;
|
||||
$allowedLocales = ['en', 'es', 'fr', 'ru'];
|
||||
|
||||
// 1. From authenticated user preference
|
||||
if (Auth::check()) {
|
||||
$userLocale = Auth::user()->locale;
|
||||
if ($userLocale && in_array($userLocale, ['en', 'es'])) {
|
||||
if ($userLocale && in_array($userLocale, $allowedLocales)) {
|
||||
$locale = $userLocale;
|
||||
}
|
||||
}
|
||||
@@ -28,7 +29,7 @@ class SetLocale
|
||||
// 2. From session
|
||||
if (!$locale && Session::has('locale')) {
|
||||
$sessionLocale = Session::get('locale');
|
||||
if (in_array($sessionLocale, ['en', 'es'])) {
|
||||
if (in_array($sessionLocale, $allowedLocales)) {
|
||||
$locale = $sessionLocale;
|
||||
}
|
||||
}
|
||||
@@ -36,14 +37,14 @@ class SetLocale
|
||||
// 3. From browser Accept-Language
|
||||
if (!$locale) {
|
||||
$browserLang = substr($request->server('HTTP_ACCEPT_LANGUAGE', 'en'), 0, 2);
|
||||
if (in_array($browserLang, ['en', 'es'])) {
|
||||
if (in_array($browserLang, $allowedLocales)) {
|
||||
$locale = $browserLang;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Default to app locale
|
||||
if (!$locale) {
|
||||
$locale = config('app.locale', 'es');
|
||||
$locale = config('app.locale', 'en');
|
||||
}
|
||||
|
||||
App::setLocale($locale);
|
||||
|
||||
Reference in New Issue
Block a user